From d245afa21c785cae341099018e44c635cd6373b7 Mon Sep 17 00:00:00 2001 From: SAFINE LAGET Anis <anis.safine@beta.gouv.fr> Date: Mon, 13 Jan 2025 09:38:12 +0000 Subject: [PATCH] =?UTF-8?q?fix(filtre):=20les=20r=C3=A9gions=20prennent=20?= =?UTF-8?q?en=20compte=20les=20fa=C3=A7ades=20maritimes=20(pub/pnm-public/?= =?UTF-8?q?camino!1620)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/api/src/api/rest/etapes.ts | 9 +++-- .../api/src/api/rest/perimetre.queries.ts | 14 +++++-- .../statistiques/evolution-titres.queries.ts | 37 ++----------------- .../evolution-titres.queries.types.ts | 9 +++-- ...apes-areas-update.test.integration.ts.snap | 6 +++ ...es-etapes-areas-update.test.integration.ts | 8 ++-- .../processes/titres-etapes-areas-update.ts | 14 ++++++- .../api/src/database/models/titres-etapes.ts | 1 + .../src/database/queries/_titres-filters.ts | 16 ++++---- .../20250109134315_add-column-departements.ts | 23 ++++++++++++ packages/api/src/types.ts | 3 +- packages/common/src/static/facades.test.ts | 25 ++++++++++++- packages/common/src/static/facades.ts | 15 ++++++-- 13 files changed, 117 insertions(+), 63 deletions(-) create mode 100644 packages/api/src/knex/migrations/20250109134315_add-column-departements.ts diff --git a/packages/api/src/api/rest/etapes.ts b/packages/api/src/api/rest/etapes.ts index 2a9fb0eac..fa1d89915 100644 --- a/packages/api/src/api/rest/etapes.ts +++ b/packages/api/src/api/rest/etapes.ts @@ -318,7 +318,7 @@ type PerimetreInfos = { sdomZones: SDOMZoneId[] surface: KM2 | null } & Pick<GraphqlEtape, 'geojson4326Forages' | 'geojsonOrigineForages'> & - Pick<GetGeojsonInformation, 'communes' | 'forets'> + Pick<GetGeojsonInformation, 'communes' | 'forets' | 'departements'> export const getPerimetreInfosInternal = ( pool: Pool, geojson4326Perimetre: GraphqlEtape['geojson4326Perimetre'], @@ -340,11 +340,13 @@ export const getPerimetreInfosInternal = ( ), Effect.bind('geojsonInformation', value => getGeojsonInformation(pool, value.geometry)), Effect.bind('forage', () => getForagesProperties(titreTypeId, geojsonOrigineGeoSystemeId, geojsonOrigineForages, pool)), - Effect.map(({ geojsonInformation: { surface, communes, forets, secteurs, sdom }, forage: { geojson4326Forages } }) => ({ + Effect.let('secteursMaritime', ({ geojsonInformation: { secteurs } }) => secteurs.map(s => getSecteurMaritime(s))), + Effect.map(({ secteursMaritime, geojsonInformation: { departements, surface, communes, forets, sdom }, forage: { geojson4326Forages } }) => ({ surface, communes, forets, - secteursMaritime: secteurs.map(s => getSecteurMaritime(s)), + secteursMaritime, + departements, sdomZones: sdom, geojson4326Forages, geojsonOrigineForages, @@ -355,6 +357,7 @@ export const getPerimetreInfosInternal = ( forets: [], secteursMaritime: [], sdomZones: [], + departements: [], surface: null, geojson4326Forages: null, geojsonOrigineForages: null, diff --git a/packages/api/src/api/rest/perimetre.queries.ts b/packages/api/src/api/rest/perimetre.queries.ts index 7864f53d6..a76f15078 100644 --- a/packages/api/src/api/rest/perimetre.queries.ts +++ b/packages/api/src/api/rest/perimetre.queries.ts @@ -9,14 +9,15 @@ import { TitreStatutId, TitresStatutIds, titreStatutIdValidator } from 'camino-c import { DOMAINES_IDS, DomaineId } from 'camino-common/src/static/domaines' import { TitreSlug, titreSlugValidator } from 'camino-common/src/validators/titres' import { communeIdValidator } from 'camino-common/src/static/communes' -import { secteurDbIdValidator } from 'camino-common/src/static/facades' +import { getDepartementsBySecteurs, getSecteurMaritime, secteurDbIdValidator } from 'camino-common/src/static/facades' import { foretIdValidator } from 'camino-common/src/static/forets' import { sdomZoneIdValidator } from 'camino-common/src/static/sdom' import { KM2, M2, km2Validator, m2Validator } from 'camino-common/src/number' -import { isNullOrUndefined } from 'camino-common/src/typescript-tools' +import { isNullOrUndefined, onlyUnique } from 'camino-common/src/typescript-tools' import { ZodUnparseable, zodParseEffect, zodParseEffectCallback } from '../../tools/fp-tools' import { CaminoError } from 'camino-common/src/zod-tools' import { Effect, pipe } from 'effect' +import { departementIdValidator, toDepartementId } from 'camino-common/src/static/departement' const arrayTuple4326CoordinateValidator = z.array(z.tuple([z.number().min(-180).max(180), z.number().min(-90).max(90)])) const convertPointsStringifyError = 'Impossible de transformer la feature collection' as const @@ -205,12 +206,16 @@ export const getGeojsonInformation = (pool: Pool, geojson4326_perimetre: MultiPo effectDbQueryAndValidate(getGeojsonInformationDb, { geojson4326_perimetre }, pool, getGeojsonInformationDbValidator), Effect.bind('response', result => (result.length === 1 ? Effect.succeed(result[0]) : Effect.fail({ message: requestError }))), Effect.bind('surface', result => m2ToKm2(result.response.surface)), + Effect.let('secteursMaritime', ({ response }) => response.secteurs.map(s => getSecteurMaritime(s))), + Effect.let('departements', ({ response, secteursMaritime }) => { + return [...getDepartementsBySecteurs(secteursMaritime), ...response.communes.map(c => toDepartementId(c.id))].filter(onlyUnique) + }), Effect.filterOrFail( ({ response }) => response.surface <= SURFACE_M2_MAX, () => ({ message: 'Problème de validation de données' as const, detail: `Le périmètre ne doit pas excéder ${SURFACE_M2_MAX}M²` }) ), - Effect.map(({ response, surface }) => { - return { ...response, surface } + Effect.map(({ response, departements, surface }) => { + return { ...response, departements, surface } }) ) } @@ -231,6 +236,7 @@ const getGeojsonInformationValidator = z.object({ .nullable() .transform(nullToEmptyArray), secteurs: z.array(secteurDbIdValidator).nullable().transform(nullToEmptyArray), + departements: z.array(departementIdValidator).nullable().transform(nullToEmptyArray), }) // Surface maximale acceptée pour un titre diff --git a/packages/api/src/api/rest/statistiques/evolution-titres.queries.ts b/packages/api/src/api/rest/statistiques/evolution-titres.queries.ts index 190769ba6..630906c0c 100644 --- a/packages/api/src/api/rest/statistiques/evolution-titres.queries.ts +++ b/packages/api/src/api/rest/statistiques/evolution-titres.queries.ts @@ -17,7 +17,6 @@ interface GetDepotProps { etapeTypeId?: EtapeTypeId titreTypeId?: TitreTypeId } -// TODO 2023-09-26 utiliser left et right plutôt que substring et mettre des index comme pour packages/api/src/knex/migrations/20230926083529_add-index-on-left-right.ts export const getDepot = async (pool: Pool, params: GetDepotProps): Promise<AnneeCountStatistique[]> => dbQueryAndValidate(getDepotDb, params, pool, anneeCountStatistiqueValidator) const getDepotDb = sql<Redefine<IGetDepotDbQuery, GetDepotProps, AnneeCountStatistique>>` select @@ -34,14 +33,7 @@ where and td.type_id in $$ demarcheTypeIds and t.type_id = $ titreTypeId and substring(et. "date", 0, 5)::int >= $ anneeDepart - and exists ( - select - * - from - jsonb_array_elements(etapes_communes.communes) as c - where (substring(c ->> 'id', 1, 2) != '97' - and substring(c ->> 'id', 1, 2) in $$ departements) - or substring(c ->> 'id', 1, 3) in $$ departements) + and etapes_communes.departements ?| $departements group by substring(et. "date", 0, 5) ` @@ -66,14 +58,7 @@ where td.type_id in $$ demarcheTypeIds and t.type_id = $ titreTypeId and substring(td. "demarche_date_debut", 0, 5)::int >= $ anneeDepart - and exists ( - select - * - from - jsonb_array_elements(t_points.communes) as c - where (substring(c ->> 'id', 1, 2) != '97' - and substring(c ->> 'id', 1, 2) in $$ departements) - or substring(c ->> 'id', 1, 3) in $$ departements) + and t_points.departements ?| $ departements group by substring(td. "demarche_date_debut", 0, 5) ` @@ -97,14 +82,7 @@ where td.type_id in $$ demarcheTypeIds and t.type_id = $ titreTypeId and substring(td. "demarche_date_debut", 0, 5)::int >= $ anneeDepart - and exists ( - select - * - from - jsonb_array_elements(t_points.communes) as c - where (substring(c ->> 'id', 1, 2) != '97' - and substring(c ->> 'id', 1, 2) in $$ departements) - or substring(c ->> 'id', 1, 3) in $$ departements) + and t_points.departements ?| $ departements group by substring(td. "demarche_date_debut", 0, 5) ` @@ -142,14 +120,7 @@ and td.type_id in $$ demarcheTypeIds and t.type_id = $ titreTypeId and td.statut_id in $$ demarcheStatutIds and substring(et. "date", 0, 5)::int >= $ anneeDepart -and exists ( - select - * - from - jsonb_array_elements(etapes_communes.communes) as c - where (substring(c ->> 'id', 1, 2) != '97' - and substring(c ->> 'id', 1, 2) in $$ departements) - or substring(c ->> 'id', 1, 3) in $$ departements) +and etapes_communes.departements ?| $ departements group by substring(et. "date", 0, 5) ` diff --git a/packages/api/src/api/rest/statistiques/evolution-titres.queries.types.ts b/packages/api/src/api/rest/statistiques/evolution-titres.queries.types.ts index 4e9d8e4ea..d95285643 100644 --- a/packages/api/src/api/rest/statistiques/evolution-titres.queries.types.ts +++ b/packages/api/src/api/rest/statistiques/evolution-titres.queries.types.ts @@ -1,10 +1,11 @@ /** Types generated for queries found in "src/api/rest/statistiques/evolution-titres.queries.ts" */ +export type stringArray = (string)[]; /** 'GetDepotDb' parameters type */ export interface IGetDepotDbParams { anneeDepart?: number | null | void; demarcheTypeIds: readonly (string | null | void)[]; - departements: readonly (string | null | void)[]; + departements?: stringArray | null | void; etapeTypeId?: string | null | void; titreTypeId?: string | null | void; } @@ -25,7 +26,7 @@ export interface IGetDepotDbQuery { export interface IGetOctroiDbParams { anneeDepart?: number | null | void; demarcheTypeIds: readonly (string | null | void)[]; - departements: readonly (string | null | void)[]; + departements?: stringArray | null | void; titreTypeId?: string | null | void; } @@ -45,7 +46,7 @@ export interface IGetOctroiDbQuery { export interface IGetSurfaceDbParams { anneeDepart?: number | null | void; demarcheTypeIds: readonly (string | null | void)[]; - departements: readonly (string | null | void)[]; + departements?: stringArray | null | void; titreTypeId?: string | null | void; } @@ -66,7 +67,7 @@ export interface IGetEtapesTypesDecisionRefusDbParams { anneeDepart?: number | null | void; demarcheStatutIds: readonly (string | null | void)[]; demarcheTypeIds: readonly (string | null | void)[]; - departements: readonly (string | null | void)[]; + departements?: stringArray | null | void; etapeStatutFait?: string | null | void; etapeStatutRejet: readonly (string | null | void)[]; etapesTypesDecisionRefus: readonly (string | null | void)[]; diff --git a/packages/api/src/business/processes/__snapshots__/titres-etapes-areas-update.test.integration.ts.snap b/packages/api/src/business/processes/__snapshots__/titres-etapes-areas-update.test.integration.ts.snap index 08be4e26e..f1109fe75 100644 --- a/packages/api/src/business/processes/__snapshots__/titres-etapes-areas-update.test.integration.ts.snap +++ b/packages/api/src/business/processes/__snapshots__/titres-etapes-areas-update.test.integration.ts.snap @@ -25,6 +25,9 @@ exports[`titresEtapesAreasUpdate > met à jour les communes, forêts et zone du "dateFin": null, "demarcheIdEnConcurrence": null, "demarcheIdsConsentement": [], + "departements": [ + "973", + ], "duree": null, "etapeFondamentaleId": "titreEtapeIdUniquePourMiseAJourAreas", "forets": [ @@ -170,6 +173,9 @@ exports[`titresEtapesAreasUpdate > met à jour les communes, forêts et zone du "dateFin": null, "demarcheIdEnConcurrence": null, "demarcheIdsConsentement": [], + "departements": [ + "973", + ], "duree": null, "etapeFondamentaleId": "titreEtapeIdUniquePourMiseAJourAreas", "forets": [ diff --git a/packages/api/src/business/processes/titres-etapes-areas-update.test.integration.ts b/packages/api/src/business/processes/titres-etapes-areas-update.test.integration.ts index 9f4ad589d..4cc8fd96d 100644 --- a/packages/api/src/business/processes/titres-etapes-areas-update.test.integration.ts +++ b/packages/api/src/business/processes/titres-etapes-areas-update.test.integration.ts @@ -103,15 +103,17 @@ describe('titresEtapesAreasUpdate', () => { { id: baisieuxId, surface: 12 }, { id: saintElieId, surface: 12 }, ], + departements: [], geojson4326Perimetre: multiPolygonWith4Points, geojsonOriginePerimetre: multiPolygonWith4Points, geojsonOrigineGeoSystemeId: '4326', }, ]) - + expect((await TitresEtapes.query().where('id', titreEtapeId))[0].departements).toStrictEqual([]) await titresEtapesAreasUpdate(dbPool, [titreEtapeId]) - - expect(await TitresEtapes.query().where('id', titreEtapeId)).toMatchSnapshot() + const newEtape = await TitresEtapes.query().where('id', titreEtapeId) + expect(newEtape[0].departements).toStrictEqual(['973']) + expect(newEtape).toMatchSnapshot() await Titres.query() .patch({ propsTitreEtapesIds: { points: titreEtapeId } }) .where({ id: titreId }) diff --git a/packages/api/src/business/processes/titres-etapes-areas-update.ts b/packages/api/src/business/processes/titres-etapes-areas-update.ts index 6a93e0f3b..5779249ae 100644 --- a/packages/api/src/business/processes/titres-etapes-areas-update.ts +++ b/packages/api/src/business/processes/titres-etapes-areas-update.ts @@ -12,6 +12,7 @@ import type { Pool } from 'pg' import { SDOMZoneId } from 'camino-common/src/static/sdom' import { M2 } from 'camino-common/src/number' import { callAndExit } from '../../tools/fp-tools' +import { DepartementId } from 'camino-common/src/static/departement' /** * Met à jour tous les territoires d’une liste d’étapes @@ -41,18 +42,29 @@ export const titresEtapesAreasUpdate = async (pool: Pool, titresEtapesIds?: stri } try { if (isNotNullNorUndefined(titreEtape.geojson4326Perimetre)) { - const { forets, sdom, secteurs, communes } = await callAndExit(getGeojsonInformation(pool, titreEtape.geojson4326Perimetre.geometry)) + const { forets, sdom, secteurs, communes, departements } = await callAndExit(getGeojsonInformation(pool, titreEtape.geojson4326Perimetre.geometry)) await intersectForets(titreEtape, forets) await intersectSdom(titreEtape, sdom) await intersectCommunes(titreEtape, communes) await intersectSecteursMaritime(titreEtape, secteurs) + await updateDepartements(titreEtape, departements) } } catch (e) { console.error(`Une erreur est survenue lors du traitement de l'étape ${titreEtape.id}`) } } } + +async function updateDepartements(titreEtape: Pick<ITitreEtape, 'id' | 'departements'>, departementIds: DepartementId[]) { + const sortedDepartementIds = departementIds.toSorted() + + if (sortedDepartementIds.length !== titreEtape.departements?.length || titreEtape.departements.some((elem, index) => elem !== sortedDepartementIds[index])) { + console.info(`nouveaux départements pour l'étape ${titreEtape.id}. Anciens: ${JSON.stringify(titreEtape.departements)}, nouveaux: ${JSON.stringify(sortedDepartementIds)}`) + await knex.raw(`update titres_etapes set departements = '["${sortedDepartementIds.join('","')}"]' where id ='${titreEtape.id}'`) + } +} + async function intersectSdom(titreEtape: Pick<ITitreEtape, 'sdomZones' | 'id'>, sdomZonesIds: SDOMZoneId[]) { if (sdomZonesIds.length > 0) { const sortedSdomZonesIds = sdomZonesIds.toSorted() diff --git a/packages/api/src/database/models/titres-etapes.ts b/packages/api/src/database/models/titres-etapes.ts index e220cb206..a159c69b2 100644 --- a/packages/api/src/database/models/titres-etapes.ts +++ b/packages/api/src/database/models/titres-etapes.ts @@ -51,6 +51,7 @@ class TitresEtapes extends Model { titulaireIds: { type: ['array', 'null'] }, amodiataireIds: { type: ['array', 'null'] }, communes: { type: ['array', 'null'] }, + departements: { type: ['array', 'null'] }, forets: { type: ['array', 'null'] }, secteursMaritime: { type: ['array', 'null'] }, administrationsLocales: { type: ['array', 'null'] }, diff --git a/packages/api/src/database/queries/_titres-filters.ts b/packages/api/src/database/queries/_titres-filters.ts index 45c985553..b2f56e64e 100644 --- a/packages/api/src/database/queries/_titres-filters.ts +++ b/packages/api/src/database/queries/_titres-filters.ts @@ -161,17 +161,17 @@ export const titresFiltersQueryModify = ( } departementIds = departementIds.filter(onlyUnique).filter(isDepartementId) + if (isNotNullNorUndefinedNorEmpty(departementIds)) { + if (name === 'titre') { + q.leftJoinRelated('titre') + } + q.joinRaw(`join titres_etapes as departements_points_etapes on departements_points_etapes.id = ${name}."props_titre_etapes_ids" #>> '{points}'`) - if (departementIds.length > 0) { - q.joinRaw(`join titres_etapes as departements_points_etapes on departements_points_etapes.id = ${name}."props_titre_etapes_ids" #>> '{points}'`).whereRaw( - `exists (select 1 from jsonb_array_elements(departements_points_etapes.communes) departements_filter_communes where substring(departements_filter_communes ->> 'id', 1, 2) in (${departementIds - .map(() => '?') - .join(',')}) or substring(departements_filter_communes ->> 'id', 1, 3) in (${departementIds.map(() => '?').join(',')}))`, - [...departementIds, ...departementIds] - ) + q.leftJoinRelated(jointureFormat(name, 'pointsEtape')) + q.whereRaw(`?? \\?| array['${departementIds.join("','")}']`, 'departements_points_etapes.departements') } - if (facadesMaritimes && facadesMaritimes.length > 0) { + if (isNotNullNorUndefinedNorEmpty(facadesMaritimes)) { const secteurs = facadesMaritimes.flatMap(facade => getSecteurs(facade)) if (name === 'titre') { q.leftJoinRelated('titre') diff --git a/packages/api/src/knex/migrations/20250109134315_add-column-departements.ts b/packages/api/src/knex/migrations/20250109134315_add-column-departements.ts new file mode 100644 index 000000000..e001ed0a1 --- /dev/null +++ b/packages/api/src/knex/migrations/20250109134315_add-column-departements.ts @@ -0,0 +1,23 @@ +import { EtapeId } from 'camino-common/src/etape' +import { CommuneId } from 'camino-common/src/static/communes' +import { toDepartementId } from 'camino-common/src/static/departement' +import { getDepartementsBySecteurs, SecteursMaritimes } from 'camino-common/src/static/facades' +import { onlyUnique, toSorted } from 'camino-common/src/typescript-tools' +import { Knex } from 'knex' + +export const up = async (knex: Knex): Promise<void> => { + await knex.raw("ALTER TABLE titres_etapes ADD COLUMN departements JSONB NOT NULL DEFAULT '[]'::jsonb") + await knex.raw('CREATE INDEX titres_etapes_departements_index ON titres_etapes USING gin(departements)') + const rows: { rows: { id: EtapeId; secteurs_maritime: SecteursMaritimes[]; communes: { id: CommuneId }[] }[] } = await knex.raw( + 'SELECT id, secteurs_maritime, communes FROM titres_etapes WHERE jsonb_array_length(secteurs_maritime) > 0 OR jsonb_array_length(communes) > 0' + ) + + for (const etape of rows.rows) { + await knex.raw('UPDATE titres_etapes SET departements = ? WHERE id = ?', [ + JSON.stringify(toSorted([...getDepartementsBySecteurs(etape.secteurs_maritime), ...etape.communes.map(c => toDepartementId(c.id))].filter(onlyUnique))), + etape.id, + ]) + } +} + +export const down = (): void => {} diff --git a/packages/api/src/types.ts b/packages/api/src/types.ts index 05b47c72d..2cd306f89 100644 --- a/packages/api/src/types.ts +++ b/packages/api/src/types.ts @@ -1,5 +1,5 @@ import { AdministrationId } from 'camino-common/src/static/administrations' -import { CodePostal } from 'camino-common/src/static/departement' +import { CodePostal, DepartementId } from 'camino-common/src/static/departement' import { User } from 'camino-common/src/roles' import { TitreTypeId } from 'camino-common/src/static/titresTypes' import { DemarcheTypeId } from 'camino-common/src/static/demarchesTypes' @@ -251,6 +251,7 @@ export type ITitreEtape = { entrepriseDocumentIds?: EntrepriseDocumentId[] | null etapeDocuments?: unknown[] communes?: ICommune[] | null + departements?: DepartementId[] | null forets?: ForetId[] | null sdomZones?: SDOMZoneId[] | null secteursMaritime?: SecteursMaritimes[] | null diff --git a/packages/common/src/static/facades.test.ts b/packages/common/src/static/facades.test.ts index c32ac9968..cb2a9d325 100644 --- a/packages/common/src/static/facades.test.ts +++ b/packages/common/src/static/facades.test.ts @@ -1,12 +1,33 @@ -import { assertsFacade, assertsSecteur, getDepartementsBySecteurs, getSecteurMaritime, getSecteurs, secteurAJour } from './facades' +import { DEPARTEMENT_IDS } from './departement' +import { assertsFacade, assertsSecteur, getDepartementsBySecteurs, getSecteurMaritime, getSecteurs, getSecteursByDepartements, secteurAJour } from './facades' import { test, expect, describe } from 'vitest' -describe('getDepartementsBySecteur()', () => { +describe('getDepartementsBySecteur', () => { test('retourne la liste des départements pour un secteur donné', () => { expect(getDepartementsBySecteurs(["Golfe d'Ajaccio", 'Bretagne nord'])).toStrictEqual(['35', '22', '29']) }) }) +describe('getSecteursByDepartements', () => { + test('retourne la liste des secteurs maritimes pour un département donnée', () => { + expect(getSecteursByDepartements([DEPARTEMENT_IDS.Nord])).toMatchInlineSnapshot(` + [ + "Caps et détroit du Pas de Calais", + ] + `) + expect(getSecteursByDepartements([DEPARTEMENT_IDS.Finistère])).toMatchInlineSnapshot(` + [ + "Plateau continental nord", + "Plateau continental central", + "Manche occidentale", + "Bretagne nord", + "Parc naturel marin d'Iroise", + "Rade de Brest", + "Bretagne sud", + ] + `) + }) +}) describe('getSecteurMaritime()', () => { test("retourne le secteur maritime selon l'id", () => { expect(getSecteurMaritime(43)).toEqual('Riviera') diff --git a/packages/common/src/static/facades.ts b/packages/common/src/static/facades.ts index 458991560..94409e10a 100644 --- a/packages/common/src/static/facades.ts +++ b/packages/common/src/static/facades.ts @@ -1,5 +1,5 @@ import { DepartementId, DEPARTEMENT_IDS } from './departement' -import { getEntries, getKeys, onlyUnique } from '../typescript-tools' +import { getEntries, getKeys, getValues, onlyUnique } from '../typescript-tools' import { z } from 'zod' // prettier-ignore @@ -10,7 +10,7 @@ const SECTEURS_MARITIME_IDS = [ "Caps et détroit du Pas de Calais", "Estuaires picards et mer d'Opale", "Côte d'Albâtre et ses ouverts", "Baie de Seine", "Large baie de Seine", "Nord Cotentin", "Ouest Cotentin - Baie du Mont Saint-Michel", "Manche Ouest au large des îles anglo-normandes", "Plaine abyssale", "Talus continental", "Plateau continental nord", "Plateau continental central", "Manche occidentale", "Golfe normand breton et baie du Mont Saint-Michel", "Bretagne nord", "Parc naturel marin d'Iroise", "Rade de Brest", "Bretagne sud", "Estuaire de la Loire", "Baie de Bourgneuf et littoral vendéen", "Parc naturel marin de l'estuaire de la Gironde et de la mer des Pertuis", "Parc Naturel Marin de l'estuaire de la Gironde et de la Mer des Pertuis", "Côte sableuse aquitaine", "Parc naturel marin du Bassin d'Arcachon", "Côte rocheuse basque, estuaire de l'Adour, Gouf de Capbreton", "Talus du Golfe de Gascogne", "Plaine abyssale du Golfe de Gascogne", "Périmètre du Parc naturel marin du Golfe du Lion", "Port-la-Nouvelle", "Littoral languedocien", "Sète", "Camargue", "Plateau du Golfe du Lion", "Côte Bleue", "Golfe de Fos-sur-Mer", "Rade de Marseille", "Périmètre du parc national des Calanques", "Littoral varois Ouest", "Rade de Toulon", "Périmètre du Parc national de Port-Cros", "Littoral varois Est", "Riviera", "Nice et abords", "Littoral des Alpes-Maritimes", "Large Provence Alpes Côte d'Azur", "Plaine bathyale", "Canyons du Golfe du Lion", "Périmètre du Parc naturel marin du Cap Corse et de l'Agriate", "Bastia", "Balagne", "Scandola", "Littoral occidental de la Corse", "Golfe d'Ajaccio", "Large côte occidental de la Corse", "Bouches de Bonifacio Ouest", "Bouches de Bonifacio Est - Porto-Vecchio", "Plaine orientale et large Est de la Corse", "Plateau continental du Golfe de Gascogne",] as const // prettier-ignore -const TECHNICAL_IDS = [z.literal(1), z.literal(2), z.literal(3), z.literal(4), z.literal(5), z.literal(8), z.literal(6), z.literal(7), z.literal(9), z.literal(10), z.literal(18), z.literal(17), z.literal(11), z.literal(15), z.literal(13), z.literal(14), z.literal(12), z.literal(21), z.literal(20), z.literal(19), z.literal(16), z.literal(22), z.literal(25), z.literal(24), z.literal(28), z.literal(23), z.literal(26), z.literal(27), z.literal(44), z.literal(35), z.literal(38), z.literal(39), z.literal(29), z.literal(31), z.literal(47), z.literal(48), z.literal(58), z.literal(32), z.literal(51), z.literal(30), z.literal(50), z.literal(40), z.literal(45), z.literal(43), z.literal(52), z.literal(41), z.literal(55), z.literal(54), z.literal(37), z.literal(46), z.literal(36), z.literal(61), z.literal(62), z.literal(59), z.literal(49), z.literal(53), z.literal(33), z.literal(57), z.literal(34),z.literal(42), z.literal(56), z.literal(60) ] as const +const TECHNICAL_IDS = [z.literal(1), z.literal(2), z.literal(3), z.literal(4), z.literal(5), z.literal(8), z.literal(6), z.literal(7), z.literal(9), z.literal(10), z.literal(18), z.literal(17), z.literal(11), z.literal(15), z.literal(13), z.literal(14), z.literal(12), z.literal(21), z.literal(20), z.literal(19), z.literal(16), z.literal(22), z.literal(25), z.literal(24), z.literal(28), z.literal(23), z.literal(26), z.literal(27), z.literal(44), z.literal(35), z.literal(38), z.literal(39), z.literal(29), z.literal(31), z.literal(47), z.literal(48), z.literal(58), z.literal(32), z.literal(51), z.literal(30), z.literal(50), z.literal(40), z.literal(45), z.literal(43), z.literal(52), z.literal(41), z.literal(55), z.literal(54), z.literal(37), z.literal(46), z.literal(36), z.literal(61), z.literal(62), z.literal(59), z.literal(49), z.literal(53), z.literal(33), z.literal(57), z.literal(34),z.literal(42), z.literal(56), z.literal(60) ] as const export const secteurDbIdValidator = z.union(TECHNICAL_IDS) export type SecteursMaritimesIds = z.infer<typeof secteurDbIdValidator> @@ -19,6 +19,7 @@ export const facadeMaritimeIdValidator = z.enum(IDS) export type FacadesMaritimes = z.infer<typeof facadeMaritimeIdValidator> export const secteurMaritimeValidator = z.enum(SECTEURS_MARITIME_IDS) +type SecteurMaritimeData = { ids: Readonly<SecteursMaritimesIds[]>; secteurId: string; departementIds: Readonly<DepartementId[]> } export type SecteursMaritimes = z.infer<typeof secteurMaritimeValidator> const facades = { 'Manche Est - Mer du Nord': { @@ -91,7 +92,7 @@ const facades = { 'Bouches de Bonifacio Est - Porto-Vecchio': { ids: [57], secteurId: '29', departementIds: [] }, 'Plaine orientale et large Est de la Corse': { ids: [34], secteurId: '30', departementIds: [] }, }, -} as const satisfies Record<FacadesMaritimes, { [key in SecteursMaritimes]?: { ids: Readonly<SecteursMaritimesIds[]>; secteurId: string; departementIds: Readonly<DepartementId[]> } }> +} as const satisfies Record<FacadesMaritimes, { [key in SecteursMaritimes]?: SecteurMaritimeData }> export const FACADES = Object.keys(facades) as FacadesMaritimes[] const SECTEURS = Object.values(facades).flatMap(f => Object.keys(f)) as SecteursMaritimes[] @@ -104,7 +105,13 @@ export const getDepartementsBySecteurs = (ids: SecteursMaritimes[]): Departement ) .filter(onlyUnique) } - +export const getSecteursByDepartements = (ids: DepartementId[]): SecteursMaritimes[] => { + return getValues(facades) + .flatMap(facade => Object.entries(facade).filter(([_key, secteur]) => ids.some(departementId => secteur.departementIds.includes(departementId)))) + .map(([key, _name]) => key) + .filter(onlyUnique) + .filter(isSecteurMaritime) +} export const getSecteurMaritime = (id: SecteursMaritimesIds): SecteursMaritimes => { const result: [SecteursMaritimes, any] | undefined = Object.values<Record<string, { ids: readonly number[] }>>(facades) .flatMap(f => getEntries(f, isSecteurMaritime)) -- GitLab