<commit_before>#! /usr/bin/env python
# -*- coding: utf-8 -*-

# Grenouille - An online service for weather data.
# Copyright (C) 2014 Cédric Bonhomme - http://cedricbonhomme.org/
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

__author__ = "Cedric Bonhomme"
__version__ = "$Revision: 0.3 $"
__date__ = "$Date: 2014/03/16 $"
__revision__ = "$Date: 2014/03/04 $"
__copyright__ = "Copyright (c) Cedric Bonhomme"
__license__ = "AGPLv3"

import json
from datetime import datetime
from flask import (
    render_template,
    jsonify,
    request,
    flash,
    session,
    url_for,
    redirect,
    g,
    current_app,
    make_response,
)
from flask_login import (
    LoginManager,
    login_user,
    logout_user,
    login_required,
    current_user,
    AnonymousUserMixin,
)
from flask_principal import (
    Principal,
    Identity,
    AnonymousIdentity,
    identity_changed,
    identity_loaded,
    Permission,
    RoleNeed,
    UserNeed,
)
from sqlalchemy.exc import IntegrityError

from web import app, db
from web.models import User, Station, Role
from web.forms import SignupForm, SigninForm, ProfileForm, StationForm
from werkzeug.security import generate_password_hash

import logging

logging.getLogger("pycountry").addHandler(logging.NullHandler())
import pycountry

Principal(app)
# Create a permission with a single Need, in this case a RoleNeed.
admin_permission = Permission(RoleNeed("admin"))

login_manager = LoginManager()
login_manager.init_app(app)

#
# Management of the user's session.
#
@identity_loaded.connect_via(app)
def on_identity_loaded(sender, identity):
    # Set the identity user object
    identity.user = current_user

    # Add the UserNeed to the identity
    if hasattr(current_user, "id"):
        identity.provides.add(UserNeed(current_user.id))

    # Assuming the User model has a list of roles, update the
    # identity with the roles that the user provides
    if hasattr(current_user, "roles"):
        for role in current_user.roles:
            identity.provides.add(RoleNeed(role.name))

@app.before_request
def before_request():
    g.user = current_user
    if g.user.is_authenticated:
        g.user.last_seen = datetime.utcnow()
        db.session.add(g.user)
        db.session.commit()

#
# Custom error pages.
#
@app.errorhandler(401)
def authentication_required(e):
    flash("Authentication required.", "info")
    return redirect(url_for("login"))

@app.errorhandler(403)
def authentication_failed(e):
    flash("Forbidden.", "danger")
    return redirect(url_for("login"))

@app.errorhandler(404)
def page_not_found(e):
    return render_template("errors/404.html"), 404

@app.errorhandler(500)
def internal_server_error(e):
    return render_template("errors/500.html"), 500

@login_manager.user_loader
def load_user(email):
    # Return an instance of the User model
    return User.query.filter(User.email == email).first()

def redirect_url(default="map_view"):
    return request.args.get("next") or request.referrer or url_for(default)

#
# Views.
#
@app.route("/login/", methods=["GET", "POST"])
def login():
    """
    Log in view.
    """
    g.user = AnonymousUserMixin()
    form = SigninForm()

    if form.validate():
        user = User.query.filter(User.email == form.email.data).first()
        login_user(user)
        g.user = user
        identity_changed.send(
            current_app._get_current_object(), identity=Identity(user.id)
        )
        flash("Logged in successfully.", "success")
        return redirect(url_for("profile"))
    return render_template("login.html", form=form)

@app.route("/logout/")
@login_required
def logout():
    """
    Log out view. Removes the user information from the session.
    """
    # Update last_seen field
    g.user.last_seen = datetime.utcnow()
    db.session.add(g.user)
    db.session.commit()

    # Remove the user information from the session
    logout_user()

    # Remove session keys set by Flask-Principal
    for key in ("identity.name", "identity.auth_type"):
        session.pop(key, None)

    # Tell Flask-Principal the user is anonymous
    identity_changed.send(
        current_app._get_current_object(), identity=AnonymousIdentity()
    )

    flash("Logged out successfully.", "success")
    return redirect(url_for("map_view"))

@app.route("/signup/", methods=["GET", "POST"])
def signup():
    """
    Signup page.
    """
    form = SignupForm()

    if form.validate():
        role_user = Role.query.filter(Role.name == "user").first()
        user = User(
            firstname=form.firstname.data,
            lastname=form.lastname.data,
            email=form.email.data,
            pwdhash=generate_password_hash(form.password.data),
        )
        user.roles = [role_user]
        db.session.add(user)
        try:
            db.session.commit()
        except IntegrityError:
            flash("Email already used.", "warning")
            return render_template("signup.html", form=form)

        flash("Your account has been created. You can now sign in.", "success")
        return redirect(url_for("login"))

    return render_template("signup.html", form=form)

@app.route("/", methods=["GET"])
def map_view():
    """
    Main view which displays all public stations.
    """
    return render_template("map.html")

@app.route("/profile/", methods=["GET", "POST"])
@login_required
def profile():
    """
    Edit the profile of the currently logged user.
    """
    user = User.query.filter(User.email == g.user.email).first()
    form = ProfileForm()

    if request.method == "POST":
        if form.validate():
            form.populate_obj(user)
            if form.password.data != "":
                user.set_password(form.password.data)
            db.session.commit()
            flash('User "' + user.firstname + '" successfully updated.', "success")
            return redirect(url_for("profile"))
        else:
            return render_template("profile.html", form=form)

    if request.method == "GET":
        form = ProfileForm(obj=user)
        return render_template("profile.html", user=user, form=form)

@app.route("/station/<int:station_id>/", methods=["GET"])
def station(station_id=None):
    """
    Edit the information about a station.
    """
    station = Station.query.filter(Station.id == station_id).first()
    country = pycountry.countries.get(alpha2=station.country).official_name
    return render_template("station.html", station=station, country=country)

@app.route("/create_station/", methods=["GET", "POST"])
@app.route("/edit_station/<int:station_id>/", methods=["GET", "POST"])
@login_required
def edit_station(station_id=None):
    """
    Add or edit a station.
    """
    user = User.query.filter(User.email == g.user.email).first()
    form = StationForm()

    if station_id != None:
        station = None
        for station in user.stations:
            if station.id == station_id:
                station = station
                break
        else:
            flash("This station does not exist.", "danger")
            return redirect(redirect_url())

    if request.method == "POST":
        if form.validate():
            if station_id != None:
                # Edit a station
                form.populate_obj(station)
                flash('Station "' + station.name + '" successfully updated.', "success")
            else:
                # Create a new station
                station = Station(
                    name=form.name.data,
                    country=form.country.data,
                    altitude=form.altitude.data,
                    latitude=form.latitude.data,
                    longitude=form.longitude.data,
                    user_id=user.id,
                )
                user.stations.append(station)
                flash('Station "' + station.name + '" successfully created.', "success")
            db.session.commit()
        else:
            flash("Problem with the form.", "danger")
            return redirect(redirect_url())
        return redirect("/edit_station/" + str(station.id) + "/")

    if request.method == "GET":
        if station_id != None:
            form = StationForm(obj=station)
            message = "Edit the station " + station.name + ""
        else:
            form = StationForm()
            message = "Add a new station"
        return render_template(
            "edit_station.html", user=user, form=form, message=message
        )

@app.route("/delete_station/<int:station_id>/", methods=["GET"])
@login_required
def delete_station(station_id=None):
    """
    Delete a station.
    """
    user = User.query.filter(User.email == g.user.email).first()
    for station in user.stations:
        if station.id == station_id:
            db.session.delete(station)
            db.session.commit()
            flash('Station "' + station.name + '" successfully deleted.', "success")
            break
    else:
        flash("This station does not exist.", "danger")
    return redirect(redirect_url())

@app.route("/download_station/<int:station_id>/", methods=["GET"])
@login_required
def download_station(station_id):
    """
    Return all measures sent by a station in a JSON file.
    """
    user = User.query.filter(User.email == g.user.email).first()
    measures = []
    for station in user.stations:
        if station.id == station_id:
            measures.extend([json.loads(str(measure)) for measure in station.measures])
            break
    r = make_response(jsonify(measures=measures))
    r.mimetype = "application/json"
    r.headers["Content-Disposition"] = (
        "attachment; filename=measures_station_" + str(station.id) + ".json"
    )
    return r

@app.route("/delete_account/", methods=["GET"])
@login_required
def delete_account():
    """
    Delete the account of the user (with all its data).
    """
    user = User.query.filter(User.email == g.user.email).first()
    if user is not None:
        db.session.delete(user)
        db.session.commit()
        flash("Your account has been deleted.", "success")
    else:
        flash("This user does not exist.", "danger")
    return redirect(url_for("login"))

@app.route("/about/", methods=["GET"])
def about():
    """
    'About' page.
    """
    return render_template("about.html")

#
# Views dedicated to administration tasks.
#
@app.route("/admin/dashboard/", methods=["GET", "POST"])
@login_required
@admin_permission.require()
def dashboard():
    """
    Adminstrator's dashboard.
    """
    users = User.query.all()
    users.remove(g.user)
    return render_template("admin/dashboard.html", users=users)

@app.route("/admin/create_user/", methods=["GET", "POST"])
@app.route("/admin/edit_user/<int:user_id>/", methods=["GET", "POST"])
@login_required
@admin_permission.require()
def create_user(user_id=None):
    """
    Create or edit a user.
    """
    form = ProfileForm()

    if request.method == "POST":
        if form.validate():
            if user_id != None:
                # Edit a user
                user = User.query.filter(User.id == user_id).first()
                form.populate_obj(user)
                if form.password.data != "":
                    user.set_password(form.password.data)
                db.session.commit()
                flash('User "' + user.firstname + '" successfully updated.', "success")
            else:
                # Create a new user
                role_user = Role.query.filter(Role.name == "user").first()
                user = User(
                    firstname=form.firstname.data,
                    lastname=form.lastname.data,
                    email=form.email.data,
                    pwdhash=generate_password_hash(form.password.data),
                )
                user.roles.extend([role_user])
                db.session.add(user)
                try:
                    db.session.commit()
                except IntegrityError:
                    flash("Email already used.", "warning")
                    return redirect(redirect_url())
                flash('User "' + user.firstname + '" successfully created.', "success")
            return redirect("/admin/edit_user/" + str(user.id) + "/")
        else:
            return render_template("profile.html", form=form)

    if request.method == "GET":
        if user_id != None:
            user = User.query.filter(User.id == user_id).first()
            form = ProfileForm(obj=user)
            message = "Edit the user " + user.firstname + ""
        else:
            form = ProfileForm()
            message = "Add a new user"
        return render_template("/admin/create_user.html", form=form, message=message)

