several fixes, mainly page design

This commit is contained in:
Lunaresk 2022-03-10 12:32:50 +01:00
parent 7f59f20b78
commit fa798a5da1
8 changed files with 139 additions and 38 deletions

View File

@ -1,7 +1,7 @@
from app.models import Brand, Category from app.models import Brand, Category, User
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField, SelectMultipleField, DateField, IntegerField, SelectField, FloatField 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): class LoginForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()]) username = StringField('Username', validators=[DataRequired()])
@ -9,6 +9,24 @@ class LoginForm(FlaskForm):
remember_me = BooleanField('Remember Me') remember_me = BooleanField('Remember Me')
submit = SubmitField('Sign In') 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): class NewItemForm(FlaskForm):
id = IntegerField("Product EAN", validators=[DataRequired()]) id = IntegerField("Product EAN", validators=[DataRequired()])
name = StringField("Name", validators=[DataRequired()]) name = StringField("Name", validators=[DataRequired()])
@ -16,10 +34,17 @@ class NewItemForm(FlaskForm):
date = DateField("Insert Date", validators=[DataRequired()]) date = DateField("Insert Date", validators=[DataRequired()])
price_change = FloatField("Price", validators=[DataRequired()]) price_change = FloatField("Price", validators=[DataRequired()])
amount_change = IntegerField("Amount", validators=[Optional()]) 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()]) category = SelectMultipleField("Categories", choices=[], validators=[Optional()])
brand = SelectField("Brand", choices=[(b.id, b.name) for b in Brand.query.order_by("name").all()], validators=[DataRequired()]) brand = SelectField("Brand", choices=[], validators=[DataRequired()])
submit = SubmitField("Submit") 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): class NewCategoryForm(FlaskForm):
name = StringField("Name", validators=[DataRequired()]) name = StringField("Name", validators=[DataRequired()])
submit = SubmitField("Submit") submit = SubmitField("Submit")

View File

@ -1,7 +1,8 @@
from app import app, db, LOGGER from app import app, db, LOGGER
from app.forms import NewItemForm, LoginForm from app.forms import NewItemForm, LoginForm, RegistrationForm
from app.models import LoginToken, User, Item, Brand, Category, PriceChange, AmountChange from app.models import LoginToken, User, Item, Brand, PriceChange, AmountChange
from app.utils import view_utils, database_utils, routes_utils 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 datetime import date
from flask import abort, flash, redirect, request, url_for from flask import abort, flash, redirect, request, url_for
from flask.json import jsonify from flask.json import jsonify
@ -10,11 +11,9 @@ from werkzeug.urls import url_parse
APPNAME = "scan2kasse" APPNAME = "scan2kasse"
render_template = routes_utils.render_custom_template @app.route(f'/{APPNAME}')
@app.route('/')
def index(): def index():
return "<h1>Hello, World!</h>", 200 return render_template("base.html")
@app.route('/test') @app.route('/test')
def test(): def test():
@ -23,7 +22,6 @@ def test():
form = NewItemForm() form = NewItemForm()
return render_template("test.html", form=form) return render_template("test.html", form=form)
@app.route(f'/{APPNAME}/token_authorization') @app.route(f'/{APPNAME}/token_authorization')
def token_authorization(): def token_authorization():
LOGGER.debug("Token Login") LOGGER.debug("Token Login")
@ -33,7 +31,6 @@ def token_authorization():
abort(403) abort(403)
return jsonify({}), 200 return jsonify({}), 200
@app.route(f'/{APPNAME}/token_insert', methods=['POST']) @app.route(f'/{APPNAME}/token_insert', methods=['POST'])
def insert(): def insert():
match request.json: match request.json:
@ -47,6 +44,19 @@ def insert():
return jsonify(failed), 400 return jsonify(failed), 400
return jsonify({'inserted': True}), 201 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']) @app.route(f'/{APPNAME}/login', methods=['GET', 'POST'])
def web_login(): def web_login():
@ -65,18 +75,23 @@ def web_login():
return redirect(next_page) return redirect(next_page)
return render_template('login.html', title='Sign In', form=form) 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']) @app.route(f'/{APPNAME}/newitem', methods=['GET', 'POST'])
@login_required @login_required
def new_item(): def new_item():
if current_user.is_anonymous: if current_user.is_anonymous:
abort(403) abort(403)
form=NewItemForm() form=NewItemForm.new()
if form.is_submitted(): if form.is_submitted():
LOGGER.debug("submitted") LOGGER.debug("submitted")
if form.validate(): if form.validate():
LOGGER.debug("valid") LOGGER.debug("valid")
LOGGER.debug(form.errors) else:
LOGGER.debug(form.errors)
if form.validate_on_submit(): if form.validate_on_submit():
LOGGER.debug("valid form") LOGGER.debug("valid form")
brand = Brand.query.get(form.brand.data) brand = Brand.query.get(form.brand.data)
@ -93,7 +108,10 @@ def new_item():
return render_template('admin/new_item.html', form=form) return render_template('admin/new_item.html', form=form)
@app.route(f'/{APPNAME}/overview', methods=['GET']) @app.route(f'/{APPNAME}/overview', methods=['GET'])
@login_required
def get_report_from_user(): def get_report_from_user():
if current_user.is_anonymous:
abort(403)
if 'month' in request.args: if 'month' in request.args:
try: try:
month = int(request.args['month']) month = int(request.args['month'])
@ -104,7 +122,7 @@ def get_report_from_user():
if (month > 12 or month < 1): if (month > 12 or month < 1):
abort(400) abort(400)
LOGGER.info("Getting results.") 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}") LOGGER.debug(f"Results received: {results}")
if results: if results:
result_list = view_utils.group_results(results) result_list = view_utils.group_results(results)

View File

@ -14,7 +14,7 @@ main {
height: -webkit-fill-available; height: -webkit-fill-available;
max-height: 100vh; max-height: 100vh;
overflow-x: auto; overflow-x: auto;
overflow-y: hidden; overflow-y: auto;
} }
.b-example-divider { .b-example-divider {

View File

@ -13,15 +13,15 @@
</head> </head>
<body> <body>
<main> <main>
<div class="flex-shrink-0 p-3 bg-white" style="width: 280px;"> <div class="flex-shrink-0 p-3 bg-black bg-opacity-10 scrollbar-primary h-100 position-fixed" style="width: 280px;">
<a href="/" class="d-flex align-items-center pb-3 mb-3 link-dark text-decoration-none border-bottom"> <a href="/" class="d-flex align-items-center pb-3 mb-3 link-dark text-decoration-none border-bottom border-dark">
<svg class="bi me-2" width="30" height="24"><use xlink:href="#bootstrap"></use></svg> <svg class="bi me-2" width="30" height="24"><use xlink:href="#bootstrap"></use></svg>
<span class="fs-5 fw-semibold">Scan2Kasse</span> <span class="fs-5 fw-semibold">Scan2Kasse</span>
</a> </a>
<ul class="list-unstyled ps-0"> <ul class="list-unstyled ps-0">
<li class="mb-1"> <li class="mb-1">
<button class="btn btn-toggle align-items-center rounded collapsed" data-bs-toggle="collapse" data-bs-target="#home-collapse" aria-expanded="true"> <button class="btn btn-toggle align-items-center rounded collapsed" data-bs-toggle="collapse" data-bs-target="#home-collapse" aria-expanded="true">
Home Home (WIP)
</button> </button>
<div class="collapse show" id="home-collapse"> <div class="collapse show" id="home-collapse">
<ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small"> <ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
@ -33,14 +33,16 @@
</li> </li>
<li class="mb-1"> <li class="mb-1">
<button class="btn btn-toggle align-items-center rounded collapsed" data-bs-toggle="collapse" data-bs-target="#dashboard-collapse" aria-expanded="false"> <button class="btn btn-toggle align-items-center rounded collapsed" data-bs-toggle="collapse" data-bs-target="#dashboard-collapse" aria-expanded="false">
Dashboard Übersicht
</button> </button>
<div class="collapse" id="dashboard-collapse"> <div class="collapse" id="dashboard-collapse">
<ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small"> <ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
<li><a href="#" class="link-dark rounded">Overview</a></li> {% if establishments %}
<li><a href="#" class="link-dark rounded">Weekly</a></li> <li><a href="#" class="link-dark rounded">Allgemein</a></li>
<li><a href="#" class="link-dark rounded">Monthly</a></li> {% for establishment in establishments %}
<li><a href="#" class="link-dark rounded">Annually</a></li> <li><a href="{{ url_for('get_report_from_user', establishment=establishment.id) }}" class="link-dark rounded">{{ establishment.name }}</a></li>
{% endfor %}
{% endif %}
</ul> </ul>
</div> </div>
</li> </li>
@ -57,17 +59,22 @@
</ul> </ul>
</div> </div>
</li> --> </li> -->
<li class="border-top my-3"></li> <li class="border-top border-dark my-3"></li>
<li class="mb-1"> <li class="mb-1">
<button class="btn btn-toggle align-items-center rounded collapsed" data-bs-toggle="collapse" data-bs-target="#account-collapse" aria-expanded="false"> <button class="btn btn-toggle align-items-center rounded collapsed" data-bs-toggle="collapse" data-bs-target="#account-collapse" aria-expanded="false">
Account Account
</button> </button>
<div class="collapse" id="account-collapse" style=""> <div class="collapse" id="account-collapse" style="">
<ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small"> <ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
<li><a href="#" class="link-dark rounded">New...</a></li> <!-- <li><a href="#" class="link-dark rounded">New...</a></li>
<li><a href="#" class="link-dark rounded">Profile</a></li> <li><a href="#" class="link-dark rounded">Settings</a></li> -->
<li><a href="#" class="link-dark rounded">Settings</a></li> {% if current_user.is_authenticated %}
<li><a href="#" class="link-dark rounded">Sign out</a></li> <!-- <li><a href="#" class="link-dark rounded">Profile</a></li> -->
<li><a href={{ url_for('web_logout') }} class="link-dark rounded">Sign out</a></li>
{% else %}
<li><a href={{ url_for('web_register') }} class="link-dark rounded">Register</a></li>
<li><a href={{ url_for('web_login') }} class="link-dark rounded">Sign in</a></li>
{% endif %}
</ul> </ul>
</div> </div>
</li> </li>
@ -82,12 +89,13 @@
</ul> </ul>
{% endif %} {% endif %}
{% endwith %} {% endwith %}
<div style="width: 280px;"></div>
<div class="container"> <div class="container">
<div class="row-md-3"> <div class="row-md-3">
{% block content %}{% endblock %} {% block content %}{% endblock %}
</div> </div>
</div> </div>
</main> </main>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
</body> </body>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
</html> </html>

View File

@ -3,11 +3,11 @@
{% block content %} {% block content %}
{% for user in results %} {% for user in results %}
<div class="card"> <div class="card">
<a class="btn btn-primary" data-bs-toggle="collapse" href="#b{{ user.id }}" role="button" aria-expanded="false" aria-controls="b{{ user.id }}"> <button class="btn btn-primary" data-bs-toggle="collapse" data-bs-target="#b{{ user.id }}" aria-expanded="true">
<div class="card-header"> <div class="card-header">
<h3>{{ user.username }}: {{ user.sum/100 }} €</h3> <h3>{{ user.username }}: {{ user.sum/100 }} €</h3>
</div> </div>
</a> </button>
<div class="collapse" id="b{{ user.id }}"> <div class="collapse" id="b{{ user.id }}">
{% for item_infos in user.item_infos %} {% for item_infos in user.item_infos %}
<div class="card-body"> <div class="card-body">

View File

@ -0,0 +1,37 @@
{% extends "base.html" %}
{% block content %}
<h1>Register</h1>
<form action="" method="post">
{{ form.hidden_tag() }}
<p>
{{ form.username.label }}<br>
{{ form.username(size=32) }}<br>
{% for error in form.username.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.email.label }}<br>
{{ form.email(size=64) }}<br>
{% for error in form.email.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.password.label }}<br>
{{ form.password(size=32) }}<br>
{% for error in form.password.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>
{{ form.password2.label }}<br>
{{ form.password2(size=32) }}<br>
{% for error in form.password2.errors %}
<span style="color: red;">[{{ error }}]</span>
{% endfor %}
</p>
<p>{{ form.submit() }}</p>
</form>
{% endblock %}

View File

@ -3,6 +3,7 @@ from app.models import Bought, Item, LoginToken, User
from app.utils.view_utils import bought_with_prices as bwp from app.utils.view_utils import bought_with_prices as bwp
from copy import deepcopy from copy import deepcopy
from datetime import date as dtdate, timedelta from datetime import date as dtdate, timedelta
from flask_login import current_user
from psycopg2 import errors from psycopg2 import errors
from random import choice as rndchoice from random import choice as rndchoice
from sqlalchemy import text from sqlalchemy import text
@ -30,15 +31,27 @@ def insert_bought_items(token: str, items: dict, date: str = None):
def get_report(**kwargs): 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 = 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) 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: match kwargs:
query_select = query_select.where(bwp.c.token == kwargs['user']) 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: match kwargs:
case {"month": month}: case {"month": month}:
LOGGER.debug("Month present")
year = kwargs["year"] if "year" in kwargs else dtdate.today().year 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}: 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) query_select = query_select.order_by(bwp.c.token, bwp.c.date, bwp.c.item)
LOGGER.debug(str(query_select))
results = query_select.all() results = query_select.all()
return tuple(results) return tuple(results)

View File

@ -12,7 +12,7 @@ def group_results(results: tuple) -> list:
try: try:
result_user_index = [result[0] == result_item['id'] for result_item in result_list].index(True) result_user_index = [result[0] == result_item['id'] for result_item in result_list].index(True)
except ValueError as e: 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_index = -1
result_user = result_list[result_user_index] result_user = result_list[result_user_index]
try: try: