diff --git a/app/forms.py b/app/forms.py index 12dd95e..f6fce45 100644 --- a/app/forms.py +++ b/app/forms.py @@ -1,7 +1,7 @@ -from app.models import Brand, Category +from app.models import Brand, Category, User from flask_wtf import FlaskForm from wtforms import StringField, PasswordField, BooleanField, SubmitField, SelectMultipleField, DateField, IntegerField, SelectField, FloatField -from wtforms.validators import DataRequired, Optional +from wtforms.validators import DataRequired, Email, EqualTo, Optional, ValidationError class LoginForm(FlaskForm): username = StringField('Username', validators=[DataRequired()]) @@ -9,6 +9,24 @@ class LoginForm(FlaskForm): remember_me = BooleanField('Remember Me') submit = SubmitField('Sign In') +class RegistrationForm(FlaskForm): + username = StringField('Username', validators=[DataRequired()]) + email = StringField('Email', validators=[DataRequired(), Email()]) + password = PasswordField('Password', validators=[DataRequired()]) + password2 = PasswordField( + 'Repeat Password', validators=[DataRequired(), EqualTo('password')]) + submit = SubmitField('Register') + + def validate_username(self, username): + user = User.query.filter_by(username=username.data).first() + if user is not None: + raise ValidationError('Please use a different username.') + + def validate_email(self, email): + user = User.query.filter_by(email=email.data).first() + if user is not None: + raise ValidationError('Please use a different email address.') + class NewItemForm(FlaskForm): id = IntegerField("Product EAN", validators=[DataRequired()]) name = StringField("Name", validators=[DataRequired()]) @@ -16,10 +34,17 @@ class NewItemForm(FlaskForm): date = DateField("Insert Date", validators=[DataRequired()]) price_change = FloatField("Price", validators=[DataRequired()]) amount_change = IntegerField("Amount", validators=[Optional()]) - category = SelectMultipleField("Categories", choices=[(c.id, c.name) for c in Category.query.order_by("name").all()], validators=[Optional()]) - brand = SelectField("Brand", choices=[(b.id, b.name) for b in Brand.query.order_by("name").all()], validators=[DataRequired()]) + category = SelectMultipleField("Categories", choices=[], validators=[Optional()]) + brand = SelectField("Brand", choices=[], validators=[DataRequired()]) submit = SubmitField("Submit") + @classmethod + def new(cls): + form = cls() + form.category.choices = [(c.id, c.name) for c in Category.query.order_by("name").all()] + form.brand.choices = [(b.id, b.name) for b in Brand.query.order_by("name").all()] + return form + class NewCategoryForm(FlaskForm): name = StringField("Name", validators=[DataRequired()]) submit = SubmitField("Submit") diff --git a/app/routes.py b/app/routes.py index 5316d20..52fc5b9 100644 --- a/app/routes.py +++ b/app/routes.py @@ -1,7 +1,8 @@ from app import app, db, LOGGER -from app.forms import NewItemForm, LoginForm -from app.models import LoginToken, User, Item, Brand, Category, PriceChange, AmountChange -from app.utils import view_utils, database_utils, routes_utils +from app.forms import NewItemForm, LoginForm, RegistrationForm +from app.models import LoginToken, User, Item, Brand, PriceChange, AmountChange +from app.utils import view_utils, database_utils +from app.utils.routes_utils import render_custom_template as render_template from datetime import date from flask import abort, flash, redirect, request, url_for from flask.json import jsonify @@ -10,11 +11,9 @@ from werkzeug.urls import url_parse APPNAME = "scan2kasse" -render_template = routes_utils.render_custom_template - -@app.route('/') +@app.route(f'/{APPNAME}') def index(): - return "

Hello, World!", 200 + return render_template("base.html") @app.route('/test') def test(): @@ -23,7 +22,6 @@ def test(): form = NewItemForm() return render_template("test.html", form=form) - @app.route(f'/{APPNAME}/token_authorization') def token_authorization(): LOGGER.debug("Token Login") @@ -33,7 +31,6 @@ def token_authorization(): abort(403) return jsonify({}), 200 - @app.route(f'/{APPNAME}/token_insert', methods=['POST']) def insert(): match request.json: @@ -47,6 +44,19 @@ def insert(): return jsonify(failed), 400 return jsonify({'inserted': True}), 201 +@app.route('/register', methods=['GET', 'POST']) +def web_register(): + if current_user.is_authenticated: + return redirect(url_for('index')) + form = RegistrationForm() + if form.validate_on_submit(): + user = User(username=form.username.data, email=form.email.data) + user.set_password(form.password.data) + db.session.add(user) + db.session.commit() + flash('Congratulations, you are now a registered user!') + return redirect(url_for('login')) + return render_template('register.html', title='Register', form=form) @app.route(f'/{APPNAME}/login', methods=['GET', 'POST']) def web_login(): @@ -65,18 +75,23 @@ def web_login(): return redirect(next_page) return render_template('login.html', title='Sign In', form=form) +@app.route(f'/{APPNAME}/logout') +def web_logout(): + logout_user() + return redirect(url_for('index')) @app.route(f'/{APPNAME}/newitem', methods=['GET', 'POST']) @login_required def new_item(): if current_user.is_anonymous: abort(403) - form=NewItemForm() + form=NewItemForm.new() if form.is_submitted(): LOGGER.debug("submitted") if form.validate(): LOGGER.debug("valid") - LOGGER.debug(form.errors) + else: + LOGGER.debug(form.errors) if form.validate_on_submit(): LOGGER.debug("valid form") brand = Brand.query.get(form.brand.data) @@ -93,7 +108,10 @@ def new_item(): return render_template('admin/new_item.html', form=form) @app.route(f'/{APPNAME}/overview', methods=['GET']) +@login_required def get_report_from_user(): + if current_user.is_anonymous: + abort(403) if 'month' in request.args: try: month = int(request.args['month']) @@ -104,7 +122,7 @@ def get_report_from_user(): if (month > 12 or month < 1): abort(400) LOGGER.info("Getting results.") - results = database_utils.get_report(kwargs = request.args) + results = database_utils.get_report(**request.args) LOGGER.debug(f"Results received: {results}") if results: result_list = view_utils.group_results(results) diff --git a/app/static/sidebars.css b/app/static/sidebars.css index 6949a37..f641a90 100644 --- a/app/static/sidebars.css +++ b/app/static/sidebars.css @@ -14,7 +14,7 @@ main { height: -webkit-fill-available; max-height: 100vh; overflow-x: auto; - overflow-y: hidden; + overflow-y: auto; } .b-example-divider { diff --git a/app/templates/base.html b/app/templates/base.html index 44054ec..3e2969d 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -13,15 +13,15 @@
-
- +
+ Scan2Kasse {% endif %} {% endwith %} +
{% block content %}{% endblock %}
- + \ No newline at end of file diff --git a/app/templates/overview.html b/app/templates/overview.html index e919b90..9b03689 100644 --- a/app/templates/overview.html +++ b/app/templates/overview.html @@ -3,11 +3,11 @@ {% block content %} {% for user in results %}
-
{% for item_infos in user.item_infos %}
diff --git a/app/templates/register.html b/app/templates/register.html new file mode 100644 index 0000000..62e1b24 --- /dev/null +++ b/app/templates/register.html @@ -0,0 +1,37 @@ +{% extends "base.html" %} + +{% block content %} +

Register

+
+ {{ form.hidden_tag() }} +

+ {{ form.username.label }}
+ {{ form.username(size=32) }}
+ {% for error in form.username.errors %} + [{{ error }}] + {% endfor %} +

+

+ {{ form.email.label }}
+ {{ form.email(size=64) }}
+ {% for error in form.email.errors %} + [{{ error }}] + {% endfor %} +

+

+ {{ form.password.label }}
+ {{ form.password(size=32) }}
+ {% for error in form.password.errors %} + [{{ error }}] + {% endfor %} +

+

+ {{ form.password2.label }}
+ {{ form.password2(size=32) }}
+ {% for error in form.password2.errors %} + [{{ error }}] + {% endfor %} +

+

{{ form.submit() }}

+
+{% endblock %} \ No newline at end of file diff --git a/app/utils/database_utils.py b/app/utils/database_utils.py index 102c9ec..df33edd 100644 --- a/app/utils/database_utils.py +++ b/app/utils/database_utils.py @@ -3,6 +3,7 @@ from app.models import Bought, Item, LoginToken, User from app.utils.view_utils import bought_with_prices as bwp from copy import deepcopy from datetime import date as dtdate, timedelta +from flask_login import current_user from psycopg2 import errors from random import choice as rndchoice from sqlalchemy import text @@ -30,15 +31,27 @@ def insert_bought_items(token: str, items: dict, date: str = None): def get_report(**kwargs): query_select = db.session.query(bwp.c.token, User.username, bwp.c.date, bwp.c.item, Item.name, bwp.c.amount, bwp.c.price) query_select = query_select.select_from(bwp).join(LoginToken, LoginToken.token==bwp.c.token).join(User, LoginToken.user==User.id).join(Item, Item.id==bwp.c.item) - if "user" in kwargs: - query_select = query_select.where(bwp.c.token == kwargs['user']) + match kwargs: + case {"token": token}: + LOGGER.debug("Token present") + query_select = query_select.filter_by(token == token) + case {"establishment": establishment}: + LOGGER.debug("Establishment present") + query_select = query_select.filter( + bwp.c.token.in_( + # db.session.query(LoginToken.token).filter_by(establishment = int(establishment), user=current_user.id))) + db.session.query(LoginToken.token).filter_by(establishment = int(establishment)))) + LOGGER.debug(str(query_select)) match kwargs: case {"month": month}: + LOGGER.debug("Month present") year = kwargs["year"] if "year" in kwargs else dtdate.today().year - query_select = query_select.where(bwp.c.date.between(dtdate(int(year), int(month), 1), dtdate(int(year), int(month)+1, 1)-timedelta(days=1))) + query_select = query_select.filter(bwp.c.date.between(dtdate(int(year), int(month), 1), dtdate(int(year), int(month)+1, 1)-timedelta(days=1))) case {"year": year}: - query_select = query_select.where(bwp.c.date.between(dtdate(int(year), 1, 1), dtdate(int(year), 12, 31))) + LOGGER.debug("Year present") + query_select = query_select.filter(bwp.c.date.between(dtdate(int(year), 1, 1), dtdate(int(year), 12, 31))) query_select = query_select.order_by(bwp.c.token, bwp.c.date, bwp.c.item) + LOGGER.debug(str(query_select)) results = query_select.all() return tuple(results) diff --git a/app/utils/view_utils.py b/app/utils/view_utils.py index d18bfe4..5978068 100644 --- a/app/utils/view_utils.py +++ b/app/utils/view_utils.py @@ -12,7 +12,7 @@ def group_results(results: tuple) -> list: try: result_user_index = [result[0] == result_item['id'] for result_item in result_list].index(True) except ValueError as e: - result_list.append({"id": result[0], "name": result[1], "sum": 0, "item_infos": []}) + result_list.append({"id": result[0], "username": result[1], "sum": 0, "item_infos": []}) result_user_index = -1 result_user = result_list[result_user_index] try: