gtfs 4.17.7 → 4.18.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/README.md +9 -9
- package/dist/bin/gtfs-export.js +11 -4
- package/dist/bin/gtfs-export.js.map +1 -1
- package/dist/bin/gtfs-import.js +413 -343
- package/dist/bin/gtfs-import.js.map +1 -1
- package/dist/bin/gtfsrealtime-update.js +240 -183
- package/dist/bin/gtfsrealtime-update.js.map +1 -1
- package/dist/index.d.ts +145 -7
- package/dist/index.js +459 -377
- package/dist/index.js.map +1 -1
- package/package.json +16 -11
package/dist/index.js
CHANGED
|
@@ -4147,7 +4147,7 @@ function isValidJSON(string) {
|
|
|
4147
4147
|
try {
|
|
4148
4148
|
JSON.parse(string);
|
|
4149
4149
|
return true;
|
|
4150
|
-
} catch
|
|
4150
|
+
} catch {
|
|
4151
4151
|
return false;
|
|
4152
4152
|
}
|
|
4153
4153
|
}
|
|
@@ -4212,7 +4212,7 @@ function formatProperties(properties) {
|
|
|
4212
4212
|
if (formattedRouteTextColor) {
|
|
4213
4213
|
formattedProperties.route_text_color = formattedRouteTextColor;
|
|
4214
4214
|
}
|
|
4215
|
-
if (properties.routes) {
|
|
4215
|
+
if (properties.routes && Array.isArray(properties.routes)) {
|
|
4216
4216
|
formattedProperties.routes = properties.routes.map(
|
|
4217
4217
|
(route) => formatProperties(route)
|
|
4218
4218
|
);
|
|
@@ -4277,7 +4277,8 @@ function setDefaultConfig(initialConfig) {
|
|
|
4277
4277
|
ignoreDuplicates: false,
|
|
4278
4278
|
ignoreErrors: false,
|
|
4279
4279
|
gtfsRealtimeExpirationSeconds: 0,
|
|
4280
|
-
verbose: true
|
|
4280
|
+
verbose: true,
|
|
4281
|
+
downloadTimeout: 3e4
|
|
4281
4282
|
};
|
|
4282
4283
|
return {
|
|
4283
4284
|
...defaults,
|
|
@@ -4425,56 +4426,9 @@ function pluralize(singularWord, pluralWord, count) {
|
|
|
4425
4426
|
}
|
|
4426
4427
|
|
|
4427
4428
|
// src/lib/import-gtfs-realtime.ts
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
method: "GET",
|
|
4432
|
-
headers: {
|
|
4433
|
-
...urlConfig.headers ?? {},
|
|
4434
|
-
"Accept-Encoding": "gzip"
|
|
4435
|
-
},
|
|
4436
|
-
signal: task.downloadTimeout ? AbortSignal.timeout(task.downloadTimeout) : void 0
|
|
4437
|
-
});
|
|
4438
|
-
if (response.status !== 200) {
|
|
4439
|
-
task.logWarning(
|
|
4440
|
-
`Unable to download GTFS-Realtime from ${urlConfig.url}. Got status ${response.status}.`
|
|
4441
|
-
);
|
|
4442
|
-
return null;
|
|
4443
|
-
}
|
|
4444
|
-
const buffer = await response.arrayBuffer();
|
|
4445
|
-
const message = GtfsRealtimeBindings.transit_realtime.FeedMessage.decode(
|
|
4446
|
-
new Uint8Array(buffer)
|
|
4447
|
-
);
|
|
4448
|
-
return GtfsRealtimeBindings.transit_realtime.FeedMessage.toObject(message, {
|
|
4449
|
-
enums: String,
|
|
4450
|
-
longs: String,
|
|
4451
|
-
bytes: String,
|
|
4452
|
-
defaults: false,
|
|
4453
|
-
arrays: true,
|
|
4454
|
-
objects: true,
|
|
4455
|
-
oneofs: true
|
|
4456
|
-
});
|
|
4457
|
-
}
|
|
4458
|
-
function removeExpiredRealtimeData(config) {
|
|
4459
|
-
const db = openDb(config);
|
|
4460
|
-
log(config)(`Removing expired GTFS-Realtime data`);
|
|
4461
|
-
db.prepare(
|
|
4462
|
-
`DELETE FROM vehicle_positions WHERE expiration_timestamp <= strftime('%s','now')`
|
|
4463
|
-
).run();
|
|
4464
|
-
db.prepare(
|
|
4465
|
-
`DELETE FROM trip_updates WHERE expiration_timestamp <= strftime('%s','now')`
|
|
4466
|
-
).run();
|
|
4467
|
-
db.prepare(
|
|
4468
|
-
`DELETE FROM stop_time_updates WHERE expiration_timestamp <= strftime('%s','now')`
|
|
4469
|
-
).run();
|
|
4470
|
-
db.prepare(
|
|
4471
|
-
`DELETE FROM service_alerts WHERE expiration_timestamp <= strftime('%s','now')`
|
|
4472
|
-
).run();
|
|
4473
|
-
db.prepare(
|
|
4474
|
-
`DELETE FROM service_alert_informed_entities WHERE expiration_timestamp <= strftime('%s','now')`
|
|
4475
|
-
).run();
|
|
4476
|
-
log(config)(`Removed expired GTFS-Realtime data\r`, true);
|
|
4477
|
-
}
|
|
4429
|
+
var BATCH_SIZE = 1e3;
|
|
4430
|
+
var MAX_RETRIES = 3;
|
|
4431
|
+
var RETRY_DELAY = 1e3;
|
|
4478
4432
|
function prepareRealtimeFieldValue(entity, column, task) {
|
|
4479
4433
|
if (column.name === "created_timestamp") {
|
|
4480
4434
|
return task.currentTimestamp;
|
|
@@ -4491,163 +4445,265 @@ function prepareRealtimeFieldValue(entity, column, task) {
|
|
|
4491
4445
|
);
|
|
4492
4446
|
return column.type === "json" ? JSON.stringify(prefixedValue) : prefixedValue;
|
|
4493
4447
|
}
|
|
4494
|
-
|
|
4495
|
-
const
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
);
|
|
4500
|
-
const informedEntityStmt = db.prepare(
|
|
4501
|
-
`REPLACE INTO ${serviceAlertInformedEntities.filenameBase} (${serviceAlertInformedEntities.schema.map((column) => column.name).join(
|
|
4502
|
-
", "
|
|
4503
|
-
)}) VALUES (${serviceAlertInformedEntities.schema.map(() => "?").join(", ")})`
|
|
4448
|
+
function createPreparedStatement(db, model) {
|
|
4449
|
+
const columns = model.schema.map((column) => column.name);
|
|
4450
|
+
const placeholders = model.schema.map(() => "?").join(", ");
|
|
4451
|
+
return db.prepare(
|
|
4452
|
+
`REPLACE INTO ${model.filenameBase} (${columns.join(", ")}) VALUES (${placeholders})`
|
|
4504
4453
|
);
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4454
|
+
}
|
|
4455
|
+
async function processBatch(items, batchSize, processor) {
|
|
4456
|
+
let totalRecordCount = 0;
|
|
4457
|
+
let totalErrorCount = 0;
|
|
4458
|
+
for (let i = 0; i < items.length; i += batchSize) {
|
|
4459
|
+
const batch = items.slice(i, i + batchSize);
|
|
4460
|
+
try {
|
|
4461
|
+
const result = await processor(batch);
|
|
4462
|
+
totalRecordCount += result.recordCount;
|
|
4463
|
+
totalErrorCount += result.errorCount;
|
|
4464
|
+
} catch (error) {
|
|
4465
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4466
|
+
totalErrorCount += batch.length;
|
|
4467
|
+
console.error(`Batch processing error: ${errorMessage}`);
|
|
4468
|
+
}
|
|
4469
|
+
}
|
|
4470
|
+
return { recordCount: totalRecordCount, errorCount: totalErrorCount };
|
|
4471
|
+
}
|
|
4472
|
+
async function fetchGtfsRealtimeData(type, task) {
|
|
4473
|
+
const urlConfig = getUrlConfig(type, task);
|
|
4474
|
+
if (!urlConfig) {
|
|
4475
|
+
return null;
|
|
4476
|
+
}
|
|
4477
|
+
task.log(`Importing - GTFS-Realtime from ${urlConfig.url}`);
|
|
4478
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
4479
|
+
try {
|
|
4480
|
+
const response = await fetch(urlConfig.url, {
|
|
4481
|
+
method: "GET",
|
|
4482
|
+
headers: {
|
|
4483
|
+
...urlConfig.headers ?? {},
|
|
4484
|
+
"Accept-Encoding": "gzip"
|
|
4485
|
+
},
|
|
4486
|
+
signal: task.downloadTimeout ? AbortSignal.timeout(task.downloadTimeout) : void 0
|
|
4487
|
+
});
|
|
4488
|
+
if (response.status !== 200) {
|
|
4489
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
4490
|
+
}
|
|
4491
|
+
const buffer = await response.arrayBuffer();
|
|
4492
|
+
const message = GtfsRealtimeBindings.transit_realtime.FeedMessage.decode(
|
|
4493
|
+
new Uint8Array(buffer)
|
|
4510
4494
|
);
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4495
|
+
const feedMessage = GtfsRealtimeBindings.transit_realtime.FeedMessage.toObject(message, {
|
|
4496
|
+
enums: String,
|
|
4497
|
+
longs: String,
|
|
4498
|
+
bytes: String,
|
|
4499
|
+
defaults: false,
|
|
4500
|
+
arrays: true,
|
|
4501
|
+
objects: true,
|
|
4502
|
+
oneofs: true
|
|
4503
|
+
});
|
|
4504
|
+
return feedMessage;
|
|
4505
|
+
} catch (error) {
|
|
4506
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4507
|
+
if (attempt === MAX_RETRIES) {
|
|
4508
|
+
if (task.ignoreErrors) {
|
|
4509
|
+
task.logError(
|
|
4510
|
+
`Failed to fetch ${type} after ${MAX_RETRIES} attempts: ${errorMessage}`
|
|
4511
|
+
);
|
|
4512
|
+
return null;
|
|
4513
|
+
}
|
|
4514
|
+
throw error;
|
|
4515
|
+
}
|
|
4516
|
+
task.logWarning(`Attempt ${attempt} failed for ${type}: ${errorMessage}`);
|
|
4517
|
+
await new Promise(
|
|
4518
|
+
(resolve) => setTimeout(resolve, RETRY_DELAY * attempt)
|
|
4519
|
+
);
|
|
4520
|
+
}
|
|
4521
|
+
}
|
|
4522
|
+
return null;
|
|
4523
|
+
}
|
|
4524
|
+
function getUrlConfig(type, task) {
|
|
4525
|
+
switch (type) {
|
|
4526
|
+
case "alerts":
|
|
4527
|
+
return task.realtimeAlerts;
|
|
4528
|
+
case "tripupdates":
|
|
4529
|
+
return task.realtimeTripUpdates;
|
|
4530
|
+
case "vehiclepositions":
|
|
4531
|
+
return task.realtimeVehiclePositions;
|
|
4532
|
+
default:
|
|
4533
|
+
return void 0;
|
|
4534
|
+
}
|
|
4535
|
+
}
|
|
4536
|
+
function createServiceAlertsProcessor(db, task) {
|
|
4537
|
+
const alertStmt = createPreparedStatement(db, serviceAlerts);
|
|
4538
|
+
const informedEntityStmt = createPreparedStatement(
|
|
4539
|
+
db,
|
|
4540
|
+
serviceAlertInformedEntities
|
|
4541
|
+
);
|
|
4542
|
+
return async (batch) => {
|
|
4543
|
+
let recordCount = 0;
|
|
4544
|
+
let errorCount = 0;
|
|
4545
|
+
db.transaction(() => {
|
|
4546
|
+
for (const entity of batch) {
|
|
4547
|
+
try {
|
|
4548
|
+
const alertValues = serviceAlerts.schema.map((column) => prepareRealtimeFieldValue(entity, column, task));
|
|
4549
|
+
alertStmt.run(alertValues);
|
|
4550
|
+
recordCount++;
|
|
4551
|
+
if (entity.alert?.informedEntity?.length) {
|
|
4552
|
+
for (const informedEntity of entity.alert.informedEntity) {
|
|
4516
4553
|
informedEntity.parent = entity;
|
|
4517
|
-
|
|
4554
|
+
const entityValues = serviceAlertInformedEntities.schema.map(
|
|
4518
4555
|
(column) => prepareRealtimeFieldValue(informedEntity, column, task)
|
|
4519
4556
|
);
|
|
4557
|
+
informedEntityStmt.run(entityValues);
|
|
4558
|
+
recordCount++;
|
|
4520
4559
|
}
|
|
4521
|
-
);
|
|
4522
|
-
for (const values of informedEntities) {
|
|
4523
|
-
informedEntityStmt.run(values);
|
|
4524
4560
|
}
|
|
4561
|
+
} catch (error) {
|
|
4562
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4563
|
+
errorCount++;
|
|
4564
|
+
task.logWarning(`Alert processing error: ${errorMessage}`);
|
|
4525
4565
|
}
|
|
4526
|
-
totalLineCount++;
|
|
4527
|
-
} catch (error) {
|
|
4528
|
-
task.logWarning(`Import error: ${error.message}`);
|
|
4529
4566
|
}
|
|
4530
|
-
}
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
true
|
|
4534
|
-
);
|
|
4535
|
-
})();
|
|
4567
|
+
})();
|
|
4568
|
+
return { recordCount, errorCount };
|
|
4569
|
+
};
|
|
4536
4570
|
}
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
", "
|
|
4542
|
-
)}) VALUES (${tripUpdates.schema.map(() => "?").join(", ")})`
|
|
4571
|
+
function createTripUpdatesProcessor(db, task) {
|
|
4572
|
+
const tripUpdateStmt = createPreparedStatement(
|
|
4573
|
+
db,
|
|
4574
|
+
tripUpdates
|
|
4543
4575
|
);
|
|
4544
|
-
const stopTimeStmt =
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
)}) VALUES (${stopTimeUpdates.schema.map(() => "?").join(", ")})`
|
|
4576
|
+
const stopTimeStmt = createPreparedStatement(
|
|
4577
|
+
db,
|
|
4578
|
+
stopTimeUpdates
|
|
4548
4579
|
);
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
4552
|
-
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
(
|
|
4560
|
-
|
|
4561
|
-
|
|
4580
|
+
return async (batch) => {
|
|
4581
|
+
let recordCount = 0;
|
|
4582
|
+
let errorCount = 0;
|
|
4583
|
+
db.transaction(() => {
|
|
4584
|
+
for (const entity of batch) {
|
|
4585
|
+
try {
|
|
4586
|
+
const tripUpdateValues = tripUpdates.schema.map((column) => prepareRealtimeFieldValue(entity, column, task));
|
|
4587
|
+
tripUpdateStmt.run(tripUpdateValues);
|
|
4588
|
+
recordCount++;
|
|
4589
|
+
if (entity.tripUpdate?.stopTimeUpdate?.length) {
|
|
4590
|
+
for (const stopTimeUpdate of entity.tripUpdate.stopTimeUpdate) {
|
|
4591
|
+
stopTimeUpdate.parent = entity;
|
|
4592
|
+
const stopTimeValues = stopTimeUpdates.schema.map(
|
|
4593
|
+
(column) => prepareRealtimeFieldValue(stopTimeUpdate, column, task)
|
|
4594
|
+
);
|
|
4595
|
+
stopTimeStmt.run(stopTimeValues);
|
|
4596
|
+
recordCount++;
|
|
4597
|
+
}
|
|
4598
|
+
}
|
|
4599
|
+
} catch (error) {
|
|
4600
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4601
|
+
errorCount++;
|
|
4602
|
+
task.logWarning(`Trip update processing error: ${errorMessage}`);
|
|
4562
4603
|
}
|
|
4563
|
-
totalLineCount++;
|
|
4564
|
-
} catch (error) {
|
|
4565
|
-
task.logWarning(`Import error: ${error.message}`);
|
|
4566
4604
|
}
|
|
4567
|
-
}
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
true
|
|
4571
|
-
);
|
|
4572
|
-
})();
|
|
4605
|
+
})();
|
|
4606
|
+
return { recordCount, errorCount };
|
|
4607
|
+
};
|
|
4573
4608
|
}
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
", "
|
|
4579
|
-
)}) VALUES (${vehiclePositions.schema.map(() => "?").join(", ")})`
|
|
4609
|
+
function createVehiclePositionsProcessor(db, task) {
|
|
4610
|
+
const vehiclePositionStmt = createPreparedStatement(
|
|
4611
|
+
db,
|
|
4612
|
+
vehiclePositions
|
|
4580
4613
|
);
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4614
|
+
return async (batch) => {
|
|
4615
|
+
let recordCount = 0;
|
|
4616
|
+
let errorCount = 0;
|
|
4617
|
+
db.transaction(() => {
|
|
4618
|
+
for (const entity of batch) {
|
|
4619
|
+
try {
|
|
4620
|
+
const fieldValues = vehiclePositions.schema.map((column) => prepareRealtimeFieldValue(entity, column, task));
|
|
4621
|
+
vehiclePositionStmt.run(fieldValues);
|
|
4622
|
+
recordCount++;
|
|
4623
|
+
} catch (error) {
|
|
4624
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4625
|
+
errorCount++;
|
|
4626
|
+
task.logWarning(`Vehicle position processing error: ${errorMessage}`);
|
|
4627
|
+
}
|
|
4589
4628
|
}
|
|
4629
|
+
})();
|
|
4630
|
+
return { recordCount, errorCount };
|
|
4631
|
+
};
|
|
4632
|
+
}
|
|
4633
|
+
function removeExpiredRealtimeData(config) {
|
|
4634
|
+
const db = openDb(config);
|
|
4635
|
+
log(config)(`Removing expired GTFS-Realtime data`);
|
|
4636
|
+
db.transaction(() => {
|
|
4637
|
+
const tables = [
|
|
4638
|
+
"vehicle_positions",
|
|
4639
|
+
"trip_updates",
|
|
4640
|
+
"stop_time_updates",
|
|
4641
|
+
"service_alerts",
|
|
4642
|
+
"service_alert_informed_entities"
|
|
4643
|
+
];
|
|
4644
|
+
for (const table of tables) {
|
|
4645
|
+
db.prepare(
|
|
4646
|
+
`DELETE FROM ${table} WHERE expiration_timestamp <= strftime('%s','now')`
|
|
4647
|
+
).run();
|
|
4590
4648
|
}
|
|
4591
|
-
task.log(
|
|
4592
|
-
`Importing - GTFS-Realtime vehicle positions - ${totalLineCount} entries imported\r`,
|
|
4593
|
-
true
|
|
4594
|
-
);
|
|
4595
4649
|
})();
|
|
4650
|
+
log(config)(`Removed expired GTFS-Realtime data\r`, true);
|
|
4596
4651
|
}
|
|
4597
4652
|
async function updateGtfsRealtimeData(task) {
|
|
4598
|
-
if (task.realtimeAlerts
|
|
4653
|
+
if (!task.realtimeAlerts && !task.realtimeTripUpdates && !task.realtimeVehiclePositions) {
|
|
4599
4654
|
return;
|
|
4600
4655
|
}
|
|
4656
|
+
const [alertsData, tripUpdatesData, vehiclePositionsData] = await Promise.all(
|
|
4657
|
+
[
|
|
4658
|
+
task.realtimeAlerts?.url ? fetchGtfsRealtimeData("alerts", task) : null,
|
|
4659
|
+
task.realtimeTripUpdates?.url ? fetchGtfsRealtimeData("tripupdates", task) : null,
|
|
4660
|
+
task.realtimeVehiclePositions?.url ? fetchGtfsRealtimeData("vehiclepositions", task) : null
|
|
4661
|
+
]
|
|
4662
|
+
);
|
|
4601
4663
|
const db = openDb({ sqlitePath: task.sqlitePath });
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4664
|
+
const recordCounts = {
|
|
4665
|
+
alerts: 0,
|
|
4666
|
+
tripupdates: 0,
|
|
4667
|
+
vehiclepositions: 0
|
|
4668
|
+
};
|
|
4669
|
+
const processingPromises = [];
|
|
4670
|
+
if (alertsData?.entity?.length) {
|
|
4671
|
+
processingPromises.push(
|
|
4672
|
+
processBatch(
|
|
4673
|
+
alertsData.entity,
|
|
4674
|
+
BATCH_SIZE,
|
|
4675
|
+
createServiceAlertsProcessor(db, task)
|
|
4676
|
+
).then((result) => {
|
|
4677
|
+
recordCounts.alerts = result.recordCount;
|
|
4678
|
+
})
|
|
4679
|
+
);
|
|
4615
4680
|
}
|
|
4616
|
-
if (
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4621
|
-
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
}
|
|
4625
|
-
|
|
4626
|
-
if (task.ignoreErrors) {
|
|
4627
|
-
task.logError(error.message);
|
|
4628
|
-
} else {
|
|
4629
|
-
throw error;
|
|
4630
|
-
}
|
|
4631
|
-
}
|
|
4681
|
+
if (tripUpdatesData?.entity?.length) {
|
|
4682
|
+
processingPromises.push(
|
|
4683
|
+
processBatch(
|
|
4684
|
+
tripUpdatesData.entity,
|
|
4685
|
+
BATCH_SIZE,
|
|
4686
|
+
createTripUpdatesProcessor(db, task)
|
|
4687
|
+
).then((result) => {
|
|
4688
|
+
recordCounts.tripupdates = result.recordCount;
|
|
4689
|
+
})
|
|
4690
|
+
);
|
|
4632
4691
|
}
|
|
4633
|
-
if (
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
}
|
|
4642
|
-
|
|
4643
|
-
if (task.ignoreErrors) {
|
|
4644
|
-
task.logError(error.message);
|
|
4645
|
-
} else {
|
|
4646
|
-
throw error;
|
|
4647
|
-
}
|
|
4648
|
-
}
|
|
4692
|
+
if (vehiclePositionsData?.entity?.length) {
|
|
4693
|
+
processingPromises.push(
|
|
4694
|
+
processBatch(
|
|
4695
|
+
vehiclePositionsData.entity,
|
|
4696
|
+
BATCH_SIZE,
|
|
4697
|
+
createVehiclePositionsProcessor(db, task)
|
|
4698
|
+
).then((result) => {
|
|
4699
|
+
recordCounts.vehiclepositions = result.recordCount;
|
|
4700
|
+
})
|
|
4701
|
+
);
|
|
4649
4702
|
}
|
|
4650
|
-
|
|
4703
|
+
await Promise.all(processingPromises);
|
|
4704
|
+
task.log(
|
|
4705
|
+
`GTFS-Realtime import complete: ${recordCounts.alerts} alerts, ${recordCounts.tripupdates} trip updates, ${recordCounts.vehiclepositions} vehicle positions`
|
|
4706
|
+
);
|
|
4651
4707
|
}
|
|
4652
4708
|
async function updateGtfsRealtime(initialConfig) {
|
|
4653
4709
|
const config = setDefaultConfig(initialConfig);
|
|
@@ -4681,8 +4737,9 @@ async function updateGtfsRealtime(initialConfig) {
|
|
|
4681
4737
|
};
|
|
4682
4738
|
await updateGtfsRealtimeData(task);
|
|
4683
4739
|
} catch (error) {
|
|
4740
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4684
4741
|
if (config.ignoreErrors) {
|
|
4685
|
-
logError(config)(
|
|
4742
|
+
logError(config)(errorMessage);
|
|
4686
4743
|
} else {
|
|
4687
4744
|
throw error;
|
|
4688
4745
|
}
|
|
@@ -4697,7 +4754,7 @@ async function updateGtfsRealtime(initialConfig) {
|
|
|
4697
4754
|
`
|
|
4698
4755
|
);
|
|
4699
4756
|
} catch (error) {
|
|
4700
|
-
if (error
|
|
4757
|
+
if (error.code === "SQLITE_CANTOPEN") {
|
|
4701
4758
|
logError(config)(
|
|
4702
4759
|
`Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
|
|
4703
4760
|
);
|
|
@@ -4736,7 +4793,7 @@ var extractGtfsFiles = async (task) => {
|
|
|
4736
4793
|
throw new Error("No `path` specified in config");
|
|
4737
4794
|
}
|
|
4738
4795
|
const gtfsPath = untildify(task.path);
|
|
4739
|
-
task.log(`Importing GTFS from ${task.path}\r`);
|
|
4796
|
+
task.log(`Importing static GTFS from ${task.path}\r`);
|
|
4740
4797
|
if (path2.extname(gtfsPath) === ".zip") {
|
|
4741
4798
|
try {
|
|
4742
4799
|
await unzip(gtfsPath, task.downloadDir);
|
|
@@ -4819,11 +4876,11 @@ var createGtfsTables = (db) => {
|
|
|
4819
4876
|
if (column.type === "time") {
|
|
4820
4877
|
sqlColumnCreateStatements.push(
|
|
4821
4878
|
`${getTimestampColumnName(column.name)} INTEGER GENERATED ALWAYS AS (
|
|
4822
|
-
CASE
|
|
4823
|
-
WHEN ${column.name} IS NULL OR ${column.name} = '' THEN NULL
|
|
4879
|
+
CASE
|
|
4880
|
+
WHEN ${column.name} IS NULL OR ${column.name} = '' THEN NULL
|
|
4824
4881
|
ELSE CAST(
|
|
4825
|
-
substr(${column.name}, 1, instr(${column.name}, ':') - 1) * 3600 +
|
|
4826
|
-
substr(${column.name}, instr(${column.name}, ':') + 1, 2) * 60 +
|
|
4882
|
+
substr(${column.name}, 1, instr(${column.name}, ':') - 1) * 3600 +
|
|
4883
|
+
substr(${column.name}, instr(${column.name}, ':') + 1, 2) * 60 +
|
|
4827
4884
|
substr(${column.name}, -2) AS INTEGER
|
|
4828
4885
|
)
|
|
4829
4886
|
END
|
|
@@ -4880,7 +4937,7 @@ var formatGtfsLine = (line, model, totalLineCount) => {
|
|
|
4880
4937
|
continue;
|
|
4881
4938
|
}
|
|
4882
4939
|
if (type === "date") {
|
|
4883
|
-
value = value.replace(/-/g, "");
|
|
4940
|
+
value = value?.toString().replace(/-/g, "");
|
|
4884
4941
|
if (value.length !== 8) {
|
|
4885
4942
|
throw new Error(
|
|
4886
4943
|
`Invalid date in ${filenameBase}.${filenameExtension} for ${name} on line ${lineNumber}.`
|
|
@@ -4896,203 +4953,212 @@ var formatGtfsLine = (line, model, totalLineCount) => {
|
|
|
4896
4953
|
}
|
|
4897
4954
|
return formattedLine;
|
|
4898
4955
|
};
|
|
4899
|
-
var
|
|
4900
|
-
var importGtfsFiles = (db, task) =>
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
task.
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
if (model.extension === "gtfs-realtime") {
|
|
4911
|
-
resolve();
|
|
4912
|
-
return;
|
|
4913
|
-
}
|
|
4914
|
-
const filepath = path2.join(task.downloadDir, `${filename}`);
|
|
4915
|
-
if (!existsSync2(filepath)) {
|
|
4916
|
-
if (!model.nonstandard) {
|
|
4917
|
-
task.log(`Importing - ${filename} - No file found\r`);
|
|
4956
|
+
var BATCH_SIZE2 = 1e5;
|
|
4957
|
+
var importGtfsFiles = async (db, task) => {
|
|
4958
|
+
await mapSeries2(
|
|
4959
|
+
Object.values(models_exports),
|
|
4960
|
+
(model) => new Promise((resolve, reject) => {
|
|
4961
|
+
let totalLineCount = 0;
|
|
4962
|
+
const filename = `${model.filenameBase}.${model.filenameExtension}`;
|
|
4963
|
+
if (task.exclude && task.exclude.includes(model.filenameBase)) {
|
|
4964
|
+
task.log(`Skipping - ${filename}\r`);
|
|
4965
|
+
resolve();
|
|
4966
|
+
return;
|
|
4918
4967
|
}
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
const prepareStatement = `INSERT ${task.ignoreDuplicates ? "OR IGNORE" : ""} INTO ${model.filenameBase} (${columns.map(({ name }) => name).join(", ")}) VALUES (${columns.map(({ name }) => `@${name}`).join(", ")})`;
|
|
4928
|
-
const insert = db.prepare(prepareStatement);
|
|
4929
|
-
const insertLines = db.transaction((lines) => {
|
|
4930
|
-
for (const [rowNumber, line] of Object.entries(lines)) {
|
|
4931
|
-
try {
|
|
4932
|
-
if (task.prefix === void 0) {
|
|
4933
|
-
insert.run(line);
|
|
4934
|
-
} else {
|
|
4935
|
-
const prefixedLine = Object.fromEntries(
|
|
4936
|
-
Object.entries(
|
|
4937
|
-
line
|
|
4938
|
-
).map(([columnName, value]) => [
|
|
4939
|
-
columnName,
|
|
4940
|
-
applyPrefixToValue(
|
|
4941
|
-
value,
|
|
4942
|
-
prefixedColumns.has(columnName),
|
|
4943
|
-
task.prefix
|
|
4944
|
-
)
|
|
4945
|
-
])
|
|
4946
|
-
);
|
|
4947
|
-
insert.run(prefixedLine);
|
|
4948
|
-
}
|
|
4949
|
-
} catch (error) {
|
|
4950
|
-
if (error.code === "SQLITE_CONSTRAINT_PRIMARYKEY") {
|
|
4951
|
-
const primaryColumns = columns.filter(
|
|
4952
|
-
(column) => column.primary
|
|
4953
|
-
);
|
|
4954
|
-
task.logWarning(
|
|
4955
|
-
`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`
|
|
4956
|
-
);
|
|
4957
|
-
}
|
|
4958
|
-
task.logWarning(
|
|
4959
|
-
`Check ${filename} for invalid data on line ${rowNumber + 1}.`
|
|
4960
|
-
);
|
|
4961
|
-
throw error;
|
|
4968
|
+
if (model.extension === "gtfs-realtime") {
|
|
4969
|
+
resolve();
|
|
4970
|
+
return;
|
|
4971
|
+
}
|
|
4972
|
+
const filepath = path2.join(task.downloadDir, `${filename}`);
|
|
4973
|
+
if (!existsSync2(filepath)) {
|
|
4974
|
+
if (!model.nonstandard) {
|
|
4975
|
+
task.log(`Importing - ${filename} - No file found\r`);
|
|
4962
4976
|
}
|
|
4977
|
+
resolve();
|
|
4978
|
+
return;
|
|
4963
4979
|
}
|
|
4964
|
-
|
|
4965
|
-
|
|
4966
|
-
const
|
|
4967
|
-
columns
|
|
4968
|
-
|
|
4969
|
-
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
|
|
4973
|
-
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4980
|
+
task.log(`Importing - ${filename}\r`);
|
|
4981
|
+
const columns = model.schema;
|
|
4982
|
+
const prefixedColumns = new Set(
|
|
4983
|
+
columns.filter((column) => column.prefix).map((column) => column.name)
|
|
4984
|
+
);
|
|
4985
|
+
const prepareStatement = `INSERT ${task.ignoreDuplicates ? "OR IGNORE" : ""} INTO ${model.filenameBase} (${columns.map(({ name }) => name).join(", ")}) VALUES (${columns.map(({ name }) => `@${name}`).join(", ")})`;
|
|
4986
|
+
const insert = db.prepare(prepareStatement);
|
|
4987
|
+
const insertLines = db.transaction((lines) => {
|
|
4988
|
+
for (const [rowNumber, line] of Object.entries(lines)) {
|
|
4989
|
+
try {
|
|
4990
|
+
if (task.prefix === void 0) {
|
|
4991
|
+
insert.run(line);
|
|
4992
|
+
} else {
|
|
4993
|
+
const prefixedLine = Object.fromEntries(
|
|
4994
|
+
Object.entries(
|
|
4995
|
+
line
|
|
4996
|
+
).map(([columnName, value]) => [
|
|
4997
|
+
columnName,
|
|
4998
|
+
applyPrefixToValue(
|
|
4999
|
+
value,
|
|
5000
|
+
prefixedColumns.has(columnName),
|
|
5001
|
+
task.prefix
|
|
5002
|
+
)
|
|
5003
|
+
])
|
|
4986
5004
|
);
|
|
5005
|
+
insert.run(prefixedLine);
|
|
4987
5006
|
}
|
|
4988
|
-
}
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
5007
|
+
} catch (error) {
|
|
5008
|
+
if (error.code === "SQLITE_CONSTRAINT_PRIMARYKEY") {
|
|
5009
|
+
const primaryColumns = columns.filter(
|
|
5010
|
+
(column) => column.primary
|
|
5011
|
+
);
|
|
5012
|
+
task.logWarning(
|
|
5013
|
+
`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`
|
|
5014
|
+
);
|
|
5015
|
+
}
|
|
5016
|
+
task.logWarning(
|
|
5017
|
+
`Check ${filename} for invalid data on line ${rowNumber + 1}.`
|
|
5018
|
+
);
|
|
5019
|
+
throw error;
|
|
4995
5020
|
}
|
|
4996
5021
|
}
|
|
4997
5022
|
});
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5023
|
+
if (model.filenameExtension === "txt") {
|
|
5024
|
+
const parser = parse({
|
|
5025
|
+
columns: true,
|
|
5026
|
+
relax_quotes: true,
|
|
5027
|
+
trim: true,
|
|
5028
|
+
skip_empty_lines: true,
|
|
5029
|
+
...task.csvOptions
|
|
5030
|
+
});
|
|
5031
|
+
let lines = [];
|
|
5032
|
+
parser.on("readable", () => {
|
|
5033
|
+
try {
|
|
5034
|
+
let record;
|
|
5035
|
+
while (record = parser.read()) {
|
|
5036
|
+
totalLineCount += 1;
|
|
5037
|
+
lines.push(formatGtfsLine(record, model, totalLineCount));
|
|
5038
|
+
if (lines.length >= BATCH_SIZE2) {
|
|
5039
|
+
insertLines(lines);
|
|
5040
|
+
lines = [];
|
|
5041
|
+
task.log(
|
|
5042
|
+
`Importing - ${filename} - ${totalLineCount} lines imported\r`,
|
|
5043
|
+
true
|
|
5007
5044
|
);
|
|
5008
|
-
resolve();
|
|
5009
|
-
return;
|
|
5010
|
-
} else {
|
|
5011
|
-
reject(error);
|
|
5012
|
-
return;
|
|
5013
5045
|
}
|
|
5014
5046
|
}
|
|
5047
|
+
} catch (error) {
|
|
5048
|
+
if (task.ignoreErrors) {
|
|
5049
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5050
|
+
task.logError(`Error processing ${filename}: ${errorMessage}`);
|
|
5051
|
+
resolve();
|
|
5052
|
+
} else {
|
|
5053
|
+
reject(error);
|
|
5054
|
+
}
|
|
5015
5055
|
}
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5056
|
+
});
|
|
5057
|
+
parser.on("end", () => {
|
|
5058
|
+
try {
|
|
5059
|
+
if (lines.length > 0) {
|
|
5060
|
+
try {
|
|
5061
|
+
insertLines(lines);
|
|
5062
|
+
} catch (error) {
|
|
5063
|
+
if (task.ignoreErrors) {
|
|
5064
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5065
|
+
task.logError(
|
|
5066
|
+
`Error inserting data for ${filename}: ${errorMessage}`
|
|
5067
|
+
);
|
|
5068
|
+
resolve();
|
|
5069
|
+
return;
|
|
5070
|
+
} else {
|
|
5071
|
+
reject(error);
|
|
5072
|
+
return;
|
|
5073
|
+
}
|
|
5074
|
+
}
|
|
5075
|
+
}
|
|
5076
|
+
task.log(
|
|
5077
|
+
`Importing - ${filename} - ${totalLineCount} lines imported\r`,
|
|
5078
|
+
true
|
|
5079
|
+
);
|
|
5024
5080
|
resolve();
|
|
5025
|
-
}
|
|
5026
|
-
|
|
5081
|
+
} catch (error) {
|
|
5082
|
+
if (task.ignoreErrors) {
|
|
5083
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5084
|
+
task.logError(`Error finalizing ${filename}: ${errorMessage}`);
|
|
5085
|
+
resolve();
|
|
5086
|
+
} else {
|
|
5087
|
+
reject(error);
|
|
5088
|
+
}
|
|
5027
5089
|
}
|
|
5028
|
-
}
|
|
5029
|
-
|
|
5030
|
-
parser.on("error", (error) => {
|
|
5031
|
-
if (task.ignoreErrors) {
|
|
5032
|
-
task.logError(`Parser error for ${filename}: ${error.message}`);
|
|
5033
|
-
resolve();
|
|
5034
|
-
} else {
|
|
5035
|
-
reject(error);
|
|
5036
|
-
}
|
|
5037
|
-
});
|
|
5038
|
-
createReadStream(filepath).pipe(stripBomStream()).pipe(parser);
|
|
5039
|
-
} else if (model.filenameExtension === "geojson") {
|
|
5040
|
-
readFile2(filepath, "utf8").then((data) => {
|
|
5041
|
-
if (isValidJSON(data) === false) {
|
|
5090
|
+
});
|
|
5091
|
+
parser.on("error", (error) => {
|
|
5042
5092
|
if (task.ignoreErrors) {
|
|
5043
|
-
|
|
5093
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5094
|
+
task.logError(`Parser error for ${filename}: ${errorMessage}`);
|
|
5044
5095
|
resolve();
|
|
5045
|
-
return;
|
|
5046
5096
|
} else {
|
|
5047
|
-
reject(
|
|
5048
|
-
return;
|
|
5097
|
+
reject(error);
|
|
5049
5098
|
}
|
|
5050
|
-
}
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5099
|
+
});
|
|
5100
|
+
createReadStream(filepath).pipe(stripBomStream()).pipe(parser);
|
|
5101
|
+
} else if (model.filenameExtension === "geojson") {
|
|
5102
|
+
readFile2(filepath, "utf8").then((data) => {
|
|
5103
|
+
if (isValidJSON(data) === false) {
|
|
5104
|
+
if (task.ignoreErrors) {
|
|
5105
|
+
task.logError(`Invalid JSON in ${filename}`);
|
|
5106
|
+
resolve();
|
|
5107
|
+
return;
|
|
5108
|
+
} else {
|
|
5109
|
+
reject(new Error(`Invalid JSON in ${filename}`));
|
|
5110
|
+
return;
|
|
5111
|
+
}
|
|
5112
|
+
}
|
|
5113
|
+
totalLineCount += 1;
|
|
5114
|
+
const line = formatGtfsLine(
|
|
5115
|
+
{ geojson: data },
|
|
5116
|
+
model,
|
|
5117
|
+
totalLineCount
|
|
5062
5118
|
);
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5119
|
+
try {
|
|
5120
|
+
insertLines([line]);
|
|
5121
|
+
task.log(
|
|
5122
|
+
`Importing - ${filename} - ${totalLineCount} lines imported\r`,
|
|
5123
|
+
true
|
|
5068
5124
|
);
|
|
5069
5125
|
resolve();
|
|
5126
|
+
} catch (error) {
|
|
5127
|
+
if (task.ignoreErrors) {
|
|
5128
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5129
|
+
task.logError(
|
|
5130
|
+
`Error inserting data for ${filename}: ${errorMessage}`
|
|
5131
|
+
);
|
|
5132
|
+
resolve();
|
|
5133
|
+
} else {
|
|
5134
|
+
reject(error);
|
|
5135
|
+
}
|
|
5136
|
+
}
|
|
5137
|
+
}).catch((error) => {
|
|
5138
|
+
if (task.ignoreErrors) {
|
|
5139
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5140
|
+
task.logError(`Error reading ${filename}: ${errorMessage}`);
|
|
5141
|
+
resolve();
|
|
5070
5142
|
} else {
|
|
5071
5143
|
reject(error);
|
|
5072
5144
|
}
|
|
5073
|
-
}
|
|
5074
|
-
}
|
|
5145
|
+
});
|
|
5146
|
+
} else {
|
|
5075
5147
|
if (task.ignoreErrors) {
|
|
5076
|
-
task.logError(
|
|
5148
|
+
task.logError(
|
|
5149
|
+
`Unsupported file type: ${model.filenameExtension} for ${filename}`
|
|
5150
|
+
);
|
|
5077
5151
|
resolve();
|
|
5078
5152
|
} else {
|
|
5079
|
-
reject(
|
|
5153
|
+
reject(
|
|
5154
|
+
new Error(`Unsupported file type: ${model.filenameExtension}`)
|
|
5155
|
+
);
|
|
5080
5156
|
}
|
|
5081
|
-
});
|
|
5082
|
-
} else {
|
|
5083
|
-
if (task.ignoreErrors) {
|
|
5084
|
-
task.logError(
|
|
5085
|
-
`Unsupported file type: ${model.filenameExtension} for ${filename}`
|
|
5086
|
-
);
|
|
5087
|
-
resolve();
|
|
5088
|
-
} else {
|
|
5089
|
-
reject(
|
|
5090
|
-
new Error(`Unsupported file type: ${model.filenameExtension}`)
|
|
5091
|
-
);
|
|
5092
5157
|
}
|
|
5093
|
-
}
|
|
5094
|
-
|
|
5095
|
-
);
|
|
5158
|
+
})
|
|
5159
|
+
);
|
|
5160
|
+
task.log(`Static GTFS import complete`);
|
|
5161
|
+
};
|
|
5096
5162
|
async function importGtfs(initialConfig) {
|
|
5097
5163
|
const startTime = process.hrtime.bigint();
|
|
5098
5164
|
const config = setDefaultConfig(initialConfig);
|
|
@@ -5109,7 +5175,6 @@ async function importGtfs(initialConfig) {
|
|
|
5109
5175
|
const tempPath = temporaryDirectory();
|
|
5110
5176
|
const task = {
|
|
5111
5177
|
exclude: agency2.exclude,
|
|
5112
|
-
url: agency2.url,
|
|
5113
5178
|
headers: agency2.headers,
|
|
5114
5179
|
realtimeAlerts: agency2.realtimeAlerts,
|
|
5115
5180
|
realtimeTripUpdates: agency2.realtimeTripUpdates,
|
|
@@ -5117,7 +5182,6 @@ async function importGtfs(initialConfig) {
|
|
|
5117
5182
|
downloadDir: tempPath,
|
|
5118
5183
|
downloadTimeout: config.downloadTimeout,
|
|
5119
5184
|
gtfsRealtimeExpirationSeconds: config.gtfsRealtimeExpirationSeconds,
|
|
5120
|
-
path: agency2.path,
|
|
5121
5185
|
csvOptions: config.csvOptions || {},
|
|
5122
5186
|
ignoreDuplicates: config.ignoreDuplicates,
|
|
5123
5187
|
ignoreErrors: config.ignoreErrors,
|
|
@@ -5128,8 +5192,13 @@ async function importGtfs(initialConfig) {
|
|
|
5128
5192
|
logWarning: logWarning(config),
|
|
5129
5193
|
logError: logError(config)
|
|
5130
5194
|
};
|
|
5131
|
-
if (
|
|
5195
|
+
if ("url" in agency2) {
|
|
5196
|
+
Object.assign(task, { url: agency2.url });
|
|
5132
5197
|
await downloadGtfsFiles(task);
|
|
5198
|
+
} else {
|
|
5199
|
+
Object.assign(task, {
|
|
5200
|
+
path: agency2.path
|
|
5201
|
+
});
|
|
5133
5202
|
}
|
|
5134
5203
|
await extractGtfsFiles(task);
|
|
5135
5204
|
await importGtfsFiles(db, task);
|
|
@@ -5137,7 +5206,8 @@ async function importGtfs(initialConfig) {
|
|
|
5137
5206
|
await rm2(tempPath, { recursive: true });
|
|
5138
5207
|
} catch (error) {
|
|
5139
5208
|
if (config.ignoreErrors) {
|
|
5140
|
-
|
|
5209
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5210
|
+
logError(config)(errorMessage);
|
|
5141
5211
|
} else {
|
|
5142
5212
|
throw error;
|
|
5143
5213
|
}
|
|
@@ -5152,7 +5222,7 @@ async function importGtfs(initialConfig) {
|
|
|
5152
5222
|
`
|
|
5153
5223
|
);
|
|
5154
5224
|
} catch (error) {
|
|
5155
|
-
if (error
|
|
5225
|
+
if (error.code === "SQLITE_CANTOPEN") {
|
|
5156
5226
|
logError(config)(
|
|
5157
5227
|
`Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
|
|
5158
5228
|
);
|
|
@@ -5171,7 +5241,7 @@ import mapSeries3 from "promise-map-series";
|
|
|
5171
5241
|
var getAgencies = (db, config) => {
|
|
5172
5242
|
try {
|
|
5173
5243
|
return db.prepare("SELECT agency_name FROM agency;").all();
|
|
5174
|
-
} catch
|
|
5244
|
+
} catch {
|
|
5175
5245
|
if (config.sqlitePath === ":memory:") {
|
|
5176
5246
|
throw new Error(
|
|
5177
5247
|
'No agencies found in SQLite. You are using an in-memory database - if running this from command line be sure to specify a value for `sqlitePath` in config.json other than ":memory:".'
|
|
@@ -5238,11 +5308,17 @@ var exportGtfs = async (initialConfig) => {
|
|
|
5238
5308
|
}
|
|
5239
5309
|
} else if (model.filenameBase === "fare_attributes") {
|
|
5240
5310
|
for (const line of lines) {
|
|
5241
|
-
line.price = formatCurrency(
|
|
5311
|
+
line.price = formatCurrency(
|
|
5312
|
+
line.price,
|
|
5313
|
+
line.currency_type
|
|
5314
|
+
);
|
|
5242
5315
|
}
|
|
5243
5316
|
} else if (model.filenameBase === "fare_products") {
|
|
5244
5317
|
for (const line of lines) {
|
|
5245
|
-
line.amount = formatCurrency(
|
|
5318
|
+
line.amount = formatCurrency(
|
|
5319
|
+
line.amount,
|
|
5320
|
+
line.currency
|
|
5321
|
+
);
|
|
5246
5322
|
}
|
|
5247
5323
|
}
|
|
5248
5324
|
const columns = without(
|
|
@@ -5617,10 +5693,7 @@ function getRoutes(query = {}, fields = [], orderBy2 = [], options = {}) {
|
|
|
5617
5693
|
let whereClause = "";
|
|
5618
5694
|
const orderByClause = formatOrderByClause(orderBy2);
|
|
5619
5695
|
const routeQuery = omit3(query, ["stop_id", "service_id"]);
|
|
5620
|
-
const tripQuery = pick(query, [
|
|
5621
|
-
"stop_id",
|
|
5622
|
-
"service_id"
|
|
5623
|
-
]);
|
|
5696
|
+
const tripQuery = pick(query, ["stop_id", "service_id"]);
|
|
5624
5697
|
const whereClauses = Object.entries(routeQuery).map(
|
|
5625
5698
|
([key, value]) => formatWhereClause(key, value)
|
|
5626
5699
|
);
|
|
@@ -5668,7 +5741,12 @@ function getShapes(query = {}, fields = [], orderBy2 = [], options = {}) {
|
|
|
5668
5741
|
"service_id",
|
|
5669
5742
|
"direction_id"
|
|
5670
5743
|
]);
|
|
5671
|
-
const tripQuery = pick2(query, [
|
|
5744
|
+
const tripQuery = pick2(query, [
|
|
5745
|
+
"route_id",
|
|
5746
|
+
"trip_id",
|
|
5747
|
+
"service_id",
|
|
5748
|
+
"direction_id"
|
|
5749
|
+
]);
|
|
5672
5750
|
const whereClauses = Object.entries(shapeQuery).map(
|
|
5673
5751
|
([key, value]) => formatWhereClause(key, value)
|
|
5674
5752
|
);
|
|
@@ -5775,7 +5853,7 @@ function getStops(query = {}, fields = [], orderBy2 = [], options = {}) {
|
|
|
5775
5853
|
if (options.bounding_box_side_m !== void 0) {
|
|
5776
5854
|
stopQueryOmitKeys.push("stop_lat", "stop_lon");
|
|
5777
5855
|
}
|
|
5778
|
-
|
|
5856
|
+
const stopQuery = omit5(query, stopQueryOmitKeys);
|
|
5779
5857
|
const tripQuery = pick3(query, [
|
|
5780
5858
|
"route_id",
|
|
5781
5859
|
"trip_id",
|
|
@@ -5840,7 +5918,7 @@ function getStoptimes(query = {}, fields = [], orderBy2 = [], options = {}) {
|
|
|
5840
5918
|
let whereClause = "";
|
|
5841
5919
|
const orderByClause = formatOrderByClause(orderBy2);
|
|
5842
5920
|
const stoptimeQueryOmitKeys = ["date", "start_time", "end_time"];
|
|
5843
|
-
|
|
5921
|
+
const stoptimeQuery = omit6(query, stoptimeQueryOmitKeys);
|
|
5844
5922
|
const whereClauses = Object.entries(stoptimeQuery).map(
|
|
5845
5923
|
([key, value]) => formatWhereClause(key, value)
|
|
5846
5924
|
);
|
|
@@ -5848,7 +5926,7 @@ function getStoptimes(query = {}, fields = [], orderBy2 = [], options = {}) {
|
|
|
5848
5926
|
if (typeof query.date !== "number") {
|
|
5849
5927
|
throw new Error("`date` must be a number in yyyymmdd format");
|
|
5850
5928
|
}
|
|
5851
|
-
const serviceIds = getServiceIdsByDate(query.date);
|
|
5929
|
+
const serviceIds = getServiceIdsByDate(query.date, options);
|
|
5852
5930
|
const tripSubquery = `SELECT DISTINCT trip_id FROM trips WHERE service_id IN (${serviceIds.map((id) => sqlString4.escape(id)).join(",")})`;
|
|
5853
5931
|
whereClauses.push(`trip_id IN (${tripSubquery})`);
|
|
5854
5932
|
}
|
|
@@ -5922,7 +6000,7 @@ function getTrips(query = {}, fields = [], orderBy2 = [], options = {}) {
|
|
|
5922
6000
|
let whereClause = "";
|
|
5923
6001
|
const orderByClause = formatOrderByClause(orderBy2);
|
|
5924
6002
|
const tripQueryOmitKeys = ["date"];
|
|
5925
|
-
|
|
6003
|
+
const tripQuery = omit7(query, tripQueryOmitKeys);
|
|
5926
6004
|
const whereClauses = Object.entries(tripQuery).map(
|
|
5927
6005
|
([key, value]) => formatWhereClause(key, value)
|
|
5928
6006
|
);
|
|
@@ -5930,7 +6008,7 @@ function getTrips(query = {}, fields = [], orderBy2 = [], options = {}) {
|
|
|
5930
6008
|
if (typeof query.date !== "number") {
|
|
5931
6009
|
throw new Error("`date` must be a number in yyyymmdd format");
|
|
5932
6010
|
}
|
|
5933
|
-
const serviceIds = getServiceIdsByDate(query.date);
|
|
6011
|
+
const serviceIds = getServiceIdsByDate(query.date, options);
|
|
5934
6012
|
whereClauses.push(
|
|
5935
6013
|
`service_id IN (${serviceIds.map((id) => sqlString5.escape(id)).join(",")})`
|
|
5936
6014
|
);
|
|
@@ -6212,6 +6290,7 @@ export {
|
|
|
6212
6290
|
closeDb,
|
|
6213
6291
|
deleteDb,
|
|
6214
6292
|
exportGtfs,
|
|
6293
|
+
generateFolderName,
|
|
6215
6294
|
getAgencies2 as getAgencies,
|
|
6216
6295
|
getAreas,
|
|
6217
6296
|
getAttributions,
|
|
@@ -6272,6 +6351,9 @@ export {
|
|
|
6272
6351
|
getVehiclePositions,
|
|
6273
6352
|
importGtfs,
|
|
6274
6353
|
openDb,
|
|
6354
|
+
prepDirectory,
|
|
6355
|
+
untildify,
|
|
6356
|
+
unzip,
|
|
6275
6357
|
updateGtfsRealtime
|
|
6276
6358
|
};
|
|
6277
6359
|
//# sourceMappingURL=index.js.map
|