diff --git a/modules/app_trip.py b/modules/app_trip.py index 6e91da6..f50cd46 100644 --- a/modules/app_trip.py +++ b/modules/app_trip.py @@ -198,16 +198,28 @@ def build_trip_summaries(raw_rows): if not any(event["obd"] for event in events) and duration_seconds < 10: continue + # Speed-only rows (logged before the first GPS fix) have NULL coords. + # Use the first/last row that actually has a position for start/end GPS, + # and skip a trip that never got any fix at all (no location to record). + gps_rows = [ + r for r in trip_rows + if r["latitude"] is not None and r["longitude"] is not None + ] + if not gps_rows: + continue + start_gps = gps_rows[0] + end_gps = gps_rows[-1] + trips.append({ "id": start["id"], "start_tracking_id": start["id"], "end_tracking_id": end["id"], "start_datetime": start["datetime"].isoformat(sep=" "), "end_datetime": end["datetime"].isoformat(sep=" "), - "start_gpsx": start["latitude"], - "start_gpsy": start["longitude"], - "end_gpsx": end["latitude"], - "end_gpsy": end["longitude"], + "start_gpsx": start_gps["latitude"], + "start_gpsy": start_gps["longitude"], + "end_gpsx": end_gps["latitude"], + "end_gpsy": end_gps["longitude"], "distance_km": round(distance_km, 3), "duration_seconds": duration_seconds, "point_count": len(trip_rows), diff --git a/tests/test_app_trip.py b/tests/test_app_trip.py index 76cb5b9..ba6995b 100644 --- a/tests/test_app_trip.py +++ b/tests/test_app_trip.py @@ -115,7 +115,7 @@ class TripBuilderTest(unittest.TestCase): self.assertEqual(trips[0]["start_tracking_id"], 1) self.assertEqual(trips[0]["end_tracking_id"], 5) - def test_speed_only_rows_without_gps_build_trip(self): + def test_speed_only_rows_without_any_gps_are_skipped(self): started = datetime(2026, 1, 1, 8) rows = [ (index + 1, started + timedelta(seconds=index * 5), None, None, 30) @@ -124,12 +124,27 @@ class TripBuilderTest(unittest.TestCase): trips = app_trip.build_trip_summaries(rows) + # No GPS fix at all -> no location to record -> no trip (the speed-only + # rows remain in the DB but can't form a geolocated trip). + self.assertEqual(len(trips), 0) + + def test_speed_only_start_uses_first_gps_fix_for_start_coords(self): + started = datetime(2026, 1, 1, 8) + rows = [ + (1, started, None, None, 30), + (2, started + timedelta(seconds=5), None, None, 30), + (3, started + timedelta(seconds=10), 45.5000, -73.5000, 30), + (4, started + timedelta(seconds=15), 45.5001, -73.5000, 30), + (5, started + timedelta(seconds=20), 45.5002, -73.5000, 30), + ] + + trips = app_trip.build_trip_summaries(rows) + self.assertEqual(len(trips), 1) self.assertEqual(trips[0]["start_tracking_id"], 1) - self.assertEqual(trips[0]["end_tracking_id"], 4) - self.assertEqual(trips[0]["distance_km"], 0) - self.assertEqual(trips[0]["max_speed_kmh"], 30) - self.assertEqual(trips[0]["point_count"], 4) + self.assertEqual(trips[0]["end_tracking_id"], 5) + self.assertEqual(trips[0]["start_gpsx"], 45.5) + self.assertEqual(trips[0]["start_gpsy"], -73.5) def test_clock_rollback_does_not_merge_motion_episodes(self): first = self.rows_for_route(