From 87ecab4a72fb94334071903107ce908a2e1005a3 Mon Sep 17 00:00:00 2001 From: David Kiania Date: Sat, 11 Apr 2026 18:42:22 +0300 Subject: [PATCH] Wire /pushevent to device_events table (was log-only) LOGIN/LOGOUT events from Jimi now persist to tracksolid.device_events. Table already existed with correct schema (imei, event_type, event_time, timezone, unique constraint). Follows same SAVEPOINT + log_ingestion pattern as all other DB-writing endpoints. Co-Authored-By: Claude Sonnet 4.6 --- webhook_receiver_rev.py | 44 ++++++++++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/webhook_receiver_rev.py b/webhook_receiver_rev.py index a5c338e..1b87ffe 100644 --- a/webhook_receiver_rev.py +++ b/webhook_receiver_rev.py @@ -15,7 +15,7 @@ ENDPOINTS: /pushgps — GPS positions (Priority 2) /pushhb — Device heartbeats (Priority 2) /pushtripreport — Trip reports (Priority 2) - /pushevent — Device events (Priority 3, log-only) + /pushevent — Device LOGIN/LOGOUT events (Priority 3) /health — Healthcheck for Docker/monitoring REVISIONS (QA-Verified): @@ -23,7 +23,7 @@ REVISIONS (QA-Verified): [BUG-02] push_alarm: guard also checks alarm_type is not null (prevents FK violation). [BUG-03] push_trip_report: _parse_trip_ts handles Jimi BCD format YYMMDDHHmmss. [BUG-04] SAVEPOINT per item in all DB-writing endpoints (one bad item won't abort batch). - [BUG-05] Added /pushevent endpoint (log-only, prevents Jimi 404 errors). + [BUG-05] Added /pushevent endpoint → writes to tracksolid.device_events. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ """ @@ -513,14 +513,44 @@ def push_trip_report(token: str = Form(""), data_list: str = Form("")): log.info("pushtripreport: %d/%d items processed.", inserted, len(items)) return JSONResponse(content=SUCCESS) -# ── 7. Device Events (Priority 3 — log only) ───────────────────────────────── +# ── 7. Device Events (LOGIN / LOGOUT) ──────────────────────────────────────── @app.post("/pushevent") def push_event(token: str = Form(""), data_list: str = Form("")): - """[BUG-05] Accept Jimi event pushes so they don't 404. Log for future schema work.""" _validate_token(token) items = _parse_data_list(data_list) - for item in items: - log.info("pushevent: imei=%s type=%s gateTime=%s", - item.get("deviceImei"), item.get("type"), item.get("gateTime")) + if not items: + return JSONResponse(content=SUCCESS) + + t0 = time.time() + inserted = 0 + + with get_conn() as conn: + with conn.cursor() as cur: + for item in items: + try: + cur.execute("SAVEPOINT sp") + imei = clean(item.get("deviceImei")) + event_type = clean(item.get("type")) + event_time = clean_ts(item.get("gateTime")) + if not imei or not event_type or not event_time: + cur.execute("RELEASE SAVEPOINT sp") + continue + + cur.execute(""" + INSERT INTO tracksolid.device_events + (imei, event_type, event_time, timezone) + VALUES (%s, %s, %s, %s) + ON CONFLICT (imei, event_type, event_time) DO NOTHING + """, (imei, event_type, event_time, clean(item.get("timezone")))) + cur.execute("RELEASE SAVEPOINT sp") + inserted += 1 + except Exception: + cur.execute("ROLLBACK TO SAVEPOINT sp") + log.warning("Failed to process event for %s", item.get("deviceImei"), exc_info=True) + + log_ingestion(cur, "webhook/pushevent", len(items), 0, inserted, + int((time.time() - t0) * 1000), True) + + log.info("pushevent: %d/%d items processed.", inserted, len(items)) return JSONResponse(content=SUCCESS)