gtfs 3.8.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/@types/index.d.ts +130 -137
- package/@types/tests.ts +18 -13
- package/CHANGELOG.md +18 -0
- package/README.md +233 -212
- package/config-sample-rtupdates.json +6 -18
- package/lib/advancedQuery.js +8 -17
- package/lib/db.js +35 -59
- package/lib/export.js +16 -10
- package/lib/gtfs/agencies.js +8 -6
- package/lib/gtfs/areas.js +8 -11
- package/lib/gtfs/attributions.js +8 -6
- package/lib/gtfs/calendar-dates.js +8 -6
- package/lib/gtfs/calendars.js +8 -6
- package/lib/gtfs/fare-attributes.js +8 -6
- package/lib/gtfs/fare-leg-rules.js +8 -6
- package/lib/gtfs/fare-products.js +8 -6
- package/lib/gtfs/fare-rules.js +8 -6
- package/lib/gtfs/fare-transfer-rules.js +8 -6
- package/lib/gtfs/feed-info.js +8 -6
- package/lib/gtfs/frequencies.js +8 -6
- package/lib/gtfs/levels.js +8 -11
- package/lib/gtfs/pathways.js +8 -6
- package/lib/gtfs/routes.js +8 -11
- package/lib/gtfs/shapes.js +34 -39
- package/lib/gtfs/stop-areas.js +8 -6
- package/lib/gtfs/stop-times.js +8 -6
- package/lib/gtfs/stops.js +27 -33
- package/lib/gtfs/transfers.js +8 -6
- package/lib/gtfs/translations.js +8 -6
- package/lib/gtfs/trips.js +8 -11
- package/lib/gtfs-realtime/service-alerts.js +9 -7
- package/lib/gtfs-realtime/stop-times-updates.js +9 -7
- package/lib/gtfs-realtime/trip-updates.js +9 -7
- package/lib/gtfs-realtime/vehicle-positions.js +9 -7
- package/lib/gtfs-ride/board-alights.js +8 -6
- package/lib/gtfs-ride/ride-feed-infos.js +8 -6
- package/lib/gtfs-ride/rider-trips.js +8 -6
- package/lib/gtfs-ride/riderships.js +8 -6
- package/lib/gtfs-ride/trip-capacities.js +8 -6
- package/lib/gtfs.js +3 -9
- package/lib/import.js +213 -273
- package/lib/non-standard/directions.js +8 -6
- package/lib/non-standard/stop-attributes.js +8 -6
- package/lib/non-standard/timetable-notes-references.js +8 -6
- package/lib/non-standard/timetable-notes.js +8 -6
- package/lib/non-standard/timetable-pages.js +8 -6
- package/lib/non-standard/timetable-stop-order.js +8 -6
- package/lib/non-standard/timetables.js +8 -6
- package/lib/non-standard/trips-dated-vehicle-journey.js +9 -7
- package/package.json +4 -4
- package/test/mocha/advanced-query.js +9 -15
- package/test/mocha/export-gtfs.js +5 -6
- package/test/mocha/fare-transfer-rules.js +32 -0
- package/test/mocha/get-agencies.js +13 -19
- package/test/mocha/get-areas.js +27 -0
- package/test/mocha/get-attributions.js +7 -13
- package/test/mocha/get-board-alights.js +7 -13
- package/test/mocha/get-calendar-dates.js +11 -17
- package/test/mocha/get-calendars.js +11 -17
- package/test/mocha/get-directions.js +7 -13
- package/test/mocha/get-fare-attributes.js +9 -15
- package/test/mocha/get-fare-leg-rules.js +27 -0
- package/test/mocha/get-fare-products.js +27 -0
- package/test/mocha/get-fare-rules.js +9 -15
- package/test/mocha/get-feed-info.js +7 -13
- package/test/mocha/get-frequencies.js +7 -13
- package/test/mocha/get-levels.js +7 -7
- package/test/mocha/get-pathways.js +7 -13
- package/test/mocha/get-ride-feed-infos.js +7 -13
- package/test/mocha/get-rider-trips.js +7 -13
- package/test/mocha/get-riderships.js +7 -13
- package/test/mocha/get-routes.js +13 -13
- package/test/mocha/get-shapes-as-geojson.js +14 -15
- package/test/mocha/get-shapes.js +25 -25
- package/test/mocha/get-stop-attributes.js +7 -13
- package/test/mocha/get-stops-as-geojson.js +13 -19
- package/test/mocha/get-stops.js +19 -19
- package/test/mocha/get-stoptimes.js +11 -17
- package/test/mocha/get-timetable-pages.js +7 -13
- package/test/mocha/get-timetable-stop-orders.js +6 -7
- package/test/mocha/get-timetables.js +7 -13
- package/test/mocha/get-transfers.js +7 -13
- package/test/mocha/get-translations.js +7 -13
- package/test/mocha/get-trip-capacities.js +7 -13
- package/test/mocha/get-trips.js +9 -9
- package/test/mocha/import-gtfs.js +11 -11
- package/test/mocha/{get-db.js → open-db.js} +37 -29
- package/test/mocha/raw-query.js +34 -0
- package/test/mocha/exec-raw-query.js +0 -39
- package/test/mocha/run-raw-query.js +0 -54
package/lib/import.js
CHANGED
|
@@ -13,7 +13,7 @@ import gtfsrt from 'gtfs-realtime-bindings';
|
|
|
13
13
|
import sqlString from 'sqlstring-sqlite';
|
|
14
14
|
|
|
15
15
|
import models from '../models/models.js';
|
|
16
|
-
import { openDb
|
|
16
|
+
import { openDb } from './db.js';
|
|
17
17
|
import { unzip } from './file-utils.js';
|
|
18
18
|
import {
|
|
19
19
|
log as _log,
|
|
@@ -82,72 +82,33 @@ function getDescendantProp(obj, desc, defaultvalue) {
|
|
|
82
82
|
return obj;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
const markRealtimeDataStale =
|
|
86
|
-
const
|
|
87
|
-
(x) => x.filenameBase === 'vehicle_positions'
|
|
88
|
-
);
|
|
89
|
-
const tripUpdatesModel = models.find(
|
|
90
|
-
(x) => x.filenameBase === 'trip_updates'
|
|
91
|
-
);
|
|
92
|
-
const stopTimesUpdatesModel = models.find(
|
|
93
|
-
(x) => x.filenameBase === 'stop_times_updates'
|
|
94
|
-
);
|
|
95
|
-
const serviceAlertsModel = models.find(
|
|
96
|
-
(x) => x.filenameBase === 'service_alerts'
|
|
97
|
-
);
|
|
98
|
-
const serviceAlertTargetsModel = models.find(
|
|
99
|
-
(x) => x.filenameBase === 'service_alert_targets'
|
|
100
|
-
);
|
|
85
|
+
const markRealtimeDataStale = (config, log) => {
|
|
86
|
+
const db = openDb(config);
|
|
101
87
|
|
|
102
|
-
// Mark all data as stale
|
|
103
88
|
log(`Marking GTFS-Realtime data as stale..`);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
`UPDATE ${serviceAlertTargetsModel.filenameBase} SET isUpdated=0`
|
|
110
|
-
);
|
|
89
|
+
db.prepare(`UPDATE vehicle_positions SET isUpdated=0`).run();
|
|
90
|
+
db.prepare(`UPDATE trip_updates SET isUpdated=0`).run();
|
|
91
|
+
db.prepare(`UPDATE stop_times_updates SET isUpdated=0`).run();
|
|
92
|
+
db.prepare(`UPDATE service_alerts SET isUpdated=0`).run();
|
|
93
|
+
db.prepare(`UPDATE service_alert_targets SET isUpdated=0`).run();
|
|
111
94
|
log(`Marked GTFS-Realtime data as stale\r`, true);
|
|
112
95
|
};
|
|
113
96
|
|
|
114
|
-
const cleanStaleRealtimeData =
|
|
115
|
-
const
|
|
116
|
-
(x) => x.filenameBase === 'vehicle_positions'
|
|
117
|
-
);
|
|
118
|
-
const tripUpdatesModel = models.find(
|
|
119
|
-
(x) => x.filenameBase === 'trip_updates'
|
|
120
|
-
);
|
|
121
|
-
const stopTimesUpdatesModel = models.find(
|
|
122
|
-
(x) => x.filenameBase === 'stop_times_updates'
|
|
123
|
-
);
|
|
124
|
-
const serviceAlertsModel = models.find(
|
|
125
|
-
(x) => x.filenameBase === 'service_alerts'
|
|
126
|
-
);
|
|
127
|
-
const serviceAlertTargetsModel = models.find(
|
|
128
|
-
(x) => x.filenameBase === 'service_alert_targets'
|
|
129
|
-
);
|
|
97
|
+
const cleanStaleRealtimeData = (config, log) => {
|
|
98
|
+
const db = openDb(config);
|
|
130
99
|
|
|
131
100
|
log(`Cleaning stale GTFS-RT data..`);
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
);
|
|
138
|
-
await db.run(
|
|
139
|
-
`DELETE FROM ${stopTimesUpdatesModel.filenameBase} WHERE isUpdated=0`
|
|
140
|
-
);
|
|
141
|
-
await db.run(
|
|
142
|
-
`DELETE FROM ${serviceAlertsModel.filenameBase} WHERE isUpdated=0`
|
|
143
|
-
);
|
|
144
|
-
await db.run(
|
|
145
|
-
`DELETE FROM ${serviceAlertTargetsModel.filenameBase} WHERE isUpdated=0`
|
|
146
|
-
);
|
|
101
|
+
db.prepare(`DELETE FROM vehicle_positions WHERE isUpdated=0`).run();
|
|
102
|
+
db.prepare(`DELETE FROM trip_updates WHERE isUpdated=0`).run();
|
|
103
|
+
db.prepare(`DELETE FROM stop_times_updates WHERE isUpdated=0`).run();
|
|
104
|
+
db.prepare(`DELETE FROM service_alerts WHERE isUpdated=0`).run();
|
|
105
|
+
db.prepare(`DELETE FROM service_alert_targets WHERE isUpdated=0`).run();
|
|
147
106
|
log(`Cleaned stale GTFS-Realtime data\r`, true);
|
|
148
107
|
};
|
|
149
108
|
|
|
150
109
|
const updateRealtimeData = async (task) => {
|
|
110
|
+
const db = openDb(task);
|
|
111
|
+
|
|
151
112
|
const model = {
|
|
152
113
|
vehicle_positions: models.find(
|
|
153
114
|
(x) => x.filenameBase === 'vehicle_positions'
|
|
@@ -220,16 +181,15 @@ const updateRealtimeData = async (task) => {
|
|
|
220
181
|
)
|
|
221
182
|
);
|
|
222
183
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
.run(
|
|
184
|
+
try {
|
|
185
|
+
db.prepare(
|
|
226
186
|
`REPLACE INTO ${model[gtfsRealtimeType].filenameBase} (${
|
|
227
187
|
fields[gtfsRealtimeType]
|
|
228
188
|
}) VALUES (${fieldValues.join(', ')})`
|
|
229
|
-
)
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
189
|
+
).run();
|
|
190
|
+
} catch (error) {
|
|
191
|
+
task.warn('Import error: ' + error.message);
|
|
192
|
+
}
|
|
233
193
|
|
|
234
194
|
// Special processing for tripUpdates
|
|
235
195
|
if (entity.tripUpdate) {
|
|
@@ -245,16 +205,15 @@ const updateRealtimeData = async (task) => {
|
|
|
245
205
|
totalLineCount++;
|
|
246
206
|
}
|
|
247
207
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
.run(
|
|
208
|
+
try {
|
|
209
|
+
db.prepare(
|
|
251
210
|
`REPLACE INTO ${model.stop_times_updates.filenameBase} (${
|
|
252
211
|
fields.stop_times_updates
|
|
253
212
|
}) VALUES ${stopUpdateArray.join(', ')}`
|
|
254
|
-
)
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
213
|
+
).run();
|
|
214
|
+
} catch (error) {
|
|
215
|
+
task.warn('Import error: ' + error.message);
|
|
216
|
+
}
|
|
258
217
|
}
|
|
259
218
|
|
|
260
219
|
// Special processing for serviceAlerts
|
|
@@ -271,16 +230,15 @@ const updateRealtimeData = async (task) => {
|
|
|
271
230
|
totalLineCount++;
|
|
272
231
|
}
|
|
273
232
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
.run(
|
|
233
|
+
try {
|
|
234
|
+
db.prepare(
|
|
277
235
|
`REPLACE INTO ${model.service_alert_targets.filenameBase} (${
|
|
278
236
|
fields.service_alert_targets
|
|
279
237
|
}) VALUES ${alertTargetArray.join(', ')}`
|
|
280
|
-
)
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
238
|
+
).run();
|
|
239
|
+
} catch (error) {
|
|
240
|
+
task.warn('Import error: ' + error.message);
|
|
241
|
+
}
|
|
284
242
|
}
|
|
285
243
|
|
|
286
244
|
task.log(`Importing - ${totalLineCount++} entries imported\r`, true);
|
|
@@ -355,159 +313,147 @@ const readFiles = async (task) => {
|
|
|
355
313
|
}
|
|
356
314
|
};
|
|
357
315
|
|
|
358
|
-
const
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
316
|
+
const createTables = (db) => {
|
|
317
|
+
for (const model of models) {
|
|
318
|
+
if (!model.schema) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
362
321
|
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
322
|
+
const columns = model.schema.map((column) => {
|
|
323
|
+
let check = '';
|
|
324
|
+
if (column.min !== undefined && column.max) {
|
|
325
|
+
check = `CHECK( ${column.name} >= ${column.min} AND ${column.name} <= ${column.max} )`;
|
|
326
|
+
} else if (column.min) {
|
|
327
|
+
check = `CHECK( ${column.name} >= ${column.min} )`;
|
|
328
|
+
} else if (column.max) {
|
|
329
|
+
check = `CHECK( ${column.name} <= ${column.max} )`;
|
|
368
330
|
}
|
|
369
331
|
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
} else if (column.max) {
|
|
377
|
-
check = `CHECK( ${column.name} <= ${column.max} )`;
|
|
378
|
-
}
|
|
332
|
+
const primary = column.primary ? 'PRIMARY KEY' : '';
|
|
333
|
+
const required = column.required ? 'NOT NULL' : '';
|
|
334
|
+
const columnDefault = column.default ? 'DEFAULT ' + column.default : '';
|
|
335
|
+
const columnCollation = column.nocase ? 'COLLATE NOCASE' : '';
|
|
336
|
+
return `${column.name} ${column.type} ${check} ${primary} ${required} ${columnDefault} ${columnCollation}`;
|
|
337
|
+
});
|
|
379
338
|
|
|
380
|
-
|
|
381
|
-
const required = column.required ? 'NOT NULL' : '';
|
|
382
|
-
const columnDefault = column.default ? 'DEFAULT ' + column.default : '';
|
|
383
|
-
const columnCollation = column.nocase ? 'COLLATE NOCASE' : '';
|
|
384
|
-
return `${column.name} ${column.type} ${check} ${primary} ${required} ${columnDefault} ${columnCollation}`;
|
|
385
|
-
});
|
|
339
|
+
db.prepare(`DROP TABLE IF EXISTS ${model.filenameBase};`).run();
|
|
386
340
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
341
|
+
db.prepare(
|
|
342
|
+
`CREATE TABLE ${model.filenameBase} (${columns.join(', ')});`
|
|
343
|
+
).run();
|
|
390
344
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
})
|
|
400
|
-
);
|
|
401
|
-
})
|
|
402
|
-
);
|
|
345
|
+
for (const column of model.schema.filter((column) => column.index)) {
|
|
346
|
+
const unique = column.index === 'unique' ? 'UNIQUE' : '';
|
|
347
|
+
db.prepare(
|
|
348
|
+
`CREATE ${unique} INDEX idx_${model.filenameBase}_${column.name} ON ${model.filenameBase} (${column.name});`
|
|
349
|
+
).run();
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
};
|
|
403
353
|
|
|
404
354
|
const formatLine = (line, model, totalLineCount) => {
|
|
405
355
|
const lineNumber = totalLineCount + 1;
|
|
406
|
-
for (const fieldName of Object.keys(line)) {
|
|
407
|
-
const columnSchema = model.schema.find(
|
|
408
|
-
(schema) => schema.name === fieldName
|
|
409
|
-
);
|
|
410
356
|
|
|
411
|
-
|
|
412
|
-
if (!columnSchema) {
|
|
413
|
-
delete line[fieldName];
|
|
414
|
-
continue;
|
|
415
|
-
}
|
|
357
|
+
const formattedLine = {};
|
|
416
358
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
delete line[fieldName];
|
|
420
|
-
}
|
|
359
|
+
for (const columnSchema of model.schema) {
|
|
360
|
+
const lineValue = line[columnSchema.name];
|
|
421
361
|
|
|
422
|
-
// Convert fields that should be integer
|
|
423
362
|
if (columnSchema.type === 'integer') {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
363
|
+
// Convert fields that should be integer
|
|
364
|
+
formattedLine[columnSchema.name] = Number.parseInt(lineValue, 10);
|
|
365
|
+
} else if (columnSchema.type === 'real') {
|
|
366
|
+
// Convert fields that should be float
|
|
367
|
+
formattedLine[columnSchema.name] = Number.parseFloat(lineValue);
|
|
368
|
+
} else {
|
|
369
|
+
formattedLine[columnSchema.name] = lineValue;
|
|
431
370
|
}
|
|
432
371
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
}
|
|
372
|
+
if (
|
|
373
|
+
formattedLine[columnSchema.name] === '' ||
|
|
374
|
+
formattedLine[columnSchema.name] === undefined ||
|
|
375
|
+
formattedLine[columnSchema.name] === null ||
|
|
376
|
+
Number.isNaN(formattedLine[columnSchema.name])
|
|
377
|
+
) {
|
|
378
|
+
// Add null values
|
|
379
|
+
formattedLine[columnSchema.name] = null;
|
|
442
380
|
}
|
|
443
381
|
|
|
444
382
|
// Validate required
|
|
445
383
|
if (
|
|
446
384
|
columnSchema.required === true &&
|
|
447
|
-
|
|
385
|
+
formattedLine[columnSchema.name] === null
|
|
448
386
|
) {
|
|
449
387
|
throw new Error(
|
|
450
|
-
`Missing required value in ${model.filenameBase}.txt for ${
|
|
388
|
+
`Missing required value in ${model.filenameBase}.txt for ${columnSchema.name} on line ${lineNumber}.`
|
|
451
389
|
);
|
|
452
390
|
}
|
|
453
391
|
|
|
454
392
|
// Validate minimum
|
|
455
|
-
if (
|
|
393
|
+
if (
|
|
394
|
+
columnSchema.min !== undefined &&
|
|
395
|
+
formattedLine[columnSchema.name] < columnSchema.min
|
|
396
|
+
) {
|
|
456
397
|
throw new Error(
|
|
457
|
-
`Invalid value in ${model.filenameBase}.txt for ${
|
|
398
|
+
`Invalid value in ${model.filenameBase}.txt for ${columnSchema.name} on line ${lineNumber}: below minimum value of ${columnSchema.min}.`
|
|
458
399
|
);
|
|
459
400
|
}
|
|
460
401
|
|
|
461
402
|
// Validate maximum
|
|
462
|
-
if (
|
|
403
|
+
if (
|
|
404
|
+
columnSchema.max !== undefined &&
|
|
405
|
+
formattedLine[columnSchema.name] > columnSchema.max
|
|
406
|
+
) {
|
|
463
407
|
throw new Error(
|
|
464
|
-
`Invalid value in ${model.filenameBase}.txt for ${
|
|
408
|
+
`Invalid value in ${model.filenameBase}.txt for ${columnSchema.name} on line ${lineNumber}: above maximum value of ${columnSchema.max}.`
|
|
465
409
|
);
|
|
466
410
|
}
|
|
467
411
|
}
|
|
468
412
|
|
|
469
413
|
// Convert to midnight timestamp
|
|
470
|
-
const
|
|
414
|
+
const timestampColumnNames = [
|
|
471
415
|
'start_time',
|
|
472
416
|
'end_time',
|
|
473
417
|
'arrival_time',
|
|
474
418
|
'departure_time',
|
|
475
419
|
];
|
|
476
420
|
|
|
477
|
-
for (const
|
|
478
|
-
if (
|
|
479
|
-
|
|
421
|
+
for (const timestampColumnName of timestampColumnNames) {
|
|
422
|
+
if (formattedLine[timestampColumnName]) {
|
|
423
|
+
formattedLine[`${timestampColumnName}stamp`] =
|
|
424
|
+
calculateSecondsFromMidnight(formattedLine[timestampColumnName]);
|
|
480
425
|
}
|
|
481
426
|
}
|
|
482
427
|
|
|
483
|
-
return
|
|
428
|
+
return formattedLine;
|
|
484
429
|
};
|
|
485
430
|
|
|
486
|
-
const importLines =
|
|
431
|
+
const importLines = (task, lines, model, totalLineCount) => {
|
|
432
|
+
const db = openDb(task);
|
|
433
|
+
|
|
487
434
|
if (lines.length === 0) {
|
|
488
435
|
return;
|
|
489
436
|
}
|
|
490
437
|
|
|
491
438
|
const linesToImportCount = lines.length;
|
|
492
|
-
const fieldNames = model.schema
|
|
439
|
+
const fieldNames = model.schema
|
|
440
|
+
.filter((column) => column.name !== 'id')
|
|
441
|
+
.map((column) => column.name);
|
|
493
442
|
const placeholders = [];
|
|
494
443
|
const values = [];
|
|
495
444
|
|
|
496
445
|
while (lines.length > 0) {
|
|
497
446
|
const line = lines.pop();
|
|
498
447
|
placeholders.push(`(${fieldNames.map(() => '?').join(', ')})`);
|
|
499
|
-
|
|
500
|
-
values.push(line[fieldName]);
|
|
501
|
-
}
|
|
448
|
+
values.push(...fieldNames.map((fieldName) => line[fieldName]));
|
|
502
449
|
}
|
|
503
450
|
|
|
504
451
|
try {
|
|
505
|
-
|
|
452
|
+
db.prepare(
|
|
506
453
|
`INSERT ${task.ignoreDuplicates ? 'OR IGNORE' : ''} INTO ${
|
|
507
454
|
model.filenameBase
|
|
508
|
-
}(${fieldNames.join(', ')}) VALUES${placeholders.join(',')}
|
|
509
|
-
|
|
510
|
-
);
|
|
455
|
+
} (${fieldNames.join(', ')}) VALUES ${placeholders.join(',')}`
|
|
456
|
+
).run(...values);
|
|
511
457
|
} catch (error) {
|
|
512
458
|
task.warn(
|
|
513
459
|
`Check ${model.filenameBase}.txt for invalid data between lines ${
|
|
@@ -536,7 +482,7 @@ const importFiles = (task) =>
|
|
|
536
482
|
return;
|
|
537
483
|
}
|
|
538
484
|
|
|
539
|
-
// If the model is a database/gtfs-realtime model then
|
|
485
|
+
// If the model is a database/gtfs-realtime model then silently exit
|
|
540
486
|
if (model.extension === 'gtfs-realtime') {
|
|
541
487
|
resolve();
|
|
542
488
|
return;
|
|
@@ -569,18 +515,16 @@ const importFiles = (task) =>
|
|
|
569
515
|
...task.csvOptions,
|
|
570
516
|
});
|
|
571
517
|
|
|
572
|
-
parser.on('readable',
|
|
518
|
+
parser.on('readable', () => {
|
|
573
519
|
let record;
|
|
574
520
|
|
|
575
521
|
while ((record = parser.read())) {
|
|
576
522
|
try {
|
|
577
523
|
totalLineCount += 1;
|
|
578
524
|
lines.push(formatLine(record, model, totalLineCount));
|
|
579
|
-
|
|
580
525
|
// If we have a bunch of lines ready to insert, then do it
|
|
581
526
|
if (lines.length >= maxInsertVariables / model.schema.length) {
|
|
582
|
-
|
|
583
|
-
await importLines(task, lines, model, totalLineCount);
|
|
527
|
+
importLines(task, lines, model, totalLineCount);
|
|
584
528
|
}
|
|
585
529
|
} catch (error) {
|
|
586
530
|
reject(error);
|
|
@@ -588,9 +532,9 @@ const importFiles = (task) =>
|
|
|
588
532
|
}
|
|
589
533
|
});
|
|
590
534
|
|
|
591
|
-
parser.on('end',
|
|
535
|
+
parser.on('end', () => {
|
|
592
536
|
// Insert all remaining lines
|
|
593
|
-
|
|
537
|
+
importLines(task, lines, model, totalLineCount);
|
|
594
538
|
resolve();
|
|
595
539
|
});
|
|
596
540
|
|
|
@@ -606,70 +550,65 @@ export async function importGtfs(initialConfig) {
|
|
|
606
550
|
const log = _log(config);
|
|
607
551
|
const logError = _logError(config);
|
|
608
552
|
const logWarning = _logWarning(config);
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
553
|
+
try {
|
|
554
|
+
const db = openDb(config);
|
|
555
|
+
|
|
556
|
+
const agencyCount = config.agencies.length;
|
|
557
|
+
log(
|
|
558
|
+
`Starting GTFS import for ${pluralize(
|
|
559
|
+
'file',
|
|
560
|
+
agencyCount,
|
|
561
|
+
true
|
|
562
|
+
)} using SQLite database at ${config.sqlitePath}`
|
|
563
|
+
);
|
|
615
564
|
|
|
616
|
-
|
|
617
|
-
|
|
565
|
+
createTables(db);
|
|
566
|
+
|
|
567
|
+
await mapSeries(config.agencies, async (agency) => {
|
|
568
|
+
const { path, cleanup } = await dir({ unsafeCleanup: true });
|
|
569
|
+
|
|
570
|
+
const task = {
|
|
571
|
+
exclude: agency.exclude,
|
|
572
|
+
agency_url: agency.url,
|
|
573
|
+
headers: agency.headers || false,
|
|
574
|
+
realtime_headers: agency.realtimeHeaders || false,
|
|
575
|
+
realtime_urls: agency.realtimeUrls || false,
|
|
576
|
+
downloadDir: path,
|
|
577
|
+
path: agency.path,
|
|
578
|
+
csvOptions: config.csvOptions || {},
|
|
579
|
+
ignoreDuplicates: config.ignoreDuplicates,
|
|
580
|
+
sqlitePath: config.sqlitePath,
|
|
581
|
+
log,
|
|
582
|
+
warn: logWarning,
|
|
583
|
+
error: logError,
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
if (task.agency_url) {
|
|
587
|
+
await downloadFiles(task);
|
|
588
|
+
}
|
|
618
589
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
`Starting GTFS import for ${pluralize(
|
|
622
|
-
'file',
|
|
623
|
-
agencyCount,
|
|
624
|
-
true
|
|
625
|
-
)} using SQLite database at ${config.sqlitePath}`
|
|
626
|
-
);
|
|
590
|
+
await readFiles(task);
|
|
591
|
+
await importFiles(task);
|
|
627
592
|
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
await setupDb(db);
|
|
632
|
-
|
|
633
|
-
await mapSeries(config.agencies, async (agency) => {
|
|
634
|
-
const { path, cleanup } = await dir({ unsafeCleanup: true });
|
|
635
|
-
|
|
636
|
-
const task = {
|
|
637
|
-
exclude: agency.exclude,
|
|
638
|
-
agency_url: agency.url,
|
|
639
|
-
headers: agency.headers || false,
|
|
640
|
-
realtime_headers: agency.realtimeHeaders || false,
|
|
641
|
-
realtime_urls: agency.realtimeUrls || false,
|
|
642
|
-
downloadDir: path,
|
|
643
|
-
path: agency.path,
|
|
644
|
-
csvOptions: config.csvOptions || {},
|
|
645
|
-
ignoreDuplicates: config.ignoreDuplicates,
|
|
646
|
-
db,
|
|
647
|
-
log(message, overwrite) {
|
|
648
|
-
log(message, overwrite);
|
|
649
|
-
},
|
|
650
|
-
warn(message) {
|
|
651
|
-
logWarning(message);
|
|
652
|
-
},
|
|
653
|
-
error(message) {
|
|
654
|
-
logError(message);
|
|
655
|
-
},
|
|
656
|
-
};
|
|
657
|
-
|
|
658
|
-
if (task.agency_url) {
|
|
659
|
-
await downloadFiles(task);
|
|
660
|
-
}
|
|
593
|
+
if (task.realtime_urls) {
|
|
594
|
+
await updateRealtimeData(task);
|
|
595
|
+
}
|
|
661
596
|
|
|
662
|
-
|
|
663
|
-
|
|
597
|
+
cleanup();
|
|
598
|
+
});
|
|
664
599
|
|
|
665
|
-
|
|
666
|
-
|
|
600
|
+
log(
|
|
601
|
+
`Completed GTFS import for ${pluralize('agency', agencyCount, true)}\n`
|
|
602
|
+
);
|
|
603
|
+
} catch (error) {
|
|
604
|
+
if (error instanceof Error && error.code === 'SQLITE_CANTOPEN') {
|
|
605
|
+
logError(
|
|
606
|
+
`Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
|
|
607
|
+
);
|
|
667
608
|
}
|
|
668
609
|
|
|
669
|
-
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
log(`Completed GTFS import for ${pluralize('agency', agencyCount, true)}\n`);
|
|
610
|
+
throw error;
|
|
611
|
+
}
|
|
673
612
|
}
|
|
674
613
|
|
|
675
614
|
export async function updateGtfsRealtime(initialConfig) {
|
|
@@ -678,7 +617,49 @@ export async function updateGtfsRealtime(initialConfig) {
|
|
|
678
617
|
const log = _log(config);
|
|
679
618
|
const logError = _logError(config);
|
|
680
619
|
const logWarning = _logWarning(config);
|
|
681
|
-
|
|
620
|
+
|
|
621
|
+
try {
|
|
622
|
+
openDb(config);
|
|
623
|
+
|
|
624
|
+
const agencyCount = config.agencies.length;
|
|
625
|
+
log(
|
|
626
|
+
`Starting GTFS-Realtime refresh for ${pluralize(
|
|
627
|
+
'agencies',
|
|
628
|
+
agencyCount,
|
|
629
|
+
true
|
|
630
|
+
)} using SQLite database at ${config.sqlitePath}`
|
|
631
|
+
);
|
|
632
|
+
|
|
633
|
+
markRealtimeDataStale(config, log);
|
|
634
|
+
|
|
635
|
+
await Promise.all(
|
|
636
|
+
config.agencies.map(async (agency) => {
|
|
637
|
+
if (!agency.realtimeUrls) {
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
const task = {
|
|
642
|
+
realtime_headers: agency.realtimeHeaders || false,
|
|
643
|
+
realtime_urls: agency.realtimeUrls || false,
|
|
644
|
+
sqlitePath: config.sqlitePath,
|
|
645
|
+
log,
|
|
646
|
+
warn: logWarning,
|
|
647
|
+
error: logError,
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
await updateRealtimeData(task);
|
|
651
|
+
})
|
|
652
|
+
);
|
|
653
|
+
|
|
654
|
+
cleanStaleRealtimeData(config, log);
|
|
655
|
+
log(
|
|
656
|
+
`Completed GTFS-Realtime refresh for ${pluralize(
|
|
657
|
+
'agencies',
|
|
658
|
+
agencyCount,
|
|
659
|
+
true
|
|
660
|
+
)}\n`
|
|
661
|
+
);
|
|
662
|
+
} catch (error) {
|
|
682
663
|
if (error instanceof Error && error.code === 'SQLITE_CANTOPEN') {
|
|
683
664
|
logError(
|
|
684
665
|
`Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
|
|
@@ -686,46 +667,5 @@ export async function updateGtfsRealtime(initialConfig) {
|
|
|
686
667
|
}
|
|
687
668
|
|
|
688
669
|
throw error;
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
const agencyCount = config.agencies.length;
|
|
692
|
-
log(
|
|
693
|
-
`Starting GTFS-Realtime refresh for ${pluralize(
|
|
694
|
-
'agencies',
|
|
695
|
-
agencyCount,
|
|
696
|
-
true
|
|
697
|
-
)} using SQLite database at ${config.sqlitePath}`
|
|
698
|
-
);
|
|
699
|
-
|
|
700
|
-
await markRealtimeDataStale(db, log);
|
|
701
|
-
|
|
702
|
-
await mapSeries(config.agencies, async (agency) => {
|
|
703
|
-
const task = {
|
|
704
|
-
realtime_headers: agency.realtimeHeaders || false,
|
|
705
|
-
realtime_urls: agency.realtimeUrls || false,
|
|
706
|
-
db,
|
|
707
|
-
log(message, overwrite) {
|
|
708
|
-
log(message, overwrite);
|
|
709
|
-
},
|
|
710
|
-
warn(message) {
|
|
711
|
-
logWarning(message);
|
|
712
|
-
},
|
|
713
|
-
error(message) {
|
|
714
|
-
logError(message);
|
|
715
|
-
},
|
|
716
|
-
};
|
|
717
|
-
|
|
718
|
-
if (task.realtime_urls) {
|
|
719
|
-
await updateRealtimeData(task);
|
|
720
|
-
}
|
|
721
|
-
});
|
|
722
|
-
|
|
723
|
-
await cleanStaleRealtimeData(db, log);
|
|
724
|
-
log(
|
|
725
|
-
`Completed GTFS-Realtime refresh for ${pluralize(
|
|
726
|
-
'agencies',
|
|
727
|
-
agencyCount,
|
|
728
|
-
true
|
|
729
|
-
)}\n`
|
|
730
|
-
);
|
|
670
|
+
}
|
|
731
671
|
}
|