@tmlmobilidade/export-data 20251229.1441.35
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.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +100 -0
- package/dist/prompts/export-types.d.ts +2 -0
- package/dist/prompts/export-types.js +34 -0
- package/dist/prompts/filter-agency-ids.d.ts +1 -0
- package/dist/prompts/filter-agency-ids.js +25 -0
- package/dist/prompts/filter-dates.d.ts +5 -0
- package/dist/prompts/filter-dates.js +73 -0
- package/dist/prompts/filter-line-ids.d.ts +1 -0
- package/dist/prompts/filter-line-ids.js +25 -0
- package/dist/prompts/filter-pattern-ids.d.ts +1 -0
- package/dist/prompts/filter-pattern-ids.js +25 -0
- package/dist/prompts/filter-stop-ids.d.ts +1 -0
- package/dist/prompts/filter-stop-ids.js +26 -0
- package/dist/prompts/filter-types.d.ts +1 -0
- package/dist/prompts/filter-types.js +20 -0
- package/dist/prompts/filter-vehicle-ids.d.ts +1 -0
- package/dist/prompts/filter-vehicle-ids.js +25 -0
- package/dist/tasks/apex-validations/validations-by-line.d.ts +2 -0
- package/dist/tasks/apex-validations/validations-by-line.js +85 -0
- package/dist/tasks/apex-validations/validations-by-pattern.d.ts +5 -0
- package/dist/tasks/apex-validations/validations-by-pattern.js +87 -0
- package/dist/tasks/apex-validations/validations-by-stop-by-pattern.d.ts +5 -0
- package/dist/tasks/apex-validations/validations-by-stop-by-pattern.js +88 -0
- package/dist/tasks/apex-validations/validations-by-stop-by-trip.d.ts +5 -0
- package/dist/tasks/apex-validations/validations-by-stop-by-trip.js +88 -0
- package/dist/tasks/apex-validations/validations-by-stop.d.ts +5 -0
- package/dist/tasks/apex-validations/validations-by-stop.js +87 -0
- package/dist/tasks/apex-validations/validations-raw.d.ts +2 -0
- package/dist/tasks/apex-validations/validations-raw.js +64 -0
- package/dist/tasks/rides/rides-raw.d.ts +2 -0
- package/dist/tasks/rides/rides-raw.js +54 -0
- package/dist/tasks/vehicle-events/vehicle-events-raw.d.ts +2 -0
- package/dist/tasks/vehicle-events/vehicle-events-raw.js +64 -0
- package/dist/types.d.ts +31 -0
- package/dist/types.js +12 -0
- package/dist/utils/init-context.d.ts +2 -0
- package/dist/utils/init-context.js +31 -0
- package/package.json +54 -0
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { promptExportTypes } from './prompts/export-types.js';
|
|
3
|
+
import { promptFilterByAgencyIds } from './prompts/filter-agency-ids.js';
|
|
4
|
+
import { promptFilterByDates } from './prompts/filter-dates.js';
|
|
5
|
+
import { promptFilterByLineIds } from './prompts/filter-line-ids.js';
|
|
6
|
+
import { promptFilterByPatternIds } from './prompts/filter-pattern-ids.js';
|
|
7
|
+
import { promptFilterByStopIds } from './prompts/filter-stop-ids.js';
|
|
8
|
+
import { promptFilterTypes } from './prompts/filter-types.js';
|
|
9
|
+
import { promptFilterByVehicleIds } from './prompts/filter-vehicle-ids.js';
|
|
10
|
+
import { exportValidationsByLine } from './tasks/apex-validations/validations-by-line.js';
|
|
11
|
+
import { exportValidationsByPattern } from './tasks/apex-validations/validations-by-pattern.js';
|
|
12
|
+
import { exportValidationsByStopByPattern } from './tasks/apex-validations/validations-by-stop-by-pattern.js';
|
|
13
|
+
import { exportValidationsByStopByTrip } from './tasks/apex-validations/validations-by-stop-by-trip.js';
|
|
14
|
+
import { exportValidationsByStop } from './tasks/apex-validations/validations-by-stop.js';
|
|
15
|
+
import { exportValidationsRaw } from './tasks/apex-validations/validations-raw.js';
|
|
16
|
+
import { exportRidesRaw } from './tasks/rides/rides-raw.js';
|
|
17
|
+
import { exportVehicleEventsRaw } from './tasks/vehicle-events/vehicle-events-raw.js';
|
|
18
|
+
import { exportTypeLabels } from './types.js';
|
|
19
|
+
import { initExportContext } from './utils/init-context.js';
|
|
20
|
+
import { intro, log, outro, tasks } from '@clack/prompts';
|
|
21
|
+
import { ASCII_CM_SHORT } from '@tmlmobilidade/consts';
|
|
22
|
+
/* * */
|
|
23
|
+
(async function main() {
|
|
24
|
+
//
|
|
25
|
+
//
|
|
26
|
+
// Initialize the export context
|
|
27
|
+
const context = initExportContext();
|
|
28
|
+
//
|
|
29
|
+
// Greet the user
|
|
30
|
+
console.log(ASCII_CM_SHORT);
|
|
31
|
+
intro('Bem-vindo ao exportador de dados da CM!');
|
|
32
|
+
log.info(`O ID desta exportação é: ${context._id}`);
|
|
33
|
+
log.info(`Todos os resultados serão guardados aqui: ${context.output}`);
|
|
34
|
+
//
|
|
35
|
+
// Request the export types and which filters to apply
|
|
36
|
+
const exportTypes = await promptExportTypes();
|
|
37
|
+
const filterTypes = await promptFilterTypes();
|
|
38
|
+
//
|
|
39
|
+
// For the selected filters, request the filter values
|
|
40
|
+
if (filterTypes.includes('agency-ids'))
|
|
41
|
+
context.filters.agency_ids = await promptFilterByAgencyIds();
|
|
42
|
+
if (filterTypes.includes('line-ids'))
|
|
43
|
+
context.filters.line_ids = await promptFilterByLineIds();
|
|
44
|
+
if (filterTypes.includes('pattern-ids'))
|
|
45
|
+
context.filters.pattern_ids = await promptFilterByPatternIds();
|
|
46
|
+
if (filterTypes.includes('stop-ids'))
|
|
47
|
+
context.filters.stop_ids = await promptFilterByStopIds();
|
|
48
|
+
if (filterTypes.includes('vehicle-ids'))
|
|
49
|
+
context.filters.vehicle_ids = await promptFilterByVehicleIds();
|
|
50
|
+
context.dates = await promptFilterByDates();
|
|
51
|
+
//
|
|
52
|
+
// Build the tasks array for the selected export types
|
|
53
|
+
await tasks([
|
|
54
|
+
{
|
|
55
|
+
enabled: exportTypes.includes('validations-raw'),
|
|
56
|
+
task: async (message) => await exportValidationsRaw({ context, message }),
|
|
57
|
+
title: exportTypeLabels['validations-raw'],
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
enabled: exportTypes.includes('validations-by-stop-by-trip'),
|
|
61
|
+
task: async (message) => await exportValidationsByStopByTrip({ context, message }),
|
|
62
|
+
title: exportTypeLabels['validations-by-stop-by-trip'],
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
enabled: exportTypes.includes('validations-by-stop-by-pattern'),
|
|
66
|
+
task: async (message) => await exportValidationsByStopByPattern({ context, message }),
|
|
67
|
+
title: exportTypeLabels['validations-by-stop-by-pattern'],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
enabled: exportTypes.includes('validations-by-stop'),
|
|
71
|
+
task: async (message) => await exportValidationsByStop({ context, message }),
|
|
72
|
+
title: exportTypeLabels['validations-by-stop'],
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
enabled: exportTypes.includes('validations-by-pattern'),
|
|
76
|
+
task: async (message) => await exportValidationsByPattern({ context, message }),
|
|
77
|
+
title: exportTypeLabels['validations-by-pattern'],
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
enabled: exportTypes.includes('validations-by-line'),
|
|
81
|
+
task: async (message) => await exportValidationsByLine({ context, message }),
|
|
82
|
+
title: exportTypeLabels['validations-by-line'],
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
enabled: exportTypes.includes('rides-raw'),
|
|
86
|
+
task: async (message) => await exportRidesRaw({ context, message }),
|
|
87
|
+
title: exportTypeLabels['rides-raw'],
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
enabled: exportTypes.includes('vehicle-events-raw'),
|
|
91
|
+
task: async (message) => await exportVehicleEventsRaw({ context, message }),
|
|
92
|
+
title: exportTypeLabels['vehicle-events-raw'],
|
|
93
|
+
},
|
|
94
|
+
]);
|
|
95
|
+
//
|
|
96
|
+
// Terminate the process
|
|
97
|
+
outro('Exportação terminada.');
|
|
98
|
+
process.exit(0);
|
|
99
|
+
//
|
|
100
|
+
})().catch(console.error);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { exportTypeLabels } from '../types.js';
|
|
3
|
+
import { cancel, groupMultiselect, isCancel } from '@clack/prompts';
|
|
4
|
+
/* * */
|
|
5
|
+
export async function promptExportTypes() {
|
|
6
|
+
//
|
|
7
|
+
const values = await groupMultiselect({
|
|
8
|
+
message: 'Escolhe os dados que queres exportar:',
|
|
9
|
+
options: {
|
|
10
|
+
'1. Validações APEX': [
|
|
11
|
+
{ label: exportTypeLabels['validations-raw'], value: 'validations-raw' },
|
|
12
|
+
{ label: exportTypeLabels['validations-by-stop-by-trip'], value: 'validations-by-stop-by-trip' },
|
|
13
|
+
{ label: exportTypeLabels['validations-by-stop-by-pattern'], value: 'validations-by-stop-by-pattern' },
|
|
14
|
+
{ label: exportTypeLabels['validations-by-stop'], value: 'validations-by-stop' },
|
|
15
|
+
{ label: exportTypeLabels['validations-by-pattern'], value: 'validations-by-pattern' },
|
|
16
|
+
{ label: exportTypeLabels['validations-by-line'], value: 'validations-by-line' },
|
|
17
|
+
],
|
|
18
|
+
'2. Rides': [
|
|
19
|
+
{ label: exportTypeLabels['rides-raw'], value: 'rides-raw' },
|
|
20
|
+
],
|
|
21
|
+
'3. Vehicle Events': [
|
|
22
|
+
{ label: exportTypeLabels['vehicle-events-raw'], value: 'vehicle-events-raw' },
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
required: true,
|
|
26
|
+
});
|
|
27
|
+
if (isCancel(values)) {
|
|
28
|
+
cancel('Operação cancelada pelo utilizador.');
|
|
29
|
+
process.exit(0);
|
|
30
|
+
}
|
|
31
|
+
if (!values)
|
|
32
|
+
return [];
|
|
33
|
+
return values;
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function promptFilterByAgencyIds(): Promise<string[]>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { log, text } from '@clack/prompts';
|
|
3
|
+
/* * */
|
|
4
|
+
export async function promptFilterByAgencyIds() {
|
|
5
|
+
//
|
|
6
|
+
log.step('FILTRAR POR AGENCY ID:');
|
|
7
|
+
log.message('- Introduz os Agency IDs separados por vírgulas. Exemplo: 41,42,etc...');
|
|
8
|
+
log.message('- Se não introduzires nenhum Agency ID, este filtro não será aplicado.');
|
|
9
|
+
const value = await text({
|
|
10
|
+
message: 'Agency IDs:',
|
|
11
|
+
placeholder: '41,42,etc...',
|
|
12
|
+
validate(value) {
|
|
13
|
+
if (value.length === 0)
|
|
14
|
+
return;
|
|
15
|
+
const ids = value.replace(/,$/, '').split(',').map(id => id.trim());
|
|
16
|
+
const invalidIds = ids.filter(id => !/^\d{2}$/.test(id));
|
|
17
|
+
if (invalidIds.length > 0)
|
|
18
|
+
return `Estes IDs são inválidos: ${invalidIds.join(', ')}`;
|
|
19
|
+
return;
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
if (!value)
|
|
23
|
+
return [];
|
|
24
|
+
return value.split(',').map(id => id.trim());
|
|
25
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { cancel, isCancel, log, text } from '@clack/prompts';
|
|
3
|
+
import { validateOperationalDate } from '@tmlmobilidade/types';
|
|
4
|
+
/* * */
|
|
5
|
+
export async function promptFilterByDates() {
|
|
6
|
+
//
|
|
7
|
+
log.step('FILTRAR POR DATAS:');
|
|
8
|
+
log.message('- Introduz as datas operacionais no formato ano-mês-dia. Exemplo: 20250101 ou 2025-01-01');
|
|
9
|
+
log.message('- Devido ao enorme volume de dados, filtrar por datas é obrigatório.');
|
|
10
|
+
log.message('- A data de início não pode ser anterior a 1 Jan. 2024 (20240101).');
|
|
11
|
+
const startDate = await text({
|
|
12
|
+
initialValue: '20250101',
|
|
13
|
+
message: 'Data de Início:',
|
|
14
|
+
placeholder: '20240101 ou 2024-01-01',
|
|
15
|
+
validate(value) {
|
|
16
|
+
try {
|
|
17
|
+
if (value.length === 0)
|
|
18
|
+
return 'A data de início é obrigatória.';
|
|
19
|
+
const formattedValue = formatOperationalDateInput(value);
|
|
20
|
+
validateOperationalDate(formattedValue);
|
|
21
|
+
if (Number(formattedValue) < 20240101)
|
|
22
|
+
return 'A data de início não pode ser anterior a 2024-01-01.';
|
|
23
|
+
if (Number(formattedValue) > 20291231)
|
|
24
|
+
return 'A data de início não pode ser posterior a 2029-12-31.';
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
return error.message;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
if (isCancel(startDate)) {
|
|
32
|
+
cancel('Operação cancelada pelo utilizador.');
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
const endDate = await text({
|
|
36
|
+
initialValue: '20250102',
|
|
37
|
+
message: 'Data de Fim:',
|
|
38
|
+
placeholder: '20240101 ou 2024-01-01',
|
|
39
|
+
validate(value) {
|
|
40
|
+
try {
|
|
41
|
+
if (value.length === 0)
|
|
42
|
+
return 'A data de fim é obrigatória.';
|
|
43
|
+
const formattedValue = formatOperationalDateInput(value);
|
|
44
|
+
validateOperationalDate(formattedValue);
|
|
45
|
+
if (Number(formattedValue) < Number(startDate))
|
|
46
|
+
return 'A data de fim não pode ser anterior à data de início.';
|
|
47
|
+
if (Number(formattedValue) > 20291231)
|
|
48
|
+
return 'A data de fim não pode ser posterior a 2029-12-31.';
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
return error.message;
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
if (isCancel(startDate)) {
|
|
56
|
+
cancel('Operação cancelada pelo utilizador.');
|
|
57
|
+
process.exit(0);
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
end: validateOperationalDate(formatOperationalDateInput(endDate)),
|
|
61
|
+
start: validateOperationalDate(formatOperationalDateInput(startDate)),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* This function formats the operational date
|
|
66
|
+
* string input by removing all non-numeric characters.
|
|
67
|
+
* @param value The input string representing an operational date.
|
|
68
|
+
* @returns The formatted operational date string containing only numeric characters.
|
|
69
|
+
*/
|
|
70
|
+
function formatOperationalDateInput(value) {
|
|
71
|
+
// Remove all characters that are not numbers
|
|
72
|
+
return value.replaceAll(/[^0-9]/g, '');
|
|
73
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function promptFilterByLineIds(): Promise<string[]>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { log, text } from '@clack/prompts';
|
|
3
|
+
/* * */
|
|
4
|
+
export async function promptFilterByLineIds() {
|
|
5
|
+
//
|
|
6
|
+
log.step('FILTRAR POR LINE ID:');
|
|
7
|
+
log.message('- Introduz os Line IDs separados por vírgulas. Exemplo: 1001,1002,etc...');
|
|
8
|
+
log.message('- Se não introduzires nenhum Line ID, este filtro não será aplicado.');
|
|
9
|
+
const value = await text({
|
|
10
|
+
message: 'Line IDs:',
|
|
11
|
+
placeholder: '1001,1002,etc...',
|
|
12
|
+
validate(value) {
|
|
13
|
+
if (value.length === 0)
|
|
14
|
+
return;
|
|
15
|
+
const ids = value.replace(/,$/, '').split(',').map(id => id.trim());
|
|
16
|
+
const invalidIds = ids.filter(id => !/^\d{4}$/.test(id));
|
|
17
|
+
if (invalidIds.length > 0)
|
|
18
|
+
return `Estes IDs são inválidos: ${invalidIds.join(', ')}`;
|
|
19
|
+
return;
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
if (!value)
|
|
23
|
+
return [];
|
|
24
|
+
return value.split(',').map(id => id.trim());
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function promptFilterByPatternIds(): Promise<string[]>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { log, text } from '@clack/prompts';
|
|
3
|
+
/* * */
|
|
4
|
+
export async function promptFilterByPatternIds() {
|
|
5
|
+
//
|
|
6
|
+
log.step('FILTRAR POR PATTERN ID:');
|
|
7
|
+
log.message('- Introduz os Pattern IDs separados por vírgulas. Exemplo: 1001_0_1,1001_0_2,etc...');
|
|
8
|
+
log.message('- Se não introduzires nenhum Pattern ID, este filtro não será aplicado.');
|
|
9
|
+
const value = await text({
|
|
10
|
+
message: 'Pattern IDs:',
|
|
11
|
+
placeholder: '1001_0_1,1001_0_2,etc...',
|
|
12
|
+
validate(value) {
|
|
13
|
+
if (value.length === 0)
|
|
14
|
+
return;
|
|
15
|
+
const ids = value.replace(/,$/, '').split(',').map(id => id.trim());
|
|
16
|
+
const invalidIds = ids.filter(id => !/^\d{4}_\d_\d$/.test(id));
|
|
17
|
+
if (invalidIds.length > 0)
|
|
18
|
+
return `Estes IDs são inválidos: ${invalidIds.join(', ')}`;
|
|
19
|
+
return;
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
if (!value)
|
|
23
|
+
return [];
|
|
24
|
+
return value.split(',').map(id => id.trim());
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function promptFilterByStopIds(): Promise<string[]>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { log, text } from '@clack/prompts';
|
|
3
|
+
/* * */
|
|
4
|
+
export async function promptFilterByStopIds() {
|
|
5
|
+
//
|
|
6
|
+
log.step('FILTRAR POR STOP ID:');
|
|
7
|
+
log.message('- Introduz os Stop IDs separados por vírgulas. Exemplo: 010101,020202,etc...');
|
|
8
|
+
log.message('- Não te esqueças do zero à esquerda.');
|
|
9
|
+
log.message('- Se não introduzires nenhum Stop ID, este filtro não será aplicado.');
|
|
10
|
+
const value = await text({
|
|
11
|
+
message: 'Stop IDs:',
|
|
12
|
+
placeholder: '010101,020202,etc...',
|
|
13
|
+
validate(value) {
|
|
14
|
+
if (value.length === 0)
|
|
15
|
+
return;
|
|
16
|
+
const ids = value.replace(/,$/, '').split(',').map(id => id.trim());
|
|
17
|
+
const invalidIds = ids.filter(id => !/^\d{6}$/.test(id));
|
|
18
|
+
if (invalidIds.length > 0)
|
|
19
|
+
return `Estes IDs são inválidos: ${invalidIds.join(', ')}`;
|
|
20
|
+
return;
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
if (!value)
|
|
24
|
+
return [];
|
|
25
|
+
return value.split(',').map(id => id.trim());
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function promptFilterTypes(): Promise<string[]>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { multiselect } from '@clack/prompts';
|
|
3
|
+
/* * */
|
|
4
|
+
export async function promptFilterTypes() {
|
|
5
|
+
//
|
|
6
|
+
const values = await multiselect({
|
|
7
|
+
message: 'Escolhe os filtros que queres aplicar:',
|
|
8
|
+
options: [
|
|
9
|
+
{ label: 'Operador (agency_id)', value: 'agency-ids' },
|
|
10
|
+
{ label: 'Paragem (stop_id)', value: 'stop-ids' },
|
|
11
|
+
{ label: 'Linha (line_id)', value: 'line-ids' },
|
|
12
|
+
{ label: 'Pattern (pattern_id)', value: 'pattern-ids' },
|
|
13
|
+
{ label: 'Veículo (vehicle_id)', value: 'vehicle-ids' },
|
|
14
|
+
],
|
|
15
|
+
required: false,
|
|
16
|
+
});
|
|
17
|
+
if (!values)
|
|
18
|
+
return [];
|
|
19
|
+
return values;
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function promptFilterByVehicleIds(): Promise<number[]>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { log, text } from '@clack/prompts';
|
|
3
|
+
/* * */
|
|
4
|
+
export async function promptFilterByVehicleIds() {
|
|
5
|
+
//
|
|
6
|
+
log.step('FILTRAR POR VEHICLE ID:');
|
|
7
|
+
log.message('- Introduz os Vehicle IDs separados por vírgulas. Exemplo: 1234,9876,etc...');
|
|
8
|
+
log.message('- Se não introduzires nenhum Vehicle ID, este filtro não será aplicado.');
|
|
9
|
+
const value = await text({
|
|
10
|
+
message: 'Vehicle IDs:',
|
|
11
|
+
placeholder: '1234,9876,etc...',
|
|
12
|
+
validate(value) {
|
|
13
|
+
if (value.length === 0)
|
|
14
|
+
return;
|
|
15
|
+
const ids = value.replace(/,$/, '').split(',').map(id => id.trim());
|
|
16
|
+
const invalidIds = ids.filter(id => !/^\d+$/.test(id));
|
|
17
|
+
if (invalidIds.length > 0)
|
|
18
|
+
return `Estes IDs são inválidos: ${invalidIds.join(', ')}`;
|
|
19
|
+
return;
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
if (!value)
|
|
23
|
+
return [];
|
|
24
|
+
return value.split(',').map(id => Number(id.trim()));
|
|
25
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { Dates } from '@tmlmobilidade/dates';
|
|
3
|
+
import { simplifiedApexValidations } from '@tmlmobilidade/interfaces';
|
|
4
|
+
import { CsvWriter } from '@tmlmobilidade/writers';
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
/* * */
|
|
7
|
+
const TASK_ID = 'validations-by-line';
|
|
8
|
+
/* * */
|
|
9
|
+
export async function exportValidationsByLine({ context, message }) {
|
|
10
|
+
//
|
|
11
|
+
message('A iniciar a exportação de Validações APEX por Linha...');
|
|
12
|
+
//
|
|
13
|
+
// Prepare the filter params
|
|
14
|
+
const filterQuery = {
|
|
15
|
+
is_passenger: true,
|
|
16
|
+
};
|
|
17
|
+
filterQuery.created_at = {
|
|
18
|
+
$gte: Dates
|
|
19
|
+
.fromOperationalDate(context.dates.start, 'Europe/Lisbon')
|
|
20
|
+
.set({ hour: 4, millisecond: 0, minute: 0, second: 0 })
|
|
21
|
+
.unix_timestamp,
|
|
22
|
+
$lt: Dates
|
|
23
|
+
.fromOperationalDate(context.dates.end, 'Europe/Lisbon')
|
|
24
|
+
.set({ hour: 4, millisecond: 0, minute: 0, second: 0 })
|
|
25
|
+
.unix_timestamp,
|
|
26
|
+
};
|
|
27
|
+
if (context.filters.agency_ids.length) {
|
|
28
|
+
filterQuery.agency_id = { $in: context.filters.agency_ids };
|
|
29
|
+
}
|
|
30
|
+
if (context.filters.line_ids.length) {
|
|
31
|
+
filterQuery.line_id = { $in: context.filters.line_ids };
|
|
32
|
+
}
|
|
33
|
+
if (context.filters.pattern_ids.length) {
|
|
34
|
+
filterQuery.pattern_id = { $in: context.filters.pattern_ids };
|
|
35
|
+
}
|
|
36
|
+
if (context.filters.stop_ids.length) {
|
|
37
|
+
filterQuery.stop_id = { $in: context.filters.stop_ids };
|
|
38
|
+
}
|
|
39
|
+
if (context.filters.vehicle_ids.length) {
|
|
40
|
+
filterQuery.vehicle_id = { $in: context.filters.vehicle_ids };
|
|
41
|
+
}
|
|
42
|
+
//
|
|
43
|
+
// Setup a database stream to export data
|
|
44
|
+
message(`A iniciar ligação à base de dados...`);
|
|
45
|
+
const simplifiedApexValidationsCollection = await simplifiedApexValidations.getCollection();
|
|
46
|
+
const stream = simplifiedApexValidationsCollection.find(filterQuery).stream();
|
|
47
|
+
//
|
|
48
|
+
// Prepare the output directory and CSV writer
|
|
49
|
+
message(`A preparar a pasta para guardar os resultados...`);
|
|
50
|
+
if (!fs.existsSync(context.output))
|
|
51
|
+
fs.mkdirSync(context.output, { recursive: true });
|
|
52
|
+
const csvWriter = new CsvWriter('output', `${context.output}/${TASK_ID}-${context.dates.start}-${context.dates.end}.csv`, { batch_size: 100000, logs: false });
|
|
53
|
+
//
|
|
54
|
+
// Export the data
|
|
55
|
+
let counter = 0;
|
|
56
|
+
message(`A aguardar o resultado da pesquisa...`);
|
|
57
|
+
const result = {};
|
|
58
|
+
for await (const doc of stream) {
|
|
59
|
+
const document = doc;
|
|
60
|
+
//
|
|
61
|
+
// Prepare the result key
|
|
62
|
+
const operationalDate = Dates
|
|
63
|
+
.fromUnixTimestamp(document.created_at)
|
|
64
|
+
.operational_date;
|
|
65
|
+
const resultKey = `${operationalDate}:${document.line_id}`;
|
|
66
|
+
//
|
|
67
|
+
// Update the result with the current document
|
|
68
|
+
if (!result[resultKey]) {
|
|
69
|
+
result[resultKey] = {
|
|
70
|
+
date: operationalDate,
|
|
71
|
+
line_id: document.line_id,
|
|
72
|
+
validations: 0,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
result[resultKey].validations += 1;
|
|
76
|
+
if (counter % 1000 === 0)
|
|
77
|
+
message(`Processados ${counter} documentos até agora...`);
|
|
78
|
+
counter++;
|
|
79
|
+
//
|
|
80
|
+
}
|
|
81
|
+
message(`A escrever os resultados no ficheiro CSV...`);
|
|
82
|
+
await csvWriter.write(Object.values(result));
|
|
83
|
+
await csvWriter.flush();
|
|
84
|
+
//
|
|
85
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { Dates } from '@tmlmobilidade/dates';
|
|
3
|
+
import { simplifiedApexValidations } from '@tmlmobilidade/interfaces';
|
|
4
|
+
import { CsvWriter } from '@tmlmobilidade/writers';
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
/* * */
|
|
7
|
+
const TASK_ID = 'validations-by-pattern';
|
|
8
|
+
/**
|
|
9
|
+
* Export Validations By Pattern data applying the given filters.
|
|
10
|
+
*/
|
|
11
|
+
export async function exportValidationsByPattern({ context, message }) {
|
|
12
|
+
//
|
|
13
|
+
message('A iniciar a exportação de Validações APEX por Pattern...');
|
|
14
|
+
//
|
|
15
|
+
// Prepare the filter params
|
|
16
|
+
const filterQuery = {
|
|
17
|
+
is_passenger: true,
|
|
18
|
+
};
|
|
19
|
+
filterQuery.created_at = {
|
|
20
|
+
$gte: Dates
|
|
21
|
+
.fromOperationalDate(context.dates.start, 'Europe/Lisbon')
|
|
22
|
+
.set({ hour: 4, millisecond: 0, minute: 0, second: 0 })
|
|
23
|
+
.unix_timestamp,
|
|
24
|
+
$lt: Dates
|
|
25
|
+
.fromOperationalDate(context.dates.end, 'Europe/Lisbon')
|
|
26
|
+
.set({ hour: 4, millisecond: 0, minute: 0, second: 0 })
|
|
27
|
+
.unix_timestamp,
|
|
28
|
+
};
|
|
29
|
+
if (context.filters.agency_ids.length) {
|
|
30
|
+
filterQuery.agency_id = { $in: context.filters.agency_ids };
|
|
31
|
+
}
|
|
32
|
+
if (context.filters.line_ids.length) {
|
|
33
|
+
filterQuery.line_id = { $in: context.filters.line_ids };
|
|
34
|
+
}
|
|
35
|
+
if (context.filters.pattern_ids.length) {
|
|
36
|
+
filterQuery.pattern_id = { $in: context.filters.pattern_ids };
|
|
37
|
+
}
|
|
38
|
+
if (context.filters.stop_ids.length) {
|
|
39
|
+
filterQuery.stop_id = { $in: context.filters.stop_ids };
|
|
40
|
+
}
|
|
41
|
+
if (context.filters.vehicle_ids.length) {
|
|
42
|
+
filterQuery.vehicle_id = { $in: context.filters.vehicle_ids };
|
|
43
|
+
}
|
|
44
|
+
//
|
|
45
|
+
// Setup a database stream to export data
|
|
46
|
+
message(`A iniciar ligação à base de dados...`);
|
|
47
|
+
const simplifiedApexValidationsCollection = await simplifiedApexValidations.getCollection();
|
|
48
|
+
const stream = simplifiedApexValidationsCollection.find(filterQuery).stream();
|
|
49
|
+
//
|
|
50
|
+
// Prepare the output directory and CSV writer
|
|
51
|
+
message(`A preparar a pasta para guardar os resultados...`);
|
|
52
|
+
if (!fs.existsSync(context.output))
|
|
53
|
+
fs.mkdirSync(context.output, { recursive: true });
|
|
54
|
+
const csvWriter = new CsvWriter('output', `${context.output}/${TASK_ID}-${context.dates.start}-${context.dates.end}.csv`, { batch_size: 100000, logs: false });
|
|
55
|
+
//
|
|
56
|
+
// Export the data
|
|
57
|
+
let counter = 0;
|
|
58
|
+
message(`A aguardar o resultado da pesquisa...`);
|
|
59
|
+
const result = {};
|
|
60
|
+
for await (const doc of stream) {
|
|
61
|
+
const document = doc;
|
|
62
|
+
//
|
|
63
|
+
// Prepare the result key
|
|
64
|
+
const operationalDate = Dates
|
|
65
|
+
.fromUnixTimestamp(document.created_at)
|
|
66
|
+
.operational_date;
|
|
67
|
+
const resultKey = `${operationalDate}:${document.pattern_id}`;
|
|
68
|
+
//
|
|
69
|
+
// Update the result with the current document
|
|
70
|
+
if (!result[resultKey]) {
|
|
71
|
+
result[resultKey] = {
|
|
72
|
+
date: operationalDate,
|
|
73
|
+
pattern_id: document.pattern_id,
|
|
74
|
+
validations: 0,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
result[resultKey].validations += 1;
|
|
78
|
+
if (counter % 1000 === 0)
|
|
79
|
+
message(`Processados ${counter} documentos até agora...`);
|
|
80
|
+
counter++;
|
|
81
|
+
//
|
|
82
|
+
}
|
|
83
|
+
message(`A escrever os resultados no ficheiro CSV...`);
|
|
84
|
+
await csvWriter.write(Object.values(result));
|
|
85
|
+
await csvWriter.flush();
|
|
86
|
+
//
|
|
87
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { Dates } from '@tmlmobilidade/dates';
|
|
3
|
+
import { simplifiedApexValidations } from '@tmlmobilidade/interfaces';
|
|
4
|
+
import { CsvWriter } from '@tmlmobilidade/writers';
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
/* * */
|
|
7
|
+
const TASK_ID = 'validations-by-stop-by-pattern';
|
|
8
|
+
/**
|
|
9
|
+
* Export Validations By Stop By Pattern data applying the given filters.
|
|
10
|
+
*/
|
|
11
|
+
export async function exportValidationsByStopByPattern({ context, message }) {
|
|
12
|
+
//
|
|
13
|
+
message('A iniciar a exportação de Validações APEX por Paragem e por Pattern...');
|
|
14
|
+
//
|
|
15
|
+
// Prepare the filter params
|
|
16
|
+
const filterQuery = {
|
|
17
|
+
is_passenger: true,
|
|
18
|
+
};
|
|
19
|
+
filterQuery.created_at = {
|
|
20
|
+
$gte: Dates
|
|
21
|
+
.fromOperationalDate(context.dates.start, 'Europe/Lisbon')
|
|
22
|
+
.set({ hour: 4, millisecond: 0, minute: 0, second: 0 })
|
|
23
|
+
.unix_timestamp,
|
|
24
|
+
$lt: Dates
|
|
25
|
+
.fromOperationalDate(context.dates.end, 'Europe/Lisbon')
|
|
26
|
+
.set({ hour: 4, millisecond: 0, minute: 0, second: 0 })
|
|
27
|
+
.unix_timestamp,
|
|
28
|
+
};
|
|
29
|
+
if (context.filters.agency_ids.length) {
|
|
30
|
+
filterQuery.agency_id = { $in: context.filters.agency_ids };
|
|
31
|
+
}
|
|
32
|
+
if (context.filters.line_ids.length) {
|
|
33
|
+
filterQuery.line_id = { $in: context.filters.line_ids };
|
|
34
|
+
}
|
|
35
|
+
if (context.filters.pattern_ids.length) {
|
|
36
|
+
filterQuery.pattern_id = { $in: context.filters.pattern_ids };
|
|
37
|
+
}
|
|
38
|
+
if (context.filters.stop_ids.length) {
|
|
39
|
+
filterQuery.stop_id = { $in: context.filters.stop_ids };
|
|
40
|
+
}
|
|
41
|
+
if (context.filters.vehicle_ids.length) {
|
|
42
|
+
filterQuery.vehicle_id = { $in: context.filters.vehicle_ids };
|
|
43
|
+
}
|
|
44
|
+
//
|
|
45
|
+
// Setup a database stream to export data
|
|
46
|
+
message(`A iniciar ligação à base de dados...`);
|
|
47
|
+
const simplifiedApexValidationsCollection = await simplifiedApexValidations.getCollection();
|
|
48
|
+
const stream = simplifiedApexValidationsCollection.find(filterQuery).stream();
|
|
49
|
+
//
|
|
50
|
+
// Prepare the output directory and CSV writer
|
|
51
|
+
message(`A preparar a pasta para guardar os resultados...`);
|
|
52
|
+
if (!fs.existsSync(context.output))
|
|
53
|
+
fs.mkdirSync(context.output, { recursive: true });
|
|
54
|
+
const csvWriter = new CsvWriter('output', `${context.output}/${TASK_ID}-${context.dates.start}-${context.dates.end}.csv`, { batch_size: 100000, logs: false });
|
|
55
|
+
//
|
|
56
|
+
// Export the data
|
|
57
|
+
let counter = 0;
|
|
58
|
+
message(`A aguardar o resultado da pesquisa...`);
|
|
59
|
+
const result = {};
|
|
60
|
+
for await (const doc of stream) {
|
|
61
|
+
const document = doc;
|
|
62
|
+
//
|
|
63
|
+
// Prepare the result key
|
|
64
|
+
const operationalDate = Dates
|
|
65
|
+
.fromUnixTimestamp(document.created_at)
|
|
66
|
+
.operational_date;
|
|
67
|
+
const resultKey = `${operationalDate}:${document.stop_id}:${document.pattern_id}`;
|
|
68
|
+
//
|
|
69
|
+
// Update the result with the current document
|
|
70
|
+
if (!result[resultKey]) {
|
|
71
|
+
result[resultKey] = {
|
|
72
|
+
date: operationalDate,
|
|
73
|
+
pattern_id: document.pattern_id,
|
|
74
|
+
stop_id: document.stop_id,
|
|
75
|
+
validations: 0,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
result[resultKey].validations += 1;
|
|
79
|
+
if (counter % 1000 === 0)
|
|
80
|
+
message(`Processados ${counter} documentos até agora...`);
|
|
81
|
+
counter++;
|
|
82
|
+
//
|
|
83
|
+
}
|
|
84
|
+
message(`A escrever os resultados no ficheiro CSV...`);
|
|
85
|
+
await csvWriter.write(Object.values(result));
|
|
86
|
+
await csvWriter.flush();
|
|
87
|
+
//
|
|
88
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { Dates } from '@tmlmobilidade/dates';
|
|
3
|
+
import { simplifiedApexValidations } from '@tmlmobilidade/interfaces';
|
|
4
|
+
import { CsvWriter } from '@tmlmobilidade/writers';
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
/* * */
|
|
7
|
+
const TASK_ID = 'validations-by-stop-by-trip';
|
|
8
|
+
/**
|
|
9
|
+
* Export Validations By Stop By Trip data applying the given filters.
|
|
10
|
+
*/
|
|
11
|
+
export async function exportValidationsByStopByTrip({ context, message }) {
|
|
12
|
+
//
|
|
13
|
+
message('A iniciar a exportação de Validações APEX por Paragem e por Viagem...');
|
|
14
|
+
//
|
|
15
|
+
// Prepare the filter params
|
|
16
|
+
const filterQuery = {
|
|
17
|
+
is_passenger: true,
|
|
18
|
+
};
|
|
19
|
+
filterQuery.created_at = {
|
|
20
|
+
$gte: Dates
|
|
21
|
+
.fromOperationalDate(context.dates.start, 'Europe/Lisbon')
|
|
22
|
+
.set({ hour: 4, millisecond: 0, minute: 0, second: 0 })
|
|
23
|
+
.unix_timestamp,
|
|
24
|
+
$lt: Dates
|
|
25
|
+
.fromOperationalDate(context.dates.end, 'Europe/Lisbon')
|
|
26
|
+
.set({ hour: 4, millisecond: 0, minute: 0, second: 0 })
|
|
27
|
+
.unix_timestamp,
|
|
28
|
+
};
|
|
29
|
+
if (context.filters.agency_ids.length) {
|
|
30
|
+
filterQuery.agency_id = { $in: context.filters.agency_ids };
|
|
31
|
+
}
|
|
32
|
+
if (context.filters.line_ids.length) {
|
|
33
|
+
filterQuery.line_id = { $in: context.filters.line_ids };
|
|
34
|
+
}
|
|
35
|
+
if (context.filters.pattern_ids.length) {
|
|
36
|
+
filterQuery.pattern_id = { $in: context.filters.pattern_ids };
|
|
37
|
+
}
|
|
38
|
+
if (context.filters.stop_ids.length) {
|
|
39
|
+
filterQuery.stop_id = { $in: context.filters.stop_ids };
|
|
40
|
+
}
|
|
41
|
+
if (context.filters.vehicle_ids.length) {
|
|
42
|
+
filterQuery.vehicle_id = { $in: context.filters.vehicle_ids };
|
|
43
|
+
}
|
|
44
|
+
//
|
|
45
|
+
// Setup a database stream to export data
|
|
46
|
+
message(`A iniciar ligação à base de dados...`);
|
|
47
|
+
const simplifiedApexValidationsCollection = await simplifiedApexValidations.getCollection();
|
|
48
|
+
const stream = simplifiedApexValidationsCollection.find(filterQuery).stream();
|
|
49
|
+
//
|
|
50
|
+
// Prepare the output directory and CSV writer
|
|
51
|
+
message(`A preparar a pasta para guardar os resultados...`);
|
|
52
|
+
if (!fs.existsSync(context.output))
|
|
53
|
+
fs.mkdirSync(context.output, { recursive: true });
|
|
54
|
+
const csvWriter = new CsvWriter('output', `${context.output}/${TASK_ID}-${context.dates.start}-${context.dates.end}.csv`, { batch_size: 100000, logs: false });
|
|
55
|
+
//
|
|
56
|
+
// Export the data
|
|
57
|
+
let counter = 0;
|
|
58
|
+
message(`A aguardar o resultado da pesquisa...`);
|
|
59
|
+
const result = {};
|
|
60
|
+
for await (const doc of stream) {
|
|
61
|
+
const document = doc;
|
|
62
|
+
//
|
|
63
|
+
// Prepare the result key
|
|
64
|
+
const operationalDate = Dates
|
|
65
|
+
.fromUnixTimestamp(document.created_at)
|
|
66
|
+
.operational_date;
|
|
67
|
+
const resultKey = `${operationalDate}:${document.stop_id}:${document.trip_id}`;
|
|
68
|
+
//
|
|
69
|
+
// Update the result with the current document
|
|
70
|
+
if (!result[resultKey]) {
|
|
71
|
+
result[resultKey] = {
|
|
72
|
+
date: operationalDate,
|
|
73
|
+
stop_id: document.stop_id,
|
|
74
|
+
trip_id: document.trip_id,
|
|
75
|
+
validations: 0,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
result[resultKey].validations += 1;
|
|
79
|
+
if (counter % 1000 === 0)
|
|
80
|
+
message(`Processados ${counter} documentos até agora...`);
|
|
81
|
+
counter++;
|
|
82
|
+
//
|
|
83
|
+
}
|
|
84
|
+
message(`A escrever os resultados no ficheiro CSV...`);
|
|
85
|
+
await csvWriter.write(Object.values(result));
|
|
86
|
+
await csvWriter.flush();
|
|
87
|
+
//
|
|
88
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { Dates } from '@tmlmobilidade/dates';
|
|
3
|
+
import { simplifiedApexValidations } from '@tmlmobilidade/interfaces';
|
|
4
|
+
import { CsvWriter } from '@tmlmobilidade/writers';
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
/* * */
|
|
7
|
+
const TASK_ID = 'validations-by-stop';
|
|
8
|
+
/**
|
|
9
|
+
* Export Validations By Stop data applying the given filters.
|
|
10
|
+
*/
|
|
11
|
+
export async function exportValidationsByStop({ context, message }) {
|
|
12
|
+
//
|
|
13
|
+
message('A iniciar a exportação de Validações APEX por Paragem...');
|
|
14
|
+
//
|
|
15
|
+
// Prepare the filter params
|
|
16
|
+
const filterQuery = {
|
|
17
|
+
is_passenger: true,
|
|
18
|
+
};
|
|
19
|
+
filterQuery.created_at = {
|
|
20
|
+
$gte: Dates
|
|
21
|
+
.fromOperationalDate(context.dates.start, 'Europe/Lisbon')
|
|
22
|
+
.set({ hour: 4, millisecond: 0, minute: 0, second: 0 })
|
|
23
|
+
.unix_timestamp,
|
|
24
|
+
$lt: Dates
|
|
25
|
+
.fromOperationalDate(context.dates.end, 'Europe/Lisbon')
|
|
26
|
+
.set({ hour: 4, millisecond: 0, minute: 0, second: 0 })
|
|
27
|
+
.unix_timestamp,
|
|
28
|
+
};
|
|
29
|
+
if (context.filters.agency_ids.length) {
|
|
30
|
+
filterQuery.agency_id = { $in: context.filters.agency_ids };
|
|
31
|
+
}
|
|
32
|
+
if (context.filters.line_ids.length) {
|
|
33
|
+
filterQuery.line_id = { $in: context.filters.line_ids };
|
|
34
|
+
}
|
|
35
|
+
if (context.filters.pattern_ids.length) {
|
|
36
|
+
filterQuery.pattern_id = { $in: context.filters.pattern_ids };
|
|
37
|
+
}
|
|
38
|
+
if (context.filters.stop_ids.length) {
|
|
39
|
+
filterQuery.stop_id = { $in: context.filters.stop_ids };
|
|
40
|
+
}
|
|
41
|
+
if (context.filters.vehicle_ids.length) {
|
|
42
|
+
filterQuery.vehicle_id = { $in: context.filters.vehicle_ids };
|
|
43
|
+
}
|
|
44
|
+
//
|
|
45
|
+
// Setup a database stream to export data
|
|
46
|
+
message(`A iniciar ligação à base de dados...`);
|
|
47
|
+
const simplifiedApexValidationsCollection = await simplifiedApexValidations.getCollection();
|
|
48
|
+
const stream = simplifiedApexValidationsCollection.find(filterQuery).stream();
|
|
49
|
+
//
|
|
50
|
+
// Prepare the output directory and CSV writer
|
|
51
|
+
message(`A preparar a pasta para guardar os resultados...`);
|
|
52
|
+
if (!fs.existsSync(context.output))
|
|
53
|
+
fs.mkdirSync(context.output, { recursive: true });
|
|
54
|
+
const csvWriter = new CsvWriter('output', `${context.output}/${TASK_ID}-${context.dates.start}-${context.dates.end}.csv`, { batch_size: 100000, logs: false });
|
|
55
|
+
//
|
|
56
|
+
// Export the data
|
|
57
|
+
let counter = 0;
|
|
58
|
+
message(`A aguardar o resultado da pesquisa...`);
|
|
59
|
+
const result = {};
|
|
60
|
+
for await (const doc of stream) {
|
|
61
|
+
const document = doc;
|
|
62
|
+
//
|
|
63
|
+
// Prepare the result key
|
|
64
|
+
const operationalDate = Dates
|
|
65
|
+
.fromUnixTimestamp(document.created_at)
|
|
66
|
+
.operational_date;
|
|
67
|
+
const resultKey = `${operationalDate}:${document.stop_id}`;
|
|
68
|
+
//
|
|
69
|
+
// Update the result with the current document
|
|
70
|
+
if (!result[resultKey]) {
|
|
71
|
+
result[resultKey] = {
|
|
72
|
+
date: operationalDate,
|
|
73
|
+
stop_id: document.stop_id,
|
|
74
|
+
validations: 0,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
result[resultKey].validations += 1;
|
|
78
|
+
if (counter % 1000 === 0)
|
|
79
|
+
message(`Processados ${counter} documentos até agora...`);
|
|
80
|
+
counter++;
|
|
81
|
+
//
|
|
82
|
+
}
|
|
83
|
+
message(`A escrever os resultados no ficheiro CSV...`);
|
|
84
|
+
await csvWriter.write(Object.values(result));
|
|
85
|
+
await csvWriter.flush();
|
|
86
|
+
//
|
|
87
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { Dates } from '@tmlmobilidade/dates';
|
|
3
|
+
import { simplifiedApexValidations } from '@tmlmobilidade/interfaces';
|
|
4
|
+
import { CsvWriter } from '@tmlmobilidade/writers';
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
/* * */
|
|
7
|
+
const TASK_ID = 'validations-raw';
|
|
8
|
+
/* * */
|
|
9
|
+
export async function exportValidationsRaw({ context, message }) {
|
|
10
|
+
//
|
|
11
|
+
message('A iniciar a exportação de Validações APEX em bruto...');
|
|
12
|
+
//
|
|
13
|
+
// Prepare the filter params
|
|
14
|
+
const filterQuery = {};
|
|
15
|
+
filterQuery.created_at = {
|
|
16
|
+
$gte: Dates
|
|
17
|
+
.fromOperationalDate(context.dates.start, 'Europe/Lisbon')
|
|
18
|
+
.set({ hour: 4, millisecond: 0, minute: 0, second: 0 })
|
|
19
|
+
.unix_timestamp,
|
|
20
|
+
$lt: Dates
|
|
21
|
+
.fromOperationalDate(context.dates.end, 'Europe/Lisbon')
|
|
22
|
+
.set({ hour: 4, millisecond: 0, minute: 0, second: 0 })
|
|
23
|
+
.unix_timestamp,
|
|
24
|
+
};
|
|
25
|
+
if (context.filters.agency_ids.length) {
|
|
26
|
+
filterQuery.agency_id = { $in: context.filters.agency_ids };
|
|
27
|
+
}
|
|
28
|
+
if (context.filters.line_ids.length) {
|
|
29
|
+
filterQuery.line_id = { $in: context.filters.line_ids };
|
|
30
|
+
}
|
|
31
|
+
if (context.filters.pattern_ids.length) {
|
|
32
|
+
filterQuery.pattern_id = { $in: context.filters.pattern_ids };
|
|
33
|
+
}
|
|
34
|
+
if (context.filters.stop_ids.length) {
|
|
35
|
+
filterQuery.stop_id = { $in: context.filters.stop_ids };
|
|
36
|
+
}
|
|
37
|
+
if (context.filters.vehicle_ids.length) {
|
|
38
|
+
filterQuery.vehicle_id = { $in: context.filters.vehicle_ids };
|
|
39
|
+
}
|
|
40
|
+
//
|
|
41
|
+
// Setup a database stream to export data
|
|
42
|
+
message(`A iniciar ligação à base de dados...`);
|
|
43
|
+
const simplifiedApexValidationsCollection = await simplifiedApexValidations.getCollection();
|
|
44
|
+
const stream = simplifiedApexValidationsCollection.find(filterQuery).stream();
|
|
45
|
+
//
|
|
46
|
+
// Prepare the output directory and CSV writer
|
|
47
|
+
message(`A preparar a pasta para guardar os resultados...`);
|
|
48
|
+
if (!fs.existsSync(context.output))
|
|
49
|
+
fs.mkdirSync(context.output, { recursive: true });
|
|
50
|
+
const csvWriter = new CsvWriter('output', `${context.output}/${TASK_ID}-${context.dates.start}-${context.dates.end}.csv`, { batch_size: 100000, logs: false });
|
|
51
|
+
//
|
|
52
|
+
// Export the data
|
|
53
|
+
let counter = 0;
|
|
54
|
+
message(`A aguardar o resultado da pesquisa...`);
|
|
55
|
+
for await (const doc of stream) {
|
|
56
|
+
const document = doc;
|
|
57
|
+
await csvWriter.write(document);
|
|
58
|
+
if (counter % 1000 === 0)
|
|
59
|
+
message(`Processados ${counter} documentos até agora...`);
|
|
60
|
+
counter++;
|
|
61
|
+
}
|
|
62
|
+
await csvWriter.flush();
|
|
63
|
+
//
|
|
64
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { rides } from '@tmlmobilidade/interfaces';
|
|
3
|
+
import { CsvWriter } from '@tmlmobilidade/writers';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
/* * */
|
|
6
|
+
const TASK_ID = 'rides-raw';
|
|
7
|
+
/* * */
|
|
8
|
+
export async function exportRidesRaw({ context, message }) {
|
|
9
|
+
//
|
|
10
|
+
message('A iniciar a exportação de Rides em bruto...');
|
|
11
|
+
//
|
|
12
|
+
// Prepare the filter params
|
|
13
|
+
const filterQuery = {};
|
|
14
|
+
filterQuery.operational_date = {
|
|
15
|
+
$gte: context.dates.start,
|
|
16
|
+
$lte: context.dates.end,
|
|
17
|
+
};
|
|
18
|
+
if (context.filters.agency_ids.length) {
|
|
19
|
+
filterQuery.agency_id = { $in: context.filters.agency_ids };
|
|
20
|
+
}
|
|
21
|
+
if (context.filters.line_ids.length) {
|
|
22
|
+
filterQuery.line_id = { $in: context.filters.line_ids.map(Number) };
|
|
23
|
+
}
|
|
24
|
+
if (context.filters.pattern_ids.length) {
|
|
25
|
+
filterQuery.pattern_id = { $in: context.filters.pattern_ids };
|
|
26
|
+
}
|
|
27
|
+
if (context.filters.vehicle_ids.length) {
|
|
28
|
+
filterQuery.vehicle_ids = { $in: context.filters.vehicle_ids };
|
|
29
|
+
}
|
|
30
|
+
//
|
|
31
|
+
// Setup a database stream to export data
|
|
32
|
+
message(`A iniciar ligação à base de dados...`);
|
|
33
|
+
const ridesCollection = await rides.getCollection();
|
|
34
|
+
const stream = ridesCollection.find(filterQuery).stream();
|
|
35
|
+
//
|
|
36
|
+
// Prepare the output directory and CSV writer
|
|
37
|
+
message(`A preparar a pasta para guardar os resultados...`);
|
|
38
|
+
if (!fs.existsSync(context.output))
|
|
39
|
+
fs.mkdirSync(context.output, { recursive: true });
|
|
40
|
+
const csvWriter = new CsvWriter('output', `${context.output}/${TASK_ID}-${context.dates.start}-${context.dates.end}.csv`, { batch_size: 100000, logs: false });
|
|
41
|
+
//
|
|
42
|
+
// Export the data
|
|
43
|
+
let counter = 0;
|
|
44
|
+
message(`A aguardar o resultado da pesquisa...`);
|
|
45
|
+
for await (const doc of stream) {
|
|
46
|
+
const document = doc;
|
|
47
|
+
await csvWriter.write(document);
|
|
48
|
+
if (counter % 1000 === 0)
|
|
49
|
+
message(`Processados ${counter} documentos até agora...`);
|
|
50
|
+
counter++;
|
|
51
|
+
}
|
|
52
|
+
await csvWriter.flush();
|
|
53
|
+
//
|
|
54
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { Dates } from '@tmlmobilidade/dates';
|
|
3
|
+
import { simplifiedVehicleEvents } from '@tmlmobilidade/interfaces';
|
|
4
|
+
import { CsvWriter } from '@tmlmobilidade/writers';
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
/* * */
|
|
7
|
+
const TASK_ID = 'vehicle-events-raw';
|
|
8
|
+
/* * */
|
|
9
|
+
export async function exportVehicleEventsRaw({ context, message }) {
|
|
10
|
+
//
|
|
11
|
+
message('A iniciar a exportação de Vehicle Events em bruto...');
|
|
12
|
+
//
|
|
13
|
+
// Prepare the filter params
|
|
14
|
+
const filterQuery = {};
|
|
15
|
+
filterQuery.created_at = {
|
|
16
|
+
$gte: Dates
|
|
17
|
+
.fromOperationalDate(context.dates.start, 'Europe/Lisbon')
|
|
18
|
+
.set({ hour: 4, millisecond: 0, minute: 0, second: 0 })
|
|
19
|
+
.unix_timestamp,
|
|
20
|
+
$lt: Dates
|
|
21
|
+
.fromOperationalDate(context.dates.end, 'Europe/Lisbon')
|
|
22
|
+
.set({ hour: 4, millisecond: 0, minute: 0, second: 0 })
|
|
23
|
+
.unix_timestamp,
|
|
24
|
+
};
|
|
25
|
+
if (context.filters.agency_ids.length) {
|
|
26
|
+
filterQuery.agency_id = { $in: context.filters.agency_ids };
|
|
27
|
+
}
|
|
28
|
+
if (context.filters.line_ids.length) {
|
|
29
|
+
filterQuery.line_id = { $in: context.filters.line_ids };
|
|
30
|
+
}
|
|
31
|
+
if (context.filters.pattern_ids.length) {
|
|
32
|
+
filterQuery.pattern_id = { $in: context.filters.pattern_ids };
|
|
33
|
+
}
|
|
34
|
+
if (context.filters.stop_ids.length) {
|
|
35
|
+
filterQuery.stop_id = { $in: context.filters.stop_ids };
|
|
36
|
+
}
|
|
37
|
+
// if (context.filters.vehicle_ids.length) {
|
|
38
|
+
// filterQuery.vehicle_id = { $in: context.filters.vehicle_ids };
|
|
39
|
+
// }
|
|
40
|
+
//
|
|
41
|
+
// Setup a database stream to export data
|
|
42
|
+
message(`A iniciar ligação à base de dados...`);
|
|
43
|
+
const simplifiedVehicleEventsCollection = await simplifiedVehicleEvents.getCollection();
|
|
44
|
+
const stream = simplifiedVehicleEventsCollection.find(filterQuery).stream();
|
|
45
|
+
//
|
|
46
|
+
// Prepare the output directory and CSV writer
|
|
47
|
+
message(`A preparar a pasta para guardar os resultados...`);
|
|
48
|
+
if (!fs.existsSync(context.output))
|
|
49
|
+
fs.mkdirSync(context.output, { recursive: true });
|
|
50
|
+
const csvWriter = new CsvWriter('output', `${context.output}/${TASK_ID}-${context.dates.start}-${context.dates.end}.csv`, { batch_size: 100000, logs: false });
|
|
51
|
+
//
|
|
52
|
+
// Export the data
|
|
53
|
+
let counter = 0;
|
|
54
|
+
message(`A aguardar o resultado da pesquisa...`);
|
|
55
|
+
for await (const doc of stream) {
|
|
56
|
+
const document = doc;
|
|
57
|
+
await csvWriter.write(document);
|
|
58
|
+
if (counter % 1000 === 0)
|
|
59
|
+
message(`Processados ${counter} documentos até agora...`);
|
|
60
|
+
counter++;
|
|
61
|
+
}
|
|
62
|
+
await csvWriter.flush();
|
|
63
|
+
//
|
|
64
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type OperationalDate } from '@tmlmobilidade/types';
|
|
2
|
+
export declare const exportTypeLabels: {
|
|
3
|
+
readonly 'rides-raw': "2.0. Rides em bruto (SLAs)";
|
|
4
|
+
readonly 'validations-by-line': "1.5. Validações por Line ID";
|
|
5
|
+
readonly 'validations-by-pattern': "1.4. Validações por Pattern ID";
|
|
6
|
+
readonly 'validations-by-stop': "1.3. Validações por Stop ID";
|
|
7
|
+
readonly 'validations-by-stop-by-pattern': "1.2. Validações por Stop ID, por Pattern ID";
|
|
8
|
+
readonly 'validations-by-stop-by-trip': "1.1. Validações por Stop ID, por Trip ID";
|
|
9
|
+
readonly 'validations-raw': "1.0. Validações em bruto";
|
|
10
|
+
readonly 'vehicle-events-raw': "3.0. Vehicle Events em bruto";
|
|
11
|
+
};
|
|
12
|
+
export type ExportType = keyof typeof exportTypeLabels;
|
|
13
|
+
export interface ExportContext {
|
|
14
|
+
_id: string;
|
|
15
|
+
dates: {
|
|
16
|
+
end: OperationalDate;
|
|
17
|
+
start: OperationalDate;
|
|
18
|
+
};
|
|
19
|
+
filters: {
|
|
20
|
+
agency_ids: string[];
|
|
21
|
+
line_ids: string[];
|
|
22
|
+
pattern_ids: string[];
|
|
23
|
+
stop_ids: string[];
|
|
24
|
+
vehicle_ids: number[];
|
|
25
|
+
};
|
|
26
|
+
output: string;
|
|
27
|
+
}
|
|
28
|
+
export interface TaskProps {
|
|
29
|
+
context: ExportContext;
|
|
30
|
+
message: (msg: string) => void;
|
|
31
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
/* * */
|
|
3
|
+
export const exportTypeLabels = {
|
|
4
|
+
'rides-raw': '2.0. Rides em bruto (SLAs)',
|
|
5
|
+
'validations-by-line': '1.5. Validações por Line ID',
|
|
6
|
+
'validations-by-pattern': '1.4. Validações por Pattern ID',
|
|
7
|
+
'validations-by-stop': '1.3. Validações por Stop ID',
|
|
8
|
+
'validations-by-stop-by-pattern': '1.2. Validações por Stop ID, por Pattern ID',
|
|
9
|
+
'validations-by-stop-by-trip': '1.1. Validações por Stop ID, por Trip ID',
|
|
10
|
+
'validations-raw': '1.0. Validações em bruto',
|
|
11
|
+
'vehicle-events-raw': '3.0. Vehicle Events em bruto',
|
|
12
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/* * */
|
|
2
|
+
import { generateRandomString } from '@tmlmobilidade/strings';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
/* * */
|
|
5
|
+
export function initExportContext() {
|
|
6
|
+
//
|
|
7
|
+
//
|
|
8
|
+
// Generate a random export ID
|
|
9
|
+
const exportId = generateRandomString();
|
|
10
|
+
//
|
|
11
|
+
// Setup the output path for the export by joining
|
|
12
|
+
// the current working directory with the export ID
|
|
13
|
+
const outputPath = path.join(process.cwd(), `export-${exportId}`);
|
|
14
|
+
//
|
|
15
|
+
// Build and return the export context object
|
|
16
|
+
return {
|
|
17
|
+
_id: exportId,
|
|
18
|
+
dates: {
|
|
19
|
+
end: undefined,
|
|
20
|
+
start: undefined,
|
|
21
|
+
},
|
|
22
|
+
filters: {
|
|
23
|
+
agency_ids: [],
|
|
24
|
+
line_ids: [],
|
|
25
|
+
pattern_ids: [],
|
|
26
|
+
stop_ids: [],
|
|
27
|
+
vehicle_ids: [],
|
|
28
|
+
},
|
|
29
|
+
output: outputPath,
|
|
30
|
+
};
|
|
31
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tmlmobilidade/export-data",
|
|
3
|
+
"description": "CLI tool to export data from GO.",
|
|
4
|
+
"version": "20251229.1441.35",
|
|
5
|
+
"author": {
|
|
6
|
+
"email": "iso@tmlmobilidade.pt",
|
|
7
|
+
"name": "TML-ISO"
|
|
8
|
+
},
|
|
9
|
+
"license": "AGPL-3.0-or-later",
|
|
10
|
+
"keywords": [
|
|
11
|
+
"navegante",
|
|
12
|
+
"tmlmobilidade",
|
|
13
|
+
"public-transit",
|
|
14
|
+
"carrismetropolitana"
|
|
15
|
+
],
|
|
16
|
+
"type": "module",
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"template",
|
|
20
|
+
"README.md",
|
|
21
|
+
"LICENSE",
|
|
22
|
+
"CHANGELOG.md",
|
|
23
|
+
"package.json"
|
|
24
|
+
],
|
|
25
|
+
"main": "./dist/index.js",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": "./dist/index.js"
|
|
28
|
+
},
|
|
29
|
+
"bin": {
|
|
30
|
+
"export-data": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc && resolve-tspaths",
|
|
34
|
+
"dev": "dotenv-run -f ./.env -- tsx src/index.ts",
|
|
35
|
+
"lint": "eslint .",
|
|
36
|
+
"lint:fix": "eslint . --fix"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@clack/prompts": "0.11.0",
|
|
40
|
+
"@tmlmobilidade/consts": "*",
|
|
41
|
+
"@tmlmobilidade/dates": "*",
|
|
42
|
+
"@tmlmobilidade/interfaces": "*",
|
|
43
|
+
"@tmlmobilidade/strings": "*",
|
|
44
|
+
"@tmlmobilidade/timer": "*",
|
|
45
|
+
"@tmlmobilidade/writers": "*"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@tmlmobilidade/tsconfig": "*",
|
|
49
|
+
"@tmlmobilidade/types": "*",
|
|
50
|
+
"@types/node": "25.0.3",
|
|
51
|
+
"resolve-tspaths": "0.8.23",
|
|
52
|
+
"typescript": "5.9.3"
|
|
53
|
+
}
|
|
54
|
+
}
|