commit
32c60eaff5
28 changed files with 178 additions and 141 deletions
|
@ -1,12 +1,14 @@
|
||||||
import flask_login as login
|
import flask_login as login
|
||||||
|
from flask import Flask
|
||||||
from flask_admin import Admin
|
from flask_admin import Admin
|
||||||
from flask_admin.contrib.sqla import ModelView
|
from flask_admin.contrib.sqla import ModelView
|
||||||
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
|
||||||
from models import Location, Order, OrderItem, Product, User
|
from models import Location, Order, OrderItem, Product, User
|
||||||
|
|
||||||
|
|
||||||
class ModelBaseView(ModelView):
|
class ModelBaseView(ModelView):
|
||||||
def is_accessible(self):
|
def is_accessible(self) -> bool:
|
||||||
if login.current_user.is_anonymous():
|
if login.current_user.is_anonymous():
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -29,7 +31,7 @@ class LocationAdminModel(ModelBaseView):
|
||||||
form_columns = ("name", "address", "website", "telephone")
|
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 = Admin(app, name="Haldis", url="/admin", template_mode="bootstrap3")
|
||||||
|
|
||||||
admin.add_view(UserAdminModel(User, db.session))
|
admin.add_view(UserAdminModel(User, db.session))
|
||||||
|
|
21
app/app.py
21
app/app.py
|
@ -1,4 +1,5 @@
|
||||||
import logging
|
import logging
|
||||||
|
import typing
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
|
||||||
|
@ -19,7 +20,7 @@ from utils import euro_string
|
||||||
from zeus import init_oauth
|
from zeus import init_oauth
|
||||||
|
|
||||||
|
|
||||||
def create_app():
|
def create_app() -> Manager:
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
# Load the config file
|
# Load the config file
|
||||||
|
@ -34,7 +35,7 @@ def create_app():
|
||||||
return manager
|
return manager
|
||||||
|
|
||||||
|
|
||||||
def register_plugins(app, debug: bool):
|
def register_plugins(app: Flask, debug: bool) -> Manager:
|
||||||
# Register Airbrake and enable the logrotation
|
# Register Airbrake and enable the logrotation
|
||||||
if not app.debug:
|
if not app.debug:
|
||||||
timedFileHandler = TimedRotatingFileHandler(
|
timedFileHandler = TimedRotatingFileHandler(
|
||||||
|
@ -96,17 +97,17 @@ def register_plugins(app, debug: bool):
|
||||||
return manager
|
return manager
|
||||||
|
|
||||||
|
|
||||||
def add_handlers(app):
|
def add_handlers(app: Flask) -> None:
|
||||||
@app.errorhandler(404)
|
@app.errorhandler(404)
|
||||||
def handle404(e):
|
def handle404(e) -> typing.Tuple[str, int]:
|
||||||
return render_template("errors/404.html"), 404
|
return render_template("errors/404.html"), 404
|
||||||
|
|
||||||
@app.errorhandler(401)
|
@app.errorhandler(401)
|
||||||
def handle401(e):
|
def handle401(e) -> typing.Tuple[str, int]:
|
||||||
return render_template("errors/401.html"), 401
|
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 # TODO convert to blueprint
|
||||||
# import views.stats # 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")
|
application.register_blueprint(debug_bp, url_prefix="/debug")
|
||||||
|
|
||||||
|
|
||||||
def add_template_filters(app):
|
def add_template_filters(app: Flask) -> None:
|
||||||
@app.template_filter("countdown")
|
@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()
|
delta = value - datetime.now()
|
||||||
if delta.total_seconds() < 0 and only_positive:
|
if delta.total_seconds() < 0 and only_positive:
|
||||||
return "closed"
|
return "closed"
|
||||||
|
@ -141,11 +142,11 @@ def add_template_filters(app):
|
||||||
return time
|
return time
|
||||||
|
|
||||||
@app.template_filter("year")
|
@app.template_filter("year")
|
||||||
def current_year(value):
|
def current_year(value: typing.Any) -> str:
|
||||||
return str(datetime.now().year)
|
return str(datetime.now().year)
|
||||||
|
|
||||||
@app.template_filter("euro")
|
@app.template_filter("euro")
|
||||||
def euro(value):
|
def euro(value: int) -> None:
|
||||||
euro_string(value)
|
euro_string(value)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ from app import db
|
||||||
from models import User
|
from models import User
|
||||||
|
|
||||||
|
|
||||||
def add():
|
def add() -> None:
|
||||||
feli = User()
|
feli = User()
|
||||||
feli.configure("feliciaan", True, 0)
|
feli.configure("feliciaan", True, 0)
|
||||||
db.session.add(feli)
|
db.session.add(feli)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
from models import Location, Product
|
|
||||||
from app import db
|
from app import db
|
||||||
|
from models import Location, Product
|
||||||
|
|
||||||
menuitems = [
|
menuitems = [
|
||||||
"Spicy Chicken",
|
"Spicy Chicken",
|
||||||
|
@ -23,7 +22,7 @@ menuitems = [
|
||||||
pricedict = {"Small": 799, "Medium": 999, "Large": 1199}
|
pricedict = {"Small": 799, "Medium": 999, "Large": 1199}
|
||||||
|
|
||||||
|
|
||||||
def add():
|
def add() -> None:
|
||||||
simpizza = Location()
|
simpizza = Location()
|
||||||
simpizza.configure("Fitchen", "?", "?", "https://www.fitchen.be/")
|
simpizza.configure("Fitchen", "?", "?", "https://www.fitchen.be/")
|
||||||
db.session.add(simpizza)
|
db.session.add(simpizza)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from models import Location, Product
|
|
||||||
from app import db
|
|
||||||
from itertools import product
|
from itertools import product
|
||||||
|
|
||||||
|
from app import db
|
||||||
|
from models import Location, Product
|
||||||
|
|
||||||
zetmelen = ["Nasi", "Bami"]
|
zetmelen = ["Nasi", "Bami"]
|
||||||
vlezen = ["Rundsvlees", "Varkensvlees", "Kippenstukkjes"]
|
vlezen = ["Rundsvlees", "Varkensvlees", "Kippenstukkjes"]
|
||||||
|
@ -29,7 +29,7 @@ specials = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def add():
|
def add() -> None:
|
||||||
chinees = Location()
|
chinees = Location()
|
||||||
chinees.configure(
|
chinees.configure(
|
||||||
"Oceans's Garden",
|
"Oceans's Garden",
|
||||||
|
@ -39,12 +39,12 @@ def add():
|
||||||
)
|
)
|
||||||
db.session.add(chinees)
|
db.session.add(chinees)
|
||||||
|
|
||||||
def chinees_create_entry(name):
|
def chinees_create_entry(name) -> None:
|
||||||
entry = Product()
|
entry = Product()
|
||||||
entry.configure(chinees, name, 550)
|
entry.configure(chinees, name, 550)
|
||||||
db.session.add(entry)
|
db.session.add(entry)
|
||||||
|
|
||||||
def chinees_create_regulat(zetmeel, vlees="", saus=""):
|
def chinees_create_regulat(zetmeel, vlees="", saus="") -> None:
|
||||||
chinees_create_entry("{} {} {}".format(zetmeel, vlees, saus).rstrip())
|
chinees_create_entry("{} {} {}".format(zetmeel, vlees, saus).rstrip())
|
||||||
|
|
||||||
for z, v, s in product(zetmelen, vlezen, sauzen):
|
for z, v, s in product(zetmelen, vlezen, sauzen):
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from models import Location, Product
|
|
||||||
from app import db
|
from app import db
|
||||||
|
from models import Location, Product
|
||||||
|
|
||||||
|
|
||||||
def add():
|
def add():
|
||||||
|
@ -46,7 +46,7 @@ pizzasTA = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def addTA():
|
def addTA() -> None:
|
||||||
primadonna_takeaway = Location()
|
primadonna_takeaway = Location()
|
||||||
primadonna_takeaway.configure(
|
primadonna_takeaway.configure(
|
||||||
"Primadonna (takeaway laten bezorgen)",
|
"Primadonna (takeaway laten bezorgen)",
|
||||||
|
@ -101,7 +101,7 @@ pizzasAfhalen = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def addAfhalen():
|
def addAfhalen() -> None:
|
||||||
primadonna_afhalen = Location()
|
primadonna_afhalen = Location()
|
||||||
primadonna_afhalen.configure(
|
primadonna_afhalen.configure(
|
||||||
"Primadonna (bellen en afhalen)",
|
"Primadonna (bellen en afhalen)",
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
from models import Location, Product
|
|
||||||
from app import db
|
from app import db
|
||||||
|
from models import Location, Product
|
||||||
|
|
||||||
pizzas = [
|
pizzas = [
|
||||||
"Bolognese de luxe",
|
"Bolognese de luxe",
|
||||||
|
@ -33,7 +32,7 @@ pizzas = [
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def add():
|
def add() -> None:
|
||||||
simpizza = Location()
|
simpizza = Location()
|
||||||
simpizza.configure(
|
simpizza.configure(
|
||||||
"Sim-pizza",
|
"Sim-pizza",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from models import Location, Product
|
|
||||||
from app import db
|
from app import db
|
||||||
|
from models import Location, Product
|
||||||
|
|
||||||
bickies = {
|
bickies = {
|
||||||
"Bicky Burger Original": 330,
|
"Bicky Burger Original": 330,
|
||||||
|
@ -11,7 +11,7 @@ bickies = {
|
||||||
"Bicky Veggie": 350,
|
"Bicky Veggie": 350,
|
||||||
}
|
}
|
||||||
|
|
||||||
saus = {
|
sauskes = {
|
||||||
"american": 70,
|
"american": 70,
|
||||||
"andalouse": 70,
|
"andalouse": 70,
|
||||||
"bicky saus": 70,
|
"bicky saus": 70,
|
||||||
|
@ -100,7 +100,7 @@ friet = {"Klein pak": 200, "Midden pak": 250, "Groot pak": 300}
|
||||||
data = [special_bickies, specials, vlezekes, friet]
|
data = [special_bickies, specials, vlezekes, friet]
|
||||||
|
|
||||||
|
|
||||||
def add():
|
def add() -> None:
|
||||||
stefanos = Location()
|
stefanos = Location()
|
||||||
stefanos.configure(
|
stefanos.configure(
|
||||||
"Stefano's Place",
|
"Stefano's Place",
|
||||||
|
@ -127,7 +127,7 @@ def add():
|
||||||
db.session.add(item)
|
db.session.add(item)
|
||||||
|
|
||||||
# saus in een potteke bestellen is 10 cent extra
|
# saus in een potteke bestellen is 10 cent extra
|
||||||
for name, price in saus.items():
|
for name, price in sauskes.items():
|
||||||
saus = Product()
|
saus = Product()
|
||||||
saus.configure(stefanos, name, price)
|
saus.configure(stefanos, name, price)
|
||||||
db.session.add(saus)
|
db.session.add(saus)
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
|
import add_admins
|
||||||
|
import add_fitchen
|
||||||
|
import add_oceans_garden
|
||||||
|
import add_primadonna
|
||||||
|
import add_simpizza
|
||||||
from app import db
|
from app import db
|
||||||
import add_oceans_garden, add_admins, add_simpizza, add_primadonna, add_fitchen
|
|
||||||
|
|
||||||
|
|
||||||
entry_sets = {
|
entry_sets = {
|
||||||
"Admins": add_admins.add,
|
"Admins": add_admins.add,
|
||||||
|
@ -15,23 +18,23 @@ no = ["no", "n", "N"]
|
||||||
|
|
||||||
|
|
||||||
# Commit all the things
|
# Commit all the things
|
||||||
def commit():
|
def commit() -> None:
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
print("Committing successful")
|
print("Committing successful")
|
||||||
|
|
||||||
|
|
||||||
def check_if_overwrite():
|
def check_if_overwrite() -> bool:
|
||||||
answer = input("Do you want to overwrite the previous database? (y/N) ")
|
answer = input("Do you want to overwrite the previous database? (y/N) ")
|
||||||
return answer in yes
|
return answer in yes
|
||||||
|
|
||||||
|
|
||||||
def add_all():
|
def add_all() -> None:
|
||||||
for entry_set in entry_sets.keys():
|
for entry_set in entry_sets.keys():
|
||||||
print("Adding {}.".format(entry_set))
|
print("Adding {}.".format(entry_set))
|
||||||
entry_sets[entry_set]()
|
entry_sets[entry_set]()
|
||||||
|
|
||||||
|
|
||||||
def recreate_from_scratch():
|
def recreate_from_scratch() -> None:
|
||||||
confirmation = "Are you very very sure? (Will delete previous entries!) (y/N) "
|
confirmation = "Are you very very sure? (Will delete previous entries!) (y/N) "
|
||||||
check = "I acknowledge any repercussions!"
|
check = "I acknowledge any repercussions!"
|
||||||
if input(confirmation) in yes and input("Type: '{}' ".format(check)) == check:
|
if input(confirmation) in yes and input("Type: '{}' ".format(check)) == check:
|
||||||
|
@ -41,10 +44,10 @@ def recreate_from_scratch():
|
||||||
add_to_current()
|
add_to_current()
|
||||||
|
|
||||||
|
|
||||||
def add_to_current():
|
def add_to_current() -> None:
|
||||||
available = [entry_set for entry_set in entry_sets]
|
available = [entry_set for entry_set in entry_sets]
|
||||||
|
|
||||||
def add_numbers():
|
def add_numbers() -> str:
|
||||||
return " ".join(
|
return " ".join(
|
||||||
["{}({}), ".format(loc, i) for i, loc in enumerate(available)]
|
["{}({}), ".format(loc, i) for i, loc in enumerate(available)]
|
||||||
).rstrip(", ")
|
).rstrip(", ")
|
||||||
|
@ -59,17 +62,17 @@ def add_to_current():
|
||||||
available = []
|
available = []
|
||||||
elif answer == "C":
|
elif answer == "C":
|
||||||
pass
|
pass
|
||||||
elif answer in [str(x) for x in range(len(available))]:
|
elif answer.isnumeric() and answer in [str(x) for x in range(len(available))]:
|
||||||
answer = int(answer)
|
answer_index = int(answer)
|
||||||
print("Adding {}.".format(available[answer]))
|
print("Adding {}.".format(available[answer_index]))
|
||||||
entry_sets[str(available[answer])]()
|
entry_sets[str(available[answer_index])]()
|
||||||
del available[answer]
|
del available[answer_index]
|
||||||
else:
|
else:
|
||||||
print("Not a valid answer.")
|
print("Not a valid answer.")
|
||||||
print("Thank you for adding, come again!")
|
print("Thank you for adding, come again!")
|
||||||
|
|
||||||
|
|
||||||
def init():
|
def init() -> None:
|
||||||
print("Database modification script!")
|
print("Database modification script!")
|
||||||
print("=============================\n\n")
|
print("=============================\n\n")
|
||||||
if check_if_overwrite():
|
if check_if_overwrite():
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import typing
|
||||||
|
|
||||||
from sqlalchemy.sql import desc, func
|
from sqlalchemy.sql import desc, func
|
||||||
|
|
||||||
from models import Location, Order, OrderItem, Product, User
|
from models import Location, Order, OrderItem, Product, User
|
||||||
|
@ -42,7 +44,7 @@ class FatOrderItem(OrderItem, FatModel):
|
||||||
|
|
||||||
class FatProduct(Product, FatModel):
|
class FatProduct(Product, FatModel):
|
||||||
@classmethod
|
@classmethod
|
||||||
def top4(cls):
|
def top4(cls) -> None:
|
||||||
top4 = (
|
top4 = (
|
||||||
OrderItem.query.join(Product)
|
OrderItem.query.join(Product)
|
||||||
.join(Location)
|
.join(Location)
|
||||||
|
|
11
app/forms.py
11
app/forms.py
|
@ -3,7 +3,8 @@ from datetime import datetime, timedelta
|
||||||
from flask import session
|
from flask import session
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
from flask_wtf import FlaskForm as Form
|
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 models import Location, User
|
||||||
from utils import euro_string
|
from utils import euro_string
|
||||||
|
@ -20,7 +21,7 @@ class OrderForm(Form):
|
||||||
stoptime = DateTimeField("Stoptime", format="%d-%m-%Y %H:%M")
|
stoptime = DateTimeField("Stoptime", format="%d-%m-%Y %H:%M")
|
||||||
submit_button = SubmitField("Submit")
|
submit_button = SubmitField("Submit")
|
||||||
|
|
||||||
def populate(self):
|
def populate(self) -> None:
|
||||||
if current_user.is_admin():
|
if current_user.is_admin():
|
||||||
self.courrier_id.choices = [(0, None)] + [
|
self.courrier_id.choices = [(0, None)] + [
|
||||||
(u.id, u.username) for u in User.query.order_by("username")
|
(u.id, u.username) for u in User.query.order_by("username")
|
||||||
|
@ -42,7 +43,7 @@ class OrderItemForm(Form):
|
||||||
extra = StringField("Extra")
|
extra = StringField("Extra")
|
||||||
submit_button = SubmitField("Submit")
|
submit_button = SubmitField("Submit")
|
||||||
|
|
||||||
def populate(self, location):
|
def populate(self, location: Location) -> None:
|
||||||
self.product_id.choices = [
|
self.product_id.choices = [
|
||||||
(i.id, (i.name + ": " + euro_string(i.price))) for i in location.products
|
(i.id, (i.name + ": " + euro_string(i.price))) for i in location.products
|
||||||
]
|
]
|
||||||
|
@ -51,12 +52,12 @@ class OrderItemForm(Form):
|
||||||
class AnonOrderItemForm(OrderItemForm):
|
class AnonOrderItemForm(OrderItemForm):
|
||||||
name = StringField("Name", validators=[validators.required()])
|
name = StringField("Name", validators=[validators.required()])
|
||||||
|
|
||||||
def populate(self, location):
|
def populate(self, location: Location) -> None:
|
||||||
OrderItemForm.populate(self, location)
|
OrderItemForm.populate(self, location)
|
||||||
if self.name.data is None:
|
if self.name.data is None:
|
||||||
self.name.data = session.get("anon_name", None)
|
self.name.data = session.get("anon_name", None)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self) -> bool:
|
||||||
rv = OrderForm.validate(self)
|
rv = OrderForm.validate(self)
|
||||||
if not rv:
|
if not rv:
|
||||||
return False
|
return False
|
||||||
|
|
12
app/login.py
12
app/login.py
|
@ -1,6 +1,6 @@
|
||||||
from flask import abort, Blueprint
|
from flask import Blueprint, abort, redirect, session, url_for
|
||||||
from flask import redirect, session, url_for
|
|
||||||
from flask_login import current_user, logout_user
|
from flask_login import current_user, logout_user
|
||||||
|
from werkzeug.wrappers import Response
|
||||||
|
|
||||||
from models import User
|
from models import User
|
||||||
from zeus import zeus_login
|
from zeus import zeus_login
|
||||||
|
@ -8,9 +8,9 @@ from zeus import zeus_login
|
||||||
auth_bp = Blueprint("auth_bp", __name__)
|
auth_bp = Blueprint("auth_bp", __name__)
|
||||||
|
|
||||||
|
|
||||||
def init_login(app):
|
def init_login(app) -> None:
|
||||||
@app.login_manager.user_loader
|
@app.login_manager.user_loader
|
||||||
def load_user(userid):
|
def load_user(userid) -> User:
|
||||||
return User.query.filter_by(id=userid).first()
|
return User.query.filter_by(id=userid).first()
|
||||||
|
|
||||||
|
|
||||||
|
@ -20,13 +20,13 @@ def login():
|
||||||
|
|
||||||
|
|
||||||
@auth_bp.route("/logout")
|
@auth_bp.route("/logout")
|
||||||
def logout():
|
def logout() -> Response:
|
||||||
if "zeus_token" in session:
|
if "zeus_token" in session:
|
||||||
session.pop("zeus_token", None)
|
session.pop("zeus_token", None)
|
||||||
logout_user()
|
logout_user()
|
||||||
return redirect(url_for("general_bp.home"))
|
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():
|
if current_user.is_anonymous() or not current_user.is_allowed():
|
||||||
abort(401)
|
abort(401)
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
from __future__ import with_statement
|
from __future__ import with_statement
|
||||||
from alembic import context
|
|
||||||
from sqlalchemy import engine_from_config, pool
|
|
||||||
from logging.config import fileConfig
|
from logging.config import fileConfig
|
||||||
|
|
||||||
|
from alembic import context
|
||||||
|
# add your model's MetaData object here
|
||||||
|
# for 'autogenerate' support
|
||||||
|
# from myapp import mymodel
|
||||||
|
# target_metadata = mymodel.Base.metadata
|
||||||
|
from flask import current_app
|
||||||
|
from sqlalchemy import engine_from_config, pool
|
||||||
|
|
||||||
# this is the Alembic Config object, which provides
|
# this is the Alembic Config object, which provides
|
||||||
# access to the values within the .ini file in use.
|
# access to the values within the .ini file in use.
|
||||||
config = context.config
|
config = context.config
|
||||||
|
@ -11,11 +18,6 @@ config = context.config
|
||||||
# This line sets up loggers basically.
|
# This line sets up loggers basically.
|
||||||
fileConfig(config.config_file_name)
|
fileConfig(config.config_file_name)
|
||||||
|
|
||||||
# add your model's MetaData object here
|
|
||||||
# for 'autogenerate' support
|
|
||||||
# from myapp import mymodel
|
|
||||||
# target_metadata = mymodel.Base.metadata
|
|
||||||
from flask import current_app
|
|
||||||
|
|
||||||
config.set_main_option(
|
config.set_main_option(
|
||||||
"sqlalchemy.url", current_app.config.get("SQLALCHEMY_DATABASE_URI")
|
"sqlalchemy.url", current_app.config.get("SQLALCHEMY_DATABASE_URI")
|
||||||
|
|
|
@ -10,8 +10,8 @@ Create Date: 2019-04-02 18:00:12.618368
|
||||||
revision = "150252c1cdb1"
|
revision = "150252c1cdb1"
|
||||||
down_revision = None
|
down_revision = None
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
|
||||||
def upgrade():
|
def upgrade():
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
class AnonymouseUser:
|
class AnonymouseUser:
|
||||||
id = None
|
id = None
|
||||||
|
|
||||||
def is_active(self):
|
def is_active(self) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_authenticated(self):
|
def is_authenticated(self) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_anonymous(self):
|
def is_anonymous(self) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def is_admin(self):
|
def is_admin(self) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_id(self):
|
def get_id(self) -> None:
|
||||||
return None
|
return None
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import typing
|
||||||
|
|
||||||
from models import db
|
from models import db
|
||||||
|
|
||||||
|
|
||||||
|
@ -10,11 +12,13 @@ class Location(db.Model):
|
||||||
products = db.relationship("Product", backref="location", lazy="dynamic")
|
products = db.relationship("Product", backref="location", lazy="dynamic")
|
||||||
orders = db.relationship("Order", backref="location", lazy="dynamic")
|
orders = db.relationship("Order", backref="location", lazy="dynamic")
|
||||||
|
|
||||||
def configure(self, name, address, telephone, website):
|
def configure(
|
||||||
|
self, name: str, address: str, telephone: typing.Optional[str], website: str
|
||||||
|
) -> None:
|
||||||
self.name = name
|
self.name = name
|
||||||
self.address = address
|
self.address = address
|
||||||
self.website = website
|
self.website = website
|
||||||
self.telephone = telephone
|
self.telephone = telephone
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
return "%s" % (self.name)
|
return "%s" % (self.name)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import typing
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from .database import db
|
from .database import db
|
||||||
|
from .location import Location
|
||||||
from .user import User
|
from .user import User
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,20 +15,26 @@ class Order(db.Model):
|
||||||
public = db.Column(db.Boolean, default=True)
|
public = db.Column(db.Boolean, default=True)
|
||||||
items = db.relationship("OrderItem", backref="order", lazy="dynamic")
|
items = db.relationship("OrderItem", backref="order", lazy="dynamic")
|
||||||
|
|
||||||
def configure(self, courrier, location, starttime, stoptime):
|
def configure(
|
||||||
|
self,
|
||||||
|
courrier: User,
|
||||||
|
location: Location,
|
||||||
|
starttime: db.DateTime,
|
||||||
|
stoptime: db.DateTime,
|
||||||
|
) -> None:
|
||||||
self.courrier = courrier
|
self.courrier = courrier
|
||||||
self.location = location
|
self.location = location
|
||||||
self.starttime = starttime
|
self.starttime = starttime
|
||||||
self.stoptime = stoptime
|
self.stoptime = stoptime
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
if self.location:
|
if self.location:
|
||||||
return "Order %d @ %s" % (self.id, self.location.name or "None")
|
return "Order %d @ %s" % (self.id, self.location.name or "None")
|
||||||
else:
|
else:
|
||||||
return "Order %d" % (self.id)
|
return "Order %d" % (self.id)
|
||||||
|
|
||||||
def group_by_user(self):
|
def group_by_user(self) -> typing.Dict[str, typing.Any]:
|
||||||
group = dict()
|
group: typing.Dict[str, typing.Any] = dict()
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
user = group.get(item.get_name(), dict())
|
user = group.get(item.get_name(), dict())
|
||||||
user["total"] = user.get("total", 0) + item.product.price
|
user["total"] = user.get("total", 0) + item.product.price
|
||||||
|
@ -39,8 +47,8 @@ class Order(db.Model):
|
||||||
|
|
||||||
return group
|
return group
|
||||||
|
|
||||||
def group_by_product(self):
|
def group_by_product(self) -> typing.Dict[str, typing.Any]:
|
||||||
group = dict()
|
group: typing.Dict[str, typing.Any] = dict()
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
product = group.get(item.product.name, dict())
|
product = group.get(item.product.name, dict())
|
||||||
product["count"] = product.get("count", 0) + 1
|
product["count"] = product.get("count", 0) + 1
|
||||||
|
@ -50,7 +58,7 @@ class Order(db.Model):
|
||||||
|
|
||||||
return group
|
return group
|
||||||
|
|
||||||
def can_close(self, user_id):
|
def can_close(self, user_id: int) -> bool:
|
||||||
if self.stoptime and self.stoptime < datetime.now():
|
if self.stoptime and self.stoptime < datetime.now():
|
||||||
return False
|
return False
|
||||||
user = None
|
user = None
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from .database import db
|
from .database import db
|
||||||
|
from .order import Order
|
||||||
|
from .product import Product
|
||||||
from .user import User
|
from .user import User
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,17 +19,17 @@ class OrderItem(db.Model):
|
||||||
extra = db.Column(db.String(254), nullable=True)
|
extra = db.Column(db.String(254), nullable=True)
|
||||||
name = db.Column(db.String(120))
|
name = db.Column(db.String(120))
|
||||||
|
|
||||||
def configure(self, user, order, product):
|
def configure(self, user: User, order: Order, product: Product) -> None:
|
||||||
self.user = user
|
self.user = user
|
||||||
self.order = order
|
self.order = order
|
||||||
self.product = product
|
self.product = product
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self) -> str:
|
||||||
if self.user_id is not None and self.user_id > 0:
|
if self.user_id is not None and self.user_id > 0:
|
||||||
return self.user.username
|
return self.user.username
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
product_name = None
|
product_name = None
|
||||||
if self.product:
|
if self.product:
|
||||||
product_name = self.product.name
|
product_name = self.product.name
|
||||||
|
@ -37,7 +39,7 @@ class OrderItem(db.Model):
|
||||||
product_name or "None",
|
product_name or "None",
|
||||||
)
|
)
|
||||||
|
|
||||||
def can_delete(self, order_id, user_id, name):
|
def can_delete(self, order_id: int, user_id: int, name: str) -> bool:
|
||||||
if int(self.order_id) != int(order_id):
|
if int(self.order_id) != int(order_id):
|
||||||
return False
|
return False
|
||||||
if self.order.stoptime and self.order.stoptime < datetime.now():
|
if self.order.stoptime and self.order.stoptime < datetime.now():
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from models import db
|
from models import db
|
||||||
|
|
||||||
|
from .location import Location
|
||||||
|
|
||||||
|
|
||||||
class Product(db.Model):
|
class Product(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
@ -8,12 +10,12 @@ class Product(db.Model):
|
||||||
price = db.Column(db.Integer, nullable=False)
|
price = db.Column(db.Integer, nullable=False)
|
||||||
orderItems = db.relationship("OrderItem", backref="product", lazy="dynamic")
|
orderItems = db.relationship("OrderItem", backref="product", lazy="dynamic")
|
||||||
|
|
||||||
def configure(self, location, name, price):
|
def configure(self, location: Location, name: str, price: int) -> None:
|
||||||
self.location = location
|
self.location = location
|
||||||
self.name = name
|
self.name = name
|
||||||
self.price = price
|
self.price = price
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
return "%s (€%d)from %s" % (
|
return "%s (€%d)from %s" % (
|
||||||
self.name,
|
self.name,
|
||||||
self.price / 100,
|
self.price / 100,
|
||||||
|
|
|
@ -14,25 +14,25 @@ class User(db.Model):
|
||||||
)
|
)
|
||||||
orderItems = db.relationship("OrderItem", backref="user", lazy="dynamic")
|
orderItems = db.relationship("OrderItem", backref="user", lazy="dynamic")
|
||||||
|
|
||||||
def configure(self, username, admin, bias):
|
def configure(self, username: str, admin: bool, bias: int) -> None:
|
||||||
self.username = username
|
self.username = username
|
||||||
self.admin = admin
|
self.admin = admin
|
||||||
self.bias = bias
|
self.bias = bias
|
||||||
|
|
||||||
def is_authenticated(self):
|
def is_authenticated(self) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def is_active(self):
|
def is_active(self) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def is_admin(self):
|
def is_admin(self) -> bool:
|
||||||
return self.admin
|
return self.admin
|
||||||
|
|
||||||
def is_anonymous(self):
|
def is_anonymous(self) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_id(self):
|
def get_id(self) -> str:
|
||||||
return str(self.id)
|
return str(self.id)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
return "%s" % self.username
|
return "%s" % self.username
|
||||||
|
|
|
@ -7,7 +7,7 @@ from flask import current_app as app
|
||||||
from flask import url_for
|
from flask import url_for
|
||||||
|
|
||||||
|
|
||||||
def post_order_to_webhook(order_item):
|
def post_order_to_webhook(order_item) -> None:
|
||||||
message = ""
|
message = ""
|
||||||
if order_item.courrier is not None:
|
if order_item.courrier is not None:
|
||||||
message = "<!channel|@channel> {3} is going to {1}, order <{0}|here>! Deadline in {2} minutes!".format(
|
message = "<!channel|@channel> {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):
|
class WebhookSenderThread(Thread):
|
||||||
def __init__(self, message):
|
def __init__(self, message: str) -> None:
|
||||||
super(WebhookSenderThread, self).__init__()
|
super(WebhookSenderThread, self).__init__()
|
||||||
self.message = message
|
self.message = message
|
||||||
|
|
||||||
def run(self):
|
def run(self) -> None:
|
||||||
self.slack_webhook()
|
self.slack_webhook()
|
||||||
|
|
||||||
def slack_webhook(self):
|
def slack_webhook(self) -> None:
|
||||||
js = json.dumps({"text": self.message})
|
js = json.dumps({"text": self.message})
|
||||||
url = app.config["SLACK_WEBHOOK"]
|
url = app.config["SLACK_WEBHOOK"]
|
||||||
if len(url) > 0:
|
if len(url) > 0:
|
||||||
|
@ -43,7 +43,7 @@ class WebhookSenderThread(Thread):
|
||||||
app.logger.info(str(js))
|
app.logger.info(str(js))
|
||||||
|
|
||||||
|
|
||||||
def remaining_minutes(value):
|
def remaining_minutes(value) -> str:
|
||||||
delta = value - datetime.now()
|
delta = value - datetime.now()
|
||||||
if delta.total_seconds() < 0:
|
if delta.total_seconds() < 0:
|
||||||
return "0"
|
return "0"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
def euro_string(value):
|
def euro_string(value: int) -> str:
|
||||||
"""
|
"""
|
||||||
Convert cents to string formatted euro
|
Convert cents to string formatted euro
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -8,7 +8,7 @@ debug_bp = Blueprint("debug_bp", __name__)
|
||||||
|
|
||||||
@debug_bp.route("/routes")
|
@debug_bp.route("/routes")
|
||||||
@login_required
|
@login_required
|
||||||
def list_routes():
|
def list_routes() -> str:
|
||||||
import urllib
|
import urllib
|
||||||
|
|
||||||
output = []
|
output = []
|
||||||
|
|
|
@ -7,7 +7,6 @@ from flask import render_template, send_from_directory, url_for
|
||||||
from flask_login import login_required
|
from flask_login import login_required
|
||||||
|
|
||||||
from models import Location, Order
|
from models import Location, Order
|
||||||
|
|
||||||
# import views
|
# import views
|
||||||
from views.order import get_orders
|
from views.order import get_orders
|
||||||
|
|
||||||
|
@ -15,7 +14,7 @@ general_bp = Blueprint("general_bp", __name__)
|
||||||
|
|
||||||
|
|
||||||
@general_bp.route("/")
|
@general_bp.route("/")
|
||||||
def home():
|
def home() -> str:
|
||||||
prev_day = datetime.now() - timedelta(days=1)
|
prev_day = datetime.now() - timedelta(days=1)
|
||||||
recently_closed = get_orders(
|
recently_closed = get_orders(
|
||||||
((Order.stoptime > prev_day) & (Order.stoptime < datetime.now()))
|
((Order.stoptime > prev_day) & (Order.stoptime < datetime.now()))
|
||||||
|
@ -27,19 +26,19 @@ def home():
|
||||||
|
|
||||||
@general_bp.route("/map", defaults={"id": None})
|
@general_bp.route("/map", defaults={"id": None})
|
||||||
@general_bp.route("/map/<int:id>")
|
@general_bp.route("/map/<int:id>")
|
||||||
def map(id):
|
def map(id) -> str:
|
||||||
locs = Location.query.order_by("name")
|
locs = Location.query.order_by("name")
|
||||||
return render_template("maps.html", locations=locs)
|
return render_template("maps.html", locations=locs)
|
||||||
|
|
||||||
|
|
||||||
@general_bp.route("/location")
|
@general_bp.route("/location")
|
||||||
def locations():
|
def locations() -> str:
|
||||||
locs = Location.query.order_by("name")
|
locs = Location.query.order_by("name")
|
||||||
return render_template("locations.html", locations=locs)
|
return render_template("locations.html", locations=locs)
|
||||||
|
|
||||||
|
|
||||||
@general_bp.route("/location/<int:id>")
|
@general_bp.route("/location/<int:id>")
|
||||||
def location(id):
|
def location(id) -> str:
|
||||||
loc = Location.query.filter(Location.id == id).first()
|
loc = Location.query.filter(Location.id == id).first()
|
||||||
if loc is None:
|
if loc is None:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
@ -47,27 +46,27 @@ def location(id):
|
||||||
|
|
||||||
|
|
||||||
@general_bp.route("/about/")
|
@general_bp.route("/about/")
|
||||||
def about():
|
def about() -> str:
|
||||||
return render_template("about.html")
|
return render_template("about.html")
|
||||||
|
|
||||||
|
|
||||||
@general_bp.route("/profile/")
|
@general_bp.route("/profile/")
|
||||||
@login_required
|
@login_required
|
||||||
def profile():
|
def profile() -> str:
|
||||||
return render_template("profile.html")
|
return render_template("profile.html")
|
||||||
|
|
||||||
|
|
||||||
@general_bp.route("/favicon.ico")
|
@general_bp.route("/favicon.ico")
|
||||||
def favicon():
|
def favicon() -> str:
|
||||||
if len(get_orders((Order.stoptime > datetime.now()))) == 0:
|
if len(get_orders((Order.stoptime > datetime.now()))) == 0:
|
||||||
return send_from_directory(
|
return send_from_directory(
|
||||||
os.path.join(app.root_path, "static"),
|
os.path.join(str(app.root_path), "static"),
|
||||||
"favicon.ico",
|
"favicon.ico",
|
||||||
mimetype="image/x-icon",
|
mimetype="image/x-icon",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return send_from_directory(
|
return send_from_directory(
|
||||||
os.path.join(app.root_path, "static"),
|
os.path.join(str(app.root_path), "static"),
|
||||||
"favicon_orange.ico",
|
"favicon_orange.ico",
|
||||||
mimetype="image/x-icon",
|
mimetype="image/x-icon",
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,18 +1,13 @@
|
||||||
import random
|
import random
|
||||||
|
import typing
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
import werkzeug
|
||||||
# from flask import current_app as app
|
# from flask import current_app as app
|
||||||
from flask import (
|
from flask import (Blueprint, abort, flash, redirect, render_template, request,
|
||||||
Blueprint,
|
session, url_for, wrappers)
|
||||||
abort,
|
|
||||||
flash,
|
|
||||||
redirect,
|
|
||||||
render_template,
|
|
||||||
request,
|
|
||||||
session,
|
|
||||||
url_for,
|
|
||||||
)
|
|
||||||
from flask_login import current_user, login_required
|
from flask_login import current_user, login_required
|
||||||
|
from werkzeug.wrappers import Response
|
||||||
|
|
||||||
from forms import AnonOrderItemForm, OrderForm, OrderItemForm
|
from forms import AnonOrderItemForm, OrderForm, OrderItemForm
|
||||||
from models import Order, OrderItem, User, db
|
from models import Order, OrderItem, User, db
|
||||||
|
@ -22,7 +17,7 @@ order_bp = Blueprint("order_bp", "order")
|
||||||
|
|
||||||
|
|
||||||
@order_bp.route("/")
|
@order_bp.route("/")
|
||||||
def orders(form=None):
|
def orders(form: OrderForm = None) -> str:
|
||||||
if form is None and not current_user.is_anonymous():
|
if form is None and not current_user.is_anonymous():
|
||||||
form = OrderForm()
|
form = OrderForm()
|
||||||
location_id = request.args.get("location_id")
|
location_id = request.args.get("location_id")
|
||||||
|
@ -34,7 +29,7 @@ def orders(form=None):
|
||||||
|
|
||||||
@order_bp.route("/create", methods=["POST"])
|
@order_bp.route("/create", methods=["POST"])
|
||||||
@login_required
|
@login_required
|
||||||
def order_create():
|
def order_create() -> typing.Union[str, Response]:
|
||||||
orderForm = OrderForm()
|
orderForm = OrderForm()
|
||||||
orderForm.populate()
|
orderForm.populate()
|
||||||
if orderForm.validate_on_submit():
|
if orderForm.validate_on_submit():
|
||||||
|
@ -48,7 +43,7 @@ def order_create():
|
||||||
|
|
||||||
|
|
||||||
@order_bp.route("/<id>")
|
@order_bp.route("/<id>")
|
||||||
def order(id, form=None):
|
def order(id: int, form: OrderForm = None) -> str:
|
||||||
order = Order.query.filter(Order.id == id).first()
|
order = Order.query.filter(Order.id == id).first()
|
||||||
if order is None:
|
if order is None:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
@ -68,7 +63,7 @@ def order(id, form=None):
|
||||||
|
|
||||||
|
|
||||||
@order_bp.route("/<id>/items")
|
@order_bp.route("/<id>/items")
|
||||||
def items_showcase(id, form=None):
|
def items_showcase(id: int, form: OrderForm = None) -> str:
|
||||||
order = Order.query.filter(Order.id == id).first()
|
order = Order.query.filter(Order.id == id).first()
|
||||||
if order is None:
|
if order is None:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
@ -80,7 +75,7 @@ def items_showcase(id, form=None):
|
||||||
|
|
||||||
@order_bp.route("/<id>/edit", methods=["GET", "POST"])
|
@order_bp.route("/<id>/edit", methods=["GET", "POST"])
|
||||||
@login_required
|
@login_required
|
||||||
def order_edit(id):
|
def order_edit(id: int) -> typing.Union[str, Response]:
|
||||||
order = Order.query.filter(Order.id == id).first()
|
order = Order.query.filter(Order.id == id).first()
|
||||||
if current_user.id is not order.courrier_id and not current_user.is_admin():
|
if current_user.id is not order.courrier_id and not current_user.is_admin():
|
||||||
abort(401)
|
abort(401)
|
||||||
|
@ -96,7 +91,9 @@ def order_edit(id):
|
||||||
|
|
||||||
|
|
||||||
@order_bp.route("/<id>/create", methods=["POST"])
|
@order_bp.route("/<id>/create", methods=["POST"])
|
||||||
def order_item_create(id):
|
def order_item_create(id: int) -> typing.Any:
|
||||||
|
# type is 'typing.Union[str, Response]', but this errors due to
|
||||||
|
# https://github.com/python/mypy/issues/7187
|
||||||
current_order = Order.query.filter(Order.id == id).first()
|
current_order = Order.query.filter(Order.id == id).first()
|
||||||
if current_order is None:
|
if current_order is None:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
@ -124,7 +121,7 @@ def order_item_create(id):
|
||||||
|
|
||||||
@order_bp.route("/<order_id>/<item_id>/paid")
|
@order_bp.route("/<order_id>/<item_id>/paid")
|
||||||
@login_required
|
@login_required
|
||||||
def item_paid(order_id, item_id):
|
def item_paid(order_id: int, item_id: int) -> typing.Optional[Response]:
|
||||||
item = OrderItem.query.filter(OrderItem.id == item_id).first()
|
item = OrderItem.query.filter(OrderItem.id == item_id).first()
|
||||||
id = current_user.id
|
id = current_user.id
|
||||||
if item.order.courrier_id == id or current_user.admin:
|
if item.order.courrier_id == id or current_user.admin:
|
||||||
|
@ -137,9 +134,9 @@ def item_paid(order_id, item_id):
|
||||||
|
|
||||||
@order_bp.route("/<order_id>/<user_name>/user_paid")
|
@order_bp.route("/<order_id>/<user_name>/user_paid")
|
||||||
@login_required
|
@login_required
|
||||||
def items_user_paid(order_id, user_name):
|
def items_user_paid(order_id: int, user_name: str) -> typing.Optional[Response]:
|
||||||
user = User.query.filter(User.username == user_name).first()
|
user = User.query.filter(User.username == user_name).first()
|
||||||
items = []
|
items: typing.List[OrderItem] = []
|
||||||
if user:
|
if user:
|
||||||
items = OrderItem.query.filter(
|
items = OrderItem.query.filter(
|
||||||
(OrderItem.user_id == user.id) & (OrderItem.order_id == order_id)
|
(OrderItem.user_id == user.id) & (OrderItem.order_id == order_id)
|
||||||
|
@ -155,13 +152,15 @@ def items_user_paid(order_id, user_name):
|
||||||
for item in items:
|
for item in items:
|
||||||
item.paid = True
|
item.paid = True
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash("Paid %d items for %s" % (items.count(), item.get_name()), "success")
|
flash("Paid %d items for %s" % (len(items), item.get_name()), "success")
|
||||||
return redirect(url_for("order_bp.order", id=order_id))
|
return redirect(url_for("order_bp.order", id=order_id))
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
|
||||||
@order_bp.route("/<order_id>/<item_id>/delete")
|
@order_bp.route("/<order_id>/<item_id>/delete")
|
||||||
def delete_item(order_id, item_id):
|
def delete_item(order_id: int, item_id: int) -> typing.Any:
|
||||||
|
# type is 'typing.Optional[Response]', but this errors due to
|
||||||
|
# https://github.com/python/mypy/issues/7187
|
||||||
item = OrderItem.query.filter(OrderItem.id == item_id).first()
|
item = OrderItem.query.filter(OrderItem.id == item_id).first()
|
||||||
id = None
|
id = None
|
||||||
if not current_user.is_anonymous():
|
if not current_user.is_anonymous():
|
||||||
|
@ -178,7 +177,7 @@ def delete_item(order_id, item_id):
|
||||||
|
|
||||||
@order_bp.route("/<id>/volunteer")
|
@order_bp.route("/<id>/volunteer")
|
||||||
@login_required
|
@login_required
|
||||||
def volunteer(id):
|
def volunteer(id: int) -> Response:
|
||||||
order = Order.query.filter(Order.id == id).first()
|
order = Order.query.filter(Order.id == id).first()
|
||||||
if order is None:
|
if order is None:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
@ -193,7 +192,7 @@ def volunteer(id):
|
||||||
|
|
||||||
@order_bp.route("/<id>/close")
|
@order_bp.route("/<id>/close")
|
||||||
@login_required
|
@login_required
|
||||||
def close_order(id):
|
def close_order(id: int) -> typing.Optional[Response]:
|
||||||
order = Order.query.filter(Order.id == id).first()
|
order = Order.query.filter(Order.id == id).first()
|
||||||
if order is None:
|
if order is None:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
@ -208,9 +207,13 @@ def close_order(id):
|
||||||
order.courrier_id = courrier.id
|
order.courrier_id = courrier.id
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return redirect(url_for("order_bp.order", id=id))
|
return redirect(url_for("order_bp.order", id=id))
|
||||||
|
# The line below is to make sure mypy doesn't say
|
||||||
|
# "Missing return statement"
|
||||||
|
# https://github.com/python/mypy/issues/4223
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def select_user(items):
|
def select_user(items) -> typing.Optional[User]:
|
||||||
user = None
|
user = None
|
||||||
# remove non users
|
# remove non users
|
||||||
items = [i for i in items if i.user_id]
|
items = [i for i in items if i.user_id]
|
||||||
|
@ -228,8 +231,8 @@ def select_user(items):
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
def get_orders(expression=None):
|
def get_orders(expression=None) -> typing.List[Order]:
|
||||||
orders = []
|
orders: typing.List[OrderForm] = []
|
||||||
if expression is None:
|
if expression is None:
|
||||||
expression = (datetime.now() > Order.starttime) & (
|
expression = (datetime.now() > Order.starttime) & (
|
||||||
Order.stoptime > datetime.now()
|
Order.stoptime > datetime.now()
|
||||||
|
|
|
@ -8,7 +8,7 @@ stats_blueprint = Blueprint("stats_blueprint", __name__)
|
||||||
|
|
||||||
|
|
||||||
@stats_blueprint.route("/")
|
@stats_blueprint.route("/")
|
||||||
def stats():
|
def stats() -> str:
|
||||||
data = {
|
data = {
|
||||||
"amount": {
|
"amount": {
|
||||||
"orders": FatOrder.amount(),
|
"orders": FatOrder.amount(),
|
||||||
|
|
16
app/zeus.py
16
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_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
|
from models import User, db
|
||||||
|
|
||||||
|
@ -14,7 +18,9 @@ def zeus_login():
|
||||||
|
|
||||||
|
|
||||||
@oauth_bp.route("/login/zeus/authorized")
|
@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()
|
resp = current_app.zeus.authorized_response()
|
||||||
if resp is None:
|
if resp is None:
|
||||||
return "Access denied: reason=%s error=%s" % (
|
return "Access denied: reason=%s error=%s" % (
|
||||||
|
@ -60,12 +66,12 @@ def init_oauth(app):
|
||||||
return zeus
|
return zeus
|
||||||
|
|
||||||
|
|
||||||
def login_and_redirect_user(user):
|
def login_and_redirect_user(user) -> Response:
|
||||||
login_user(user)
|
login_user(user)
|
||||||
return redirect(url_for("general_bp.home"))
|
return redirect(url_for("general_bp.home"))
|
||||||
|
|
||||||
|
|
||||||
def create_user(username):
|
def create_user(username) -> User:
|
||||||
user = User()
|
user = User()
|
||||||
user.configure(username, False, 1)
|
user.configure(username, False, 1)
|
||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
|
|
4
tests.md
Normal file
4
tests.md
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Tests
|
||||||
|
For this application we run a number of tests, at the moment the tests are:
|
||||||
|
- [mypy](http://mypy-lang.org/), type-checking, which you can run using `mypy` with the argument `--ignore-missing-imports` if it says modules cannot be found
|
||||||
|
|
Loading…
Reference in a new issue