@app.route("/admin/user/<int:user_id>/", methods=["GET"])
@login_required
@admin_permission.require()
def user(user_id=None):
    """
    See information about a user (stations, etc.).
    """
    user = User.query.filter(User.id == user_id).first()
    if user is not None:
        return render_template("/admin/user.html", user=user)
    else:
        flash("This user does not exist.", "danger")
        return redirect(redirect_url())

@app.route("/admin/delete_user/<int:user_id>/", methods=["GET"])
@login_required
@admin_permission.require()
def delete_user(user_id=None):
    """
    Delete a user (with its stations and measures).
    """
    user = User.query.filter(User.id == user_id).first()
    if user is not None:
        db.session.delete(user)
        db.session.commit()
        flash('User "' + user.firstname + '" successfully deleted.', "success")
    else:
        flash("This user does not exist.", "danger")
    return redirect(redirect_url())

@app.route("/admin/enable_user/<int:user_id>/", methods=["GET"])
@app.route("/admin/disable_user/<int:user_id>/", methods=["GET"])
@login_required
@admin_permission.require()
def disable_user(user_id=None):
    """
    Enable or disable the API key of a user.
    """
    user = User.query.filter(User.id == user_id).first()
    if user is not None:
        if user.apikey != "":
            user.apikey = ""
            flash(
                'API key of the user "' + user.firstname + '" successfully disabled.',
                "success",
            )
        else:
            import random, base64, hashlib

            user.apikey = base64.b64encode(
                hashlib.sha512(str(random.getrandbits(256))).digest(),
                random.choice(["rA", "aZ", "gQ", "hH", "hG", "aR", "DD"]),
            ).rstrip("==")
            flash(
                'API key of the user "' + user.firstname + '" successfully enabled.',
                "success",
            )
        db.session.commit()
    else:
        flash("This user does not exist.", "danger")
    return redirect(redirect_url())
<commit_msg>more fixes<commit_after>#! /usr/bin/env python
# -*- coding: utf-8 -*-

# Grenouille - An online service for weather data.
# Copyright (C) 2014 Cédric Bonhomme - http://cedricbonhomme.org/
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

__author__ = "Cedric Bonhomme"
__version__ = "$Revision: 0.3 $"
__date__ = "$Date: 2014/03/16 $"
__revision__ = "$Date: 2014/03/04 $"
__copyright__ = "Copyright (c) Cedric Bonhomme"
__license__ = "AGPLv3"

import json
from datetime import datetime
from flask import (
    render_template,
    jsonify,
    request,
    flash,
    session,
    url_for,
    redirect,
    g,
    current_app,
    make_response,
)
from flask_login import (
    LoginManager,
    login_user,
    logout_user,
    login_required,
    current_user,
    AnonymousUserMixin,
)
from flask_principal import (
    Principal,
    Identity,
    AnonymousIdentity,
    identity_changed,
    identity_loaded,
    Permission,
    RoleNeed,
    UserNeed,
)
from sqlalchemy.exc import IntegrityError

from web import app, db
from web.models import User, Station, Role
from web.forms import SignupForm, SigninForm, ProfileForm, StationForm
from werkzeug.security import generate_password_hash

import logging

logging.getLogger("pycountry").addHandler(logging.NullHandler())
import pycountry

Principal(app)
# Create a permission with a single Need, in this case a RoleNeed.
admin_permission = Permission(RoleNeed("admin"))

login_manager = LoginManager()
login_manager.init_app(app)

#
# Management of the user's session.
#
@identity_loaded.connect_via(app)
def on_identity_loaded(sender, identity):
    # Set the identity user object
    identity.user = current_user

    # Add the UserNeed to the identity
    if hasattr(current_user, "id"):
        identity.provides.add(UserNeed(current_user.id))

    # Assuming the User model has a list of roles, update the
    # identity with the roles that the user provides
    if hasattr(current_user, "roles"):
        for role in current_user.roles:
            identity.provides.add(RoleNeed(role.name))

@app.before_request
def before_request():
    g.user = current_user
    if g.user.is_authenticated:
        g.user.last_seen = datetime.utcnow()
        db.session.add(g.user)
        db.session.commit()

#
# Custom error pages.
#
@app.errorhandler(401)
def authentication_required(e):
    flash("Authentication required.", "info")
    return redirect(url_for("login"))

@app.errorhandler(403)
def authentication_failed(e):
    flash("Forbidden.", "danger")
    return redirect(url_for("login"))

@app.errorhandler(404)
def page_not_found(e):
    return render_template("errors/404.html"), 404

@app.errorhandler(500)
def internal_server_error(e):
    return render_template("errors/500.html"), 500

@login_manager.user_loader
def load_user(email):
    # Return an instance of the User model
    return User.query.filter(User.email == email).first()

def redirect_url(default="map_view"):
    return request.args.get("next") or request.referrer or url_for(default)

#
# Views.
#
@app.route("/login/", methods=["GET", "POST"])
def login():
    """
    Log in view.
    """
    g.user = AnonymousUserMixin()
    form = SigninForm()

    if form.validate():
        user = User.query.filter(User.email == form.email.data).first()
        login_user(user)
        g.user = user
        identity_changed.send(
            current_app._get_current_object(), identity=Identity(user.id)
        )
        flash("Logged in successfully.", "success")
        return redirect(url_for("profile"))
    return render_template("login.html", form=form)

@app.route("/logout/")
@login_required
def logout():
    """
    Log out view. Removes the user information from the session.
    """
    # Update last_seen field
    g.user.last_seen = datetime.utcnow()
    db.session.add(g.user)
    db.session.commit()

    # Remove the user information from the session
    logout_user()

    # Remove session keys set by Flask-Principal
    for key in ("identity.name", "identity.auth_type"):
        session.pop(key, None)

    # Tell Flask-Principal the user is anonymous
    identity_changed.send(
        current_app._get_current_object(), identity=AnonymousIdentity()
    )

    flash("Logged out successfully.", "success")
    return redirect(url_for("map_view"))

@app.route("/signup/", methods=["GET", "POST"])
def signup():
    """
    Signup page.
    """
    form = SignupForm()

    if form.validate():
        role_user = Role.query.filter(Role.name == "user").first()
        user = User(
            firstname=form.firstname.data,
            lastname=form.lastname.data,
            email=form.email.data,
            pwdhash=generate_password_hash(form.password.data),
        )
        user.roles = [role_user]
        db.session.add(user)
        try:
            db.session.commit()
        except IntegrityError:
            flash("Email already used.", "warning")
            return render_template("signup.html", form=form)

        flash("Your account has been created. You can now sign in.", "success")
        return redirect(url_for("login"))

    return render_template("signup.html", form=form)

@app.route("/", methods=["GET"])
def map_view():
    """
    Main view which displays all public stations.
    """
    return render_template("map.html")

@app.route("/profile/", methods=["GET", "POST"])
@login_required
def profile():
    """
    Edit the profile of the currently logged user.
    """
    user = User.query.filter(User.email == g.user.email).first()
    form = ProfileForm()

    if request.method == "POST":
        if form.validate():
            form.populate_obj(user)
            if form.password.data != "":
                user.set_password(form.password.data)
            db.session.commit()
            flash('User "' + user.firstname + '" successfully updated.', "success")
            return redirect(url_for("profile"))
        else:
            return render_template("profile.html", form=form)

    if request.method == "GET":
        form = ProfileForm(obj=user)
        return render_template("profile.html", user=user, form=form)

@app.route("/station/<int:station_id>/", methods=["GET"])
def station(station_id=None):
    """
    Edit the information about a station.
    """
    station = Station.query.filter(Station.id == station_id).first()
    country = pycountry.countries.get(alpha_2=station.country).official_name
    return render_template("station.html", station=station, country=country)

@app.route("/create_station/", methods=["GET", "POST"])
@app.route("/edit_station/<int:station_id>/", methods=["GET", "POST"])
@login_required
def edit_station(station_id=None):
    """
    Add or edit a station.
    """
    user = User.query.filter(User.email == g.user.email).first()
    form = StationForm()

    if station_id != None:
        station = None
        for station in user.stations:
            if station.id == station_id:
                station = station
                break
        else:
            flash("This station does not exist.", "danger")
            return redirect(redirect_url())

    if request.method == "POST":
        if form.validate():
            if station_id != None:
                # Edit a station
                form.populate_obj(station)
                flash('Station "' + station.name + '" successfully updated.', "success")
            else:
                # Create a new station
                station = Station(
                    name=form.name.data,
                    country=form.country.data,
                    altitude=form.altitude.data,
                    latitude=form.latitude.data,
                    longitude=form.longitude.data,
                    user_id=user.id,
                )
                user.stations.append(station)
                flash('Station "' + station.name + '" successfully created.', "success")
            db.session.commit()
        else:
            flash("Problem with the form.", "danger")
            return redirect(redirect_url())
        return redirect("/edit_station/" + str(station.id) + "/")

    if request.method == "GET":
        if station_id != None:
            form = StationForm(obj=station)
            message = "Edit the station " + station.name + ""
        else:
            form = StationForm()
            message = "Add a new station"
        return render_template(
            "edit_station.html", user=user, form=form, message=message
        )

@app.route("/delete_station/<int:station_id>/", methods=["GET"])
@login_required
def delete_station(station_id=None):
    """
    Delete a station.
    """
    user = User.query.filter(User.email == g.user.email).first()
    for station in user.stations:
        if station.id == station_id:
            db.session.delete(station)
            db.session.commit()
            flash('Station "' + station.name + '" successfully deleted.', "success")
            break
    else:
        flash("This station does not exist.", "danger")
    return redirect(redirect_url())

@app.route("/download_station/<int:station_id>/", methods=["GET"])
@login_required
def download_station(station_id):
    """
    Return all measures sent by a station in a JSON file.
    """
    user = User.query.filter(User.email == g.user.email).first()
    measures = []
    for station in user.stations:
        if station.id == station_id:
            measures.extend([json.loads(str(measure)) for measure in station.measures])
            break
    r = make_response(jsonify(measures=measures))
    r.mimetype = "application/json"
    r.headers["Content-Disposition"] = (
        "attachment; filename=measures_station_" + str(station.id) + ".json"
    )
    return r

@app.route("/delete_account/", methods=["GET"])
@login_required
def delete_account():
    """
    Delete the account of the user (with all its data).
    """
    user = User.query.filter(User.email == g.user.email).first()
    if user is not None:
        db.session.delete(user)
        db.session.commit()
        flash("Your account has been deleted.", "success")
    else:
        flash("This user does not exist.", "danger")
    return redirect(url_for("login"))

