gtfs 4.14.5 → 4.15.0
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/dist/bin/gtfs-export.js +29 -10
- package/dist/bin/gtfs-export.js.map +1 -1
- package/dist/bin/gtfs-import.js +293 -277
- package/dist/bin/gtfs-import.js.map +1 -1
- package/dist/bin/gtfsrealtime-update.js +171 -145
- package/dist/bin/gtfsrealtime-update.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +356 -337
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
package/dist/index.js
CHANGED
|
@@ -4,18 +4,16 @@ var __export = (target, all) => {
|
|
|
4
4
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
5
|
};
|
|
6
6
|
|
|
7
|
-
// src/lib/import.ts
|
|
7
|
+
// src/lib/import-gtfs.ts
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import { createReadStream, existsSync, lstatSync } from "node:fs";
|
|
10
10
|
import { cp, readdir, rename, readFile as readFile2, rm as rm2, writeFile } from "node:fs/promises";
|
|
11
11
|
import { parse } from "csv-parse";
|
|
12
|
-
import
|
|
12
|
+
import pluralize2 from "pluralize";
|
|
13
13
|
import stripBomStream from "strip-bom-stream";
|
|
14
14
|
import { temporaryDirectory } from "tempy";
|
|
15
15
|
import untildify3 from "untildify";
|
|
16
|
-
import
|
|
17
|
-
import GtfsRealtimeBindings from "gtfs-realtime-bindings";
|
|
18
|
-
import sqlString2 from "sqlstring-sqlite";
|
|
16
|
+
import mapSeries2 from "promise-map-series";
|
|
19
17
|
|
|
20
18
|
// src/models/models.ts
|
|
21
19
|
var models_exports = {};
|
|
@@ -3309,6 +3307,12 @@ function stopsToGeoJSONFeatureCollection(stops2) {
|
|
|
3309
3307
|
return featureCollection(features);
|
|
3310
3308
|
}
|
|
3311
3309
|
|
|
3310
|
+
// src/lib/import-gtfs-realtime.ts
|
|
3311
|
+
import pluralize from "pluralize";
|
|
3312
|
+
import GtfsRealtimeBindings from "gtfs-realtime-bindings";
|
|
3313
|
+
import sqlString2 from "sqlstring-sqlite";
|
|
3314
|
+
import mapSeries from "promise-map-series";
|
|
3315
|
+
|
|
3312
3316
|
// src/lib/log-utils.ts
|
|
3313
3317
|
import { clearLine, cursorTo } from "node:readline";
|
|
3314
3318
|
import { noop } from "lodash-es";
|
|
@@ -3480,60 +3484,10 @@ function formatOrderByClause(orderBy2) {
|
|
|
3480
3484
|
return orderByClause;
|
|
3481
3485
|
}
|
|
3482
3486
|
|
|
3483
|
-
// src/lib/import.ts
|
|
3484
|
-
|
|
3485
|
-
if (
|
|
3486
|
-
|
|
3487
|
-
}
|
|
3488
|
-
task.log(`Downloading GTFS from ${task.url}`);
|
|
3489
|
-
task.path = `${task.downloadDir}/gtfs.zip`;
|
|
3490
|
-
const response = await fetch(task.url, {
|
|
3491
|
-
method: "GET",
|
|
3492
|
-
headers: task.headers || {},
|
|
3493
|
-
signal: task.downloadTimeout ? AbortSignal.timeout(task.downloadTimeout) : void 0
|
|
3494
|
-
});
|
|
3495
|
-
if (response.status !== 200) {
|
|
3496
|
-
throw new Error(
|
|
3497
|
-
`Unable to download GTFS from ${task.url}. Got status ${response.status}.`
|
|
3498
|
-
);
|
|
3499
|
-
}
|
|
3500
|
-
const buffer = await response.arrayBuffer();
|
|
3501
|
-
await writeFile(task.path, Buffer.from(buffer));
|
|
3502
|
-
task.log("Download successful");
|
|
3503
|
-
};
|
|
3504
|
-
var downloadGtfsRealtimeData = async (urlAndHeaders, task) => {
|
|
3505
|
-
task.log(`Downloading GTFS-Realtime from ${urlAndHeaders.url}`);
|
|
3506
|
-
const response = await fetch(urlAndHeaders.url, {
|
|
3507
|
-
method: "GET",
|
|
3508
|
-
headers: {
|
|
3509
|
-
...urlAndHeaders.headers ?? {},
|
|
3510
|
-
"Accept-Encoding": "gzip"
|
|
3511
|
-
},
|
|
3512
|
-
signal: task.downloadTimeout ? AbortSignal.timeout(task.downloadTimeout) : void 0
|
|
3513
|
-
});
|
|
3514
|
-
if (response.status !== 200) {
|
|
3515
|
-
task.logWarning(
|
|
3516
|
-
`Unable to download GTFS-Realtime from ${urlAndHeaders.url}. Got status ${response.status}.`
|
|
3517
|
-
);
|
|
3518
|
-
return null;
|
|
3519
|
-
}
|
|
3520
|
-
const buffer = await response.arrayBuffer();
|
|
3521
|
-
const message = GtfsRealtimeBindings.transit_realtime.FeedMessage.decode(
|
|
3522
|
-
new Uint8Array(buffer)
|
|
3523
|
-
);
|
|
3524
|
-
return GtfsRealtimeBindings.transit_realtime.FeedMessage.toObject(message, {
|
|
3525
|
-
enums: String,
|
|
3526
|
-
longs: String,
|
|
3527
|
-
bytes: String,
|
|
3528
|
-
defaults: true,
|
|
3529
|
-
arrays: true,
|
|
3530
|
-
objects: true,
|
|
3531
|
-
oneofs: true
|
|
3532
|
-
});
|
|
3533
|
-
};
|
|
3534
|
-
function getDescendantProp(obj, defaultValue, source) {
|
|
3535
|
-
if (source === void 0) return defaultValue;
|
|
3536
|
-
const arr = source.split(".");
|
|
3487
|
+
// src/lib/import-gtfs-realtime.ts
|
|
3488
|
+
function getNestedProperty(obj, defaultValue, path3) {
|
|
3489
|
+
if (path3 === void 0) return defaultValue;
|
|
3490
|
+
const arr = path3.split(".");
|
|
3537
3491
|
while (arr.length) {
|
|
3538
3492
|
const nextKey = arr.shift();
|
|
3539
3493
|
if (nextKey === void 0) {
|
|
@@ -3562,7 +3516,37 @@ function getDescendantProp(obj, defaultValue, source) {
|
|
|
3562
3516
|
if (obj.__isLong__) return convertLongTimeToDate(obj);
|
|
3563
3517
|
return obj;
|
|
3564
3518
|
}
|
|
3565
|
-
|
|
3519
|
+
async function fetchGtfsRealtimeData(urlConfig, task) {
|
|
3520
|
+
task.log(`Downloading GTFS-Realtime from ${urlConfig.url}`);
|
|
3521
|
+
const response = await fetch(urlConfig.url, {
|
|
3522
|
+
method: "GET",
|
|
3523
|
+
headers: {
|
|
3524
|
+
...urlConfig.headers ?? {},
|
|
3525
|
+
"Accept-Encoding": "gzip"
|
|
3526
|
+
},
|
|
3527
|
+
signal: task.downloadTimeout ? AbortSignal.timeout(task.downloadTimeout) : void 0
|
|
3528
|
+
});
|
|
3529
|
+
if (response.status !== 200) {
|
|
3530
|
+
task.logWarning(
|
|
3531
|
+
`Unable to download GTFS-Realtime from ${urlConfig.url}. Got status ${response.status}.`
|
|
3532
|
+
);
|
|
3533
|
+
return null;
|
|
3534
|
+
}
|
|
3535
|
+
const buffer = await response.arrayBuffer();
|
|
3536
|
+
const message = GtfsRealtimeBindings.transit_realtime.FeedMessage.decode(
|
|
3537
|
+
new Uint8Array(buffer)
|
|
3538
|
+
);
|
|
3539
|
+
return GtfsRealtimeBindings.transit_realtime.FeedMessage.toObject(message, {
|
|
3540
|
+
enums: String,
|
|
3541
|
+
longs: String,
|
|
3542
|
+
bytes: String,
|
|
3543
|
+
defaults: true,
|
|
3544
|
+
arrays: true,
|
|
3545
|
+
objects: true,
|
|
3546
|
+
oneofs: true
|
|
3547
|
+
});
|
|
3548
|
+
}
|
|
3549
|
+
function removeExpiredRealtimeData(config) {
|
|
3566
3550
|
const log2 = log(config);
|
|
3567
3551
|
const db = openDb(config);
|
|
3568
3552
|
log2(`Removing expired GTFS-Realtime data`);
|
|
@@ -3582,8 +3566,8 @@ var deleteExpiredRealtimeData = (config) => {
|
|
|
3582
3566
|
`DELETE FROM service_alert_targets WHERE expiration_timestamp <= strftime('%s','now')`
|
|
3583
3567
|
).run();
|
|
3584
3568
|
log2(`Removed expired GTFS-Realtime data\r`, true);
|
|
3585
|
-
}
|
|
3586
|
-
|
|
3569
|
+
}
|
|
3570
|
+
function prepareRealtimeFieldValue(entity, column, task) {
|
|
3587
3571
|
if (column.name === "created_timestamp") {
|
|
3588
3572
|
return task.currentTimestamp;
|
|
3589
3573
|
}
|
|
@@ -3591,54 +3575,102 @@ var prepareRealtimeValue = (entity, column, task) => {
|
|
|
3591
3575
|
return task.currentTimestamp + task.gtfsRealtimeExpirationSeconds;
|
|
3592
3576
|
}
|
|
3593
3577
|
return sqlString2.escape(
|
|
3594
|
-
|
|
3578
|
+
getNestedProperty(entity, column.default, column.source)
|
|
3595
3579
|
);
|
|
3596
|
-
}
|
|
3597
|
-
|
|
3580
|
+
}
|
|
3581
|
+
async function processRealtimeAlerts(db, gtfsRealtimeData, task) {
|
|
3582
|
+
task.log(`Download successful`);
|
|
3583
|
+
let totalLineCount = 0;
|
|
3584
|
+
for (const entity of gtfsRealtimeData.entity) {
|
|
3585
|
+
const fieldValues = serviceAlerts.schema.map(
|
|
3586
|
+
(column) => prepareRealtimeFieldValue(entity, column, task)
|
|
3587
|
+
);
|
|
3588
|
+
try {
|
|
3589
|
+
db.prepare(
|
|
3590
|
+
`REPLACE INTO ${serviceAlerts.filenameBase} (${serviceAlerts.schema.map((column) => column.name).join(", ")}) VALUES (${fieldValues.join(", ")})`
|
|
3591
|
+
).run();
|
|
3592
|
+
} catch (error) {
|
|
3593
|
+
task.logWarning("Import error: " + error.message);
|
|
3594
|
+
}
|
|
3595
|
+
const alertTargetArray = [];
|
|
3596
|
+
for (const informedEntity of entity.alert.informedEntity) {
|
|
3597
|
+
informedEntity.parent = entity;
|
|
3598
|
+
const subValues = serviceAlertTargets.schema.map(
|
|
3599
|
+
(column) => prepareRealtimeFieldValue(informedEntity, column, task)
|
|
3600
|
+
);
|
|
3601
|
+
alertTargetArray.push(`(${subValues.join(", ")})`);
|
|
3602
|
+
totalLineCount++;
|
|
3603
|
+
}
|
|
3604
|
+
try {
|
|
3605
|
+
db.prepare(
|
|
3606
|
+
`REPLACE INTO ${serviceAlertTargets.filenameBase} (${serviceAlertTargets.schema.map((column) => column.name).join(", ")}) VALUES ${alertTargetArray.join(", ")}`
|
|
3607
|
+
).run();
|
|
3608
|
+
} catch (error) {
|
|
3609
|
+
task.logWarning("Import error: " + error.message);
|
|
3610
|
+
}
|
|
3611
|
+
task.log(`Importing - ${totalLineCount++} entries imported\r`, true);
|
|
3612
|
+
}
|
|
3613
|
+
}
|
|
3614
|
+
async function processRealtimeTripUpdates(db, gtfsRealtimeData, task) {
|
|
3615
|
+
task.log(`Download successful`);
|
|
3616
|
+
let totalLineCount = 0;
|
|
3617
|
+
for (const entity of gtfsRealtimeData.entity) {
|
|
3618
|
+
const fieldValues = tripUpdates.schema.map(
|
|
3619
|
+
(column) => prepareRealtimeFieldValue(entity, column, task)
|
|
3620
|
+
);
|
|
3621
|
+
try {
|
|
3622
|
+
db.prepare(
|
|
3623
|
+
`REPLACE INTO ${tripUpdates.filenameBase} (${tripUpdates.schema.map((column) => column.name).join(", ")}) VALUES (${fieldValues.join(", ")})`
|
|
3624
|
+
).run();
|
|
3625
|
+
} catch (error) {
|
|
3626
|
+
task.logWarning("Import error: " + error.message);
|
|
3627
|
+
}
|
|
3628
|
+
const stopTimeUpdateArray = [];
|
|
3629
|
+
for (const stopTimeUpdate of entity.tripUpdate.stopTimeUpdate) {
|
|
3630
|
+
stopTimeUpdate.parent = entity;
|
|
3631
|
+
const subValues = stopTimeUpdates.schema.map(
|
|
3632
|
+
(column) => prepareRealtimeFieldValue(stopTimeUpdate, column, task)
|
|
3633
|
+
);
|
|
3634
|
+
stopTimeUpdateArray.push(`(${subValues.join(", ")})`);
|
|
3635
|
+
totalLineCount++;
|
|
3636
|
+
}
|
|
3637
|
+
try {
|
|
3638
|
+
db.prepare(
|
|
3639
|
+
`REPLACE INTO ${stopTimeUpdates.filenameBase} (${stopTimeUpdates.schema.map((column) => column.name).join(", ")}) VALUES ${stopTimeUpdateArray.join(", ")}`
|
|
3640
|
+
).run();
|
|
3641
|
+
} catch (error) {
|
|
3642
|
+
task.logWarning("Import error: " + error.message);
|
|
3643
|
+
}
|
|
3644
|
+
task.log(`Importing - ${totalLineCount++} entries imported\r`, true);
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3647
|
+
async function processRealtimeVehiclePositions(db, gtfsRealtimeData, task) {
|
|
3648
|
+
task.log(`Download successful`);
|
|
3649
|
+
let totalLineCount = 0;
|
|
3650
|
+
for (const entity of gtfsRealtimeData.entity) {
|
|
3651
|
+
const fieldValues = vehiclePositions.schema.map(
|
|
3652
|
+
(column) => prepareRealtimeFieldValue(entity, column, task)
|
|
3653
|
+
);
|
|
3654
|
+
try {
|
|
3655
|
+
db.prepare(
|
|
3656
|
+
`REPLACE INTO ${vehiclePositions.filenameBase} (${vehiclePositions.schema.map((column) => column.name).join(", ")}) VALUES (${fieldValues.join(", ")})`
|
|
3657
|
+
).run();
|
|
3658
|
+
} catch (error) {
|
|
3659
|
+
task.logWarning("Import error: " + error.message);
|
|
3660
|
+
}
|
|
3661
|
+
task.log(`Importing - ${totalLineCount++} entries imported\r`, true);
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
async function updateGtfsRealtimeData(task) {
|
|
3598
3665
|
if (task.realtimeAlerts === void 0 && task.realtimeTripUpdates === void 0 && task.realtimeVehiclePositions === void 0) {
|
|
3599
3666
|
return;
|
|
3600
3667
|
}
|
|
3601
|
-
const db = openDb({
|
|
3602
|
-
sqlitePath: task.sqlitePath
|
|
3603
|
-
});
|
|
3668
|
+
const db = openDb({ sqlitePath: task.sqlitePath });
|
|
3604
3669
|
if (task.realtimeAlerts?.url) {
|
|
3605
3670
|
try {
|
|
3606
|
-
const
|
|
3607
|
-
|
|
3608
|
-
task
|
|
3609
|
-
);
|
|
3610
|
-
if (gtfsRealtimeData?.entity) {
|
|
3611
|
-
task.log(`Download successful`);
|
|
3612
|
-
let totalLineCount = 0;
|
|
3613
|
-
for (const entity of gtfsRealtimeData.entity) {
|
|
3614
|
-
const fieldValues = serviceAlerts.schema.map(
|
|
3615
|
-
(column) => prepareRealtimeValue(entity, column, task)
|
|
3616
|
-
);
|
|
3617
|
-
try {
|
|
3618
|
-
db.prepare(
|
|
3619
|
-
`REPLACE INTO ${serviceAlerts.filenameBase} (${serviceAlerts.schema.map((column) => column.name).join(", ")}) VALUES (${fieldValues.join(", ")})`
|
|
3620
|
-
).run();
|
|
3621
|
-
} catch (error) {
|
|
3622
|
-
task.logWarning("Import error: " + error.message);
|
|
3623
|
-
}
|
|
3624
|
-
const alertTargetArray = [];
|
|
3625
|
-
for (const informedEntity of entity.alert.informedEntity) {
|
|
3626
|
-
informedEntity.parent = entity;
|
|
3627
|
-
const subValues = serviceAlertTargets.schema.map(
|
|
3628
|
-
(column) => prepareRealtimeValue(informedEntity, column, task)
|
|
3629
|
-
);
|
|
3630
|
-
alertTargetArray.push(`(${subValues.join(", ")})`);
|
|
3631
|
-
totalLineCount++;
|
|
3632
|
-
}
|
|
3633
|
-
try {
|
|
3634
|
-
db.prepare(
|
|
3635
|
-
`REPLACE INTO ${serviceAlertTargets.filenameBase} (${serviceAlertTargets.schema.map((column) => column.name).join(", ")}) VALUES ${alertTargetArray.join(", ")}`
|
|
3636
|
-
).run();
|
|
3637
|
-
} catch (error) {
|
|
3638
|
-
task.logWarning("Import error: " + error.message);
|
|
3639
|
-
}
|
|
3640
|
-
task.log(`Importing - ${totalLineCount++} entries imported\r`, true);
|
|
3641
|
-
}
|
|
3671
|
+
const alertsData = await fetchGtfsRealtimeData(task.realtimeAlerts, task);
|
|
3672
|
+
if (alertsData?.entity) {
|
|
3673
|
+
await processRealtimeAlerts(db, alertsData, task);
|
|
3642
3674
|
}
|
|
3643
3675
|
} catch (error) {
|
|
3644
3676
|
if (task.ignoreErrors) {
|
|
@@ -3650,42 +3682,12 @@ var updateRealtimeData = async (task) => {
|
|
|
3650
3682
|
}
|
|
3651
3683
|
if (task.realtimeTripUpdates?.url) {
|
|
3652
3684
|
try {
|
|
3653
|
-
const
|
|
3685
|
+
const tripUpdatesData = await fetchGtfsRealtimeData(
|
|
3654
3686
|
task.realtimeTripUpdates,
|
|
3655
3687
|
task
|
|
3656
3688
|
);
|
|
3657
|
-
if (
|
|
3658
|
-
|
|
3659
|
-
let totalLineCount = 0;
|
|
3660
|
-
for (const entity of gtfsRealtimeData.entity) {
|
|
3661
|
-
const fieldValues = tripUpdates.schema.map(
|
|
3662
|
-
(column) => prepareRealtimeValue(entity, column, task)
|
|
3663
|
-
);
|
|
3664
|
-
try {
|
|
3665
|
-
db.prepare(
|
|
3666
|
-
`REPLACE INTO ${tripUpdates.filenameBase} (${tripUpdates.schema.map((column) => column.name).join(", ")}) VALUES (${fieldValues.join(", ")})`
|
|
3667
|
-
).run();
|
|
3668
|
-
} catch (error) {
|
|
3669
|
-
task.logWarning("Import error: " + error.message);
|
|
3670
|
-
}
|
|
3671
|
-
const stopTimeUpdateArray = [];
|
|
3672
|
-
for (const stopTimeUpdate of entity.tripUpdate.stopTimeUpdate) {
|
|
3673
|
-
stopTimeUpdate.parent = entity;
|
|
3674
|
-
const subValues = stopTimeUpdates.schema.map(
|
|
3675
|
-
(column) => prepareRealtimeValue(stopTimeUpdate, column, task)
|
|
3676
|
-
);
|
|
3677
|
-
stopTimeUpdateArray.push(`(${subValues.join(", ")})`);
|
|
3678
|
-
totalLineCount++;
|
|
3679
|
-
}
|
|
3680
|
-
try {
|
|
3681
|
-
db.prepare(
|
|
3682
|
-
`REPLACE INTO ${stopTimeUpdates.filenameBase} (${stopTimeUpdates.schema.map((column) => column.name).join(", ")}) VALUES ${stopTimeUpdateArray.join(", ")}`
|
|
3683
|
-
).run();
|
|
3684
|
-
} catch (error) {
|
|
3685
|
-
task.logWarning("Import error: " + error.message);
|
|
3686
|
-
}
|
|
3687
|
-
task.log(`Importing - ${totalLineCount++} entries imported\r`, true);
|
|
3688
|
-
}
|
|
3689
|
+
if (tripUpdatesData?.entity) {
|
|
3690
|
+
await processRealtimeTripUpdates(db, tripUpdatesData, task);
|
|
3689
3691
|
}
|
|
3690
3692
|
} catch (error) {
|
|
3691
3693
|
if (task.ignoreErrors) {
|
|
@@ -3697,26 +3699,12 @@ var updateRealtimeData = async (task) => {
|
|
|
3697
3699
|
}
|
|
3698
3700
|
if (task.realtimeVehiclePositions?.url) {
|
|
3699
3701
|
try {
|
|
3700
|
-
const
|
|
3702
|
+
const vehiclePositionsData = await fetchGtfsRealtimeData(
|
|
3701
3703
|
task.realtimeVehiclePositions,
|
|
3702
3704
|
task
|
|
3703
3705
|
);
|
|
3704
|
-
if (
|
|
3705
|
-
|
|
3706
|
-
let totalLineCount = 0;
|
|
3707
|
-
for (const entity of gtfsRealtimeData.entity) {
|
|
3708
|
-
const fieldValues = vehiclePositions.schema.map(
|
|
3709
|
-
(column) => prepareRealtimeValue(entity, column, task)
|
|
3710
|
-
);
|
|
3711
|
-
try {
|
|
3712
|
-
db.prepare(
|
|
3713
|
-
`REPLACE INTO ${vehiclePositions.filenameBase} (${vehiclePositions.schema.map((column) => column.name).join(", ")}) VALUES (${fieldValues.join(", ")})`
|
|
3714
|
-
).run();
|
|
3715
|
-
} catch (error) {
|
|
3716
|
-
task.logWarning("Import error: " + error.message);
|
|
3717
|
-
}
|
|
3718
|
-
task.log(`Importing - ${totalLineCount++} entries imported\r`, true);
|
|
3719
|
-
}
|
|
3706
|
+
if (vehiclePositionsData?.entity) {
|
|
3707
|
+
await processRealtimeVehiclePositions(db, vehiclePositionsData, task);
|
|
3720
3708
|
}
|
|
3721
3709
|
} catch (error) {
|
|
3722
3710
|
if (task.ignoreErrors) {
|
|
@@ -3727,12 +3715,120 @@ var updateRealtimeData = async (task) => {
|
|
|
3727
3715
|
}
|
|
3728
3716
|
}
|
|
3729
3717
|
task.log(`GTFS-Realtime data import complete`);
|
|
3718
|
+
}
|
|
3719
|
+
async function updateGtfsRealtime(initialConfig) {
|
|
3720
|
+
const config = setDefaultConfig(initialConfig);
|
|
3721
|
+
validateConfigForImport(config);
|
|
3722
|
+
const log2 = log(config);
|
|
3723
|
+
const logError2 = logError(config);
|
|
3724
|
+
const logWarning2 = logWarning(config);
|
|
3725
|
+
try {
|
|
3726
|
+
openDb(config);
|
|
3727
|
+
const agencyCount = config.agencies.length;
|
|
3728
|
+
log2(
|
|
3729
|
+
`Starting GTFS-Realtime refresh for ${pluralize(
|
|
3730
|
+
"agencies",
|
|
3731
|
+
agencyCount,
|
|
3732
|
+
true
|
|
3733
|
+
)} using SQLite database at ${config.sqlitePath}`
|
|
3734
|
+
);
|
|
3735
|
+
removeExpiredRealtimeData(config);
|
|
3736
|
+
await mapSeries(config.agencies, async (agency2) => {
|
|
3737
|
+
try {
|
|
3738
|
+
const task = {
|
|
3739
|
+
realtimeAlerts: agency2.realtimeAlerts,
|
|
3740
|
+
realtimeTripUpdates: agency2.realtimeTripUpdates,
|
|
3741
|
+
realtimeVehiclePositions: agency2.realtimeVehiclePositions,
|
|
3742
|
+
downloadTimeout: config.downloadTimeout,
|
|
3743
|
+
gtfsRealtimeExpirationSeconds: config.gtfsRealtimeExpirationSeconds,
|
|
3744
|
+
ignoreErrors: config.ignoreErrors,
|
|
3745
|
+
sqlitePath: config.sqlitePath,
|
|
3746
|
+
currentTimestamp: Math.floor(Date.now() / 1e3),
|
|
3747
|
+
log: log2,
|
|
3748
|
+
logWarning: logWarning2,
|
|
3749
|
+
logError: logError2
|
|
3750
|
+
};
|
|
3751
|
+
await updateGtfsRealtimeData(task);
|
|
3752
|
+
} catch (error) {
|
|
3753
|
+
if (config.ignoreErrors) {
|
|
3754
|
+
logError2(error.message);
|
|
3755
|
+
} else {
|
|
3756
|
+
throw error;
|
|
3757
|
+
}
|
|
3758
|
+
}
|
|
3759
|
+
});
|
|
3760
|
+
log2(
|
|
3761
|
+
`Completed GTFS-Realtime refresh for ${pluralize(
|
|
3762
|
+
"agencies",
|
|
3763
|
+
agencyCount,
|
|
3764
|
+
true
|
|
3765
|
+
)}
|
|
3766
|
+
`
|
|
3767
|
+
);
|
|
3768
|
+
} catch (error) {
|
|
3769
|
+
handleDatabaseError(error, config, logError2);
|
|
3770
|
+
}
|
|
3771
|
+
}
|
|
3772
|
+
function handleDatabaseError(error, config, logError2) {
|
|
3773
|
+
if (error?.code === "SQLITE_CANTOPEN") {
|
|
3774
|
+
logError2(
|
|
3775
|
+
`Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
|
|
3776
|
+
);
|
|
3777
|
+
}
|
|
3778
|
+
throw error;
|
|
3779
|
+
}
|
|
3780
|
+
|
|
3781
|
+
// src/lib/import-gtfs.ts
|
|
3782
|
+
var dateCache = {};
|
|
3783
|
+
var calculateAndCacheDate = (value) => {
|
|
3784
|
+
const cached = dateCache[value];
|
|
3785
|
+
if (cached != null) {
|
|
3786
|
+
return cached;
|
|
3787
|
+
}
|
|
3788
|
+
const seconds = calculateSecondsFromMidnight(value);
|
|
3789
|
+
const date = padLeadingZeros(value);
|
|
3790
|
+
const computed = [seconds, date];
|
|
3791
|
+
dateCache[value] = computed;
|
|
3792
|
+
return computed;
|
|
3730
3793
|
};
|
|
3731
3794
|
var getTextFiles = async (folderPath) => {
|
|
3732
3795
|
const files = await readdir(folderPath);
|
|
3733
3796
|
return files.filter((filename) => filename.slice(-3) === "txt");
|
|
3734
3797
|
};
|
|
3735
|
-
var
|
|
3798
|
+
var TIME_COLUMN_NAMES = [
|
|
3799
|
+
"start_time",
|
|
3800
|
+
"end_time",
|
|
3801
|
+
"arrival_time",
|
|
3802
|
+
"departure_time",
|
|
3803
|
+
"prior_notice_last_time",
|
|
3804
|
+
"prior_notice_start_time",
|
|
3805
|
+
"start_pickup_drop_off_window"
|
|
3806
|
+
];
|
|
3807
|
+
var TIME_COLUMN_PAIRS = TIME_COLUMN_NAMES.map((name) => [
|
|
3808
|
+
name,
|
|
3809
|
+
name.endsWith("time") ? `${name}stamp` : `${name}_timestamp`
|
|
3810
|
+
]);
|
|
3811
|
+
var downloadGtfsFiles = async (task) => {
|
|
3812
|
+
if (!task.url) {
|
|
3813
|
+
throw new Error("No `url` specified in config");
|
|
3814
|
+
}
|
|
3815
|
+
task.log(`Downloading GTFS from ${task.url}`);
|
|
3816
|
+
task.path = `${task.downloadDir}/gtfs.zip`;
|
|
3817
|
+
const response = await fetch(task.url, {
|
|
3818
|
+
method: "GET",
|
|
3819
|
+
headers: task.headers || {},
|
|
3820
|
+
signal: task.downloadTimeout ? AbortSignal.timeout(task.downloadTimeout) : void 0
|
|
3821
|
+
});
|
|
3822
|
+
if (response.status !== 200) {
|
|
3823
|
+
throw new Error(
|
|
3824
|
+
`Unable to download GTFS from ${task.url}. Got status ${response.status}.`
|
|
3825
|
+
);
|
|
3826
|
+
}
|
|
3827
|
+
const buffer = await response.arrayBuffer();
|
|
3828
|
+
await writeFile(task.path, Buffer.from(buffer));
|
|
3829
|
+
task.log("Download successful");
|
|
3830
|
+
};
|
|
3831
|
+
var extractGtfsFiles = async (task) => {
|
|
3736
3832
|
if (!task.path) {
|
|
3737
3833
|
throw new Error("No `path` specified in config");
|
|
3738
3834
|
}
|
|
@@ -3784,7 +3880,7 @@ var readFiles = async (task) => {
|
|
|
3784
3880
|
}
|
|
3785
3881
|
}
|
|
3786
3882
|
};
|
|
3787
|
-
var
|
|
3883
|
+
var createGtfsTables = (db) => {
|
|
3788
3884
|
for (const model of Object.values(models_exports)) {
|
|
3789
3885
|
if (!model.schema) {
|
|
3790
3886
|
return;
|
|
@@ -3813,6 +3909,13 @@ var createTables = (db) => {
|
|
|
3813
3909
|
db.prepare(
|
|
3814
3910
|
`CREATE TABLE ${model.filenameBase} (${columns.join(", ")});`
|
|
3815
3911
|
).run();
|
|
3912
|
+
}
|
|
3913
|
+
};
|
|
3914
|
+
var createGtfsIndexes = (db) => {
|
|
3915
|
+
for (const model of Object.values(models_exports)) {
|
|
3916
|
+
if (!model.schema) {
|
|
3917
|
+
return;
|
|
3918
|
+
}
|
|
3816
3919
|
for (const column of model.schema.filter((column2) => column2.index)) {
|
|
3817
3920
|
db.prepare(
|
|
3818
3921
|
`CREATE INDEX idx_${model.filenameBase}_${column.name} ON ${model.filenameBase} (${column.name});`
|
|
@@ -3820,7 +3923,7 @@ var createTables = (db) => {
|
|
|
3820
3923
|
}
|
|
3821
3924
|
}
|
|
3822
3925
|
};
|
|
3823
|
-
var
|
|
3926
|
+
var formatGtfsLine = (line, model, totalLineCount) => {
|
|
3824
3927
|
const lineNumber = totalLineCount + 1;
|
|
3825
3928
|
const formattedLine = {};
|
|
3826
3929
|
for (const columnSchema of model.schema) {
|
|
@@ -3861,85 +3964,23 @@ var formatLine = (line, model, totalLineCount) => {
|
|
|
3861
3964
|
);
|
|
3862
3965
|
}
|
|
3863
3966
|
}
|
|
3864
|
-
const
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
"prior_notice_start_time",
|
|
3871
|
-
"start_pickup_drop_off_window"
|
|
3872
|
-
];
|
|
3873
|
-
for (const timeColumnName of timeColumnNames) {
|
|
3874
|
-
if (formattedLine[timeColumnName]) {
|
|
3875
|
-
const timestampColumnName = timeColumnName.endsWith("time") ? `${timeColumnName}stamp` : `${timeColumnName}_timestamp`;
|
|
3876
|
-
formattedLine[timestampColumnName] = calculateSecondsFromMidnight(
|
|
3877
|
-
formattedLine[timeColumnName]
|
|
3878
|
-
);
|
|
3879
|
-
formattedLine[timeColumnName] = padLeadingZeros(
|
|
3880
|
-
formattedLine[timeColumnName]
|
|
3881
|
-
);
|
|
3967
|
+
for (const [timeColumnName, timestampColumnName] of TIME_COLUMN_PAIRS) {
|
|
3968
|
+
const value = formattedLine[timeColumnName];
|
|
3969
|
+
if (value) {
|
|
3970
|
+
const [seconds, date] = calculateAndCacheDate(value);
|
|
3971
|
+
formattedLine[timestampColumnName] = seconds;
|
|
3972
|
+
formattedLine[timeColumnName] = date;
|
|
3882
3973
|
}
|
|
3883
3974
|
}
|
|
3884
3975
|
return formattedLine;
|
|
3885
3976
|
};
|
|
3886
|
-
var
|
|
3887
|
-
const db = openDb({
|
|
3888
|
-
sqlitePath: task.sqlitePath
|
|
3889
|
-
});
|
|
3890
|
-
if (lines.length === 0) {
|
|
3891
|
-
return;
|
|
3892
|
-
}
|
|
3893
|
-
const linesToImportCount = lines.length;
|
|
3894
|
-
const columns = model.schema.filter((column) => column.name !== "id");
|
|
3895
|
-
const placeholders = [];
|
|
3896
|
-
const values = [];
|
|
3897
|
-
while (lines.length > 0) {
|
|
3898
|
-
const line = lines.pop();
|
|
3899
|
-
if (line === void 0) {
|
|
3900
|
-
continue;
|
|
3901
|
-
}
|
|
3902
|
-
placeholders.push(`(${columns.map(() => "?").join(", ")})`);
|
|
3903
|
-
values.push(
|
|
3904
|
-
...columns.map((column) => {
|
|
3905
|
-
if (task.prefix !== void 0 && column.prefix === true) {
|
|
3906
|
-
return `${task.prefix}${line[column.name]}`;
|
|
3907
|
-
}
|
|
3908
|
-
return line[column.name];
|
|
3909
|
-
})
|
|
3910
|
-
);
|
|
3911
|
-
}
|
|
3912
|
-
try {
|
|
3913
|
-
db.prepare(
|
|
3914
|
-
`INSERT ${task.ignoreDuplicates ? "OR IGNORE" : ""} INTO ${model.filenameBase} (${columns.map((column) => column.name).join(", ")}) VALUES ${placeholders.join(",")}`
|
|
3915
|
-
).run(...values);
|
|
3916
|
-
} catch (error) {
|
|
3917
|
-
if (error.code === "SQLITE_CONSTRAINT_PRIMARYKEY") {
|
|
3918
|
-
const primaryColumns = model.schema.filter((column) => column.primary);
|
|
3919
|
-
task.logWarning(
|
|
3920
|
-
`Duplicate values for primary key (${primaryColumns.map((column) => column.name).join(", ")}) found in ${model.filenameBase}.${model.filenameExtension}. Set the \`ignoreDuplicates\` option to true in config.json to ignore this error`
|
|
3921
|
-
);
|
|
3922
|
-
}
|
|
3923
|
-
task.logWarning(
|
|
3924
|
-
`Check ${model.filenameBase}.${model.filenameExtension} for invalid data between lines ${totalLineCount - linesToImportCount} and ${totalLineCount}.`
|
|
3925
|
-
);
|
|
3926
|
-
throw error;
|
|
3927
|
-
}
|
|
3928
|
-
task.log(
|
|
3929
|
-
`Importing - ${model.filenameBase}.${model.filenameExtension} - ${totalLineCount} lines imported\r`,
|
|
3930
|
-
true
|
|
3931
|
-
);
|
|
3932
|
-
};
|
|
3933
|
-
var importFiles = (task) => mapSeries(
|
|
3977
|
+
var importGtfsFiles = (db, task) => mapSeries2(
|
|
3934
3978
|
Object.values(models_exports),
|
|
3935
3979
|
(model) => new Promise((resolve, reject) => {
|
|
3936
|
-
const lines = [];
|
|
3937
3980
|
let totalLineCount = 0;
|
|
3938
|
-
const
|
|
3981
|
+
const filename = `${model.filenameBase}.${model.filenameExtension}`;
|
|
3939
3982
|
if (task.exclude && task.exclude.includes(model.filenameBase)) {
|
|
3940
|
-
task.log(
|
|
3941
|
-
`Skipping - ${model.filenameBase}.${model.filenameExtension}\r`
|
|
3942
|
-
);
|
|
3983
|
+
task.log(`Skipping - ${filename}\r`);
|
|
3943
3984
|
resolve();
|
|
3944
3985
|
return;
|
|
3945
3986
|
}
|
|
@@ -3947,22 +3988,51 @@ var importFiles = (task) => mapSeries(
|
|
|
3947
3988
|
resolve();
|
|
3948
3989
|
return;
|
|
3949
3990
|
}
|
|
3950
|
-
const filepath = path.join(
|
|
3951
|
-
task.downloadDir,
|
|
3952
|
-
`${model.filenameBase}.${model.filenameExtension}`
|
|
3953
|
-
);
|
|
3991
|
+
const filepath = path.join(task.downloadDir, `${filename}`);
|
|
3954
3992
|
if (!existsSync(filepath)) {
|
|
3955
3993
|
if (!model.nonstandard) {
|
|
3956
|
-
task.log(
|
|
3957
|
-
`Importing - ${model.filenameBase}.${model.filenameExtension} - No file found\r`
|
|
3958
|
-
);
|
|
3994
|
+
task.log(`Importing - ${filename} - No file found\r`);
|
|
3959
3995
|
}
|
|
3960
3996
|
resolve();
|
|
3961
3997
|
return;
|
|
3962
3998
|
}
|
|
3963
|
-
task.log(
|
|
3964
|
-
|
|
3965
|
-
);
|
|
3999
|
+
task.log(`Importing - ${filename}\r`);
|
|
4000
|
+
const columns = model.schema.filter((column) => column.name !== "id");
|
|
4001
|
+
const placeholder = columns.map(({ name }) => `@${name}`).join(", ");
|
|
4002
|
+
const prepareStatement = `INSERT ${task.ignoreDuplicates ? "OR IGNORE" : ""} INTO ${model.filenameBase} (${columns.map((column) => column.name).join(", ")}) VALUES (${placeholder})`;
|
|
4003
|
+
const insert = db.prepare(prepareStatement);
|
|
4004
|
+
const insertLines = db.transaction((lines) => {
|
|
4005
|
+
for (const [rowNumber, line] of Object.entries(lines)) {
|
|
4006
|
+
try {
|
|
4007
|
+
if (task.prefix === void 0) {
|
|
4008
|
+
insert.run(line);
|
|
4009
|
+
} else {
|
|
4010
|
+
const prefixedLine = Object.fromEntries(
|
|
4011
|
+
Object.entries(
|
|
4012
|
+
line
|
|
4013
|
+
).map(([columnName, value]) => [
|
|
4014
|
+
columnName,
|
|
4015
|
+
columns.find((col) => col.name === columnName)?.prefix ? `${task.prefix}${value}` : value
|
|
4016
|
+
])
|
|
4017
|
+
);
|
|
4018
|
+
insert.run(prefixedLine);
|
|
4019
|
+
}
|
|
4020
|
+
} catch (error) {
|
|
4021
|
+
if (error.code === "SQLITE_CONSTRAINT_PRIMARYKEY") {
|
|
4022
|
+
const primaryColumns = model.schema.filter(
|
|
4023
|
+
(column) => column.primary
|
|
4024
|
+
);
|
|
4025
|
+
task.logWarning(
|
|
4026
|
+
`Duplicate values for primary key (${primaryColumns.map((column) => column.name).join(", ")}) found in ${filename}. Set the \`ignoreDuplicates\` option to true in config.json to ignore this error`
|
|
4027
|
+
);
|
|
4028
|
+
}
|
|
4029
|
+
task.logWarning(
|
|
4030
|
+
`Check ${filename} for invalid data on row ${rowNumber}.`
|
|
4031
|
+
);
|
|
4032
|
+
throw error;
|
|
4033
|
+
}
|
|
4034
|
+
}
|
|
4035
|
+
});
|
|
3966
4036
|
if (model.filenameExtension === "txt") {
|
|
3967
4037
|
const parser = parse({
|
|
3968
4038
|
columns: true,
|
|
@@ -3971,23 +4041,21 @@ var importFiles = (task) => mapSeries(
|
|
|
3971
4041
|
skip_empty_lines: true,
|
|
3972
4042
|
...task.csvOptions
|
|
3973
4043
|
});
|
|
4044
|
+
let lines = [];
|
|
3974
4045
|
parser.on("readable", () => {
|
|
3975
4046
|
let record;
|
|
3976
4047
|
while (record = parser.read()) {
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
lines.push(formatLine(record, model, totalLineCount));
|
|
3980
|
-
if (lines.length >= maxInsertVariables / model.schema.length) {
|
|
3981
|
-
importLines(task, lines, model, totalLineCount);
|
|
3982
|
-
}
|
|
3983
|
-
} catch (error) {
|
|
3984
|
-
reject(error);
|
|
3985
|
-
}
|
|
4048
|
+
totalLineCount += 1;
|
|
4049
|
+
lines.push(formatGtfsLine(record, model, totalLineCount));
|
|
3986
4050
|
}
|
|
3987
4051
|
});
|
|
3988
4052
|
parser.on("end", () => {
|
|
3989
4053
|
try {
|
|
3990
|
-
|
|
4054
|
+
insertLines(lines);
|
|
4055
|
+
task.log(
|
|
4056
|
+
`Importing - ${filename} - ${totalLineCount} lines imported\r`,
|
|
4057
|
+
true
|
|
4058
|
+
);
|
|
3991
4059
|
} catch (error) {
|
|
3992
4060
|
reject(error);
|
|
3993
4061
|
}
|
|
@@ -3998,14 +4066,19 @@ var importFiles = (task) => mapSeries(
|
|
|
3998
4066
|
} else if (model.filenameExtension === "geojson") {
|
|
3999
4067
|
readFile2(filepath, "utf8").then((data) => {
|
|
4000
4068
|
if (isValidJSON(data) === false) {
|
|
4001
|
-
reject(
|
|
4002
|
-
new Error(
|
|
4003
|
-
`Invalid JSON in ${model.filenameBase}.${model.filenameExtension}`
|
|
4004
|
-
)
|
|
4005
|
-
);
|
|
4069
|
+
reject(new Error(`Invalid JSON in ${filename}`));
|
|
4006
4070
|
}
|
|
4007
|
-
|
|
4008
|
-
|
|
4071
|
+
totalLineCount += 1;
|
|
4072
|
+
const line = formatGtfsLine(
|
|
4073
|
+
{ geojson: data },
|
|
4074
|
+
model,
|
|
4075
|
+
totalLineCount
|
|
4076
|
+
);
|
|
4077
|
+
insertLines([line]);
|
|
4078
|
+
task.log(
|
|
4079
|
+
`Importing - ${filename} - ${totalLineCount} lines imported\r`,
|
|
4080
|
+
true
|
|
4081
|
+
);
|
|
4009
4082
|
resolve();
|
|
4010
4083
|
}).catch(reject);
|
|
4011
4084
|
} else {
|
|
@@ -4025,14 +4098,10 @@ async function importGtfs(initialConfig) {
|
|
|
4025
4098
|
const db = openDb(config);
|
|
4026
4099
|
const agencyCount = config.agencies.length;
|
|
4027
4100
|
log2(
|
|
4028
|
-
`Starting GTFS import for ${
|
|
4029
|
-
"file",
|
|
4030
|
-
agencyCount,
|
|
4031
|
-
true
|
|
4032
|
-
)} using SQLite database at ${config.sqlitePath}`
|
|
4101
|
+
`Starting GTFS import for ${pluralize2("file", agencyCount, true)} using SQLite database at ${config.sqlitePath}`
|
|
4033
4102
|
);
|
|
4034
|
-
|
|
4035
|
-
await
|
|
4103
|
+
createGtfsTables(db);
|
|
4104
|
+
await mapSeries2(config.agencies, async (agency2) => {
|
|
4036
4105
|
try {
|
|
4037
4106
|
const tempPath = temporaryDirectory();
|
|
4038
4107
|
const task = {
|
|
@@ -4057,100 +4126,50 @@ async function importGtfs(initialConfig) {
|
|
|
4057
4126
|
logError: logError2
|
|
4058
4127
|
};
|
|
4059
4128
|
if (task.url) {
|
|
4060
|
-
await
|
|
4129
|
+
await downloadGtfsFiles(task);
|
|
4061
4130
|
}
|
|
4062
|
-
await
|
|
4063
|
-
await
|
|
4064
|
-
await
|
|
4131
|
+
await extractGtfsFiles(task);
|
|
4132
|
+
await importGtfsFiles(db, task);
|
|
4133
|
+
await updateGtfsRealtimeData(task);
|
|
4065
4134
|
await rm2(tempPath, { recursive: true });
|
|
4066
4135
|
} catch (error) {
|
|
4067
|
-
|
|
4068
|
-
logError2(error.message);
|
|
4069
|
-
} else {
|
|
4070
|
-
throw error;
|
|
4071
|
-
}
|
|
4136
|
+
handleImportError(error, config, logError2);
|
|
4072
4137
|
}
|
|
4073
4138
|
});
|
|
4139
|
+
log2(`Creating DB indexes`);
|
|
4140
|
+
createGtfsIndexes(db);
|
|
4074
4141
|
log2(
|
|
4075
|
-
`Completed GTFS import for ${
|
|
4142
|
+
`Completed GTFS import for ${pluralize2("agency", agencyCount, true)}
|
|
4076
4143
|
`
|
|
4077
4144
|
);
|
|
4078
4145
|
} catch (error) {
|
|
4079
|
-
|
|
4080
|
-
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4146
|
+
handleDatabaseError2(error, config, logError2);
|
|
4147
|
+
}
|
|
4148
|
+
}
|
|
4149
|
+
function handleImportError(error, config, logError2) {
|
|
4150
|
+
if (config.ignoreErrors) {
|
|
4151
|
+
logError2(error.message);
|
|
4152
|
+
} else {
|
|
4084
4153
|
throw error;
|
|
4085
4154
|
}
|
|
4086
4155
|
}
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
const logError2 = logError(config);
|
|
4092
|
-
const logWarning2 = logWarning(config);
|
|
4093
|
-
try {
|
|
4094
|
-
openDb(config);
|
|
4095
|
-
const agencyCount = config.agencies.length;
|
|
4096
|
-
log2(
|
|
4097
|
-
`Starting GTFS-Realtime refresh for ${pluralize(
|
|
4098
|
-
"agencies",
|
|
4099
|
-
agencyCount,
|
|
4100
|
-
true
|
|
4101
|
-
)} using SQLite database at ${config.sqlitePath}`
|
|
4156
|
+
function handleDatabaseError2(error, config, logError2) {
|
|
4157
|
+
if (error?.code === "SQLITE_CANTOPEN") {
|
|
4158
|
+
logError2(
|
|
4159
|
+
`Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
|
|
4102
4160
|
);
|
|
4103
|
-
deleteExpiredRealtimeData(config);
|
|
4104
|
-
await mapSeries(config.agencies, async (agency2) => {
|
|
4105
|
-
try {
|
|
4106
|
-
const task = {
|
|
4107
|
-
realtimeAlerts: agency2.realtimeAlerts,
|
|
4108
|
-
realtimeTripUpdates: agency2.realtimeTripUpdates,
|
|
4109
|
-
realtimeVehiclePositions: agency2.realtimeVehiclePositions,
|
|
4110
|
-
downloadTimeout: config.downloadTimeout,
|
|
4111
|
-
gtfsRealtimeExpirationSeconds: config.gtfsRealtimeExpirationSeconds,
|
|
4112
|
-
ignoreErrors: config.ignoreErrors,
|
|
4113
|
-
sqlitePath: config.sqlitePath,
|
|
4114
|
-
currentTimestamp: Math.floor(Date.now() / 1e3),
|
|
4115
|
-
log: log2,
|
|
4116
|
-
logWarning: logWarning2,
|
|
4117
|
-
logError: logError2
|
|
4118
|
-
};
|
|
4119
|
-
await updateRealtimeData(task);
|
|
4120
|
-
} catch (error) {
|
|
4121
|
-
if (config.ignoreErrors) {
|
|
4122
|
-
logError2(error.message);
|
|
4123
|
-
} else {
|
|
4124
|
-
throw error;
|
|
4125
|
-
}
|
|
4126
|
-
}
|
|
4127
|
-
});
|
|
4128
|
-
log2(
|
|
4129
|
-
`Completed GTFS-Realtime refresh for ${pluralize(
|
|
4130
|
-
"agencies",
|
|
4131
|
-
agencyCount,
|
|
4132
|
-
true
|
|
4133
|
-
)}
|
|
4134
|
-
`
|
|
4135
|
-
);
|
|
4136
|
-
} catch (error) {
|
|
4137
|
-
if (error?.code === "SQLITE_CANTOPEN") {
|
|
4138
|
-
logError2(
|
|
4139
|
-
`Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
|
|
4140
|
-
);
|
|
4141
|
-
}
|
|
4142
|
-
throw error;
|
|
4143
4161
|
}
|
|
4162
|
+
throw error;
|
|
4144
4163
|
}
|
|
4145
4164
|
|
|
4146
4165
|
// src/lib/export.ts
|
|
4147
4166
|
import path2 from "node:path";
|
|
4148
4167
|
import { writeFile as writeFile2 } from "node:fs/promises";
|
|
4149
4168
|
import { without, compact as compact2 } from "lodash-es";
|
|
4150
|
-
import
|
|
4169
|
+
import pluralize3 from "pluralize";
|
|
4151
4170
|
import { stringify } from "csv-stringify";
|
|
4152
4171
|
import sqlString3 from "sqlstring-sqlite";
|
|
4153
|
-
import
|
|
4172
|
+
import mapSeries3 from "promise-map-series";
|
|
4154
4173
|
import untildify4 from "untildify";
|
|
4155
4174
|
var getAgencies = (db, config) => {
|
|
4156
4175
|
try {
|
|
@@ -4183,7 +4202,7 @@ var exportGtfs = async (initialConfig) => {
|
|
|
4183
4202
|
);
|
|
4184
4203
|
}
|
|
4185
4204
|
log2(
|
|
4186
|
-
`Starting GTFS export for ${
|
|
4205
|
+
`Starting GTFS export for ${pluralize3(
|
|
4187
4206
|
"agency",
|
|
4188
4207
|
agencyCount,
|
|
4189
4208
|
true
|
|
@@ -4196,7 +4215,7 @@ var exportGtfs = async (initialConfig) => {
|
|
|
4196
4215
|
const modelsToExport = Object.values(models_exports).filter(
|
|
4197
4216
|
(model) => model.extension !== "gtfs-realtime"
|
|
4198
4217
|
);
|
|
4199
|
-
const exportedFiles = await
|
|
4218
|
+
const exportedFiles = await mapSeries3(
|
|
4200
4219
|
modelsToExport,
|
|
4201
4220
|
async (model) => {
|
|
4202
4221
|
const filePath = path2.join(
|
|
@@ -4258,7 +4277,7 @@ var exportGtfs = async (initialConfig) => {
|
|
|
4258
4277
|
return;
|
|
4259
4278
|
}
|
|
4260
4279
|
log2(`Completed GTFS export to ${exportPath}`);
|
|
4261
|
-
log2(`Completed GTFS export for ${
|
|
4280
|
+
log2(`Completed GTFS export for ${pluralize3("agency", agencyCount, true)}
|
|
4262
4281
|
`);
|
|
4263
4282
|
};
|
|
4264
4283
|
|