From bdb4710f0c4c2ac94202bebecb70026243eae9b2 Mon Sep 17 00:00:00 2001 From: lelo Date: Sat, 10 May 2025 19:07:25 +0200 Subject: [PATCH 1/2] small gui fixes --- templates/base.html | 4 +- templates/mylinks.html | 30 ++-- templates/songs_dashboard.html | 271 ++++++++++++++++----------------- 3 files changed, 150 insertions(+), 155 deletions(-) diff --git a/templates/base.html b/templates/base.html index f110b80..a6949b5 100644 --- a/templates/base.html +++ b/templates/base.html @@ -41,9 +41,9 @@ | Verbindungen | - Auswertung-Downloads + Dashbord | - Auswertung-Wiederholungen + Wiederholungen {% if admin_enabled %} | Ordnerkonfiguration diff --git a/templates/mylinks.html b/templates/mylinks.html index 65c18de..1a0fd4f 100644 --- a/templates/mylinks.html +++ b/templates/mylinks.html @@ -6,8 +6,10 @@ {# page‐specific content #} {% block content %} + +
-

Übersicht deiner gültigen Links

+

Übersicht deiner Links

{% if valid_secrets %} {% for secret in valid_secrets %} @@ -17,7 +19,7 @@ class="card-img-top qr-code p-3" alt="QR Code for secret">
-
Geheimnis: {{ secret }}
+
Secret Link:

Gültig bis: {{ secret_valid_to[secret] }}

@@ -29,16 +31,14 @@ {% if secret_folders[secret] %} -
Ordner
-
    +
    Ordner:
    +
    {% for folder in secret_folders[secret] %} -
  • - {{ folder.foldername }} -
  • + {{ folder.foldername }} {% endfor %} -
+
{% else %} -

Keine Ordner für dieses Gemeimnis hinterlegt.

+

Keine Ordner für dieses Secret hinterlegt.

{% endif %}
@@ -66,16 +66,14 @@ {% if token_folders[token] %} -
Ordner
- + {% else %} -

Keine Ordner für dieses Gemeimnis hinterlegt.

+

Keine Ordner für diesen Token hinterlegt.

{% endif %} diff --git a/templates/songs_dashboard.html b/templates/songs_dashboard.html index a08c7e5..c283047 100644 --- a/templates/songs_dashboard.html +++ b/templates/songs_dashboard.html @@ -7,147 +7,144 @@ {# page content #} {% block content %} - -
-

Analyse Wiederholungen

- -
+ +
+

Analyse Wiederholungen

+ +
+ + + + + - - - - - - - - -
- - - - + + + + + + + +
+ + + + + + + + {% for count, titel in performance_data %} - - + + - - - {% for count, titel in performance_data %} - - - - - {% endfor %} - -
AnzahlTitel
AnzahlTitel{{ count }}{{ titel }}
{{ count }}{{ titel }}
-
- - - + {% endfor %} + + +
+ {% endblock %} \ No newline at end of file From 9971671688023f5549ad4e21512bce764d30e2a5 Mon Sep 17 00:00:00 2001 From: lelo Date: Sat, 10 May 2025 19:49:57 +0200 Subject: [PATCH 2/2] fix and improve songs dashboard --- analytics.py | 119 +++++++++++++++++++-------------- templates/songs_dashboard.html | 8 ++- 2 files changed, 75 insertions(+), 52 deletions(-) diff --git a/analytics.py b/analytics.py index 65c6c50..8c6cbf1 100644 --- a/analytics.py +++ b/analytics.py @@ -12,11 +12,8 @@ file_access_temp = [] app_config = auth.return_app_config() -# Example database name; you can change to whatever you want: -DB_NAME = 'access_log.db' - # Create a single global connection to SQLite -log_db = sqlite3.connect(DB_NAME, check_same_thread=False) +log_db = sqlite3.connect("access_log.db", check_same_thread=False) search_db = sqlite3.connect("search.db", check_same_thread=False) # geo location @@ -144,65 +141,89 @@ def return_file_access(): return [] def songs_dashboard(): + # — SESSION & PARAM HANDLING (unchanged) — if 'songs_dashboard_timeframe' not in session: session['songs_dashboard_timeframe'] = "30" timeframe_param = request.args.get("timeframe", session['songs_dashboard_timeframe']) session['songs_dashboard_timeframe'] = timeframe_param - + if 'songs_dashboard_category' not in session: session['songs_dashboard_category'] = "Gemeinsamer Gesang" category = request.args.get("category", session['songs_dashboard_category']) session['songs_dashboard_category'] = category - + if 'songs_dashboard_site' not in session: session['songs_dashboard_site'] = "Speyer" site = request.args.get("site", session['songs_dashboard_site']) session['songs_dashboard_site'] = site - - # Determine cutoff_date based on the days parameter - if timeframe_param == "all": - cutoff_date = None # No date filtering when analyzing all time - timeframe = "all" # Pass the string to the template if needed - else: - timeframe = int(timeframe_param) - now = datetime.now() - cutoff_date = now - timedelta(days=timeframe) - + + # — DETERMINE CUTOFF + TODAY STRINGS — + now = datetime.now() + params = [category, site] + date_clauses = [] + if timeframe_param != "all": + cutoff = now - timedelta(days=int(timeframe_param)) + date_clauses.append("performance_date >= ?") + params.append(cutoff.strftime("%Y-%m-%d")) + # filter out any future-dated rows at the DB level + date_clauses.append("performance_date <= ?") + params.append(now.strftime("%Y-%m-%d")) + + where_sql = " AND ".join(["category = ?", "site = ?"] + date_clauses) + cursor = search_db.cursor() - # Query rows with category "Gemeinsamer Gesang" - query = "SELECT titel, performance_date FROM files WHERE category = ? and site = ?" - cursor.execute(query, (category, site)) + cursor.execute( + f"SELECT titel, performance_date FROM files WHERE {where_sql}", + params + ) rows = cursor.fetchall() - - # Group and count performances per titel (only if performance_date is within the timeframe, - # or count all if cutoff_date is None) - performance_counts = defaultdict(int) - for titel, performance_date in rows: - if performance_date: - try: - # Convert date from "dd.mm.yyyy" format - date_obj = datetime.strptime(performance_date, "%d.%m.%Y") - except ValueError: - continue - # If cutoff_date is None, count all dates; otherwise, filter by cutoff_date. - if cutoff_date is None or date_obj >= cutoff_date: - performance_counts[titel] += 1 - - # Create a list of tuples: (count, titel), sorted in descending order by count. - performance_data = [(count, titel) for titel, count in performance_counts.items()] - performance_data.sort(reverse=True, key=lambda x: x[0]) - - title_short = app_config.get('TITLE_SHORT', 'Default Title') - title_long = app_config.get('TITLE_LONG' , 'Default Title') - - return render_template('songs_dashboard.html', - timeframe=timeframe_param, - performance_data=performance_data, - site=site, - category=category, - admin_enabled=auth.is_admin(), - title_short=title_short, - title_long=title_long) + + # — AGGREGATE COUNTS + LAST-PERFORMED, WITH ERROR LOGGING — + performance_counts = defaultdict(int) + last_performed_dates = {} + + for titel, perf_date_str in rows: + if not perf_date_str: + continue + + perf_date_str = perf_date_str.strip() + try: + perf_date = datetime.strptime(perf_date_str, "%Y-%m-%d") + except ValueError: + print(f"[songs_dashboard] bad date format for “{titel}”: “{perf_date_str}”") + continue + + performance_counts[titel] += 1 + + prev = last_performed_dates.get(titel) + if prev is None or perf_date > prev: + last_performed_dates[titel] = perf_date + + # — BUILD LIST FOR TEMPLATE — + performance_data = [] + for titel, count in performance_counts.items(): + last_str = last_performed_dates[titel].strftime("%d.%m.%Y") + performance_data.append({ + "titel": titel, + "count": count, + "last_performed": last_str + }) + + performance_data.sort(key=lambda x: x["count"], reverse=True) + + # — RENDER — + return render_template( + 'songs_dashboard.html', + timeframe=timeframe_param, + performance_data=performance_data, + site=site, + category=category, + admin_enabled=auth.is_admin(), + title_short=app_config.get('TITLE_SHORT', 'Default Title'), + title_long= app_config.get('TITLE_LONG', 'Default Title'), + ) + + @require_secret def connections(): diff --git a/templates/songs_dashboard.html b/templates/songs_dashboard.html index c283047..99001b6 100644 --- a/templates/songs_dashboard.html +++ b/templates/songs_dashboard.html @@ -134,13 +134,15 @@ Anzahl Titel + Zuletzt gesungen - {% for count, titel in performance_data %} + {% for item in performance_data %} - {{ count }} - {{ titel }} + {{ item['count'] }} + {{ item['titel'] }} + {{ item['last_performed'] }} {% endfor %}