@app.route("/about/", methods=["GET"])
def about():
    """
    'About' page.
    """
    return render_template("about.html")

#
# Views dedicated to administration tasks.
#
@app.route("/admin/dashboard/", methods=["GET", "POST"])
@login_required
@admin_permission.require()
def dashboard():
    """
    Adminstrator's dashboard.
    """
    users = User.query.all()
    users.remove(g.user)
    return render_template("admin/dashboard.html", users=users)

@app.route("/admin/create_user/", methods=["GET", "POST"])
@app.route("/admin/edit_user/<int:user_id>/", methods=["GET", "POST"])
@login_required
@admin_permission.require()
def create_user(user_id=None):
    """
    Create or edit a user.
    """
    form = ProfileForm()

    if request.method == "POST":
        if form.validate():
            if user_id != None:
                # Edit a user
                user = User.query.filter(User.id == user_id).first()
                form.populate_obj(user)
                if form.password.data != "":
                    user.set_password(form.password.data)
                db.session.commit()
                flash('User "' + user.firstname + '" successfully updated.', "success")
            else:
                # Create a new user
                role_user = Role.query.filter(Role.name == "user").first()
                user = User(
                    firstname=form.firstname.data,
                    lastname=form.lastname.data,
                    email=form.email.data,
                    pwdhash=generate_password_hash(form.password.data),
                )
                user.roles.extend([role_user])
                db.session.add(user)
                try:
                    db.session.commit()
                except IntegrityError:
                    flash("Email already used.", "warning")
                    return redirect(redirect_url())
                flash('User "' + user.firstname + '" successfully created.', "success")
            return redirect("/admin/edit_user/" + str(user.id) + "/")
        else:
            return render_template("profile.html", form=form)

    if request.method == "GET":
        if user_id != None:
            user = User.query.filter(User.id == user_id).first()
            form = ProfileForm(obj=user)
            message = "Edit the user " + user.firstname + ""
        else:
            form = ProfileForm()
            message = "Add a new user"
        return render_template("/admin/create_user.html", form=form, message=message)

@app.route("/admin/user/<int:user_id>/", methods=["GET"])
@login_required
@admin_permission.require()
def user(user_id=None):
    """
    See information about a user (stations, etc.).
    """
    user = User.query.filter(User.id == user_id).first()
    if user is not None:
        return render_template("/admin/user.html", user=user)
    else:
        flash("This user does not exist.", "danger")
        return redirect(redirect_url())

@app.route("/admin/delete_user/<int:user_id>/", methods=["GET"])
@login_required
@admin_permission.require()
def delete_user(user_id=None):
    """
    Delete a user (with its stations and measures).
    """
    user = User.query.filter(User.id == user_id).first()
    if user is not None:
        db.session.delete(user)
        db.session.commit()
        flash('User "' + user.firstname + '" successfully deleted.', "success")
    else:
        flash("This user does not exist.", "danger")
    return redirect(redirect_url())

@app.route("/admin/enable_user/<int:user_id>/", methods=["GET"])
@app.route("/admin/disable_user/<int:user_id>/", methods=["GET"])
@login_required
@admin_permission.require()
def disable_user(user_id=None):
    """
    Enable or disable the API key of a user.
    """
    user = User.query.filter(User.id == user_id).first()
    if user is not None:
        if user.apikey != "":
            user.apikey = ""
            flash(
                'API key of the user "' + user.firstname + '" successfully disabled.',
                "success",
            )
        else:
            import random, base64, hashlib

            user.apikey = base64.b64encode(
                hashlib.sha512(str(random.getrandbits(256))).digest(),
                random.choice(["rA", "aZ", "gQ", "hH", "hG", "aR", "DD"]),
            ).rstrip("==")
            flash(
                'API key of the user "' + user.firstname + '" successfully enabled.',
                "success",
            )
        db.session.commit()
    else:
        flash("This user does not exist.", "danger")
    return redirect(redirect_url())
<|endoftext|>


<commit_before>import asyncio
from collections import defaultdict
from functools import partial
import json
import logging
import random
import uuid

from again.utils import unique_hex
import aiohttp

from .services import TCPServiceClient, HTTPServiceClient
from .pubsub import PubSub
from .packet import ControlPacket, MessagePacket
from .protocol_factory import get_vyked_protocol
from .utils.jsonencoder import VykedEncoder
from .exceptions import ClientNotFoundError, ClientDisconnected

HTTP = 'http'
TCP = 'tcp'

def _retry_for_pub(result):
    return not result

def _retry_for_exception(_):
    return True

class HTTPBus:

    def __init__(self, registry_client):
        self._registry_client = registry_client

    def send_http_request(self, app: str, service: str, version: str, method: str, entity: str, params: dict):
        """
        A convenience method that allows you to send a well formatted http request to another service
        """
        host, port, node_id, service_type = self._registry_client.resolve(service, version, entity, HTTP)

        url = 'http://{}:{}{}'.format(host, port, params.pop('path'))

        http_keys = ['data', 'headers', 'cookies', 'auth', 'allow_redirects', 'compress', 'chunked']
        kwargs = {k: params[k] for k in http_keys if k in params}

        query_params = params.pop('params', {})

        if app is not None:
            query_params['app'] = app

        query_params['version'] = version
        query_params['service'] = service

        response = yield from aiohttp.request(method, url, params=query_params, **kwargs)
        return response

class TCPBus:

    def __init__(self, registry_client):
        registry_client.conn_handler = self
        self._registry_client = registry_client
        self._client_protocols = {}
        self._pingers = {}
        self._node_clients = {}
        self._service_clients = []
        self.tcp_host = None
        self.http_host = None
        self._host_id = unique_hex()
        self._ronin = False
        self._registered = False
        self._logger = logging.getLogger(__name__)

    def _create_service_clients(self):
        futures = []
        for sc in self._service_clients:
            for host, port, node_id, service_type in self._registry_client.get_all_addresses(*sc.properties):
                if service_type == 'tcp':
                    self._node_clients[node_id] = sc
                    future = self._connect_to_client(host, node_id, port, service_type, sc)
                    futures.append(future)
        return asyncio.gather(*futures, return_exceptions=False)

    def register(self):
        clients = self.tcp_host.clients if self.tcp_host else self.http_host.clients
        for client in clients:
            if isinstance(client, (TCPServiceClient, HTTPServiceClient)):
                client.bus = self
        self._service_clients = clients
        asyncio.get_event_loop().run_until_complete(self._registry_client.connect())

    def registration_complete(self):
        if not self._registered:
            self._create_service_clients()
            self._registered = True

    def new_instance(self, service, version, host, port, node_id, type):
        sc = next(sc for sc in self._service_clients if sc.name == service and sc.version == version)
        if type == 'tcp':
            self._node_clients[node_id] = sc
            asyncio.async(self._connect_to_client(host, node_id, port, type, sc))

    def send(self, packet: dict):
        packet['from'] = self._host_id
        func = getattr(self, '_' + packet['type'] + '_sender')
        func(packet)

    def _request_sender(self, packet: dict):
        """
        Sends a request to a server from a ServiceClient
        auto dispatch method called from self.send()
        """
        node_id = self._get_node_id_for_packet(packet)
        client_protocol = self._client_protocols.get(node_id)

        if node_id and client_protocol:
            if client_protocol.is_connected():
                packet['to'] = node_id
                client_protocol.send(packet)
            else:
                self._logger.error('Client protocol is not connected for packet %s', packet)
                raise ClientDisconnected()
        else:
            # No node found to send request
            self._logger.error('Out of %s, Client Not found for packet %s', self._client_protocols.keys(), packet)
            raise ClientNotFoundError()

    def _connect_to_client(self, host, node_id, port, service_type, service_client):

        future = asyncio.async(
            asyncio.get_event_loop().create_connection(partial(get_vyked_protocol, service_client), host, port,
                                                       ssl=service_client._ssl_context))
        future.add_done_callback(
            partial(self._service_client_connection_callback, self._node_clients[node_id], node_id, service_type))
        return future

    def _service_client_connection_callback(self, sc, node_id, service_type, future):
        _, protocol = future.result()
        # TODO : handle pinging
        # if service_type == TCP:
        #     pinger = Pinger(self, asyncio.get_event_loop())
        #     self._pingers[node_id] = pinger
        #     pinger.register_tcp_service(protocol, node_id)
        #     asyncio.async(pinger.start_ping())
        self._client_protocols[node_id] = protocol

    @staticmethod
    def _create_json_service_name(app, service, version):
        return {'app': app, 'service': service, 'version': version}

    @staticmethod
    def _handle_ping(packet, protocol):
        protocol.send(ControlPacket.pong(packet['node_id']))

    def _handle_pong(self, node_id, count):
        pinger = self._pingers[node_id]
        asyncio.async(pinger.pong_received(count))

    def _get_node_id_for_packet(self, packet):
        service, version, entity = packet['service'], packet['version'], packet['entity']
        node = self._registry_client.resolve(service, version, entity, TCP)
        return node[2] if node else None

    def handle_ping_timeout(self, node_id):
        self._logger.info("Service client connection timed out {}".format(node_id))
        self._pingers.pop(node_id, None)
        service_props = self._registry_client.get_for_node(node_id)
        self._logger.info('service client props {}'.format(service_props))
        if service_props is not None:
            host, port, _node_id, _type = service_props
            asyncio.async(self._connect_to_client(host, _node_id, port, _type))

    def receive(self, packet: dict, protocol, transport):
        if packet['type'] == 'ping':
            self._handle_ping(packet, protocol)
        elif packet['type'] == 'pong':
            self._handle_pong(packet['node_id'], packet['count'])
        elif packet['type'] == 'publish':
            self._handle_publish(packet, protocol)
        else:
            if self.tcp_host.is_for_me(packet['service'], packet['version']):
                func = getattr(self, '_' + packet['type'] + '_receiver')
                func(packet, protocol)
            else:
                self._logger.warn('wrongly routed packet: ', packet)

    def _request_receiver(self, packet, protocol):
        api_fn = getattr(self.tcp_host, packet['endpoint'])
        if api_fn.is_api:
            from_node_id = packet['from']
            entity = packet['entity']
            future = asyncio.async(api_fn(from_id=from_node_id, entity=entity, **packet['payload']))

            def send_result(f):
                result_packet = f.result()
                protocol.send(result_packet)

            future.add_done_callback(send_result)
        else:
            print('no api found for packet: ', packet)

    def _handle_publish(self, packet, protocol):
        service, version, endpoint, payload, publish_id = (packet['service'], packet['version'], packet['endpoint'],
                                                           packet['payload'], packet['publish_id'])
        for client in self._service_clients:
            if client.name == service and client.version == version:
                fun = getattr(client, endpoint)
                asyncio.async(fun(payload))
        protocol.send(MessagePacket.ack(publish_id))

    def handle_connected(self):
        if self.tcp_host:
            self._registry_client.register(self.tcp_host.host, self.tcp_host.port, self.tcp_host.name,
                                           self.tcp_host.version, self.tcp_host.node_id, self.tcp_host.clients, 'tcp')
        if self.http_host:
            self._registry_client.register(self.http_host.host, self.http_host.port, self.http_host.name,
                                           self.http_host.version, self.http_host.node_id, self.http_host.clients,
                                           'http')

class PubSubBus:
    PUBSUB_DELAY = 5

    def __init__(self, registry_client, ssl_context=None):
        self._pubsub_handler = None
        self._registry_client = registry_client
        self._clients = None
        self._pending_publishes = {}
        self._ssl_context = ssl_context

    def create_pubsub_handler(self, host, port):
        self._pubsub_handler = PubSub(host, port)
        yield from self._pubsub_handler.connect()

    def register_for_subscription(self, host, port, node_id, clients):
        self._clients = clients
        subscription_list = []
        xsubscription_list = []
        for client in clients:
            if isinstance(client, TCPServiceClient):
                for each in dir(client):
                    fn = getattr(client, each)
                    if callable(fn) and getattr(fn, 'is_subscribe', False):
                        subscription_list.append(self._get_pubsub_key(client.name, client.version, fn.__name__))
                    elif callable(fn) and getattr(fn, 'is_xsubscribe', False):
                        xsubscription_list.append((client.name, client.version, fn.__name__, getattr(fn, 'strategy')))
        self._registry_client.x_subscribe(host, port, node_id, xsubscription_list)
        yield from self._pubsub_handler.subscribe(subscription_list, handler=self.subscription_handler)

    def publish(self, service, version, endpoint, payload):
        endpoint_key = self._get_pubsub_key(service, version, endpoint)
        asyncio.async(self._retry_publish(endpoint_key, json.dumps(payload, cls=VykedEncoder)))
        publish_id = str(uuid.uuid4())
        future = asyncio.async(self.xpublish(publish_id, service, version, endpoint, payload))
        self._pending_publishes[publish_id] = future

    def xpublish(self, publish_id, service, version, endpoint, payload):
        subscribers = yield from self._registry_client.get_subscribers(service, version, endpoint)
        strategies = defaultdict(list)
        for subscriber in subscribers:
            strategies[(subscriber['service'], subscriber['version'])].append(
                (subscriber['host'], subscriber['port'], subscriber['node_id'], subscriber['strategy']))
        if not len(subscribers):
            future = self._pending_publishes[publish_id]
            future.cancel()
            return
        yield from self._connect_and_publish(publish_id, service, version, endpoint, strategies, payload)
        yield from asyncio.sleep(self.PUBSUB_DELAY)
        yield from self.xpublish(publish_id, service, version, endpoint, payload)

    def receive(self, packet, transport, protocol):
        if packet['type'] == 'ack':
            future = self._pending_publishes.pop(packet['request_id'])
            future.cancel()
            transport.close()

    def _retry_publish(self, endpoint, payload):
        return (yield from self._pubsub_handler.publish(endpoint, payload))

    def subscription_handler(self, endpoint, payload):
        service, version, endpoint = endpoint.split('/')
        client = [sc for sc in self._clients if (sc.name == service and sc.version == version)][0]
        func = getattr(client, endpoint)
        asyncio.async(func(**json.loads(payload)))

    @staticmethod
    def _get_pubsub_key(service, version, endpoint):
        return '/'.join((service, str(version), endpoint))

    def _connect_and_publish(self, publish_id, service, version, endpoint, strategies, payload):
        for key, value in strategies.items():
            if value[0][3] == 'LEADER':
                host, port = value[0][0], value[0][1]
            else:
                random_metadata = random.choice(value)
                host, port = random_metadata[0], random_metadata[1]
            transport, protocol = yield from asyncio.get_event_loop().create_connection(
                partial(get_vyked_protocol, self), host, port)
            packet = MessagePacket.publish(publish_id, service, version, endpoint, payload)
            protocol.send(packet)
<commit_msg>Fix race condition failure while publishing to multiple subscribers<commit_after>import asyncio
from collections import defaultdict
from functools import partial
import json
import logging
import random
import uuid

from again.utils import unique_hex
import aiohttp

from .services import TCPServiceClient, HTTPServiceClient
from .pubsub import PubSub
from .packet import ControlPacket, MessagePacket
from .protocol_factory import get_vyked_protocol
from .utils.jsonencoder import VykedEncoder
from .exceptions import ClientNotFoundError, ClientDisconnected

HTTP = 'http'
TCP = 'tcp'

def _retry_for_pub(result):
    return not result

def _retry_for_exception(_):
    return True

class HTTPBus:
    def __init__(self, registry_client):
        self._registry_client = registry_client

    def send_http_request(self, app: str, service: str, version: str, method: str, entity: str, params: dict):
        """
        A convenience method that allows you to send a well formatted http request to another service
        """
        host, port, node_id, service_type = self._registry_client.resolve(service, version, entity, HTTP)

        url = 'http://{}:{}{}'.format(host, port, params.pop('path'))

        http_keys = ['data', 'headers', 'cookies', 'auth', 'allow_redirects', 'compress', 'chunked']
        kwargs = {k: params[k] for k in http_keys if k in params}

        query_params = params.pop('params', {})

        if app is not None:
            query_params['app'] = app

        query_params['version'] = version
        query_params['service'] = service

        response = yield from aiohttp.request(method, url, params=query_params, **kwargs)
        return response

class TCPBus:
    def __init__(self, registry_client):
        registry_client.conn_handler = self
        self._registry_client = registry_client
        self._client_protocols = {}
        self._pingers = {}
        self._node_clients = {}
        self._service_clients = []
        self.tcp_host = None
        self.http_host = None
        self._host_id = unique_hex()
        self._ronin = False
        self._registered = False
        self._logger = logging.getLogger(__name__)

    def _create_service_clients(self):
        futures = []
        for sc in self._service_clients:
            for host, port, node_id, service_type in self._registry_client.get_all_addresses(*sc.properties):
                if service_type == 'tcp':
                    self._node_clients[node_id] = sc
                    future = self._connect_to_client(host, node_id, port, service_type, sc)
                    futures.append(future)
        return asyncio.gather(*futures, return_exceptions=False)

    def register(self):
        clients = self.tcp_host.clients if self.tcp_host else self.http_host.clients
        for client in clients:
            if isinstance(client, (TCPServiceClient, HTTPServiceClient)):
                client.bus = self
        self._service_clients = clients
        asyncio.get_event_loop().run_until_complete(self._registry_client.connect())

    def registration_complete(self):
        if not self._registered:
            self._create_service_clients()
            self._registered = True

    def new_instance(self, service, version, host, port, node_id, type):
        sc = next(sc for sc in self._service_clients if sc.name == service and sc.version == version)
        if type == 'tcp':
            self._node_clients[node_id] = sc
            asyncio.async(self._connect_to_client(host, node_id, port, type, sc))

    def send(self, packet: dict):
        packet['from'] = self._host_id
        func = getattr(self, '_' + packet['type'] + '_sender')
        func(packet)

    def _request_sender(self, packet: dict):
        """
        Sends a request to a server from a ServiceClient
        auto dispatch method called from self.send()
        """
        node_id = self._get_node_id_for_packet(packet)
        client_protocol = self._client_protocols.get(node_id)

        if node_id and client_protocol:
            if client_protocol.is_connected():
                packet['to'] = node_id
                client_protocol.send(packet)
            else:
                self._logger.error('Client protocol is not connected for packet %s', packet)
                raise ClientDisconnected()
        else:
            # No node found to send request
            self._logger.error('Out of %s, Client Not found for packet %s', self._client_protocols.keys(), packet)
            raise ClientNotFoundError()

    def _connect_to_client(self, host, node_id, port, service_type, service_client):

        future = asyncio.async(
            asyncio.get_event_loop().create_connection(partial(get_vyked_protocol, service_client), host, port,
                                                       ssl=service_client._ssl_context))
        future.add_done_callback(
            partial(self._service_client_connection_callback, self._node_clients[node_id], node_id, service_type))
        return future

    def _service_client_connection_callback(self, sc, node_id, service_type, future):
        _, protocol = future.result()
        # TODO : handle pinging
        # if service_type == TCP:
        #     pinger = Pinger(self, asyncio.get_event_loop())
        #     self._pingers[node_id] = pinger
        #     pinger.register_tcp_service(protocol, node_id)
        #     asyncio.async(pinger.start_ping())
        self._client_protocols[node_id] = protocol

    @staticmethod
    def _create_json_service_name(app, service, version):
        return {'app': app, 'service': service, 'version': version}

    @staticmethod
    def _handle_ping(packet, protocol):
        protocol.send(ControlPacket.pong(packet['node_id']))

    def _handle_pong(self, node_id, count):
        pinger = self._pingers[node_id]
        asyncio.async(pinger.pong_received(count))

    def _get_node_id_for_packet(self, packet):
        service, version, entity = packet['service'], packet['version'], packet['entity']
        node = self._registry_client.resolve(service, version, entity, TCP)
        return node[2] if node else None

    def handle_ping_timeout(self, node_id):
        self._logger.info("Service client connection timed out {}".format(node_id))
        self._pingers.pop(node_id, None)
        service_props = self._registry_client.get_for_node(node_id)
        self._logger.info('service client props {}'.format(service_props))
        if service_props is not None:
            host, port, _node_id, _type = service_props
            asyncio.async(self._connect_to_client(host, _node_id, port, _type))

    def receive(self, packet: dict, protocol, transport):
        if packet['type'] == 'ping':
            self._handle_ping(packet, protocol)
        elif packet['type'] == 'pong':
            self._handle_pong(packet['node_id'], packet['count'])
        elif packet['type'] == 'publish':
            self._handle_publish(packet, protocol)
        else:
            if self.tcp_host.is_for_me(packet['service'], packet['version']):
                func = getattr(self, '_' + packet['type'] + '_receiver')
                func(packet, protocol)
            else:
                self._logger.warn('wrongly routed packet: ', packet)

    def _request_receiver(self, packet, protocol):
        api_fn = getattr(self.tcp_host, packet['endpoint'])
        if api_fn.is_api:
            from_node_id = packet['from']
            entity = packet['entity']
            future = asyncio.async(api_fn(from_id=from_node_id, entity=entity, **packet['payload']))

            def send_result(f):
                result_packet = f.result()
                protocol.send(result_packet)

            future.add_done_callback(send_result)
        else:
            print('no api found for packet: ', packet)

    def _handle_publish(self, packet, protocol):
        service, version, endpoint, payload, publish_id = (packet['service'], packet['version'], packet['endpoint'],
                                                           packet['payload'], packet['publish_id'])
        for client in self._service_clients:
            if client.name == service and client.version == version:
                fun = getattr(client, endpoint)
                asyncio.async(fun(payload))
        protocol.send(MessagePacket.ack(publish_id))

    def handle_connected(self):
        if self.tcp_host:
            self._registry_client.register(self.tcp_host.host, self.tcp_host.port, self.tcp_host.name,
                                           self.tcp_host.version, self.tcp_host.node_id, self.tcp_host.clients, 'tcp')
        if self.http_host:
            self._registry_client.register(self.http_host.host, self.http_host.port, self.http_host.name,
                                           self.http_host.version, self.http_host.node_id, self.http_host.clients,
                                           'http')

