gtfs 4.2.0 → 4.3.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.
Files changed (48) hide show
  1. package/.github/workflows/nodejs.yml +3 -3
  2. package/CHANGELOG.md +11 -0
  3. package/README.md +62 -13
  4. package/lib/db.js +14 -15
  5. package/lib/file-utils.js +7 -9
  6. package/lib/import.js +16 -6
  7. package/models/gtfs/agency.js +1 -0
  8. package/models/gtfs/areas.js +1 -0
  9. package/models/gtfs/attributions.js +5 -1
  10. package/models/gtfs/calendar-dates.js +1 -0
  11. package/models/gtfs/calendar.js +1 -0
  12. package/models/gtfs/fare-attributes.js +2 -0
  13. package/models/gtfs/fare-leg-rules.js +5 -0
  14. package/models/gtfs/fare-products.js +2 -0
  15. package/models/gtfs/fare-rules.js +5 -0
  16. package/models/gtfs/fare-transfer-rules.js +4 -0
  17. package/models/gtfs/frequencies.js +1 -0
  18. package/models/gtfs/levels.js +1 -0
  19. package/models/gtfs/pathways.js +3 -0
  20. package/models/gtfs/routes.js +3 -0
  21. package/models/gtfs/shapes.js +1 -0
  22. package/models/gtfs/stop-areas.js +2 -0
  23. package/models/gtfs/stop-times.js +2 -0
  24. package/models/gtfs/stops.js +3 -0
  25. package/models/gtfs/transfers.js +6 -0
  26. package/models/gtfs/translations.js +2 -0
  27. package/models/gtfs/trips.js +5 -0
  28. package/models/gtfs-plus/calendar-attributes.js +1 -0
  29. package/models/gtfs-plus/directions.js +1 -0
  30. package/models/gtfs-plus/route-attributes.js +1 -0
  31. package/models/gtfs-plus/stop-attributes.js +1 -0
  32. package/models/gtfs-ride/board-alight.js +2 -0
  33. package/models/gtfs-ride/rider-trip.js +5 -0
  34. package/models/gtfs-ride/ridership.js +5 -0
  35. package/models/gtfs-ride/trip-capacity.js +2 -0
  36. package/models/non-standard/timetable-notes-references.js +5 -0
  37. package/models/non-standard/timetable-notes.js +1 -0
  38. package/models/non-standard/timetable-pages.js +1 -0
  39. package/models/non-standard/timetable-stop-order.js +3 -0
  40. package/models/non-standard/timetables.js +3 -0
  41. package/models/non-standard/trips-dated-vehicle-journey.js +1 -0
  42. package/models/ods/deadhead-times.js +4 -0
  43. package/models/ods/deadheads.js +8 -0
  44. package/models/ods/ops-locations.js +1 -0
  45. package/models/ods/run-events.js +4 -0
  46. package/package.json +7 -7
  47. package/test/mocha/export-gtfs.js +6 -1
  48. package/test/mocha/import-gtfs.js +32 -5
@@ -8,12 +8,12 @@ jobs:
8
8
 
9
9
  strategy:
10
10
  matrix:
11
- node-version: [14.x, 16.x, 18.x]
11
+ node-version: [16.x, 18.x, 20.x]
12
12
 
13
13
  steps:
14
- - uses: actions/checkout@v1
14
+ - uses: actions/checkout@v3
15
15
  - name: Use Node.js ${{ matrix.node-version }}
16
- uses: actions/setup-node@v1
16
+ uses: actions/setup-node@v3
17
17
  with:
18
18
  node-version: ${{ matrix.node-version }}
19
19
  - name: npm install and test
package/CHANGELOG.md CHANGED
@@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [4.3.0] - 2023-05-04
9
+
10
+ ### Updated
11
+
12
+ - Updated Readme to add closeDb documentation
13
+ - Use node-stream-zip instead of unzipper
14
+
15
+ ### Added
16
+
17
+ - Support for prefixes when importing multiple GTFS files
18
+
8
19
  ## [4.2.0] - 2023-04-13
9
20
 
10
21
  ### Updated
package/README.md CHANGED
@@ -176,11 +176,21 @@ Copy `config-sample.json` to `config.json` and then add your projects configurat
176
176
 
177
177
  For GTFS files that contain more than one agency, you only need to list each GTFS file once in the `agencies` array, not once per agency that it contains.
178
178
 
179
- To find an agency's GTFS file, visit [transitfeeds.com](http://transitfeeds.com). You can use the
180
- URL from the agency's website or you can use a URL generated from the transitfeeds.com
181
- API along with your API token.
179
+ To find an agency's GTFS file, visit [transitfeeds.com](http://transitfeeds.com).
182
180
 
183
- - Specify a download URL:
181
+ #### agencies options
182
+
183
+ | option | type | description |
184
+ | ----------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------- |
185
+ | `url` | string | The URL to a zipped GTFS file. Required if `path` not present. |
186
+ | `path` | string | A path to a zipped GTFS file or a directory of unzipped .txt files. Required if `url` is not present. |
187
+ | `headers` | object | An object of HTTP headers in key:value format to use when fetching GTFS from the `url` specified. Optional. |
188
+ | `prefix` | string | A prefix to be added to every ID field maintain uniqueness when importing multiple GTFS from multiple agencies. Optional. |
189
+ | `exclude` | array | An array of GTFS file names (without `.txt`) to exclude when importing. Optional. |
190
+ | `realtimeUrls` | array | An array of GTFS-Realtime urls to import. Optional. |
191
+ | `realtimeHeaders` | array | An object of HTTP headers in key:value format to use when fetching GTFS-Realtime data from the `realtimeUrls` specified. Optional. |
192
+
193
+ - Specify a `url` to download GTFS:
184
194
 
185
195
  ```json
186
196
  {
@@ -192,7 +202,7 @@ API along with your API token.
192
202
  }
193
203
  ```
194
204
 
195
- - Specify a download URL with custom headers:
205
+ - Specify a download URL with custom headers using the `headers` field:
196
206
 
197
207
  ```json
198
208
  {
@@ -208,7 +218,7 @@ API along with your API token.
208
218
  }
209
219
  ```
210
220
 
211
- - Specify a path to a zipped GTFS file:
221
+ - Specify a `path` to a zipped GTFS file:
212
222
 
213
223
  ```json
214
224
  {
@@ -220,7 +230,7 @@ API along with your API token.
220
230
  }
221
231
  ```
222
232
 
223
- - Specify a path to an unzipped GTFS file:
233
+ - Specify a `path` to an unzipped GTFS file:
224
234
 
225
235
  ```json
226
236
  {
@@ -232,7 +242,7 @@ API along with your API token.
232
242
  }
233
243
  ```
234
244
 
235
- - Exclude files - if you don't want all GTFS files to be imported, you can specify an array of files to exclude.
245
+ - If you don't want all GTFS files to be imported, you can specify an array of files to `exclude`. This can save a lot of time for larger GTFS.
236
246
 
237
247
  ```json
238
248
  {
@@ -279,6 +289,23 @@ API along with your API token.
279
289
  }
280
290
  ```
281
291
 
292
+ - When importing multiple agencies their IDs may overlap. Specify a `prefix` to be added to every ID field to maintain uniqueness.
293
+
294
+ ```json
295
+ {
296
+ "agencies": [
297
+ {
298
+ "path": "/path/to/the/gtfs.zip",
299
+ "prefix": "A"
300
+ },
301
+ {
302
+ "path": "/path/to/the/othergtfs.zip",
303
+ "prefix": 10000
304
+ }
305
+ ]
306
+ }
307
+ ```
308
+
282
309
  ### csvOptions
283
310
 
284
311
  {Object} Add options to be passed to [`csv-parse`](https://csv.js.org/parse/) with the key `csvOptions`. This is an optional parameter.
@@ -506,13 +533,29 @@ Most query methods accept three optional arguments: `query`, `fields`, `sortBy`
506
533
 
507
534
  For more advanced queries, you can use `advancedQuery` or raw SQL queries using query method from [better-sqlite3](#raw-sqlite-query).
508
535
 
509
- ### Setup
536
+ ### Database Setup
510
537
 
511
- To use any of the query methods, first open the database before making any queries:
538
+ To use any of the query methods, first open the database using `openDb` before making any queries:
512
539
 
513
540
  ```js
514
541
  import { openDb } from 'gtfs';
542
+ import { readFile } from 'fs/promises';
543
+ const config = JSON.parse(
544
+ await readFile(new URL('./config.json', import.meta.url))
545
+ );
546
+ const db = openDb(config);
547
+ ```
548
+
549
+ If you no longer need a database (especially if using an in-memory database) you can use `closeDb`:
550
+
551
+ ```js
552
+ import { closeDb, openDb } from 'gtfs';
515
553
  const db = openDb(config);
554
+
555
+ // Do some stuff here
556
+
557
+ // Close database connection when done.
558
+ closeDb(db);
516
559
  ```
517
560
 
518
561
  ### Examples
@@ -520,7 +563,7 @@ const db = openDb(config);
520
563
  For example, to get a list of all routes with just `route_id`, `route_short_name` and `route_color` sorted by `route_short_name`:
521
564
 
522
565
  ```js
523
- import { openDb, getRoutes } from 'gtfs';
566
+ import { closeDb, openDb, getRoutes } from 'gtfs';
524
567
  import { readFile } from 'fs/promises';
525
568
  const config = JSON.parse(
526
569
  await readFile(new URL('./config.json', import.meta.url))
@@ -533,12 +576,14 @@ const routes = getRoutes(
533
576
  [['route_short_name', 'ASC']], // Sort by this field and direction
534
577
  { db: db } // Options for the query. Can specify which database to use if more than one are open
535
578
  );
579
+
580
+ closeDb(db);
536
581
  ```
537
582
 
538
583
  To get a list of all trip_ids for a specific route:
539
584
 
540
585
  ```js
541
- import { openDb, getTrips } from 'gtfs';
586
+ import { closeDb, openDb, getTrips } from 'gtfs';
542
587
  import { readFile } from 'fs/promises';
543
588
  const config = JSON.parse(
544
589
  await readFile(new URL('./config.json', import.meta.url))
@@ -551,12 +596,14 @@ const trips = getTrips(
551
596
  },
552
597
  ['trip_id']
553
598
  );
599
+
600
+ closeDb(db);
554
601
  ```
555
602
 
556
603
  To get a few stops by specific stop_ids:
557
604
 
558
605
  ```js
559
- import { openDb, getStops } from 'gtfs';
606
+ import { closeDb, openDb, getStops } from 'gtfs';
560
607
  import { readFile } from 'fs/promises';
561
608
  const config = JSON.parse(await readFile(new URL('./config.json', import.meta.url)));
562
609
 
@@ -570,6 +617,8 @@ const stops = getStops(
570
617
  ]
571
618
  }
572
619
  );
620
+
621
+ closeDb(db);
573
622
  ```
574
623
 
575
624
  ### Static GTFS Files
package/lib/db.js CHANGED
@@ -4,21 +4,26 @@ import untildify from 'untildify';
4
4
  import { setDefaultConfig } from './utils.js';
5
5
  const dbs = {};
6
6
 
7
- const getOrCreateDbConnection = (sqlitePath) => {
8
- if (dbs[sqlitePath]) {
9
- return dbs[sqlitePath];
10
- }
11
-
7
+ function setupDb(sqlitePath) {
12
8
  const db = new Database(untildify(sqlitePath));
13
- setupDb(db);
14
-
9
+ db.pragma('journal_mode = OFF');
10
+ db.pragma('synchronous = OFF');
11
+ db.pragma('temp_store = MEMORY');
15
12
  dbs[sqlitePath] = db;
13
+
16
14
  return db;
17
- };
15
+ }
18
16
 
19
17
  export function openDb(config) {
18
+ // If config is passed, use that to open or return db
20
19
  if (config) {
21
- return getOrCreateDbConnection(setDefaultConfig(config).sqlitePath);
20
+ const { sqlitePath } = setDefaultConfig(config);
21
+
22
+ if (dbs[sqlitePath]) {
23
+ return dbs[sqlitePath];
24
+ }
25
+
26
+ return setupDb(sqlitePath);
22
27
  }
23
28
 
24
29
  // If only one db connection already exists, use it
@@ -36,12 +41,6 @@ export function openDb(config) {
36
41
  throw new Error('Unable to find database connection.');
37
42
  }
38
43
 
39
- export function setupDb(db) {
40
- db.pragma('journal_mode = OFF');
41
- db.pragma('synchronous = OFF');
42
- db.pragma('temp_store = MEMORY');
43
- }
44
-
45
44
  export function closeDb(db) {
46
45
  if (Object.keys(dbs).length === 0) {
47
46
  throw new Error(
package/lib/file-utils.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import path from 'node:path';
2
- import { createReadStream, existsSync } from 'node:fs';
2
+ import { existsSync } from 'node:fs';
3
3
  import { mkdir, readFile, rm } from 'node:fs/promises';
4
4
  import { omit, snakeCase } from 'lodash-es';
5
5
  import sanitize from 'sanitize-filename';
6
6
  import untildify from 'untildify';
7
- import { Extract } from 'unzipper';
7
+ import StreamZip from 'node-stream-zip';
8
8
 
9
9
  /*
10
10
  * Attempt to parse any config JSON file and read values from CLI.
@@ -91,13 +91,11 @@ export async function prepDirectory(exportPath) {
91
91
  /*
92
92
  * Unzip a zipfile into a specified directory
93
93
  */
94
- export function unzip(zipfilePath, exportPath) {
95
- /* eslint-disable new-cap */
96
- return createReadStream(zipfilePath)
97
- .pipe(Extract({ path: exportPath }))
98
- .on('entry', (entry) => entry.autodrain())
99
- .promise();
100
- /* eslint-enable new-cap */
94
+ export async function unzip(zipfilePath, exportPath) {
95
+ /* eslint-disable-next-line new-cap */
96
+ const zip = new StreamZip.async({ file: zipfilePath });
97
+ await zip.extract(null, exportPath);
98
+ await zip.close();
101
99
  }
102
100
 
103
101
  /*
package/lib/import.js CHANGED
@@ -446,23 +446,32 @@ const importLines = (task, lines, model, totalLineCount) => {
446
446
  }
447
447
 
448
448
  const linesToImportCount = lines.length;
449
- const fieldNames = model.schema
450
- .filter((column) => column.name !== 'id')
451
- .map((column) => column.name);
449
+ const columns = model.schema.filter((column) => column.name !== 'id');
452
450
  const placeholders = [];
453
451
  const values = [];
454
452
 
455
453
  while (lines.length > 0) {
456
454
  const line = lines.pop();
457
- placeholders.push(`(${fieldNames.map(() => '?').join(', ')})`);
458
- values.push(...fieldNames.map((fieldName) => line[fieldName]));
455
+ placeholders.push(`(${columns.map(() => '?').join(', ')})`);
456
+ values.push(
457
+ ...columns.map((column) => {
458
+ if (task.prefix !== undefined && column.prefix === true) {
459
+ // Add prefixes to field values if needed
460
+ return `${task.prefix}${line[column.name]}`;
461
+ }
462
+
463
+ return line[column.name];
464
+ })
465
+ );
459
466
  }
460
467
 
461
468
  try {
462
469
  db.prepare(
463
470
  `INSERT ${task.ignoreDuplicates ? 'OR IGNORE' : ''} INTO ${
464
471
  model.filenameBase
465
- } (${fieldNames.join(', ')}) VALUES ${placeholders.join(',')}`
472
+ } (${columns
473
+ .map((column) => column.name)
474
+ .join(', ')}) VALUES ${placeholders.join(',')}`
466
475
  ).run(...values);
467
476
  } catch (error) {
468
477
  task.warn(
@@ -588,6 +597,7 @@ export async function importGtfs(initialConfig) {
588
597
  csvOptions: config.csvOptions || {},
589
598
  ignoreDuplicates: config.ignoreDuplicates,
590
599
  sqlitePath: config.sqlitePath,
600
+ prefix: agency.prefix,
591
601
  log,
592
602
  warn: logWarning,
593
603
  error: logError,
@@ -5,6 +5,7 @@ const model = {
5
5
  name: 'agency_id',
6
6
  type: 'varchar(255)',
7
7
  primary: true,
8
+ prefix: true,
8
9
  },
9
10
  {
10
11
  name: 'agency_name',
@@ -6,6 +6,7 @@ const model = {
6
6
  type: 'varchar(255)',
7
7
  required: true,
8
8
  primary: true,
9
+ prefix: true,
9
10
  },
10
11
  {
11
12
  name: 'area_name',
@@ -6,18 +6,22 @@ const model = {
6
6
  type: 'varchar(255)',
7
7
  primary: true,
8
8
  required: true,
9
+ prefix: true,
9
10
  },
10
11
  {
11
12
  name: 'agency_id',
12
- type: 'varchar(255)',
13
+ type: 'varchar(255)',
14
+ prefix: true,
13
15
  },
14
16
  {
15
17
  name: 'route_id',
16
18
  type: 'varchar(255)',
19
+ prefix: true,
17
20
  },
18
21
  {
19
22
  name: 'trip_id',
20
23
  type: 'varchar(255)',
24
+ prefix: true,
21
25
  },
22
26
  {
23
27
  name: 'organization_name',
@@ -6,6 +6,7 @@ const model = {
6
6
  type: 'varchar(255)',
7
7
  required: true,
8
8
  primary: true,
9
+ prefix: true,
9
10
  },
10
11
  {
11
12
  name: 'date',
@@ -6,6 +6,7 @@ const model = {
6
6
  type: 'varchar(255)',
7
7
  required: true,
8
8
  primary: true,
9
+ prefix: true,
9
10
  },
10
11
  {
11
12
  name: 'monday',
@@ -6,6 +6,7 @@ const model = {
6
6
  type: 'varchar(255)',
7
7
  required: true,
8
8
  primary: true,
9
+ prefix: true,
9
10
  },
10
11
  {
11
12
  name: 'price',
@@ -33,6 +34,7 @@ const model = {
33
34
  {
34
35
  name: 'agency_id',
35
36
  type: 'varchar(255)',
37
+ prefix: true,
36
38
  },
37
39
  {
38
40
  name: 'transfer_duration',
@@ -4,27 +4,32 @@ const model = {
4
4
  {
5
5
  name: 'leg_group_id',
6
6
  type: 'varchar(255)',
7
+ prefix: true,
7
8
  },
8
9
  {
9
10
  name: 'network_id',
10
11
  type: 'varchar(255)',
11
12
  primary: true,
13
+ prefix: true,
12
14
  },
13
15
  {
14
16
  name: 'from_area_id',
15
17
  type: 'varchar(255)',
16
18
  primary: true,
19
+ prefix: true,
17
20
  },
18
21
  {
19
22
  name: 'to_area_id',
20
23
  type: 'varchar(255)',
21
24
  primary: true,
25
+ prefix: true,
22
26
  },
23
27
  {
24
28
  name: 'fare_product_id',
25
29
  type: 'varchar(255)',
26
30
  required: true,
27
31
  primary: true,
32
+ prefix: true,
28
33
  },
29
34
  ],
30
35
  };
@@ -6,6 +6,7 @@ const model = {
6
6
  type: 'varchar(255)',
7
7
  required: true,
8
8
  primary: true,
9
+ prefix: true,
9
10
  },
10
11
  {
11
12
  name: 'fare_product_name',
@@ -15,6 +16,7 @@ const model = {
15
16
  name: 'fare_media_id',
16
17
  type: 'varchar(255)',
17
18
  primary: true,
19
+ prefix: true,
18
20
  },
19
21
  {
20
22
  name: 'amount',
@@ -5,22 +5,27 @@ const model = {
5
5
  name: 'fare_id',
6
6
  type: 'varchar(255)',
7
7
  required: true,
8
+ prefix: true,
8
9
  },
9
10
  {
10
11
  name: 'route_id',
11
12
  type: 'varchar(255)',
13
+ prefix: true,
12
14
  },
13
15
  {
14
16
  name: 'origin_id',
15
17
  type: 'varchar(255)',
18
+ prefix: true,
16
19
  },
17
20
  {
18
21
  name: 'destination_id',
19
22
  type: 'varchar(255)',
23
+ prefix: true,
20
24
  },
21
25
  {
22
26
  name: 'contains_id',
23
27
  type: 'varchar(255)',
28
+ prefix: true,
24
29
  },
25
30
  ],
26
31
  };
@@ -5,11 +5,13 @@ const model = {
5
5
  name: 'from_leg_group_id',
6
6
  type: 'varchar(255)',
7
7
  primary: true,
8
+ prefix: true,
8
9
  },
9
10
  {
10
11
  name: 'to_leg_group_id',
11
12
  type: 'varchar(255)',
12
13
  primary: true,
14
+ prefix: true,
13
15
  },
14
16
  {
15
17
  name: 'transfer_count',
@@ -20,6 +22,7 @@ const model = {
20
22
  {
21
23
  name: 'transfer_id',
22
24
  type: 'varchar(255)',
25
+ prefix: true,
23
26
  },
24
27
  {
25
28
  name: 'duration_limit',
@@ -44,6 +47,7 @@ const model = {
44
47
  name: 'fare_product_id',
45
48
  type: 'varchar(255)',
46
49
  primary: true,
50
+ prefix: true,
47
51
  },
48
52
  ],
49
53
  };
@@ -6,6 +6,7 @@ const model = {
6
6
  type: 'varchar(255)',
7
7
  required: true,
8
8
  primary: true,
9
+ prefix: true,
9
10
  },
10
11
  {
11
12
  name: 'start_time',
@@ -6,6 +6,7 @@ const model = {
6
6
  type: 'varchar(255)',
7
7
  primary: true,
8
8
  required: true,
9
+ prefix: true,
9
10
  },
10
11
  {
11
12
  name: 'level_index',
@@ -6,16 +6,19 @@ const model = {
6
6
  type: 'varchar(255)',
7
7
  primary: true,
8
8
  required: true,
9
+ prefix: true,
9
10
  },
10
11
  {
11
12
  name: 'from_stop_id',
12
13
  type: 'varchar(255)',
13
14
  required: true,
15
+ prefix: true,
14
16
  },
15
17
  {
16
18
  name: 'to_stop_id',
17
19
  type: 'varchar(255)',
18
20
  required: true,
21
+ prefix: true,
19
22
  },
20
23
  {
21
24
  name: 'pathway_mode',
@@ -6,10 +6,12 @@ const model = {
6
6
  type: 'varchar(255)',
7
7
  primary: true,
8
8
  required: true,
9
+ prefix: true,
9
10
  },
10
11
  {
11
12
  name: 'agency_id',
12
13
  type: 'varchar(255)',
14
+ prefix: true,
13
15
  },
14
16
  {
15
17
  name: 'route_short_name',
@@ -68,6 +70,7 @@ const model = {
68
70
  {
69
71
  name: 'network_id',
70
72
  type: 'varchar(255)',
73
+ prefix: true,
71
74
  },
72
75
  ],
73
76
  };
@@ -6,6 +6,7 @@ const model = {
6
6
  type: 'varchar(255)',
7
7
  required: true,
8
8
  primary: true,
9
+ prefix: true,
9
10
  },
10
11
  {
11
12
  name: 'shape_pt_lat',
@@ -5,11 +5,13 @@ const model = {
5
5
  name: 'area_id',
6
6
  type: 'varchar(255)',
7
7
  required: true,
8
+ prefix: true,
8
9
  },
9
10
  {
10
11
  name: 'stop_id',
11
12
  type: 'varchar(255)',
12
13
  required: true,
14
+ prefix: true,
13
15
  },
14
16
  ],
15
17
  };
@@ -6,6 +6,7 @@ const model = {
6
6
  type: 'varchar(255)',
7
7
  required: true,
8
8
  primary: true,
9
+ prefix: true,
9
10
  },
10
11
  {
11
12
  name: 'arrival_time',
@@ -29,6 +30,7 @@ const model = {
29
30
  name: 'stop_id',
30
31
  type: 'varchar(255)',
31
32
  required: true,
33
+ prefix: true,
32
34
  },
33
35
  {
34
36
  name: 'stop_sequence',
@@ -6,6 +6,7 @@ const model = {
6
6
  type: 'varchar(255)',
7
7
  primary: true,
8
8
  required: true,
9
+ prefix: true,
9
10
  },
10
11
  {
11
12
  name: 'stop_code',
@@ -41,6 +42,7 @@ const model = {
41
42
  {
42
43
  name: 'zone_id',
43
44
  type: 'varchar(255)',
45
+ prefix: true,
44
46
  },
45
47
  {
46
48
  name: 'stop_url',
@@ -70,6 +72,7 @@ const model = {
70
72
  {
71
73
  name: 'level_id',
72
74
  type: 'varchar(255)',
75
+ prefix: true,
73
76
  },
74
77
  {
75
78
  name: 'platform_code',
@@ -5,31 +5,37 @@ const model = {
5
5
  name: 'from_stop_id',
6
6
  type: 'varchar(255)',
7
7
  primary: true,
8
+ prefix: true,
8
9
  },
9
10
  {
10
11
  name: 'to_stop_id',
11
12
  type: 'varchar(255)',
12
13
  primary: true,
14
+ prefix: true,
13
15
  },
14
16
  {
15
17
  name: 'from_route_id',
16
18
  type: 'varchar(255)',
17
19
  primary: true,
20
+ prefix: true,
18
21
  },
19
22
  {
20
23
  name: 'to_route_id',
21
24
  type: 'varchar(255)',
22
25
  primary: true,
26
+ prefix: true,
23
27
  },
24
28
  {
25
29
  name: 'from_trip_id',
26
30
  type: 'varchar(255)',
27
31
  primary: true,
32
+ prefix: true,
28
33
  },
29
34
  {
30
35
  name: 'to_trip_id',
31
36
  type: 'varchar(255)',
32
37
  primary: true,
38
+ prefix: true,
33
39
  },
34
40
  {
35
41
  name: 'transfer_type',
@@ -28,11 +28,13 @@ const model = {
28
28
  name: 'record_id',
29
29
  type: 'varchar(255)',
30
30
  primary: true,
31
+ prefix: true,
31
32
  },
32
33
  {
33
34
  name: 'record_sub_id',
34
35
  type: 'varchar(255)',
35
36
  primary: true,
37
+ prefix: true,
36
38
  },
37
39
  {
38
40
  name: 'field_value',
@@ -6,18 +6,21 @@ const model = {
6
6
  type: 'varchar(255)',
7
7
  required: true,
8
8
  index: true,
9
+ prefix: true,
9
10
  },
10
11
  {
11
12
  name: 'service_id',
12
13
  type: 'varchar(255)',
13
14
  required: true,
14
15
  index: true,
16
+ prefix: true,
15
17
  },
16
18
  {
17
19
  name: 'trip_id',
18
20
  type: 'varchar(255)',
19
21
  primary: true,
20
22
  required: true,
23
+ prefix: true,
21
24
  },
22
25
  {
23
26
  name: 'trip_headsign',
@@ -40,11 +43,13 @@ const model = {
40
43
  name: 'block_id',
41
44
  type: 'varchar(255)',
42
45
  index: true,
46
+ prefix: true,
43
47
  },
44
48
  {
45
49
  name: 'shape_id',
46
50
  type: 'varchar(255)',
47
51
  index: true,
52
+ prefix: true,
48
53
  },
49
54
  {
50
55
  name: 'wheelchair_accessible',
@@ -7,6 +7,7 @@ const model = {
7
7
  name: 'service_id',
8
8
  type: 'varchar(255)',
9
9
  primary: true,
10
+ prefix: true,
10
11
  },
11
12
  {
12
13
  name: 'service_description',
@@ -8,6 +8,7 @@ const model = {
8
8
  type: 'varchar(255)',
9
9
  required: true,
10
10
  primary: true,
11
+ prefix: true,
11
12
  },
12
13
  {
13
14
  name: 'direction_id',
@@ -7,6 +7,7 @@ const model = {
7
7
  name: 'route_id',
8
8
  type: 'varchar(255)',
9
9
  primary: true,
10
+ prefix: true,
10
11
  },
11
12
  {
12
13
  name: 'category',
@@ -8,6 +8,7 @@ const model = {
8
8
  type: 'varchar(255)',
9
9
  required: true,
10
10
  primary: true,
11
+ prefix: true,
11
12
  },
12
13
  {
13
14
  name: 'accessibility_id',
@@ -8,12 +8,14 @@ const model = {
8
8
  type: 'varchar(255)',
9
9
  required: true,
10
10
  index: true,
11
+ prefix: true,
11
12
  },
12
13
  {
13
14
  name: 'stop_id',
14
15
  type: 'varchar(255)',
15
16
  required: true,
16
17
  index: true,
18
+ prefix: true,
17
19
  },
18
20
  {
19
21
  name: 'stop_sequence',
@@ -7,21 +7,25 @@ const model = {
7
7
  name: 'rider_id',
8
8
  type: 'varchar(255)',
9
9
  primary: true,
10
+ prefix: true,
10
11
  },
11
12
  {
12
13
  name: 'agency_id',
13
14
  type: 'varchar(255)',
14
15
  index: true,
16
+ prefix: true,
15
17
  },
16
18
  {
17
19
  name: 'trip_id',
18
20
  type: 'varchar(255)',
19
21
  index: true,
22
+ prefix: true,
20
23
  },
21
24
  {
22
25
  name: 'boarding_stop_id',
23
26
  type: 'varchar(255)',
24
27
  index: true,
28
+ prefix: true,
25
29
  },
26
30
  {
27
31
  name: 'boarding_stop_sequence',
@@ -33,6 +37,7 @@ const model = {
33
37
  name: 'alighting_stop_id',
34
38
  type: 'varchar(255)',
35
39
  index: true,
40
+ prefix: true,
36
41
  },
37
42
  {
38
43
  name: 'alighting_stop_sequence',
@@ -47,6 +47,7 @@ const model = {
47
47
  name: 'service_id',
48
48
  type: 'varchar(255)',
49
49
  index: true,
50
+ prefix: true,
50
51
  },
51
52
  {
52
53
  name: 'monday',
@@ -94,11 +95,13 @@ const model = {
94
95
  name: 'agency_id',
95
96
  type: 'varchar(255)',
96
97
  index: true,
98
+ prefix: true,
97
99
  },
98
100
  {
99
101
  name: 'route_id',
100
102
  type: 'varchar(255)',
101
103
  index: true,
104
+ prefix: true,
102
105
  },
103
106
  {
104
107
  name: 'direction_id',
@@ -110,10 +113,12 @@ const model = {
110
113
  {
111
114
  name: 'trip_id',
112
115
  type: 'varchar(255)',
116
+ prefix: true,
113
117
  },
114
118
  {
115
119
  name: 'stop_id',
116
120
  type: 'varchar(255)',
121
+ prefix: true,
117
122
  },
118
123
  ],
119
124
  };
@@ -7,11 +7,13 @@ const model = {
7
7
  name: 'agency_id',
8
8
  type: 'varchar(255)',
9
9
  index: true,
10
+ prefix: true,
10
11
  },
11
12
  {
12
13
  name: 'trip_id',
13
14
  type: 'varchar(255)',
14
15
  index: true,
16
+ prefix: true,
15
17
  },
16
18
  {
17
19
  name: 'service_date',
@@ -5,26 +5,31 @@ const model = {
5
5
  {
6
6
  name: 'note_id',
7
7
  type: 'varchar(255)',
8
+ prefix: true,
8
9
  },
9
10
  {
10
11
  name: 'timetable_id',
11
12
  type: 'varchar(255)',
12
13
  index: true,
14
+ prefix: true,
13
15
  },
14
16
  {
15
17
  name: 'route_id',
16
18
  type: 'varchar(255)',
17
19
  index: true,
20
+ prefix: true,
18
21
  },
19
22
  {
20
23
  name: 'trip_id',
21
24
  type: 'varchar(255)',
22
25
  index: true,
26
+ prefix: true,
23
27
  },
24
28
  {
25
29
  name: 'stop_id',
26
30
  type: 'varchar(255)',
27
31
  index: true,
32
+ prefix: true,
28
33
  },
29
34
  {
30
35
  name: 'stop_sequence',
@@ -6,6 +6,7 @@ const model = {
6
6
  name: 'note_id',
7
7
  type: 'varchar(255)',
8
8
  primary: true,
9
+ prefix: true,
9
10
  },
10
11
  {
11
12
  name: 'symbol',
@@ -6,6 +6,7 @@ const model = {
6
6
  name: 'timetable_page_id',
7
7
  type: 'varchar(255)',
8
8
  primary: true,
9
+ prefix: true,
9
10
  },
10
11
  {
11
12
  name: 'timetable_page_label',
@@ -6,15 +6,18 @@ const model = {
6
6
  name: 'id',
7
7
  type: 'integer',
8
8
  primary: true,
9
+ prefix: true,
9
10
  },
10
11
  {
11
12
  name: 'timetable_id',
12
13
  type: 'varchar(255)',
13
14
  index: true,
15
+ prefix: true,
14
16
  },
15
17
  {
16
18
  name: 'stop_id',
17
19
  type: 'varchar(255)',
20
+ prefix: true,
18
21
  },
19
22
  {
20
23
  name: 'stop_sequence',
@@ -6,14 +6,17 @@ const model = {
6
6
  name: 'id',
7
7
  type: 'integer',
8
8
  primary: true,
9
+ prefix: true,
9
10
  },
10
11
  {
11
12
  name: 'timetable_id',
12
13
  type: 'varchar(255)',
14
+ prefix: true,
13
15
  },
14
16
  {
15
17
  name: 'route_id',
16
18
  type: 'varchar(255)',
19
+ prefix: true,
17
20
  },
18
21
  {
19
22
  name: 'direction_id',
@@ -7,6 +7,7 @@ const model = {
7
7
  type: 'varchar(255)',
8
8
  required: true,
9
9
  index: true,
10
+ prefix: true,
10
11
  },
11
12
  {
12
13
  name: 'operating_day_date',
@@ -7,12 +7,14 @@ const model = {
7
7
  name: 'id',
8
8
  type: 'integer',
9
9
  primary: true,
10
+ prefix: true,
10
11
  },
11
12
  {
12
13
  name: 'deadhead_id',
13
14
  type: 'varchar(255)',
14
15
  required: true,
15
16
  index: true,
17
+ prefix: true,
16
18
  },
17
19
  {
18
20
  name: 'arrival_time',
@@ -37,10 +39,12 @@ const model = {
37
39
  {
38
40
  name: 'ops_location_id',
39
41
  type: 'varchar(255)',
42
+ prefix: true,
40
43
  },
41
44
  {
42
45
  name: 'stop_id',
43
46
  type: 'varchar(255)',
47
+ prefix: true,
44
48
  },
45
49
  {
46
50
  name: 'location_sequence',
@@ -8,42 +8,50 @@ const model = {
8
8
  type: 'varchar(255)',
9
9
  primary: true,
10
10
  required: true,
11
+ prefix: true,
11
12
  },
12
13
  {
13
14
  name: 'service_id',
14
15
  type: 'varchar(255)',
15
16
  required: true,
17
+ prefix: true,
16
18
  },
17
19
  {
18
20
  name: 'block_id',
19
21
  type: 'varchar(255)',
20
22
  required: true,
21
23
  index: true,
24
+ prefix: true,
22
25
  },
23
26
  {
24
27
  name: 'shape_id',
25
28
  type: 'varchar(255)',
26
29
  index: true,
30
+ prefix: true,
27
31
  },
28
32
  {
29
33
  name: 'to_trip_id',
30
34
  type: 'varchar(255)',
31
35
  index: true,
36
+ prefix: true,
32
37
  },
33
38
  {
34
39
  name: 'from_trip_id',
35
40
  type: 'varchar(255)',
36
41
  index: true,
42
+ prefix: true,
37
43
  },
38
44
  {
39
45
  name: 'to_deadhead_id',
40
46
  type: 'varchar(255)',
41
47
  index: true,
48
+ prefix: true,
42
49
  },
43
50
  {
44
51
  name: 'from_deadhead_id',
45
52
  type: 'varchar(255)',
46
53
  index: true,
54
+ prefix: true,
47
55
  },
48
56
  ],
49
57
  };
@@ -8,6 +8,7 @@ const model = {
8
8
  type: 'varchar(255)',
9
9
  primary: true,
10
10
  required: true,
11
+ prefix: true,
11
12
  },
12
13
  {
13
14
  name: 'ops_location_code',
@@ -8,11 +8,13 @@ const model = {
8
8
  type: 'varchar(255)',
9
9
  primary: true,
10
10
  required: true,
11
+ prefix: true,
11
12
  },
12
13
  {
13
14
  name: 'piece_id',
14
15
  type: 'varchar(255)',
15
16
  required: true,
17
+ prefix: true,
16
18
  },
17
19
  {
18
20
  name: 'event_type',
@@ -47,6 +49,7 @@ const model = {
47
49
  {
48
50
  name: 'event_from_location_id',
49
51
  type: 'varchar(255)',
52
+ prefix: true,
50
53
  },
51
54
  {
52
55
  name: 'event_to_location_type',
@@ -58,6 +61,7 @@ const model = {
58
61
  {
59
62
  name: 'event_to_location_id',
60
63
  type: 'varchar(255)',
64
+ prefix: true,
61
65
  },
62
66
  ],
63
67
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtfs",
3
- "version": "4.2.0",
3
+ "version": "4.3.0",
4
4
  "description": "Import GTFS transit data into SQLite and query routes, stops, times, fares and more",
5
5
  "keywords": [
6
6
  "transit",
@@ -75,12 +75,13 @@
75
75
  "dependencies": {
76
76
  "@turf/helpers": "^6.5.0",
77
77
  "better-sqlite3": "^8.3.0",
78
- "csv-parse": "^5.3.6",
79
- "csv-stringify": "^6.3.0",
78
+ "csv-parse": "^5.3.8",
79
+ "csv-stringify": "^6.3.2",
80
80
  "gtfs-realtime-bindings": "^1.1.1",
81
81
  "lodash-es": "^4.17.21",
82
- "long": "^5.2.1",
82
+ "long": "^5.2.3",
83
83
  "node-fetch": "^3.3.1",
84
+ "node-stream-zip": "^1.15.0",
84
85
  "pluralize": "^8.0.0",
85
86
  "pretty-error": "^4.0.0",
86
87
  "promise-map-series": "^0.3.0",
@@ -90,19 +91,18 @@
90
91
  "strip-bom-stream": "^5.0.0",
91
92
  "tmp-promise": "^3.0.3",
92
93
  "untildify": "^4.0.0",
93
- "unzipper": "^0.10.11",
94
94
  "yargs": "^17.7.1",
95
95
  "yoctocolors": "^1.0.0"
96
96
  },
97
97
  "devDependencies": {
98
98
  "@types/better-sqlite3": "^7.6.4",
99
99
  "dtslint": "^4.2.1",
100
- "eslint": "^8.38.0",
100
+ "eslint": "^8.39.0",
101
101
  "eslint-config-prettier": "^8.8.0",
102
102
  "eslint-config-xo": "^0.43.1",
103
103
  "husky": "^8.0.3",
104
104
  "mocha": "^10.2.0",
105
- "prettier": "^2.8.7",
105
+ "prettier": "^2.8.8",
106
106
  "pretty-quick": "^3.1.3",
107
107
  "should": "^13.2.3"
108
108
  },
@@ -8,7 +8,11 @@ import { rm } from 'node:fs/promises';
8
8
  import { parse } from 'csv-parse';
9
9
  import should from 'should';
10
10
 
11
- import { unzip, generateFolderName } from '../../lib/file-utils.js';
11
+ import {
12
+ unzip,
13
+ generateFolderName,
14
+ prepDirectory,
15
+ } from '../../lib/file-utils.js';
12
16
  import config from '../test-config.js';
13
17
  import {
14
18
  openDb,
@@ -45,6 +49,7 @@ describe('exportGtfs():', function () {
45
49
  );
46
50
 
47
51
  before(async () => {
52
+ await prepDirectory(temporaryDir);
48
53
  await unzip(config.agencies[0].path, temporaryDir);
49
54
 
50
55
  await Promise.all(
@@ -7,9 +7,15 @@ import { fileURLToPath } from 'node:url';
7
7
  import { parse } from 'csv-parse';
8
8
  import should from 'should';
9
9
 
10
- import { unzip } from '../../lib/file-utils.js';
10
+ import { prepDirectory, unzip } from '../../lib/file-utils.js';
11
11
  import config from '../test-config.js';
12
- import { openDb, closeDb, importGtfs, getRoutes } from '../../index.js';
12
+ import {
13
+ openDb,
14
+ closeDb,
15
+ importGtfs,
16
+ getRoutes,
17
+ getStops,
18
+ } from '../../index.js';
13
19
  import models from '../../models/models.js';
14
20
 
15
21
  let db;
@@ -63,15 +69,35 @@ describe('importGtfs():', function () {
63
69
  routes.length.should.equal(4);
64
70
  });
65
71
 
66
- it("should throw an error when importing from local filesystem which doesn't exist", async () => {
67
- return importGtfs({
72
+ it("should throw an error when importing from local filesystem which doesn't exist", async () =>
73
+ importGtfs({
68
74
  ...config,
69
75
  agencies: [
70
76
  {
71
77
  path: '/does/not/exist',
72
78
  },
73
79
  ],
74
- }).should.be.rejected();
80
+ }).should.be.rejected());
81
+
82
+ it('should add a prefix to imported data if present in config', async () => {
83
+ const prefix = 'test-prefix';
84
+ await importGtfs({
85
+ ...config,
86
+ agencies: [
87
+ {
88
+ ...agenciesFixturesLocal[0],
89
+ prefix,
90
+ },
91
+ ],
92
+ });
93
+
94
+ const routes = getRoutes();
95
+ should.exist(routes);
96
+ routes[0].route_id.should.startWith(prefix);
97
+
98
+ const stops = getStops();
99
+ should.exist(stops);
100
+ stops[0].stop_id.should.startWith(prefix);
75
101
  });
76
102
  });
77
103
 
@@ -83,6 +109,7 @@ describe('importGtfs():', function () {
83
109
  );
84
110
 
85
111
  before(async () => {
112
+ await prepDirectory(temporaryDir);
86
113
  await unzip(agenciesFixturesLocal[0].path, temporaryDir);
87
114
 
88
115
  await Promise.all(