-- migrate:up -- serve.fn_live_view — single source of truth for the live dashboard. -- Implements PRD F1.6 dedup (tracker-first → 24h freshness → camera fallback -- → most-recent fix → activation_time tiebreak) AND PRD F1.11 lifecycle -- filter (lifecycle='active' only). CREATE OR REPLACE FUNCTION serve.fn_live_view(filters jsonb) RETURNS jsonb LANGUAGE plpgsql STABLE AS $$ DECLARE fresh_window interval := COALESCE((filters->>'fresh_window')::interval, interval '24 hours'); offline_after interval := COALESCE((filters->>'offline_after')::interval, interval '5 minutes'); move_speed_kmh numeric := COALESCE((filters->>'move_speed_kmh')::numeric, 5); p_cost_centre text := filters->>'cost_centre'; p_assigned_city text := filters->>'assigned_city'; p_vehicle_numbers text[] := CASE WHEN filters ? 'vehicle_numbers' THEN ARRAY(SELECT jsonb_array_elements_text(filters->'vehicle_numbers')) ELSE NULL END; result jsonb; BEGIN WITH candidates AS ( SELECT lp.imei, lp.occurred_at, lp.geom, lp.speed_kmh, lp.direction_deg, d.device_type, d.activation_at, v.vehicle_id, v.plate, v.cost_centre, v.assigned_city FROM state.live_positions lp JOIN domain.devices d ON d.imei = lp.imei JOIN domain.vehicles v ON v.vehicle_id = d.vehicle_id WHERE d.lifecycle = 'active' AND (p_cost_centre IS NULL OR v.cost_centre = p_cost_centre) AND (p_assigned_city IS NULL OR v.assigned_city = p_assigned_city) AND (p_vehicle_numbers IS NULL OR v.plate = ANY (p_vehicle_numbers)) ), ranked AS ( SELECT c.*, ROW_NUMBER() OVER ( PARTITION BY c.vehicle_id ORDER BY -- tracker-first CASE c.device_type WHEN 'tracker' THEN 0 ELSE 1 END, -- 24h freshness gate: fresh tracker beats stale tracker CASE WHEN c.occurred_at > now() - fresh_window THEN 0 ELSE 1 END, -- intra-type tiebreak by most-recent fix c.occurred_at DESC, -- final tiebreak by activation_time c.activation_at DESC NULLS LAST ) AS rn FROM candidates c ), deduped AS ( SELECT * FROM ranked WHERE rn = 1 ), enriched AS ( SELECT d.*, CASE WHEN d.occurred_at <= now() - offline_after THEN 'offline' WHEN d.speed_kmh IS NOT NULL AND d.speed_kmh > move_speed_kmh THEN 'moving' ELSE 'parked' END AS operational_state FROM deduped d ), summary AS ( SELECT jsonb_build_object( 'total_active', count(*), 'moving', count(*) FILTER (WHERE operational_state = 'moving'), 'parked', count(*) FILTER (WHERE operational_state = 'parked'), 'offline', count(*) FILTER (WHERE operational_state = 'offline'), 'below_freshness_slo', count(*) FILTER ( WHERE occurred_at <= now() - interval '90 seconds' ), 'as_of', to_char(now() AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS"Z"') ) AS s FROM enriched ), features AS ( SELECT COALESCE(jsonb_agg( jsonb_build_object( 'type', 'Feature', 'geometry', ST_AsGeoJSON(e.geom)::jsonb, 'properties', jsonb_build_object( 'vehicle_id', e.vehicle_id, 'plate', e.plate, 'imei', e.imei, 'device_type', e.device_type, 'cost_centre', e.cost_centre, 'assigned_city', e.assigned_city, 'occurred_at', to_char(e.occurred_at AT TIME ZONE 'UTC', 'YYYY-MM-DD"T"HH24:MI:SS"Z"'), 'speed_kmh', e.speed_kmh, 'direction_deg', e.direction_deg, 'operational_state', e.operational_state, 'style_class', 'vehicle-' || e.operational_state ) ) ), '[]'::jsonb) AS feats FROM enriched e ), slo_block AS ( SELECT COALESCE(jsonb_object_agg( metric, jsonb_build_object( 'threshold', threshold, 'current', current_value, 'status', status ) ), '{}'::jsonb) AS ss FROM slo.v_current_status ) SELECT jsonb_build_object( 'summary', (SELECT s FROM summary), 'geojson', jsonb_build_object( 'type', 'FeatureCollection', 'features', (SELECT feats FROM features) ), 'slo_status', (SELECT ss FROM slo_block) ) INTO result; RETURN result; END; $$; -- migrate:down DROP FUNCTION IF EXISTS serve.fn_live_view(jsonb);