class PubSubBus:
    PUBSUB_DELAY = 5

    def __init__(self, registry_client, ssl_context=None):
        self._pubsub_handler = None
        self._registry_client = registry_client
        self._clients = None
        self._pending_publishes = {}
        self._ssl_context = ssl_context

    def create_pubsub_handler(self, host, port):
        self._pubsub_handler = PubSub(host, port)
        yield from self._pubsub_handler.connect()

    def register_for_subscription(self, host, port, node_id, clients):
        self._clients = clients
        subscription_list = []
        xsubscription_list = []
        for client in clients:
            if isinstance(client, TCPServiceClient):
                for each in dir(client):
                    fn = getattr(client, each)
                    if callable(fn) and getattr(fn, 'is_subscribe', False):
                        subscription_list.append(self._get_pubsub_key(client.name, client.version, fn.__name__))
                    elif callable(fn) and getattr(fn, 'is_xsubscribe', False):
                        xsubscription_list.append((client.name, client.version, fn.__name__, getattr(fn, 'strategy')))
        self._registry_client.x_subscribe(host, port, node_id, xsubscription_list)
        yield from self._pubsub_handler.subscribe(subscription_list, handler=self.subscription_handler)

    def publish(self, service, version, endpoint, payload):
        endpoint_key = self._get_pubsub_key(service, version, endpoint)
        asyncio.async(self._pubsub_handler.publish(endpoint_key, json.dumps(payload, cls=VykedEncoder)))
        publish_id = str(uuid.uuid4())
        asyncio.async(self.xpublish(publish_id, service, version, endpoint, payload))

    def xpublish(self, service, version, endpoint, payload):
        subscribers = yield from self._registry_client.get_subscribers(service, version, endpoint)
        strategies = defaultdict(list)
        for subscriber in subscribers:
            strategies[(subscriber['service'], subscriber['version'])].append(
                (subscriber['host'], subscriber['port'], subscriber['node_id'], subscriber['strategy']))
        for key, value in strategies.items():
            publish_id = str(uuid.uuid4())
            future = asyncio.async(
                self._connect_and_publish(publish_id, service, version, endpoint, value, payload))
            self._pending_publishes[publish_id] = future

    def receive(self, packet, transport, protocol):
        if packet['type'] == 'ack':
            future = self._pending_publishes.pop(packet['request_id'], None)
            if future:
                future.cancel()
                transport.close()

    def subscription_handler(self, endpoint, payload):
        service, version, endpoint = endpoint.split('/')
        client = [sc for sc in self._clients if (sc.name == service and sc.version == version)][0]
        func = getattr(client, endpoint)
        asyncio.async(func(**json.loads(payload)))

    @staticmethod
    def _get_pubsub_key(service, version, endpoint):
        return '/'.join((service, str(version), endpoint))

    def _connect_and_publish(self, publish_id, service, version, endpoint, subscribers, payload):
        if subscribers[0][3] == 'LEADER':
            host, port = subscribers[0][0], subscribers[0][1]
        else:
            random_metadata = random.choice(subscribers)
            host, port = random_metadata[0], random_metadata[1]
        transport, protocol = yield from asyncio.get_event_loop().create_connection(
            partial(get_vyked_protocol, self), host, port)
        packet = MessagePacket.publish(publish_id, service, version, endpoint, payload)
        protocol.send(packet)
        yield from asyncio.sleep(self.PUBSUB_DELAY)
<|endoftext|>


<commit_before>#!/bin/sh
"""": # -*-python-*-
bup_python="$(dirname "$0")/bup-python" || exit $?
exec "$bup_python" "$0" ${1+"$@"}
"""
# end of bup preamble

from collections import namedtuple
import mimetypes, os, posixpath, signal, stat, sys, time, urllib, webbrowser

from bup import options, git, vfs
from bup.helpers import (debug1, handle_ctrl_c, log, resource_path,
                         saved_errors)

try:
    from tornado.httpserver import HTTPServer
    from tornado.ioloop import IOLoop
    from tornado.netutil import bind_unix_socket
    import tornado.web
except ImportError:
    log('error: cannot find the python "tornado" module; please install it\n')
    sys.exit(1)

handle_ctrl_c()

def _compute_breadcrumbs(path, show_hidden=False):
    """Returns a list of breadcrumb objects for a path."""
    breadcrumbs = []
    breadcrumbs.append(('[root]', '/'))
    path_parts = path.split('/')[1:-1]
    full_path = '/'
    for part in path_parts:
        full_path += part + "/"
        url_append = ""
        if show_hidden:
            url_append = '?hidden=1'
        breadcrumbs.append((part, full_path+url_append))
    return breadcrumbs

def _contains_hidden_files(n):
    """Return True if n contains files starting with a '.', False otherwise."""
    for sub in n:
        name = sub.name
        if len(name)>1 and name.startswith('.'):
            return True

    return False

def _compute_dir_contents(n, path, show_hidden=False):
    """Given a vfs node, returns an iterator for display info of all subs."""
    url_append = ""
    if show_hidden:
        url_append = "?hidden=1"

    if path != "/":
        yield('..', '../' + url_append, '')
    for sub in n:
        display = sub.name
        link = urllib.quote(sub.name)

        # link should be based on fully resolved type to avoid extra
        # HTTP redirect.
        if stat.S_ISDIR(sub.try_resolve().mode):
            link += "/"

        if not show_hidden and len(display)>1 and display.startswith('.'):
            continue

        size = None
        if stat.S_ISDIR(sub.mode):
            display += '/'
        elif stat.S_ISLNK(sub.mode):
            display += '@'
        else:
            size = sub.size()
            size = (opt.human_readable and format_filesize(size)) or size

        yield (display, link + url_append, size)

class BupRequestHandler(tornado.web.RequestHandler):
    def get(self, path):
        return self._process_request(path)

    def head(self, path):
        return self._process_request(path)

    @tornado.web.asynchronous
    def _process_request(self, path):
        path = urllib.unquote(path)
        print 'Handling request for %s' % path
        try:
            n = top.resolve(path)
        except vfs.NoSuchFile:
            self.send_error(404)
            return
        f = None
        if stat.S_ISDIR(n.mode):
            self._list_directory(path, n)
        else:
            self._get_file(path, n)

    def _list_directory(self, path, n):
        """Helper to produce a directory listing.

        Return value is either a file object, or None (indicating an
        error).  In either case, the headers are sent.
        """
        if not path.endswith('/') and len(path) > 0:
            print 'Redirecting from %s to %s' % (path, path + '/')
            return self.redirect(path + '/', permanent=True)

        try:
            show_hidden = int(self.request.arguments.get('hidden', [0])[-1])
        except ValueError as e:
            show_hidden = False

        self.render(
            'list-directory.html',
            path=path,
            breadcrumbs=_compute_breadcrumbs(path, show_hidden),
            files_hidden=_contains_hidden_files(n),
            hidden_shown=show_hidden,
            dir_contents=_compute_dir_contents(n, path, show_hidden))

    def _get_file(self, path, n):
        """Process a request on a file.

        Return value is either a file object, or None (indicating an error).
        In either case, the headers are sent.
        """
        ctype = self._guess_type(path)

        self.set_header("Last-Modified", self.date_time_string(n.mtime))
        self.set_header("Content-Type", ctype)
        size = n.size()
        self.set_header("Content-Length", str(size))
        assert(len(n.hash) == 20)
        self.set_header("Etag", n.hash.encode('hex'))

        if self.request.method != 'HEAD':
            self.flush()
            f = n.open()
            it = chunkyreader(f)
            def write_more(me):
                try:
                    blob = it.next()
                except StopIteration:
                    f.close()
                    self.finish()
                    return
                self.request.connection.stream.write(blob,
                                                     callback=lambda: me(me))
            write_more(write_more)
        else:
            self.finish()

    def _guess_type(self, path):
        """Guess the type of a file.

        Argument is a PATH (a filename).

        Return value is a string of the form type/subtype,
        usable for a MIME Content-type header.

        The default implementation looks the file's extension
        up in the table self.extensions_map, using application/octet-stream
        as a default; however it would be permissible (if
        slow) to look inside the data to make a better guess.
        """
        base, ext = posixpath.splitext(path)
        if ext in self.extensions_map:
            return self.extensions_map[ext]
        ext = ext.lower()
        if ext in self.extensions_map:
            return self.extensions_map[ext]
        else:
            return self.extensions_map['']

    if not mimetypes.inited:
        mimetypes.init() # try to read system mime.types
    extensions_map = mimetypes.types_map.copy()
    extensions_map.update({
        '': 'text/plain', # Default
        '.py': 'text/plain',
        '.c': 'text/plain',
        '.h': 'text/plain',
        })

    def date_time_string(self, t):
        return time.strftime('%a, %d %b %Y %H:%M:%S', time.gmtime(t))

io_loop = None

def handle_sigterm(signum, frame):
    global io_loop
    debug1('\nbup-web: signal %d received\n' % signum)
    log('Shutdown requested\n')
    if not io_loop:
        sys.exit(0)
    io_loop.stop()

signal.signal(signal.SIGTERM, handle_sigterm)

UnixAddress = namedtuple('UnixAddress', ['path'])
InetAddress = namedtuple('InetAddress', ['host', 'port'])

optspec = """
bup web [[hostname]:port]
bup web unix://path
--
human-readable    display human readable file sizes (i.e. 3.9K, 4.7M)
browser           show repository in default browser (incompatible with unix://)
"""
o = options.Options(optspec)
(opt, flags, extra) = o.parse(sys.argv[1:])

if len(extra) > 1:
    o.fatal("at most one argument expected")

if len(extra) == 0:
    address = InetAddress(host='127.0.0.1', port=8080)
else:
    bind_url = extra[0]
    if bind_url.startswith('unix://'):
        address = UnixAddress(path=bind_url[len('unix://'):])
    else:
        addr_parts = extra[0].split(':', 1)
        if len(addr_parts) == 1:
            host = '127.0.0.1'
            port = addr_parts[0]
        else:
            host, port = addr_parts
        try:
            port = int(port)
        except (TypeError, ValueError) as ex:
            o.fatal('port must be an integer, not %r', port)
        address = InetAddress(host=host, port=port)

git.check_repo_or_die()
top = vfs.RefList(None)

settings = dict(
    debug = 1,
    template_path = resource_path('web'),
    static_path = resource_path('web/static')
)

# Disable buffering on stdout, for debug messages
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

application = tornado.web.Application([
    (r"(/.*)", BupRequestHandler),
], **settings)

http_server = HTTPServer(application)
io_loop_pending = IOLoop.instance()

if isinstance(address, InetAddress):
    http_server.listen(address.port, address.host)
    try:
        sock = http_server._socket # tornado < 2.0
    except AttributeError as e:
        sock = http_server._sockets.values()[0]
    print "Serving HTTP on %s:%d..." % sock.getsockname()
    if opt.browser:
        browser_addr = 'http://' + address[0] + ':' + str(address[1])
        io_loop_pending.add_callback(lambda : webbrowser.open(browser_addr))
elif isinstance(address, UnixAddress):
    unix_socket = bind_unix_socket(address.path)
    http_server.add_socket(unix_socket)
    print "Serving HTTP on filesystem socket %r" % address.path
else:
    log('error: unexpected address %r', address)
    sys.exit(1)

io_loop = io_loop_pending
io_loop.start()

if saved_errors:
    log('WARNING: %d errors encountered while saving.\n' % len(saved_errors))
    sys.exit(1)
<commit_msg>web: add missing chunkyreader import<commit_after>#!/bin/sh
"""": # -*-python-*-
bup_python="$(dirname "$0")/bup-python" || exit $?
exec "$bup_python" "$0" ${1+"$@"}
"""
# end of bup preamble

from collections import namedtuple
import mimetypes, os, posixpath, signal, stat, sys, time, urllib, webbrowser

from bup import options, git, vfs
from bup.helpers import (chunkyreader, debug1, handle_ctrl_c, log,
                         resource_path, saved_errors)

try:
    from tornado.httpserver import HTTPServer
    from tornado.ioloop import IOLoop
    from tornado.netutil import bind_unix_socket
    import tornado.web
except ImportError:
    log('error: cannot find the python "tornado" module; please install it\n')
    sys.exit(1)

handle_ctrl_c()

def _compute_breadcrumbs(path, show_hidden=False):
    """Returns a list of breadcrumb objects for a path."""
    breadcrumbs = []
    breadcrumbs.append(('[root]', '/'))
    path_parts = path.split('/')[1:-1]
    full_path = '/'
    for part in path_parts:
        full_path += part + "/"
        url_append = ""
        if show_hidden:
            url_append = '?hidden=1'
        breadcrumbs.append((part, full_path+url_append))
    return breadcrumbs

def _contains_hidden_files(n):
    """Return True if n contains files starting with a '.', False otherwise."""
    for sub in n:
        name = sub.name
        if len(name)>1 and name.startswith('.'):
            return True

    return False

def _compute_dir_contents(n, path, show_hidden=False):
    """Given a vfs node, returns an iterator for display info of all subs."""
    url_append = ""
    if show_hidden:
        url_append = "?hidden=1"

    if path != "/":
        yield('..', '../' + url_append, '')
    for sub in n:
        display = sub.name
        link = urllib.quote(sub.name)

        # link should be based on fully resolved type to avoid extra
        # HTTP redirect.
        if stat.S_ISDIR(sub.try_resolve().mode):
            link += "/"

        if not show_hidden and len(display)>1 and display.startswith('.'):
            continue

        size = None
        if stat.S_ISDIR(sub.mode):
            display += '/'
        elif stat.S_ISLNK(sub.mode):
            display += '@'
        else:
            size = sub.size()
            size = (opt.human_readable and format_filesize(size)) or size

        yield (display, link + url_append, size)

class BupRequestHandler(tornado.web.RequestHandler):
    def get(self, path):
        return self._process_request(path)

    def head(self, path):
        return self._process_request(path)

    @tornado.web.asynchronous
    def _process_request(self, path):
        path = urllib.unquote(path)
        print 'Handling request for %s' % path
        try:
            n = top.resolve(path)
        except vfs.NoSuchFile:
            self.send_error(404)
            return
        f = None
        if stat.S_ISDIR(n.mode):
            self._list_directory(path, n)
        else:
            self._get_file(path, n)

    def _list_directory(self, path, n):
        """Helper to produce a directory listing.

        Return value is either a file object, or None (indicating an
        error).  In either case, the headers are sent.
        """
        if not path.endswith('/') and len(path) > 0:
            print 'Redirecting from %s to %s' % (path, path + '/')
            return self.redirect(path + '/', permanent=True)

        try:
            show_hidden = int(self.request.arguments.get('hidden', [0])[-1])
        except ValueError as e:
            show_hidden = False

        self.render(
            'list-directory.html',
            path=path,
            breadcrumbs=_compute_breadcrumbs(path, show_hidden),
            files_hidden=_contains_hidden_files(n),
            hidden_shown=show_hidden,
            dir_contents=_compute_dir_contents(n, path, show_hidden))

    def _get_file(self, path, n):
        """Process a request on a file.

        Return value is either a file object, or None (indicating an error).
        In either case, the headers are sent.
        """
        ctype = self._guess_type(path)

        self.set_header("Last-Modified", self.date_time_string(n.mtime))
        self.set_header("Content-Type", ctype)
        size = n.size()
        self.set_header("Content-Length", str(size))
        assert(len(n.hash) == 20)
        self.set_header("Etag", n.hash.encode('hex'))

        if self.request.method != 'HEAD':
            self.flush()
            f = n.open()
            it = chunkyreader(f)
            def write_more(me):
                try:
                    blob = it.next()
                except StopIteration:
                    f.close()
                    self.finish()
                    return
                self.request.connection.stream.write(blob,
                                                     callback=lambda: me(me))
            write_more(write_more)
        else:
            self.finish()

    def _guess_type(self, path):
        """Guess the type of a file.

        Argument is a PATH (a filename).

        Return value is a string of the form type/subtype,
        usable for a MIME Content-type header.

        The default implementation looks the file's extension
        up in the table self.extensions_map, using application/octet-stream
        as a default; however it would be permissible (if
        slow) to look inside the data to make a better guess.
        """
        base, ext = posixpath.splitext(path)
        if ext in self.extensions_map:
            return self.extensions_map[ext]
        ext = ext.lower()
        if ext in self.extensions_map:
            return self.extensions_map[ext]
        else:
            return self.extensions_map['']

    if not mimetypes.inited:
        mimetypes.init() # try to read system mime.types
    extensions_map = mimetypes.types_map.copy()
    extensions_map.update({
        '': 'text/plain', # Default
        '.py': 'text/plain',
        '.c': 'text/plain',
        '.h': 'text/plain',
        })

    def date_time_string(self, t):
        return time.strftime('%a, %d %b %Y %H:%M:%S', time.gmtime(t))

io_loop = None

def handle_sigterm(signum, frame):
    global io_loop
    debug1('\nbup-web: signal %d received\n' % signum)
    log('Shutdown requested\n')
    if not io_loop:
        sys.exit(0)
    io_loop.stop()

signal.signal(signal.SIGTERM, handle_sigterm)

UnixAddress = namedtuple('UnixAddress', ['path'])
InetAddress = namedtuple('InetAddress', ['host', 'port'])

optspec = """
bup web [[hostname]:port]
bup web unix://path
--
human-readable    display human readable file sizes (i.e. 3.9K, 4.7M)
browser           show repository in default browser (incompatible with unix://)
"""
o = options.Options(optspec)
(opt, flags, extra) = o.parse(sys.argv[1:])

if len(extra) > 1:
    o.fatal("at most one argument expected")

if len(extra) == 0:
    address = InetAddress(host='127.0.0.1', port=8080)
else:
    bind_url = extra[0]
    if bind_url.startswith('unix://'):
        address = UnixAddress(path=bind_url[len('unix://'):])
    else:
        addr_parts = extra[0].split(':', 1)
        if len(addr_parts) == 1:
            host = '127.0.0.1'
            port = addr_parts[0]
        else:
            host, port = addr_parts
        try:
            port = int(port)
        except (TypeError, ValueError) as ex:
            o.fatal('port must be an integer, not %r', port)
        address = InetAddress(host=host, port=port)

git.check_repo_or_die()
top = vfs.RefList(None)

settings = dict(
    debug = 1,
    template_path = resource_path('web'),
    static_path = resource_path('web/static')
)

# Disable buffering on stdout, for debug messages
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

application = tornado.web.Application([
    (r"(/.*)", BupRequestHandler),
], **settings)

http_server = HTTPServer(application)
io_loop_pending = IOLoop.instance()

if isinstance(address, InetAddress):
    http_server.listen(address.port, address.host)
    try:
        sock = http_server._socket # tornado < 2.0
    except AttributeError as e:
        sock = http_server._sockets.values()[0]
    print "Serving HTTP on %s:%d..." % sock.getsockname()
    if opt.browser:
        browser_addr = 'http://' + address[0] + ':' + str(address[1])
        io_loop_pending.add_callback(lambda : webbrowser.open(browser_addr))
elif isinstance(address, UnixAddress):
    unix_socket = bind_unix_socket(address.path)
    http_server.add_socket(unix_socket)
    print "Serving HTTP on filesystem socket %r" % address.path
else:
    log('error: unexpected address %r', address)
    sys.exit(1)

io_loop = io_loop_pending
io_loop.start()

if saved_errors:
    log('WARNING: %d errors encountered while saving.\n' % len(saved_errors))
    sys.exit(1)
<|endoftext|>


<commit_before># ----------------------------------------------------------------------
# Numenta Platform for Intelligent Computing (NuPIC)
# Copyright (C) 2014, Numenta, Inc.  Unless you have an agreement
# with Numenta, Inc., for a separate license for this software code, the
# following terms and conditions apply:
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see http://www.gnu.org/licenses.
#
# http://numenta.org/licenses/
# ----------------------------------------------------------------------

"""
utils.py are a collection of methods that can be reused by different classes
in our codebase.
"""

import numpy
import numbers

class MovingAverage(object):
  """Helper class for computing moving average and sliding window"""

  def __init__(self, windowSize, existingHistoricalValues=None):
    """
    new instance of MovingAverage, so method .next() can be used
    @param windowSize - length of sliding window
    @param existingHistoricalValues - construct the object with already
    	some values in it.
    """
    if not isinstance(windowSize, numbers.Integral):
      raise TypeError("MovingAverage - windowSize must be integer type")
    if  windowSize <= 0:
      raise ValueError("MovingAverage - windowSize must be >0")

    self.windowSize = windowSize
    if existingHistoricalValues is not None:
      self.slidingWindow = existingHistoricalValues[
                              len(existingHistoricalValues)-windowSize:]
    else:
      self.slidingWindow = []
    self.slidingWindow = numpy.array(self.slidingWindow)
    self.total = sum(self.slidingWindow)

  @staticmethod
  def compute(historicalValues, total, newVal, windowSize):
    """
    Routine for computing a moving average. Given
    @param historicalValues - a list of historical numbers
    @param total -  a running total of those values
    @param newVal - a new number compute the new windowed average
    @param windowSize - length of sliding window used

    @returns an updated windowed average, the new list of ``historicalValues``,
        and the new running total. Ensures the list of ``historicalValues`` is
        at most ``windowSize``.
    """
    while len(historicalValues) >= windowSize:
      total -= historicalValues[0]
      historicalValues = numpy.delete(historicalValues, 0)
    historicalValues = numpy.append(historicalValues, [newVal])
    total += newVal
    newAverage = numpy.average(historicalValues)
    return newAverage, historicalValues, total

  def next(self, newValue):
    """
    update moving average with the new value added
    @param newValue - integer value to be added
    @return an updated windowed average, the new list of ``historicalValues``,
        and the new running total. Ensures the list of ``historicalValues`` is
        at most ``windowSize``.
    """
    newAverage, self.slidingWindow, self.total = (self.compute(
							self.slidingWindow,
							self.total,
							newValue,
							self.windowSize) )
    return newAverage, self.slidingWindow, self.total

  def getSlidingWindow(self):
    return self.slidingWindow
<commit_msg>MovingAverage speedup - use deque as a buffer<commit_after># ----------------------------------------------------------------------
# Numenta Platform for Intelligent Computing (NuPIC)
# Copyright (C) 2014, Numenta, Inc.  Unless you have an agreement
# with Numenta, Inc., for a separate license for this software code, the
# following terms and conditions apply:
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see http://www.gnu.org/licenses.
#
# http://numenta.org/licenses/
# ----------------------------------------------------------------------

"""
utils.py are a collection of methods that can be reused by different classes
in our codebase.
"""

import numpy
import numbers
from collections import deque

class MovingAverage(object):
  """Helper class for computing moving average and sliding window"""

  def __init__(self, windowSize, existingHistoricalValues=None):
    """
    new instance of MovingAverage, so method .next() can be used
    @param windowSize - length of sliding window
    @param existingHistoricalValues - construct the object with already
    	some values in it.
    """
    if not isinstance(windowSize, numbers.Integral):
      raise TypeError("MovingAverage - windowSize must be integer type")
    if  windowSize <= 0:
      raise ValueError("MovingAverage - windowSize must be >0")

    self.windowSize = windowSize
    if existingHistoricalValues is not None:
      self.slidingWindow = existingHistoricalValues[
                              len(existingHistoricalValues)-windowSize:]
    else:
      self.slidingWindow = []
    self.slidingWindow = deque(self.slidingWindow)
    self.total = sum(self.slidingWindow)

  @staticmethod
  def compute(historicalValues, total, newVal, windowSize):
    """
    Routine for computing a moving average. Given
    @param historicalValues - a list of historical numbers
    @param total -  a running total of those values
    @param newVal - a new number compute the new windowed average
    @param windowSize - length of sliding window used

    @returns an updated windowed average, the new list of ``historicalValues``,
        and the new running total. Ensures the list of ``historicalValues`` is
        at most ``windowSize``.
    """
    while len(historicalValues) >= windowSize:
      total -= historicalValues[0]
      historicalValues = numpy.delete(historicalValues, 0)
    historicalValues = numpy.append(historicalValues, [newVal])
    total += newVal
    newAverage = numpy.average(historicalValues)
    return newAverage, historicalValues, total

  def next(self, newValue):
    """
    update moving average with the new value added
    @param newValue - integer value to be added
    @return an updated windowed average, the new list of ``historicalValues``,
        and the new running total. Ensures the list of ``historicalValues`` is
        at most ``windowSize``.
    """
    newAverage, self.slidingWindow, self.total = (self.compute(
							self.slidingWindow,
							self.total,
							newValue,
							self.windowSize) )
    return newAverage, self.slidingWindow, self.total

  def getSlidingWindow(self):
    return self.slidingWindow
<|endoftext|>


<commit_before>from flask import Flask, render_template
from datetime import datetime
from flask import request

import time
import json

import sqlite3

app = Flask(__name__)

@app.route('/', methods=['GET'])
def index():
    conn = sqlite3.connect('sql/TweetInfo.db')
    tableNamesList = conn.execute("select name from sqlite_master where type = 'table'")
    return render_template("index.html", tableNamesList=tableNamesList)

@app.route('/results', methods=['POST','GET'])
def results():
    # Dispaly results
    try:
        # Connect to database
        conn = sqlite3.connect('sql/TweetInfo.db')

        # Get the chosen table
        table_choice = request.form.get('table_choice')

        # Select chosen table
        cursor = conn.execute("SELECT * FROM " + str(table_choice[2:-3]))

        tweetsList = []
        index = 0
        for row in cursor:
            tweetsList.append([row[1]*1000, 100*row[3]])
            index += 1

        # Must sort becuase tweets can come out of order. FIXME Fix this at an earlier
        # part
        tweetsList.sort()
        #tweetsList.reverse()

        return render_template("results.html", tweetsList=tweetsList,\
                table_choice=table_choice[2:-3])
    except:
        return render_template("error.html")

# Starts up the webserver
if __name__ == "__main__":
    app.run()
<commit_msg>Added tweet text to be passed into highcharts<commit_after>from flask import Flask, render_template
from datetime import datetime
from flask import request

import time
import json

import sqlite3

app = Flask(__name__)

@app.route('/', methods=['GET'])
def index():
    conn = sqlite3.connect('sql/TweetInfo.db')
    tableNamesList = conn.execute("select name from sqlite_master where type = 'table'")
    return render_template("index.html", tableNamesList=tableNamesList)

@app.route('/results', methods=['POST','GET'])
def results():
    # Dispaly results
    try:
        # Connect to database
        conn = sqlite3.connect('sql/TweetInfo.db')

        # Get the chosen table
        table_choice = request.form.get('table_choice')

        # Select chosen table
        cursor = conn.execute("SELECT * FROM " + str(table_choice[2:-3]))

        # Format data for highCharts
        tweetsList = []
        index = -1
        for row in cursor:
            index += 1

            # Ignore the highlighted tweets
            #if(index < 20 and len(row) > 150): continue

            timeMS = row[1] * 1000      # Micro -> MiliSeconds
            sentiment = row[3] * 100
            tweetId = row[0]
            tweetsList.append({ 'x': timeMS,
                                'y' : sentiment,
                                'tweetId' : tweetId,
                                'tweetText' : row[2]})

            #tweetsList.append([timeMS, sentiment])

        # Must sort becuase tweets can come out of order. FIXME Fix this at an earlier
        # part

        print(tweetsList)
        tweetsList = sorted(tweetsList, key=lambda k: k['x'])
        print(len(tweetsList))

        return render_template("results.html", tweetsList=tweetsList,\
                table_choice=table_choice[2:-3])
    except:
        return render_template("error.html")

# Starts up the webserver
if __name__ == "__main__":
    app.run()
<|endoftext|>


<commit_before>#! /usr/bin/env python3
# -*- coding:Utf8 -*-

import shlex

import os
import re
import random as rd
import argparse as ag
import subprocess as sb

from Daemon import Daemon

#from   shlex                      import split

def SearchFile(motif, pdir=".", recurse=False, exclude=None):
	"""Cherche dans le répertoire courant (et dans les sous-répertoires, si recurse = True (False par défaut)) si un motif ou un dossier contient la chaîne "motif".

	motif           :: motif recherché.
	pdir    = "."   :: Répertoire parent à partir duquel rechercher.
	recurse = False :: récursif ou non (défaut : non récursif).
	"""

	res = []

	for i in os.listdir(pdir):
		i = os.path.join(pdir, i)
		if recurse and os.path.isdir(i):
			#res.append(SearchFile(motif, pdir=i, recurse=recurse))
			res = res + SearchFile(motif, pdir=i, recurse=recurse)
			while [] in res:
				res.remove([])
		elif re.search(motif, i) is not None:
			if exclude is None or re.search(exclude, i) is None:
				res.append(i)
	return res

class WallPaper(Daemon):

    """Gère les fond d'écran.
    """

    def __init__(
            self,
            directory,
            motif=".*",
            recurse=True,
            Time=None,
            pidfile="/tmp/pid.wallpaper"):
        super().__init__(pidfile)
        self.motif = motif
        self.recurse = True
        if directory is not None:
            self.directory = directory
            #self.List_File = dir.SearchFile(motif, pdir=directory, recurse=recurse)
        self.prog = "feh"
        self.opt = "--bg-scale"
        self.time = Time
        self.dual = False

    def Set_cmd(self, prog):
        self.prog = prog

    def Get_cmd(self):
        return prog

    def Del_cmd(self):
        self.prog = "feh"

    def Set_directory(self, directory):
        self.directory = directory
        # if directory is not None:
            #self.List_File = dir.SearchFile(motif, pdir=directory, recurse=recurse)

    def Get_directory(self):
        return directory

    def Del_directory(self):
        self.directory = None

    def Set_opt(self, opt):
        self.opt = opt

    def Get_opt(self):
        return opt

    def Del_opt(self):
        self.opt = "--bg-scale"

    def Get_Time(self):
        return self.time

    def Set_Time(self, Time):
        self.time = Time

    def Del_Time(self):
        self.time = None

    def Get_Dual(self):
        return self.dual

    def Set_Dual(self, val):
        if isinstance(val, bool):
            self.dual = val
        else:
            raise TypeError(
                "Mauvais type donnée. Attendu : " +
                type(
                    self.dual) +
                ", Reçu : " +
                type(val))

    def Del_Dual(self):
        self.dual = False

    def __call__(self):
        self.List_File = self._search_file()
        file = self.List_File[rd.randint(0, len(self.List_File)-1)]
        cmd = self.prog + " " + self.opt + " " + '"' + file + '"'
        if self.dual:
            cmd += " " + '"' + \
                self.List_File[rd.randint(0, len(self.List_File)-1)] + '"'
        print("Commande : ", cmd)
        make = sb.Popen(shlex.split(cmd), stdout=sb.PIPE, stderr=sb.PIPE)
        out, err = make.communicate()
        out = out.decode("utf-8").split('\n')
        err = err.decode("utf-8").split('\n')
        print(out)
        print(err)

    def _search_file(self):
        if self.directory is not None:
            return SearchFile(
                self.motif,
                pdir=self.directory,
                recurse=self.recurse)
        return [""]

    def run(self):
        from time import sleep
        while True:
            self.List_File = self._search_file()
            file = self.List_File[rd.randint(0, len(self.List_File)-1)]
            cmd = self.prog + " " + self.opt + " " + '"' + file + '"'
            if self.dual:
                cmd += " " + '"' + \
                    self.List_File[rd.randint(0, len(self.List_File)-1)] + '"'
            make = sb.Popen(shlex.split(cmd), stdout=sb.PIPE, stderr=sb.PIPE)
            sleep(self.Time)

    Directory = property(
        Get_directory,
        Set_directory,
        Del_directory,
        doc="Répertoire où trouver les images.")
    Cmd = property(
        Get_cmd,
        Set_cmd,
        Del_cmd,
        doc="Programme à utiliser pour afficher l'image en fond d'écran.")
    Opt = property(Get_opt, Set_opt, Del_opt, doc="Option du programme.")
    Time = property(
        Get_Time,
        Set_Time,
        Del_Time,
        doc="Temps entre chaque changement de fond d'écran (mode démon uniquement).")
    Dual = property(Get_Dual, Set_Dual, Del_Dual, doc="Multi-écran ou non.")

def main(args):
    if args.kill:
        import sys
        wall = WallPaper(None)
        wall.stop()
        sys.exit(0)
    if args.Directory is None:
        import sys
        print("Vous devez indiquez un dossier ou l'option -k")
        sys.exit(1)

    wall = WallPaper(args.Directory, args.expr)
    wall.Cmd = args.prog
    wall.Opt = args.opt

    if args.dual:
        wall.Dual = True

    if args.daemon:
        wall.Time = args.time
        if args.time is None:
            raise ValueError("Le temps de pause est invalide : None")

        wall.start()
    else:
        wall()

if __name__ == '__main__':
    parser = ag.ArgumentParser()
    parser.add_argument(
        "Directory",
        nargs='?',
        help="Répertoire contenant les images à mettre en fond d'écran.")
    parser.add_argument(
        "-E",
        "--expr",
        type=str,
        help="Motif (expression régulière) de sélection des images.",
        default=".*\.(png|jpg)$")
    parser.add_argument(
        "--opt",
        type=str,
        help="Options à passer au programme chargé de mettre le fond d'écran en place.",
        default="--bg-scale")
    parser.add_argument(
        "--prog",
        type=str,
        help="Programme mettant le place un fond d'écran.",
        default="feh")
    parser.add_argument(
        "-d",
        "--daemon",
        help="Lancer le programme en tant que démon.",
        action='store_true')
    parser.add_argument(
        "-t",
        "--time",
        type=float,
        help="Temps entre chaque changement de fond d'écran.",
        default=120.0 *
        60.0)
    parser.add_argument(
        "-k",
        "--kill",
        help="Temps entre chaque changement de fond d'écran.",
        action='store_true')
    parser.add_argument(
        "-m",
        "--dual",
        help="Multi-écran : un fond d'écran différent sur chaque écran.",
        action='store_true')

    main(parser.parse_args())
<commit_msg>Correct to pep8 function SearchFile. Correct a few getter. Add default value in option.<commit_after>#! /usr/bin/env python3
# -*- coding:Utf8 -*-

import shlex

import os
import re
import random as rd
import argparse as ag
import subprocess as sb

from Daemon import Daemon

def SearchFile(motif, pdir=".", recurse=False, exclude=None):
    """Cherche dans le répertoire courant (et dans les sous-répertoires, si
    recurse = True (False par défaut)) si un motif ou un dossier contient la
    chaîne "motif".

    motif           :: motif recherché.
    pdir    = "."   :: Répertoire parent à partir duquel rechercher.
    recurse = False :: récursif ou non (défaut : non récursif).
    """

    res = []

    for i in os.listdir(pdir):
        i = os.path.join(pdir, i)
        if recurse and os.path.isdir(i):
            # res.append(SearchFile(motif, pdir=i, recurse=recurse))
            res = res + SearchFile(motif, pdir=i, recurse=recurse)
            while [] in res:
                res.remove([])
        elif re.search(motif, i) is not None:
            if exclude is None or re.search(exclude, i) is None:
                res.append(i)
    return res

class WallPaper(Daemon):

    """Gère les fond d'écran.
    """

    def __init__(
            self,
            directory,
            motif=".*",
            recurse=True,
            Time=None,
            pidfile="/tmp/pid.wallpaper"):
        super().__init__(pidfile)
        self.motif = motif
        self.recurse = True
        if directory is not None:
            self.directory = directory
            # self.List_File = dir.SearchFile(motif, pdir=directory, recurse=recurse)
        self.prog = "feh"
        self.opt = "--bg-scale"
        self.time = Time
        self.dual = False

    def Set_cmd(self, prog):
        self.prog = prog

    def Get_cmd(self):
        return self.prog

    def Del_cmd(self):
        self.prog = "feh"

    def Set_directory(self, directory):
        self.directory = directory
        # if directory is not None:
            # self.List_File = dir.SearchFile(motif, pdir=directory, recurse=recurse)

    def Get_directory(self):
        return self.directory

    def Del_directory(self):
        self.directory = None

    def Set_opt(self, opt):
        self.opt = opt

    def Get_opt(self):
        return self.opt

    def Del_opt(self):
        self.opt = "--bg-scale"

    def Get_Time(self):
        return self.time

    def Set_Time(self, Time):
        self.time = Time

    def Del_Time(self):
        self.time = None

    def Get_Dual(self):
        return self.dual

    def Set_Dual(self, val):
        if isinstance(val, bool):
            self.dual = val
        else:
            raise TypeError(
                "Mauvais type donnée. Attendu : " +
                type(
                    self.dual) +
                ", Reçu : " +
                type(val))

    def Del_Dual(self):
        self.dual = False

    def __call__(self):
        self.List_File = self._search_file()
        file = self.List_File[rd.randint(0, len(self.List_File)-1)]
        cmd = self.prog + " " + self.opt + " " + '"' + file + '"'
        if self.dual:
            cmd += " " + '"' + \
                self.List_File[rd.randint(0, len(self.List_File)-1)] + '"'
        print("Commande : ", cmd)
        make = sb.Popen(shlex.split(cmd), stdout=sb.PIPE, stderr=sb.PIPE)
        out, err = make.communicate()
        out = out.decode("utf-8").split('\n')
        err = err.decode("utf-8").split('\n')
        print(out)
        print(err)

    def _search_file(self):
        if self.directory is not None:
            return SearchFile(
                self.motif,
                pdir=self.directory,
                recurse=self.recurse)
        return [""]

    def run(self):
        from time import sleep
        while True:
            self.List_File = self._search_file()
            file = self.List_File[rd.randint(0, len(self.List_File)-1)]
            cmd = self.prog + " " + self.opt + " " + '"' + file + '"'
            if self.dual:
                cmd += " " + '"' + \
                    self.List_File[rd.randint(0, len(self.List_File)-1)] + '"'
            make = sb.Popen(shlex.split(cmd), stdout=sb.PIPE, stderr=sb.PIPE)
            sleep(self.Time)

    Directory = property(
        Get_directory,
        Set_directory,
        Del_directory,
        doc="Répertoire où trouver les images.")
    Cmd = property(
        Get_cmd,
        Set_cmd,
        Del_cmd,
        doc="Programme à utiliser pour afficher l'image en fond d'écran.")
    Opt = property(Get_opt, Set_opt, Del_opt, doc="Option du programme.")
    Time = property(
        Get_Time,
        Set_Time,
        Del_Time,
        doc="Temps entre chaque changement de fond d'écran (mode démon uniquement).")
    Dual = property(Get_Dual, Set_Dual, Del_Dual, doc="Multi-écran ou non.")

def main(args):
    if args.kill:
        import sys
        wall = WallPaper(None)
        wall.stop()
        sys.exit(0)
    if args.Directory is None:
        import sys
        print("Vous devez indiquez un dossier ou l'option -k")
        sys.exit(1)

    wall = WallPaper(args.Directory, args.expr)
    wall.Cmd = args.prog
    wall.Opt = args.opt

    if args.dual:
        wall.Dual = True

    if args.daemon:
        wall.Time = args.time
        if args.time is None:
            raise ValueError("Le temps de pause est invalide : None")

        wall.start()
    else:
        wall()

if __name__ == '__main__':
    parser = ag.ArgumentParser(
        formatter_class=ag.ArgumentDefaultsHelpFormatter,
    )
    parser.add_argument(
        "Directory",
        nargs='?',
        help="Répertoire contenant les images à mettre en fond d'écran.")
    parser.add_argument(
        "-E",
        "--expr",
        type=str,
        help="Motif (expression régulière) de sélection des images.",
        default=".*\.(png|jpg)$")
    parser.add_argument(
        "--opt",
        type=str,
        help="Options à passer au programme chargé de mettre le fond d'écran en place.",
        default="--bg-scale")
    parser.add_argument(
        "--prog",
        type=str,
        help="Programme mettant le place un fond d'écran.",
        default="feh")
    parser.add_argument(
        "-d",
        "--daemon",
        help="Lancer le programme en tant que démon.",
        action='store_true')
    parser.add_argument(
        "-t",
        "--time",
        type=float,
        help="Temps entre chaque changement de fond d'écran.",
        default=120.0 *
        60.0)
    parser.add_argument(
        "-k",
        "--kill",
        help="Temps entre chaque changement de fond d'écran.",
        action='store_true')
    parser.add_argument(
        "-m",
        "--dual",
        help="Multi-écran : un fond d'écran différent sur chaque écran.",
        action='store_true')

    main(parser.parse_args())
<|endoftext|>