Merge branch 'development' of gitea.centx.de:lelo/bethaus-app into development

This commit is contained in:
lelo 2025-06-14 21:57:38 +02:00
commit 5f57de04c8
3 changed files with 100 additions and 78 deletions

8
app.py
View File

@ -249,6 +249,7 @@ def serve_sw():
@app.route('/api/path/<path:subpath>') @app.route('/api/path/<path:subpath>')
@auth.require_secret @auth.require_secret
def api_browse(subpath): def api_browse(subpath):
toplist_categories = app_config.get('toplist_categories', None)
if subpath == '': # root directory if subpath == '': # root directory
foldernames = [] foldernames = []
@ -260,7 +261,8 @@ def api_browse(subpath):
'directories': foldernames, 'directories': foldernames,
'files': [], 'files': [],
'folder_today': a.return_folder_today(), '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': if subpath == 'heute' or subpath == 'gestern':
@ -284,14 +286,12 @@ def api_browse(subpath):
foldernames = [] foldernames = []
files = [] files = []
split_path = subpath.split('/') 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': if len(split_path) == 1 and split_path[0] == 'toplist':
foldernames = [ foldernames = [
{ {
'name': categorie, 'path': 'toplist/' + categorie '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': elif len(split_path) > 1 and split_path[0] == 'toplist':
files = hf.generate_top_list(split_path[1]) files = hf.generate_top_list(split_path[1])

View File

@ -1,57 +1,78 @@
from flask import session from flask import session
from datetime import datetime
import re import re
import os import os
import sqlite3 import sqlite3
from datetime import datetime, timedelta from datetime import datetime, timedelta
from typing import Optional
import auth
app_config = auth.return_app_config()
BASE_DIR = os.path.realpath(app_config['BASE_DIR'])
log_db = sqlite3.connect("access_log.db", check_same_thread=False) 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 14 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” # Precompiled regex to find date-like patterns: either dotted X.Y.Z or ISO dashed YYYY-MM-DD
elif len(parts) == 3 and len(parts[0]) == 4: _DATE_REGEX = re.compile(
fmt = '%Y.%m.%d' 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 14 digits (year may be 14)
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: def _try_parse(date_str: str, fmt: str) -> Optional[datetime]:
# optional: handle ISO with dashes """Try to parse date_str with fmt, return datetime or None."""
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
try: try:
dt = datetime.strptime(date_str, fmt) return datetime.strptime(date_str, fmt)
return dt.strftime('%Y-%m-%d')
except ValueError: except ValueError:
return None 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): def extract_structure_from_string(input_string):
# extract category and titel from filename # extract category and titel from filename
filepathname_ext = os.path.splitext(input_string)[0] # remove file extension filepathname_ext = os.path.splitext(input_string)[0] # remove file extension
@ -67,30 +88,28 @@ def extract_structure_from_string(input_string):
# first part not a number # first part not a number
pass pass
if 'predig' in left_side.lower() or 'thema' in left_side.lower(): # define your mapping: category → list of trigger-words
category = 'Predigt' CATEGORY_KEYWORDS = {
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(): 'Predigt': ['predig', 'thema'],
category = 'Vorwort' 'Vorwort': ['wort', 'einladung', 'begrüßung', 'ansprache', 'einleitung', 'aufruf zum', 'zuruf zum'],
elif 'kinderchor' in left_side.lower(): 'Kinderchor': ['kinderchor'],
category = 'Kinderchor' 'Jugendchor': ['jugendchor'],
elif 'jugendchor' in left_side.lower(): 'Orchester': ['orchester', 'sinfonie', 'symphonie'],
category = 'Jugendchor' 'Chor': ['chor'],
elif 'orchester' in input_string.lower() or 'sinfonie' in input_string.lower() or 'symphonie' in input_string.lower(): 'Gemeinsamer Gesang': ['gemeinsam', 'gemeindelied', 'gemeinsamer gesang'],
category = 'Orchester' 'Gruppenlied': ['gruppenlied', 'jugend', 'lied', 'musikgruppe'],
elif 'chor' in left_side.lower(): 'Gedicht': ['gedicht'],
category = 'Chor' 'Erzählung': ['vortrag', 'erzä', 'program'],
elif 'gemeinsam' in left_side.lower() or 'gemeindelied' in left_side.lower() or 'gemeinsamer gesang' in input_string.lower(): 'Instrumental': ['instrumental', 'musikstück', 'harfenstück'],
category = 'Gemeinsamer Gesang' }
elif 'gruppenlied' in left_side.lower() or 'jugend' in left_side.lower() or 'lied' in left_side.lower():
category = 'Gruppenlied' # walk the dict in your desired priority order
elif 'gedicht' in left_side.lower(): category = None
category = 'Gedicht' text = left_side.lower()
elif 'vortrag' in left_side.lower() or 'erzä' in left_side.lower() or 'program' in left_side.lower(): for cat, keywords in CATEGORY_KEYWORDS.items():
category = 'Erzählung' if any(kw in text for kw in keywords):
elif 'instrumental' in input_string.lower() or 'musikstück' in left_side.lower() or 'harfenstück' in left_side.lower(): category = cat
category = 'Instrumental' break
else:
category = None
if right_side: if right_side:
titel, name = right_side.split('-', 1) if '-' in right_side else (right_side, None) titel, name = right_side.split('-', 1) if '-' in right_side else (right_side, None)
@ -128,15 +147,15 @@ def generate_top_list(category):
cursor = log_db.execute(query, params_for_filter) cursor = log_db.execute(query, params_for_filter)
rows = cursor.fetchall() rows = cursor.fetchall()
# Filter by allowed basefolders # Filter by allowed base folders
allowed_basefolders = list(session['folders'].keys()) allowed_basefolders = list(session['folders'].keys())
rows = [ rows = [
(rel_path, access_count) for rel_path, access_count in rows (rel_path, access_count) for rel_path, access_count in rows
if any(rel_path.startswith(folder) for folder in allowed_basefolders) if any(rel_path.startswith(folder) for folder in allowed_basefolders)
] ]
# Convert rows to a list of dictionaries and add category # Convert rows to a list of dicts and add category
rows = [ records = [
{ {
'rel_path': rel_path, 'rel_path': rel_path,
'access_count': access_count, 'access_count': access_count,
@ -144,15 +163,18 @@ def generate_top_list(category):
} }
for rel_path, access_count in rows for rel_path, access_count in rows
] ]
rows = [r for r in rows if r['category'] == category][:20] # Filter by requested category and limit
records = [r for r in records if r['category'] == category][:20]
filelist = [ # Build file list and check existence
{ filelist = []
'name': rel_path.split('/')[-1], for record in records:
'path': rel_path, rel_path = record['rel_path']
'file_type': 'music' if os.path.exists(os.path.join(BASE_DIR, rel_path)): # ensure file exists on disk // slow operation. maybe improve later
} filelist.append({
for rel_path in [r['rel_path'] for r in rows] 'name': os.path.basename(rel_path),
] 'path': rel_path,
'file_type': 'music'
})
return filelist return filelist

View File

@ -113,7 +113,7 @@ function renderContent(data) {
} else if (data.breadcrumbs.length === 1 && Array.isArray(data.folder_yesterday) && data.folder_yesterday.length > 0) { } else if (data.breadcrumbs.length === 1 && Array.isArray(data.folder_yesterday) && data.folder_yesterday.length > 0) {
contentHTML += `<li class="directory-item"><a href="#" class="directory-link" data-path="gestern">📅 Gestern</a></li>`; contentHTML += `<li class="directory-item"><a href="#" class="directory-link" data-path="gestern">📅 Gestern</a></li>`;
} }
if (data.breadcrumbs.length === 1 ) { if (data.breadcrumbs.length === 1 && data.toplist_enabled) {
contentHTML += `<li class="directory-item"><a href="#" class="directory-link" data-path="toplist">🔥 oft angehört</a></li>`; contentHTML += `<li class="directory-item"><a href="#" class="directory-link" data-path="toplist">🔥 oft angehört</a></li>`;
} }
console.log(data.folder_today, data.folder_yesterday); console.log(data.folder_today, data.folder_yesterday);