deckgl_tracksolid/db/migrations/007_trips_for_day_v2.sql
David Kiania b0db22c669 feat(db): surface upstream trips enrichment columns in PostgREST RPCs
Adds migrations 006/007/008 that wrap tracksolid.v_trips_enriched
(introduced upstream in tracksolid_timescale_grafana_prod migration 09)
and DROP+CREATE the trips_for_day / trips_for_range RPCs to return the
new metadata columns: vehicle_plate, start_address, end_address,
driving_time_s, idle_time_s, fuel_consumed_l, and daily_seq.

Path/timestamps logic is unchanged — position_history is still scanned
to produce the per-vertex timestamps_rel that drive deck.gl TripsLayer
animation. route_geom can't replace this since it carries no per-vertex
time, but the GIST index on it is available for future area filters.

Day filter switches from start_time::date to v.trip_date_eat (the
Africa/Nairobi local date already computed by v_trips_enriched), which
fixes the latent bug where trips spanning midnight UTC could be
misbucketed.

Frontend Trip type still receives all original fields in compatible
positions; the new fields are additive and ignored by the existing
TripsLayer code.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-02 11:09:55 +03:00

82 lines
2.6 KiB
PL/PgSQL

-- 007_trips_for_day_v2.sql
-- Replaces 003_trips_for_day_rpc.sql to surface the enriched trip columns
-- exposed by 006_trips_viz_view_v2.sql.
--
-- Path/timestamps logic is unchanged: position_history is still scanned per
-- trip to build the LineString and per-vertex timestamps_rel needed by
-- deck.gl TripsLayer animation. (route_geom alone can't replace this — it
-- carries no per-vertex time.) Win is metadata: vehicle_plate, addresses,
-- daily_seq, drive/idle/fuel — all returned in one round-trip.
--
-- DROP + CREATE because the return type signature changes; CREATE OR REPLACE
-- FUNCTION cannot alter return columns.
DROP FUNCTION IF EXISTS public.trips_for_day(date, text[]);
CREATE FUNCTION public.trips_for_day(
p_date date,
p_cost_centres text[] DEFAULT NULL
) RETURNS TABLE (
trip_id bigint,
imei text,
vehicle_name text,
vehicle_number text,
cost_centre text,
start_time timestamptz,
end_time timestamptz,
distance_km numeric,
vehicle_plate text,
start_address text,
end_address text,
driving_time_s integer,
idle_time_s integer,
fuel_consumed_l numeric,
daily_seq bigint,
path_geojson json,
timestamps_rel int[]
)
LANGUAGE sql STABLE
SECURITY DEFINER
SET search_path = public, tracksolid
AS $$
WITH day_trips AS (
SELECT v.*
FROM public.trips_viz_v1 v
WHERE v.trip_date_eat = p_date
AND (p_cost_centres IS NULL OR v.cost_centre = ANY(p_cost_centres))
)
SELECT
dt.trip_id,
dt.imei,
dt.vehicle_name,
dt.vehicle_number,
dt.cost_centre,
dt.start_time,
dt.end_time,
dt.distance_km,
dt.vehicle_plate,
dt.start_address,
dt.end_address,
dt.driving_time_s,
dt.idle_time_s,
dt.fuel_consumed_l,
dt.daily_seq,
ST_AsGeoJSON(ST_MakeLine(ph.geom ORDER BY ph.gps_time))::json AS path_geojson,
array_agg(
EXTRACT(EPOCH FROM ph.gps_time - dt.start_time)::int
ORDER BY ph.gps_time
) AS timestamps_rel
FROM day_trips dt
JOIN tracksolid.position_history ph
ON ph.imei = dt.imei
AND ph.gps_time BETWEEN dt.start_time AND dt.end_time
AND ph.geom IS NOT NULL
GROUP BY dt.trip_id, dt.imei, dt.vehicle_name, dt.vehicle_number,
dt.cost_centre, dt.start_time, dt.end_time, dt.distance_km,
dt.vehicle_plate, dt.start_address, dt.end_address,
dt.driving_time_s, dt.idle_time_s, dt.fuel_consumed_l,
dt.daily_seq
HAVING count(ph.geom) >= 2;
$$;
GRANT EXECUTE ON FUNCTION public.trips_for_day(date, text[]) TO viz_anon;