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 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")

View File

@ -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 "<h1>Hello, World!</h>", 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)

View File

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

View File

@ -13,15 +13,15 @@
</head>
<body>
<main>
<div class="flex-shrink-0 p-3 bg-white" style="width: 280px;">
<a href="/" class="d-flex align-items-center pb-3 mb-3 link-dark text-decoration-none border-bottom">
<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 border-dark">
<svg class="bi me-2" width="30" height="24"><use xlink:href="#bootstrap"></use></svg>
<span class="fs-5 fw-semibold">Scan2Kasse</span>
</a>
<ul class="list-unstyled ps-0">
<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">
Home
Home (WIP)
</button>
<div class="collapse show" id="home-collapse">
<ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
@ -33,14 +33,16 @@
</li>
<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">
Dashboard
Übersicht
</button>
<div class="collapse" id="dashboard-collapse">
<ul class="btn-toggle-nav list-unstyled fw-normal pb-1 small">
<li><a href="#" class="link-dark rounded">Overview</a></li>
<li><a href="#" class="link-dark rounded">Weekly</a></li>
<li><a href="#" class="link-dark rounded">Monthly</a></li>
<li><a href="#" class="link-dark rounded">Annually</a></li>
{% if establishments %}
<li><a href="#" class="link-dark rounded">Allgemein</a></li>
{% for establishment in establishments %}
<li><a href="{{ url_for('get_report_from_user', establishment=establishment.id) }}" class="link-dark rounded">{{ establishment.name }}</a></li>
{% endfor %}
{% endif %}
</ul>
</div>
</li>
@ -57,17 +59,22 @@
</ul>
</div>
</li> -->
<li class="border-top my-3"></li>
<li class="border-top border-dark my-3"></li>
<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">
Account
</button>
<div class="collapse" id="account-collapse" style="">
<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">Profile</a></li>
<li><a href="#" class="link-dark rounded">Settings</a></li>
<li><a href="#" class="link-dark rounded">Sign out</a></li>
<!-- <li><a href="#" class="link-dark rounded">New...</a></li>
<li><a href="#" class="link-dark rounded">Settings</a></li> -->
{% if current_user.is_authenticated %}
<!-- <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>
</div>
</li>
@ -82,12 +89,13 @@
</ul>
{% endif %}
{% endwith %}
<div style="width: 280px;"></div>
<div class="container">
<div class="row-md-3">
{% block content %}{% endblock %}
</div>
</div>
</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>
<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>

View File

@ -3,11 +3,11 @@
{% block content %}
{% for user in results %}
<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">
<h3>{{ user.username }}: {{ user.sum/100 }} €</h3>
</div>
</a>
</button>
<div class="collapse" id="b{{ user.id }}">
{% for item_infos in user.item_infos %}
<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 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)

View File

@ -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: