several fixes, mainly page design
This commit is contained in:
parent
7f59f20b78
commit
fa798a5da1
33
app/forms.py
33
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")
|
||||
|
||||
@ -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,17 +75,22 @@ 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")
|
||||
else:
|
||||
LOGGER.debug(form.errors)
|
||||
if form.validate_on_submit():
|
||||
LOGGER.debug("valid form")
|
||||
@ -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)
|
||||
|
||||
@ -14,7 +14,7 @@ main {
|
||||
height: -webkit-fill-available;
|
||||
max-height: 100vh;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.b-example-divider {
|
||||
|
||||
@ -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>
|
||||
@ -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">
|
||||
|
||||
37
app/templates/register.html
Normal file
37
app/templates/register.html
Normal 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 %}
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user