Merge pull request #183 from ZeusWPI/fix/pylint

Fix pylint in haldis
This commit is contained in:
Maxime 2022-04-19 22:32:35 +02:00 committed by GitHub
commit 461664f629
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 278 additions and 233 deletions

View file

@ -5,6 +5,8 @@
# run arbitrary code.
extension-pkg-whitelist=
fail-under=9
# Add files or directories to the blacklist. They should be base names, not
# paths.
ignore=CVS
@ -28,7 +30,7 @@ limit-inference-results=100
# List of plugins (as comma separated values of python modules names) to load,
# usually to register additional checkers.
load-plugins=
load-plugins=pylint_flask_sqlalchemy,pylint_flask
# Pickle collected data for later comparisons.
persistent=yes
@ -60,7 +62,7 @@ confidence=
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use "--disable=all --enable=classes
# --disable=W".
disable=E0401,E0611,C0103,W0511,W0611
disable=E0401,E0611,C0103,W0511,W0611,C0415
# Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option

0
app/__init__.py Normal file
View file

View file

@ -1,7 +1,7 @@
"""Script for adding users as admin to Haldis."""
from models import User
from app import db
from models import User
def add() -> None:

View file

@ -3,7 +3,6 @@ from flask import Flask
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from flask_sqlalchemy import SQLAlchemy
from models import Order, OrderItem, OrderItemChoice, User
@ -26,8 +25,10 @@ class OrderAdminModel(ModelBaseView):
column_default_sort = ("starttime", True)
column_list = ["starttime", "stoptime", "location_name", "location_id", "courier"]
column_labels = {
"starttime": "Start Time", "stoptime": "Closing Time",
"location_id": "HLDS Location ID"}
"starttime": "Start Time",
"stoptime": "Closing Time",
"location_id": "HLDS Location ID",
}
form_excluded_columns = ["items", "courier_id"]
can_delete = False
@ -36,13 +37,25 @@ class OrderItemAdminModel(ModelBaseView):
# pylint: disable=too-few-public-methods
column_default_sort = ("order_id", True)
column_list = [
"order_id", "order.location_name", "user_name", "user", "dish_name", "dish_id", "comment", "price", "paid",
"hlds_data_version"
"order_id",
"order.location_name",
"user_name",
"user",
"dish_name",
"dish_id",
"comment",
"price",
"paid",
"hlds_data_version",
]
column_labels = {
"order_id": "Order", "order.location_name": "Order's Location",
"user_name": "Anon. User", "user_id": "Registered User",
"hlds_data_version": "HLDS Data Version", "dish_id": "HLDS Dish ID"}
"order_id": "Order",
"order.location_name": "Order's Location",
"user_name": "Anon. User",
"user_id": "Registered User",
"hlds_data_version": "HLDS Data Version",
"dish_id": "HLDS Dish ID",
}
def init_admin(app: Flask, database: SQLAlchemy) -> None:

View file

@ -3,10 +3,11 @@
"""Main Haldis script"""
import logging
from logging.handlers import TimedRotatingFileHandler
import typing
from datetime import datetime
from logging.handlers import TimedRotatingFileHandler
from admin import init_admin
from flask import Flask, render_template
from flask_bootstrap import Bootstrap, StaticCDN
from flask_debugtoolbar import DebugToolbarExtension
@ -14,10 +15,8 @@ from flask_login import LoginManager
from flask_migrate import Migrate, MigrateCommand
from flask_oauthlib.client import OAuth, OAuthException
from flask_script import Manager, Server
from markupsafe import Markup
from admin import init_admin
from login import init_login
from markupsafe import Markup
from models import db
from models.anonymous_user import AnonymouseUser
from utils import euro_string, price_range_string
@ -69,7 +68,8 @@ def register_plugins(app: Flask) -> Manager:
# Make cookies more secure
app.config.update(
SESSION_COOKIE_HTTPONLY=True, SESSION_COOKIE_SAMESITE="Lax",
SESSION_COOKIE_HTTPONLY=True,
SESSION_COOKIE_SAMESITE="Lax",
)
if not app.debug:
@ -95,11 +95,11 @@ def add_routes(application: Flask) -> None:
# import views # TODO convert to blueprint
# import views.stats # TODO convert to blueprint
from views.order import order_bp
from views.general import general_bp
from views.stats import stats_blueprint
from views.debug import debug_bp
from login import auth_bp
from views.debug import debug_bp
from views.general import general_bp
from views.order import order_bp
from views.stats import stats_blueprint
from zeus import oauth_bp
application.register_blueprint(general_bp, url_prefix="/")

