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

@ -10,6 +10,7 @@ import ki as KI
import httpx
import weather as weather_mod
from media_utils import convert_media, extract_video_thumb, safe_media_path, validate_upload, extract_gps_from_exif
from timeutils import safe_client_time
logger = logging.getLogger(__name__)
@ -19,6 +20,7 @@ MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media")
class DiaryCreate(BaseModel):
datum: Optional[str] = None # ISO date, default heute
client_time: Optional[str] = None # lokale Uhrzeit des Geräts (YYYY-MM-DDTHH:MM:SS)
typ: str = "eintrag"
titel: Optional[str] = None
text: Optional[str] = None
@ -288,14 +290,14 @@ async def create_diary(dog_id: int, data: DiaryCreate,
else:
all_dogs = _validate_dog_ids(data.dog_ids or [], dog_id, user["id"], conn)
ct = safe_client_time(data.client_time)
datum = data.datum or ct[:10]
conn.execute(
"""INSERT INTO diary
(dog_id, datum, typ, titel, text, tags, gps_lat, gps_lon, location_name, is_milestone)
VALUES (?,
COALESCE(?, date('now')),
?,?,?,?,?,?,?,?)""",
(dog_id, data.datum, data.typ, data.titel, data.text,
json.dumps(tags), data.gps_lat, data.gps_lon, data.location_name, int(data.is_milestone))
(dog_id, datum, typ, titel, text, tags, gps_lat, gps_lon, location_name, is_milestone, created_at)
VALUES (?,?,?,?,?,?,?,?,?,?,?)""",
(dog_id, datum, data.typ, data.titel, data.text,
json.dumps(tags), data.gps_lat, data.gps_lon, data.location_name, int(data.is_milestone), ct)
)
entry = conn.execute(
"SELECT * FROM diary WHERE dog_id=? ORDER BY id DESC LIMIT 1",