@tmlmobilidade/generate-offer-files 20251021.1643.15 → 20251229.1447.48

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.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  /* * */
3
3
  import { generateOfferOutput } from './main.js';
4
- import LOGGER from '@helperkits/logger';
5
- import { ASCII_CM_SHORT } from '@tmlmobilidade/lib';
4
+ import { ASCII_CM_SHORT } from '@tmlmobilidade/consts';
5
+ import { Logger } from '@tmlmobilidade/logger';
6
6
  import { validateOperationalDate } from '@tmlmobilidade/types';
7
7
  import { Command } from 'commander';
8
8
  import fs from 'fs';
@@ -30,29 +30,29 @@ import fs from 'fs';
30
30
  options.endDate = validateOperationalDate(options.endDate);
31
31
  }
32
32
  catch (error) {
33
- LOGGER.divider();
34
- LOGGER.error(`--start-date and/or --end-date are not valid:`, error.message);
35
- LOGGER.divider();
33
+ Logger.divider();
34
+ Logger.error(`--start-date and/or --end-date are not valid:`, error.message);
35
+ Logger.divider();
36
36
  return;
37
37
  }
38
38
  //
39
39
  // Ensure the output directory exists and is empty
40
40
  if (fs.existsSync(options.outputDir) && !options.override) {
41
- LOGGER.divider();
42
- LOGGER.error(`Output directory "${options.outputDir}" already exists. Please remove it or change it before running the script.`);
43
- LOGGER.divider();
41
+ Logger.divider();
42
+ Logger.error(`Output directory "${options.outputDir}" already exists. Please remove it or change it before running the script.`);
43
+ Logger.divider();
44
44
  return;
45
45
  }
46
46
  if (fs.existsSync(options.outputDir) && options.override) {
47
- LOGGER.info(`Output directory "${options.outputDir}" already exists. It will be overridden.`);
47
+ Logger.info(`Output directory "${options.outputDir}" already exists. It will be overridden.`);
48
48
  fs.rmSync(options.outputDir, { recursive: true });
49
49
  }
50
50
  fs.mkdirSync(options.outputDir, { recursive: true });
51
51
  //
52
52
  // Log the ASCII art
53
- LOGGER.spacer(3);
53
+ Logger.spacer(3);
54
54
  console.log(ASCII_CM_SHORT);
55
- LOGGER.spacer(3);
55
+ Logger.spacer(3);
56
56
  //
57
57
  // Start the offer generation process
58
58
  await generateOfferOutput(options.file, options.startDate, options.endDate, options.outputDir, options.feedId);
package/dist/main.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { type OperationalDate } from '@tmlmobilidade/types';
2
+ export declare function generateOfferOutput(filePath: string, startDate: OperationalDate, endDate: OperationalDate, outputDir: string, feedId: null | string): Promise<void>;
package/dist/main.js CHANGED
@@ -1,9 +1,10 @@
1
1
  /* * */
2
- import LOGGER from '@helperkits/logger';
3
- import TIMETRACKER from '@helperkits/timer';
4
- import { JsonWriter } from '@helperkits/writer';
2
+ import { Dates, getOperationalDatesFromRange } from '@tmlmobilidade/dates';
3
+ import { toMetersFromKilometersOrMeters } from '@tmlmobilidade/geo';
4
+ import { Logger } from '@tmlmobilidade/logger';
5
+ import { Timer } from '@tmlmobilidade/timer';
5
6
  import { validateGtfsCalendar, validateGtfsCalendarDate, validateGtfsRouteExtended, validateGtfsStopExtended, validateGtfsStopTime, validateGtfsTripExtended } from '@tmlmobilidade/types';
6
- import { Dates, getOperationalDatesFromRange, toMetersFromKilometersOrMeters } from '@tmlmobilidade/utils';
7
+ import { JsonWriter } from '@tmlmobilidade/writers';
7
8
  import { parse as csvParser } from 'csv-parse';
8
9
  import extract from 'extract-zip';
9
10
  import fs from 'fs';
@@ -11,8 +12,8 @@ import fs from 'fs';
11
12
  export async function generateOfferOutput(filePath, startDate, endDate, outputDir, feedId) {
12
13
  try {
13
14
  //
14
- LOGGER.init();
15
- const globalTimer = new TIMETRACKER();
15
+ Logger.init();
16
+ const globalTimer = new Timer();
16
17
  //
17
18
  // Setup the JSON batch writers to speed up the writing process and
18
19
  // reduce the number of filesystem operations. These writers keep data
@@ -39,20 +40,20 @@ export async function generateOfferOutput(filePath, startDate, endDate, outputDi
39
40
  try {
40
41
  fs.rmSync(workdirPath, { recursive: true });
41
42
  fs.mkdirSync(workdirPath, { recursive: true });
42
- LOGGER.success('Prepared working directory.');
43
- LOGGER.spacer(1);
43
+ Logger.success('Prepared working directory.');
44
+ Logger.spacer(1);
44
45
  }
45
46
  catch (error) {
46
- LOGGER.error(`Error preparing workdir path "${workdirPath}".`, error);
47
+ Logger.error(`Error preparing workdir path "${workdirPath}".`, error);
47
48
  process.exit(1);
48
49
  }
49
50
  try {
50
51
  await unzipFile(filePath, extractDirPath);
51
- LOGGER.success(`Unzipped GTFS file from "${filePath}" to "${extractDirPath}".`);
52
- LOGGER.spacer(1);
52
+ Logger.success(`Unzipped GTFS file from "${filePath}" to "${extractDirPath}".`);
53
+ Logger.spacer(1);
53
54
  }
54
55
  catch (error) {
55
- LOGGER.error('Error unzipping the file.', error);
56
+ Logger.error('Error unzipping the file.', error);
56
57
  process.exit(1);
57
58
  }
58
59
  //
@@ -71,7 +72,7 @@ export async function generateOfferOutput(filePath, startDate, endDate, outputDi
71
72
  // that are valid between the given start_date and end_date.
72
73
  try {
73
74
  //
74
- LOGGER.info(`Reading zip entry "calendar.txt"...`);
75
+ Logger.info(`Reading zip entry "calendar.txt"...`);
75
76
  const parseEachRow = async (data) => {
76
77
  //
77
78
  //
@@ -120,17 +121,17 @@ export async function generateOfferOutput(filePath, startDate, endDate, outputDi
120
121
  // Setup the CSV parsing operation only if the file exists
121
122
  if (fs.existsSync(`${extractDirPath}/calendar.txt`)) {
122
123
  await parseCsvFile(`${extractDirPath}/calendar.txt`, parseEachRow);
123
- LOGGER.success(`Finished processing "calendar.txt"`);
124
- LOGGER.spacer(1);
124
+ Logger.success(`Finished processing "calendar.txt"`);
125
+ Logger.spacer(1);
125
126
  }
126
127
  else {
127
- LOGGER.info(`Optional file "calendar.txt" not found. This may or may not be an error. Proceeding...`);
128
- LOGGER.spacer(1);
128
+ Logger.info(`Optional file "calendar.txt" not found. This may or may not be an error. Proceeding...`);
129
+ Logger.spacer(1);
129
130
  }
130
131
  //
131
132
  }
132
133
  catch (error) {
133
- LOGGER.error('Error processing "calendar.txt" file.', error);
134
+ Logger.error('Error processing "calendar.txt" file.', error);
134
135
  throw new Error('✖︎ Error processing "calendar.txt" file.');
135
136
  }
136
137
  /* * */
@@ -141,7 +142,7 @@ export async function generateOfferOutput(filePath, startDate, endDate, outputDi
141
142
  // present in calendar.txt and are between the given start and end dates.
142
143
  try {
143
144
  //
144
- LOGGER.info(`Reading zip entry "calendar_dates.txt"...`);
145
+ Logger.info(`Reading zip entry "calendar_dates.txt"...`);
145
146
  const parseEachRow = async (data) => {
146
147
  //
147
148
  //
@@ -180,17 +181,17 @@ export async function generateOfferOutput(filePath, startDate, endDate, outputDi
180
181
  // Setup the CSV parsing operation only if the file exists
181
182
  if (fs.existsSync(`${extractDirPath}/calendar_dates.txt`)) {
182
183
  await parseCsvFile(`${extractDirPath}/calendar_dates.txt`, parseEachRow);
183
- LOGGER.success(`Finished processing "calendar_dates.txt"`);
184
- LOGGER.spacer(1);
184
+ Logger.success(`Finished processing "calendar_dates.txt"`);
185
+ Logger.spacer(1);
185
186
  }
186
187
  else {
187
- LOGGER.info(`Optional file "calendar_dates.txt" not found. This may or may not be an error. Proceeding...`);
188
- LOGGER.spacer(1);
188
+ Logger.info(`Optional file "calendar_dates.txt" not found. This may or may not be an error. Proceeding...`);
189
+ Logger.spacer(1);
189
190
  }
190
191
  //
191
192
  }
192
193
  catch (error) {
193
- LOGGER.error('Error processing "calendar_dates.txt" file.', error);
194
+ Logger.error('Error processing "calendar_dates.txt" file.', error);
194
195
  throw new Error('✖︎ Error processing "calendar_dates.txt" file.');
195
196
  }
196
197
  /* * */
@@ -201,7 +202,7 @@ export async function generateOfferOutput(filePath, startDate, endDate, outputDi
201
202
  // Only include trips which have the referenced service IDs saved before.
202
203
  try {
203
204
  //
204
- LOGGER.info(`Reading zip entry "trips.txt"...`);
205
+ Logger.info(`Reading zip entry "trips.txt"...`);
205
206
  const parseEachRow = async (data) => {
206
207
  // Validate the current row against the proper type
207
208
  const validatedData = validateGtfsTripExtended(data);
@@ -217,12 +218,12 @@ export async function generateOfferOutput(filePath, startDate, endDate, outputDi
217
218
  //
218
219
  // Setup the CSV parsing operation
219
220
  await parseCsvFile(`${extractDirPath}/trips.txt`, parseEachRow);
220
- LOGGER.success(`Finished processing "trips.txt"`);
221
- LOGGER.spacer(1);
221
+ Logger.success(`Finished processing "trips.txt"`);
222
+ Logger.spacer(1);
222
223
  //
223
224
  }
224
225
  catch (error) {
225
- LOGGER.error('Error processing "trips.txt" file.', error);
226
+ Logger.error('Error processing "trips.txt" file.', error);
226
227
  throw new Error('✖︎ Error processing "trips.txt" file.');
227
228
  }
228
229
  /* * */
@@ -232,7 +233,7 @@ export async function generateOfferOutput(filePath, startDate, endDate, outputDi
232
233
  // For routes, only include the ones referenced in the filtered trips.
233
234
  try {
234
235
  //
235
- LOGGER.info(`Reading zip entry "routes.txt"...`);
236
+ Logger.info(`Reading zip entry "routes.txt"...`);
236
237
  const parseEachRow = async (data) => {
237
238
  // Validate the current row against the proper type
238
239
  const validatedData = validateGtfsRouteExtended(data);
@@ -246,12 +247,12 @@ export async function generateOfferOutput(filePath, startDate, endDate, outputDi
246
247
  //
247
248
  // Setup the CSV parsing operation
248
249
  await parseCsvFile(`${extractDirPath}/routes.txt`, parseEachRow);
249
- LOGGER.success(`Finished processing "routes.txt"`);
250
- LOGGER.spacer(1);
250
+ Logger.success(`Finished processing "routes.txt"`);
251
+ Logger.spacer(1);
251
252
  //
252
253
  }
253
254
  catch (error) {
254
- LOGGER.error('Error processing "routes.txt" file.', error);
255
+ Logger.error('Error processing "routes.txt" file.', error);
255
256
  throw new Error('✖︎ Error processing "routes.txt" file.');
256
257
  }
257
258
  /* * */
@@ -262,7 +263,7 @@ export async function generateOfferOutput(filePath, startDate, endDate, outputDi
262
263
  // By saving all of them, we also speed up the processing of each stop_time by including the stop data right away.
263
264
  try {
264
265
  //
265
- LOGGER.info(`Reading zip entry "stops.txt"...`);
266
+ Logger.info(`Reading zip entry "stops.txt"...`);
266
267
  const parseEachRow = async (data) => {
267
268
  // Validate the current row against the proper type
268
269
  const validatedData = validateGtfsStopExtended(data);
@@ -272,12 +273,12 @@ export async function generateOfferOutput(filePath, startDate, endDate, outputDi
272
273
  //
273
274
  // Setup the CSV parsing operation
274
275
  await parseCsvFile(`${extractDirPath}/stops.txt`, parseEachRow);
275
- LOGGER.success(`Finished processing "stops.txt"`);
276
- LOGGER.spacer(1);
276
+ Logger.success(`Finished processing "stops.txt"`);
277
+ Logger.spacer(1);
277
278
  //
278
279
  }
279
280
  catch (error) {
280
- LOGGER.error('Error processing "stops.txt" file.', error);
281
+ Logger.error('Error processing "stops.txt" file.', error);
281
282
  throw new Error('✖︎ Error processing "stops.txt" file.');
282
283
  }
283
284
  /* * */
@@ -289,7 +290,7 @@ export async function generateOfferOutput(filePath, startDate, endDate, outputDi
289
290
  // right away to avoid another lookup later.
290
291
  try {
291
292
  //
292
- LOGGER.info(`Reading zip entry "stop_times.txt"...`);
293
+ Logger.info(`Reading zip entry "stop_times.txt"...`);
293
294
  const parseEachRow = async (data) => {
294
295
  //
295
296
  //
@@ -318,12 +319,12 @@ export async function generateOfferOutput(filePath, startDate, endDate, outputDi
318
319
  //
319
320
  // Setup the CSV parsing operation
320
321
  await parseCsvFile(`${extractDirPath}/stop_times.txt`, parseEachRow);
321
- LOGGER.success(`Finished processing "stop_times.txt"`);
322
- LOGGER.spacer(1);
322
+ Logger.success(`Finished processing "stop_times.txt"`);
323
+ Logger.spacer(1);
323
324
  //
324
325
  }
325
326
  catch (error) {
326
- LOGGER.error('Error processing "stop_times.txt" file.', error);
327
+ Logger.error('Error processing "stop_times.txt" file.', error);
327
328
  throw new Error('✖︎ Error processing "stop_times.txt" file.');
328
329
  }
329
330
  /* * */
@@ -341,7 +342,7 @@ export async function generateOfferOutput(filePath, startDate, endDate, outputDi
341
342
  const stopTimesData = savedStopTimes.get(currentTrip.trip_id);
342
343
  const routeData = savedRoutes.get(currentTrip.route_id);
343
344
  if (!stopTimesData || stopTimesData.length === 0) {
344
- LOGGER.error(`Trip ${currentTrip.trip_id} has no path data. Skipping...`);
345
+ Logger.error(`Trip ${currentTrip.trip_id} has no path data. Skipping...`);
345
346
  continue;
346
347
  }
347
348
  //
@@ -474,21 +475,21 @@ export async function generateOfferOutput(filePath, startDate, endDate, outputDi
474
475
  //
475
476
  }
476
477
  catch (error) {
477
- LOGGER.error('Error transforming or saving Offer documents.', error);
478
+ Logger.error('Error transforming or saving Offer documents.', error);
478
479
  throw new Error('✖︎ Error transforming or saving Offer documents.');
479
480
  }
480
481
  //
481
482
  offerStopsWriter.close();
482
483
  offerJourneysWriter.close();
483
- LOGGER.spacer(1);
484
- LOGGER.success(`Total OfferJourneys written: ${totalOfferJourneysCounter}`);
485
- LOGGER.success(`Total OfferStops written: ${totalOfferStopsCounter}`);
486
- LOGGER.terminate(`Finished processing GTFS file. Run took ${globalTimer.get()}`);
484
+ Logger.spacer(1);
485
+ Logger.success(`Total OfferJourneys written: ${totalOfferJourneysCounter}`);
486
+ Logger.success(`Total OfferStops written: ${totalOfferStopsCounter}`);
487
+ Logger.terminate(`Finished processing GTFS file. Run took ${globalTimer.get()}`);
487
488
  //
488
489
  }
489
490
  catch (error) {
490
- LOGGER.error('An error occurred. Halting execution.', error);
491
- LOGGER.error('Retrying in 10 seconds...');
491
+ Logger.error('An error occurred. Halting execution.', error);
492
+ Logger.error('Retrying in 10 seconds...');
492
493
  setTimeout(() => {
493
494
  process.exit(0); // End process
494
495
  }, 10000); // after 10 seconds
@@ -0,0 +1,94 @@
1
+ export interface OfferJourney {
2
+ agencyId: string;
3
+ arrivalTime: string;
4
+ bikesAllowed: number;
5
+ blockId: string;
6
+ circular: number;
7
+ continuousDropOff: number;
8
+ continuousPickup: number;
9
+ date: string;
10
+ dayType: number;
11
+ dayTypeName: string;
12
+ departureTime: string;
13
+ directionId: number;
14
+ endShiftId: string;
15
+ endStopCode: string;
16
+ endStopId: string;
17
+ endStopName: string;
18
+ feedId: string;
19
+ holiday: number;
20
+ holidayName: string;
21
+ lineId: string;
22
+ lineLongName: string;
23
+ lineShortName: string;
24
+ pathType: number;
25
+ patternId: string;
26
+ patternShortName: string;
27
+ period: number;
28
+ periodName: string;
29
+ routeColor: string;
30
+ routeDesc: string;
31
+ routeDestination: string;
32
+ routeId: string;
33
+ routeLongName: string;
34
+ routeOrigin: string;
35
+ routeShortName: string;
36
+ routeTextColor: string;
37
+ routeType: string;
38
+ rowId: number;
39
+ school: string;
40
+ shapeId: string;
41
+ startShiftId: string;
42
+ startStopCode: string;
43
+ startStopId: string;
44
+ startStopName: string;
45
+ tripHeadsign: string;
46
+ tripId: string;
47
+ tripLength: number;
48
+ wheelchairAccessible: number;
49
+ }
50
+ export interface OfferStop {
51
+ arrivalTime: string;
52
+ bench: number;
53
+ continuousDropOff: number;
54
+ continuousPickup: number;
55
+ date: string;
56
+ departureTime: string;
57
+ dropOffType: number;
58
+ entranceRestriction: number;
59
+ equipment: number;
60
+ exitRestriction: number;
61
+ feedId: string;
62
+ locationType: number;
63
+ municipality: number;
64
+ municipalityFare1: null;
65
+ municipalityFare2: null;
66
+ networkMap: number;
67
+ parentStation: string;
68
+ pickupType: number;
69
+ platformCode: string;
70
+ preservationState: number;
71
+ realTimeInformation: number;
72
+ region: string;
73
+ rowId: number;
74
+ schedule: number;
75
+ shapeDistTraveled: number;
76
+ shelter: number;
77
+ signalling: number;
78
+ slot: number;
79
+ stopCode: string;
80
+ stopDesc: string;
81
+ stopHeadsign: string;
82
+ stopId: string;
83
+ stopIdStepp: string;
84
+ stopLat: number;
85
+ stopLon: number;
86
+ stopName: string;
87
+ stopRemarks: string;
88
+ stopSequence: number;
89
+ tariff: number;
90
+ timepoint: number;
91
+ tripId: string;
92
+ wheelchairBoarding: number;
93
+ zoneShift: number;
94
+ }
package/package.json CHANGED
@@ -1,45 +1,47 @@
1
1
  {
2
2
  "name": "@tmlmobilidade/generate-offer-files",
3
3
  "description": "A CLI tool to generate legacy offer files for TML Mobilidade.",
4
- "version": "20251021.1643.15",
5
- "author": "João de Vasconcelos",
4
+ "version": "20251229.1447.48",
5
+ "author": {
6
+ "email": "iso@tmlmobilidade.pt",
7
+ "name": "TML-ISO"
8
+ },
6
9
  "license": "AGPL-3.0-or-later",
7
10
  "type": "module",
8
11
  "files": [
9
12
  "dist"
10
13
  ],
11
- "main": "dist/index.js",
14
+ "main": "./dist/index.js",
12
15
  "bin": {
13
16
  "generate-offer-files": "dist/index.js"
14
17
  },
15
- "types": "dist/index.d.ts",
18
+ "types": "./dist/index.d.ts",
16
19
  "scripts": {
17
- "build": "npm run clean && tsc && resolve-tspaths",
18
- "clean": "rm -rf dist",
19
- "dev": "tsx watch ./src/index.ts",
20
+ "build": "tsc && resolve-tspaths",
21
+ "dev": "tsx watch src/index.ts",
20
22
  "lint": "eslint .",
21
23
  "lint:fix": "eslint . --fix",
22
24
  "start": "tsc && resolve-tspaths && node dist/index.js"
23
25
  },
24
26
  "dependencies": {
25
- "@helperkits/logger": "20250628.901.58",
26
- "@helperkits/timer": "20240627.34.23",
27
- "@helperkits/writer": "20251010.1959.27",
27
+ "@tmlmobilidade/consts": "*",
28
+ "@tmlmobilidade/geo": "*",
28
29
  "@tmlmobilidade/interfaces": "*",
29
- "@tmlmobilidade/lib": "*",
30
+ "@tmlmobilidade/logger": "*",
31
+ "@tmlmobilidade/timer": "*",
30
32
  "@tmlmobilidade/utils": "*",
31
- "commander": "14.0.1",
33
+ "@tmlmobilidade/writers": "*",
34
+ "commander": "14.0.2",
32
35
  "csv-parse": "6.1.0",
33
36
  "extract-zip": "2.0.1",
34
37
  "gtfs-types": "5.1.0"
35
38
  },
36
39
  "devDependencies": {
37
- "@carrismetropolitana/eslint": "20250622.1204.50",
38
40
  "@tmlmobilidade/tsconfig": "*",
39
41
  "@tmlmobilidade/types": "*",
40
- "@types/node": "24.9.1",
42
+ "@types/node": "25.0.3",
41
43
  "resolve-tspaths": "0.8.23",
42
- "tsx": "4.20.6",
44
+ "tsx": "4.21.0",
43
45
  "typescript": "5.9.3"
44
46
  }
45
- }
47
+ }