Sprint 10: OSM-POI-Cache, Karten-Clustering, Routen-Redesign
Karte (map.js):
- OSM Overpass API: Restaurants, Tierärzte, Parkplätze, Bänke, Wasserstellen
- Leaflet.markercluster für alle OSM-Layer
- Standort-Dot mit GPS-Genauigkeitskreis, Wake-Lock bei Aufzeichnung
- Community-Pins setzen/löschen, Meldungen, Crosshair-Placement
- Layer-Sichtbarkeit in localStorage (by_map_visible_v1)
Routen (routes.js + routen.py):
- Komoot-Stil: SVG-Track-Preview, Foto-Upload, Nearby-POIs im Detail-Modal
- Neue Felder: is_public, hunde_tauglichkeit, foto_urls
- Rate-Endpoint (POST /api/routes/{id}/rate)
- Foto-Upload (POST /api/routes/{id}/photo)
- Fix: json_extract $[-1] → $[#-1] (SQLite-kompatibler Pfad für letztes Element)
Backend (osm.py, database.py, scheduler.py):
- /api/osm/pois: OSM-Overpass-Cache mit Tile-Logik (14 Tage TTL)
- /api/osm/user-poi: Community-Marker CRUD
- /api/osm/report: Marker als ungültig melden
- Neue Tabellen: osm_pois, osm_tiles, user_map_pois, osm_reports
- Giftköder-Archiv-Job (täglich 03:00, soft-delete nach Ablauf)
- Giftköder-Archiv-Job als APScheduler-CronJob
UI: Orte-Menüpunkt entfernt (in Karte integriert), APP_VER auf 62
This commit is contained in:
parent
bf26e5faf4
commit
ebe4ce20cf
16 changed files with 3020 additions and 737 deletions
|
|
@ -22,10 +22,17 @@ def start():
|
|||
CronTrigger(hour=8, minute=0), # täglich 08:00 Uhr
|
||||
id="health_reminders",
|
||||
replace_existing=True,
|
||||
misfire_grace_time=3600, # bis zu 1h Verzug ok (z.B. nach Neustart)
|
||||
misfire_grace_time=3600,
|
||||
)
|
||||
_scheduler.add_job(
|
||||
_job_poison_archive,
|
||||
CronTrigger(hour=3, minute=0), # täglich 03:00 Uhr (ruhige Zeit)
|
||||
id="poison_archive",
|
||||
replace_existing=True,
|
||||
misfire_grace_time=3600,
|
||||
)
|
||||
_scheduler.start()
|
||||
logger.info("Scheduler gestartet — Health-Reminder täglich 08:00 Uhr.")
|
||||
logger.info("Scheduler gestartet — Health-Reminder 08:00, Giftköder-Archiv 03:00.")
|
||||
|
||||
|
||||
def stop():
|
||||
|
|
@ -87,3 +94,31 @@ async def _job_health_reminders():
|
|||
logger.info(f"Reminder Push: user={r['user_id']} entry={r['id']} delta={delta}d")
|
||||
|
||||
logger.info(f"Health-Reminder Job fertig — {len(rows)} Einträge, {sent_total} Push gesendet.")
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# JOB: Abgelaufene Giftköder-Meldungen archivieren
|
||||
# Abgelaufene, aber noch nicht manuell aufgelöste Einträge werden
|
||||
# sauber als geloest=1 markiert — für spätere KI-Musteranalyse.
|
||||
# Die Zeilen selbst werden NIE gelöscht.
|
||||
# ------------------------------------------------------------------
|
||||
async def _job_poison_archive():
|
||||
"""
|
||||
Findet Giftköder-Meldungen deren expires_at verstrichen ist
|
||||
und die noch nicht als geloest markiert wurden.
|
||||
Setzt geloest=1, geloest_grund='automatisch_abgelaufen'.
|
||||
"""
|
||||
from datetime import datetime
|
||||
now = datetime.utcnow().isoformat()
|
||||
with db() as conn:
|
||||
result = conn.execute("""
|
||||
UPDATE poison
|
||||
SET geloest = 1,
|
||||
geloest_at = datetime('now'),
|
||||
geloest_grund = 'automatisch_abgelaufen'
|
||||
WHERE geloest = 0
|
||||
AND expires_at < ?
|
||||
""", (now,))
|
||||
count = result.rowcount
|
||||
if count:
|
||||
logger.info(f"Giftköder-Archiv: {count} abgelaufene Meldungen archiviert.")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue