From 95ee6501e471763db6b139f47eed266dc8203357 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Apr 2022 05:37:40 +0000 Subject: [PATCH 1/6] Bump minimist from 1.2.5 to 1.2.6 Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6. - [Release notes](https://github.com/substack/minimist/releases) - [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6) --- updated-dependencies: - dependency-name: minimist dependency-type: indirect ... Signed-off-by: dependabot[bot] --- package-lock.json | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index a336583..91c80b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2152,8 +2152,7 @@ "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" + "optionator": "^0.8.1" }, "bin": { "escodegen": "bin/escodegen.js", @@ -3195,7 +3194,6 @@ "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", "graceful-fs": "^4.2.9", "jest-regex-util": "^27.5.1", "jest-serializer": "^27.5.1", @@ -4092,9 +4090,9 @@ } }, "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, "node_modules/mkdirp": { "version": "0.5.5", @@ -8596,9 +8594,9 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" }, "mkdirp": { "version": "0.5.5", From f3f2e075d035bf8d4b0e84bb9a8f7e53f29c43b2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Aug 2022 19:57:27 +0000 Subject: [PATCH 2/6] Bump undici from 5.5.1 to 5.8.2 Bumps [undici](https://github.com/nodejs/undici) from 5.5.1 to 5.8.2. - [Release notes](https://github.com/nodejs/undici/releases) - [Commits](https://github.com/nodejs/undici/compare/v5.5.1...v5.8.2) --- updated-dependencies: - dependency-name: undici dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- package-lock.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index cebcfb9..c4a65b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5081,9 +5081,9 @@ } }, "node_modules/undici": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.5.1.tgz", - "integrity": "sha512-MEvryPLf18HvlCbLSzCW0U00IMftKGI5udnjrQbC5D4P0Hodwffhv+iGfWuJwg16Y/TK11ZFK8i+BPVW2z/eAw==", + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.8.2.tgz", + "integrity": "sha512-3KLq3pXMS0Y4IELV045fTxqz04Nk9Ms7yfBBHum3yxsTR4XNn+ZCaUbf/mWitgYDAhsplQ0B1G4S5D345lMO3A==", "engines": { "node": ">=12.18" } @@ -9280,9 +9280,9 @@ } }, "undici": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.5.1.tgz", - "integrity": "sha512-MEvryPLf18HvlCbLSzCW0U00IMftKGI5udnjrQbC5D4P0Hodwffhv+iGfWuJwg16Y/TK11ZFK8i+BPVW2z/eAw==" + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.8.2.tgz", + "integrity": "sha512-3KLq3pXMS0Y4IELV045fTxqz04Nk9Ms7yfBBHum3yxsTR4XNn+ZCaUbf/mWitgYDAhsplQ0B1G4S5D345lMO3A==" }, "universalify": { "version": "0.1.2", From 029c3ff2c1db8becf4636d826e437ffa2df0ab17 Mon Sep 17 00:00:00 2001 From: Julian Rojas Date: Tue, 27 Sep 2022 15:33:30 +0200 Subject: [PATCH 3/6] Output raw JSON connection by default --- bin/gtfsrt2lc.js | 10 +-- lib/Connections2CSV.js | 10 ++- lib/Connections2JSONLD.js | 58 +++++++++---- lib/Connections2Triples.js | 99 +++++++++++++--------- lib/Gtfsrt2LC.js | 165 ++++++++----------------------------- lib/Utils.js | 77 ++++++++++++++++- test/gtfsrt2lc.test.js | 21 +---- 7 files changed, 220 insertions(+), 220 deletions(-) diff --git a/bin/gtfsrt2lc.js b/bin/gtfsrt2lc.js index 4a465f4..b9bf085 100755 --- a/bin/gtfsrt2lc.js +++ b/bin/gtfsrt2lc.js @@ -30,12 +30,6 @@ if (!program.static) { process.exit(); } -if (!program.urisTemplate) { - console.error('Please provide path to a template file'); - console.error("GTFS-RT to linked connections converter use --help to discover how to use it"); - process.exit(); -} - if (!program.store) { program.store = 'MemStore'; } @@ -43,7 +37,9 @@ if (!program.store) { // Load URIs template let template = null; try { - template = JSON.parse(fs.readFileSync(program.urisTemplate, 'utf8')); + if (program.urisTemplate) { + template = JSON.parse(fs.readFileSync(program.urisTemplate, 'utf8')); + } } catch (err) { console.error('Please provide a valid path to a template file'); console.error("GTFS-RT to linked connections converter use --help to discover how to use it"); diff --git a/lib/Connections2CSV.js b/lib/Connections2CSV.js index 402b734..c3f4289 100644 --- a/lib/Connections2CSV.js +++ b/lib/Connections2CSV.js @@ -6,13 +6,15 @@ class Connections2CSV extends Transform { this._headerStreamed = false; } - _transform(lc, encoding, done) { + _transform(conn, encoding, done) { if (!this.headerStreamed) { this.headerStreamed = true; - done(null, '"id","type",departureStop","departureTime","departureDelay",arrivalStop","arrivalTime","arrivalDelay","direction",trip","route"\n'); + done(null, '"type",departureStop","departureTime","departureDelay",arrivalStop","arrivalTime","arrivalDelay","headsign",trip","route"\n'); } else { - let csv = lc['@id'] + ',' + 'http://semweb.mmlab.be/ns/linkedconnections#' + lc['@type'] + ',' + lc['departureStop'] + ',' + lc['departureTime'] + ',' + lc['departureDelay'] + ',' + lc['arrivalStop'] - + ',' + lc['arrivalTime'] + ',' + lc['arrivalDelay'] + ',' + lc['direction'] + ',' + lc['trip'] + ',' + lc['route'] + '\n'; + const csv = conn['type'] + ',' + conn['departureStop']['stop_name'] + ',' + + conn['departureTime'].toISOString() + ',' + conn['departureDelay'] + ',' + conn['arrivalStop']['stop_name'] + ',' + + conn['arrivalTime'].toISOString() + ',' + conn['arrivalDelay'] + ',' + conn['headsign'] + ',' + + conn['trip']['trip_id'] + ',' + conn['route']['route_long_name'] + '\n'; done(null, csv); } } diff --git a/lib/Connections2JSONLD.js b/lib/Connections2JSONLD.js index 022ead0..41f0d02 100644 --- a/lib/Connections2JSONLD.js +++ b/lib/Connections2JSONLD.js @@ -1,10 +1,13 @@ const { Transform } = require('stream'); +const uri_templates = require('uri-templates'); +const Utils = require('./Utils'); class Connections2JSONLD extends Transform { - constructor(streamContext) { + constructor(streamContext, templates) { super({ objectMode: true }); this._streamContext = streamContext || false; this._contextStreamed = false; + this._templates = templates; this._context = { "@context": { "xsd": "http://www.w3.org/2001/XMLSchema#", @@ -50,29 +53,46 @@ class Connections2JSONLD extends Transform { } } - _transform(lc, encoding, done) { + _transform(conn, encoding, done) { if (this._streamContext && !this.contextStreamed) { this.contextStreamed = true; this.push(this.context); } - let temp = { - "@id": lc['@id'], - "@type": lc['@type'], - "departureStop": lc['departureStop'], - "arrivalStop": lc['arrivalStop'], - "departureTime": lc['departureTime'], - "arrivalTime": lc['arrivalTime'], - "departureDelay": lc['departureDelay'], - "arrivalDelay": lc['arrivalDelay'], - "direction": lc['direction'], - "gtfs:trip": lc['trip'], - "gtfs:route": lc['route'], - "gtfs:pickupType": lc['gtfs:pickupType'], - "gtfs:dropOffType": lc['gtfs:dropOffType'] + // Predefined URI templates + const stopTemplate = uri_templates(this.templates['stop']); + const routeTemplate = uri_templates(this.templates['route']); + const tripTemplate = uri_templates(this.templates['trip']); + const connectionTemplate = uri_templates(this.templates['connection']); + + // Resolve values for URIs + const departureStopURI = Utils.resolveURI(stopTemplate, conn, this.templates['resolve'], "departureStop"); + const arrivalStopURI = Utils.resolveURI(stopTemplate, conn, this.templates['resolve'], "arrivalStop"); + const routeURI = Utils.resolveURI(routeTemplate, conn, this.templates['resolve']); + const tripURI = Utils.resolveURI(tripTemplate, conn, this.templates['resolve']); + const connectionURI = Utils.resolveURI(connectionTemplate, conn, this.templates['resolve']); + // Determine Pick Up & Drop Off types + const pickupType = Utils.resolveScheduleRelationship(conn['pickup_type']); + const dropOffType = Utils.resolveScheduleRelationship(conn['drop_off_type']); + + // Build LC + const lc = { + "@id": connectionURI, + "@type": conn.type, + "departureStop": departureStopURI, + "arrivalStop": arrivalStopURI, + "departureTime": conn.departureTime.toISOString(), + "arrivalTime": conn.arrivalTime.toISOString(), + "departureDelay": conn.departureDelay, + "arrivalDelay": conn.arrivalDelay, + "gtfs:trip": tripURI, + "gtfs:route": routeURI, + "direction": conn.trip['trip_headsign'], + "gtfs:pickupType": pickupType, + "gtfs:dropOffType": dropOffType } - done(null, temp); + done(null, lc); } get contextStreamed() { @@ -86,6 +106,10 @@ class Connections2JSONLD extends Transform { get context() { return this._context; } + + get templates() { + return this._templates; + } } module.exports = Connections2JSONLD; diff --git a/lib/Connections2Triples.js b/lib/Connections2Triples.js index 780140d..34af2d8 100644 --- a/lib/Connections2Triples.js +++ b/lib/Connections2Triples.js @@ -2,93 +2,110 @@ const { Transform } = require('stream'); const N3 = require('n3'); const { DataFactory } = N3; const { namedNode, literal, quad } = DataFactory; +const uri_templates = require('uri-templates'); +const Utils = require('./Utils'); class Connections2Triples extends Transform { - constructor() { + constructor(templates) { super({ objectMode: true }); + this._templates = templates; } - _transform(lc, encoding, done) { - if (lc['@type'] === 'Connection') { + _transform(conn, encoding, done) { + // Predefined URI templates + const stopTemplate = uri_templates(this.templates['stop']); + const routeTemplate = uri_templates(this.templates['route']); + const tripTemplate = uri_templates(this.templates['trip']); + const connectionTemplate = uri_templates(this.templates['connection']); + + // Resolve values for URIs + const departureStopURI = Utils.resolveURI(stopTemplate, conn, this.templates['resolve'], "departureStop"); + const arrivalStopURI = Utils.resolveURI(stopTemplate, conn, this.templates['resolve'], "arrivalStop"); + const routeURI = Utils.resolveURI(routeTemplate, conn, this.templates['resolve']); + const tripURI = Utils.resolveURI(tripTemplate, conn, this.templates['resolve']); + const connectionURI = Utils.resolveURI(connectionTemplate, conn, this.templates['resolve']); + // Determine Pick Up & Drop Off types + const pickupType = Utils.resolveScheduleRelationship(conn['pickup_type']); + const dropOffType = Utils.resolveScheduleRelationship(conn['drop_off_type']); + + if (conn['type'] === 'Connection') { this.push( quad( - namedNode(lc['@id']), + namedNode(connectionURI), namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), namedNode('http://semweb.mmlab.be/ns/linkedconnections#Connection'))); } else { this.push( quad( - namedNode(lc['@id']), + namedNode(connectionURI), namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), namedNode('http://semweb.mmlab.be/ns/linkedconnections#CancelledConnection'))); } this.push( quad( - namedNode(lc['@id']), + namedNode(connectionURI), namedNode('http://semweb.mmlab.be/ns/linkedconnections#departureStop'), - namedNode(lc['departureStop']))); + namedNode(departureStopURI))); this.push( quad( - namedNode(lc['@id']), + namedNode(connectionURI), namedNode('http://semweb.mmlab.be/ns/linkedconnections#arrivalStop'), - namedNode(lc['arrivalStop']))); + namedNode(arrivalStopURI))); this.push( quad( - namedNode(lc['@id']), + namedNode(connectionURI), namedNode('http://semweb.mmlab.be/ns/linkedconnections#departureTime'), - literal(lc['departureTime'], namedNode('http://www.w3.org/2001/XMLSchema#dateTime')))); + literal(conn['departureTime'].toISOString(), namedNode('http://www.w3.org/2001/XMLSchema#dateTime')))); this.push( quad( - namedNode(lc['@id']), + namedNode(connectionURI), namedNode('http://semweb.mmlab.be/ns/linkedconnections#arrivalTime'), - literal(lc['arrivalTime'], namedNode('http://www.w3.org/2001/XMLSchema#dateTime')))); + literal(conn['arrivalTime'].toISOString(), namedNode('http://www.w3.org/2001/XMLSchema#dateTime')))); this.push( quad( - namedNode(lc['@id']), + namedNode(connectionURI), namedNode('http://semweb.mmlab.be/ns/linkedconnections#departureDelay'), - literal(lc['departureDelay'], namedNode('http://www.w3.org/2001/XMLSchema#integer')))); + literal(conn['departureDelay'], namedNode('http://www.w3.org/2001/XMLSchema#integer')))); this.push( quad( - namedNode(lc['@id']), + namedNode(connectionURI), namedNode('http://semweb.mmlab.be/ns/linkedconnections#arrivalDelay'), - literal(lc['arrivalDelay'], namedNode('http://www.w3.org/2001/XMLSchema#integer')))); + literal(conn['arrivalDelay'], namedNode('http://www.w3.org/2001/XMLSchema#integer')))); this.push( quad( - namedNode(lc['@id']), + namedNode(connectionURI), namedNode('http://vocab.gtfs.org/terms#headsign'), - literal(lc['direction'], namedNode('http://www.w3.org/2001/XMLSchema#string')))); + literal(conn['headsign'], namedNode('http://www.w3.org/2001/XMLSchema#string')))); this.push( quad( - namedNode(lc['@id']), + namedNode(connectionURI), namedNode('http://vocab.gtfs.org/terms#trip'), - namedNode(lc['trip']))); + namedNode(tripURI))); this.push( quad( - namedNode(lc['@id']), + namedNode(connectionURI), namedNode('http://vocab.gtfs.org/terms#route'), - namedNode(lc['route']))); - - if (lc['gtfs:dropOffType'] && lc['gtfs:dropOffType'] !== null) { - this.push( - quad( - namedNode(lc['@id']), - namedNode('http://vocab.gtfs.org/terms#dropOffType'), - namedNode(lc['gtfs:dropOffType']) - )); - } - - if (lc['gtfs:pickupType'] && lc['gtfs:pickupType'] !== null) { - this.push( - quad( - namedNode(lc['@id']), - namedNode('http://vocab.gtfs.org/terms#pickupType'), - namedNode(lc['gtfs:pickupType']) - )); - } + namedNode(routeURI))); + this.push( + quad( + namedNode(connectionURI), + namedNode('http://vocab.gtfs.org/terms#dropOffType'), + namedNode(dropOffType) + )); + this.push( + quad( + namedNode(connectionURI), + namedNode('http://vocab.gtfs.org/terms#pickupType'), + namedNode(pickupType) + )); done(null); } + + get templates() { + return this._templates; + } } module.exports = Connections2Triples; \ No newline at end of file diff --git a/lib/Gtfsrt2LC.js b/lib/Gtfsrt2LC.js index 552af8e..2371f12 100644 --- a/lib/Gtfsrt2LC.js +++ b/lib/Gtfsrt2LC.js @@ -5,7 +5,6 @@ const fs = require('fs'); const util = require('util'); const zlib = require('zlib'); const gtfsrt = require('gtfs-realtime-bindings').transit_realtime; -const uri_templates = require('uri-templates'); const JSONStream = require('JSONStream'); const N3 = require('n3'); const { format, addHours, addMinutes, addSeconds } = require('date-fns'); @@ -19,7 +18,6 @@ const days = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', class Gtfsrt2LC { constructor(options) { this._path = options.path; - this._uris = options.uris; this._headers = options.headers || {}; this._routes = null; this._trips = null; @@ -31,6 +29,19 @@ class Gtfsrt2LC { this._calendarDates = null; this._jsonData = null; this._historyDB = null; + + this._uris = options.uris || { // Default URI templates + stop: "http://example.org/stations/{stops.stop_id}", + route: "http://example.org/routes/{routeLabel}/{routes.route_id}", + trip: "http://example.org/trips/{trips.trip_id}/{tripLabel}/{tripStartTime}", + connection: "http://example.org/connections/{tripLabel}/{depStop}/{tripStartTime}/", + resolve: { + "depStop": "connection.departureStop.stop_id", + "routeLabel": "routes.route_long_name.replace(/\\s/gi, '');", + "tripLabel": "routes.route_short_name + routes.route_id;", + "tripStartTime": "format(trips.startTime, \"yyyyMMdd'T'HHmm\");" + } + }; } async getUpdatedTrips() { @@ -166,50 +177,28 @@ class Gtfsrt2LC { return; } - let raw_connection = { + // Add start time to trip object + t.startTime = tripStartTime; + + // Build JSON Connection + let json_connection = { + type, departureStop: await this.getStop(departureStop), departureTime: departureTime, arrivalStop: await this.getStop(arrivalStop), - arrivalTime: arrivalTime + arrivalTime: arrivalTime, + departureDelay, + arrivalDelay, + trip: t, + route: r, + headsign: t['trip_headsign'], + pickup_type: cu['scheduleRelationship'] || st[pdIndex]['pickup_type'], + drop_off_type: completedUpdates[j + 1]['scheduleRelationship'] || st[pdIndex + 1]['drop_off_type'] }; - // Predefined URI templates - let stopTemplate = uri_templates(this.uris['stop']); - let routeTemplate = uri_templates(this.uris['route']); - let tripTemplate = uri_templates(this.uris['trip']); - let connectionTemplate = uri_templates(this.uris['connection']); - - // Resolve values for URIs - let departureStopURI = await this.resolveURI(stopTemplate, departureStop, null, tripId, tripStartTime, raw_connection, this.uris['resolve']); - let arrivalStopURI = await this.resolveURI(stopTemplate, null, arrivalStop, tripId, tripStartTime, raw_connection, this.uris['resolve']); - let routeURI = await this.resolveURI(routeTemplate, null, null, tripId, tripStartTime, raw_connection, this.uris['resolve']); - let tripURI = await this.resolveURI(tripTemplate, null, null, tripId, tripStartTime, raw_connection, this.uris['resolve']); - let connectionURI = await this.resolveURI(connectionTemplate, null, null, tripId, tripStartTime, raw_connection, this.uris['resolve']); - // Determine Pick Up & Drop Off types - let pickupType = this.resolveScheduleRelationship(cu['scheduleRelationship'], st[pdIndex]['pickup_type']); - let dropOffType = this.resolveScheduleRelationship(completedUpdates[j + 1]['scheduleRelationship'], st[pdIndex + 1]['drop_off_type']); - // Advance pdIndex pdIndex++; - - // Build LC - let linked_connection = { - "@id": connectionURI, - "@type": type, - "departureStop": departureStopURI, - "arrivalStop": arrivalStopURI, - "departureTime": departureTime.toISOString(), - "arrivalTime": arrivalTime.toISOString(), - "departureDelay": departureDelay, - "arrivalDelay": arrivalDelay, - "direction": t['trip_headsign'], - "trip": tripURI, - "route": routeURI, - "gtfs:pickupType": pickupType, - "gtfs:dropOffType": dropOffType - } - - readable.push(linked_connection); + readable.push(json_connection); } catch (err) { console.error('Error parsing update for Trip ' + tripId + ': '); @@ -234,16 +223,16 @@ class Gtfsrt2LC { // Serialize data according to specified format (default: json) if (options.format === 'jsonld') { if (!options.objectMode) { - return readable.pipe(new Connections2JSONLD(true)).pipe(JSONStream.stringify(false)); + return readable.pipe(new Connections2JSONLD(true, this.uris)).pipe(JSONStream.stringify(false)); } else { - return readable.pipe(new Connections2JSONLD(true)); + return readable.pipe(new Connections2JSONLD(true, this.uris)); } } else if (options.format === 'csv') { return readable.pipe(new Connections2CSV()); } else if (options.format === 'ntriples') { - return readable.pipe(new Connections2Triples()).pipe(new N3.StreamWriter({ format: 'N-Triples' })); + return readable.pipe(new Connections2Triples(this.uris)).pipe(new N3.StreamWriter({ format: 'N-Triples' })); } else if (options.format === 'turtle') { - return readable.pipe(new Connections2Triples()).pipe(new N3.StreamWriter({ + return readable.pipe(new Connections2Triples(this.uris)).pipe(new N3.StreamWriter({ prefixes: { xsd: 'http://www.w3.org/2001/XMLSchema#', lc: 'http://semweb.mmlab.be/ns/linkedconnections#', @@ -394,7 +383,7 @@ class Gtfsrt2LC { let scheduleRelationship = entity.tripUpdate.trip.scheduleRelationship; // Look for CANCELED instead of CANCELLED because that is how gtfs-realtime-bindings has it (american english) if (entity.isDeleted || scheduleRelationship == 3 || scheduleRelationship === 'CANCELED') { - return 'CancelledConnection'; // TODO: Add CancelledConnection class to LC vocabulary + return 'CancelledConnection'; } else { return 'Connection'; @@ -736,94 +725,6 @@ class Gtfsrt2LC { return { hours, minutes, seconds: seconds ? seconds : 0 }; } - async resolveURI(template, departureStop, arrivalStop, tripId, tripStartTime, connectionParams, resolve) { - let varNames = template.varNames; - let fillerObj = {}; - - for (let v of varNames) { - if (departureStop) { - fillerObj[v] = await this.resolveValue(v, departureStop, tripId, tripStartTime, connectionParams, resolve || {}); - } else if (arrivalStop) { - fillerObj[v] = await this.resolveValue(v, arrivalStop, tripId, tripStartTime, connectionParams, resolve || {}); - } else { - fillerObj[v] = await this.resolveValue(v, null, tripId, tripStartTime, connectionParams, resolve || {}); - } - } - - return template.fill(fillerObj); - } - - async resolveValue(param, stopId, tripId, tripStartTime, connection, resolve) { - // Entity objects to be resolved as needed - let trips = tripId ? await this.getTrip(tripId) : null; - let routes = tripId ? await this.getRoute(trips['route_id']) : null; - let stops = stopId ? await this.getStop(stopId) : null; - - // try first to resolve using keys in 'resolve' object - if (resolve[param]) { - trips['startTime'] = tripStartTime; - // Hotfix for route long names: usually contain --. However, we are 2019 at the time of writing and can use UTF-8! - routes.route_long_name = routes.route_long_name.replace('--', '–'); - - return eval(resolve[param]); - } - - // GTFS source file and attribute name - let source = param.split('.')[0]; - let attr = param.split('.')[1]; - // Resolved value - let value = null; - - switch (source) { - case 'trips': - if (attr.indexOf('startTime') >= 0) { - let dateFormat = attr.match(/\((.*?)\)/)[1]; - value = format(tripStartTime, dateFormat); - } else { - value = trips[attr]; - } - break; - case 'routes': - //Hotfix for route long names: usually contain --. However, we are 2019 at the time of writing and can use UTF-8! - routes.route_long_name = routes.route_long_name.replace('--', '–'); - value = routes[attr]; - break; - case 'stops': - value = stops[attr]; - break; - case 'connection': - if (attr.indexOf('departureTime') >= 0) { - let dateFormat = attr.match(/\((.*?)\)/)[1]; - value = format(connection.departureTime, dateFormat); - } else if (attr.indexOf('arrivalTime') >= 0) { - let dateFormat = attr.match(/\((.*?)\)/)[1]; - value = format(connection.arrivalTime, dateFormat); - } else { - value = connection[attr]; - } - break; - } - - return value; - } - - resolveScheduleRelationship(value, scheduleType) { - // SKIPPED - if (value === 1) { - return 'gtfs:NotAvailable'; - } else { - if (!scheduleType || scheduleType == 0) { - return 'gtfs:Regular'; - } else if (scheduleType == 1) { - return 'gtfs:NotAvailable'; - } else if (scheduleType == 2) { - return 'gtfs:MustPhone' - } else if (scheduleType == 3) { - return 'gtfs:MustCoordinateWithDriver'; - } - } - } - setIndexes(indexes) { this._routes = indexes.routes; this._trips = indexes.trips; diff --git a/lib/Utils.js b/lib/Utils.js index 420057f..0af5270 100644 --- a/lib/Utils.js +++ b/lib/Utils.js @@ -1,5 +1,6 @@ const fs = require('fs'); const unzip = require('unzipper'); +const { format } = require('date-fns'); function unzipStream(stream, outPath) { return new Promise((resolve, reject) => { @@ -17,6 +18,80 @@ function unzipStream(stream, outPath) { }); } +function resolveURI(template, raw, resolve, stopType) { + let varNames = template.varNames; + let fillerObj = {}; + + for (let v of varNames) { + fillerObj[v] = resolveValue(v, raw, resolve || {}, stopType); + } + + return template.fill(fillerObj); +} + +function resolveValue(param, connection, resolve, stopType) { + // Entity objects to be resolved as needed + let trips = connection.trip; + let routes = connection.route; + let stops = stopType ? connection[stopType] : null; + + // try first to resolve using keys in 'resolve' object + if (resolve[param]) { + return eval(resolve[param]); + } + + // GTFS source file and attribute name + let source = param.split('.')[0]; + let attr = param.split('.')[1]; + // Resolved value + let value = null; + + switch (source) { + case 'trips': + if (attr.indexOf('startTime') >= 0) { + let dateFormat = attr.match(/\((.*?)\)/)[1]; + value = format(trips.startTime, dateFormat); + } else { + value = trips[attr]; + } + break; + case 'routes': + value = routes[attr]; + break; + case 'stops': + value = stops[attr]; + break; + case 'connection': + if (attr.indexOf('departureTime') >= 0) { + let dateFormat = attr.match(/\((.*?)\)/)[1]; + value = format(connection.departureTime, dateFormat); + } else if (attr.indexOf('arrivalTime') >= 0) { + let dateFormat = attr.match(/\((.*?)\)/)[1]; + value = format(connection.arrivalTime, dateFormat); + } else { + value = connection[attr]; + } + break; + } + + return value; +} + +function resolveScheduleRelationship(value) { + if (!value || value == 0) { + return 'gtfs:Regular'; + } else if (value == 1) { + return 'gtfs:NotAvailable'; + } else if (value == 2) { + return 'gtfs:MustPhone' + } else if (value == 3) { + return 'gtfs:MustCoordinateWithDriver'; + } + +} + module.exports = { - unzipStream + unzipStream, + resolveURI, + resolveScheduleRelationship } \ No newline at end of file diff --git a/test/gtfsrt2lc.test.js b/test/gtfsrt2lc.test.js index b8d63cc..939bb3c 100644 --- a/test/gtfsrt2lc.test.js +++ b/test/gtfsrt2lc.test.js @@ -114,7 +114,7 @@ test('Extract all indexes when source is given as decompressed folder', async () test('Check all parsed connections are consistent regarding departure and arrival times', async () => { grt.setIndexes(memIndexes); - let connStream = await grt.parse({ format: 'json' }); + let connStream = await grt.parse({ format: 'jsonld' }); let flag = true; expect.assertions(2); @@ -147,7 +147,7 @@ test('Check all parsed connections are consistent regarding departure and arriva test('Check all parsed connections are consistent regarding departure and arrival times using MemStore with grep', async () => { grt.setIndexes(grepIndexes); - let connStream = await grt.parse({ format: 'json' }); + let connStream = await grt.parse({ format: 'jsonld' }); let flag = true; expect.assertions(2); @@ -180,7 +180,7 @@ test('Check all parsed connections are consistent regarding departure and arriva test('Check all parsed connections are consistent regarding departure and arrival times using LevelStore', async () => { grt.setIndexes(levelIndexes); - let connStream = await grt.parse({ format: 'json' }); + let connStream = await grt.parse({ format: 'jsonld' }); let flag = true; expect.assertions(2); @@ -582,19 +582,4 @@ test('Cover GtfsIndex functions', async () => { try { await gti.getIndexes(); } catch (err) { } -}); - -test('Cover Gtfsrt2LC functions', async () => { - grt = new Gtfsrt2lc({ path: rt_path, uris: mock_uris, headers: {} }); - // Test for resolveScheduleRelationship - const notAvailable = grt.resolveScheduleRelationship(1, 1); - const mustPhone = grt.resolveScheduleRelationship(0, 2); - const mustCoordinate = grt.resolveScheduleRelationship(0, 3); - // Test for getting headers - const headers = grt.headers; - - expect(notAvailable).toBe('gtfs:NotAvailable'); - expect(mustPhone).toBe('gtfs:MustPhone'); - expect(mustCoordinate).toBe('gtfs:MustCoordinateWithDriver'); - expect(headers).toBeDefined(); }); \ No newline at end of file From 21ade40033422b845fe65dd8afee014dc85ffcda Mon Sep 17 00:00:00 2001 From: Julian Rojas Date: Tue, 27 Sep 2022 15:39:30 +0200 Subject: [PATCH 4/6] 2.0.4 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index c4a65b5..a07e813 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "gtfsrt2lc", - "version": "2.0.3", + "version": "2.0.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "gtfsrt2lc", - "version": "2.0.3", + "version": "2.0.4", "license": "MIT", "dependencies": { "commander": "^5.0.0", diff --git a/package.json b/package.json index b5e22d4..c157a4d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gtfsrt2lc", - "version": "2.0.3", + "version": "2.0.4", "description": "Converts the GTFS-RT to Linked Connections", "main": "./Gtfsrt2LC.js", "bin": { From fb67f2ebc16d51d2584a3e2e08529cd4c13ee3fc Mon Sep 17 00:00:00 2001 From: Julian Rojas Date: Tue, 27 Sep 2022 17:20:20 +0200 Subject: [PATCH 5/6] Test for Utils functions --- test/gtfsrt2lc.test.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/gtfsrt2lc.test.js b/test/gtfsrt2lc.test.js index 939bb3c..b888b4f 100644 --- a/test/gtfsrt2lc.test.js +++ b/test/gtfsrt2lc.test.js @@ -1,5 +1,6 @@ const fs = require('fs'); const del = require('del'); +const uri_templates = require('uri-templates'); const GtfsIndex = require('../lib/GtfsIndex'); const Gtfsrt2lc = require('../lib/Gtfsrt2LC'); const Utils = require('../lib/Utils'); @@ -582,4 +583,24 @@ test('Cover GtfsIndex functions', async () => { try { await gti.getIndexes(); } catch (err) { } +}); + +test('Cover Utils functions', async () => { + // Test for resolve ScheduleRelationship + const regular = Utils.resolveScheduleRelationship(0); + const notAvailable = Utils.resolveScheduleRelationship(1); + const mustPhone = Utils.resolveScheduleRelationship(2); + const mustCoordinate = Utils.resolveScheduleRelationship(3); + + // Test for URI building function + const connTimes = Utils.resolveURI( + uri_templates("http://example.org/test/{connection.departureTime(yyyyMMdd'T'HHmm)}/{connection.arrivalTime(yyyyMMdd'T'HHmm)}"), + { departureTime: new Date('Sep 27 2022 19:00:00 GMT+0200'), arrivalTime: new Date('Sep 27 2022 19:10:00 GMT+0200') } + ); + + expect(regular).toBe('gtfs:Regular'); + expect(notAvailable).toBe('gtfs:NotAvailable'); + expect(mustPhone).toBe('gtfs:MustPhone'); + expect(mustCoordinate).toBe('gtfs:MustCoordinateWithDriver'); + expect(connTimes).toBe("http://example.org/test/20220927T1900/20220927T1910") }); \ No newline at end of file From a531a8d680ee457faff9fe7652b519f8ea2c4606 Mon Sep 17 00:00:00 2001 From: Julian Rojas Date: Tue, 27 Sep 2022 17:25:38 +0200 Subject: [PATCH 6/6] Fix test --- test/gtfsrt2lc.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/gtfsrt2lc.test.js b/test/gtfsrt2lc.test.js index b888b4f..fd62951 100644 --- a/test/gtfsrt2lc.test.js +++ b/test/gtfsrt2lc.test.js @@ -594,13 +594,13 @@ test('Cover Utils functions', async () => { // Test for URI building function const connTimes = Utils.resolveURI( - uri_templates("http://example.org/test/{connection.departureTime(yyyyMMdd'T'HHmm)}/{connection.arrivalTime(yyyyMMdd'T'HHmm)}"), - { departureTime: new Date('Sep 27 2022 19:00:00 GMT+0200'), arrivalTime: new Date('Sep 27 2022 19:10:00 GMT+0200') } + uri_templates("http://example.org/test/{connection.departureTime(yyyyMMdd)}/{connection.arrivalTime(yyyyMMdd)}"), + { departureTime: new Date('2022-09-27'), arrivalTime: new Date('2022-09-27') } ); expect(regular).toBe('gtfs:Regular'); expect(notAvailable).toBe('gtfs:NotAvailable'); expect(mustPhone).toBe('gtfs:MustPhone'); expect(mustCoordinate).toBe('gtfs:MustCoordinateWithDriver'); - expect(connTimes).toBe("http://example.org/test/20220927T1900/20220927T1910") + expect(connTimes).toBe("http://example.org/test/20220927/20220927") }); \ No newline at end of file