diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index f1e12b7dbd..c4c0b154a4 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -115,6 +115,8 @@
"jest-environment-jsdom": "29.7.0",
"lint-staged": "14.0.1",
"madge": "8.0.0",
+ "ora": "8.1.1",
+ "postgres": "3.4.5",
"prettier": "3.4.1",
"puppeteer": "22.15.0",
"strip-json-comments": "5.0.1",
@@ -5844,7 +5846,6 @@
"resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
"integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
"dev": true,
- "license": "MIT",
"dependencies": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
@@ -6375,7 +6376,6 @@
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
"integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
"dev": true,
- "license": "MIT",
"engines": {
"node": ">=0.8"
}
@@ -7216,7 +7216,6 @@
"resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
"integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
"dev": true,
- "license": "MIT",
"dependencies": {
"clone": "^1.0.2"
},
@@ -9736,6 +9735,18 @@
"node": "6.* || 8.* || >= 10.*"
}
},
+ "node_modules/get-east-asian-width": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz",
+ "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/get-intrinsic": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
@@ -10898,13 +10909,15 @@
}
},
"node_modules/is-interactive": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
- "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz",
+ "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==",
"dev": true,
- "license": "MIT",
"engines": {
- "node": ">=8"
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-map": {
@@ -13459,6 +13472,38 @@
"node": ">= 10"
}
},
+ "node_modules/madge/node_modules/is-interactive": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
+ "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/madge/node_modules/ora": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
+ "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+ "dev": true,
+ "dependencies": {
+ "bl": "^4.1.0",
+ "chalk": "^4.1.0",
+ "cli-cursor": "^3.1.0",
+ "cli-spinners": "^2.5.0",
+ "is-interactive": "^1.0.0",
+ "is-unicode-supported": "^0.1.0",
+ "log-symbols": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "wcwidth": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/magic-string": {
"version": "0.30.11",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz",
@@ -14107,6 +14152,18 @@
"node": ">=6"
}
},
+ "node_modules/mimic-function": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
+ "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/mimic-response": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz",
@@ -14689,29 +14746,188 @@
}
},
"node_modules/ora": {
- "version": "5.4.1",
- "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
- "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-8.1.1.tgz",
+ "integrity": "sha512-YWielGi1XzG1UTvOaCFaNgEnuhZVMSHYkW/FQ7UX8O26PtlpdM84c0f7wLPlkvx2RfiQmnzd61d/MGxmpQeJPw==",
"dev": true,
- "license": "MIT",
"dependencies": {
- "bl": "^4.1.0",
- "chalk": "^4.1.0",
- "cli-cursor": "^3.1.0",
- "cli-spinners": "^2.5.0",
- "is-interactive": "^1.0.0",
- "is-unicode-supported": "^0.1.0",
- "log-symbols": "^4.1.0",
- "strip-ansi": "^6.0.0",
- "wcwidth": "^1.0.1"
+ "chalk": "^5.3.0",
+ "cli-cursor": "^5.0.0",
+ "cli-spinners": "^2.9.2",
+ "is-interactive": "^2.0.0",
+ "is-unicode-supported": "^2.0.0",
+ "log-symbols": "^6.0.0",
+ "stdin-discarder": "^0.2.2",
+ "string-width": "^7.2.0",
+ "strip-ansi": "^7.1.0"
},
"engines": {
- "node": ">=10"
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/ansi-regex": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ora/node_modules/chalk": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+ "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+ "dev": true,
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/ora/node_modules/cli-cursor": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
+ "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
+ "dev": true,
+ "dependencies": {
+ "restore-cursor": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/emoji-regex": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
+ "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
+ "dev": true
+ },
+ "node_modules/ora/node_modules/is-unicode-supported": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
+ "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/log-symbols": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz",
+ "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^5.3.0",
+ "is-unicode-supported": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz",
+ "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/onetime": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
+ "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
+ "dev": true,
+ "dependencies": {
+ "mimic-function": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/restore-cursor": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
+ "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
+ "dev": true,
+ "dependencies": {
+ "onetime": "^7.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/ora/node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/ora/node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
"node_modules/ospath": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz",
@@ -15299,6 +15515,19 @@
"postcss": "^8.2.9"
}
},
+ "node_modules/postgres": {
+ "version": "3.4.5",
+ "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.5.tgz",
+ "integrity": "sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://github.com/sponsors/porsager"
+ }
+ },
"node_modules/precinct": {
"version": "12.1.2",
"resolved": "https://registry.npmjs.org/precinct/-/precinct-12.1.2.tgz",
@@ -17274,6 +17503,18 @@
"node": ">=8"
}
},
+ "node_modules/stdin-discarder": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz",
+ "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/stream-events": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
@@ -19204,7 +19445,6 @@
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
"integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
"dev": true,
- "license": "MIT",
"dependencies": {
"defaults": "^1.0.3"
}
diff --git a/frontend/package.json b/frontend/package.json
index 61560a902f..5a5a1b18c1 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -17,6 +17,7 @@
"dev-puppeteer": "FRONTEND_OIDC_ENABLED=false FRONTEND_MONITORENV_URL=//localhost:9880 import-meta-env-prepare -u -x ./.env.local.defaults && vite --port 3000",
"bundle-sw": "esbuild src/workers/serviceWorker.ts --bundle --outfile=public/service-worker.js",
"prepare": "cd .. && ./frontend/node_modules/.bin/husky ./frontend/config/husky",
+ "generate:perfdata": "node ./scripts/generate_perf_data.js",
"generate:testdata": "node ./scripts/generate_test_data_seeds.js",
"start": "import-meta-env-prepare -u -x ./.env.local.defaults && vite preview --port 3000",
"start:emulate": "npm run clean && npm run build && import-meta-env -x ./.env.example -p ./build/index.html && import-meta-env-prepare -u -x ./.env.local.defaults && vite preview --port 3000",
@@ -140,6 +141,8 @@
"jest": "29.7.0",
"jest-environment-jsdom": "29.7.0",
"lint-staged": "14.0.1",
+ "ora": "8.1.1",
+ "postgres": "3.4.5",
"madge": "8.0.0",
"prettier": "3.4.1",
"puppeteer": "22.15.0",
diff --git a/frontend/scripts/generate_perf_data.js b/frontend/scripts/generate_perf_data.js
new file mode 100644
index 0000000000..d026426d2f
--- /dev/null
+++ b/frontend/scripts/generate_perf_data.js
@@ -0,0 +1,280 @@
+/* eslint-disable no-await-in-loop */
+
+import { faker } from '@faker-js/faker'
+import dayjs from 'dayjs'
+import ora from 'ora'
+import postgres from 'postgres'
+
+const BATCH_SIZE = 1000
+const BATCH_SKELETON = new Array(BATCH_SIZE).fill(null)
+const START_DATE = dayjs().subtract(6, 'months').toDate()
+const END_DATE = dayjs().add(6, 'months').toDate()
+
+const FAKE_PNO_COUNT = 1000000
+const FAKE_PNO_BATCH_COUNT = FAKE_PNO_COUNT / BATCH_SIZE
+
+const FAKE_VESSEL_COUNT = 10000
+const FAKE_VESSEL_BATCH_COUNT = FAKE_VESSEL_COUNT / BATCH_SIZE
+
+const FAKE_REPORTING_COUNT = FAKE_VESSEL_COUNT * 3
+const FAKE_REPORTING_BATCH_COUNT = FAKE_REPORTING_COUNT / BATCH_SIZE
+
+function getFakePnoLogbookRawMessages(index) {
+ return [
+ {
+ operation_number: `FAKE_OPERATION_${FAKE_PNO_COUNT + index}`,
+ xml_message: `Message FLUX xml`
+ },
+ {
+ operation_number: `FAKE_OPERATION_${FAKE_PNO_COUNT + index}_RET`,
+ xml_message: `Message FLUX xml`
+ }
+ ]
+}
+
+function getFakePnoLogbookReports(index) {
+ const vesselId = faker.number.int({ max: FAKE_VESSEL_COUNT * 2, min: FAKE_VESSEL_COUNT + 1 })
+ const cfr = `FAKCFR_${vesselId}`
+ const vessel_name = `FAUX NAVIRE ${vesselId}`
+
+ const tripStartDateAsDayjs = dayjs(
+ faker.date.between({
+ from: START_DATE,
+ to: END_DATE
+ })
+ )
+
+ const operation_datetime_utc = tripStartDateAsDayjs.add(26, 'hours').format('YYYY-MM-DD HH:mm:ss')
+ const report_id = `FAKE_OPERATION_${FAKE_PNO_COUNT + index}`
+ const transmission_format = 'ERS'
+
+ return [
+ {
+ cfr,
+ enriched: true,
+ flag_state: 'FRA',
+ id: FAKE_PNO_COUNT + index,
+ integration_datetime_utc: operation_datetime_utc,
+ log_type: 'PNO',
+ operation_datetime_utc,
+ operation_number: report_id,
+ operation_type: 'DAT',
+ referenced_report_id: null,
+ report_datetime_utc: operation_datetime_utc,
+ report_id,
+ transmission_format,
+ trip_gears: [
+ { dimensions: '250;180', gear: 'TBN', mesh: 100 },
+ { dimensions: '250;280', gear: 'OTT', mesh: 120.5 }
+ ],
+ trip_segments: [
+ { segment: 'SWW04', segmentName: 'Chaluts pélagiques' },
+ { segment: 'SWW06', segmentName: 'Sennes' }
+ ],
+ value: {
+ authorTrigram: null,
+ catchOnboard: [
+ {
+ economicZone: 'FRA',
+ effortZone: 'C',
+ faoZone: '27.8.a',
+ nbFish: null,
+ species: 'ANF',
+ statisticalRectangle: '23E6',
+ weight: 150
+ }
+ ],
+ pnoTypes: [
+ {
+ hasDesignatedPorts: false,
+ minimumNotificationPeriod: 4,
+ pnoTypeName: 'Préavis type Z'
+ }
+ ],
+ port: 'BROIA',
+ predictedArrivalDatetimeUtc: tripStartDateAsDayjs.add(30, 'hours').format('YYYY-MM-DDTHH:mm:ss[Z]'),
+ predictedLandingDatetimeUtc: tripStartDateAsDayjs.add(31, 'hours').format('YYYY-MM-DDTHH:mm:ss[Z]'),
+ purpose: 'LAN',
+ riskFactor: 2.9,
+ tripStartDate: tripStartDateAsDayjs.format('YYYY-MM-DDTHH:mm:ss[Z]'),
+ updatedAt: null,
+ updatedBy: null
+ },
+ vessel_name
+ },
+ // We need to have the exact same props as the DAT report otherwise `sql()` batch insert will fail
+ {
+ cfr: null,
+ enriched: true,
+ flag_state: null,
+ id: 2 * FAKE_PNO_COUNT + index,
+ integration_datetime_utc: operation_datetime_utc,
+ log_type: null,
+ operation_datetime_utc,
+ operation_number: `${report_id}_RET`,
+ operation_type: 'RET',
+ referenced_report_id: report_id,
+ report_datetime_utc: null,
+ report_id: null,
+ transmission_format,
+ trip_gears: null,
+ trip_segments: null,
+ value: {
+ returnStatus: '000'
+ },
+ vessel_name: null
+ }
+ ]
+}
+
+function getFakeReporting(index) {
+ const vessel_id = faker.number.int({ max: FAKE_VESSEL_COUNT * 2, min: FAKE_VESSEL_COUNT + 1 })
+ const internal_reference_number = `FAKCFR_${vessel_id}`
+ const vessel_name = `FAUX NAVIRE ${vessel_id}`
+
+ const creationDate = dayjs(
+ faker.date.between({
+ from: START_DATE,
+ to: END_DATE
+ })
+ )
+
+ const type = faker.helpers.arrayElement(['ALERT', 'INFRACTION_SUSPICION', 'OBSERVATION'])
+ const value =
+ type === 'ALERT'
+ ? {
+ natinfCode: 7059,
+ riskFactor: faker.number.float({ max: 5, min: 0 }),
+ seaFront: faker.helpers.arrayElement(['MED', 'MEMN', 'NAMO', 'SA']),
+ type: 'THREE_MILES_TRAWLING_ALERT'
+ }
+ : {
+ authorContact: '',
+ authorTrigram: faker.string.alpha(3).toUpperCase(),
+ controlUnitId: null,
+ description: faker.lorem.sentence(),
+ dml: 'DML 29',
+ natinfCode: 23588,
+ reportingActor: faker.helpers.arrayElement(['OPS', 'UNIT']),
+ seaFront: faker.helpers.arrayElement(['MED', 'MEMN', 'NAMO', 'SA']),
+ title: faker.lorem.sentence(),
+ type
+ }
+
+ return {
+ archived: false,
+ creation_date: creationDate.format('YYYY-MM-DD HH:mm:ss'),
+ deleted: false,
+ external_reference_number: null,
+ flag_state: 'FR',
+ id: FAKE_REPORTING_COUNT + index,
+ internal_reference_number,
+ ircs: null,
+ latitude: faker.location.latitude({ max: 51, min: 42, precision: 3 }),
+ longitude: faker.location.longitude({ max: 10, min: -7, precision: 3 }),
+ type,
+ validation_date: creationDate.add(2, 'hours').format('YYYY-MM-DD HH:mm:ss'),
+ value,
+ vessel_id,
+ vessel_identifier: 'INTERNAL_REFERENCE_NUMBER',
+ vessel_name
+ }
+}
+
+function getFakeVessel(index) {
+ const id = FAKE_VESSEL_COUNT + index
+
+ return {
+ cfr: `FAKCFR_${id}`,
+ flag_state: 'FR',
+ id,
+ length: 99,
+ vessel_name: `FAUX NAVIRE ${id}`
+ }
+}
+
+const sql = postgres('postgres://postgres:postgres@localhost:5432/monitorfishdb')
+
+async function run() {
+ let batchIndex
+ const spinner = ora('Starting...').start()
+
+ try {
+ // Ignore SQL notices
+ await sql`SET client_min_messages TO WARNING`
+
+ spinner.text = 'Deleting fake vessels...'
+ await sql`DELETE FROM vessels WHERE id >= ${FAKE_VESSEL_COUNT + 1} AND id <= ${FAKE_VESSEL_COUNT * 2}`
+ spinner.succeed('Fake vessels successfully deleted.')
+
+ spinner.start('Generating fake vessels...')
+ batchIndex = 1
+ while (batchIndex <= FAKE_VESSEL_BATCH_COUNT) {
+ const startIndex = (batchIndex - 1) * BATCH_SIZE + 1
+ const endIndex = batchIndex * BATCH_SIZE
+ spinner.text = `Generating fake vessels ${startIndex}->${endIndex} / ${FAKE_VESSEL_COUNT} (${Math.round((10000 * startIndex) / FAKE_VESSEL_COUNT) / 100}%)...`
+
+ const fakeVessels = BATCH_SKELETON.map((_, index) => getFakeVessel(startIndex + index))
+ await sql`INSERT INTO vessels ${sql(fakeVessels)}`
+
+ batchIndex += 1
+ }
+ spinner.succeed('Fake vessels successfully generated.')
+
+ spinner.start('Deleting fake reportings...')
+ await sql`DELETE FROM reportings WHERE id >= ${FAKE_REPORTING_COUNT + 1} AND id <= ${FAKE_REPORTING_COUNT * 2}`
+ spinner.succeed('Fake reportings successfully deleted.')
+
+ spinner.start('Generating fake reporting...')
+ batchIndex = 1
+ while (batchIndex <= FAKE_REPORTING_BATCH_COUNT) {
+ const startIndex = (batchIndex - 1) * BATCH_SIZE + 1
+ const endIndex = batchIndex * BATCH_SIZE
+ spinner.text = `Generating fake reportings ${startIndex}->${endIndex} / ${FAKE_REPORTING_COUNT} (${Math.round((10000 * startIndex) / FAKE_REPORTING_COUNT) / 100}%)...`
+
+ const fakeReportings = BATCH_SKELETON.map((_, index) => getFakeReporting(startIndex + index))
+ await sql`INSERT INTO reportings ${sql(fakeReportings)}`
+
+ batchIndex += 1
+ }
+ spinner.succeed('Fake reportings successfully generated.')
+
+ spinner.start('Deleting fake PNO logbook reports...')
+ // Prevent TimescaleDB truncate cascade notices from spamming output (`truncate cascades to table "_hyper_2_135_chunk"`)
+ await sql`SET client_min_messages TO WARNING`
+ await sql`TRUNCATE TABLE logbook_raw_messages CASCADE`
+ await sql`TRUNCATE TABLE logbook_reports`
+ await sql`RESET client_min_messages`
+ spinner.succeed('Fake PNO logbook reports successfully deleted.')
+
+ spinner.start('Generating fake PNO logbook report...')
+ batchIndex = 1
+ while (batchIndex <= FAKE_PNO_BATCH_COUNT) {
+ const startIndex = (batchIndex - 1) * BATCH_SIZE + 1
+ const endIndex = batchIndex * BATCH_SIZE
+ spinner.text = `Generating fake PNO logbook report ${startIndex}->${endIndex} / ${FAKE_PNO_COUNT} (${Math.round((10000 * startIndex) / FAKE_PNO_COUNT) / 100}%)...`
+
+ const fakeLogbookRawMessages = BATCH_SKELETON.flatMap((_, index) =>
+ getFakePnoLogbookRawMessages(startIndex + index)
+ )
+ const fakeLogbookReports = BATCH_SKELETON.flatMap((_, index) => getFakePnoLogbookReports(startIndex + index))
+
+ spinner.text = `Generating fake PNO logbook report ${startIndex}->${endIndex} / ${FAKE_PNO_COUNT} (${Math.round((10000 * startIndex) / FAKE_PNO_COUNT) / 100}%) [RANDOMNESS CHECK: ${fakeLogbookReports[0].operation_datetime_utc}]...`
+ await sql`INSERT INTO logbook_raw_messages ${sql(fakeLogbookRawMessages)}`
+ await sql`INSERT INTO logbook_reports ${sql(fakeLogbookReports)}`
+
+ batchIndex += 1
+ }
+ spinner.succeed('Fake PNO logbook report successfully generated.')
+ } catch (error) {
+ spinner.fail('Failed to generate perf data.')
+
+ console.error(error)
+
+ process.exit(1)
+ } finally {
+ await sql.end()
+ }
+}
+
+run()