feat(reporting): add vehicle_type + fleet_segment to live map feed (migration 16)
fn_live_positions now emits 'vehicle_type' (devices.vehicle_models) and 'fleet_segment' (reporting.fn_fleet_segment) in each GeoJSON feature so FleetNow can give specialist vehicles (Crane/Motorbike/Pick-Up) their own marker icons. Additive only — no signature change, STABLE function read immediately by dashboard_api (no redeploy). Function body reproduced verbatim from prod via pg_get_functiondef plus the two new properties. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
0c32094347
commit
a8e1327aa8
2 changed files with 100 additions and 0 deletions
99
migrations/16_live_feed_vehicle_type.sql
Normal file
99
migrations/16_live_feed_vehicle_type.sql
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
-- 16_live_feed_vehicle_type.sql
|
||||
-- Expose vehicle_type + fleet_segment on the live-map GeoJSON feed so FleetNow can give the
|
||||
-- specialist vehicles (Crane / Motorbike / Pick-Up) their own marker icons. All other vehicles
|
||||
-- (field-service + unassigned) keep their current marker — FleetNow only overrides icons when
|
||||
-- vehicle_type is one of the specialist types.
|
||||
--
|
||||
-- reporting.fn_live_positions is reproduced verbatim from the live prod definition
|
||||
-- (== migrations/11_reporting_schema.sql, captured via pg_get_functiondef) with TWO added
|
||||
-- feature properties:
|
||||
-- 'vehicle_type' = devices.vehicle_models (authoritative API type, surfaced by v_live_positions)
|
||||
-- 'fleet_segment' = reporting.fn_fleet_segment(vehicle_models) (field_service|specialist|unassigned)
|
||||
-- No signature change, so dependents are unaffected; STABLE function, read immediately by
|
||||
-- dashboard_api (no redeploy/restart). Safe to re-apply (CREATE OR REPLACE).
|
||||
|
||||
SET search_path = tracksolid, reporting, public;
|
||||
|
||||
CREATE OR REPLACE FUNCTION reporting.fn_live_positions(p_cost_centre text DEFAULT NULL::text, p_acc_status text DEFAULT NULL::text)
|
||||
RETURNS jsonb
|
||||
LANGUAGE plpgsql
|
||||
STABLE
|
||||
AS $function$
|
||||
DECLARE
|
||||
v_result jsonb;
|
||||
BEGIN
|
||||
p_cost_centre := NULLIF(p_cost_centre, '');
|
||||
p_acc_status := NULLIF(p_acc_status, '');
|
||||
WITH filtered AS (
|
||||
SELECT * FROM reporting.v_live_positions
|
||||
WHERE (p_cost_centre IS NULL OR cost_centre = p_cost_centre)
|
||||
AND (p_acc_status IS NULL OR acc_status = p_acc_status)
|
||||
)
|
||||
SELECT jsonb_build_object(
|
||||
'summary', jsonb_build_object(
|
||||
'vehicle_count', COUNT(*),
|
||||
-- "moving" and "parked" both restrict to devices that have reported
|
||||
-- within the OFFLINE_THRESHOLD (24 h) so they represent the live
|
||||
-- fleet, not equipment-failure stragglers. "offline" is its own
|
||||
-- counter for the > 24 h tail.
|
||||
'moving', COUNT(*) FILTER (WHERE acc_status = '1'
|
||||
AND source_age_hours < 24),
|
||||
'parked', COUNT(*) FILTER (WHERE acc_status = '0'
|
||||
AND source_age_hours < 24),
|
||||
'offline', COUNT(*) FILTER (WHERE source_age_hours >= 24),
|
||||
'median_speed_moving', percentile_cont(0.5) WITHIN GROUP (ORDER BY speed)
|
||||
FILTER (WHERE acc_status = '1'
|
||||
AND source_age_hours < 24
|
||||
AND speed > 0),
|
||||
'last_batch_at', to_char(MAX(updated_at) AT TIME ZONE 'Africa/Nairobi',
|
||||
'YYYY-MM-DD HH24:MI:SS'),
|
||||
'oldest_fix_at', to_char(MIN(gps_time) AT TIME ZONE 'Africa/Nairobi',
|
||||
'YYYY-MM-DD HH24:MI:SS'),
|
||||
'newest_fix_at', to_char(MAX(gps_time) AT TIME ZONE 'Africa/Nairobi',
|
||||
'YYYY-MM-DD HH24:MI:SS'),
|
||||
'last_batch_utc', MAX(updated_at),
|
||||
'newest_fix_utc', MAX(gps_time)
|
||||
),
|
||||
'geojson', jsonb_build_object(
|
||||
'type', 'FeatureCollection',
|
||||
'features', COALESCE(jsonb_agg(
|
||||
jsonb_build_object(
|
||||
'type', 'Feature',
|
||||
'properties', jsonb_build_object(
|
||||
'imei', imei,
|
||||
'vehicle_number', vehicle_number,
|
||||
'driver', assigned_driver,
|
||||
'cost_centre', cost_centre,
|
||||
'assigned_city', assigned_city,
|
||||
'vehicle_category', vehicle_category,
|
||||
'vehicle_type', vehicle_models,
|
||||
'fleet_segment', reporting.fn_fleet_segment(vehicle_models),
|
||||
'mc_type', mc_type,
|
||||
'device_kind', device_kind,
|
||||
'source_age_hours', source_age_hours,
|
||||
'speed', speed,
|
||||
'direction', direction,
|
||||
'acc_status', acc_status,
|
||||
'device_status', device_status,
|
||||
'gps_signal', gps_signal,
|
||||
'gps_num', gps_num,
|
||||
'current_mileage', current_mileage,
|
||||
'loc_desc', loc_desc,
|
||||
'gps_time', to_char(gps_time AT TIME ZONE 'Africa/Nairobi',
|
||||
'YYYY-MM-DD HH24:MI:SS'),
|
||||
'updated_at', to_char(updated_at AT TIME ZONE 'Africa/Nairobi',
|
||||
'YYYY-MM-DD HH24:MI:SS'),
|
||||
'gps_time_utc', gps_time,
|
||||
'updated_at_utc', updated_at
|
||||
),
|
||||
'geometry', jsonb_build_object(
|
||||
'type', 'Point',
|
||||
'coordinates', jsonb_build_array(lng, lat)
|
||||
)
|
||||
)
|
||||
), '[]'::jsonb)
|
||||
)
|
||||
) INTO v_result FROM filtered;
|
||||
|
||||
RETURN v_result;
|
||||
END $function$;
|
||||
|
|
@ -39,6 +39,7 @@ MIGRATIONS = [
|
|||
"13_drop_dwh_gold.sql", # purge dormant dwh_gold schema + v_utilisation_daily
|
||||
"14_fleet_segment_and_vehicles_view.sql", # reporting.fn_fleet_segment + reporting.v_vehicles roster
|
||||
"15_map_exclude_cost_centres.sql", # hide personal/management/mtn vehicles from the live map
|
||||
"16_live_feed_vehicle_type.sql", # add vehicle_type + fleet_segment to fn_live_positions feed
|
||||
]
|
||||
|
||||
# ── Tables that must exist before the service is allowed to start ─────────────
|
||||
|
|
|
|||
Loading…
Reference in a new issue