gtfs 4.14.4 → 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.
@@ -125,15 +125,13 @@ function formatError(error) {
125
125
  return colors.red(errorMessage);
126
126
  }
127
127
 
128
- // src/lib/import.ts
128
+ // src/lib/import-gtfs.ts
129
129
  import { parse } from "csv-parse";
130
- import pluralize from "pluralize";
130
+ import pluralize2 from "pluralize";
131
131
  import stripBomStream from "strip-bom-stream";
132
132
  import { temporaryDirectory } from "tempy";
133
133
  import untildify3 from "untildify";
134
- import mapSeries from "promise-map-series";
135
- import GtfsRealtimeBindings from "gtfs-realtime-bindings";
136
- import sqlString2 from "sqlstring-sqlite";
134
+ import mapSeries2 from "promise-map-series";
137
135
 
138
136
  // src/models/gtfs-realtime/trip-updates.ts
139
137
  var tripUpdates = {
@@ -588,6 +586,12 @@ import {
588
586
  } from "lodash-es";
589
587
  import { feature, featureCollection } from "@turf/helpers";
590
588
 
589
+ // src/lib/import-gtfs-realtime.ts
590
+ import pluralize from "pluralize";
591
+ import GtfsRealtimeBindings from "gtfs-realtime-bindings";
592
+ import sqlString2 from "sqlstring-sqlite";
593
+ import mapSeries from "promise-map-series";
594
+
591
595
  // src/lib/utils.ts
592
596
  import sqlString from "sqlstring-sqlite";
593
597
  import Long from "long";
@@ -621,40 +625,10 @@ function convertLongTimeToDate(longDate) {
621
625
  return new Date(new Long(low, high, unsigned).toInt() * 1e3).toISOString();
622
626
  }
623
627
 
624
- // src/lib/import.ts
625
- var downloadGtfsRealtimeData = async (urlAndHeaders, task) => {
626
- task.log(`Downloading GTFS-Realtime from ${urlAndHeaders.url}`);
627
- const response = await fetch(urlAndHeaders.url, {
628
- method: "GET",
629
- headers: {
630
- ...urlAndHeaders.headers ?? {},
631
- "Accept-Encoding": "gzip"
632
- },
633
- signal: task.downloadTimeout ? AbortSignal.timeout(task.downloadTimeout) : void 0
634
- });
635
- if (response.status !== 200) {
636
- task.logWarning(
637
- `Unable to download GTFS-Realtime from ${urlAndHeaders.url}. Got status ${response.status}.`
638
- );
639
- return null;
640
- }
641
- const buffer = await response.arrayBuffer();
642
- const message = GtfsRealtimeBindings.transit_realtime.FeedMessage.decode(
643
- new Uint8Array(buffer)
644
- );
645
- return GtfsRealtimeBindings.transit_realtime.FeedMessage.toObject(message, {
646
- enums: String,
647
- longs: String,
648
- bytes: String,
649
- defaults: true,
650
- arrays: true,
651
- objects: true,
652
- oneofs: true
653
- });
654
- };
655
- function getDescendantProp(obj, defaultValue, source) {
656
- if (source === void 0) return defaultValue;
657
- const arr = source.split(".");
628
+ // src/lib/import-gtfs-realtime.ts
629
+ function getNestedProperty(obj, defaultValue, path2) {
630
+ if (path2 === void 0) return defaultValue;
631
+ const arr = path2.split(".");
658
632
  while (arr.length) {
659
633
  const nextKey = arr.shift();
660
634
  if (nextKey === void 0) {
@@ -683,7 +657,37 @@ function getDescendantProp(obj, defaultValue, source) {
683
657
  if (obj.__isLong__) return convertLongTimeToDate(obj);
684
658
  return obj;
685
659
  }
686
- var deleteExpiredRealtimeData = (config) => {
660
+ async function fetchGtfsRealtimeData(urlConfig, task) {
661
+ task.log(`Downloading GTFS-Realtime from ${urlConfig.url}`);
662
+ const response = await fetch(urlConfig.url, {
663
+ method: "GET",
664
+ headers: {
665
+ ...urlConfig.headers ?? {},
666
+ "Accept-Encoding": "gzip"
667
+ },
668
+ signal: task.downloadTimeout ? AbortSignal.timeout(task.downloadTimeout) : void 0
669
+ });
670
+ if (response.status !== 200) {
671
+ task.logWarning(
672
+ `Unable to download GTFS-Realtime from ${urlConfig.url}. Got status ${response.status}.`
673
+ );
674
+ return null;
675
+ }
676
+ const buffer = await response.arrayBuffer();
677
+ const message = GtfsRealtimeBindings.transit_realtime.FeedMessage.decode(
678
+ new Uint8Array(buffer)
679
+ );
680
+ return GtfsRealtimeBindings.transit_realtime.FeedMessage.toObject(message, {
681
+ enums: String,
682
+ longs: String,
683
+ bytes: String,
684
+ defaults: true,
685
+ arrays: true,
686
+ objects: true,
687
+ oneofs: true
688
+ });
689
+ }
690
+ function removeExpiredRealtimeData(config) {
687
691
  const log2 = log(config);
688
692
  const db = openDb(config);
689
693
  log2(`Removing expired GTFS-Realtime data`);
@@ -703,8 +707,8 @@ var deleteExpiredRealtimeData = (config) => {
703
707
  `DELETE FROM service_alert_targets WHERE expiration_timestamp <= strftime('%s','now')`
704
708
  ).run();
705
709
  log2(`Removed expired GTFS-Realtime data\r`, true);
706
- };
707
- var prepareRealtimeValue = (entity, column, task) => {
710
+ }
711
+ function prepareRealtimeFieldValue(entity, column, task) {
708
712
  if (column.name === "created_timestamp") {
709
713
  return task.currentTimestamp;
710
714
  }
@@ -712,54 +716,102 @@ var prepareRealtimeValue = (entity, column, task) => {
712
716
  return task.currentTimestamp + task.gtfsRealtimeExpirationSeconds;
713
717
  }
714
718
  return sqlString2.escape(
715
- getDescendantProp(entity, column.default, column.source)
719
+ getNestedProperty(entity, column.default, column.source)
716
720
  );
717
- };
718
- var updateRealtimeData = async (task) => {
721
+ }
722
+ async function processRealtimeAlerts(db, gtfsRealtimeData, task) {
723
+ task.log(`Download successful`);
724
+ let totalLineCount = 0;
725
+ for (const entity of gtfsRealtimeData.entity) {
726
+ const fieldValues = serviceAlerts.schema.map(
727
+ (column) => prepareRealtimeFieldValue(entity, column, task)
728
+ );
729
+ try {
730
+ db.prepare(
731
+ `REPLACE INTO ${serviceAlerts.filenameBase} (${serviceAlerts.schema.map((column) => column.name).join(", ")}) VALUES (${fieldValues.join(", ")})`
732
+ ).run();
733
+ } catch (error) {
734
+ task.logWarning("Import error: " + error.message);
735
+ }
736
+ const alertTargetArray = [];
737
+ for (const informedEntity of entity.alert.informedEntity) {
738
+ informedEntity.parent = entity;
739
+ const subValues = serviceAlertTargets.schema.map(
740
+ (column) => prepareRealtimeFieldValue(informedEntity, column, task)
741
+ );
742
+ alertTargetArray.push(`(${subValues.join(", ")})`);
743
+ totalLineCount++;
744
+ }
745
+ try {
746
+ db.prepare(
747
+ `REPLACE INTO ${serviceAlertTargets.filenameBase} (${serviceAlertTargets.schema.map((column) => column.name).join(", ")}) VALUES ${alertTargetArray.join(", ")}`
748
+ ).run();
749
+ } catch (error) {
750
+ task.logWarning("Import error: " + error.message);
751
+ }
752
+ task.log(`Importing - ${totalLineCount++} entries imported\r`, true);
753
+ }
754
+ }
755
+ async function processRealtimeTripUpdates(db, gtfsRealtimeData, task) {
756
+ task.log(`Download successful`);
757
+ let totalLineCount = 0;
758
+ for (const entity of gtfsRealtimeData.entity) {
759
+ const fieldValues = tripUpdates.schema.map(
760
+ (column) => prepareRealtimeFieldValue(entity, column, task)
761
+ );
762
+ try {
763
+ db.prepare(
764
+ `REPLACE INTO ${tripUpdates.filenameBase} (${tripUpdates.schema.map((column) => column.name).join(", ")}) VALUES (${fieldValues.join(", ")})`
765
+ ).run();
766
+ } catch (error) {
767
+ task.logWarning("Import error: " + error.message);
768
+ }
769
+ const stopTimeUpdateArray = [];
770
+ for (const stopTimeUpdate of entity.tripUpdate.stopTimeUpdate) {
771
+ stopTimeUpdate.parent = entity;
772
+ const subValues = stopTimeUpdates.schema.map(
773
+ (column) => prepareRealtimeFieldValue(stopTimeUpdate, column, task)
774
+ );
775
+ stopTimeUpdateArray.push(`(${subValues.join(", ")})`);
776
+ totalLineCount++;
777
+ }
778
+ try {
779
+ db.prepare(
780
+ `REPLACE INTO ${stopTimeUpdates.filenameBase} (${stopTimeUpdates.schema.map((column) => column.name).join(", ")}) VALUES ${stopTimeUpdateArray.join(", ")}`
781
+ ).run();
782
+ } catch (error) {
783
+ task.logWarning("Import error: " + error.message);
784
+ }
785
+ task.log(`Importing - ${totalLineCount++} entries imported\r`, true);
786
+ }
787
+ }
788
+ async function processRealtimeVehiclePositions(db, gtfsRealtimeData, task) {
789
+ task.log(`Download successful`);
790
+ let totalLineCount = 0;
791
+ for (const entity of gtfsRealtimeData.entity) {
792
+ const fieldValues = vehiclePositions.schema.map(
793
+ (column) => prepareRealtimeFieldValue(entity, column, task)
794
+ );
795
+ try {
796
+ db.prepare(
797
+ `REPLACE INTO ${vehiclePositions.filenameBase} (${vehiclePositions.schema.map((column) => column.name).join(", ")}) VALUES (${fieldValues.join(", ")})`
798
+ ).run();
799
+ } catch (error) {
800
+ task.logWarning("Import error: " + error.message);
801
+ }
802
+ task.log(`Importing - ${totalLineCount++} entries imported\r`, true);
803
+ }
804
+ }
805
+ async function updateGtfsRealtimeData(task) {
719
806
  if (task.realtimeAlerts === void 0 && task.realtimeTripUpdates === void 0 && task.realtimeVehiclePositions === void 0) {
720
807
  return;
721
808
  }
722
- const db = openDb({
723
- sqlitePath: task.sqlitePath
724
- });
809
+ const db = openDb({ sqlitePath: task.sqlitePath });
725
810
  if (task.realtimeAlerts?.url) {
726
811
  try {
727
- const gtfsRealtimeData = await downloadGtfsRealtimeData(
728
- task.realtimeAlerts,
729
- task
730
- );
731
- if (gtfsRealtimeData?.entity) {
732
- task.log(`Download successful`);
733
- let totalLineCount = 0;
734
- for (const entity of gtfsRealtimeData.entity) {
735
- const fieldValues = serviceAlerts.schema.map(
736
- (column) => prepareRealtimeValue(entity, column, task)
737
- );
738
- try {
739
- db.prepare(
740
- `REPLACE INTO ${serviceAlerts.filenameBase} (${serviceAlerts.schema.map((column) => column.name).join(", ")}) VALUES (${fieldValues.join(", ")})`
741
- ).run();
742
- } catch (error) {
743
- task.logWarning("Import error: " + error.message);
744
- }
745
- const alertTargetArray = [];
746
- for (const informedEntity of entity.alert.informedEntity) {
747
- informedEntity.parent = entity;
748
- const subValues = serviceAlertTargets.schema.map(
749
- (column) => prepareRealtimeValue(informedEntity, column, task)
750
- );
751
- alertTargetArray.push(`(${subValues.join(", ")})`);
752
- totalLineCount++;
753
- }
754
- try {
755
- db.prepare(
756
- `REPLACE INTO ${serviceAlertTargets.filenameBase} (${serviceAlertTargets.schema.map((column) => column.name).join(", ")}) VALUES ${alertTargetArray.join(", ")}`
757
- ).run();
758
- } catch (error) {
759
- task.logWarning("Import error: " + error.message);
760
- }
761
- task.log(`Importing - ${totalLineCount++} entries imported\r`, true);
762
- }
812
+ const alertsData = await fetchGtfsRealtimeData(task.realtimeAlerts, task);
813
+ if (alertsData?.entity) {
814
+ await processRealtimeAlerts(db, alertsData, task);
763
815
  }
764
816
  } catch (error) {
765
817
  if (task.ignoreErrors) {
@@ -771,42 +823,12 @@ var updateRealtimeData = async (task) => {
771
823
  }
772
824
  if (task.realtimeTripUpdates?.url) {
773
825
  try {
774
- const gtfsRealtimeData = await downloadGtfsRealtimeData(
826
+ const tripUpdatesData = await fetchGtfsRealtimeData(
775
827
  task.realtimeTripUpdates,
776
828
  task
777
829
  );
778
- if (gtfsRealtimeData?.entity) {
779
- task.log(`Download successful`);
780
- let totalLineCount = 0;
781
- for (const entity of gtfsRealtimeData.entity) {
782
- const fieldValues = tripUpdates.schema.map(
783
- (column) => prepareRealtimeValue(entity, column, task)
784
- );
785
- try {
786
- db.prepare(
787
- `REPLACE INTO ${tripUpdates.filenameBase} (${tripUpdates.schema.map((column) => column.name).join(", ")}) VALUES (${fieldValues.join(", ")})`
788
- ).run();
789
- } catch (error) {
790
- task.logWarning("Import error: " + error.message);
791
- }
792
- const stopTimeUpdateArray = [];
793
- for (const stopTimeUpdate of entity.tripUpdate.stopTimeUpdate) {
794
- stopTimeUpdate.parent = entity;
795
- const subValues = stopTimeUpdates.schema.map(
796
- (column) => prepareRealtimeValue(stopTimeUpdate, column, task)
797
- );
798
- stopTimeUpdateArray.push(`(${subValues.join(", ")})`);
799
- totalLineCount++;
800
- }
801
- try {
802
- db.prepare(
803
- `REPLACE INTO ${stopTimeUpdates.filenameBase} (${stopTimeUpdates.schema.map((column) => column.name).join(", ")}) VALUES ${stopTimeUpdateArray.join(", ")}`
804
- ).run();
805
- } catch (error) {
806
- task.logWarning("Import error: " + error.message);
807
- }
808
- task.log(`Importing - ${totalLineCount++} entries imported\r`, true);
809
- }
830
+ if (tripUpdatesData?.entity) {
831
+ await processRealtimeTripUpdates(db, tripUpdatesData, task);
810
832
  }
811
833
  } catch (error) {
812
834
  if (task.ignoreErrors) {
@@ -818,26 +840,12 @@ var updateRealtimeData = async (task) => {
818
840
  }
819
841
  if (task.realtimeVehiclePositions?.url) {
820
842
  try {
821
- const gtfsRealtimeData = await downloadGtfsRealtimeData(
843
+ const vehiclePositionsData = await fetchGtfsRealtimeData(
822
844
  task.realtimeVehiclePositions,
823
845
  task
824
846
  );
825
- if (gtfsRealtimeData?.entity) {
826
- task.log(`Download successful`);
827
- let totalLineCount = 0;
828
- for (const entity of gtfsRealtimeData.entity) {
829
- const fieldValues = vehiclePositions.schema.map(
830
- (column) => prepareRealtimeValue(entity, column, task)
831
- );
832
- try {
833
- db.prepare(
834
- `REPLACE INTO ${vehiclePositions.filenameBase} (${vehiclePositions.schema.map((column) => column.name).join(", ")}) VALUES (${fieldValues.join(", ")})`
835
- ).run();
836
- } catch (error) {
837
- task.logWarning("Import error: " + error.message);
838
- }
839
- task.log(`Importing - ${totalLineCount++} entries imported\r`, true);
840
- }
847
+ if (vehiclePositionsData?.entity) {
848
+ await processRealtimeVehiclePositions(db, vehiclePositionsData, task);
841
849
  }
842
850
  } catch (error) {
843
851
  if (task.ignoreErrors) {
@@ -848,7 +856,7 @@ var updateRealtimeData = async (task) => {
848
856
  }
849
857
  }
850
858
  task.log(`GTFS-Realtime data import complete`);
851
- };
859
+ }
852
860
  async function updateGtfsRealtime(initialConfig) {
853
861
  const config = setDefaultConfig(initialConfig);
854
862
  validateConfigForImport(config);
@@ -865,7 +873,7 @@ async function updateGtfsRealtime(initialConfig) {
865
873
  true
866
874
  )} using SQLite database at ${config.sqlitePath}`
867
875
  );
868
- deleteExpiredRealtimeData(config);
876
+ removeExpiredRealtimeData(config);
869
877
  await mapSeries(config.agencies, async (agency2) => {
870
878
  try {
871
879
  const task = {
@@ -881,7 +889,7 @@ async function updateGtfsRealtime(initialConfig) {
881
889
  logWarning: logWarning2,
882
890
  logError: logError2
883
891
  };
884
- await updateRealtimeData(task);
892
+ await updateGtfsRealtimeData(task);
885
893
  } catch (error) {
886
894
  if (config.ignoreErrors) {
887
895
  logError2(error.message);
@@ -899,21 +907,39 @@ async function updateGtfsRealtime(initialConfig) {
899
907
  `
900
908
  );
901
909
  } catch (error) {
902
- if (error?.code === "SQLITE_CANTOPEN") {
903
- logError2(
904
- `Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
905
- );
906
- }
907
- throw error;
910
+ handleDatabaseError(error, config, logError2);
908
911
  }
909
912
  }
913
+ function handleDatabaseError(error, config, logError2) {
914
+ if (error?.code === "SQLITE_CANTOPEN") {
915
+ logError2(
916
+ `Unable to open sqlite database "${config.sqlitePath}" defined as \`sqlitePath\` config.json. Ensure the parent directory exists or remove \`sqlitePath\` from config.json.`
917
+ );
918
+ }
919
+ throw error;
920
+ }
921
+
922
+ // src/lib/import-gtfs.ts
923
+ var TIME_COLUMN_NAMES = [
924
+ "start_time",
925
+ "end_time",
926
+ "arrival_time",
927
+ "departure_time",
928
+ "prior_notice_last_time",
929
+ "prior_notice_start_time",
930
+ "start_pickup_drop_off_window"
931
+ ];
932
+ var TIME_COLUMN_PAIRS = TIME_COLUMN_NAMES.map((name) => [
933
+ name,
934
+ name.endsWith("time") ? `${name}stamp` : `${name}_timestamp`
935
+ ]);
910
936
 
911
937
  // src/lib/export.ts
912
938
  import { without, compact as compact2 } from "lodash-es";
913
- import pluralize2 from "pluralize";
939
+ import pluralize3 from "pluralize";
914
940
  import { stringify } from "csv-stringify";
915
941
  import sqlString3 from "sqlstring-sqlite";
916
- import mapSeries2 from "promise-map-series";
942
+ import mapSeries3 from "promise-map-series";
917
943
  import untildify4 from "untildify";
918
944
 
919
945
  // src/lib/advancedQuery.ts