Feature: Ratings, Lightbox, Forum-Standort, Notifications, Routen-Recording, Chat-Picker

- Bewertungssystem (ratings.py): Sterne für Sitter/Walks/Places/Routen
- Admin: Server-Log-Viewer + OSM-Cache-Statistiken
- Chat: "Neue Nachricht"-Button mit Freundesliste-Picker
- Forum: 5 neue Kategorien, Standorteingabe (locationPicker), Absende-Toast, Lightbox
- Freunde: Aktivitäts-Filter (Chips), Freundschaftsanfrage → In-App-Notification
- Sitter: locationPicker statt manuelle Koordinateneingabe + ratingStars
- Tagebuch: Bilder-Lightbox im Detail-View, iOS-Modal-Header-Fix (90svh)
- Routen: Start/Stopp-Button wechselt Zustand, nutzt Page_map.isRecording()
- Benachrichtigungen: Delete-Button sichtbar, typ-basierte Navigation, Toast-Feedback
- OSM: globales Semaphore + 429-Retry-Logic; Scheduler: München-Umland, täglich
- SW by-v225, APP_VER 202
This commit is contained in:
rene 2026-04-19 09:40:35 +02:00
parent aa70a838f2
commit e56183b642
21 changed files with 648 additions and 175 deletions

View file

@ -20,6 +20,9 @@ CACHE_ZOOM = 12
CACHE_DAYS = 14
OVERPASS_URL = 'https://overpass-api.de/api/interpreter'
# Globales Limit: max 2 gleichzeitige Overpass-Anfragen (Prewarm + User geteilt)
_overpass_sem = asyncio.Semaphore(2)
OSM_QUERIES = {
'waste_basket': '[out:json][timeout:20];node["amenity"="waste_basket"]({bbox});out;',
'dog_park': '[out:json][timeout:25];(way["leisure"="dog_park"]({bbox});node["leisure"="dog_park"]({bbox});way["leisure"="park"]["dog"="yes"]({bbox});node["leisure"="park"]["dog"="yes"]({bbox}););out center;',
@ -61,10 +64,17 @@ def _covering_tiles(south, west, north, east, zoom):
# Overpass-Fetch + Cache
# ------------------------------------------------------------------
async def _fetch_overpass(query):
async with httpx.AsyncClient(timeout=40) as client:
r = await client.post(OVERPASS_URL, data={'data': query})
r.raise_for_status()
return r.json().get('elements', [])
for attempt in range(3):
async with _overpass_sem:
async with httpx.AsyncClient(timeout=40) as client:
r = await client.post(OVERPASS_URL, data={'data': query})
if r.status_code != 429:
r.raise_for_status()
return r.json().get('elements', [])
logger.warning(f"Overpass 429 (Versuch {attempt + 1}/3)")
# Semaphore freigeben, dann warten
await asyncio.sleep(45 * (attempt + 1))
raise Exception("Overpass 429 nach 3 Versuchen")
def _stale_tiles(poi_type, tiles):
stale = []
@ -143,11 +153,7 @@ async def get_pois(
stale = _stale_tiles(type, tiles)
if stale and not fast:
sem = asyncio.Semaphore(3)
async def _limited(x, y):
async with sem:
await _fetch_and_store_tile(type, x, y)
await asyncio.gather(*[_limited(x, y) for (x, y) in stale])
await asyncio.gather(*[_fetch_and_store_tile(type, x, y) for (x, y) in stale])
fetched_fresh = True
with db() as conn:
@ -313,12 +319,8 @@ async def analyze_region(
tiles = _covering_tiles(south, west, north, east, CACHE_ZOOM)
async def _warmup():
sem = asyncio.Semaphore(3)
async def _limited(poi_type, x, y):
async with sem:
await _fetch_and_store_tile(poi_type, x, y)
tasks = [
_limited(pt, x, y)
_fetch_and_store_tile(pt, x, y)
for pt in OSM_QUERIES
for (x, y) in _stale_tiles(pt, tiles)
]