Bisher holte nur der Create-/Foto-EXIF-Pfad Wetter+POIs. Wer einem Eintrag nachträglich (Edit) einen Standort gab, bekam nichts. Jetzt: GPS neu/geändert im Update-Handler -> POIs immer + Wetter fürs Eintragsdatum. - weather.get_weather_for_date(): heute -> aktuelles Wetter; Vergangenheit -> stündliche Historie (Open-Meteo Forecast <=90 Tage, sonst Archive-API), Stunde aus created_at. Gleiche Dict-Struktur wie get_weather_for_location. - diary.update_diary(): erfasst alten GPS-Stand, reichert nach DB-Commit an (async), schreibt nur erfolgreich geholte Felder (kein Datenverlust bei API-Fehler), identische Koordinaten -> kein erneuter Abruf. - Tests: tests/test_diary_location_enrich.py (Anreicherung, kein GPS=kein Abruf, Resave ohne Re-Fetch).
95 lines
3.7 KiB
Python
95 lines
3.7 KiB
Python
"""Nachträgliches Positionieren eines Tagebucheintrags reichert POIs + Wetter an.
|
|
|
|
Deckt die Lücke ab: Der PATCH-/Update-Pfad holte bisher kein Wetter/POI, wenn
|
|
man einem Eintrag (z.B. Indoor-Foto ohne EXIF-GPS) nachträglich einen Standort
|
|
gibt. Jetzt: GPS neu gesetzt/geändert → POIs (immer) + Wetter fürs Eintragsdatum
|
|
(historisch korrekt via get_weather_for_date).
|
|
|
|
Wetter- und POI-Fetch sind gestubbt (kein echtes Netzwerk im Test).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture
|
|
def stub_enrich(monkeypatch, app):
|
|
# WICHTIG: routes.diary erst HIER importieren (nach dem app-Fixture, das
|
|
# DB_PATH setzt) — ein Top-Level-Import würde database mit dem Default-Pfad
|
|
# binden und die ganze Test-Session vergiften.
|
|
import routes.diary as diary_mod
|
|
calls = {}
|
|
|
|
async def _fake_weather(lat, lon, datum, hour=None):
|
|
calls["weather"] = {"lat": lat, "lon": lon, "datum": datum, "hour": hour}
|
|
return {"temp_c": 18.5, "weathercode": 61, "desc": "Regen",
|
|
"is_day": True, "historical": True}
|
|
|
|
async def _fake_pois(lat, lon, limit=5):
|
|
calls["poi"] = {"lat": lat, "lon": lon}
|
|
return [{"name": "Stadtpark", "type": "park", "distance_m": 120}]
|
|
|
|
monkeypatch.setattr(diary_mod.weather_mod, "get_weather_for_date", _fake_weather)
|
|
monkeypatch.setattr(diary_mod, "_fetch_pois_for_coords", _fake_pois)
|
|
return calls
|
|
|
|
|
|
def _create_without_gps(client, user, dog, datum="2026-06-12"):
|
|
r = client.post(
|
|
f"/api/dogs/{dog['id']}/diary",
|
|
headers=user["headers"],
|
|
json={"titel": "Ohne Standort", "text": "Indoor-Eintrag ohne GPS.", "datum": datum},
|
|
)
|
|
assert r.status_code == 201, r.text
|
|
e = r.json()
|
|
assert not e.get("weather_json") and not e.get("poi_json")
|
|
return e
|
|
|
|
|
|
def _loads(v):
|
|
return json.loads(v) if isinstance(v, str) else v
|
|
|
|
|
|
def test_manual_position_enriches_weather_and_pois(client, user, dog, stub_enrich):
|
|
entry = _create_without_gps(client, user, dog)
|
|
r = client.patch(
|
|
f"/api/dogs/{dog['id']}/diary/{entry['id']}",
|
|
headers=user["headers"],
|
|
json={"gps_lat": 48.137, "gps_lon": 11.575, "location_name": "München"},
|
|
)
|
|
assert r.status_code == 200, r.text
|
|
out = r.json()
|
|
w = _loads(out["weather_json"])
|
|
p = _loads(out["poi_json"])
|
|
assert w["weathercode"] == 61
|
|
assert p[0]["name"] == "Stadtpark"
|
|
assert out["location_name"] == "München"
|
|
# Wetter wurde fürs EINTRAGSDATUM geholt (historisch korrekt), nicht "heute".
|
|
assert stub_enrich["weather"]["datum"] == "2026-06-12"
|
|
|
|
|
|
def test_edit_without_gps_does_not_enrich(client, user, dog, stub_enrich):
|
|
entry = _create_without_gps(client, user, dog)
|
|
r = client.patch(
|
|
f"/api/dogs/{dog['id']}/diary/{entry['id']}",
|
|
headers=user["headers"],
|
|
json={"titel": "Nur Titel geändert"},
|
|
)
|
|
assert r.status_code == 200, r.text
|
|
assert "weather" not in stub_enrich and "poi" not in stub_enrich
|
|
|
|
|
|
def test_resave_same_coords_does_not_refetch(client, user, dog, stub_enrich):
|
|
entry = _create_without_gps(client, user, dog)
|
|
# 1. Positionierung → reichert an
|
|
client.patch(f"/api/dogs/{dog['id']}/diary/{entry['id']}", headers=user["headers"],
|
|
json={"gps_lat": 48.137, "gps_lon": 11.575})
|
|
assert "weather" in stub_enrich
|
|
stub_enrich.clear()
|
|
# erneut speichern mit IDENTISCHEN Koordinaten → keine erneute Anreicherung
|
|
r = client.patch(f"/api/dogs/{dog['id']}/diary/{entry['id']}", headers=user["headers"],
|
|
json={"gps_lat": 48.137, "gps_lon": 11.575})
|
|
assert r.status_code == 200
|
|
assert "weather" not in stub_enrich and "poi" not in stub_enrich
|