Routen-Validierung: >15 km/h Ø zählt nicht für Stats/Trophäen, SW by-v331
This commit is contained in:
parent
b5e4eab84d
commit
7ac421fcf9
7 changed files with 40 additions and 16 deletions
|
|
@ -1017,3 +1017,10 @@ def _migrate(conn_factory):
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
logger.info("Migration: push_subscriptions last_lat/lon bereit.")
|
logger.info("Migration: push_subscriptions last_lat/lon bereit.")
|
||||||
|
|
||||||
|
# Routen: Geschwindigkeits-Validierung
|
||||||
|
try:
|
||||||
|
conn.execute("ALTER TABLE routes ADD COLUMN is_valid INTEGER NOT NULL DEFAULT 1")
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
logger.info("Migration: routes.is_valid bereit.")
|
||||||
|
|
|
||||||
|
|
@ -125,10 +125,10 @@ def update_streak(user_id: int, conn):
|
||||||
def check_and_award(user_id: int, conn):
|
def check_and_award(user_id: int, conn):
|
||||||
stats = conn.execute("""
|
stats = conn.execute("""
|
||||||
SELECT ROUND(
|
SELECT ROUND(
|
||||||
COALESCE((SELECT SUM(r.distanz_km) FROM routes r WHERE r.user_id=? ), 0) +
|
COALESCE((SELECT SUM(r.distanz_km) FROM routes r WHERE r.user_id=? AND r.is_valid=1), 0) +
|
||||||
COALESCE((SELECT SUM(w.walked_km) FROM route_walks w WHERE w.user_id=?), 0),
|
COALESCE((SELECT SUM(w.walked_km) FROM route_walks w WHERE w.user_id=?), 0),
|
||||||
1) AS total_km,
|
1) AS total_km,
|
||||||
(SELECT COUNT(*) FROM routes r WHERE r.user_id=? ) AS routen,
|
(SELECT COUNT(*) FROM routes r WHERE r.user_id=? AND r.is_valid=1) AS routen,
|
||||||
(SELECT COUNT(*) FROM user_map_pois p WHERE p.user_id=?) AS pois
|
(SELECT COUNT(*) FROM user_map_pois p WHERE p.user_id=?) AS pois
|
||||||
FROM (SELECT 1)
|
FROM (SELECT 1)
|
||||||
""", (user_id, user_id, user_id, user_id)).fetchone()
|
""", (user_id, user_id, user_id, user_id)).fetchone()
|
||||||
|
|
@ -178,17 +178,17 @@ async def my_achievements(user=Depends(get_current_user)):
|
||||||
|
|
||||||
stats = conn.execute("""
|
stats = conn.execute("""
|
||||||
SELECT ROUND(
|
SELECT ROUND(
|
||||||
COALESCE((SELECT SUM(r.distanz_km) FROM routes r WHERE r.user_id=? ), 0) +
|
COALESCE((SELECT SUM(r.distanz_km) FROM routes r WHERE r.user_id=? AND r.is_valid=1), 0) +
|
||||||
COALESCE((SELECT SUM(w.walked_km) FROM route_walks w WHERE w.user_id=?), 0),
|
COALESCE((SELECT SUM(w.walked_km) FROM route_walks w WHERE w.user_id=?), 0),
|
||||||
1) AS total_km,
|
1) AS total_km,
|
||||||
(SELECT COUNT(*) FROM routes r WHERE r.user_id=? ) AS routen,
|
(SELECT COUNT(*) FROM routes r WHERE r.user_id=? AND r.is_valid=1) AS routen,
|
||||||
(SELECT COUNT(*) FROM user_map_pois p WHERE p.user_id=?) AS pois,
|
(SELECT COUNT(*) FROM user_map_pois p WHERE p.user_id=?) AS pois,
|
||||||
ROUND(
|
ROUND(
|
||||||
COALESCE((SELECT SUM(r.distanz_km) FROM routes r WHERE r.user_id=? ), 0) +
|
COALESCE((SELECT SUM(r.distanz_km) FROM routes r WHERE r.user_id=? AND r.is_valid=1), 0) +
|
||||||
COALESCE((SELECT SUM(w.walked_km) FROM route_walks w WHERE w.user_id=?), 0),
|
COALESCE((SELECT SUM(w.walked_km) FROM route_walks w WHERE w.user_id=?), 0),
|
||||||
1)*1
|
1)*1
|
||||||
+ (SELECT COUNT(*) FROM user_map_pois p WHERE p.user_id=?)*5
|
+ (SELECT COUNT(*) FROM user_map_pois p WHERE p.user_id=?)*5
|
||||||
+ (SELECT COUNT(*) FROM routes r WHERE r.user_id=? )*10 AS punkte
|
+ (SELECT COUNT(*) FROM routes r WHERE r.user_id=? AND r.is_valid=1)*10 AS punkte
|
||||||
FROM (SELECT 1)
|
FROM (SELECT 1)
|
||||||
""", (uid, uid, uid, uid, uid, uid, uid, uid)).fetchone()
|
""", (uid, uid, uid, uid, uid, uid, uid, uid)).fetchone()
|
||||||
|
|
||||||
|
|
@ -208,7 +208,8 @@ async def my_achievements(user=Depends(get_current_user)):
|
||||||
+COUNT(DISTINCT p.id)*5
|
+COUNT(DISTINCT p.id)*5
|
||||||
+COUNT(DISTINCT r.id)*10 AS punkte
|
+COUNT(DISTINCT r.id)*10 AS punkte
|
||||||
FROM users u
|
FROM users u
|
||||||
LEFT JOIN routes r ON r.user_id=u.id LEFT JOIN user_map_pois p ON p.user_id=u.id
|
LEFT JOIN routes r ON r.user_id=u.id AND r.is_valid=1
|
||||||
|
LEFT JOIN user_map_pois p ON p.user_id=u.id
|
||||||
GROUP BY u.id
|
GROUP BY u.id
|
||||||
) WHERE punkte > ?
|
) WHERE punkte > ?
|
||||||
""", (stats["punkte"] if stats else 0,)).fetchone()
|
""", (stats["punkte"] if stats else 0,)).fetchone()
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,14 @@ from routes.push import send_push_to_user
|
||||||
|
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
_MAX_AVG_KMH = 15.0 # Über diesem Wert wird die Route nicht für Stats/Trophäen gewertet
|
||||||
|
|
||||||
|
def _check_speed(distanz_km, dauer_min) -> bool:
|
||||||
|
"""True = gültig, False = zu schnell (wahrscheinlich motorisiert)."""
|
||||||
|
if not distanz_km or not dauer_min or dauer_min <= 0:
|
||||||
|
return True
|
||||||
|
return (distanz_km / (dauer_min / 60)) <= _MAX_AVG_KMH
|
||||||
|
|
||||||
|
|
||||||
def _haversine(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
|
def _haversine(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
|
||||||
R = 6_371_000
|
R = 6_371_000
|
||||||
|
|
@ -136,25 +144,29 @@ async def create_route(data: RouteCreate, user=Depends(get_current_user)):
|
||||||
raise HTTPException(400, "GPS-Track braucht mindestens 2 Punkte.")
|
raise HTTPException(400, "GPS-Track braucht mindestens 2 Punkte.")
|
||||||
|
|
||||||
gps_json = json.dumps([p.model_dump() for p in data.gps_track])
|
gps_json = json.dumps([p.model_dump() for p in data.gps_track])
|
||||||
|
is_valid = int(_check_speed(data.distanz_km, data.dauer_min))
|
||||||
|
|
||||||
with db() as conn:
|
with db() as conn:
|
||||||
cur = conn.execute("""
|
cur = conn.execute("""
|
||||||
INSERT INTO routes
|
INSERT INTO routes
|
||||||
(user_id, name, beschreibung, gps_track, distanz_km, dauer_min,
|
(user_id, name, beschreibung, gps_track, distanz_km, dauer_min,
|
||||||
schwierigkeit, untergrund, schatten, leine_empfohlen, is_public, hunde_tauglichkeit)
|
schwierigkeit, untergrund, schatten, leine_empfohlen, is_public,
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
hunde_tauglichkeit, is_valid)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
""", (
|
""", (
|
||||||
user['id'], data.name, data.beschreibung, gps_json,
|
user['id'], data.name, data.beschreibung, gps_json,
|
||||||
data.distanz_km, data.dauer_min, data.schwierigkeit, data.untergrund,
|
data.distanz_km, data.dauer_min, data.schwierigkeit, data.untergrund,
|
||||||
int(data.schatten) if data.schatten is not None else None,
|
int(data.schatten) if data.schatten is not None else None,
|
||||||
int(data.leine_empfohlen) if data.leine_empfohlen is not None else None,
|
int(data.leine_empfohlen) if data.leine_empfohlen is not None else None,
|
||||||
int(data.is_public) if data.is_public is not None else 1,
|
int(data.is_public) if data.is_public is not None else 1,
|
||||||
data.hunde_tauglichkeit,
|
data.hunde_tauglichkeit, is_valid,
|
||||||
))
|
))
|
||||||
row = conn.execute("SELECT * FROM routes WHERE id = ?", (cur.lastrowid,)).fetchone()
|
row = conn.execute("SELECT * FROM routes WHERE id = ?", (cur.lastrowid,)).fetchone()
|
||||||
update_streak(user['id'], conn)
|
update_streak(user['id'], conn)
|
||||||
check_and_award(user['id'], conn)
|
check_and_award(user['id'], conn)
|
||||||
return _parse(row)
|
result = _parse(row)
|
||||||
|
result['is_valid'] = bool(is_valid)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ _STATS_SQL = """
|
||||||
+ COUNT(DISTINCT p.id) * 5
|
+ COUNT(DISTINCT p.id) * 5
|
||||||
+ COUNT(DISTINCT r.id) * 10 AS punkte
|
+ COUNT(DISTINCT r.id) * 10 AS punkte
|
||||||
FROM users u
|
FROM users u
|
||||||
LEFT JOIN routes r ON r.user_id = u.id
|
LEFT JOIN routes r ON r.user_id = u.id AND r.is_valid = 1
|
||||||
LEFT JOIN user_map_pois p ON p.user_id = u.id
|
LEFT JOIN user_map_pois p ON p.user_id = u.id
|
||||||
GROUP BY u.id
|
GROUP BY u.id
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
Router, State-Management, Navigation, Initialisierung.
|
Router, State-Management, Navigation, Initialisierung.
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
const APP_VER = '318'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
const APP_VER = '319'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||||
|
|
||||||
const App = (() => {
|
const App = (() => {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1510,7 +1510,11 @@ window.Page_map = (() => {
|
||||||
UI.modal.close();
|
UI.modal.close();
|
||||||
if (_recPolyline) { _recPolyline.remove(); _recPolyline = null; }
|
if (_recPolyline) { _recPolyline.remove(); _recPolyline = null; }
|
||||||
if (_recMarker) { _recMarker.remove(); _recMarker = null; }
|
if (_recMarker) { _recMarker.remove(); _recMarker = null; }
|
||||||
|
if (saved.is_valid === false) {
|
||||||
|
UI.toast.warning(`Route „${saved.name}" gespeichert — wird nicht für Statistiken gewertet (Geschwindigkeit zu hoch).`);
|
||||||
|
} else {
|
||||||
UI.toast.success(`Route „${saved.name}" gespeichert!`);
|
UI.toast.success(`Route „${saved.name}" gespeichert!`);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
Offline-Cache + Push Notifications + Tile-Cache
|
Offline-Cache + Push Notifications + Tile-Cache
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
const CACHE_VERSION = 'by-v330';
|
const CACHE_VERSION = 'by-v331';
|
||||||
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
||||||
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue