Skip to content

Commit

Permalink
Merge branch 'main' into commuter-rail-in-dd
Browse files Browse the repository at this point in the history
  • Loading branch information
devinmatte authored Feb 11, 2025
2 parents 81ccc94 + 7f6eda4 commit da3b53e
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 29 deletions.
8 changes: 7 additions & 1 deletion common/constants/charts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ export const DATA_LABELS_LANDING: LabelOptions = {
font: {
size: 12,
},
display: (context) => context.dataIndex === context.dataset.data.length - 1,
display: (context) => {
const lastNonNullIndex = context.dataset.data.reduce(
(lastIndex, item, index) => (item !== null ? index : lastIndex),
-1
);
return context.dataIndex === lastNonNullIndex;
},
formatter: (value) => `${value}%`, // Format the label content
};
1 change: 1 addition & 0 deletions common/types/lines.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export const LANDING_RAIL_LINES: Line[] = [
'line-orange',
'line-blue',
'line-green',
'line-mattapan',
'line-commuter-rail',
];

Expand Down
6 changes: 3 additions & 3 deletions modules/landing/charts/OverallRidershipChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ import React from 'react';
import type { RidershipCount } from '../../../common/types/dataPoints';
import { RidershipBaseline } from '../../../copy/landingCopy';
import type { Line as LineType } from '../../../common/types/lines';
import { convertToRidershipDataset } from '../utils';
import { convertToRidershipDataset, LANDING_CHART_LABELS } from '../utils';
import { LandingChartDiv } from '../LandingChartDiv';
import { LandingPageChart } from './LandingPageChart';

interface OverallRidershipChartProps {
ridershipData: { [key in LineType]: RidershipCount[] };
}
export const OverallRidershipChart: React.FC<OverallRidershipChartProps> = ({ ridershipData }) => {
const labels = Object.values(ridershipData)[0].map((point) => point.date);
const datasets = convertToRidershipDataset(ridershipData);
const labels = LANDING_CHART_LABELS;
const datasets = convertToRidershipDataset(ridershipData, labels);

return (
<LandingChartDiv>
Expand Down
6 changes: 3 additions & 3 deletions modules/landing/charts/OverallServiceChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import type { DeliveredTripMetrics } from '../../../common/types/dataPoints';
import type { Line } from '../../../common/types/lines';
import { ServiceBaseline } from '../../../copy/landingCopy';
import { LandingChartDiv } from '../LandingChartDiv';
import { convertToServiceDataset } from '../utils';
import { convertToServiceDataset, LANDING_CHART_LABELS } from '../utils';
import { LandingPageChart } from './LandingPageChart';

interface OverallServiceChartProps {
serviceData: { [key in Line]?: DeliveredTripMetrics[] };
}
export const OverallServiceChart: React.FC<OverallServiceChartProps> = ({ serviceData }) => {
const labels = Object.values(serviceData)[0].map((point) => point.date);
const datasets = convertToServiceDataset(serviceData);
const labels = LANDING_CHART_LABELS;
const datasets = convertToServiceDataset(serviceData, labels);

return (
<LandingChartDiv>
Expand Down
6 changes: 3 additions & 3 deletions modules/landing/charts/OverallSpeedChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import type { DeliveredTripMetrics } from '../../../common/types/dataPoints';
import { SpeedBaseline } from '../../../copy/landingCopy';
import type { Line } from '../../../common/types/lines';
import { LandingChartDiv } from '../LandingChartDiv';
import { convertToSpeedDataset } from '../utils';
import { convertToSpeedDataset, LANDING_CHART_LABELS } from '../utils';
import { LandingPageChart } from './LandingPageChart';

interface OverallSpeedChartProps {
speedData: { [key in Line]?: DeliveredTripMetrics[] };
}
export const OverallSpeedChart: React.FC<OverallSpeedChartProps> = ({ speedData }) => {
const labels = Object.values(speedData)[0].map((point) => point.date);
const datasets = convertToSpeedDataset(speedData);
const labels = LANDING_CHART_LABELS;
const datasets = convertToSpeedDataset(speedData, labels);
return (
<LandingChartDiv>
<LandingPageChart datasets={datasets} labels={labels} id="system-speed" />
Expand Down
81 changes: 66 additions & 15 deletions modules/landing/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { ChartDataset } from 'chart.js';
import { round } from 'lodash';
import dayjs from 'dayjs';
import {
PEAK_RIDERSHIP,
PEAK_SCHEDULED_SERVICE,
Expand All @@ -10,6 +11,7 @@ import type { RidershipCount, DeliveredTripMetrics } from '../../common/types/da
import type { Line } from '../../common/types/lines';
import type { AggregateDataPoint, SingleDayDataPoint } from '../../common/types/charts';
import { getStationDistance } from '../../common/utils/stations';
import { DATE_FORMAT } from '../../common/constants/dates';

const getDatasetOptions = (line: Line): Partial<ChartDataset<'line'>> => {
return {
Expand All @@ -29,20 +31,32 @@ const getDatasetOptions = (line: Line): Partial<ChartDataset<'line'>> => {
};
};

export const convertToSpeedDataset = (data: { [key in Line]?: DeliveredTripMetrics[] }) => {
export const convertToSpeedDataset = (
data: { [key in Line]?: DeliveredTripMetrics[] },
labels: string[]
) => {
return Object.keys(data).map((line: Line) => {
// We don't need to show the Mattapan line on the landing page
if (line === 'line-mattapan') {
return { data: [] };
}

const datasetOptions = getDatasetOptions(line);
return {
...datasetOptions,
data:
data[line]?.map((datapoint) =>
datapoint.miles_covered
labels?.map((label) => {
const datapoint = data[line]?.find((datapoint) => datapoint.date === label);
if (!datapoint) {
return null;
}
return datapoint.miles_covered
? round(
(100 * datapoint.miles_covered) / (datapoint.total_time / 3600) / PEAK_SPEED[line],
1
)
: Number.NaN
) ?? [],
: null;
}) ?? [],
};
});
};
Expand Down Expand Up @@ -95,33 +109,70 @@ export const convertToAggregateStationSpeedDataset = (
return ret;
};

export const convertToServiceDataset = (data: { [key in Line]?: DeliveredTripMetrics[] }) => {
export const convertToServiceDataset = (
data: { [key in Line]?: DeliveredTripMetrics[] },
labels: string[]
) => {
return Object.keys(data).map((line: Line) => {
// We don't need to show the Mattapan line on the landing page
if (line === 'line-mattapan') {
return { data: [] };
}

const datasetOptions = getDatasetOptions(line);
return {
...datasetOptions,
data:
data[line]?.map((datapoint) =>
datapoint.miles_covered
labels.map((label) => {
const datapoint = data[line]?.find((datapoint) => datapoint.date === label);
if (!datapoint) return null;
return datapoint.miles_covered
? round((100 * datapoint.count) / PEAK_SCHEDULED_SERVICE[line], 1)
: Number.NaN
) ?? [],
: null;
}) ?? [],
};
});
};

export const convertToRidershipDataset = (data: { [key in Line]: RidershipCount[] }) => {
export const convertToRidershipDataset = (
data: { [key in Line]: RidershipCount[] },
labels: string[]
) => {
return (
Object.keys(data).map((line: Line) => {
// We don't need to show the Mattapan line on the landing page
if (line === 'line-mattapan') {
return { data: [] };
}

const datasetOptions = getDatasetOptions(line);
return {
...datasetOptions,
data: data[line]?.map((datapoint) =>
datapoint.count
data: labels.map((labels) => {
const datapoint = data[line]?.find((datapoint) => datapoint.date === labels);
if (!datapoint) return null;
return datapoint.count
? Math.round(10 * 100 * (datapoint.count / PEAK_RIDERSHIP[line])) / 10
: Number.NaN
),
: null;
}),
};
}) ?? []
);
};

const getLabels = () => {
// Get the most recent Monday. This date may or may not be in the dataset, but we can still use it to construct the labels.
// It's important to use Monday, that is the day the weekly trip metrics are based on.
const latestDate: dayjs.Dayjs = dayjs().startOf('week').add(1, 'day');

// Generate the labels for the last 14 weeks, starting from the most recent Monday.
// The endpoint returns 12 weeks, but we add 2 extra weeks in case the past Monday is not in the dataset.
// There's no harm in having extra labels.
const labels: string[] = [];
for (let i = 13; i >= 0; i--) {
labels.push(latestDate.subtract(i, 'weeks').format(DATE_FORMAT));
}
return labels;
};

export const LANDING_CHART_LABELS = getLabels();
7 changes: 4 additions & 3 deletions modules/slowzones/map/segment.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Diagram, SegmentLocation } from '@transitmatters/stripmap';

import dayjs from 'dayjs';
import type {
SlowZoneAllSlowResponse,
SlowZoneResponse,
Expand Down Expand Up @@ -44,11 +45,11 @@ type WithSegmentLocation<T> = {
* Get the latest date that we have data on slow zones for
*/
const getEffectiveDate = (desiredDate: Date, slowZones: SlowZoneAllSlowResponse) => {
const maxEndDate = new Date(slowZones.updated_on);
if (desiredDate.valueOf() > maxEndDate.valueOf()) {
const maxEndDate = dayjs(new Date(slowZones.updated_on)).startOf('day').toDate();
if (dayjs(desiredDate).isAfter(maxEndDate)) {
return maxEndDate;
}
return desiredDate;
return dayjs(desiredDate).startOf('day').toDate();
};

const filterActiveElements = <T extends Record<string, unknown>>(
Expand Down
2 changes: 1 addition & 1 deletion server/chalicelib/date_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
EASTERN_TIME = ZoneInfo("US/Eastern")

# The most recent date for which we have monthly data
MAX_MONTH_DATA_DATE = "2024-06-30"
MAX_MONTH_DATA_DATE = "2024-12-31"


def get_max_monthly_data_date():
Expand Down

0 comments on commit da3b53e

Please sign in to comment.