Skip to content

Commit 8a86ec9

Browse files
committed
Accessibility improvements
Consistent timetable_label and timetable_page_label
1 parent 3b90af3 commit 8a86ec9

File tree

10 files changed

+151
-112
lines changed

10 files changed

+151
-112
lines changed

lib/file-utils.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ const fs = require('fs-extra');
55
const sanitize = require('sanitize-filename');
66
const untildify = require('untildify');
77

8+
const formatters = require('./formatters');
9+
810
exports.prepDirectory = (exportPath, assetPath) => {
911
return fs.remove(exportPath)
1012
.then(() => fs.ensureDir(exportPath))
@@ -25,6 +27,23 @@ exports.zipFolder = exportPath => {
2527
});
2628
};
2729

30+
exports.generateFileName = (timetable, route) => {
31+
let routeName = route.route_short_name;
32+
if (routeName !== '' && routeName !== undefined) {
33+
routeName = route.route_long_name;
34+
}
35+
36+
let filename = `${timetable.timetable_id}_${routeName}_`;
37+
38+
if (timetable.direction_id !== '' && timetable.direction_id !== null) {
39+
filename += `${timetable.direction_id}_`;
40+
}
41+
42+
filename += `${formatters.formatDays(timetable).toLowerCase()}.html`;
43+
44+
return sanitize(filename).replace(/\s/g, '');
45+
};
46+
2847
exports.generateFolderName = timetablePage => {
2948
// Use first timetable in timetable page for start date and end date
3049
const timetable = timetablePage.timetables[0];

lib/formatters.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,6 @@ exports.formatDays = calendar => {
105105
return dayString;
106106
};
107107

108-
exports.formatRouteName = route => {
109-
if (route.route_short_name !== '' && route.route_short_name !== undefined) {
110-
return route.route_short_name;
111-
}
112-
return route.route_long_name;
113-
};
114-
115108
exports.formatTimetableId = (route, direction) => {
116109
let timetableId = route.route_id;
117110
if (direction) {
@@ -121,9 +114,16 @@ exports.formatTimetableId = (route, direction) => {
121114
};
122115

123116
exports.formatTimetableLabel = (route, direction) => {
124-
let timetableLabel = exports.formatRouteName(route);
117+
const timetableLabelParts = [];
118+
if (route.route_short_name !== '' && route.route_short_name !== undefined) {
119+
timetableLabelParts.push(route.route_short_name);
120+
}
121+
if (route.route_long_name !== '' && route.route_long_name !== undefined) {
122+
timetableLabelParts.push(route.route_long_name);
123+
}
124+
125125
if (direction) {
126-
timetableLabel += ` to ${direction.trip_headsign}`;
126+
timetableLabelParts.push(`to ${direction.trip_headsign}`);
127127
}
128-
return timetableLabel;
128+
return timetableLabelParts.join(' ');
129129
};

lib/utils.js

Lines changed: 90 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,39 @@ const beautify = require('js-beautify').html_beautify;
33
const gtfs = require('gtfs');
44
const pug = require('pug');
55
const moment = require('moment');
6-
const sanitize = require('sanitize-filename');
76

87
const fileUtils = require('./file-utils');
98
const formatters = require('./formatters');
109
const geoJSONUtils = require('./geojson-utils');
1110

11+
const getTimetableColors = async timetablePage => {
12+
const routes = await gtfs.getRoutes({
13+
agency_key: timetablePage.agency_key,
14+
route_id: {$in: timetablePage.routeIds}
15+
});
16+
return _.compact(_.uniq(_.map(routes, 'route_color')));
17+
};
18+
19+
function getDaysFromCalendars(calendars) {
20+
const days = {
21+
monday: 0,
22+
tuesday: 0,
23+
wednesday: 0,
24+
thursday: 0,
25+
friday: 0,
26+
saturday: 0,
27+
sunday: 0
28+
};
29+
30+
for (const calendar of calendars) {
31+
_.each(days, (value, day) => {
32+
days[day] = value | calendar[day];
33+
});
34+
}
35+
36+
return days;
37+
}
38+
1239
function filterAndSortCalendarDates(calendarDates, startDate, endDate) {
1340
if (!calendarDates) {
1441
return [];
@@ -188,7 +215,59 @@ const processStops = async timetable => {
188215
}
189216
};
190217

191-
const generateFileName = async timetable => {
218+
const formatTimetable = async timetable => {
219+
if (!timetable.timetable_label) {
220+
const routes = await gtfs.getRoutes({
221+
agency_key: timetable.agency_key,
222+
route_id: timetable.route_id
223+
});
224+
if (!routes || routes.length === 0) {
225+
throw new Error(`No route found for route_id=${timetable.route_id}, timetable_id=${timetable.timetable_id}`);
226+
}
227+
let direction;
228+
if (timetable.direction_id !== null && timetable.direction_id !== '') {
229+
const directions = await gtfs.getDirectionsByRoute({
230+
agency_key: timetable.agency_key,
231+
route_id: timetable.route_id,
232+
direction_id: timetable.direction_id
233+
});
234+
direction = _.first(directions);
235+
}
236+
timetable.timetable_label = formatters.formatTimetableLabel(routes[0], direction);
237+
}
238+
return timetable;
239+
};
240+
241+
const formatTimetablePage = async timetablePage => {
242+
// If timetable_page_label not set, use first route to name it
243+
if (timetablePage.timetable_page_label === '' || timetablePage.timetable_page_label === undefined) {
244+
const routes = await gtfs.getRoutes({
245+
agency_key: timetablePage.agency_key,
246+
route_id: timetablePage.timetables[0].route_id
247+
});
248+
249+
if (routes.length === 0) {
250+
throw new Error(`No route found for route_id=${timetablePage.timetables[0].route_id}, timetable_id=${timetablePage.timetables[0].timetable_id}`);
251+
}
252+
const route = routes[0];
253+
254+
timetablePage.timetable_page_label = formatters.formatTimetableLabel(route);
255+
}
256+
257+
// Format each timetable in timetable_page
258+
timetablePage.timetables = await Promise.all(timetablePage.timetables.map(timetable => formatTimetable(timetable)));
259+
260+
// Summarize timetables in timetablePage
261+
timetablePage.dayList = formatters.formatDays(getDaysFromCalendars(timetablePage.timetables));
262+
timetablePage.dayLists = _.uniq(timetablePage.timetables.map(timetable => timetable.dayList));
263+
timetablePage.routeIds = _.uniq(_.map(timetablePage.timetables, 'route_id'));
264+
timetablePage.routeColors = await getTimetableColors(timetablePage);
265+
timetablePage.directionNames = _.uniq(timetablePage.timetables.map(timetable => timetable.direction_name));
266+
267+
return timetablePage;
268+
};
269+
270+
const convertTimetableToTimetablePage = async timetable => {
192271
const routes = await gtfs.getRoutes({
193272
agency_key: timetable.agency_key,
194273
route_id: timetable.route_id
@@ -199,25 +278,13 @@ const generateFileName = async timetable => {
199278
}
200279

201280
const route = routes[0];
202-
const routeName = formatters.formatRouteName(route);
203-
let filename = `${timetable.timetable_id}_${routeName}_`;
204-
205-
if (timetable.direction_id !== '' && timetable.direction_id !== null) {
206-
filename += `${timetable.direction_id}_`;
207-
}
208-
209-
filename += `${formatters.formatDays(timetable).toLowerCase()}.html`;
210-
211-
return sanitize(filename).replace(/\s/g, '');
212-
};
213-
214-
const convertTimetableToTimetablePage = async timetable => {
215-
const filename = await generateFileName(timetable);
281+
const filename = await fileUtils.generateFileName(timetable, route);
282+
const formattedTimetable = await formatTimetable(timetable);
216283
return {
217-
agency_key: timetable.agency_key,
218-
timetable_page_id: timetable.timetable_id,
219-
timetable_page_label: timetable.timetable_label,
220-
timetables: [timetable],
284+
agency_key: formattedTimetable.agency_key,
285+
timetable_page_id: formattedTimetable.timetable_id,
286+
timetable_page_label: formattedTimetable.timetable_label,
287+
timetables: [formattedTimetable],
221288
filename
222289
};
223290
};
@@ -260,11 +327,11 @@ const convertRouteToTimetablePage = async (route, direction) => {
260327
const convertRoutesToTimetablePages = async agencyKey => {
261328
const routes = await gtfs.getRoutes({agency_key: agencyKey});
262329
const timetablePages = await Promise.all(routes.map(async route => {
263-
const results = await gtfs.getDirectionsByRoute({
330+
const directions = await gtfs.getDirectionsByRoute({
264331
agency_key: agencyKey,
265332
route_id: route.route_id
266333
});
267-
const directionGroups = _.groupBy(results, direction => direction.direction_id);
334+
const directionGroups = _.groupBy(directions, direction => direction.direction_id);
268335
return Promise.all(_.map(directionGroups, directionGroup => {
269336
const direction = directionGroup[0];
270337
return convertRouteToTimetablePage(route, direction);
@@ -310,60 +377,6 @@ function getStops(timetableStopOrders, stoptimes) {
310377
return stops;
311378
}
312379

313-
const getTimetableColors = async timetablePage => {
314-
const routes = await gtfs.getRoutes({
315-
agency_key: timetablePage.agency_key,
316-
route_id: {$in: timetablePage.routeIds}
317-
});
318-
return _.compact(_.uniq(_.map(routes, 'route_color')));
319-
};
320-
321-
const formatTimetablePage = async timetablePage => {
322-
// If timetable_page_label not set, use first route to name it
323-
if (timetablePage.timetable_page_label === '' || timetablePage.timetable_page_label === undefined) {
324-
const routes = await gtfs.getRoutes({
325-
agency_key: timetablePage.agency_key,
326-
route_id: timetablePage.timetables[0].route_id
327-
});
328-
329-
if (routes.length === 0) {
330-
throw new Error(`No route found for route_id=${timetablePage.timetables[0].route_id}, timetable_id=${timetablePage.timetables[0].timetable_id}`);
331-
}
332-
const route = routes[0];
333-
334-
timetablePage.timetable_page_label = `${route.route_short_name} ${route.route_long_name}`;
335-
}
336-
337-
// Summarize timetables in timetablePage
338-
timetablePage.dayList = formatters.formatDays(getDaysFromCalendars(timetablePage.timetables));
339-
timetablePage.dayLists = _.uniq(timetablePage.timetables.map(timetable => timetable.dayList));
340-
timetablePage.routeIds = _.uniq(_.map(timetablePage.timetables, 'route_id'));
341-
timetablePage.routeColors = await getTimetableColors(timetablePage);
342-
timetablePage.directionNames = _.uniq(timetablePage.timetables.map(timetable => timetable.direction_name));
343-
344-
return timetablePage;
345-
};
346-
347-
function getDaysFromCalendars(calendars) {
348-
const days = {
349-
monday: 0,
350-
tuesday: 0,
351-
wednesday: 0,
352-
thursday: 0,
353-
friday: 0,
354-
saturday: 0,
355-
sunday: 0
356-
};
357-
358-
for (calendar of calendars) {
359-
_.each(days, (value, day) => {
360-
days[day] = value | calendar[day];
361-
});
362-
}
363-
364-
return days;
365-
}
366-
367380
exports.setDefaultConfig = config => {
368381
const defaults = {
369382
beautify: false,
@@ -672,9 +685,7 @@ exports.getTimetablePages = async agencyKey => {
672685
});
673686
}
674687

675-
return Promise.all(timetablePages.map(timetablePage => {
676-
return formatTimetablePage(timetablePage);
677-
}));
688+
return Promise.all(timetablePages.map(timetablePage => formatTimetablePage(timetablePage)));
678689
};
679690

680691
exports.log = config => {

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "gtfs-to-html",
33
"description": "Build transit timetables in HTML from a GTFS file",
4-
"version": "0.11.0",
4+
"version": "0.11.1",
55
"keywords": [
66
"transit",
77
"gtfs",

views/timetable/timetable_horizontal.pug

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
.table-container
2-
table.table.table-striped.table-horizontal
2+
table.table.table-striped.table-horizontal(summary= getTimetableSummary(timetable))
3+
caption.sr-only= `${timetable.timetable_label} | ${timetable.dayList}`
34
colgroup
45
col
56
each trip, idx in timetable.orderedTrips
67
col(id=`trip_id_${trip.trip_id}` class=`run_${idx} ${(trip.trip_short_name) ? 'trip_short_name_' + trip.trip_short_name : ''}`)
78
thead
89
tr
9-
th.stop-header(scope="column") Stop
10+
th.stop-header(scope="col") Stop
1011
each trip, idx in timetable.orderedTrips
11-
th.run-header(scope="column")= formatTripName(trip, idx, timetable.showDayList)
12+
th.run-header(scope="col")= formatTripName(trip, idx, timetable.showDayList)
1213

1314
tbody
1415
- var previousCity

views/timetable/timetable_hourly.pug

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,12 @@
44
- }
55

66
.table-container
7-
table.table.table-striped.table-hourly
7+
table.table.table-striped.table-hourly(summary= getTimetableSummary(timetable))
8+
caption.sr-only= `${timetable.timetable_label} | ${timetable.dayList}`
89
thead
910
tr
10-
th.stop-header(scope="column") Stop
11-
th(scope="column") Minutes after the hour
11+
th.stop-header(scope="col") Stop
12+
th(scope="col") Minutes after the hour
1213

1314
tbody
1415
- var previousCity
@@ -21,7 +22,7 @@
2122
tr.stop-row(id=`stop_id_${stop.stop_id}`)
2223
th(scope="row")
2324
include timetable_stopname.pug
24-
25+
2526
- trip = stop.trips[0]
2627
td.stop-time(class=`${trip.classes.join(' ')}`)= formatMinutesAfterTheHour(trip.arrival_formatted_time)
2728

views/timetable/timetable_menu.pug

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ if timetablePage.timetables.length > 1
1212
.col-md-4
1313
h3= dayList
1414
each timetable in group
15-
a.btn.btn-default(href=`#timetable_id_${timetable.timetable_id}`)= getTimetableLabel(timetable)
16-
15+
a.btn.btn-default(href=`#timetable_id_${timetable.timetable_id}`)= timetable.timetable_label
16+
1717
if config.menuType === 'radio'
1818
.row
1919
.col-md-4

views/timetable/timetable_vertical.pug

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
.table-container
2-
table.table.table-vertical
2+
table.table.table-vertical(summary= getTimetableSummary(timetable))
3+
caption.sr-only= `${timetable.timetable_label} | ${timetable.dayList}`
34
colgroup
45
each stop, idx in timetable.stops
56
col(id=`stop_id_${stop.stop_id}` class=`stop-${idx}` data-stop-id=`${stop.stop_id}`)
67
thead
78
tr
89
each stop, idx in timetable.stops
9-
th.stop-header(scope="column")
10+
th.stop-header(scope="col")
1011
include timetable_stopname.pug
1112

1213
tbody

0 commit comments

Comments
 (0)