Merge branch 'development' of gitea.centx.de:lelo/bethaus-app into development
This commit is contained in:
commit
5f57de04c8
8
app.py
8
app.py
@ -249,6 +249,7 @@ def serve_sw():
|
||||
@app.route('/api/path/<path:subpath>')
|
||||
@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':
|
||||
@ -285,13 +287,11 @@ def api_browse(subpath):
|
||||
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])
|
||||
|
||||
@ -1,54 +1,75 @@
|
||||
from flask import session
|
||||
from datetime import datetime
|
||||
import re
|
||||
import os
|
||||
import sqlite3
|
||||
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)
|
||||
|
||||
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:
|
||||
|
||||
|
||||
# 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")"
|
||||
)
|
||||
|
||||
|
||||
def _try_parse(date_str: str, fmt: str) -> Optional[datetime]:
|
||||
"""Try to parse date_str with fmt, return datetime or None."""
|
||||
try:
|
||||
return datetime.strptime(date_str, fmt)
|
||||
except ValueError:
|
||||
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'
|
||||
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
|
||||
|
||||
# 2) Unambiguous “first group = YYYY”
|
||||
elif len(parts) == 3 and len(parts[0]) == 4:
|
||||
fmt = '%Y.%m.%d'
|
||||
date_str = match.group(1)
|
||||
|
||||
# 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
|
||||
# 1) ISO dashed format takes priority
|
||||
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
|
||||
dt = _try_parse(date_str, '%Y-%m-%d')
|
||||
return dt.strftime('%Y-%m-%d') if dt else None
|
||||
|
||||
# parse with whichever fmt we settled on
|
||||
try:
|
||||
dt = datetime.strptime(date_str, fmt)
|
||||
# 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')
|
||||
except ValueError:
|
||||
|
||||
# no valid parse
|
||||
return None
|
||||
|
||||
|
||||
@ -67,30 +88,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():
|
||||
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:
|
||||
# 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)
|
||||
@ -128,15 +147,15 @@ def generate_top_list(category):
|
||||
cursor = log_db.execute(query, params_for_filter)
|
||||
rows = cursor.fetchall()
|
||||
|
||||
# Filter by allowed basefolders
|
||||
# Filter by allowed base folders
|
||||
allowed_basefolders = list(session['folders'].keys())
|
||||
rows = [
|
||||
(rel_path, access_count) for rel_path, access_count in rows
|
||||
if any(rel_path.startswith(folder) for folder in allowed_basefolders)
|
||||
]
|
||||
|
||||
# Convert rows to a list of dictionaries and add category
|
||||
rows = [
|
||||
# Convert rows to a list of dicts and add category
|
||||
records = [
|
||||
{
|
||||
'rel_path': rel_path,
|
||||
'access_count': access_count,
|
||||
@ -144,15 +163,18 @@ def generate_top_list(category):
|
||||
}
|
||||
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 = [
|
||||
{
|
||||
'name': rel_path.split('/')[-1],
|
||||
# Build file list and check existence
|
||||
filelist = []
|
||||
for record in records:
|
||||
rel_path = record['rel_path']
|
||||
if os.path.exists(os.path.join(BASE_DIR, rel_path)): # ensure file exists on disk // slow operation. maybe improve later
|
||||
filelist.append({
|
||||
'name': os.path.basename(rel_path),
|
||||
'path': rel_path,
|
||||
'file_type': 'music'
|
||||
}
|
||||
for rel_path in [r['rel_path'] for r in rows]
|
||||
]
|
||||
})
|
||||
|
||||
return filelist
|
||||
|
||||
@ -113,7 +113,7 @@ function renderContent(data) {
|
||||
} 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>`;
|
||||
}
|
||||
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>`;
|
||||
}
|
||||
console.log(data.folder_today, data.folder_yesterday);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user