@transportme/load-ptv-gtfs 1.4.2 → 1.4.3

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 (81) hide show
  1. package/lib/gtfs-parser/GTFSRoute.mjs +22 -4
  2. package/lib/gtfs-parser/GTFSTrip.mjs +23 -1
  3. package/lib/gtfs-parser/readers/GTFSCalendarReader.mjs +1 -0
  4. package/lib/gtfs-parser/readers/line-reader.mjs +1 -6
  5. package/lib/loader/TripLoader.mjs +1 -0
  6. package/package.json +1 -1
  7. package/.github/workflows/unit-test.yml +0 -46
  8. package/test/gtfs-calendar.test.mjs +0 -182
  9. package/test/gtfs-route-loader.test.mjs +0 -208
  10. package/test/gtfs-routes.test.mjs +0 -77
  11. package/test/gtfs-shapes-loader.test.mjs +0 -105
  12. package/test/gtfs-shapes.test.mjs +0 -23
  13. package/test/gtfs-stop-loader.test.mjs +0 -415
  14. package/test/gtfs-stop-times.test.mjs +0 -37
  15. package/test/gtfs-stops.test.mjs +0 -311
  16. package/test/gtfs-trip-loader.test.mjs +0 -188
  17. package/test/gtfs-trips.test.mjs +0 -108
  18. package/test/line-reader.test.mjs +0 -120
  19. package/test/merge-stops-full.test.mjs +0 -118
  20. package/test/merge-stops.test.mjs +0 -376
  21. package/test/new-metro-loader-test.test.mjs +0 -244
  22. package/test/new-tram-format.test.mjs +0 -49
  23. package/test/newer-metro-loader-test.test.mjs +0 -80
  24. package/test/newer-tram-format.test.mjs +0 -67
  25. package/test/ptv-gtfs.test.mjs +0 -106
  26. package/test/sample-data/calendar/calendar.txt +0 -34
  27. package/test/sample-data/calendar/calendar_dates.txt +0 -26
  28. package/test/sample-data/gtfs-splitting/force_unique_stops_bus.txt +0 -7
  29. package/test/sample-data/gtfs-splitting/force_unique_stops_tram.txt +0 -3
  30. package/test/sample-data/gtfs-splitting/general-stores.txt +0 -19
  31. package/test/sample-data/gtfs-splitting/line_reader_test.txt +0 -8
  32. package/test/sample-data/gtfs-splitting/name_overrides.txt +0 -4
  33. package/test/sample-data/gtfs-splitting/sample_stops.txt +0 -20
  34. package/test/sample-data/gtfs-splitting/special_chars_stops_csv.txt +0 -6
  35. package/test/sample-data/gtfs-splitting/stops_without_secondary.txt +0 -6
  36. package/test/sample-data/gtfs-splitting/union_with_spaces.txt +0 -5
  37. package/test/sample-data/gtfs-unzipping/gtfs.zip +0 -0
  38. package/test/sample-data/merge/shapes.txt +0 -1224
  39. package/test/sample-data/merge/stop_times.txt +0 -103
  40. package/test/sample-data/merge/stops.txt +0 -39
  41. package/test/sample-data/merge/trips.txt +0 -7
  42. package/test/sample-data/mtm-rail/stops.txt +0 -4
  43. package/test/sample-data/new-metro/calendar.txt +0 -13
  44. package/test/sample-data/new-metro/calendar_dates.txt +0 -1
  45. package/test/sample-data/new-metro/routes.txt +0 -18
  46. package/test/sample-data/new-metro/simple_stop_test.txt +0 -6
  47. package/test/sample-data/new-metro/stop_times.txt +0 -64
  48. package/test/sample-data/new-metro/stops.txt +0 -139
  49. package/test/sample-data/new-metro/trips.txt +0 -6
  50. package/test/sample-data/new-metro-2/calendar.txt +0 -13
  51. package/test/sample-data/new-metro-2/calendar_dates.txt +0 -1
  52. package/test/sample-data/new-metro-2/routes.txt +0 -2
  53. package/test/sample-data/new-metro-2/stop_times.txt +0 -5
  54. package/test/sample-data/new-metro-2/stops.txt +0 -9
  55. package/test/sample-data/new-metro-2/trips.txt +0 -2
  56. package/test/sample-data/new-tram/routes.txt +0 -25
  57. package/test/sample-data/new-tram/stops.txt +0 -5
  58. package/test/sample-data/new-tram-2/routes.txt +0 -25
  59. package/test/sample-data/new-tram-2/stops.txt +0 -8
  60. package/test/sample-data/one_line.csv +0 -2
  61. package/test/sample-data/parent-stop/multi_stop.txt +0 -4
  62. package/test/sample-data/parent-stop/stops.txt +0 -15
  63. package/test/sample-data/routes/agency.txt +0 -12
  64. package/test/sample-data/routes/bad_routes.txt +0 -4
  65. package/test/sample-data/routes/metro_bus_routes.txt +0 -4
  66. package/test/sample-data/routes/metro_lines.txt +0 -8
  67. package/test/sample-data/routes/regional_bus_routes.txt +0 -7
  68. package/test/sample-data/routes/vline.txt +0 -7
  69. package/test/sample-data/routes/west_gipp_transit.txt +0 -20
  70. package/test/sample-data/shapes/shapes.txt +0 -89
  71. package/test/sample-data/shapes/stop_times.txt +0 -9
  72. package/test/sample-data/shapes/stops.txt +0 -4
  73. package/test/sample-data/shapes/trips.txt +0 -4
  74. package/test/sample-data/stop-data/name-overrides.json +0 -25
  75. package/test/sample-data/stop-data/unique-stops.json +0 -1
  76. package/test/sample-data/suburbs.json +0 -77
  77. package/test/sample-data/trips/bus_trips.txt +0 -5
  78. package/test/sample-data/trips/stop_times.txt +0 -29
  79. package/test/sample-data/trips/stops.txt +0 -93
  80. package/test/sample-data/trips/trips.txt +0 -5
  81. package/test/utils-test.mjs +0 -12
@@ -23,7 +23,7 @@ export default class GTFSRoute {
23
23
  this.agencyID = agencyID
24
24
  this.routeName = routeName
25
25
 
26
- if (routeNumber !== null && routeNumber.length === 0) routeNumber = null
26
+ if (!routeNumber || routeNumber.toString().trim().length === 0) routeNumber = null
27
27
  this.routeNumber = routeNumber
28
28
  }
29
29
 
@@ -57,7 +57,7 @@ export default class GTFSRoute {
57
57
  */
58
58
  static create(data, mode) {
59
59
  let { routeGTFSID } = data
60
- let routeTypes = [ RailGTFSRoute, NewGTFSRoute, SmartrakGTFSRoute, GTFSRoute ]
60
+ let routeTypes = [ RailGTFSRoute, NewGTFSRoute, SmartrakGTFSRoute, MTMRailRoute, GTFSRoute ]
61
61
  for (let type of routeTypes) {
62
62
  if (type.canProcess(routeGTFSID, mode)) return new type(data)
63
63
  }
@@ -78,7 +78,7 @@ class SmartrakGTFSRoute extends GTFSRoute {
78
78
  parseRouteID(routeGTFSID) {
79
79
  let [ _, routeNumber ] = routeGTFSID.match(/^\d+\-(\w+)/)
80
80
  return `4-${routeNumber}`
81
- }
81
+ }
82
82
 
83
83
  static canProcess(routeGTFSID, mode) {
84
84
  return routeGTFSID.includes('-aus-')
@@ -100,7 +100,7 @@ class NewGTFSRoute extends GTFSRoute {
100
100
  parseRouteID(routeGTFSID) {
101
101
  let [ _, modeID, routeNumber ] = routeGTFSID.match(/vic-0*(\d+)\-(\w+)/)
102
102
  return `${modeID}-${routeNumber}`
103
- }
103
+ }
104
104
 
105
105
  }
106
106
 
@@ -122,3 +122,21 @@ class RailGTFSRoute extends NewGTFSRoute {
122
122
  }
123
123
 
124
124
  }
125
+
126
+ class MTMRailRoute extends GTFSRoute {
127
+
128
+ constructor(data) {
129
+ super(data)
130
+ this.routeName = 'Rail Replacement Bus'
131
+ this.routeNumber = null
132
+ }
133
+
134
+ static canProcess(routeGTFSID, mode) {
135
+ return !routeGTFSID.match(/^\d+-\w{1,3}/) && mode === 'metro train'
136
+ }
137
+
138
+ parseRouteID(routeGTFSID) {
139
+ return '2-RRB'
140
+ }
141
+
142
+ }
@@ -48,7 +48,7 @@ export default class GTFSTrip {
48
48
  * @returns {GTFSTrip} A trip instance representing the data being passed in
49
49
  */
50
50
  static create(data, mode) {
51
- let types = [ SmartrakTrip, MetroTrip, GTFSTrip ]
51
+ let types = [ SmartrakTrip, MetroTrip, MTMRailTrip, GTFSTrip ]
52
52
  for (let type of types) {
53
53
  if (type.canProcess(data, mode)) return new type(data)
54
54
  }
@@ -189,4 +189,26 @@ export class MetroRRBTrip extends MetroTrip {
189
189
  return super.canProcess(data, mode) && data.id.match(/^02-\w{3}-R-/)
190
190
  }
191
191
 
192
+ }
193
+
194
+ export class MTMRailTrip extends GTFSTrip {
195
+
196
+ constructor(data) {
197
+ if (data.headsign && data.headsign.length === 6 && data.headsign.match(/[A-Z]{3}\d{3}/)) {
198
+ data.block = data.headsign
199
+ }
200
+
201
+ super(data)
202
+ }
203
+
204
+ static canProcess(data, mode) {
205
+ return mode === TRANSIT_MODES.metroTrain && data.routeGTFSID === '2-RRB'
206
+ }
207
+
208
+ getTripData() {
209
+ return {
210
+ ...super.getTripData(),
211
+ isRailReplacementBus: true
212
+ }
213
+ }
192
214
  }
@@ -21,6 +21,7 @@ export default class GTFSCalendarReader extends GTFSReader {
21
21
  async open() {
22
22
  await super.open()
23
23
 
24
+ if (!this.#calendarDateFile) return
24
25
  let calendarDateReader = new GTFSCalendarDateReader(this.#calendarDateFile)
25
26
  await calendarDateReader.open()
26
27
  while (calendarDateReader.available()) {
@@ -58,12 +58,7 @@ export default class CSVLineReader {
58
58
  }
59
59
 
60
60
  #setHeaders(line) {
61
- let parts
62
- if (line.includes('"')) {
63
- parts = line.match(/"([^"]*)"/g).map(f => f.slice(1, -1))
64
- } else {
65
- parts = line.split(',')
66
- }
61
+ let parts = [ ...line.matchAll(/(?:,"|^")(""|[\w\W]*?)(?=",|"$)|(?:,(?!")|^(?!"))([^,]*?)(?=$|,)|(\r\n|\n)/gm) ].map(match => match[1] || match[2])
67
62
 
68
63
  return this.#headers.reduce((acc, header, i) => {
69
64
  acc[header] = parts[i]
@@ -105,6 +105,7 @@ export default class TripLoader {
105
105
 
106
106
  let stopTimings = await async.map(stops, async stop => {
107
107
  let stopData = await this.findMatchingStop(stop.stopID)
108
+ if (!stopData) throw new Error(`Failed to match stop ${stop.stopID} in trip ${tripData.getTripID()}`)
108
109
  let bay = stopData.bays.find(bay => bay.stopGTFSID === stop.stopID)
109
110
 
110
111
  let data = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@transportme/load-ptv-gtfs",
3
- "version": "1.4.2",
3
+ "version": "1.4.3",
4
4
  "description": "",
5
5
  "main": "index.mjs",
6
6
  "scripts": {
@@ -1,46 +0,0 @@
1
- # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
2
- # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
3
-
4
- name: Node.js CI
5
-
6
- on:
7
- push:
8
- branches: [ "master" ]
9
- pull_request:
10
- branches: [ "master" ]
11
-
12
- jobs:
13
- build:
14
-
15
- runs-on: ubuntu-latest
16
-
17
- steps:
18
- - uses: actions/checkout@v4
19
- with:
20
- submodules: recursive
21
-
22
- - name: Cache node modules
23
- id: cache-npm
24
- uses: actions/cache@v4
25
- env:
26
- cache-name: cache-node-modules
27
- with:
28
- # npm cache files are stored in `~/.npm` on Linux/macOS
29
- path: ~/.npm
30
- key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
31
- restore-keys: |
32
- ${{ runner.os }}-build-${{ env.cache-name }}-
33
- ${{ runner.os }}-build-
34
- ${{ runner.os }}-
35
-
36
- - name: Use Node.js 20.x
37
- uses: actions/setup-node@v4
38
- with:
39
- node-version: '20.x'
40
- cache: 'npm'
41
-
42
- - name: Install dependencies
43
- run: npm install
44
-
45
- - name: Run test
46
- run: npm test
@@ -1,182 +0,0 @@
1
- import { expect } from 'chai'
2
- import GTFSCalendarReader from '../lib/gtfs-parser/readers/GTFSCalendarReader.mjs'
3
- import GTFSCalendarDateReader from '../lib/gtfs-parser/readers/GTFSCalendarDatesReader.mjs'
4
- import GTFSCalendar from '../lib/gtfs-parser/GTFSCalendar.mjs'
5
- import GTFSCalendarDate from '../lib/gtfs-parser/GTFSCalendarDate.mjs'
6
- import path from 'path'
7
- import url from 'url'
8
- import { CALENDAR_DATES } from '../lib/constants.mjs'
9
- import { dateUtils } from '@transportme/transportvic-utils'
10
-
11
- const __filename = url.fileURLToPath(import.meta.url)
12
- const __dirname = path.dirname(__filename)
13
-
14
- const calendarFile = path.join(__dirname, 'sample-data', 'calendar', 'calendar.txt')
15
- const calendarDatesFile = path.join(__dirname, 'sample-data', 'calendar', 'calendar_dates.txt')
16
-
17
- describe('The GTFSCalendar class', () => {
18
- it('Should process the calendar data to a list of operation days', () => {
19
- expect(new GTFSCalendar({
20
- id: 'T1',
21
- startDay: '20241122',
22
- endDay: '20241129',
23
- daysOfWeek: ["0","0","0","0","1","1","0"]
24
- }).getOperationDays()).to.have.members([
25
- "20241122", "20241123", "20241129"
26
- ])
27
-
28
- expect(new GTFSCalendar({
29
- id: 'T2',
30
- startDay: '20241123',
31
- endDay: '20241130',
32
- daysOfWeek: ["1","1","1","1","1","0","0"]
33
- }).getOperationDays()).to.have.members([
34
- "20241125", "20241126", "20241127", "20241128", "20241129"
35
- ])
36
- })
37
-
38
- it('Be inclusive of the end date', () => {
39
- expect(new GTFSCalendar({
40
- id: 'T1',
41
- startDay: '20241122',
42
- endDay: '20241122',
43
- daysOfWeek: ["1","1","1","1","1","1","1"]
44
- }).getOperationDays()).to.have.members([
45
- "20241122"
46
- ])
47
- })
48
-
49
- it('Should allow adding of an extra date into the calendar', () => {
50
- let cal = new GTFSCalendar({
51
- id: 'T1',
52
- startDay: '20241122',
53
- endDay: '20241122',
54
- daysOfWeek: ["1","1","1","1","1","1","1"]
55
- }, [new GTFSCalendarDate({
56
- id: 'T1',
57
- date: '20241125',
58
- type: CALENDAR_DATES.ADDED
59
- })])
60
-
61
- expect(cal.getOperationDays()).to.have.members([
62
- "20241122", "20241125"
63
- ])
64
- })
65
-
66
- it('Should allow removing of a specific date from the calendar', () => {
67
- let cal = new GTFSCalendar({
68
- id: 'T1',
69
- startDay: '20241224',
70
- endDay: '20241226',
71
- daysOfWeek: ["1","1","1","1","1","1","1"]
72
- }, [new GTFSCalendarDate({
73
- id: 'T1',
74
- date: '20241225',
75
- type: CALENDAR_DATES.REMOVED
76
- })])
77
-
78
- expect(cal.getOperationDays()).to.deep.equal([
79
- "20241224", "20241226"
80
- ])
81
- })
82
-
83
- it('Should ensure that adding an extra date into the calendar does not introduce duplicates', () => {
84
- let cal = new GTFSCalendar({
85
- id: 'T1',
86
- startDay: '20241122',
87
- endDay: '20241122',
88
- daysOfWeek: ["1","1","1","1","1","1","1"]
89
- }, [new GTFSCalendarDate({
90
- id: 'T1',
91
- date: '20241122',
92
- type: CALENDAR_DATES.ADDED
93
- })])
94
-
95
- expect(cal.getOperationDays()).to.deep.equal([
96
- "20241122"
97
- ])
98
- })
99
-
100
- it('Should ensure that duplicate extra dates do not introduce duplicates', () => {
101
- let calendarDate = new GTFSCalendarDate({
102
- id: 'T1',
103
- date: '20241122',
104
- type: CALENDAR_DATES.ADDED
105
- })
106
-
107
- let cal = new GTFSCalendar({
108
- id: 'T1',
109
- startDay: '20241122',
110
- endDay: '20241122',
111
- daysOfWeek: ["1","1","1","1","1","1","1"]
112
- }, [calendarDate, calendarDate, calendarDate])
113
-
114
- expect(cal.getOperationDays()).to.deep.equal([
115
- "20241122"
116
- ])
117
- })
118
-
119
- it('Should ensure that the date exists when removing a specific date from the calendar, and not do anything otherwise', () => {
120
- let cal = new GTFSCalendar({
121
- id: 'T1',
122
- startDay: '20241224',
123
- endDay: '20241226',
124
- daysOfWeek: ["1","1","1","1","1","1","1"]
125
- }, [new GTFSCalendarDate({
126
- id: 'T1',
127
- date: '20241227',
128
- type: CALENDAR_DATES.REMOVED
129
- })])
130
-
131
- expect(cal.getOperationDays()).to.deep.equal([
132
- "20241224", "20241225", "20241226"
133
- ])
134
- })
135
- })
136
-
137
- describe('The GTFSCalendarDate class', () => {
138
- it('Should convert the CSV data into a proper date object', () => {
139
- expect(new GTFSCalendarDate({
140
- id: 'T6',
141
- date: '20241125',
142
- type: CALENDAR_DATES.ADDED
143
- }).date.toUTC().toISO()).to.equal('2024-11-24T13:00:00.000Z')
144
- })
145
- })
146
-
147
- describe('The GTFSCalendarDatesReader class', () => {
148
- it('Should read a calendar dates and return a GTFSCalendarDate object', async () => {
149
- let reader = new GTFSCalendarDateReader(calendarDatesFile)
150
- await reader.open()
151
-
152
- let date = await reader.getNextEntity()
153
- expect(date.id).to.equal('T3_2')
154
- expect(dateUtils.toGTFSDate(date.date)).to.equal('20250126')
155
- expect(date.type).to.equal(CALENDAR_DATES.REMOVED)
156
-
157
- let date2 = await reader.getNextEntity()
158
- expect(date2.id).to.equal('T5_2')
159
- expect(dateUtils.toGTFSDate(date2.date)).to.equal('20241225')
160
- expect(date2.type).to.equal(CALENDAR_DATES.REMOVED)
161
- })
162
- })
163
-
164
- describe('The GTFSCalendarReader class', () => {
165
- it('Should read both a calendar dates and calendar file and return a GTFSCalendar object with the appropriate exceptions filled', async () => {
166
- let reader = new GTFSCalendarReader(calendarFile, calendarDatesFile)
167
- await reader.open()
168
-
169
- let allCalendars = {}
170
- while (reader.available()) {
171
- let calendar = await reader.getNextEntity()
172
- allCalendars[calendar.id] = calendar
173
- }
174
-
175
- expect(allCalendars['T2'].getOperationDays()).to.deep.equal(["20241109"])
176
-
177
- expect(allCalendars['T3_2'].getOperationDays()).to.contain("20241124")
178
- expect(allCalendars['T3_2'].getOperationDays()).to.contain("20241201")
179
- expect(allCalendars['T3_2'].getOperationDays()).to.contain("20250209")
180
- expect(allCalendars['T3_2'].getOperationDays()).to.not.contain("20250126")
181
- })
182
- })
@@ -1,208 +0,0 @@
1
- import { LokiDatabaseConnection } from '@transportme/database'
2
- import RouteLoader from '../lib/loader/RouteLoader.mjs'
3
- import GTFSAgencyReader from '../lib/gtfs-parser/readers/GTFSAgencyReader.mjs'
4
-
5
- import { TRANSIT_MODES } from '../lib/constants.mjs'
6
-
7
- import path from 'path'
8
- import url from 'url'
9
- import { expect } from 'chai'
10
- import GTFSAgency from '../lib/gtfs-parser/GTFSAgency.mjs'
11
-
12
- const __filename = url.fileURLToPath(import.meta.url)
13
- const __dirname = path.dirname(__filename)
14
-
15
- const regionalRoutesFile = path.join(__dirname, 'sample-data', 'routes', 'regional_bus_routes.txt')
16
- const metroRoutesFile = path.join(__dirname, 'sample-data', 'routes', 'metro_bus_routes.txt')
17
- const metroLinesFile = path.join(__dirname, 'sample-data', 'routes', 'metro_lines.txt')
18
- const agencyFile = path.join(__dirname, 'sample-data', 'routes', 'agency.txt')
19
- const wgtFile = path.join(__dirname, 'sample-data', 'routes', 'west_gipp_transit.txt')
20
- const vlineFile = path.join(__dirname, 'sample-data', 'routes', 'vline.txt')
21
-
22
- const badRoutesFile_NoAgency = path.join(__dirname, 'sample-data', 'routes', 'bad_routes.txt')
23
-
24
- describe('The GTFS Agency Reader', () => {
25
- it('Should read the agencies one line at a time', async () => {
26
- let reader = new GTFSAgencyReader(agencyFile)
27
- await reader.open()
28
- let operator = await reader.getNextEntity()
29
-
30
- expect(operator.id).to.equal('43')
31
- expect(operator.name).to.equal('CDC Ballarat')
32
- expect(operator.website).to.equal('https://www.ptv.vic.gov.au/footer/customer-service/operator-contact-details/?utm_source=open_data_click_agency&utm_medium=open_data_agency_click&utm_campaign=open_data_click')
33
- expect(operator.phone).to.equal('1800 800 007')
34
- })
35
- })
36
-
37
- describe('The GTFS Routes Loader', () => {
38
- it('Should process the routes and add them to the database', async () => {
39
- let database = new LokiDatabaseConnection('test-db')
40
- let routes = await database.createCollection('gtfs-routes')
41
-
42
- let loader = new RouteLoader(regionalRoutesFile, agencyFile, TRANSIT_MODES.bus, database)
43
- await loader.loadRoutes()
44
-
45
- let bal10 = await routes.findDocument({
46
- routeGTFSID: '6-10x'
47
- })
48
-
49
- expect(bal10).to.not.be.null
50
- expect(bal10.routeNumber).to.equal('10')
51
- expect(bal10.operators).to.deep.equal(['CDC Ballarat'])
52
- })
53
-
54
- it('Should open the agency.txt file and load all operators', async () => {
55
- let database = new LokiDatabaseConnection('test-db')
56
- await database.createCollection('gtfs-routes')
57
-
58
- let loader = new RouteLoader(regionalRoutesFile, agencyFile, TRANSIT_MODES.bus, database)
59
- await loader.loadAgencies()
60
-
61
- let paynesville = loader.getOperator('99')
62
-
63
- expect(paynesville).to.not.be.undefined
64
- expect(paynesville.name).to.equal('Paynesville Bus Lines')
65
- })
66
-
67
- it('Should handle smartrak routes', async () => {
68
- let database = new LokiDatabaseConnection('test-db')
69
- let routes = await database.createCollection('gtfs-routes')
70
-
71
- let loader = new RouteLoader(metroRoutesFile, agencyFile, TRANSIT_MODES.bus, database)
72
- await loader.loadRoutes()
73
-
74
- let smart900 = await routes.findDocument({
75
- routeGTFSID: '4-900'
76
- })
77
-
78
- expect(smart900).to.not.be.null
79
- expect(smart900.mode).to.equal(TRANSIT_MODES.bus)
80
- expect(smart900.routeNumber).to.equal('900')
81
- expect(smart900.operators).to.have.members(['CDC', 'Ventura Bus Lines'])
82
- })
83
-
84
- it('Should handle metro train routes', async () => {
85
- let database = new LokiDatabaseConnection('test-db')
86
- let routes = await database.createCollection('gtfs-routes')
87
-
88
- let loader = new RouteLoader(metroLinesFile, agencyFile, TRANSIT_MODES.metroTrain, database)
89
- await loader.loadRoutes()
90
-
91
- let stony = await routes.findDocument({
92
- routeGTFSID: '2-STY'
93
- })
94
-
95
- expect(stony).to.not.be.null
96
- expect(stony.mode).to.equal('metro train')
97
- expect(stony.routeNumber).to.be.null
98
- expect(stony.routeName).to.equal('Stony Point')
99
- expect(stony.operators).to.have.members(['Metro'])
100
- })
101
-
102
- it('Should allow for custom route rewriting', async () => {
103
- let database = new LokiDatabaseConnection('test-db')
104
- let routes = await database.createCollection('gtfs-routes')
105
-
106
- let loader = new RouteLoader(wgtFile, agencyFile, TRANSIT_MODES.bus, database)
107
- await loader.loadRoutes({
108
- processRoute: route => {
109
- if (route.routeGTFSID.match(/6-w\d\d/)) {
110
- route.routeGTFSID = '6-WGT'
111
- route.routeName = 'West Gippsland Transit'
112
- }
113
-
114
- return route
115
- }
116
- })
117
-
118
- let wgt = await routes.findDocument({
119
- routeGTFSID: '6-WGT'
120
- })
121
-
122
- expect(wgt, 'Expected West Gippsland Transit route 6-WGT to be created').to.not.be.null
123
- expect(wgt.mode).to.equal('bus')
124
- expect(wgt.routeNumber).to.be.null
125
- expect(wgt.routeName).to.equal('West Gippsland Transit')
126
- expect(wgt.operators).to.have.members(['Warragul Bus Lines'])
127
-
128
- expect(await routes.findDocument({
129
- routeGTFSID: '6-w40'
130
- }), 'Expected 6-w40 Moe - Garfield to not exist').to.be.null
131
-
132
- expect(await routes.findDocument({
133
- routeGTFSID: '6-w41'
134
- }), 'Expected 6-w41 Warragul - Pakenham to not exist').to.be.null
135
-
136
- let nojee = await routes.findDocument({
137
- routeGTFSID: '6-W89'
138
- })
139
- expect(nojee, 'Expected 6-W89 Warragul - Nojee to exist and not be modified').to.not.be.null
140
- expect(nojee.mode).to.equal('bus')
141
- expect(nojee.routeNumber).to.equal('89')
142
- expect(nojee.routeName).to.equal('Noojee - Warragul Station Via Buln Buln')
143
- expect(nojee.operators).to.have.members(['Warragul Bus Lines'])
144
- })
145
-
146
-
147
- it('Should return a route ID map', async () => {
148
- let database = new LokiDatabaseConnection('test-db')
149
- let routes = await database.createCollection('gtfs-routes')
150
-
151
- let loader = new RouteLoader(wgtFile, agencyFile, TRANSIT_MODES.bus, database)
152
- await loader.loadRoutes({
153
- processRoute: route => {
154
- if (route.routeGTFSID.match(/6-w\d\d/)) {
155
- route.routeGTFSID = '6-WGT'
156
- route.routeName = 'West Gippsland Transit'
157
- }
158
-
159
- return route
160
- }
161
- })
162
-
163
- let map = loader.getRouteIDMap()
164
- expect(map['6-w19-mjp-1']).to.equal('6-WGT')
165
- expect(map['6-W89-mjp-1']).to.equal('6-W89')
166
- })
167
-
168
- it('Should allow for dropping of unwanted routes', async () => {
169
- let database = new LokiDatabaseConnection('test-db')
170
- let routes = await database.createCollection('gtfs-routes')
171
-
172
- let loader = new RouteLoader(vlineFile, agencyFile, TRANSIT_MODES.regionalTrain, database)
173
- await loader.loadRoutes({
174
- processRoute: route => {
175
- if (route.routeGTFSID === '1-vPK') return null
176
- return route
177
- }
178
- })
179
-
180
- expect(await routes.findDocument({
181
- routeGTFSID: '1-vPK'
182
- }), 'Expected 1-vPK V/Line Dandenong Metro Services to not exist').to.be.null
183
-
184
- let traralgon = await routes.findDocument({
185
- routeGTFSID: '1-TRN'
186
- })
187
- expect(traralgon, 'Expected 1-TRN Traralgon to exist and not be modified').to.not.be.null
188
- expect(traralgon.mode).to.equal('regional train')
189
- expect(traralgon.routeNumber).to.be.null
190
- expect(traralgon.routeName).to.equal('Traralgon - Melbourne Via Pakenham, Moe & Morwell')
191
- expect(traralgon.operators).to.have.members(['V/Line'])
192
- })
193
-
194
- it('Should handle invalid agency references', async () => {
195
- let database = new LokiDatabaseConnection('test-db')
196
- let routes = await database.createCollection('gtfs-routes')
197
-
198
- let loader = new RouteLoader(badRoutesFile_NoAgency, agencyFile, TRANSIT_MODES.bus, database)
199
- await loader.loadRoutes()
200
-
201
- let skybus = await routes.findDocument({
202
- routeGTFSID: '11-SKY'
203
- })
204
-
205
- expect(skybus).to.not.be.null
206
- expect(skybus.operators).to.deep.equal([GTFSAgency.UNKNOWN_AGENCY.name])
207
- })
208
- })
@@ -1,77 +0,0 @@
1
- import { expect } from 'chai'
2
- import GTFSRouteReader from '../lib/gtfs-parser/readers/GTFSRouteReader.mjs'
3
- import { TRANSIT_MODES } from '../lib/constants.mjs'
4
- import path from 'path'
5
- import url from 'url'
6
-
7
- const __filename = url.fileURLToPath(import.meta.url)
8
- const __dirname = path.dirname(__filename)
9
-
10
- const regionalRoutesFile = path.join(__dirname, 'sample-data', 'routes', 'regional_bus_routes.txt')
11
- const metroRoutesFile = path.join(__dirname, 'sample-data', 'routes', 'metro_bus_routes.txt')
12
-
13
- let regionalBusRoute = {
14
- route_id: '6-10x-mjp-1',
15
- agency_id: '43',
16
- route_short_name: '10',
17
- route_long_name: 'Alfredton - Ballarat Station Via Wendouree'
18
- }
19
-
20
- describe('The GTFSRouteReader class', () => {
21
- describe('The processRoute function', () => {
22
- it('Should take in the raw CSV line and process it', () => {
23
- let routeData = new GTFSRouteReader('', TRANSIT_MODES.bus).processEntity(regionalBusRoute)
24
-
25
- expect(routeData.routeGTFSID).to.equal('6-10x')
26
- expect(routeData.agencyID).to.equal(regionalBusRoute.agency_id)
27
- expect(routeData.routeNumber).to.equal(regionalBusRoute.route_short_name)
28
- expect(routeData.routeName).to.equal(regionalBusRoute.route_long_name)
29
- })
30
- })
31
-
32
- describe('The getNextEntity function', () => {
33
- it('Should read the CSV file and return the next stop', async () => {
34
- let routeReader = new GTFSRouteReader(regionalRoutesFile, TRANSIT_MODES.bus)
35
- await routeReader.open()
36
-
37
- await routeReader.getNextEntity()
38
- let routeData = await routeReader.getNextEntity()
39
-
40
- expect(routeData.agencyID).to.equal('99')
41
- expect(routeData.routeNumber).to.equal('13')
42
- expect(routeData.routeName).to.equal('Paynesville - Bairnsdale Via Paynesville')
43
- })
44
- })
45
- })
46
-
47
- describe('The GTFSRoute class', () => {
48
- describe('The route ID parsing', () => {
49
- it('Should parse a regular route ID and remove the -mjp- portion', async () => {
50
- let routeReader = new GTFSRouteReader(regionalRoutesFile, TRANSIT_MODES.bus)
51
- await routeReader.open()
52
-
53
- let routeData = await routeReader.getNextEntity()
54
-
55
- expect(routeData.routeGTFSID).to.equal('6-10x')
56
- })
57
-
58
- it('Should parse a regular route ID and pad with 0s as needed', async () => {
59
- let routeReader = new GTFSRouteReader(regionalRoutesFile, TRANSIT_MODES.bus)
60
- await routeReader.open()
61
-
62
- await routeReader.getNextEntity()
63
- let routeData = await routeReader.getNextEntity()
64
-
65
- expect(routeData.routeGTFSID).to.equal('6-013')
66
- })
67
-
68
- it('Should detect a smartrak route ID and convert to a legacy 4-XXX trip', async () => {
69
- let routeReader = new GTFSRouteReader(metroRoutesFile, TRANSIT_MODES.bus)
70
- await routeReader.open()
71
-
72
- expect((await routeReader.getNextEntity()).routeGTFSID).to.equal('4-900')
73
- expect((await routeReader.getNextEntity()).routeGTFSID).to.equal('4-900')
74
- expect((await routeReader.getNextEntity()).routeGTFSID).to.equal('4-601')
75
- })
76
- })
77
- })