View file

@ -2,7 +2,7 @@
import add_admins
from app import db, app_manager
from app import app_manager, db
entry_sets = {
"admins": add_admins.add,
@ -27,7 +27,7 @@ def check_if_overwrite() -> bool:
def add_all() -> None:
"Add all possible entries in the entry_sets to the database"
for entry_set, function in entry_sets.items():
print("Adding {}.".format(entry_set))
print(f"Adding {entry_set}.")
function()
@ -45,14 +45,14 @@ def add_to_current() -> None:
def add_numbers() -> str:
return " ".join(
["{}({}), ".format(loc, i) for i, loc in enumerate(available)]
[f"{loc}({i}), " for i, loc in enumerate(available)]
).rstrip(", ")
while input("Do you still want to add something? (Y/n) ").lower() not in no:
print(
"What do you want to add? (Use numbers, or A for all, or C for cancel) "
)
answer = input("Available: {} : ".format(add_numbers()))
answer = input(f"Available: {add_numbers()} : ")
if answer.lower() == "a":
add_all()
available = []
@ -60,7 +60,7 @@ def add_to_current() -> None:
pass
elif answer.isnumeric() and answer in [str(x) for x in range(len(available))]:
answer_index = int(answer)
print("Adding {}.".format(available[answer_index]))
print(f"Adding {available[answer_index]}.")
entry_sets[str(available[answer_index])]()
del available[answer_index]
else:

View file

@ -1,10 +1,9 @@
import typing
from sqlalchemy.sql import desc, func
from hlds.definitions import location_definitions
from hlds.models import Location, Dish
from hlds.models import Dish, Location
from models import Order, OrderItem, User
from sqlalchemy.sql import desc, func
class FatModel:

View file

@ -1,25 +1,16 @@
"Script for everything form related in Haldis"
from datetime import datetime, timedelta
from typing import Optional
from flask import session, request
from flask import request, session
from flask_login import current_user
from flask_wtf import FlaskForm as Form
from wtforms import (
DateTimeField,
SelectField,
SelectMultipleField,
StringField,
SubmitField,
FieldList,
validators,
)
from utils import euro_string, price_range_string
from hlds.definitions import location_definitions
from hlds.models import Location, Dish, Choice
from hlds.models import Choice, Dish, Location
from models import User
from utils import euro_string, price_range_string
from wtforms import (DateTimeField, FieldList, SelectField,
SelectMultipleField, StringField, SubmitField, validators)
class OrderForm(Form):

View file

@ -6,4 +6,4 @@ These are not imported in this module's init, to avoid opening the definition fi
parser on them when testing other code in this module, or when testing the parser on other files.
"""
from .models import Location, Choice, Option
from .models import Choice, Location, Option

View file

@ -1,11 +1,11 @@
# Import this class to load the standard HLDS definitions
import subprocess
from os import path
from typing import List
import subprocess
from .parser import parse_all_directory
from .models import Location
from .models import Location
from .parser import parse_all_directory
__all__ = ["location_definitions", "location_definition_version"]

View file

@ -1,26 +1,28 @@
#!/usr/bin/env python3
# pylint: disable=too-few-public-methods
from typing import Iterable, List, Tuple, Mapping, Any, Optional
from typing import Any, Iterable, List, Mapping, Optional, Tuple
from utils import euro_string, first
def _format_tags(tags: Iterable[str]) -> str:
return " :: {}".format(" ".join(["{" + tag + "}" for tag in tags])) \
if tags \
else ""
# pylint: disable=consider-using-f-string
return " :: {}".format(" ".join(["{" + tag + "}"
for tag in tags])) if tags else ""
def _format_price(price: int) -> str:
return " {}".format(euro_string(price)) if price else ""
return f" {euro_string(price)}" if price else ""
def _format_type_and_choice(type_and_choice):
type_, choice = type_and_choice
return "{} {}".format(type_, choice)
return f"{type_} {choice}"
class Option:
def __init__(self, id_, *, name, description, price, tags):
self.id: str = id_
self.name: str = name
@ -29,15 +31,17 @@ class Option:
self.tags: List[str] = tags
def __str__(self):
# pylint: disable=consider-using-f-string
return "{0.id}: {0.name}{1}{2}{3}".format(
self,
" -- {}".format(self.description) if self.description else "",
f" -- {self.description}" if self.description else "",
_format_tags(self.tags),
_format_price(self.price),
)
class Choice:
def __init__(self, id_, *, name, description, options):
self.id: str = id_
self.name: str = name
@ -48,7 +52,7 @@ class Choice:
def __str__(self):
return "{0.id}: {0.name}{1}\n\t\t{2}".format(
self,
" -- {}".format(self.description) if self.description else "",
f" -- {self.description}" if self.description else "",
"\n\t\t".join(map(str, self.options)),
)
@ -57,6 +61,7 @@ class Choice:
class Dish:
def __init__(self, id_, *, name, description, price, tags, choices):
self.id: str = id_
self.name: str = name
@ -70,7 +75,7 @@ class Dish:
def __str__(self):
return "dish {0.id}: {0.name}{1}{2}{3}\n\t{4}".format(
self,
" -- {}".format(self.description) if self.description else "",
f" -- {self.description}" if self.description else "",
_format_tags(self.tags),
_format_price(self.price),
"\n\t".join(map(_format_type_and_choice, self.choices)),
@ -86,14 +91,20 @@ class Dish:
return sum(
f(option.price for option in choice.options)
for (choice_type, choice) in self.choices
if choice_type == "single_choice"
)
if choice_type == "single_choice")
class Location:
def __init__(
self, id_, *, name, dishes, osm=None, address=None, telephone=None, website=None
):
def __init__(self,
id_,
*,
name,
dishes,
osm=None,
address=None,
telephone=None,
website=None):
self.id: str = id_
self.name: str = name
self.osm: Optional[str] = osm
@ -107,24 +118,18 @@ class Location:
return first(filter(lambda d: d.id == dish_id, self.dishes))
def __str__(self):
return (
"============================\n"
"{0.id}: {0.name}"
"{1}\n"
"============================\n"
"\n"
"{2}"
).format(
self,
"".join(
"\n\t{} {}".format(k, v)
for k, v in (
("osm", self.osm),
("address", self.address),
("telephone", self.telephone),
("website", self.website),
return ("============================\n"
"{0.id}: {0.name}"
"{1}\n"
"============================\n"
"\n"
"{2}").format(
self,
"".join(f"\n\t{k} {v}" for k, v in (
("osm", self.osm),
("address", self.address),
("telephone", self.telephone),
("website", self.website),
) if v is not None),
"\n".join(map(str, self.dishes)),
)
if v is not None
),
"\n".join(map(str, self.dishes)),
)

View file

@ -1,16 +1,17 @@
#!/usr/bin/env python3
from glob import glob
from os import path
import itertools
from copy import deepcopy
from typing import Iterable, List, Union, Tuple
from glob import glob
from os import path
from typing import Iterable, List, Tuple, Union
from tatsu import parse as tatsu_parse
from tatsu.ast import AST
from tatsu.exceptions import SemanticError
from .models import Location, Choice, Option, Dish
from utils import first
from .models import Choice, Dish, Location, Option
# TODO Use proper way to get resources, see https://stackoverflow.com/a/10935674
with open(path.join(path.dirname(__file__), "hlds.tatsu")) as fh:
@ -58,14 +59,16 @@ class HldsSemanticActions:
option.price += dish.price
dish.price = 0
dishes = list(dishes)
dishes.append(Dish(
"custom",
name="Vrije keuze",
description="Zet wat je wil in comment",
price=0,
tags=[],
choices=[],
))
dishes.append(
Dish(
"custom",
name="Vrije keuze",
description="Zet wat je wil in comment",
price=0,
tags=[],
choices=[],
)
)
attributes = {att["key"]: att["value"] for att in ast["attributes"]}
@ -145,7 +148,7 @@ def parse(menu: str) -> List[Location]:
def parse_file(filename: str) -> List[Location]:
with open(filename, "r") as file_handle:
with open(filename) as file_handle:
return parse(file_handle.read())

View file

@ -1,9 +1,8 @@
"Script for everything related to logging in and out"
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 werkzeug.wrappers import Response
from zeus import zeus_login
auth_bp = Blueprint("auth_bp", __name__)

View file

@ -1,10 +1,8 @@
"Script that runs migrations online or offline"
from __future__ import with_statement
from logging.config import fileConfig
from alembic import context
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel

View file

@ -12,11 +12,11 @@ revision = "9159a6fed021"
down_revision = "150252c1cdb1"
from itertools import chain
from alembic import op
import sqlalchemy as sa
from sqlalchemy.sql import table, column, text
import sqlalchemy as sa
from alembic import op
from hlds.definitions import location_definitions
from sqlalchemy.sql import column, table, text
LOCATION_LEGACY_TO_HLDS = {
2: "blauw_kotje",
@ -50,71 +50,106 @@ LOCATION_LEGACY_TO_HLDS = {
def upgrade():
# First the simple actions
op.create_table("order_item_choice",
op.create_table(
"order_item_choice",
sa.Column("id", sa.Integer, nullable=False),
sa.Column("choice_id", sa.String(length=64), nullable=True),
sa.Column("order_item_id", sa.Integer, nullable=False),
sa.Column("kind", sa.String(length=1), nullable=False),
sa.Column("name", sa.String(length=120), nullable=True),
sa.Column("value", sa.String(length=120), nullable=True),
sa.ForeignKeyConstraint(["order_item_id"], ["order_item.id"], ),
sa.PrimaryKeyConstraint("id")
sa.ForeignKeyConstraint(
["order_item_id"],
["order_item.id"],
),
sa.PrimaryKeyConstraint("id"),
)
op.add_column(
"order_item",
sa.Column("hlds_data_version", sa.String(length=40), nullable=True),
)
op.alter_column(
"order", "courrier_id", new_column_name="courier_id", type_=sa.Integer
)
op.alter_column(
"order_item",
"extra",
new_column_name="comment",
existing_type=sa.String(254),
type_=sa.Text,
)
op.alter_column(
"order_item", "name", new_column_name="user_name", type_=sa.String(120)
)
op.add_column("order_item", sa.Column("hlds_data_version", sa.String(length=40), nullable=True))
op.alter_column("order", "courrier_id", new_column_name="courier_id", type_=sa.Integer)
op.alter_column("order_item", "extra", new_column_name="comment",
existing_type=sa.String(254), type_=sa.Text)
op.alter_column("order_item", "name", new_column_name="user_name", type_=sa.String(120))
#----------------------------------------------------------------------------------------------
# ----------------------------------------------------------------------------------------------
# Migrate historical product data to order items
# First create the new columns we will populate
op.add_column("order_item", sa.Column("dish_id", sa.String(length=64), nullable=True))
op.add_column("order_item", sa.Column("dish_name", sa.String(length=120), nullable=True))
op.add_column(
"order_item", sa.Column("dish_id", sa.String(length=64), nullable=True)
)
op.add_column(
"order_item", sa.Column("dish_name", sa.String(length=120), nullable=True)
)
op.add_column("order_item", sa.Column("price", sa.Integer(), nullable=True))
# Brief, ad-hoc table constructs just for our UPDATE statement, see
# https://alembic.sqlalchemy.org/en/latest/ops.html#alembic.operations.Operations.execute
order_item = table("order_item",
order_item = table(
"order_item",
column("product_id", sa.Integer),
column("dish_id", sa.String),
column("dish_name", sa.String),
column("price", sa.Integer)
column("price", sa.Integer),
)
# Construct and execute queries
op.execute(text("""
op.execute(
text(
"""
UPDATE order_item
SET dish_name = (SELECT product.name FROM product WHERE product.id = order_item.product_id),
price = (SELECT product.price FROM product WHERE product.id = order_item.product_id)"""
))
)
)
# Historical product data migrated, drop obsolete column and table
op.execute(text("ALTER TABLE order_item DROP FOREIGN KEY order_item_ibfk_3"))
op.drop_column("order_item", "product_id")
op.drop_table("product")
#----------------------------------------------------------------------------------------------
# ----------------------------------------------------------------------------------------------
# Migrate historical location data to orders
op.execute(text("ALTER TABLE `order` DROP FOREIGN KEY order_ibfk_2"))
op.alter_column("order", "location_id", new_column_name="legacy_location_id",
type_=sa.Integer, nullable=True)
op.add_column("order", sa.Column("location_id", sa.String(length=64), nullable=True))
op.add_column("order", sa.Column("location_name", sa.String(length=128), nullable=True))
op.alter_column(
"order",
"location_id",
new_column_name="legacy_location_id",
type_=sa.Integer,
nullable=True,
)
op.add_column(
"order", sa.Column("location_id", sa.String(length=64), nullable=True)
)
op.add_column(
"order", sa.Column("location_name", sa.String(length=128), nullable=True)
)
# Brief, ad-hoc table constructs just for our UPDATE statement, see
# https://alembic.sqlalchemy.org/en/latest/ops.html#alembic.operations.Operations.execute
order = table("order",
order = table(
"order",
column("legacy_location_id", sa.Integer),
column("location_id", sa.String),
column("location_name", sa.String)
column("location_name", sa.String),
)
# Construct and execute queries
new_location_id = [
order.update()
.where(order.c.legacy_location_id == old_id)
.values(location_id=new_id)
.where(order.c.legacy_location_id == old_id)
.values(location_id=new_id)
for old_id, new_id in LOCATION_LEGACY_TO_HLDS.items()
]
location_name_from_location = text("""
location_name_from_location = text(
"""
UPDATE `order`
SET location_name = (SELECT location.name FROM location
WHERE location.id = `order`.legacy_location_id)"""

View file

@ -1,10 +1,11 @@
"Script for everything Order related in the database"
import typing
from datetime import datetime
from collections import defaultdict
from datetime import datetime
from utils import first
from hlds.definitions import location_definitions
from utils import first
from .database import db
from .user import User
@ -31,9 +32,9 @@ class Order(db.Model):
def __repr__(self) -> str:
# pylint: disable=R1705
if self.location:
return "Order %d @ %s" % (self.id, self.location.name or "None")
return f"Order {self.id} @ {self.location.name or 'None'}"
else:
return "Order %d" % (self.id)
return f"Order {self.id}"
def update_from_hlds(self) -> None:
"""
@ -46,19 +47,21 @@ class Order(db.Model):
self.location_name = self.location.name
def for_user(self, anon=None, user=None) -> typing.List:
"Get the items for a certain user"
return list(
filter(
(lambda i: i.user == user)
if user is not None
else (lambda i: i.user_name == anon),
self.items
self.items,
)
)
def group_by_user(self) -> typing.List[typing.Tuple[str, typing.List]]:
"Group items of an Order by user"
group: typing.Dict[str, typing.List] = dict()
group: typing.Dict[str, typing.List] = {}
# pylint: disable=E1133
for item in self.items:
if item.for_name not in group:
group[item.for_name] = []
@ -70,12 +73,17 @@ class Order(db.Model):
return list(sorted(group.items(), key=lambda t: (t[0] or "", t[1] or "")))
def group_by_dish(self) \
-> typing.List[typing.Tuple[str, int, typing.List[typing.Tuple[str, typing.List]]]]:
def group_by_dish(
self,
) -> typing.List[
typing.Tuple[str, int, typing.List[typing.Tuple[str, typing.List]]]
]:
"Group items of an Order by dish"
group: typing.Dict[str, typing.Dict[str, typing.List]] = \
defaultdict(lambda: defaultdict(list))
group: typing.Dict[str, typing.Dict[str, typing.List]] = defaultdict(
lambda: defaultdict(list)
)
# pylint: disable=E1133
for item in self.items:
group[item.dish_name][item.comment].append(item)
@ -87,12 +95,13 @@ class Order(db.Model):
sorted(
(comment, sorted(items, key=lambda x: (x.for_name or "")))
for comment, items in comment_group.items()
)
),
)
for dish_name, comment_group in group.items()
)
def is_closed(self) -> bool:
"Return whether or not the order is closed"
return self.stoptime and datetime.now() > self.stoptime
def can_close(self, user_id: int) -> bool:

