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

@ -156,7 +156,7 @@ async def _fetch_and_store_tile(poi_type, x, y):
ON CONFLICT(osm_id, type) DO UPDATE SET
lat=excluded.lat, lon=excluded.lon,
name=excluded.name,
opening_hours=excluded.opening_hours,
opening_hours=CASE WHEN user_edited=1 THEN opening_hours ELSE excluded.opening_hours END,
phone=excluded.phone,
website=excluded.website,
cached_at=excluded.cached_at
@ -372,3 +372,44 @@ async def analyze_region(
background_tasks.add_task(_warmup)
return {'status': 'gestartet', 'tiles': len(tiles), 'types': list(OSM_QUERIES.keys())}
# ------------------------------------------------------------------
# POST /pois/{osm_id}/edit — Nutzer schlägt Korrektur vor
# ------------------------------------------------------------------
class PoiEditCreate(BaseModel):
poi_name: str
field: str = 'opening_hours'
new_value: str
@router.post('/pois/{osm_id}/edit', status_code=201)
async def submit_poi_edit(osm_id: str, data: PoiEditCreate,
user=Depends(get_current_user)):
if data.field not in ('opening_hours',):
raise HTTPException(400, "Nur 'opening_hours' kann korrigiert werden.")
if not data.new_value.strip():
raise HTTPException(400, "Neuer Wert darf nicht leer sein.")
with db() as conn:
poi = conn.execute(
"SELECT name, opening_hours FROM osm_pois WHERE osm_id=?", (osm_id,)
).fetchone()
if not poi:
raise HTTPException(404, "POI nicht gefunden.")
existing = conn.execute(
"""SELECT id FROM osm_poi_edits
WHERE osm_id=? AND field=? AND status='pending' AND user_id=?""",
(osm_id, data.field, user["id"])
).fetchone()
if existing:
raise HTTPException(409, "Du hast bereits eine ausstehende Korrektur für diesen POI.")
conn.execute(
"""INSERT INTO osm_poi_edits (osm_id, poi_name, field, old_value, new_value, user_id)
VALUES (?, ?, ?, ?, ?, ?)""",
(osm_id, data.poi_name or poi["name"], data.field,
poi[data.field], data.new_value.strip(), user["id"])
)
return {"status": "pending", "message": "Korrektur wurde zur Prüfung eingereicht."}