moderation preview of pictures and audio files
Sonarqube Scanner / Prepare environment (push) Successful in 9s
Sonarqube Scanner / Build and test (push) Successful in 26s
Sonarqube Scanner / SonarQube analysis (push) Successful in 1m11s
Sonarqube Scanner / Deploy to Server (push) Successful in 8s

This commit is contained in:
Florian Zumpe
2026-10-03 15:48:04 +02:00
parent 94793b530d
commit 4b8bdcad5f
7 changed files with 209 additions and 8 deletions
+62 -7
View File
@@ -9,12 +9,13 @@ import re
import shlex
import sqlite3
import sys
from urllib.parse import urlsplit
PROJECT_ROOT = Path(__file__).resolve().parents[2]
def database_path(project_root):
"""Liest die .env; bereits gesetzte Umgebungsvariablen haben Vorrang."""
def environment_settings(project_root):
"""Liest die .env, ohne die Prozessumgebung zu verändern."""
settings = {}
env_file = project_root / ".env"
try:
@@ -32,6 +33,12 @@ def database_path(project_root):
settings[key.strip()] = " ".join(shlex.split(value, comments=True))
except ValueError as error:
raise ValueError(f"Ungültige .env-Anführungszeichen in Zeile {number}.") from error
return settings
def database_path(project_root):
"""Bereits gesetzte Umgebungsvariablen haben Vorrang vor der .env."""
settings = environment_settings(project_root)
data_dir = os.environ.get("DATA_DIR", settings.get("DATA_DIR", "")) or "data"
return (project_root / data_dir / "wegwichtel.sqlite").resolve()
@@ -53,15 +60,61 @@ def arguments(argv):
return parser.parse_args(argv)
def list_routes(database):
def review_settings(project_root):
settings = environment_settings(project_root)
url = os.environ.get("MODERATION_REVIEW_URL", settings.get("MODERATION_REVIEW_URL", ""))
parsed = urlsplit(url)
if url and (parsed.scheme != "https" or not parsed.netloc or parsed.fragment or parsed.query
or parsed.username or parsed.password):
raise ValueError("MODERATION_REVIEW_URL muss eine HTTPS-Adresse ohne Zugangsdaten, Query oder Fragment sein.")
return url.rstrip("/")
def table_text(value):
plain = re.sub(r'[\x00-\x1f\x7f-\x9f]', ' ', str(value or ""))
return " ".join(plain.split())
def print_table(headers, rows):
cells = [[table_text(value) for value in row] for row in [headers, *rows]]
widths = [max(len(row[index]) for row in cells) for index in range(len(headers))]
for row in cells:
print(" | ".join(value.ljust(width) for value, width in zip(row, widths)))
def show_pois(database, identifier, review_url, active):
rows = database.execute("""SELECT id, sequence, title, description, created_at, audio_path
FROM pois WHERE route_id=? ORDER BY sequence, id""", (identifier,)).fetchall()
print("Stationen:")
if not rows:
print("Keine Stationen vorhanden.")
return
details = []
for number, poi in enumerate(rows, 1):
access = []
if review_url and active:
base = f"{review_url}/routes/{identifier}/pois/{poi['id']}"
images = database.execute("SELECT id FROM poi_images WHERE poi_id=? ORDER BY sequence, id",
(poi["id"],)).fetchall()
access.extend(f"{base}/pictures/{image['id']}" for image in images)
if poi["audio_path"]:
access.append(f"{base}/audio")
description = poi["description"] or poi["title"]
details.append([poi["id"], number, description, poi["created_at"] or "–", " ; ".join(access) or "–"])
print_table(["ID", "Platz", "Beschreibung", "Erstellt am (UTC)", "Bilder / Ton (Anmeldung)"], details)
def list_routes(database, review_url=""):
rows = database.execute("SELECT id, name, status, activated_at FROM routes ORDER BY id").fetchall()
if not rows:
print("Keine Strecken vorhanden.")
return
print("ID\tStatus\tFreigegeben am (UTC)\tName")
for row in rows:
name = row["name"].replace("\n", " ").replace("\r", " ").replace("\t", " ")
name = table_text(row["name"])
print(f'{row["id"]}\t{row["status"]}\t{row["activated_at"] or "nicht freigegeben"}\t{name}')
show_pois(database, row["id"], review_url, row["status"] == "active")
print()
def moderate(database, identifier, enable):
@@ -84,12 +137,14 @@ def main(argv=None, project_root=None):
args = arguments(argv)
try:
path = database_path(project_root or PROJECT_ROOT)
mode = "ro" if args.enable is None and args.disable is None else "rw"
listing = args.enable is None and args.disable is None
review_url = review_settings(project_root or PROJECT_ROOT) if listing else ""
mode = "ro" if listing else "rw"
database = sqlite3.connect(f"{path.as_uri()}?mode={mode}", uri=True, timeout=10)
try:
database.row_factory = sqlite3.Row
if mode == "ro":
list_routes(database)
if listing:
list_routes(database, review_url)
else:
identifier = args.enable if args.enable is not None else args.disable
moderate(database, identifier, args.enable is not None)