mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
fix number of wagon
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
/**
|
||||
* Assign EDR export/import run numbers to the wagon fleet.
|
||||
*
|
||||
* Runs AFTER SeedEdrWagonFleetErNumbering2260000000000, which recreates every
|
||||
* wagon with NULL run numbers — so this must stay later in timestamp order.
|
||||
*
|
||||
* Source data below is the operator-supplied roster, kept verbatim rather than
|
||||
* pre-resolved so its quirks stay visible:
|
||||
* - ER0697 is listed twice under run 8101 (deduped here -> 49, not 50).
|
||||
* - Four wagons are claimed by two runs each. A wagon holds a single run, so
|
||||
* FIRST-LISTED WINS, which is why four runs land one short of their listed
|
||||
* count:
|
||||
* ER0484 8301 over 8401
|
||||
* ER0451 8401 over 8701
|
||||
* ER0887 8701 over 9001
|
||||
* ER0936 8801 over 8901
|
||||
*
|
||||
* Wagons outside this roster (PW2 ER0001-0220 and ER0941-1100) keep NULL runs.
|
||||
*/
|
||||
|
||||
/** Odd EXPORT run (Ethiopia -> Djibouti) -> the wagons rostered to it. */
|
||||
const RUN_WAGONS: Record<string, string[]> = {
|
||||
'8001': [
|
||||
'ER0744', 'ER0734', 'ER0791', 'ER0885', 'ER0410', 'ER0901',
|
||||
'ER0692', 'ER0784', 'ER0663', 'ER0547', 'ER0635', 'ER0840',
|
||||
'ER0660', 'ER0541', 'ER0850', 'ER0764', 'ER0786', 'ER0694',
|
||||
'ER0656', 'ER0432', 'ER0666', 'ER0879', 'ER0724', 'ER0868',
|
||||
'ER0835', 'ER0650', 'ER0926', 'ER0915', 'ER0858', 'ER0826',
|
||||
'ER0474', 'ER0539', 'ER0419', 'ER0695', 'ER0462', 'ER0825',
|
||||
'ER0820', 'ER0790', 'ER0905', 'ER0557', 'ER0712', 'ER0782',
|
||||
'ER0816', 'ER0447', 'ER0674', 'ER0424', 'ER0544', 'ER0519',
|
||||
'ER0479', 'ER0440',
|
||||
],
|
||||
'8101': [
|
||||
'ER0458', 'ER0600', 'ER0521', 'ER0559', 'ER0846', 'ER0459',
|
||||
'ER0863', 'ER0925', 'ER0746', 'ER0821', 'ER0914', 'ER0768',
|
||||
'ER0676', 'ER0470', 'ER0697', 'ER0697', 'ER0923', 'ER0937',
|
||||
'ER0431', 'ER0412', 'ER0254', 'ER0555', 'ER0527', 'ER0590',
|
||||
'ER0480', 'ER0723', 'ER0316', 'ER0800', 'ER0648', 'ER0435',
|
||||
'ER0844', 'ER0939', 'ER0747', 'ER0654', 'ER0752', 'ER0633',
|
||||
'ER0725', 'ER0567', 'ER0838', 'ER0920', 'ER0843', 'ER0520',
|
||||
'ER0646', 'ER0407', 'ER0515', 'ER0760', 'ER0703', 'ER0880',
|
||||
'ER0422', 'ER0852',
|
||||
],
|
||||
'8201': [
|
||||
'ER0322', 'ER0314', 'ER0274', 'ER0514', 'ER0505', 'ER0618',
|
||||
'ER0812', 'ER0776', 'ER0698', 'ER0662', 'ER0888', 'ER0625',
|
||||
'ER0568', 'ER0596', 'ER0918', 'ER0524', 'ER0684', 'ER0231',
|
||||
'ER0907', 'ER0445', 'ER0839', 'ER0430', 'ER0799', 'ER0464',
|
||||
'ER0491', 'ER0833', 'ER0855', 'ER0571', 'ER0452', 'ER0733',
|
||||
'ER0606', 'ER0822', 'ER0845', 'ER0771', 'ER0542', 'ER0588',
|
||||
'ER0443', 'ER0585', 'ER0624', 'ER0538', 'ER0642', 'ER0928',
|
||||
'ER0411', 'ER0794', 'ER0564', 'ER0906', 'ER0348', 'ER0236',
|
||||
'ER0933', 'ER0456',
|
||||
],
|
||||
'8301': [
|
||||
'ER0264', 'ER0691', 'ER0562', 'ER0686', 'ER0881', 'ER0780',
|
||||
'ER0400', 'ER0420', 'ER0475', 'ER0425', 'ER0396', 'ER0818',
|
||||
'ER0537', 'ER0917', 'ER0421', 'ER0766', 'ER0728', 'ER0485',
|
||||
'ER0830', 'ER0804', 'ER0935', 'ER0898', 'ER0577', 'ER0762',
|
||||
'ER0558', 'ER0612', 'ER0484', 'ER0566', 'ER0876', 'ER0528',
|
||||
'ER0292', 'ER0630', 'ER0761', 'ER0849', 'ER0578', 'ER0232',
|
||||
'ER0673', 'ER0870', 'ER0575', 'ER0250', 'ER0599', 'ER0622',
|
||||
'ER0801', 'ER0806', 'ER0594', 'ER0831', 'ER0513',
|
||||
],
|
||||
'8401': [
|
||||
'ER0616', 'ER0730', 'ER0415', 'ER0522', 'ER0454', 'ER0758',
|
||||
'ER0715', 'ER0658', 'ER0602', 'ER0649', 'ER0540', 'ER0434',
|
||||
'ER0678', 'ER0550', 'ER0402', 'ER0636', 'ER0500', 'ER0740',
|
||||
'ER0664', 'ER0397', 'ER0565', 'ER0704', 'ER0720', 'ER0787',
|
||||
'ER0884', 'ER0573', 'ER0755', 'ER0392', 'ER0739', 'ER0530',
|
||||
'ER0437', 'ER0484', 'ER0653', 'ER0502', 'ER0615', 'ER0563',
|
||||
'ER0641', 'ER0391', 'ER0789', 'ER0451', 'ER0819', 'ER0442',
|
||||
'ER0798', 'ER0729', 'ER0772', 'ER0940', 'ER0682', 'ER0614',
|
||||
'ER0561', 'ER0393',
|
||||
],
|
||||
'8501': [
|
||||
'ER0807', 'ER0289', 'ER0587', 'ER0902', 'ER0877', 'ER0748',
|
||||
'ER0837', 'ER0408', 'ER0307', 'ER0759', 'ER0847', 'ER0433',
|
||||
'ER0498', 'ER0492', 'ER0735', 'ER0503', 'ER0461', 'ER0508',
|
||||
'ER0243', 'ER0583', 'ER0924', 'ER0395', 'ER0707', 'ER0572',
|
||||
'ER0536', 'ER0796', 'ER0929', 'ER0713', 'ER0603', 'ER0814',
|
||||
'ER0756', 'ER0398', 'ER0853', 'ER0276', 'ER0405', 'ER0418',
|
||||
'ER0517', 'ER0919', 'ER0781', 'ER0516', 'ER0417', 'ER0702',
|
||||
'ER0857', 'ER0486', 'ER0637', 'ER0736', 'ER0859', 'ER0483',
|
||||
'ER0824', 'ER0640', 'ER0714',
|
||||
],
|
||||
'8601': [
|
||||
'ER0455', 'ER0930', 'ER0293', 'ER0294', 'ER0677', 'ER0808',
|
||||
'ER0785', 'ER0628', 'ER0545', 'ER0551', 'ER0644', 'ER0922',
|
||||
'ER0670', 'ER0864', 'ER0629', 'ER0306', 'ER0494', 'ER0496',
|
||||
'ER0679', 'ER0874', 'ER0921', 'ER0910', 'ER0621', 'ER0667',
|
||||
'ER0262', 'ER0774', 'ER0488', 'ER0300', 'ER0234', 'ER0711',
|
||||
'ER0605', 'ER0897', 'ER0841', 'ER0778', 'ER0769', 'ER0487',
|
||||
'ER0556', 'ER0526', 'ER0795', 'ER0268', 'ER0266', 'ER0257',
|
||||
],
|
||||
'8701': [
|
||||
'ER0263', 'ER0661', 'ER0282', 'ER0394', 'ER0423', 'ER0665',
|
||||
'ER0598', 'ER0909', 'ER0481', 'ER0854', 'ER0471', 'ER0582',
|
||||
'ER0671', 'ER0466', 'ER0788', 'ER0934', 'ER0683', 'ER0680',
|
||||
'ER0890', 'ER0531', 'ER0647', 'ER0823', 'ER0608', 'ER0900',
|
||||
'ER0467', 'ER0607', 'ER0554', 'ER0233', 'ER0911', 'ER0726',
|
||||
'ER0675', 'ER0291', 'ER0313', 'ER0619', 'ER0775', 'ER0705',
|
||||
'ER0548', 'ER0891', 'ER0560', 'ER0904', 'ER0429', 'ER0655',
|
||||
'ER0224', 'ER0700', 'ER0797', 'ER0706', 'ER0533', 'ER0861',
|
||||
'ER0580', 'ER0449', 'ER0409', 'ER0613', 'ER0645', 'ER0315',
|
||||
'ER0718', 'ER0553', 'ER0444', 'ER0593', 'ER0499', 'ER0693',
|
||||
'ER0525', 'ER0451', 'ER0634', 'ER0689', 'ER0878', 'ER0518',
|
||||
'ER0887',
|
||||
],
|
||||
'8801': [
|
||||
'ER0811', 'ER0652', 'ER0889', 'ER0886', 'ER0936', 'ER0476',
|
||||
'ER0832', 'ER0626', 'ER0669', 'ER0404', 'ER0546', 'ER0501',
|
||||
'ER0894', 'ER0460', 'ER0805', 'ER0465', 'ER0717', 'ER0601',
|
||||
'ER0751', 'ER0777', 'ER0504', 'ER0749', 'ER0827', 'ER0896',
|
||||
'ER0903', 'ER0591', 'ER0436', 'ER0552', 'ER0716', 'ER0895',
|
||||
'ER0463', 'ER0809', 'ER0473', 'ER0883', 'ER0569', 'ER0610',
|
||||
'ER0275', 'ER0333', 'ER0344', 'ER0469',
|
||||
],
|
||||
'8901': [
|
||||
'ER0913', 'ER0310', 'ER0873', 'ER0448', 'ER0763', 'ER0441',
|
||||
'ER0936', 'ER0767', 'ER0416', 'ER0413', 'ER0589', 'ER0453',
|
||||
'ER0507', 'ER0287', 'ER0414', 'ER0406', 'ER0584', 'ER0866',
|
||||
'ER0893', 'ER0627', 'ER0227', 'ER0403', 'ER0428', 'ER0908',
|
||||
'ER0349', 'ER0221', 'ER0271', 'ER0659', 'ER0765', 'ER0478',
|
||||
'ER0511', 'ER0506', 'ER0743', 'ER0512', 'ER0916', 'ER0497',
|
||||
'ER0643', 'ER0638', 'ER0468', 'ER0597',
|
||||
],
|
||||
'9001': [
|
||||
'ER0446', 'ER0802', 'ER0570', 'ER0836', 'ER0576', 'ER0672',
|
||||
'ER0631', 'ER0490', 'ER0851', 'ER0450', 'ER0872', 'ER0912',
|
||||
'ER0815', 'ER0882', 'ER0738', 'ER0899', 'ER0620', 'ER0399',
|
||||
'ER0685', 'ER0477', 'ER0842', 'ER0529', 'ER0617', 'ER0865',
|
||||
'ER0754', 'ER0737', 'ER0753', 'ER0732', 'ER0623', 'ER0574',
|
||||
'ER0803', 'ER0651', 'ER0489', 'ER0668', 'ER0741', 'ER0699',
|
||||
'ER0592', 'ER0225', 'ER0229', 'ER0298', 'ER0270', 'ER0259',
|
||||
'ER0337', 'ER0770', 'ER0327', 'ER0251', 'ER0285', 'ER0927',
|
||||
'ER0810', 'ER0681', 'ER0887',
|
||||
],
|
||||
};
|
||||
|
||||
/**
|
||||
* Even IMPORT run (Djibouti -> Ethiopia) for each export run. Listed rather
|
||||
* than computed as export+1 so a run that ever breaks the convention stays
|
||||
* correct. Run numbers are always 4 digits (8401, never 84001).
|
||||
*/
|
||||
const IMPORT_RUN: Record<string, string> = {
|
||||
'8001': '8002',
|
||||
'8101': '8102',
|
||||
'8201': '8202',
|
||||
'8301': '8302',
|
||||
'8401': '8402',
|
||||
'8501': '8502',
|
||||
'8601': '8602',
|
||||
'8701': '8702',
|
||||
'8801': '8802',
|
||||
'8901': '8902',
|
||||
'9001': '9002',
|
||||
};
|
||||
|
||||
export class SeedWagonRunNumbers2280000000000 implements MigrationInterface {
|
||||
name = 'SeedWagonRunNumbers2280000000000';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
// Idempotent: clear the roster's runs first so a re-run cannot leave a
|
||||
// wagon on a run it was since moved off of.
|
||||
await queryRunner.query(`
|
||||
UPDATE freight.wagons
|
||||
SET export_train_number = NULL, import_train_number = NULL
|
||||
WHERE export_train_number IS NOT NULL;
|
||||
`);
|
||||
|
||||
const claimed = new Set<string>();
|
||||
|
||||
for (const [exportRun, wagons] of Object.entries(RUN_WAGONS)) {
|
||||
const importRun = IMPORT_RUN[exportRun];
|
||||
if (!importRun) throw new Error(`import_run_missing:${exportRun}`);
|
||||
|
||||
// First-listed wins — skip any wagon an earlier run already claimed.
|
||||
const fresh = wagons.filter((w) => !claimed.has(w));
|
||||
fresh.forEach((w) => claimed.add(w));
|
||||
if (!fresh.length) continue;
|
||||
|
||||
await queryRunner.query(
|
||||
`
|
||||
UPDATE freight.wagons
|
||||
SET export_train_number = $1,
|
||||
import_train_number = $2,
|
||||
updated_at = now()
|
||||
WHERE wagon_number = ANY($3::text[]);
|
||||
`,
|
||||
[exportRun, importRun, fresh],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`
|
||||
UPDATE freight.wagons
|
||||
SET export_train_number = NULL, import_train_number = NULL
|
||||
WHERE export_train_number IS NOT NULL;
|
||||
`);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import { AppDataSource } from '../data-source';
|
||||
import { SeedEdrWagonFleetErNumbering2260000000000 } from '../migrations/2260000000000-SeedEdrWagonFleetErNumbering';
|
||||
import { AddWagonTrainNumbers2270000000000 } from '../migrations/2270000000000-AddWagonTrainNumbers';
|
||||
import { SeedWagonRunNumbers2280000000000 } from '../migrations/2280000000000-SeedWagonRunNumbers';
|
||||
|
||||
async function seedEdRWagons() {
|
||||
await AppDataSource.initialize();
|
||||
@@ -10,7 +12,12 @@ async function seedEdRWagons() {
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
// Fleet first (recreates every wagon with NULL runs), then the columns are
|
||||
// ensured to exist, then the run roster is applied on top. Same order the
|
||||
// migrations run in, so the script and a fresh migrate agree.
|
||||
await new SeedEdrWagonFleetErNumbering2260000000000().up(queryRunner);
|
||||
await new AddWagonTrainNumbers2270000000000().up(queryRunner);
|
||||
await new SeedWagonRunNumbers2280000000000().up(queryRunner);
|
||||
|
||||
const summary = await queryRunner.query(`
|
||||
SELECT
|
||||
@@ -20,7 +27,8 @@ async function seedEdRWagons() {
|
||||
MIN(w.wagon_number) AS first_wagon,
|
||||
MAX(w.wagon_number) AS last_wagon,
|
||||
COUNT(*) FILTER (WHERE w.status = 'AVAILABLE')::int AS available,
|
||||
COUNT(*) FILTER (WHERE w.current_yard_id IS NULL)::int AS unassigned_yard
|
||||
COUNT(*) FILTER (WHERE w.current_yard_id IS NULL)::int AS unassigned_yard,
|
||||
COUNT(*) FILTER (WHERE w.export_train_number IS NOT NULL)::int AS on_a_run
|
||||
FROM freight.wagons w
|
||||
JOIN freight.wagon_types wt ON wt.id = w.wagon_type_id
|
||||
WHERE w.wagon_number BETWEEN 'ER0001' AND 'ER1100'
|
||||
@@ -29,13 +37,33 @@ async function seedEdRWagons() {
|
||||
`);
|
||||
|
||||
const [totals] = await queryRunner.query(`
|
||||
SELECT COUNT(*)::int AS total FROM freight.wagons;
|
||||
SELECT
|
||||
COUNT(*)::int AS total,
|
||||
COUNT(*) FILTER (WHERE export_train_number IS NOT NULL)::int AS on_a_run
|
||||
FROM freight.wagons;
|
||||
`);
|
||||
|
||||
const runs = await queryRunner.query(`
|
||||
SELECT
|
||||
export_train_number AS export_run,
|
||||
import_train_number AS import_run,
|
||||
COUNT(*)::int AS wagons
|
||||
FROM freight.wagons
|
||||
WHERE export_train_number IS NOT NULL
|
||||
GROUP BY export_train_number, import_train_number
|
||||
ORDER BY export_train_number;
|
||||
`);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
console.log('\nFleet by wagon type:');
|
||||
console.table(summary);
|
||||
console.log(`Seeded EDR wagon fleet — ${totals.total} wagons total (expected 1100).`);
|
||||
console.log('Run roster (export/import pairs):');
|
||||
console.table(runs);
|
||||
console.log(
|
||||
`Seeded EDR wagon fleet — ${totals.total} wagons total (expected 1100), ` +
|
||||
`${totals.on_a_run} on a run (expected 533).`,
|
||||
);
|
||||
} catch (error) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
|
||||
Reference in New Issue
Block a user