Skip to content

Commit

Permalink
Add a reliability page for alert analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
devinmatte committed Jun 25, 2024
1 parent fcd4bef commit 918759a
Show file tree
Hide file tree
Showing 17 changed files with 874 additions and 6 deletions.
13 changes: 13 additions & 0 deletions common/api/hooks/reliability.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useQuery } from '@tanstack/react-query';
import { ONE_HOUR } from '../../constants/time';
import { fetchLineDelaysByLine } from '../reliability';
import type { FetchAlertDelaysByLineOptions } from '../../types/api';

export const useAlertDelays = (options: FetchAlertDelaysByLineOptions, enabled?: boolean) => {
return useQuery({
queryKey: ['lineReliability', options],
queryFn: () => fetchLineDelaysByLine(options),
enabled: enabled,
staleTime: ONE_HOUR,
});
};
15 changes: 15 additions & 0 deletions common/api/reliability.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { FetchAlertDelaysByLineParams, type FetchAlertDelaysByLineOptions } from '../types/api';
import type { LineDelays } from '../types/reliability';
import { apiFetch } from './utils/fetch';

export const fetchLineDelaysByLine = async (
options: FetchAlertDelaysByLineOptions
): Promise<LineDelays[]> => {
if (!options[FetchAlertDelaysByLineParams.line]) return [];

return await apiFetch({
path: '/api/linedelays',
options,
errorMessage: 'Failed to fetch reliability metrics',
});
};
2 changes: 1 addition & 1 deletion common/components/charts/ChartDiv.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ interface ChartDivProps {

export const ChartDiv: React.FC<ChartDivProps> = ({ children, isMobile = false }) => {
return (
<div className={classNames(isMobile ? 'h-50' : 'h-60', 'flex w-full flex-row')}>{children}</div>
<div className={classNames(isMobile ? 'h-48' : 'h-60', 'flex w-full flex-row')}>{children}</div>
);
};
15 changes: 13 additions & 2 deletions common/constants/pages.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import type { IconDefinition } from '@fortawesome/free-solid-svg-icons';
import {
faMapLocationDot,
faCalendar,
faHouse,
faUsers,
faWarning,
faClockFour,
faGaugeHigh,
faTableColumns,
faStopwatch20,
faCalendarDays,
faCalendarXmark,
} from '@fortawesome/free-solid-svg-icons';
import type { Line } from '../types/lines';

Expand All @@ -19,6 +20,7 @@ export enum PAGES {
overview = 'overview',
speed = 'speed',
predictions = 'predictions',
reliability = 'reliability',
service = 'service',
slowzones = 'slowzones',
systemSlowzones = 'systemSlowzones',
Expand Down Expand Up @@ -77,7 +79,7 @@ export const ALL_PAGES: PageMap = {
name: 'Multi-day trips',
title: 'Multi-day trips',
lines: ['line-red', 'line-blue', 'line-green', 'line-orange', 'line-bus'],
icon: faCalendar,
icon: faCalendarDays,
dateStoreSection: 'multiTrips',
hasStationStore: true,
},
Expand Down Expand Up @@ -113,6 +115,14 @@ export const ALL_PAGES: PageMap = {
dateStoreSection: 'line',
icon: faClockFour,
},
reliability: {
key: 'reliability',
path: '/reliability',
name: 'Reliability',
lines: ['line-red', 'line-orange', 'line-blue', 'line-green'],
icon: faCalendarXmark,
dateStoreSection: 'line',
},
slowzones: {
key: 'slowzones',
path: '/slowzones',
Expand Down Expand Up @@ -161,6 +171,7 @@ export const LINE_PAGES = [
ALL_PAGES.slowzones,
ALL_PAGES.speed,
ALL_PAGES.predictions,
ALL_PAGES.reliability,
ALL_PAGES.ridership,
];

Expand Down
12 changes: 12 additions & 0 deletions common/types/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,25 @@ export type FetchDeliveredTripMetricsOptions = {
line?: Line;
};

export type FetchAlertDelaysByLineOptions = {
start_date?: string;
end_date?: string;
line?: Line;
};

export enum FetchDeliveredTripMetricsParams {
startDate = 'start_date',
endDate = 'end_date',
agg = 'agg',
line = 'line',
}

export enum FetchAlertDelaysByLineParams {
startDate = 'start_date',
endDate = 'end_date',
line = 'line',
}

export enum FetchSpeedsParams {
startDate = 'start_date',
endDate = 'end_date',
Expand Down
19 changes: 19 additions & 0 deletions common/types/reliability.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Line } from './lines';

export interface LineDelays {
date: string;
disabled_train: number;
door_problem: number;
flooding: number;
fire: number;
line: Line;
medical_emergency: number;
other: number;
police_activity: number;
power_problem: number;
signal_problem: number;
brake_problem: number;
switch_problem: number;
total_delay_time: number;
track_issue: number;
}
3 changes: 3 additions & 0 deletions common/utils/time.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,14 @@ export const getFormattedTimeString = (value: number, unit: 'minutes' | 'seconds
const secondsValue = unit === 'seconds' ? value : value * 60;
const absValue = Math.round(Math.abs(secondsValue));
const duration = dayjs.duration(absValue, 'seconds');
const hoursDuration = duration.asHours();
switch (true) {
case absValue < 100:
return `${absValue}s`;
case absValue < 3600:
return `${duration.format('m')}m ${duration.format('s').padStart(2, '0')}s`;
case absValue > 86400:
return `${hoursDuration.toFixed(0)}h ${duration.format('m').padStart(2, '0')}m`;
default:
return `${duration.format('H')}h ${duration.format('m').padStart(2, '0')}m`;
}
Expand Down
2 changes: 1 addition & 1 deletion modules/navigation/SidebarTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const SidebarTabs: React.FC<SidebarTabs> = ({ tabs, close }) => {
selected
? 'bg-stone-900 text-white'
: enabled && 'text-stone-300 hover:bg-stone-800 hover:text-white',
'group flex select-none items-center gap-x-3 rounded-sm py-2 pl-2 text-sm font-semibold leading-6',
'group flex select-none items-center gap-x-3 rounded-sm py-1.5 pl-2 text-sm font-semibold leading-6',
enabled ? 'cursor-pointer' : 'cursor-default text-stone-600'
)}
>
Expand Down
74 changes: 74 additions & 0 deletions modules/reliability/ReliabilityDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
'use client';

import React from 'react';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import { useDelimitatedRoute } from '../../common/utils/router';
import { ChartPlaceHolder } from '../../common/components/graphics/ChartPlaceHolder';
import { Layout } from '../../common/layouts/layoutTypes';
import { PageWrapper } from '../../common/layouts/PageWrapper';
import { ChartPageDiv } from '../../common/components/charts/ChartPageDiv';
import { useAlertDelays } from '../../common/api/hooks/reliability';
import { Widget } from '../../common/components/widgets';
import { TotalDelayGraph } from './charts/TotalDelayGraph';
import { DelayBreakdownGraph } from './charts/DelayBreakdownGraph';
import { DelayByCategoryGraph } from './charts/DelayByCategoryGraph';

dayjs.extend(utc);

export function ReliabilityDetails() {
const {
line,
query: { startDate, endDate },
} = useDelimitatedRoute();

const enabled = Boolean(startDate && endDate && line);
const alertDelays = useAlertDelays(
{
start_date: startDate,
end_date: endDate,
line,
},
enabled
);
const reliabilityReady = alertDelays && line && !alertDelays.isError && alertDelays.data;
if (!startDate || !endDate) {
return <p>Select a date range to load graphs.</p>;
}

return (
<PageWrapper pageTitle={'Reliability'}>
<ChartPageDiv>
<Widget title="Total Time Delayed" ready={[alertDelays]}>
{reliabilityReady ? (
<TotalDelayGraph data={alertDelays.data} startDate={startDate} endDate={endDate} />
) : (
<div className="relative flex h-full">
<ChartPlaceHolder query={alertDelays} />
</div>
)}
</Widget>
<Widget title="Delay Time by Reason" ready={[alertDelays]}>
{reliabilityReady ? (
<DelayBreakdownGraph data={alertDelays.data} startDate={startDate} endDate={endDate} />
) : (
<div className="relative flex h-full">
<ChartPlaceHolder query={alertDelays} />
</div>
)}
</Widget>
<Widget title="Delay Time by Reason" ready={[alertDelays]}>
{reliabilityReady ? (
<DelayByCategoryGraph data={alertDelays.data} />
) : (
<div className="relative flex h-full">
<ChartPlaceHolder query={alertDelays} />
</div>
)}
</Widget>
</ChartPageDiv>
</PageWrapper>
);
}

ReliabilityDetails.Layout = Layout.Dashboard;
Loading

0 comments on commit 918759a

Please sign in to comment.