Sprint 15: Zeitzone-Fix, Gewichts-Sync, Öffnungszeiten, KI-Bericht, POI-Moderation — SW by-v432, APP_VER 411

- client_time: Browser-Lokalzeit bei allen Creates mitschicken (Tagebuch, Notizen,
  Forum, Verlorener Hund, Routen) — kein UTC-Versatz mehr bei Einträgen
- Gewicht-Sync: health typ=gewicht schreibt dogs.gewicht_kg, einmalige Migration
- Praxen: opening_hours + lat/lon/osm_id in tieraerzte-Tabelle, OSM-Nearby-Lookup,
  Öffnungszeiten in Karte und Detailansicht
- KI-Gesundheitsbericht: alle 2 Wochen automatisch, ki_health_reports-Tabelle,
  Frontend-Banner mit Archiv (letzten 5 Berichte)
- POI-Korrekturen: User schlägt Öffnungszeiten-Änderung vor, Moderatoren-Tab
  genehmigt/lehnt ab, user_edited-Flag schützt vor Overpass-Überschreibung
- timeutils.py: safe_client_time() zentral für alle Routen
This commit is contained in:
rene 2026-04-26 15:38:50 +02:00
parent 679dbdd862
commit 06bd8525ed
21 changed files with 724 additions and 75 deletions

View file

@ -440,9 +440,13 @@ def _migrate(conn_factory):
("health", "datei_typ", "TEXT"),
("health", "tierarzt_id", "INTEGER"),
# Tierärzte: Adresse aufgeteilt in Strasse/PLZ/Ort
("tieraerzte", "strasse", "TEXT"),
("tieraerzte", "plz", "TEXT"),
("tieraerzte", "ort", "TEXT"),
("tieraerzte", "strasse", "TEXT"),
("tieraerzte", "plz", "TEXT"),
("tieraerzte", "ort", "TEXT"),
("tieraerzte", "opening_hours", "TEXT"),
("tieraerzte", "lat", "REAL"),
("tieraerzte", "lon", "REAL"),
("tieraerzte", "osm_id", "TEXT"),
# Gesundheit: Erinnerungsintervall für wiederkehrende Einträge
("health", "intervall_tage", "INTEGER"),
# Routen: neue Felder
@ -453,6 +457,7 @@ def _migrate(conn_factory):
("osm_pois", "opening_hours", "TEXT"),
("osm_pois", "phone", "TEXT"),
("osm_pois", "website", "TEXT"),
("osm_pois", "user_edited", "INTEGER NOT NULL DEFAULT 0"),
# Forum: Threads brauchen text + antworten-Zähler
("forum_threads", "text", "TEXT NOT NULL DEFAULT ''"),
("forum_threads", "antworten", "INTEGER NOT NULL DEFAULT 0"),
@ -1157,3 +1162,51 @@ def _migrate(conn_factory):
ON notes(user_id, created_at DESC);
""")
logger.info("Migration: notes Tabelle bereit.")
conn.execute("""
CREATE TABLE IF NOT EXISTS ki_health_reports (
id INTEGER PRIMARY KEY AUTOINCREMENT,
dog_id INTEGER NOT NULL REFERENCES dogs(id) ON DELETE CASCADE,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
bericht TEXT NOT NULL,
erstellt_at TEXT NOT NULL DEFAULT (datetime('now'))
)
""")
conn.execute("""
CREATE INDEX IF NOT EXISTS idx_ki_health_reports_dog
ON ki_health_reports(dog_id, erstellt_at DESC)
""")
logger.info("Migration: ki_health_reports Tabelle bereit.")
conn.execute("""
CREATE TABLE IF NOT EXISTS osm_poi_edits (
id INTEGER PRIMARY KEY AUTOINCREMENT,
osm_id TEXT NOT NULL,
poi_name TEXT NOT NULL,
field TEXT NOT NULL DEFAULT 'opening_hours',
old_value TEXT,
new_value TEXT NOT NULL,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
status TEXT NOT NULL DEFAULT 'pending',
mod_id INTEGER REFERENCES users(id),
created_at TEXT NOT NULL DEFAULT (datetime('now')),
resolved_at TEXT
);
CREATE INDEX IF NOT EXISTS idx_osm_poi_edits_status
ON osm_poi_edits(status, created_at DESC);
""")
logger.info("Migration: osm_poi_edits Tabelle bereit.")
# Einmalige Datenmigration: dogs.gewicht_kg mit aktuellem Gesundheits-Gewicht synchronisieren
conn.execute("""
UPDATE dogs SET gewicht_kg = (
SELECT wert FROM health
WHERE health.dog_id = dogs.id AND health.typ = 'gewicht' AND health.wert IS NOT NULL
ORDER BY health.datum DESC, health.id DESC LIMIT 1
)
WHERE EXISTS (
SELECT 1 FROM health
WHERE health.dog_id = dogs.id AND health.typ = 'gewicht' AND health.wert IS NOT NULL
)
""")
logger.info("Migration: dogs.gewicht_kg aus health synchronisiert.")