From 20eade824f3093a14459eb086d43692353249f21 Mon Sep 17 00:00:00 2001 From: Devin Matte Date: Mon, 3 Mar 2025 20:00:16 -0500 Subject: [PATCH] Fetch segment speed data from API --- common/api/hooks/tripmetrics.ts | 25 +- common/api/tripmetrics.ts | 18 +- common/types/api.ts | 7 +- modules/speed/map/SegmentSpeedMap.tsx | 26 +- modules/speed/map/constants.ts | 2258 ------------------------- modules/speed/map/segment.ts | 4 +- server/.chalice/config.json | 2 +- server/.chalice/policy.json | 1 + server/app.py | 6 + server/chalicelib/dynamo.py | 8 +- server/chalicelib/speed.py | 20 + 11 files changed, 99 insertions(+), 2276 deletions(-) delete mode 100644 modules/speed/map/constants.ts diff --git a/common/api/hooks/tripmetrics.ts b/common/api/hooks/tripmetrics.ts index 7ea5745f7..7cb60dbba 100644 --- a/common/api/hooks/tripmetrics.ts +++ b/common/api/hooks/tripmetrics.ts @@ -1,7 +1,14 @@ import { useQuery } from '@tanstack/react-query'; -import type { FetchDeliveredTripMetricsOptions } from '../../types/api'; -import { FIVE_MINUTES } from '../../constants/time'; -import { fetchActualTripsByLine, fetchLandingTripMetrics } from '../tripmetrics'; +import type { + FetchDeliveredTripMetricsOptions, + FetchSegmentTripMetricsOptions, +} from '../../types/api'; +import { FIVE_MINUTES, THIRTY_MINUTES } from '../../constants/time'; +import { + fetchActualTripsByLine, + fetchLandingTripMetrics, + fetchSegmentTripMetrics, +} from '../tripmetrics'; export const useDeliveredTripMetrics = ( options: FetchDeliveredTripMetricsOptions, @@ -18,3 +25,15 @@ export const useDeliveredTripMetrics = ( export const useTripMetricsForLanding = () => { return useQuery({ queryKey: ['landingTrips'], queryFn: () => fetchLandingTripMetrics() }); }; + +export const useSegmentTripMetricsData = ( + options: FetchSegmentTripMetricsOptions, + enabled: boolean = true +) => { + return useQuery({ + queryKey: ['segmentTripMetrics', options], + queryFn: () => fetchSegmentTripMetrics(options), + enabled: enabled && !!options.date, + staleTime: THIRTY_MINUTES, + }); +}; diff --git a/common/api/tripmetrics.ts b/common/api/tripmetrics.ts index e7fb810e2..dab1e1d4c 100644 --- a/common/api/tripmetrics.ts +++ b/common/api/tripmetrics.ts @@ -1,4 +1,8 @@ -import type { FetchDeliveredTripMetricsOptions } from '../types/api'; +import type { SpeedPairData } from '../../modules/speed/map/segment'; +import type { + FetchDeliveredTripMetricsOptions, + FetchSegmentTripMetricsOptions, +} from '../types/api'; import { FetchDeliveredTripMetricsParams } from '../types/api'; import type { DeliveredTripMetrics } from '../types/dataPoints'; import type { Line } from '../types/lines'; @@ -20,3 +24,15 @@ export const fetchLandingTripMetrics = (): Promise<{ [key in Line]: DeliveredTri const tripMetricsURL = new URL(`/static/landing/trip_metrics.json`, window.location.origin); return fetch(tripMetricsURL.toString()).then((resp) => resp.json()); }; + +export const fetchSegmentTripMetrics = async ( + options: FetchSegmentTripMetricsOptions +): Promise => { + if (!options.line) return []; + + return await apiFetch({ + path: '/api/tripmetrics/segment', + options, + errorMessage: 'Failed to fetch segment trip metrics', + }); +}; diff --git a/common/types/api.ts b/common/types/api.ts index 6e78bf36b..f84e6cc7e 100644 --- a/common/types/api.ts +++ b/common/types/api.ts @@ -1,6 +1,6 @@ import type { AggType } from '../../modules/speed/constants/speeds'; import type { ServiceHours, SpeedRestriction } from './dataPoints'; -import type { Line, LineRouteId } from './lines'; +import type { Line, LineRouteId, LineShort } from './lines'; export enum QueryNameKeys { traveltimes = 'traveltimes', @@ -44,6 +44,11 @@ export type FetchSpeedsOptions = { line?: Line; }; +export type FetchSegmentTripMetricsOptions = { + date?: string; + line: LineShort; +}; + export type FetchDeliveredTripMetricsOptions = { agg: AggType; start_date?: string; diff --git a/modules/speed/map/SegmentSpeedMap.tsx b/modules/speed/map/SegmentSpeedMap.tsx index 1c766afac..b4f0cc1ce 100644 --- a/modules/speed/map/SegmentSpeedMap.tsx +++ b/modules/speed/map/SegmentSpeedMap.tsx @@ -12,8 +12,8 @@ import { getSegmentLabelOverrides } from '../../slowzones/map/segment'; import type { SlowZonesLineName } from '../../slowzones/types'; import { getStationDistance } from '../../../common/utils/stations'; import { convertSecondsToMph } from '../../landing/utils'; +import { useSegmentTripMetricsData } from '../../../common/api/hooks/tripmetrics'; import { segmentStationPairs } from './segment'; -import { TEST_DATA } from './constants'; import { SpeedSegmentLabel } from './SegmentSpeedLabel'; import { SegmentSpeedTooltip } from './SegmentSpeedTooltip'; @@ -49,16 +49,24 @@ export const SegmentSpeedMap: React.FC = ({ lineName, dire const { query } = useDelimitatedRoute(); const { endDate } = query; - // TODO: Pull from dynamo calculated hourly in data-ingestion - const speedData = TEST_DATA.map((data) => { - const intervalDistance = getStationDistance(data.from_id, data.to_id); - - return { - ...data, - speed: convertSecondsToMph(data.travel_time, intervalDistance), - }; + const { data: segmentData } = useSegmentTripMetricsData({ + date: endDate, + line: lineName, }); + const speedData = useMemo( + () => + segmentData?.map((data) => { + const intervalDistance = getStationDistance(data.from_id, data.to_id); + + return { + ...data, + speed: convertSecondsToMph(data.travel_time, intervalDistance), + }; + }) ?? [], + [segmentData] + ); + const { segments } = useMemo( () => segmentStationPairs({ diff --git a/modules/speed/map/constants.ts b/modules/speed/map/constants.ts deleted file mode 100644 index 8b063acaf..000000000 --- a/modules/speed/map/constants.ts +++ /dev/null @@ -1,2258 +0,0 @@ -import type { SpeedPairData } from './segment'; - -export const TEST_DATA: SpeedPairData[] = [ - { - service_date: '2025-02-24', - travel_time: 16.0, - route: 'Blue', - from_id: 'place-bomnl', - to_id: 'place-gover', - }, - { - service_date: '2025-02-25', - travel_time: 16.0, - route: 'Blue', - from_id: 'place-bomnl', - to_id: 'place-gover', - }, - { - service_date: '2025-02-24', - travel_time: 36.0, - route: 'Blue', - from_id: 'place-gover', - to_id: 'place-bomnl', - }, - { - service_date: '2025-02-25', - travel_time: 37.0, - route: 'Blue', - from_id: 'place-gover', - to_id: 'place-bomnl', - }, - { - service_date: '2025-02-24', - travel_time: 34.0, - route: 'Blue', - from_id: 'place-gover', - to_id: 'place-state', - }, - { - service_date: '2025-02-25', - travel_time: 34.0, - route: 'Blue', - from_id: 'place-gover', - to_id: 'place-state', - }, - { - service_date: '2025-02-24', - travel_time: 34.0, - route: 'Blue', - from_id: 'place-state', - to_id: 'place-gover', - }, - { - service_date: '2025-02-25', - travel_time: 35.0, - route: 'Blue', - from_id: 'place-state', - to_id: 'place-gover', - }, - { - service_date: '2025-02-24', - travel_time: 30.0, - route: 'Blue', - from_id: 'place-state', - to_id: 'place-aqucl', - }, - { - service_date: '2025-02-25', - travel_time: 31.0, - route: 'Blue', - from_id: 'place-state', - to_id: 'place-aqucl', - }, - { - service_date: '2025-02-24', - travel_time: 29.0, - route: 'Blue', - from_id: 'place-aqucl', - to_id: 'place-state', - }, - { - service_date: '2025-02-25', - travel_time: 30.0, - route: 'Blue', - from_id: 'place-aqucl', - to_id: 'place-state', - }, - { - service_date: '2025-02-24', - travel_time: 109.0, - route: 'Blue', - from_id: 'place-aqucl', - to_id: 'place-mvbcl', - }, - { - service_date: '2025-02-25', - travel_time: 109.0, - route: 'Blue', - from_id: 'place-aqucl', - to_id: 'place-mvbcl', - }, - { - service_date: '2025-02-24', - travel_time: 90.0, - route: 'Blue', - from_id: 'place-mvbcl', - to_id: 'place-aqucl', - }, - { - service_date: '2025-02-25', - travel_time: 90.0, - route: 'Blue', - from_id: 'place-mvbcl', - to_id: 'place-aqucl', - }, - { - service_date: '2025-02-24', - travel_time: 81.0, - route: 'Blue', - from_id: 'place-mvbcl', - to_id: 'place-aport', - }, - { - service_date: '2025-02-25', - travel_time: 81.0, - route: 'Blue', - from_id: 'place-mvbcl', - to_id: 'place-aport', - }, - { - service_date: '2025-02-24', - travel_time: 98.0, - route: 'Blue', - from_id: 'place-aport', - to_id: 'place-mvbcl', - }, - { - service_date: '2025-02-25', - travel_time: 99.0, - route: 'Blue', - from_id: 'place-aport', - to_id: 'place-mvbcl', - }, - { - service_date: '2025-02-24', - travel_time: 59.0, - route: 'Blue', - from_id: 'place-aport', - to_id: 'place-wimnl', - }, - { - service_date: '2025-02-25', - travel_time: 59.0, - route: 'Blue', - from_id: 'place-aport', - to_id: 'place-wimnl', - }, - { - service_date: '2025-02-24', - travel_time: 56.5, - route: 'Blue', - from_id: 'place-wimnl', - to_id: 'place-aport', - }, - { - service_date: '2025-02-25', - travel_time: 56.0, - route: 'Blue', - from_id: 'place-wimnl', - to_id: 'place-aport', - }, - { - service_date: '2025-02-24', - travel_time: 108.0, - route: 'Blue', - from_id: 'place-wimnl', - to_id: 'place-orhte', - }, - { - service_date: '2025-02-25', - travel_time: 108.0, - route: 'Blue', - from_id: 'place-wimnl', - to_id: 'place-orhte', - }, - { - service_date: '2025-02-24', - travel_time: 104.0, - route: 'Blue', - from_id: 'place-orhte', - to_id: 'place-wimnl', - }, - { - service_date: '2025-02-25', - travel_time: 105.0, - route: 'Blue', - from_id: 'place-orhte', - to_id: 'place-wimnl', - }, - { - service_date: '2025-02-24', - travel_time: 58.0, - route: 'Blue', - from_id: 'place-orhte', - to_id: 'place-sdmnl', - }, - { - service_date: '2025-02-25', - travel_time: 58.0, - route: 'Blue', - from_id: 'place-orhte', - to_id: 'place-sdmnl', - }, - { - service_date: '2025-02-24', - travel_time: 61.0, - route: 'Blue', - from_id: 'place-sdmnl', - to_id: 'place-orhte', - }, - { - service_date: '2025-02-25', - travel_time: 61.0, - route: 'Blue', - from_id: 'place-sdmnl', - to_id: 'place-orhte', - }, - { - service_date: '2025-02-24', - travel_time: 47.0, - route: 'Blue', - from_id: 'place-sdmnl', - to_id: 'place-bmmnl', - }, - { - service_date: '2025-02-25', - travel_time: 47.0, - route: 'Blue', - from_id: 'place-sdmnl', - to_id: 'place-bmmnl', - }, - { - service_date: '2025-02-24', - travel_time: 46.0, - route: 'Blue', - from_id: 'place-bmmnl', - to_id: 'place-sdmnl', - }, - { - service_date: '2025-02-25', - travel_time: 46.5, - route: 'Blue', - from_id: 'place-bmmnl', - to_id: 'place-sdmnl', - }, - { - service_date: '2025-02-24', - travel_time: 73.5, - route: 'Blue', - from_id: 'place-bmmnl', - to_id: 'place-rbmnl', - }, - { - service_date: '2025-02-25', - travel_time: 74.0, - route: 'Blue', - from_id: 'place-bmmnl', - to_id: 'place-rbmnl', - }, - { - service_date: '2025-02-24', - travel_time: 69.0, - route: 'Blue', - from_id: 'place-rbmnl', - to_id: 'place-bmmnl', - }, - { - service_date: '2025-02-25', - travel_time: 70.0, - route: 'Blue', - from_id: 'place-rbmnl', - to_id: 'place-bmmnl', - }, - { - service_date: '2025-02-24', - travel_time: 78.5, - route: 'Blue', - from_id: 'place-rbmnl', - to_id: 'place-wondl', - }, - { - service_date: '2025-02-25', - travel_time: 75.0, - route: 'Blue', - from_id: 'place-rbmnl', - to_id: 'place-wondl', - }, - { - service_date: '2025-02-24', - travel_time: 35.0, - route: 'Blue', - from_id: 'place-wondl', - to_id: 'place-rbmnl', - }, - { - service_date: '2025-02-25', - travel_time: 35.0, - route: 'Blue', - from_id: 'place-wondl', - to_id: 'place-rbmnl', - }, - { - service_date: '2025-02-24', - travel_time: 117.5, - route: 'Green', - from_id: 'place-kencl', - to_id: 'place-hymnl', - }, - { - service_date: '2025-02-25', - travel_time: 120.0, - route: 'Green', - from_id: 'place-kencl', - to_id: 'place-hymnl', - }, - { - service_date: '2025-02-24', - travel_time: 142.0, - route: 'Green', - from_id: 'place-kencl', - to_id: 'place-fenwy', - }, - { - service_date: '2025-02-25', - travel_time: 143.0, - route: 'Green', - from_id: 'place-kencl', - to_id: 'place-fenwy', - }, - { - service_date: '2025-02-24', - travel_time: 109.0, - route: 'Green', - from_id: 'place-hymnl', - to_id: 'place-coecl', - }, - { - service_date: '2025-02-25', - travel_time: 107.0, - route: 'Green', - from_id: 'place-hymnl', - to_id: 'place-coecl', - }, - { - service_date: '2025-02-24', - travel_time: 117.0, - route: 'Green', - from_id: 'place-hymnl', - to_id: 'place-kencl', - }, - { - service_date: '2025-02-25', - travel_time: 116.0, - route: 'Green', - from_id: 'place-hymnl', - to_id: 'place-kencl', - }, - { - service_date: '2025-02-24', - travel_time: 66.0, - route: 'Green', - from_id: 'place-coecl', - to_id: 'place-armnl', - }, - { - service_date: '2025-02-25', - travel_time: 67.0, - route: 'Green', - from_id: 'place-coecl', - to_id: 'place-armnl', - }, - { - service_date: '2025-02-24', - travel_time: 100.5, - route: 'Green', - from_id: 'place-coecl', - to_id: 'place-hymnl', - }, - { - service_date: '2025-02-25', - travel_time: 99.0, - route: 'Green', - from_id: 'place-coecl', - to_id: 'place-hymnl', - }, - { - service_date: '2025-02-24', - travel_time: 118.0, - route: 'Green', - from_id: 'place-armnl', - to_id: 'place-boyls', - }, - { - service_date: '2025-02-25', - travel_time: 120.0, - route: 'Green', - from_id: 'place-armnl', - to_id: 'place-boyls', - }, - { - service_date: '2025-02-24', - travel_time: 67.0, - route: 'Green', - from_id: 'place-armnl', - to_id: 'place-coecl', - }, - { - service_date: '2025-02-25', - travel_time: 60.0, - route: 'Green', - from_id: 'place-armnl', - to_id: 'place-coecl', - }, - { - service_date: '2025-02-24', - travel_time: 66.0, - route: 'Green', - from_id: 'place-boyls', - to_id: 'place-pktrm', - }, - { - service_date: '2025-02-25', - travel_time: 67.0, - route: 'Green', - from_id: 'place-boyls', - to_id: 'place-pktrm', - }, - { - service_date: '2025-02-24', - travel_time: 111.0, - route: 'Green', - from_id: 'place-boyls', - to_id: 'place-armnl', - }, - { - service_date: '2025-02-25', - travel_time: 107.0, - route: 'Green', - from_id: 'place-boyls', - to_id: 'place-armnl', - }, - { - service_date: '2025-02-24', - travel_time: 66.0, - route: 'Green', - from_id: 'place-river', - to_id: 'place-woodl', - }, - { - service_date: '2025-02-25', - travel_time: 63.0, - route: 'Green', - from_id: 'place-river', - to_id: 'place-woodl', - }, - { - service_date: '2025-02-24', - travel_time: 93.0, - route: 'Green', - from_id: 'place-woodl', - to_id: 'place-waban', - }, - { - service_date: '2025-02-25', - travel_time: 91.0, - route: 'Green', - from_id: 'place-woodl', - to_id: 'place-waban', - }, - { - service_date: '2025-02-24', - travel_time: 96.0, - route: 'Green', - from_id: 'place-woodl', - to_id: 'place-river', - }, - { - service_date: '2025-02-25', - travel_time: 96.0, - route: 'Green', - from_id: 'place-woodl', - to_id: 'place-river', - }, - { - service_date: '2025-02-24', - travel_time: 85.0, - route: 'Green', - from_id: 'place-waban', - to_id: 'place-eliot', - }, - { - service_date: '2025-02-25', - travel_time: 84.0, - route: 'Green', - from_id: 'place-waban', - to_id: 'place-eliot', - }, - { - service_date: '2025-02-24', - travel_time: 90.0, - route: 'Green', - from_id: 'place-waban', - to_id: 'place-woodl', - }, - { - service_date: '2025-02-25', - travel_time: 89.0, - route: 'Green', - from_id: 'place-waban', - to_id: 'place-woodl', - }, - { - service_date: '2025-02-24', - travel_time: 96.0, - route: 'Green', - from_id: 'place-eliot', - to_id: 'place-newtn', - }, - { - service_date: '2025-02-25', - travel_time: 94.0, - route: 'Green', - from_id: 'place-eliot', - to_id: 'place-newtn', - }, - { - service_date: '2025-02-24', - travel_time: 84.0, - route: 'Green', - from_id: 'place-eliot', - to_id: 'place-waban', - }, - { - service_date: '2025-02-25', - travel_time: 82.0, - route: 'Green', - from_id: 'place-eliot', - to_id: 'place-waban', - }, - { - service_date: '2025-02-24', - travel_time: 86.0, - route: 'Green', - from_id: 'place-newtn', - to_id: 'place-newto', - }, - { - service_date: '2025-02-25', - travel_time: 84.0, - route: 'Green', - from_id: 'place-newtn', - to_id: 'place-newto', - }, - { - service_date: '2025-02-24', - travel_time: 91.0, - route: 'Green', - from_id: 'place-newtn', - to_id: 'place-eliot', - }, - { - service_date: '2025-02-25', - travel_time: 90.0, - route: 'Green', - from_id: 'place-newtn', - to_id: 'place-eliot', - }, - { - service_date: '2025-02-24', - travel_time: 168.0, - route: 'Green', - from_id: 'place-newto', - to_id: 'place-chhil', - }, - { - service_date: '2025-02-25', - travel_time: 164.0, - route: 'Green', - from_id: 'place-newto', - to_id: 'place-chhil', - }, - { - service_date: '2025-02-24', - travel_time: 89.0, - route: 'Green', - from_id: 'place-newto', - to_id: 'place-newtn', - }, - { - service_date: '2025-02-25', - travel_time: 87.0, - route: 'Green', - from_id: 'place-newto', - to_id: 'place-newtn', - }, - { - service_date: '2025-02-24', - travel_time: 126.0, - route: 'Green', - from_id: 'place-chhil', - to_id: 'place-rsmnl', - }, - { - service_date: '2025-02-25', - travel_time: 120.0, - route: 'Green', - from_id: 'place-chhil', - to_id: 'place-rsmnl', - }, - { - service_date: '2025-02-24', - travel_time: 166.0, - route: 'Green', - from_id: 'place-chhil', - to_id: 'place-newto', - }, - { - service_date: '2025-02-25', - travel_time: 164.0, - route: 'Green', - from_id: 'place-chhil', - to_id: 'place-newto', - }, - { - service_date: '2025-02-24', - travel_time: 120.0, - route: 'Green', - from_id: 'place-rsmnl', - to_id: 'place-bcnfd', - }, - { - service_date: '2025-02-25', - travel_time: 120.0, - route: 'Green', - from_id: 'place-rsmnl', - to_id: 'place-bcnfd', - }, - { - service_date: '2025-02-24', - travel_time: 126.0, - route: 'Green', - from_id: 'place-rsmnl', - to_id: 'place-chhil', - }, - { - service_date: '2025-02-25', - travel_time: 120.0, - route: 'Green', - from_id: 'place-rsmnl', - to_id: 'place-chhil', - }, - { - service_date: '2025-02-24', - travel_time: 102.0, - route: 'Green', - from_id: 'place-bcnfd', - to_id: 'place-brkhl', - }, - { - service_date: '2025-02-25', - travel_time: 102.0, - route: 'Green', - from_id: 'place-bcnfd', - to_id: 'place-brkhl', - }, - { - service_date: '2025-02-24', - travel_time: 131.0, - route: 'Green', - from_id: 'place-bcnfd', - to_id: 'place-rsmnl', - }, - { - service_date: '2025-02-25', - travel_time: 126.0, - route: 'Green', - from_id: 'place-bcnfd', - to_id: 'place-rsmnl', - }, - { - service_date: '2025-02-24', - travel_time: 68.0, - route: 'Green', - from_id: 'place-brkhl', - to_id: 'place-bvmnl', - }, - { - service_date: '2025-02-25', - travel_time: 68.0, - route: 'Green', - from_id: 'place-brkhl', - to_id: 'place-bvmnl', - }, - { - service_date: '2025-02-24', - travel_time: 97.0, - route: 'Green', - from_id: 'place-brkhl', - to_id: 'place-bcnfd', - }, - { - service_date: '2025-02-25', - travel_time: 98.0, - route: 'Green', - from_id: 'place-brkhl', - to_id: 'place-bcnfd', - }, - { - service_date: '2025-02-24', - travel_time: 96.0, - route: 'Green', - from_id: 'place-bvmnl', - to_id: 'place-longw', - }, - { - service_date: '2025-02-25', - travel_time: 92.0, - route: 'Green', - from_id: 'place-bvmnl', - to_id: 'place-longw', - }, - { - service_date: '2025-02-24', - travel_time: 87.0, - route: 'Green', - from_id: 'place-bvmnl', - to_id: 'place-brkhl', - }, - { - service_date: '2025-02-25', - travel_time: 89.0, - route: 'Green', - from_id: 'place-bvmnl', - to_id: 'place-brkhl', - }, - { - service_date: '2025-02-24', - travel_time: 66.0, - route: 'Green', - from_id: 'place-longw', - to_id: 'place-fenwy', - }, - { - service_date: '2025-02-25', - travel_time: 66.0, - route: 'Green', - from_id: 'place-longw', - to_id: 'place-fenwy', - }, - { - service_date: '2025-02-24', - travel_time: 83.0, - route: 'Green', - from_id: 'place-longw', - to_id: 'place-bvmnl', - }, - { - service_date: '2025-02-25', - travel_time: 84.0, - route: 'Green', - from_id: 'place-longw', - to_id: 'place-bvmnl', - }, - { - service_date: '2025-02-24', - travel_time: 159.5, - route: 'Green', - from_id: 'place-fenwy', - to_id: 'place-kencl', - }, - { - service_date: '2025-02-25', - travel_time: 159.5, - route: 'Green', - from_id: 'place-fenwy', - to_id: 'place-kencl', - }, - { - service_date: '2025-02-24', - travel_time: 67.0, - route: 'Green', - from_id: 'place-fenwy', - to_id: 'place-longw', - }, - { - service_date: '2025-02-25', - travel_time: 68.0, - route: 'Green', - from_id: 'place-fenwy', - to_id: 'place-longw', - }, - { - service_date: '2025-02-24', - travel_time: 55.0, - route: 'Green', - from_id: 'place-pktrm', - to_id: 'place-boyls', - }, - { - service_date: '2025-02-25', - travel_time: 60.0, - route: 'Green', - from_id: 'place-pktrm', - to_id: 'place-boyls', - }, - { - service_date: '2025-02-24', - travel_time: 76.0, - route: 'Green', - from_id: 'place-pktrm', - to_id: 'place-gover', - }, - { - service_date: '2025-02-25', - travel_time: 69.0, - route: 'Green', - from_id: 'place-pktrm', - to_id: 'place-gover', - }, - { - service_date: '2025-02-24', - travel_time: 100.0, - route: 'Green', - from_id: 'place-gover', - to_id: 'place-haecl', - }, - { - service_date: '2025-02-25', - travel_time: 101.0, - route: 'Green', - from_id: 'place-gover', - to_id: 'place-haecl', - }, - { - service_date: '2025-02-24', - travel_time: 86.0, - route: 'Green', - from_id: 'place-gover', - to_id: 'place-pktrm', - }, - { - service_date: '2025-02-25', - travel_time: 85.0, - route: 'Green', - from_id: 'place-gover', - to_id: 'place-pktrm', - }, - { - service_date: '2025-02-24', - travel_time: 68.0, - route: 'Green', - from_id: 'place-haecl', - to_id: 'place-north', - }, - { - service_date: '2025-02-25', - travel_time: 66.0, - route: 'Green', - from_id: 'place-haecl', - to_id: 'place-north', - }, - { - service_date: '2025-02-24', - travel_time: 82.0, - route: 'Green', - from_id: 'place-haecl', - to_id: 'place-gover', - }, - { - service_date: '2025-02-25', - travel_time: 86.0, - route: 'Green', - from_id: 'place-haecl', - to_id: 'place-gover', - }, - { - service_date: '2025-02-24', - travel_time: 151.5, - route: 'Green', - from_id: 'place-north', - to_id: 'place-spmnl', - }, - { - service_date: '2025-02-25', - travel_time: 153.0, - route: 'Green', - from_id: 'place-north', - to_id: 'place-spmnl', - }, - { - service_date: '2025-02-24', - travel_time: 50.0, - route: 'Green', - from_id: 'place-north', - to_id: 'place-haecl', - }, - { - service_date: '2025-02-25', - travel_time: 49.0, - route: 'Green', - from_id: 'place-north', - to_id: 'place-haecl', - }, - { - service_date: '2025-02-24', - travel_time: 74.0, - route: 'Green', - from_id: 'place-spmnl', - to_id: 'place-lech', - }, - { - service_date: '2025-02-25', - travel_time: 73.0, - route: 'Green', - from_id: 'place-spmnl', - to_id: 'place-lech', - }, - { - service_date: '2025-02-24', - travel_time: 148.0, - route: 'Green', - from_id: 'place-spmnl', - to_id: 'place-north', - }, - { - service_date: '2025-02-25', - travel_time: 148.0, - route: 'Green', - from_id: 'place-spmnl', - to_id: 'place-north', - }, - { - service_date: '2025-02-24', - travel_time: 61.0, - route: 'Green', - from_id: 'place-prmnl', - to_id: 'place-symcl', - }, - { - service_date: '2025-02-25', - travel_time: 61.0, - route: 'Green', - from_id: 'place-prmnl', - to_id: 'place-symcl', - }, - { - service_date: '2025-02-24', - travel_time: 50.0, - route: 'Green', - from_id: 'place-symcl', - to_id: 'place-prmnl', - }, - { - service_date: '2025-02-25', - travel_time: 50.0, - route: 'Green', - from_id: 'place-symcl', - to_id: 'place-prmnl', - }, - { - service_date: '2025-02-24', - travel_time: 316.0, - route: 'Green', - from_id: 'place-lech', - to_id: 'place-unsqu', - }, - { - service_date: '2025-02-25', - travel_time: 315.5, - route: 'Green', - from_id: 'place-lech', - to_id: 'place-unsqu', - }, - { - service_date: '2025-02-24', - travel_time: 79.0, - route: 'Green', - from_id: 'place-lech', - to_id: 'place-spmnl', - }, - { - service_date: '2025-02-25', - travel_time: 78.0, - route: 'Green', - from_id: 'place-lech', - to_id: 'place-spmnl', - }, - { - service_date: '2025-02-24', - travel_time: 67.0, - route: 'Green', - from_id: 'place-gilmn', - to_id: 'place-mgngl', - }, - { - service_date: '2025-02-25', - travel_time: 68.0, - route: 'Green', - from_id: 'place-gilmn', - to_id: 'place-mgngl', - }, - { - service_date: '2025-02-24', - travel_time: 84.0, - route: 'Green', - from_id: 'place-gilmn', - to_id: 'place-esomr', - }, - { - service_date: '2025-02-25', - travel_time: 87.0, - route: 'Green', - from_id: 'place-gilmn', - to_id: 'place-esomr', - }, - { - service_date: '2025-02-24', - travel_time: 54.0, - route: 'Green', - from_id: 'place-mgngl', - to_id: 'place-balsq', - }, - { - service_date: '2025-02-25', - travel_time: 55.0, - route: 'Green', - from_id: 'place-mgngl', - to_id: 'place-balsq', - }, - { - service_date: '2025-02-24', - travel_time: 67.0, - route: 'Green', - from_id: 'place-mgngl', - to_id: 'place-gilmn', - }, - { - service_date: '2025-02-25', - travel_time: 69.0, - route: 'Green', - from_id: 'place-mgngl', - to_id: 'place-gilmn', - }, - { - service_date: '2025-02-24', - travel_time: 108.0, - route: 'Green', - from_id: 'place-balsq', - to_id: 'place-mdftf', - }, - { - service_date: '2025-02-25', - travel_time: 120.0, - route: 'Green', - from_id: 'place-balsq', - to_id: 'place-mdftf', - }, - { - service_date: '2025-02-24', - travel_time: 53.0, - route: 'Green', - from_id: 'place-balsq', - to_id: 'place-mgngl', - }, - { - service_date: '2025-02-25', - travel_time: 54.0, - route: 'Green', - from_id: 'place-balsq', - to_id: 'place-mgngl', - }, - { - service_date: '2025-02-24', - travel_time: 94.5, - route: 'Green', - from_id: 'place-mdftf', - to_id: 'place-balsq', - }, - { - service_date: '2025-02-25', - travel_time: 96.0, - route: 'Green', - from_id: 'place-mdftf', - to_id: 'place-balsq', - }, - { - service_date: '2025-02-24', - travel_time: 79.0, - route: 'Green', - from_id: 'place-esomr', - to_id: 'place-gilmn', - }, - { - service_date: '2025-02-25', - travel_time: 82.0, - route: 'Green', - from_id: 'place-esomr', - to_id: 'place-gilmn', - }, - { - service_date: '2025-02-24', - travel_time: 70.0, - route: 'Orange', - from_id: 'place-forhl', - to_id: 'place-grnst', - }, - { - service_date: '2025-02-25', - travel_time: 71.0, - route: 'Orange', - from_id: 'place-forhl', - to_id: 'place-grnst', - }, - { - service_date: '2025-02-24', - travel_time: 119.0, - route: 'Orange', - from_id: 'place-grnst', - to_id: 'place-forhl', - }, - { - service_date: '2025-02-25', - travel_time: 128.5, - route: 'Orange', - from_id: 'place-grnst', - to_id: 'place-forhl', - }, - { - service_date: '2025-02-24', - travel_time: 78.0, - route: 'Orange', - from_id: 'place-grnst', - to_id: 'place-sbmnl', - }, - { - service_date: '2025-02-25', - travel_time: 79.0, - route: 'Orange', - from_id: 'place-grnst', - to_id: 'place-sbmnl', - }, - { - service_date: '2025-02-24', - travel_time: 46.0, - route: 'Orange', - from_id: 'place-sbmnl', - to_id: 'place-grnst', - }, - { - service_date: '2025-02-25', - travel_time: 47.0, - route: 'Orange', - from_id: 'place-sbmnl', - to_id: 'place-grnst', - }, - { - service_date: '2025-02-24', - travel_time: 79.0, - route: 'Orange', - from_id: 'place-sbmnl', - to_id: 'place-jaksn', - }, - { - service_date: '2025-02-25', - travel_time: 80.0, - route: 'Orange', - from_id: 'place-sbmnl', - to_id: 'place-jaksn', - }, - { - service_date: '2025-02-24', - travel_time: 54.0, - route: 'Orange', - from_id: 'place-jaksn', - to_id: 'place-sbmnl', - }, - { - service_date: '2025-02-25', - travel_time: 56.5, - route: 'Orange', - from_id: 'place-jaksn', - to_id: 'place-sbmnl', - }, - { - service_date: '2025-02-24', - travel_time: 50.0, - route: 'Orange', - from_id: 'place-jaksn', - to_id: 'place-rcmnl', - }, - { - service_date: '2025-02-25', - travel_time: 50.0, - route: 'Orange', - from_id: 'place-jaksn', - to_id: 'place-rcmnl', - }, - { - service_date: '2025-02-24', - travel_time: 52.0, - route: 'Orange', - from_id: 'place-rcmnl', - to_id: 'place-jaksn', - }, - { - service_date: '2025-02-25', - travel_time: 52.0, - route: 'Orange', - from_id: 'place-rcmnl', - to_id: 'place-jaksn', - }, - { - service_date: '2025-02-24', - travel_time: 45.0, - route: 'Orange', - from_id: 'place-rcmnl', - to_id: 'place-rugg', - }, - { - service_date: '2025-02-25', - travel_time: 45.0, - route: 'Orange', - from_id: 'place-rcmnl', - to_id: 'place-rugg', - }, - { - service_date: '2025-02-24', - travel_time: 46.0, - route: 'Orange', - from_id: 'place-rugg', - to_id: 'place-rcmnl', - }, - { - service_date: '2025-02-25', - travel_time: 46.0, - route: 'Orange', - from_id: 'place-rugg', - to_id: 'place-rcmnl', - }, - { - service_date: '2025-02-24', - travel_time: 43.0, - route: 'Orange', - from_id: 'place-rugg', - to_id: 'place-masta', - }, - { - service_date: '2025-02-25', - travel_time: 43.0, - route: 'Orange', - from_id: 'place-rugg', - to_id: 'place-masta', - }, - { - service_date: '2025-02-24', - travel_time: 38.0, - route: 'Orange', - from_id: 'place-masta', - to_id: 'place-rugg', - }, - { - service_date: '2025-02-25', - travel_time: 39.0, - route: 'Orange', - from_id: 'place-masta', - to_id: 'place-rugg', - }, - { - service_date: '2025-02-24', - travel_time: 78.0, - route: 'Orange', - from_id: 'place-masta', - to_id: 'place-bbsta', - }, - { - service_date: '2025-02-25', - travel_time: 78.0, - route: 'Orange', - from_id: 'place-masta', - to_id: 'place-bbsta', - }, - { - service_date: '2025-02-24', - travel_time: 80.0, - route: 'Orange', - from_id: 'place-bbsta', - to_id: 'place-masta', - }, - { - service_date: '2025-02-25', - travel_time: 80.0, - route: 'Orange', - from_id: 'place-bbsta', - to_id: 'place-masta', - }, - { - service_date: '2025-02-24', - travel_time: 72.0, - route: 'Orange', - from_id: 'place-bbsta', - to_id: 'place-tumnl', - }, - { - service_date: '2025-02-25', - travel_time: 72.0, - route: 'Orange', - from_id: 'place-bbsta', - to_id: 'place-tumnl', - }, - { - service_date: '2025-02-24', - travel_time: 100.0, - route: 'Orange', - from_id: 'place-tumnl', - to_id: 'place-bbsta', - }, - { - service_date: '2025-02-25', - travel_time: 100.0, - route: 'Orange', - from_id: 'place-tumnl', - to_id: 'place-bbsta', - }, - { - service_date: '2025-02-24', - travel_time: 28.0, - route: 'Orange', - from_id: 'place-tumnl', - to_id: 'place-chncl', - }, - { - service_date: '2025-02-25', - travel_time: 28.0, - route: 'Orange', - from_id: 'place-tumnl', - to_id: 'place-chncl', - }, - { - service_date: '2025-02-24', - travel_time: 25.0, - route: 'Orange', - from_id: 'place-chncl', - to_id: 'place-tumnl', - }, - { - service_date: '2025-02-25', - travel_time: 25.0, - route: 'Orange', - from_id: 'place-chncl', - to_id: 'place-tumnl', - }, - { - service_date: '2025-02-24', - travel_time: 28.0, - route: 'Orange', - from_id: 'place-chncl', - to_id: 'place-dwnxg', - }, - { - service_date: '2025-02-25', - travel_time: 28.0, - route: 'Orange', - from_id: 'place-chncl', - to_id: 'place-dwnxg', - }, - { - service_date: '2025-02-24', - travel_time: 28.0, - route: 'Orange', - from_id: 'place-dwnxg', - to_id: 'place-chncl', - }, - { - service_date: '2025-02-25', - travel_time: 28.0, - route: 'Orange', - from_id: 'place-dwnxg', - to_id: 'place-chncl', - }, - { - service_date: '2025-02-24', - travel_time: 24.0, - route: 'Orange', - from_id: 'place-dwnxg', - to_id: 'place-state', - }, - { - service_date: '2025-02-25', - travel_time: 25.0, - route: 'Orange', - from_id: 'place-dwnxg', - to_id: 'place-state', - }, - { - service_date: '2025-02-24', - travel_time: 14.0, - route: 'Orange', - from_id: 'place-state', - to_id: 'place-dwnxg', - }, - { - service_date: '2025-02-25', - travel_time: 14.0, - route: 'Orange', - from_id: 'place-state', - to_id: 'place-dwnxg', - }, - { - service_date: '2025-02-24', - travel_time: 20.0, - route: 'Orange', - from_id: 'place-state', - to_id: 'place-haecl', - }, - { - service_date: '2025-02-25', - travel_time: 20.0, - route: 'Orange', - from_id: 'place-state', - to_id: 'place-haecl', - }, - { - service_date: '2025-02-24', - travel_time: 42.0, - route: 'Orange', - from_id: 'place-haecl', - to_id: 'place-state', - }, - { - service_date: '2025-02-25', - travel_time: 42.0, - route: 'Orange', - from_id: 'place-haecl', - to_id: 'place-state', - }, - { - service_date: '2025-02-24', - travel_time: 30.0, - route: 'Orange', - from_id: 'place-haecl', - to_id: 'place-north', - }, - { - service_date: '2025-02-25', - travel_time: 30.0, - route: 'Orange', - from_id: 'place-haecl', - to_id: 'place-north', - }, - { - service_date: '2025-02-24', - travel_time: 32.5, - route: 'Orange', - from_id: 'place-north', - to_id: 'place-haecl', - }, - { - service_date: '2025-02-25', - travel_time: 32.0, - route: 'Orange', - from_id: 'place-north', - to_id: 'place-haecl', - }, - { - service_date: '2025-02-24', - travel_time: 86.0, - route: 'Orange', - from_id: 'place-north', - to_id: 'place-ccmnl', - }, - { - service_date: '2025-02-25', - travel_time: 86.0, - route: 'Orange', - from_id: 'place-north', - to_id: 'place-ccmnl', - }, - { - service_date: '2025-02-24', - travel_time: 89.0, - route: 'Orange', - from_id: 'place-ccmnl', - to_id: 'place-north', - }, - { - service_date: '2025-02-25', - travel_time: 90.0, - route: 'Orange', - from_id: 'place-ccmnl', - to_id: 'place-north', - }, - { - service_date: '2025-02-24', - travel_time: 75.0, - route: 'Orange', - from_id: 'place-ccmnl', - to_id: 'place-sull', - }, - { - service_date: '2025-02-25', - travel_time: 75.0, - route: 'Orange', - from_id: 'place-ccmnl', - to_id: 'place-sull', - }, - { - service_date: '2025-02-24', - travel_time: 78.0, - route: 'Orange', - from_id: 'place-sull', - to_id: 'place-ccmnl', - }, - { - service_date: '2025-02-25', - travel_time: 78.0, - route: 'Orange', - from_id: 'place-sull', - to_id: 'place-ccmnl', - }, - { - service_date: '2025-02-24', - travel_time: 49.0, - route: 'Orange', - from_id: 'place-sull', - to_id: 'place-astao', - }, - { - service_date: '2025-02-25', - travel_time: 49.0, - route: 'Orange', - from_id: 'place-sull', - to_id: 'place-astao', - }, - { - service_date: '2025-02-24', - travel_time: 53.0, - route: 'Orange', - from_id: 'place-welln', - to_id: 'place-astao', - }, - { - service_date: '2025-02-25', - travel_time: 53.0, - route: 'Orange', - from_id: 'place-welln', - to_id: 'place-astao', - }, - { - service_date: '2025-02-24', - travel_time: 163.0, - route: 'Orange', - from_id: 'place-welln', - to_id: 'place-mlmnl', - }, - { - service_date: '2025-02-25', - travel_time: 184.0, - route: 'Orange', - from_id: 'place-welln', - to_id: 'place-mlmnl', - }, - { - service_date: '2025-02-24', - travel_time: 160.0, - route: 'Orange', - from_id: 'place-mlmnl', - to_id: 'place-welln', - }, - { - service_date: '2025-02-25', - travel_time: 170.0, - route: 'Orange', - from_id: 'place-mlmnl', - to_id: 'place-welln', - }, - { - service_date: '2025-02-24', - travel_time: 187.0, - route: 'Orange', - from_id: 'place-mlmnl', - to_id: 'place-ogmnl', - }, - { - service_date: '2025-02-25', - travel_time: 174.5, - route: 'Orange', - from_id: 'place-mlmnl', - to_id: 'place-ogmnl', - }, - { - service_date: '2025-02-24', - travel_time: 69.0, - route: 'Orange', - from_id: 'place-ogmnl', - to_id: 'place-mlmnl', - }, - { - service_date: '2025-02-25', - travel_time: 73.0, - route: 'Orange', - from_id: 'place-ogmnl', - to_id: 'place-mlmnl', - }, - { - service_date: '2025-02-24', - travel_time: 48.0, - route: 'Orange', - from_id: 'place-astao', - to_id: 'place-sull', - }, - { - service_date: '2025-02-25', - travel_time: 48.0, - route: 'Orange', - from_id: 'place-astao', - to_id: 'place-sull', - }, - { - service_date: '2025-02-24', - travel_time: 56.0, - route: 'Orange', - from_id: 'place-astao', - to_id: 'place-welln', - }, - { - service_date: '2025-02-25', - travel_time: 56.0, - route: 'Orange', - from_id: 'place-astao', - to_id: 'place-welln', - }, - { - service_date: '2025-02-24', - travel_time: 93.0, - route: 'Red', - from_id: 'place-alfcl', - to_id: 'place-davis', - }, - { - service_date: '2025-02-25', - travel_time: 100.5, - route: 'Red', - from_id: 'place-alfcl', - to_id: 'place-davis', - }, - { - service_date: '2025-02-24', - travel_time: 83.0, - route: 'Red', - from_id: 'place-davis', - to_id: 'place-portr', - }, - { - service_date: '2025-02-25', - travel_time: 83.0, - route: 'Red', - from_id: 'place-davis', - to_id: 'place-portr', - }, - { - service_date: '2025-02-24', - travel_time: 203.0, - route: 'Red', - from_id: 'place-davis', - to_id: 'place-alfcl', - }, - { - service_date: '2025-02-25', - travel_time: 238.0, - route: 'Red', - from_id: 'place-davis', - to_id: 'place-alfcl', - }, - { - service_date: '2025-02-24', - travel_time: 104.0, - route: 'Red', - from_id: 'place-portr', - to_id: 'place-harsq', - }, - { - service_date: '2025-02-25', - travel_time: 103.0, - route: 'Red', - from_id: 'place-portr', - to_id: 'place-harsq', - }, - { - service_date: '2025-02-24', - travel_time: 104.0, - route: 'Red', - from_id: 'place-portr', - to_id: 'place-davis', - }, - { - service_date: '2025-02-25', - travel_time: 103.5, - route: 'Red', - from_id: 'place-portr', - to_id: 'place-davis', - }, - { - service_date: '2025-02-24', - travel_time: 152.0, - route: 'Red', - from_id: 'place-harsq', - to_id: 'place-cntsq', - }, - { - service_date: '2025-02-25', - travel_time: 154.0, - route: 'Red', - from_id: 'place-harsq', - to_id: 'place-cntsq', - }, - { - service_date: '2025-02-24', - travel_time: 116.0, - route: 'Red', - from_id: 'place-harsq', - to_id: 'place-portr', - }, - { - service_date: '2025-02-25', - travel_time: 117.0, - route: 'Red', - from_id: 'place-harsq', - to_id: 'place-portr', - }, - { - service_date: '2025-02-24', - travel_time: 82.0, - route: 'Red', - from_id: 'place-cntsq', - to_id: 'place-knncl', - }, - { - service_date: '2025-02-25', - travel_time: 82.0, - route: 'Red', - from_id: 'place-cntsq', - to_id: 'place-knncl', - }, - { - service_date: '2025-02-24', - travel_time: 154.0, - route: 'Red', - from_id: 'place-cntsq', - to_id: 'place-harsq', - }, - { - service_date: '2025-02-25', - travel_time: 152.0, - route: 'Red', - from_id: 'place-cntsq', - to_id: 'place-harsq', - }, - { - service_date: '2025-02-24', - travel_time: 69.0, - route: 'Red', - from_id: 'place-knncl', - to_id: 'place-chmnl', - }, - { - service_date: '2025-02-25', - travel_time: 69.0, - route: 'Red', - from_id: 'place-knncl', - to_id: 'place-chmnl', - }, - { - service_date: '2025-02-24', - travel_time: 65.0, - route: 'Red', - from_id: 'place-knncl', - to_id: 'place-cntsq', - }, - { - service_date: '2025-02-25', - travel_time: 65.0, - route: 'Red', - from_id: 'place-knncl', - to_id: 'place-cntsq', - }, - { - service_date: '2025-02-24', - travel_time: 61.0, - route: 'Red', - from_id: 'place-chmnl', - to_id: 'place-pktrm', - }, - { - service_date: '2025-02-25', - travel_time: 63.0, - route: 'Red', - from_id: 'place-chmnl', - to_id: 'place-pktrm', - }, - { - service_date: '2025-02-24', - travel_time: 46.0, - route: 'Red', - from_id: 'place-chmnl', - to_id: 'place-knncl', - }, - { - service_date: '2025-02-25', - travel_time: 46.0, - route: 'Red', - from_id: 'place-chmnl', - to_id: 'place-knncl', - }, - { - service_date: '2025-02-24', - travel_time: 22.0, - route: 'Red', - from_id: 'place-pktrm', - to_id: 'place-dwnxg', - }, - { - service_date: '2025-02-25', - travel_time: 22.0, - route: 'Red', - from_id: 'place-pktrm', - to_id: 'place-dwnxg', - }, - { - service_date: '2025-02-24', - travel_time: 61.0, - route: 'Red', - from_id: 'place-pktrm', - to_id: 'place-chmnl', - }, - { - service_date: '2025-02-25', - travel_time: 61.0, - route: 'Red', - from_id: 'place-pktrm', - to_id: 'place-chmnl', - }, - { - service_date: '2025-02-24', - travel_time: 25.0, - route: 'Red', - from_id: 'place-dwnxg', - to_id: 'place-sstat', - }, - { - service_date: '2025-02-25', - travel_time: 25.0, - route: 'Red', - from_id: 'place-dwnxg', - to_id: 'place-sstat', - }, - { - service_date: '2025-02-24', - travel_time: 17.0, - route: 'Red', - from_id: 'place-dwnxg', - to_id: 'place-pktrm', - }, - { - service_date: '2025-02-25', - travel_time: 17.0, - route: 'Red', - from_id: 'place-dwnxg', - to_id: 'place-pktrm', - }, - { - service_date: '2025-02-24', - travel_time: 130.0, - route: 'Red', - from_id: 'place-sstat', - to_id: 'place-brdwy', - }, - { - service_date: '2025-02-25', - travel_time: 134.0, - route: 'Red', - from_id: 'place-sstat', - to_id: 'place-brdwy', - }, - { - service_date: '2025-02-24', - travel_time: 23.0, - route: 'Red', - from_id: 'place-sstat', - to_id: 'place-dwnxg', - }, - { - service_date: '2025-02-25', - travel_time: 23.0, - route: 'Red', - from_id: 'place-sstat', - to_id: 'place-dwnxg', - }, - { - service_date: '2025-02-24', - travel_time: 114.0, - route: 'Red', - from_id: 'place-brdwy', - to_id: 'place-andrw', - }, - { - service_date: '2025-02-25', - travel_time: 117.0, - route: 'Red', - from_id: 'place-brdwy', - to_id: 'place-andrw', - }, - { - service_date: '2025-02-24', - travel_time: 94.0, - route: 'Red', - from_id: 'place-brdwy', - to_id: 'place-sstat', - }, - { - service_date: '2025-02-25', - travel_time: 93.0, - route: 'Red', - from_id: 'place-brdwy', - to_id: 'place-sstat', - }, - { - service_date: '2025-02-24', - travel_time: 100.0, - route: 'Red', - from_id: 'place-andrw', - to_id: 'place-jfk', - }, - { - service_date: '2025-02-25', - travel_time: 102.0, - route: 'Red', - from_id: 'place-andrw', - to_id: 'place-jfk', - }, - { - service_date: '2025-02-24', - travel_time: 102.5, - route: 'Red', - from_id: 'place-andrw', - to_id: 'place-jfk', - }, - { - service_date: '2025-02-25', - travel_time: 102.0, - route: 'Red', - from_id: 'place-andrw', - to_id: 'place-jfk', - }, - { - service_date: '2025-02-24', - travel_time: 113.0, - route: 'Red', - from_id: 'place-andrw', - to_id: 'place-brdwy', - }, - { - service_date: '2025-02-25', - travel_time: 115.0, - route: 'Red', - from_id: 'place-andrw', - to_id: 'place-brdwy', - }, - { - service_date: '2025-02-24', - travel_time: 147.0, - route: 'Red', - from_id: 'place-jfk', - to_id: 'place-shmnl', - }, - { - service_date: '2025-02-25', - travel_time: 146.0, - route: 'Red', - from_id: 'place-jfk', - to_id: 'place-shmnl', - }, - { - service_date: '2025-02-24', - travel_time: 102.0, - route: 'Red', - from_id: 'place-jfk', - to_id: 'place-andrw', - }, - { - service_date: '2025-02-25', - travel_time: 101.5, - route: 'Red', - from_id: 'place-jfk', - to_id: 'place-andrw', - }, - { - service_date: '2025-02-24', - travel_time: 130.0, - route: 'Red', - from_id: 'place-shmnl', - to_id: 'place-fldcr', - }, - { - service_date: '2025-02-25', - travel_time: 131.0, - route: 'Red', - from_id: 'place-shmnl', - to_id: 'place-fldcr', - }, - { - service_date: '2025-02-24', - travel_time: 67.0, - route: 'Red', - from_id: 'place-shmnl', - to_id: 'place-jfk', - }, - { - service_date: '2025-02-25', - travel_time: 67.0, - route: 'Red', - from_id: 'place-shmnl', - to_id: 'place-jfk', - }, - { - service_date: '2025-02-24', - travel_time: 54.0, - route: 'Red', - from_id: 'place-fldcr', - to_id: 'place-smmnl', - }, - { - service_date: '2025-02-25', - travel_time: 55.0, - route: 'Red', - from_id: 'place-fldcr', - to_id: 'place-smmnl', - }, - { - service_date: '2025-02-24', - travel_time: 148.0, - route: 'Red', - from_id: 'place-fldcr', - to_id: 'place-shmnl', - }, - { - service_date: '2025-02-25', - travel_time: 148.0, - route: 'Red', - from_id: 'place-fldcr', - to_id: 'place-shmnl', - }, - { - service_date: '2025-02-24', - travel_time: 99.0, - route: 'Red', - from_id: 'place-smmnl', - to_id: 'place-asmnl', - }, - { - service_date: '2025-02-25', - travel_time: 99.0, - route: 'Red', - from_id: 'place-smmnl', - to_id: 'place-asmnl', - }, - { - service_date: '2025-02-24', - travel_time: 48.0, - route: 'Red', - from_id: 'place-smmnl', - to_id: 'place-fldcr', - }, - { - service_date: '2025-02-25', - travel_time: 47.0, - route: 'Red', - from_id: 'place-smmnl', - to_id: 'place-fldcr', - }, - { - service_date: '2025-02-24', - travel_time: 58.0, - route: 'Red', - from_id: 'place-asmnl', - to_id: 'place-smmnl', - }, - { - service_date: '2025-02-25', - travel_time: 56.0, - route: 'Red', - from_id: 'place-asmnl', - to_id: 'place-smmnl', - }, - { - service_date: '2025-02-24', - travel_time: 330.5, - route: 'Red', - from_id: 'place-jfk', - to_id: 'place-nqncy', - }, - { - service_date: '2025-02-25', - travel_time: 330.0, - route: 'Red', - from_id: 'place-jfk', - to_id: 'place-nqncy', - }, - { - service_date: '2025-02-24', - travel_time: 104.0, - route: 'Red', - from_id: 'place-jfk', - to_id: 'place-andrw', - }, - { - service_date: '2025-02-25', - travel_time: 104.0, - route: 'Red', - from_id: 'place-jfk', - to_id: 'place-andrw', - }, - { - service_date: '2025-02-24', - travel_time: 68.0, - route: 'Red', - from_id: 'place-nqncy', - to_id: 'place-wlsta', - }, - { - service_date: '2025-02-25', - travel_time: 67.0, - route: 'Red', - from_id: 'place-nqncy', - to_id: 'place-wlsta', - }, - { - service_date: '2025-02-24', - travel_time: 330.0, - route: 'Red', - from_id: 'place-nqncy', - to_id: 'place-jfk', - }, - { - service_date: '2025-02-25', - travel_time: 328.0, - route: 'Red', - from_id: 'place-nqncy', - to_id: 'place-jfk', - }, - { - service_date: '2025-02-24', - travel_time: 133.5, - route: 'Red', - from_id: 'place-wlsta', - to_id: 'place-qnctr', - }, - { - service_date: '2025-02-25', - travel_time: 137.0, - route: 'Red', - from_id: 'place-wlsta', - to_id: 'place-qnctr', - }, - { - service_date: '2025-02-24', - travel_time: 72.0, - route: 'Red', - from_id: 'place-wlsta', - to_id: 'place-nqncy', - }, - { - service_date: '2025-02-25', - travel_time: 72.0, - route: 'Red', - from_id: 'place-wlsta', - to_id: 'place-nqncy', - }, - { - service_date: '2025-02-24', - travel_time: 123.0, - route: 'Red', - from_id: 'place-qnctr', - to_id: 'place-qamnl', - }, - { - service_date: '2025-02-25', - travel_time: 122.0, - route: 'Red', - from_id: 'place-qnctr', - to_id: 'place-qamnl', - }, - { - service_date: '2025-02-24', - travel_time: 154.0, - route: 'Red', - from_id: 'place-qnctr', - to_id: 'place-wlsta', - }, - { - service_date: '2025-02-25', - travel_time: 155.0, - route: 'Red', - from_id: 'place-qnctr', - to_id: 'place-wlsta', - }, - { - service_date: '2025-02-24', - travel_time: 248.5, - route: 'Red', - from_id: 'place-qamnl', - to_id: 'place-brntn', - }, - { - service_date: '2025-02-25', - travel_time: 252.0, - route: 'Red', - from_id: 'place-qamnl', - to_id: 'place-brntn', - }, - { - service_date: '2025-02-24', - travel_time: 124.0, - route: 'Red', - from_id: 'place-qamnl', - to_id: 'place-qnctr', - }, - { - service_date: '2025-02-25', - travel_time: 123.0, - route: 'Red', - from_id: 'place-qamnl', - to_id: 'place-qnctr', - }, - { - service_date: '2025-02-24', - travel_time: 186.0, - route: 'Red', - from_id: 'place-brntn', - to_id: 'place-qamnl', - }, - { - service_date: '2025-02-25', - travel_time: 184.0, - route: 'Red', - from_id: 'place-brntn', - to_id: 'place-qamnl', - }, -]; diff --git a/modules/speed/map/segment.ts b/modules/speed/map/segment.ts index 207610156..79ba944b5 100644 --- a/modules/speed/map/segment.ts +++ b/modules/speed/map/segment.ts @@ -14,7 +14,7 @@ import type { LineShort } from '../../../common/types/lines'; import { getStationById } from '../../../common/utils/stations'; export type SpeedPairData = { - service_date: string; + date: string; travel_time: number; speed?: number; route: LineShort; @@ -57,7 +57,7 @@ export const segmentStationPairs = ( speedPairs, lineName, desiredDate, - (segment) => [new Date(segment.service_date), new Date(desiredDate)], + (segment) => [new Date(segment.date), new Date(desiredDate)], (segment) => segment.route ), (segment) => getStationById(segment.from_id), diff --git a/server/.chalice/config.json b/server/.chalice/config.json index ac3e048a3..424006925 100644 --- a/server/.chalice/config.json +++ b/server/.chalice/config.json @@ -5,7 +5,7 @@ "minimum_compression_size": 1000, "lambda_timeout": 30, "lambda_memory_size": 1024, - "layers": ["arn:aws:lambda:us-east-1:464622532012:layer:Datadog-Extension:68"], + "layers": ["arn:aws:lambda:us-east-1:464622532012:layer:Datadog-Extension:70"], "stages": { "production": { "api_gateway_stage": "production", diff --git a/server/.chalice/policy.json b/server/.chalice/policy.json index d848b9d1b..dbbd28010 100644 --- a/server/.chalice/policy.json +++ b/server/.chalice/policy.json @@ -27,6 +27,7 @@ "arn:aws:dynamodb:*:*:table/ScheduledServiceDaily", "arn:aws:dynamodb:*:*:table/Ridership", "arn:aws:dynamodb:*:*:table/SpeedRestrictions", + "arn:aws:dynamodb:*:*:table/SegmentTravelTimes", "arn:aws:dynamodb:*:*:table/TimePredictions", "arn:aws:dynamodb:*:*:table/AlertDelaysWeekly" ] diff --git a/server/app.py b/server/app.py index 83d11fb2d..1d82c03f2 100644 --- a/server/app.py +++ b/server/app.py @@ -177,6 +177,12 @@ def get_trips_by_line(): return json.dumps(response, indent=4, sort_keys=True) +@app.route("/api/tripmetrics/segment", cors=cors_config) +def get_trips_by_segment(): + response = speed.query_speed_segment(app.current_request.query_params) + return json.dumps(response, indent=4, sort_keys=True) + + @app.route("/api/scheduledservice", cors=cors_config) def get_scheduled_service(): query = app.current_request.query_params diff --git a/server/chalicelib/dynamo.py b/server/chalicelib/dynamo.py index 73b4703eb..5e27a7715 100644 --- a/server/chalicelib/dynamo.py +++ b/server/chalicelib/dynamo.py @@ -10,12 +10,18 @@ dynamodb = boto3.resource("dynamodb") -def query_daily_trips_on_route(table_name: str, route, start_date: str | date, end_date: str | date): +def query_daily_trips_on_route(table_name: str, route: str, start_date: str | date, end_date: str | date): table = dynamodb.Table(table_name) response = table.query(KeyConditionExpression=Key("route").eq(route) & Key("date").between(start_date, end_date)) return ddb_json.loads(response["Items"]) +def query_segment_tts_on_route(route: str, date: str | date): + table = dynamodb.Table("SegmentTravelTimes") + response = table.query(KeyConditionExpression=Key("route").eq(route) & Key("date_stop_pair").begins_with(date)) + return ddb_json.loads(response["Items"]) + + def query_daily_trips_on_line(table_name: str, line: str, start_date: str | date, end_date: str | date): route_keys = constants.LINE_TO_ROUTE_MAP[line] with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor: diff --git a/server/chalicelib/speed.py b/server/chalicelib/speed.py index e8f738f7e..644187dcc 100644 --- a/server/chalicelib/speed.py +++ b/server/chalicelib/speed.py @@ -14,6 +14,11 @@ class TripMetricsByLineParams(TypedDict): line: str +class TripMetricsByLineAndDateParams(TypedDict): + date: str | date + line: str + + # Delta values put limits on the numbers of days for which data that can be requested. For each table it is approximately 150 entries. AGG_TO_CONFIG_MAP = { "daily": {"table_name": "DeliveredTripMetrics", "delta": 150}, @@ -74,6 +79,21 @@ def trip_metrics_by_line(params: TripMetricsByLineParams): return dynamo.query_agg_trip_metrics(start_date, end_date, config["table_name"], line) +def query_speed_segment(params: TripMetricsByLineAndDateParams): + try: + date = params["date"] + line = params["line"] + if line not in ["Red", "Blue", "Green", "Orange", "Mattapan"]: + raise BadRequestError("Invalid Line key.") + except KeyError: + raise BadRequestError("Missing or invalid parameters.") + actual_trips = dynamo.query_segment_tts_on_route(line, date) + for trip in actual_trips: + if "date_stop_pair" in trip: + del trip["date_stop_pair"] + return actual_trips + + def is_invalid_range(start_date, end_date, max_delta): """Check if number of requested entries is more than maximum for the table""" start_datetime = datetime.strptime(start_date, DATE_FORMAT_BACKEND)