Skip to content

Commit

Permalink
add csv download link to speed, prediction accuracy, service, ridersh…
Browse files Browse the repository at this point in the history
…ip (#935)

* add csv download link to speed, prediction accuracy, service, ridership

* make DownloadButton data type agnostic

* create strict typing for csv util

* lint fixes

* remove unknown typing for data prop

* remove unused data types

---------

Co-authored-by: Casey Whittaker <[email protected]>
  • Loading branch information
cdwhitt and Casey Whittaker authored Jan 15, 2024
1 parent 43f3de4 commit 0f0631d
Show file tree
Hide file tree
Showing 18 changed files with 170 additions and 78 deletions.
56 changes: 15 additions & 41 deletions common/components/buttons/DownloadButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,59 +3,26 @@ import React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faFileArrowDown } from '@fortawesome/free-solid-svg-icons';
import classNames from 'classnames';
import type { DataPoint } from '../../types/dataPoints';
import type { AggregateDataPoint, Location } from '../../types/charts';
import type { Location } from '../../types/charts';
import { lineColorTextHover } from '../../styles/general';
import { useDelimitatedRoute } from '../../utils/router';

const directionAbbrs = {
northbound: 'NB',
southbound: 'SB',
eastbound: 'EB',
westbound: 'WB',
inbound: 'IB',
outbound: 'OB',
};

function filename(
datasetName: string,
location: Location,
bothStops: boolean,
startDate: string,
endDate?: string
) {
// CharlesMGH-SB_dwells_20210315.csv
// CentralSquareCambridge-MelneaCassWashington_traveltimesByHour-weekday_20200101-20201231.csv
// BostonUniversityWest-EB_headways_20161226-20170328.csv
const fromStop = location.from.replace(/[^A-z]/g, '');
const toStop = location.to.replace(/[^A-z]/g, '');
const dir = directionAbbrs[location.direction];
const where = `${fromStop}-${bothStops ? toStop : dir}`;

const what = datasetName;

const date1 = startDate.replaceAll('-', '');
const date2 = endDate ? `-${endDate.replaceAll('-', '')}` : '';
const when = `${date1}${date2}`;

return `${where}_${what}_${when}.csv`;
}
import { getCsvFilename } from '../../utils/csv';

interface DownloadButtonProps {
datasetName: string;
data: (DataPoint | AggregateDataPoint)[];
location: Location;
bothStops: boolean;
data: Record<string, any>[];

Check warning on line 13 in common/components/buttons/DownloadButton.tsx

View workflow job for this annotation

GitHub Actions / frontend (20, 3.11)

Unexpected any. Specify a different type
startDate: string;
includeBothStopsForLocation?: boolean;
location?: Location;
endDate?: string;
}

export const DownloadButton: React.FC<DownloadButtonProps> = ({
datasetName,
data,
location,
bothStops,
includeBothStopsForLocation,
startDate,
location,
endDate,
}) => {
const { line } = useDelimitatedRoute();
Expand All @@ -65,7 +32,14 @@ export const DownloadButton: React.FC<DownloadButtonProps> = ({
className={'csv-link'}
data={data}
title={'Download data as CSV'}
filename={filename(datasetName, location, bothStops, startDate, endDate)}
filename={getCsvFilename({
datasetName,
includeBothStopsForLocation,
startDate,
line,
location,
endDate,
})}
>
<FontAwesomeIcon
icon={faFileArrowDown}
Expand Down
4 changes: 2 additions & 2 deletions common/components/charts/AggregateLineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const AggregateLineChart: React.FC<AggregateLineProps> = ({
data,
location,
pointField,
bothStops = false,
includeBothStopsForLocation = false,
fname,
timeUnit,
timeFormat,
Expand Down Expand Up @@ -179,7 +179,7 @@ export const AggregateLineChart: React.FC<AggregateLineProps> = ({
data={data}
datasetName={fname}
location={location}
bothStops={bothStops}
includeBothStopsForLocation={includeBothStopsForLocation}
startDate={startDate}
/>
)}
Expand Down
4 changes: 2 additions & 2 deletions common/components/charts/SingleDayLineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const SingleDayLineChart: React.FC<SingleDayLineProps> = ({
pointField,
benchmarkField,
fname,
bothStops = false,
includeBothStopsForLocation = false,
location,
units,
showLegend = true,
Expand Down Expand Up @@ -229,7 +229,7 @@ export const SingleDayLineChart: React.FC<SingleDayLineProps> = ({
data={data}
datasetName={fname}
location={location}
bothStops={bothStops}
includeBothStopsForLocation={includeBothStopsForLocation}
startDate={date}
/>
)}
Expand Down
7 changes: 1 addition & 6 deletions common/components/charts/TimeSeriesChart/TimeSeriesChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import type { ChartData } from 'chart.js';

import { enUS } from 'date-fns/locale';
import { useBreakpoint } from '../../../hooks/useBreakpoint';
import { ChartBorder } from '../ChartBorder';
import { ChartDiv } from '../ChartDiv';
import { CHART_COLORS, COLORS } from '../../../constants/colors';

Expand Down Expand Up @@ -282,9 +281,5 @@ export const TimeSeriesChart = <Data extends Dataset[]>(props: Props<Data>) => {
);
}, [isMobile, chartJsData, chartJsOptions, chartJsPlugins]);

return (
<ChartBorder>
<ChartDiv isMobile={isMobile}>{chart}</ChartDiv>
</ChartBorder>
);
return <ChartDiv isMobile={isMobile}>{chart}</ChartDiv>;
};
4 changes: 2 additions & 2 deletions common/types/charts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export interface LineProps {
chartId: string;
location: Location;
pointField: PointField; // X value
bothStops?: boolean;
includeBothStopsForLocation?: boolean;
fname: DataName;
showLegend?: boolean;
}
Expand Down Expand Up @@ -111,7 +111,7 @@ export interface HeadwayHistogramProps {
date: string | undefined;
location: Location;
isLoading: boolean;
bothStops?: boolean;
includeBothStopsForLocation?: boolean;
fname: DataName;
showLegend?: boolean;
metricField: MetricField;
Expand Down
2 changes: 2 additions & 0 deletions common/types/dataPoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export interface DeliveredTripMetrics {
miles_covered: number;
total_time: number;
count: number;
miles_per_hour?: string;
}

export type LineSegmentData = {
Expand Down Expand Up @@ -136,6 +137,7 @@ export interface TimePrediction {
weekly: string;
num_accurate_predictions: number;
num_predictions: number;
accuracy_percentage?: string;
}

export type PredictionBin = '0-3 min' | '3-6 min' | '6-12 min' | '12-30 min';
Expand Down
61 changes: 61 additions & 0 deletions common/utils/csv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { flatten } from 'lodash';
import type { Location } from '../types/charts';
import type { DeliveredTripMetrics, TimePredictionWeek } from '../types/dataPoints';

const directionAbbrs = {
northbound: 'NB',
southbound: 'SB',
eastbound: 'EB',
westbound: 'WB',
inbound: 'IB',
outbound: 'OB',
};

type GetCsvFilenameOptions = {
datasetName: string;
startDate: string;
endDate?: string;
line?: string;
location?: Location;
includeBothStopsForLocation?: boolean | undefined;
};

export function getCsvFilename(options: GetCsvFilenameOptions) {
const { datasetName, startDate, endDate, line, location, includeBothStopsForLocation } = options;
// CharlesMGH-SB_dwells_20210315.csv
// CentralSquareCambridge-MelneaCassWashington_traveltimesByHour-weekday_20200101-20201231.csv
// BostonUniversityWest-EB_headways_20161226-20170328.csv
const fromStop = location?.from.replace(/[^A-z]/g, '');
const toStop = location?.to.replace(/[^A-z]/g, '');
const dir = location && directionAbbrs[location.direction];

//Location does not exist on all widgets - in that case, 'where' will just be the name of the line
const where = location ? `${fromStop}-${includeBothStopsForLocation ? toStop : dir}` : line;
const what = datasetName;
const date1 = startDate.replaceAll('-', '');
const date2 = endDate ? `-${endDate.replaceAll('-', '')}` : '';
const when = `${date1}${date2}`;

return `${where}_${what}_${when}.csv`;
}

export const addAccuracyPercentageToData = (data: TimePredictionWeek[]) => {
const predictionsList = flatten(data.map(({ prediction }) => prediction));

const newData = predictionsList.map((item) => {
const accuracyPercentage = (item?.num_accurate_predictions / item?.num_predictions) * 100;
return { ...item, accuracy_percentage: accuracyPercentage.toFixed(1) };
});

return newData;
};

export const addMPHToSpeedData = (data: DeliveredTripMetrics[]) => {
const newData = data.map((item) => {
const hours = item.total_time / 3600;
const mph = item.miles_covered / hours;
return { ...item, miles_per_hour: mph.toFixed(1) };
});

return newData;
};
2 changes: 1 addition & 1 deletion modules/dwells/charts/DwellsAggregateChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const DwellsAggregateChart: React.FC<DwellsAggregateChartProps> = ({
endDate={endDate}
fillColor={CHART_COLORS.FILL}
location={getLocationDetails(fromStation, toStation)}
bothStops={false}
includeBothStopsForLocation={false}
fname="dwells"
yUnit="Minutes"
/>
Expand Down
2 changes: 1 addition & 1 deletion modules/headways/charts/HeadwaysAggregateChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const HeadwaysAggregateChart: React.FC<HeadwaysAggregateChartProps> = ({
endDate={endDate}
fillColor={CHART_COLORS.FILL}
location={getLocationDetails(fromStation, toStation)}
bothStops={false}
includeBothStopsForLocation={false}
fname="headways"
yUnit="Minutes"
/>
Expand Down
15 changes: 15 additions & 0 deletions modules/predictions/charts/PredictionsGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import { ChartDiv } from '../../../common/components/charts/ChartDiv';
import { PEAK_SPEED } from '../../../common/constants/baselines';
import { getRemainingBlockAnnotation } from '../../service/utils/graphUtils';
import { DATE_FORMAT, TODAY } from '../../../common/constants/dates';
import { DownloadButton } from '../../../common/components/buttons/DownloadButton';
import { addAccuracyPercentageToData } from '../../../common/utils/csv';

interface PredictionsGraphProps {
data: TimePredictionWeek[];
Expand Down Expand Up @@ -61,6 +63,8 @@ export const PredictionsGraph: React.FC<PredictionsGraphProps> = ({
}, 0),
}));

const dataWithPercentage = addAccuracyPercentageToData(data);

return (
<ChartBorder>
<ChartDiv isMobile={isMobile}>
Expand Down Expand Up @@ -207,6 +211,17 @@ export const PredictionsGraph: React.FC<PredictionsGraphProps> = ({
]}
/>
</ChartDiv>
<div className="flex flex-row items-end justify-end gap-4">
{startDate && (
<DownloadButton
data={dataWithPercentage}
datasetName="ridership predictions"
includeBothStopsForLocation={false}
startDate={startDate}
endDate={endDate}
/>
)}
</div>
</ChartBorder>
);
};
12 changes: 12 additions & 0 deletions modules/ridership/RidershipGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { useBreakpoint } from '../../common/hooks/useBreakpoint';
import { watermarkLayout } from '../../common/constants/charts';
import { ChartBorder } from '../../common/components/charts/ChartBorder';
import { ChartDiv } from '../../common/components/charts/ChartDiv';
import { DownloadButton } from '../../common/components/buttons/DownloadButton';

interface RidershipGraphProps {
data: RidershipCount[];
Expand Down Expand Up @@ -198,6 +199,17 @@ export const RidershipGraph: React.FC<RidershipGraphProps> = ({
]}
/>
</ChartDiv>
<div className="flex flex-row items-end justify-end gap-4">
{startDate && (
<DownloadButton
data={data}
datasetName="ridership"
includeBothStopsForLocation={false}
startDate={startDate}
endDate={endDate}
/>
)}
</div>
</ChartBorder>
);
}, [
Expand Down
38 changes: 27 additions & 11 deletions modules/service/ServiceGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import { useDelimitatedRoute } from '../../common/utils/router';
import { PEAK_SCHEDULED_SERVICE } from '../../common/constants/baselines';
import type { DeliveredTripMetrics, ScheduledService } from '../../common/types/dataPoints';
import type { ParamsType } from '../speed/constants/speeds';
import { getShuttlingBlockAnnotations } from './utils/graphUtils';
import { ChartBorder } from '../../common/components/charts/ChartBorder';
import { DownloadButton } from '../../common/components/buttons/DownloadButton';
import { ScheduledAndDeliveredGraph } from './ScheduledAndDeliveredGraph';
import { getShuttlingBlockAnnotations } from './utils/graphUtils';

interface ServiceGraphProps {
config: ParamsType;
Expand Down Expand Up @@ -68,15 +70,29 @@ export const ServiceGraph: React.FC<ServiceGraphProps> = (props: ServiceGraphPro
}, [data, peak]);

return (
<ScheduledAndDeliveredGraph
valueAxisLabel="Round trips"
scheduled={scheduled}
delivered={delivered}
blocks={blocks}
benchmarks={benchmarks}
startDate={startDate}
endDate={endDate}
agg={config.agg}
/>
<ChartBorder>
<ScheduledAndDeliveredGraph
valueAxisLabel="Round trips"
scheduled={scheduled}
delivered={delivered}
blocks={blocks}
benchmarks={benchmarks}
startDate={startDate}
endDate={endDate}
agg={config.agg}
/>

<div className="flex flex-row items-end justify-end gap-4">
{startDate && (
<DownloadButton
data={data}
datasetName="service"
includeBothStopsForLocation={false}
startDate={startDate}
endDate={endDate}
/>
)}
</div>
</ChartBorder>
);
};
19 changes: 11 additions & 8 deletions modules/service/ServiceHoursGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useMemo } from 'react';

import type { FetchServiceHoursResponse } from '../../common/types/api';
import type { AggType } from '../speed/constants/speeds';
import { ChartBorder } from '../../common/components/charts/ChartBorder';
import { ScheduledAndDeliveredGraph } from './ScheduledAndDeliveredGraph';

interface ServiceHoursGraphProps {
Expand Down Expand Up @@ -32,13 +33,15 @@ export const ServiceHoursGraph: React.FC<ServiceHoursGraphProps> = (
}, [serviceHours]);

return (
<ScheduledAndDeliveredGraph
scheduled={scheduled}
delivered={delivered}
startDate={startDate}
endDate={endDate}
agg={agg}
valueAxisLabel="Service hours"
/>
<ChartBorder>
<ScheduledAndDeliveredGraph
scheduled={scheduled}
delivered={delivered}
startDate={startDate}
endDate={endDate}
agg={agg}
valueAxisLabel="Service hours"
/>
</ChartBorder>
);
};
Loading

0 comments on commit 0f0631d

Please sign in to comment.