From 04e6d80af43ad059ac1b7b5501dc7afbdafaa596 Mon Sep 17 00:00:00 2001 From: lelo Date: Thu, 12 Jun 2025 14:12:07 +0000 Subject: [PATCH 1/4] toplist category to config --- app.py | 8 ++++---- static/app.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app.py b/app.py index fe8d0ef..25725d1 100755 --- a/app.py +++ b/app.py @@ -249,6 +249,7 @@ def serve_sw(): @app.route('/api/path/') @auth.require_secret def api_browse(subpath): + toplist_categories = app_config.get('toplist_categories', None) if subpath == '': # root directory foldernames = [] @@ -260,7 +261,8 @@ def api_browse(subpath): 'directories': foldernames, 'files': [], 'folder_today': a.return_folder_today(), - 'folder_yesterday': a.return_folder_yesterday() + 'folder_yesterday': a.return_folder_yesterday(), + 'toplist_enabled': bool(toplist_categories) }) if subpath == 'heute' or subpath == 'gestern': @@ -284,14 +286,12 @@ def api_browse(subpath): foldernames = [] files = [] split_path = subpath.split('/') - - valid_categories = ['Predigt', 'Erzählung', 'Gedicht', 'Gemeinsamer Gesang', 'Chor', 'Kinderchor', 'Jugendchor', 'Orchester', 'Instrumental', 'Gruppenlied'] if len(split_path) == 1 and split_path[0] == 'toplist': foldernames = [ { 'name': categorie, 'path': 'toplist/' + categorie - } for categorie in valid_categories + } for categorie in toplist_categories ] elif len(split_path) > 1 and split_path[0] == 'toplist': files = hf.generate_top_list(split_path[1]) diff --git a/static/app.js b/static/app.js index ca73ca0..dce8ae4 100644 --- a/static/app.js +++ b/static/app.js @@ -113,7 +113,7 @@ function renderContent(data) { } else if (data.breadcrumbs.length === 1 && Array.isArray(data.folder_yesterday) && data.folder_yesterday.length > 0) { contentHTML += `
  • 📅 Gestern
  • `; } - if (data.breadcrumbs.length === 1 ) { + if (data.breadcrumbs.length === 1 && data.toplist_enabled) { contentHTML += `
  • 🔥 oft angehört
  • `; } console.log(data.folder_today, data.folder_yesterday); From 31ebdfe9ddf99c81b084bfc52f223cf5e037c2fa Mon Sep 17 00:00:00 2001 From: lelo Date: Fri, 13 Jun 2025 21:11:22 +0000 Subject: [PATCH 2/4] refactor extraction date from string --- helperfunctions.py | 91 +++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 37 deletions(-) diff --git a/helperfunctions.py b/helperfunctions.py index b81888d..e390f68 100644 --- a/helperfunctions.py +++ b/helperfunctions.py @@ -1,57 +1,74 @@ from flask import session -from datetime import datetime import re import os import sqlite3 from datetime import datetime, timedelta +from typing import Optional log_db = sqlite3.connect("access_log.db", check_same_thread=False) -def extract_date_from_string(string_with_date): - # grab X.Y.Z where X,Y,Z are 1–4 digits - m = re.search(r'(\d{1,4}\.\d{1,2}\.\d{1,4})', string_with_date) - if not m: - return None - date_str = m.group(1) - parts = date_str.split('.') - - # 1) Unambiguous “last group = YYYY” - if len(parts) == 3 and len(parts[2]) == 4: - fmt = '%d.%m.%Y' - # 2) Unambiguous “first group = YYYY” - elif len(parts) == 3 and len(parts[0]) == 4: - fmt = '%Y.%m.%d' +# Precompiled regex to find date-like patterns: either dotted X.Y.Z or ISO dashed YYYY-MM-DD +_DATE_REGEX = re.compile( + r"(" # start group + r"\d{4}-\d{1,2}-\d{1,2}" # ISO: YYYY-M-D or YYYY-MM-DD + r"|" # or + r"\d{1,4}\.\d{1,2}\.\d{1,4}" # dotted: X.Y.Z, where each is 1–4 digits (year may be 1–4) + r")" +) - # 3) Ambiguous “XX.XX.XX” → prefer DD.MM.YY, fallback to YY.MM.DD - elif len(parts) == 3 and all(len(p) == 2 for p in parts): - # try last-group-as-year first - try: - dt = datetime.strptime(date_str, '%d.%m.%y') - return dt.strftime('%Y-%m-%d') - except ValueError: - # fallback to first-group-as-year - fmt = '%y.%m.%d' - else: - # optional: handle ISO with dashes - if '-' in date_str: - try: - dt = datetime.strptime(date_str, '%Y-%m-%d') - return dt.strftime('%Y-%m-%d') - except ValueError: - return None - return None - - # parse with whichever fmt we settled on +def _try_parse(date_str: str, fmt: str) -> Optional[datetime]: + """Try to parse date_str with fmt, return datetime or None.""" try: - dt = datetime.strptime(date_str, fmt) - return dt.strftime('%Y-%m-%d') + return datetime.strptime(date_str, fmt) except ValueError: return None +def extract_date_from_string(text: str) -> Optional[str]: + """ + Extract the first date-like substring from text and return it in ISO format (YYYY-MM-DD). + Supports: + - ISO-style dates with dashes (YYYY-M-D or YYYY-MM-DD) + - Dotted dates (DD.MM.YYYY, YYYY.MM.DD, DD.MM.YY, YY.MM.DD) + """ + match = _DATE_REGEX.search(text) + if not match: + return None + + date_str = match.group(1) + + # 1) ISO dashed format takes priority + if '-' in date_str: + dt = _try_parse(date_str, '%Y-%m-%d') + return dt.strftime('%Y-%m-%d') if dt else None + + # 2) Dotted formats + parts = date_str.split('.') + candidates = [] + + # Unambiguous: last part 4 digits → DD.MM.YYYY + if len(parts) == 3 and len(parts[2]) == 4: + candidates.append('%d.%m.%Y') + # Unambiguous: first part 4 digits → YYYY.MM.DD + if len(parts) == 3 and len(parts[0]) == 4: + candidates.append('%Y.%m.%d') + # Ambiguous two-digit groups: try DD.MM.YY, then YY.MM.DD + if len(parts) == 3 and all(len(p) == 2 for p in parts): + candidates.extend(['%d.%m.%y', '%y.%m.%d']) + + # Try each candidate + for fmt in candidates: + dt = _try_parse(date_str, fmt) + if dt: + return dt.strftime('%Y-%m-%d') + + # no valid parse + return None + + def extract_structure_from_string(input_string): # extract category and titel from filename filepathname_ext = os.path.splitext(input_string)[0] # remove file extension From 84bf7bae0ca8ceac46752fcb1fc208a8bda8d09a Mon Sep 17 00:00:00 2001 From: lelo Date: Sat, 14 Jun 2025 11:19:06 +0000 Subject: [PATCH 3/4] add category --- helperfunctions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helperfunctions.py b/helperfunctions.py index e390f68..f899176 100644 --- a/helperfunctions.py +++ b/helperfunctions.py @@ -98,7 +98,7 @@ def extract_structure_from_string(input_string): category = 'Chor' elif 'gemeinsam' in left_side.lower() or 'gemeindelied' in left_side.lower() or 'gemeinsamer gesang' in input_string.lower(): category = 'Gemeinsamer Gesang' - elif 'gruppenlied' in left_side.lower() or 'jugend' in left_side.lower() or 'lied' in left_side.lower(): + elif 'gruppenlied' in left_side.lower() or 'jugend' in left_side.lower() or 'lied' in left_side.lower() or 'musikgruppe' in left_side.lower(): category = 'Gruppenlied' elif 'gedicht' in left_side.lower(): category = 'Gedicht' From 05782a0c33d56ee8a93d2aca66f9c0801c115647 Mon Sep 17 00:00:00 2001 From: lelo Date: Sat, 14 Jun 2025 11:26:02 +0000 Subject: [PATCH 4/4] rework category --- helperfunctions.py | 46 ++++++++++++++++++++++------------------------ 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/helperfunctions.py b/helperfunctions.py index f899176..f39356f 100644 --- a/helperfunctions.py +++ b/helperfunctions.py @@ -84,30 +84,28 @@ def extract_structure_from_string(input_string): # first part not a number pass - if 'predig' in left_side.lower() or 'thema' in left_side.lower(): - category = 'Predigt' - elif 'wort' in left_side.lower() or 'einladung' in left_side.lower() or 'begrüßung' in left_side.lower() or 'ansprache' in left_side.lower() or 'einleitung' in left_side.lower() or 'aufruf zum' in left_side.lower() or 'zuruf zum' in left_side.lower(): - category = 'Vorwort' - elif 'kinderchor' in left_side.lower(): - category = 'Kinderchor' - elif 'jugendchor' in left_side.lower(): - category = 'Jugendchor' - elif 'orchester' in input_string.lower() or 'sinfonie' in input_string.lower() or 'symphonie' in input_string.lower(): - category = 'Orchester' - elif 'chor' in left_side.lower(): - category = 'Chor' - elif 'gemeinsam' in left_side.lower() or 'gemeindelied' in left_side.lower() or 'gemeinsamer gesang' in input_string.lower(): - category = 'Gemeinsamer Gesang' - elif 'gruppenlied' in left_side.lower() or 'jugend' in left_side.lower() or 'lied' in left_side.lower() or 'musikgruppe' in left_side.lower(): - category = 'Gruppenlied' - elif 'gedicht' in left_side.lower(): - category = 'Gedicht' - elif 'vortrag' in left_side.lower() or 'erzä' in left_side.lower() or 'program' in left_side.lower(): - category = 'Erzählung' - elif 'instrumental' in input_string.lower() or 'musikstück' in left_side.lower() or 'harfenstück' in left_side.lower(): - category = 'Instrumental' - else: - category = None + # define your mapping: category → list of trigger-words + CATEGORY_KEYWORDS = { + 'Predigt': ['predig', 'thema'], + 'Vorwort': ['wort', 'einladung', 'begrüßung', 'ansprache', 'einleitung', 'aufruf zum', 'zuruf zum'], + 'Kinderchor': ['kinderchor'], + 'Jugendchor': ['jugendchor'], + 'Orchester': ['orchester', 'sinfonie', 'symphonie'], + 'Chor': ['chor'], + 'Gemeinsamer Gesang': ['gemeinsam', 'gemeindelied', 'gemeinsamer gesang'], + 'Gruppenlied': ['gruppenlied', 'jugend', 'lied', 'musikgruppe'], + 'Gedicht': ['gedicht'], + 'Erzählung': ['vortrag', 'erzä', 'program'], + 'Instrumental': ['instrumental', 'musikstück', 'harfenstück'], + } + + # walk the dict in your desired priority order + category = None + text = left_side.lower() + for cat, keywords in CATEGORY_KEYWORDS.items(): + if any(kw in text for kw in keywords): + category = cat + break if right_side: titel, name = right_side.split('-', 1) if '-' in right_side else (right_side, None)