From bbe318f9953e147a764b390dcb46dd1a4e344e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Tue, 23 Sep 2025 10:25:11 +0200 Subject: [PATCH] zmiany ux i kodowe --- app.py | 114 ++++++++++++++++++------- templates/admin/dodaj_wplate.html | 25 ++++-- templates/admin/dodaj_wydatek.html | 30 +++++-- templates/admin/edytuj_stan.html | 25 ++++-- templates/admin/formularz_zbiorek.html | 43 +++++++--- templates/admin/transakcje.html | 2 +- templates/admin/ustawienia.html | 2 +- templates/base.html | 6 +- templates/index.html | 19 ++++- templates/login.html | 2 +- templates/register.html | 2 +- templates/zbiorka.html | 6 +- 12 files changed, 205 insertions(+), 71 deletions(-) diff --git a/app.py b/app.py index 75a609c..fc5b073 100644 --- a/app.py +++ b/app.py @@ -1,3 +1,7 @@ +import markdown as md +import hashlib, os +import re +import socket from flask import Flask, render_template, request, redirect, url_for, flash from flask_sqlalchemy import SQLAlchemy from flask_login import ( @@ -11,29 +15,42 @@ from flask_login import ( from werkzeug.security import generate_password_hash, check_password_hash from datetime import datetime, timezone from markupsafe import Markup -from sqlalchemy import event, Numeric +from sqlalchemy import event, Numeric, select from sqlalchemy.engine import Engine from decimal import Decimal, InvalidOperation -import markdown as md from flask import request, flash, abort -import os -import re -import socket +try: + from zoneinfo import ZoneInfo # Python 3.9+ +except ImportError: + from backports.zoneinfo import ZoneInfo + + +def build_fingerprint(paths): + h = hashlib.sha256() + for base in paths: + if not os.path.exists(base): + continue + for root, _, files in os.walk(base): + for f in sorted(files): + p = os.path.join(root, f) + try: + with open(p, "rb") as fh: + h.update(fh.read()) + except Exception: + continue + return h.hexdigest()[:8] + +APP_VERSION = f"{datetime.now():%Y.%m.%d}+{build_fingerprint(['templates','static','app.py'])}" app = Flask(__name__) +app.config['APP_VERSION'] = APP_VERSION + # Ładujemy konfigurację z pliku config.py app.config.from_object("config.Config") db = SQLAlchemy(app) login_manager = LoginManager(app) login_manager.login_view = "zaloguj" - - -try: - from zoneinfo import ZoneInfo # Python 3.9+ -except ImportError: - from backports.zoneinfo import ZoneInfo - LOCAL_TZ = ZoneInfo("Europe/Warsaw") @@ -143,7 +160,7 @@ class GlobalSettings(db.Model): @login_manager.user_loader def load_user(user_id): - return User.query.get(int(user_id)) + return db.session.get(User, int(user_id)) @event.listens_for(Engine, "connect") @@ -235,6 +252,11 @@ def inject_globals(): } +@app.context_processor +def inject_version(): + return {'APP_VERSION': app.config['APP_VERSION']} + + # TRASY PUBLICZNE @app.route("/") def index(): @@ -255,7 +277,9 @@ def page_not_found(e): @app.route("/zbiorka/") def zbiorka(zbiorka_id): - zb = Zbiorka.query.get_or_404(zbiorka_id) + zb = db.session.get(Zbiorka, zbiorka_id) + if zb is None: + abort(404) if zb.ukryta and (not current_user.is_authenticated or not current_user.is_admin): abort(404) @@ -364,7 +388,9 @@ def formularz_zbiorek(zbiorka_id=None): # Tryb is_edit = zbiorka_id is not None - zb = Zbiorka.query.get_or_404(zbiorka_id) if is_edit else None + zb = db.session.get(Zbiorka, zbiorka_id) + if zb is None: + abort(404) global_settings = GlobalSettings.query.first() if request.method == "POST": @@ -498,7 +524,9 @@ def dodaj_wplate(zbiorka_id): flash("Brak uprawnień", "danger") return redirect(url_for("index")) - zb = Zbiorka.query.get_or_404(zbiorka_id) + zb = db.session.get(Zbiorka, zbiorka_id) if is_edit else None + if is_edit and not zb: + abort(404) if request.method == "POST": try: @@ -527,7 +555,9 @@ def usun_zbiorka(zbiorka_id): if not current_user.is_admin: flash("Brak uprawnień", "danger") return redirect(url_for("index")) - zb = Zbiorka.query.get_or_404(zbiorka_id) + zb = db.session.get(Zbiorka, zbiorka_id) + if zb is None: + abort(404) db.session.delete(zb) db.session.commit() flash("Zbiórka została usunięta", "success") @@ -540,7 +570,9 @@ def edytuj_stan(zbiorka_id): if not current_user.is_admin: flash("Brak uprawnień", "danger") return redirect(url_for("index")) - zb = Zbiorka.query.get_or_404(zbiorka_id) + zb = db.session.get(Zbiorka, zbiorka_id) + if zb is None: + abort(404) if request.method == "POST": try: nowy_stan = Decimal(request.form.get("stan", "").replace(",", ".")) @@ -560,7 +592,9 @@ def zmien_widzialnosc(zbiorka_id): if not current_user.is_admin: flash("Brak uprawnień", "danger") return redirect(url_for("index")) - zb = Zbiorka.query.get_or_404(zbiorka_id) + zb = db.session.get(Zbiorka, zbiorka_id) + if zb is None: + abort(404) zb.ukryta = not zb.ukryta db.session.commit() flash("Zbiórka została " + ("ukryta" if zb.ukryta else "przywrócona"), "success") @@ -579,7 +613,6 @@ def create_admin_account(): @app.after_request def apply_headers(response): - # --- STATIC: jak wcześniej --- if request.path.startswith("/static/"): response.headers.pop("Content-Disposition", None) response.headers["Vary"] = "Accept-Encoding" @@ -616,7 +649,7 @@ def apply_headers(response): response.headers.pop("Vary", None) else: response.headers["Vary"] = "Cookie, Accept-Encoding" - default_cache = app.config.get("CACHE_CONTROL_HEADER") or "private, max-age=0" + default_cache = app.config.get("CACHE_CONTROL_HEADER") or "private, no-store" response.headers["Cache-Control"] = default_cache if ( @@ -694,7 +727,9 @@ def dodaj_wydatek(zbiorka_id): flash("Brak uprawnień", "danger") return redirect(url_for("index")) - zb = Zbiorka.query.get_or_404(zbiorka_id) + zb = db.session.get(Zbiorka, zbiorka_id) + if zb is None: + abort(404) if request.method == "POST": try: @@ -734,7 +769,9 @@ def oznacz_zbiorka(zbiorka_id): flash("Brak uprawnień do wykonania tej operacji", "danger") return redirect(url_for("index")) - zb = Zbiorka.query.get_or_404(zbiorka_id) + zb = db.session.get(Zbiorka, zbiorka_id) + if zb is None: + abort(404) if "niezrealizowana" in request.path: zb.zrealizowana = False @@ -762,7 +799,9 @@ def robots(): def transakcje_zbiorki(zbiorka_id): if not current_user.is_admin: flash("Brak uprawnień", "danger"); return redirect(url_for("index")) - zb = Zbiorka.query.get_or_404(zbiorka_id) + zb = db.session.get(Zbiorka, zbiorka_id) + if zb is None: + abort(404) aktywnosci = ( [{"typ": "wpłata", "id": w.id, "kwota": w.kwota, "opis": w.opis, "data": w.data} for w in zb.wplaty] + [{"typ": "wydatek","id": x.id, "kwota": x.kwota,"opis": x.opis,"data": x.data} for x in zb.wydatki] @@ -776,7 +815,9 @@ def transakcje_zbiorki(zbiorka_id): def zapisz_wplate(wplata_id): if not current_user.is_admin: flash("Brak uprawnień", "danger"); return redirect(url_for("index")) - w = Wplata.query.get_or_404(wplata_id) + w = db.session.get(Wplata, wplata_id) + if w is None: + abort(404) zb = w.zbiorka try: nowa_kwota = Decimal(request.form.get("kwota", "").replace(",", ".")) @@ -800,7 +841,9 @@ def zapisz_wplate(wplata_id): def usun_wplate(wplata_id): if not current_user.is_admin: flash("Brak uprawnień", "danger"); return redirect(url_for("index")) - w = Wplata.query.get_or_404(wplata_id) + w = db.session.get(Wplata, wplata_id) + if w is None: + abort(404) zb = w.zbiorka zb.stan -= w.kwota db.session.delete(w) @@ -814,7 +857,9 @@ def usun_wplate(wplata_id): def zapisz_wydatek(wydatek_id): if not current_user.is_admin: flash("Brak uprawnień", "danger"); return redirect(url_for("index")) - x = Wydatek.query.get_or_404(wydatek_id) + x = db.session.get(Wydatek, wydatek_id) + if x is None: + abort(404) zb = x.zbiorka try: nowa_kwota = Decimal(request.form.get("kwota", "").replace(",", ".")) @@ -839,7 +884,9 @@ def zapisz_wydatek(wydatek_id): def usun_wydatek(wydatek_id): if not current_user.is_admin: flash("Brak uprawnień", "danger"); return redirect(url_for("index")) - x = Wydatek.query.get_or_404(wydatek_id) + x = db.session.get(Wydatek, wydatek_id) + if x is None: + abort(404) zb = x.zbiorka zb.stan += x.kwota db.session.delete(x) @@ -867,9 +914,16 @@ if __name__ == "__main__": with app.app_context(): db.create_all() # Tworzenie konta głównego admina, jeśli nie istnieje - if not User.query.filter_by(is_admin=True).first(): - main_admin = User(username=app.config["MAIN_ADMIN_USERNAME"], is_admin=True) + stmt = select(User).filter_by(is_admin=True) + admin = db.session.execute(stmt).scalars().first() + + if not admin: + main_admin = User( + username=app.config["MAIN_ADMIN_USERNAME"], + is_admin=True + ) main_admin.set_password(app.config["MAIN_ADMIN_PASSWORD"]) db.session.add(main_admin) db.session.commit() + app.run(debug=True) diff --git a/templates/admin/dodaj_wplate.html b/templates/admin/dodaj_wplate.html index 3352df1..3e1162f 100644 --- a/templates/admin/dodaj_wplate.html +++ b/templates/admin/dodaj_wplate.html @@ -14,11 +14,26 @@

Dodaj wpłatę: {{ zbiorka.nazwa }}

{% if zbiorka.cel %} - Cel: {{ zbiorka.cel|round(2) }} - PLN + + Cel: {{ zbiorka.cel|round(2) }} PLN + + {% endif %} + + Stan: {{ zbiorka.stan|round(2) }} PLN + + + {% if zbiorka.cel and zbiorka.cel > 0 %} + {% set delta = zbiorka.cel - zbiorka.stan %} + {% if delta > 0 %} + + Brakuje: {{ delta|round(2) }} PLN + + {% else %} + + Nadwyżka: {{ (-delta)|round(2) }} PLN + + {% endif %} {% endif %} - Stan: {{ zbiorka.stan|round(2) }} - PLN
@@ -82,5 +97,5 @@ {% endblock %} {% block extra_scripts %} {{ super() }} - + {% endblock %} \ No newline at end of file diff --git a/templates/admin/dodaj_wydatek.html b/templates/admin/dodaj_wydatek.html index e9dea75..28b714a 100644 --- a/templates/admin/dodaj_wydatek.html +++ b/templates/admin/dodaj_wydatek.html @@ -13,14 +13,30 @@

Dodaj wydatek: {{ zbiorka.nazwa }}

-
- {% if zbiorka.cel %} - Cel: {{ zbiorka.cel|round(2) }} - PLN +
+ {% if has_cel %} + + Cel: {{ zbiorka.cel|round(2) }} PLN + + {% endif %} + + Obecnie: {{ zbiorka.stan|round(2) }} PLN + + + {% if has_cel %} + {% set delta = zbiorka.cel - zbiorka.stan %} + {% if delta > 0 %} + + Brakuje: {{ delta|round(2) }} PLN + + {% else %} + + Nadwyżka: {{ (-delta)|round(2) }} PLN + + {% endif %} {% endif %} - Stan: {{ zbiorka.stan|round(2) - }} PLN
+
@@ -59,5 +75,5 @@ {% block extra_scripts %} {{ super() }} - + {% endblock %} \ No newline at end of file diff --git a/templates/admin/edytuj_stan.html b/templates/admin/edytuj_stan.html index 00d70f7..855f77b 100644 --- a/templates/admin/edytuj_stan.html +++ b/templates/admin/edytuj_stan.html @@ -20,11 +20,26 @@

Edytuj stan: {{ zbiorka.nazwa }}

{% if has_cel %} - Cel: {{ zbiorka.cel|round(2) }} - PLN + + Cel: {{ zbiorka.cel|round(2) }} PLN + + {% endif %} + + Obecnie: {{ zbiorka.stan|round(2) }} PLN + + + {% if has_cel %} + {% set delta = zbiorka.cel - zbiorka.stan %} + {% if delta > 0 %} + + Brakuje: {{ delta|round(2) }} PLN + + {% else %} + + Nadwyżka: {{ (-delta)|round(2) }} PLN + + {% endif %} {% endif %} - Obecnie: {{ zbiorka.stan|round(2) }} - PLN
@@ -124,5 +139,5 @@ {% block extra_scripts %} {{ super() }} - + {% endblock %} \ No newline at end of file diff --git a/templates/admin/formularz_zbiorek.html b/templates/admin/formularz_zbiorek.html index 3baaa42..de9b477 100644 --- a/templates/admin/formularz_zbiorek.html +++ b/templates/admin/formularz_zbiorek.html @@ -37,15 +37,35 @@ Cel: {{ zbiorka.cel|round(2) }} PLN {% endif %} - {% if zbiorka.ukryj_kwote %} - Kwoty ukryte - {% else %} - Kwoty widoczne - {% endif %} + + {% if not zbiorka.ukryj_kwote %} + + Stan: {{ zbiorka.stan|round(2) }} PLN + + + {% if zbiorka.cel %} + {% set delta = zbiorka.cel - zbiorka.stan %} + {% if delta > 0 %} + + Brakuje: {{ delta|round(2) }} PLN + + {% elif delta < 0 %} + Nadwyżka: {{ (-delta)|round(2) }} PLN + + {% endif %} + {% endif %} + {% endif %} + + {% if zbiorka.ukryj_kwote %} + Kwoty niepubliczne + {% else %} + Kwoty widoczne + {% endif %}
{% else %} Uzupełnij podstawowe dane i dane płatności {% endif %} +
@@ -82,10 +102,10 @@
-
Lista produktów
+
Lista produktów / Pozycje / Cele

Wypunktuj dokładnie produkty do zakupu — podaj nazwę, opcjonalny link do sklepu i cenę. - Status domyślnie Do kupienia; przełącz na Kupione po realizacji. + Status domyślnie Do kupienia; przełącz na Kupione po realizacji.

@@ -221,7 +241,6 @@
Cel i widoczność
- {# === BOX: zgodność sumy produktów z celem === #}