diff --git a/app/admin.py b/app/admin.py index 819b99d..7712358 100644 --- a/app/admin.py +++ b/app/admin.py @@ -1,12 +1,14 @@ import flask_login as login +from flask import Flask from flask_admin import Admin from flask_admin.contrib.sqla import ModelView +from flask_sqlalchemy import SQLAlchemy from models import Location, Order, OrderItem, Product, User class ModelBaseView(ModelView): - def is_accessible(self): + def is_accessible(self) -> bool: if login.current_user.is_anonymous(): return False @@ -29,7 +31,7 @@ class LocationAdminModel(ModelBaseView): form_columns = ("name", "address", "website", "telephone") -def init_admin(app, db): +def init_admin(app: Flask, db: SQLAlchemy) -> None: admin = Admin(app, name="Haldis", url="/admin", template_mode="bootstrap3") admin.add_view(UserAdminModel(User, db.session)) diff --git a/app/app.py b/app/app.py index e1674d5..8e6c063 100644 --- a/app/app.py +++ b/app/app.py @@ -1,4 +1,5 @@ import logging +import typing from datetime import datetime from logging.handlers import TimedRotatingFileHandler @@ -19,7 +20,7 @@ from utils import euro_string from zeus import init_oauth -def create_app(): +def create_app() -> Manager: app = Flask(__name__) # Load the config file @@ -34,7 +35,7 @@ def create_app(): return manager -def register_plugins(app, debug: bool): +def register_plugins(app: Flask, debug: bool) -> Manager: # Register Airbrake and enable the logrotation if not app.debug: timedFileHandler = TimedRotatingFileHandler( @@ -96,17 +97,17 @@ def register_plugins(app, debug: bool): return manager -def add_handlers(app): +def add_handlers(app: Flask) -> None: @app.errorhandler(404) - def handle404(e): + def handle404(e) -> typing.Tuple[str, int]: return render_template("errors/404.html"), 404 @app.errorhandler(401) - def handle401(e): + def handle401(e) -> typing.Tuple[str, int]: return render_template("errors/401.html"), 401 -def add_routes(application): +def add_routes(application: Flask) -> None: # import views # TODO convert to blueprint # import views.stats # TODO convert to blueprint @@ -127,9 +128,9 @@ def add_routes(application): application.register_blueprint(debug_bp, url_prefix="/debug") -def add_template_filters(app): +def add_template_filters(app: Flask) -> None: @app.template_filter("countdown") - def countdown(value, only_positive=True, show_text=True): + def countdown(value, only_positive: bool = True, show_text: bool = True) -> str: delta = value - datetime.now() if delta.total_seconds() < 0 and only_positive: return "closed" @@ -141,11 +142,11 @@ def add_template_filters(app): return time @app.template_filter("year") - def current_year(value): + def current_year(value: typing.Any) -> str: return str(datetime.now().year) @app.template_filter("euro") - def euro(value): + def euro(value: int) -> None: euro_string(value) diff --git a/app/fatmodels.py b/app/fatmodels.py index f4c4c53..79eac01 100644 --- a/app/fatmodels.py +++ b/app/fatmodels.py @@ -1,3 +1,5 @@ +import typing + from sqlalchemy.sql import desc, func from models import Location, Order, OrderItem, Product, User @@ -42,7 +44,7 @@ class FatOrderItem(OrderItem, FatModel): class FatProduct(Product, FatModel): @classmethod - def top4(cls): + def top4(cls) -> None: top4 = ( OrderItem.query.join(Product) .join(Location) diff --git a/app/forms.py b/app/forms.py index a41c188..4003944 100644 --- a/app/forms.py +++ b/app/forms.py @@ -3,7 +3,8 @@ from datetime import datetime, timedelta from flask import session from flask_login import current_user from flask_wtf import FlaskForm as Form -from wtforms import DateTimeField, SelectField, StringField, SubmitField, validators +from wtforms import (DateTimeField, SelectField, StringField, SubmitField, + validators) from models import Location, User from utils import euro_string @@ -20,7 +21,7 @@ class OrderForm(Form): stoptime = DateTimeField("Stoptime", format="%d-%m-%Y %H:%M") submit_button = SubmitField("Submit") - def populate(self): + def populate(self) -> None: if current_user.is_admin(): self.courrier_id.choices = [(0, None)] + [ (u.id, u.username) for u in User.query.order_by("username") @@ -42,7 +43,7 @@ class OrderItemForm(Form): extra = StringField("Extra") submit_button = SubmitField("Submit") - def populate(self, location): + def populate(self, location: Location) -> None: self.product_id.choices = [ (i.id, (i.name + ": " + euro_string(i.price))) for i in location.products ] @@ -51,12 +52,12 @@ class OrderItemForm(Form): class AnonOrderItemForm(OrderItemForm): name = StringField("Name", validators=[validators.required()]) - def populate(self, location): + def populate(self, location: Location) -> None: OrderItemForm.populate(self, location) if self.name.data is None: self.name.data = session.get("anon_name", None) - def validate(self): + def validate(self) -> bool: rv = OrderForm.validate(self) if not rv: return False diff --git a/app/login.py b/app/login.py index 27c2e8a..c50b245 100644 --- a/app/login.py +++ b/app/login.py @@ -1,6 +1,6 @@ -from flask import abort, Blueprint -from flask import redirect, session, url_for +from flask import Blueprint, abort, redirect, session, url_for from flask_login import current_user, logout_user +from werkzeug.wrappers import Response from models import User from zeus import zeus_login @@ -8,9 +8,9 @@ from zeus import zeus_login auth_bp = Blueprint("auth_bp", __name__) -def init_login(app): +def init_login(app) -> None: @app.login_manager.user_loader - def load_user(userid): + def load_user(userid) -> User: return User.query.filter_by(id=userid).first() @@ -20,13 +20,13 @@ def login(): @auth_bp.route("/logout") -def logout(): +def logout() -> Response: if "zeus_token" in session: session.pop("zeus_token", None) logout_user() return redirect(url_for("general_bp.home")) -def before_request(): +def before_request() -> None: if current_user.is_anonymous() or not current_user.is_allowed(): abort(401) diff --git a/app/notification.py b/app/notification.py index 2fc8cbc..be20c3c 100644 --- a/app/notification.py +++ b/app/notification.py @@ -7,7 +7,7 @@ from flask import current_app as app from flask import url_for -def post_order_to_webhook(order_item): +def post_order_to_webhook(order_item) -> None: message = "" if order_item.courrier is not None: message = " {3} is going to {1}, order <{0}|here>! Deadline in {2} minutes!".format( @@ -27,14 +27,14 @@ def post_order_to_webhook(order_item): class WebhookSenderThread(Thread): - def __init__(self, message): + def __init__(self, message: str) -> None: super(WebhookSenderThread, self).__init__() self.message = message - def run(self): + def run(self) -> None: self.slack_webhook() - def slack_webhook(self): + def slack_webhook(self) -> None: js = json.dumps({"text": self.message}) url = app.config["SLACK_WEBHOOK"] if len(url) > 0: @@ -43,7 +43,7 @@ class WebhookSenderThread(Thread): app.logger.info(str(js)) -def remaining_minutes(value): +def remaining_minutes(value) -> str: delta = value - datetime.now() if delta.total_seconds() < 0: return "0" diff --git a/app/utils.py b/app/utils.py index 0c456ce..1584117 100644 --- a/app/utils.py +++ b/app/utils.py @@ -1,4 +1,4 @@ -def euro_string(value): +def euro_string(value: int) -> str: """ Convert cents to string formatted euro """ diff --git a/app/zeus.py b/app/zeus.py index b56f70a..8ca77d1 100644 --- a/app/zeus.py +++ b/app/zeus.py @@ -1,6 +1,10 @@ -from flask import current_app, flash, redirect, request, session, url_for, Blueprint +import typing + +from flask import (Blueprint, current_app, flash, redirect, request, session, + url_for) from flask_login import login_user -from flask_oauthlib.client import OAuthException, OAuth +from flask_oauthlib.client import OAuth, OAuthException +from werkzeug.wrappers import Response from models import User, db @@ -14,7 +18,9 @@ def zeus_login(): @oauth_bp.route("/login/zeus/authorized") -def authorized(): +def authorized() -> typing.Any: + # type is 'typing.Union[str, Response]', but this errors due to + # https://github.com/python/mypy/issues/7187 resp = current_app.zeus.authorized_response() if resp is None: return "Access denied: reason=%s error=%s" % ( @@ -60,12 +66,12 @@ def init_oauth(app): return zeus -def login_and_redirect_user(user): +def login_and_redirect_user(user) -> Response: login_user(user) return redirect(url_for("general_bp.home")) -def create_user(username): +def create_user(username) -> User: user = User() user.configure(username, False, 1) db.session.add(user)