View file

@ -1,8 +1,9 @@
"Script for everything OrderItem related in the database"
from datetime import datetime
from utils import first
from hlds.definitions import location_definitions
from utils import first
from .database import db
from .order import Order
from .user import User
@ -21,20 +22,20 @@ class OrderItem(db.Model):
comment = db.Column(db.Text(), nullable=True)
hlds_data_version = db.Column(db.String(40), nullable=True)
choices = db.relationship("OrderItemChoice", backref="order_item", lazy="dynamic")
choices = db.relationship("OrderItemChoice",
backref="order_item",
lazy="dynamic")
def __getattr__(self, name):
if name == "dish":
location_id = (
Order.query.filter(Order.id == self.order_id).first().location_id
)
location_id = (Order.query.filter(
Order.id == self.order_id).first().location_id)
location = first(
filter(lambda l: l.id == location_id, location_definitions)
)
filter(lambda l: l.id == location_id, location_definitions))
if location:
return first(filter(lambda d: d.id == self.dish_id, location.dishes))
else:
raise ValueError("No Location found with id: " + location_id)
return first(
filter(lambda d: d.id == self.dish_id, location.dishes))
raise ValueError(f"No Location found with id: {location_id}")
raise AttributeError()
@property
@ -45,11 +46,7 @@ class OrderItem(db.Model):
return self.user_name
def __repr__(self) -> str:
return "Order %d: %s wants %s" % (
self.order_id or 0,
self.for_name,
self.dish_name or "None",
)
return "Order {self.order_id or 0}: {self.for_name} wants {self.dish_name or 'None'}"
def update_from_hlds(self) -> None:
"""

View file

@ -1,3 +1,4 @@
"Script for everything OrderItemChoice related in the database"
from datetime import datetime
from .database import db
@ -5,6 +6,7 @@ from .orderitem import OrderItem
class OrderItemChoice(db.Model):
"Class used for configuring the OrderItemChoice model in the database"
id = db.Column(db.Integer, primary_key=True)
choice_id = db.Column(db.String(64), nullable=True)
order_item_id = db.Column(
@ -16,7 +18,8 @@ class OrderItemChoice(db.Model):
# pylint: disable=attribute-defined-outside-init
def configure(self, order: OrderItem) -> None:
"Set the orderitem"
self.order = order
def __repr__(self) -> str:
return "{}: {}".format(self.name, self.value)
return f"{self.name}: {self.value}"

View file

@ -39,4 +39,4 @@ class User(db.Model):
return str(self.id)
def __repr__(self) -> str:
return "%s" % self.username
return f"{self.username}"

View file

@ -16,7 +16,7 @@ def webhook_text(order: Order) -> typing.Optional[str]:
return None
if order.courier is not None:
# pylint: disable=C0301
# pylint: disable=C0301, C0209
return "<!channel|@channel> {3} is going to {1}, order <{0}|here>! Deadline in {2} minutes!".format(
url_for("order_bp.order_from_id", order_id=order.id, _external=True),
order.location_name,
@ -24,6 +24,7 @@ def webhook_text(order: Order) -> typing.Optional[str]:
order.courier.username.title(),
)
# pylint: disable=C0209
return "<!channel|@channel> New order for {}. Deadline in {} minutes. <{}|Open here.>".format(
order.location_name,
remaining_minutes(order.stoptime),
@ -43,7 +44,7 @@ class WebhookSenderThread(Thread):
"Extension of the Thread class, which sends a webhook for the notification"
def __init__(self, message: str, url: str) -> None:
super(WebhookSenderThread, self).__init__()
super().__init__()
self.message = message
self.url = url
@ -64,4 +65,4 @@ def remaining_minutes(value) -> str:
if delta.total_seconds() < 0:
return "0"
minutes = delta.total_seconds() // 60
return "%02d" % minutes
return f"{minutes:02}"

View file

@ -1,9 +1,7 @@
#!/usr/bin/env python3
from tatsu.util import asjson
from hlds.parser import parse_files
USAGE = """{0} [filename]...
Parse HLDS files, print as JSON

View file

@ -9,9 +9,9 @@ def euro_string(value: int) -> str:
"""
euro, cents = divmod(value, 100)
if cents:
return "{}.{:02}".format(euro, cents)
return f"{euro}.{cents:02}"
else:
return "{}".format(euro)
return f"{euro}"
def price_range_string(price_range, include_upper=False):

View file

@ -17,12 +17,12 @@ def list_routes() -> str:
for rule in app.url_map.iter_rules():
options = {}
for arg in rule.arguments:
options[arg] = "[{0}]".format(arg)
options[arg] = f"[{arg}]"
print(rule.endpoint)
methods = ",".join(rule.methods)
url = url_for(rule.endpoint, **options)
line = urllib.parse.unquote(
"{:50s} {:20s} {}".format(rule.endpoint, methods, url)
f"{rule.endpoint:50s} {methods:20s} {url}"
)
output.append(line)

View file

@ -1,32 +1,26 @@
"Script to generate the general views of Haldis"
import json
import os
from datetime import datetime, timedelta
import yaml
from typing import Optional
from flask import Flask, render_template, make_response
from flask import request, jsonify
from flask import Blueprint, abort
import yaml
from flask import Blueprint, Flask, abort
from flask import current_app as app
from flask import send_from_directory, url_for
from flask import (jsonify, make_response, render_template, request,
send_from_directory, url_for)
from flask_login import login_required
from utils import first
from hlds.definitions import location_definitions
from hlds.models import Location
from models import Order
from utils import first
# import views
from views.order import get_orders
import json
from flask import jsonify
general_bp = Blueprint("general_bp", __name__)
with open(os.path.join(os.path.dirname(__file__), "themes.yml"), "r") as _stream:
with open(os.path.join(os.path.dirname(__file__), "themes.yml")) as _stream:
_theme_data = yaml.safe_load(_stream)
THEME_OPTIONS = _theme_data["options"]
THEMES = _theme_data["themes"]
@ -37,7 +31,7 @@ def home() -> str:
"Generate the home view"
prev_day = datetime.now() - timedelta(days=1)
recently_closed = get_orders(
((Order.stoptime > prev_day) & (Order.stoptime < datetime.now()))
(Order.stoptime > prev_day) & (Order.stoptime < datetime.now())
)
return render_template(
"home.html", orders=get_orders(), recently_closed=recently_closed
@ -60,7 +54,7 @@ def is_theme_active(theme, now):
return start_datetime <= now <= end_datetime
raise Exception("Unknown theme type {}".format(theme_type))
raise Exception(f"Unknown theme type {theme_type}")
def get_theme_css(theme, options):
@ -71,13 +65,18 @@ def get_theme_css(theme, options):
for option in theme.get("options", []):
theme_name = theme["name"]
assert option in THEME_OPTIONS, f"Theme `{theme_name}` uses undefined option `{option}`"
assert (
option in THEME_OPTIONS
), f"Theme `{theme_name}` uses undefined option `{option}`"
chosen_value = options[option]
possible_values = list(THEME_OPTIONS[option].keys())
value = chosen_value if chosen_value in possible_values \
value = (
chosen_value
if chosen_value in possible_values
else THEME_OPTIONS[option]["_default"]
)
filename += "_" + value
@ -119,13 +118,15 @@ def current_theme_js():
themes = get_active_themes()
selected_theme_name = request.cookies.get("theme", None)
matching_theme = first((t for t in themes if t["file"] == selected_theme_name))
matching_theme = first(t for t in themes if t["file"] == selected_theme_name)
cur_theme = matching_theme or themes[-1]
response = make_response(rf'''
response = make_response(
rf"""
var currentTheme = {json.dumps(cur_theme['file'])};
var currentThemeOptions = {json.dumps(cur_theme.get('options', []))};
''')
"""
)
response.headers["Content-Type"] = "text/javascript"
# Theme name that is not valid at this moment: delete cookie
@ -166,25 +167,27 @@ def location_dish(location_id, dish_id) -> str:
dish = loc.dish_by_id(dish_id)
if dish is None:
abort(404)
return jsonify([
{
"type": c[0],
"id": c[1].id,
"name": c[1].name,
"description": c[1].description,
"options": [
{
"id": o.id,
"name": o.name,
"description": o.description,
"price": o.price,
"tags": o.tags,
}
for o in c[1].options
],
}
for c in dish.choices
])
return jsonify(
[
{
"type": c[0],
"id": c[1].id,
"name": c[1].name,
"description": c[1].description,
"options": [
{
"id": o.id,
"name": o.name,
"description": o.description,
"price": o.price,
"tags": o.tags,
}
for o in c[1].options
],
}
for c in dish.choices
]
)
@general_bp.route("/about/")
@ -204,7 +207,7 @@ def profile() -> str:
def favicon() -> str:
"Generate the favicon"
# pylint: disable=R1705
if not get_orders((Order.stoptime > datetime.now())):
if not get_orders(Order.stoptime > datetime.now()):
return send_from_directory(
os.path.join(app.root_path, "static"),
"favicon.ico",

View file

@ -3,27 +3,16 @@ import random
import typing
from datetime import datetime
from werkzeug.wrappers import Response
# from flask import current_app as app
from flask import (
Blueprint,
abort,
flash,
redirect,
render_template,
request,
session,
url_for,
wrappers,
)
from flask import (Blueprint, abort, flash, redirect, render_template, request,
session, url_for, wrappers)
from flask_login import current_user, login_required
from forms import AnonOrderItemForm, OrderForm, OrderItemForm
from hlds.definitions import location_definition_version, location_definitions
from models import Order, OrderItem, User, db
from hlds.definitions import location_definitions, location_definition_version
from notification import post_order_to_webhook
from utils import ignore_none
from werkzeug.wrappers import Response
order_bp = Blueprint("order_bp", "order")
@ -72,8 +61,8 @@ def order_from_id(order_id: int, form: OrderForm = None, dish_id=None) -> str:
form.populate(order.location)
if order.is_closed():
form = None
total_price = sum([o.price for o in order.items])
debts = sum([o.price for o in order.items if not o.paid])
total_price = sum(o.price for o in order.items)
debts = sum(o.price for o in order.items if not o.paid)
dish = order.location.dish_by_id(dish_id) if order.location else None
@ -96,7 +85,7 @@ def items_shop_view(order_id: int) -> str:
if current_user.is_anonymous() and not order.public:
flash("Please login to see this order.", "info")
abort(401)
total_price = sum([o.price for o in order.items])
total_price = sum(o.price for o in order.items)
return render_template("order_items.html", order=order, total_price=total_price)
@ -138,7 +127,9 @@ def order_item_create(order_id: int) -> typing.Any:
abort(404)
form = AnonOrderItemForm() if current_user.is_anonymous() else OrderItemForm()
dish_id = request.form["dish_id"] if form.is_submitted() else request.args.get("dish")
dish_id = (
request.form["dish_id"] if form.is_submitted() else request.args.get("dish")
)
if dish_id and not location.dish_by_id(dish_id):
abort(404)
if not form.is_submitted():
@ -347,6 +338,6 @@ def get_orders(expression=None) -> typing.List[Order]:
else:
order_list = Order.query.filter(
# pylint: disable=C0121
(expression & (Order.public == True))
expression & (Order.public == True)
).all()
return order_list

View file

@ -1,10 +1,9 @@
"Script to generate the stats related views of Haldis"
from fatmodels import FatLocation, FatOrder, FatOrderItem, FatUser
from flask import Blueprint
from flask import current_app as app
from flask import render_template
from fatmodels import FatLocation, FatOrder, FatOrderItem, FatUser
stats_blueprint = Blueprint("stats_blueprint", __name__)

View file

@ -1,12 +1,12 @@
"Script containing everything specific to ZeusWPI"
import typing
from flask import Blueprint, current_app, flash, redirect, request, session, url_for
from flask import (Blueprint, current_app, flash, redirect, request, session,
url_for)
from flask_login import login_user
from flask_oauthlib.client import OAuth, OAuthException
from werkzeug.wrappers import Response
from models import User, db
from werkzeug.wrappers import Response
oauth_bp = Blueprint("oauth_bp", __name__)
@ -14,8 +14,7 @@ oauth_bp = Blueprint("oauth_bp", __name__)
def zeus_login():
"Log in using ZeusWPI"
return current_app.zeus.authorize(
callback=url_for("oauth_bp.authorized", _external=True)
)
callback=url_for("oauth_bp.authorized", _external=True))
@oauth_bp.route("/login/zeus/authorized")
@ -25,10 +24,8 @@ def authorized() -> typing.Any:
"Check authorized status"
resp = current_app.zeus.authorized_response()
if resp is None:
return "Access denied: reason=%s error=%s" % (
request.args["error"],
request.args["error_description"],
)
# pylint: disable=C0301
return f"Access denied: reason={request.args['error']} error={request.args['error_description']}"
if isinstance(resp, OAuthException):
return f"Access denied: {resp.message}<br>{resp.data}"

2
pylint-requirement.txt Normal file
View file

@ -0,0 +1,2 @@
pylint-flask
pylint-flask-sqlalchemy