Skip to content
Snippets Groups Projects

chore(perimetres): corriger les périmètres qui ne sont pas valides

Merged SAFINE LAGET Anis requested to merge verifier-perimetres into master
2 files
+ 190
0
Compare changes
  • Side-by-side
  • Inline
Files
2
+ 189
0
import '../init'
import pg from 'pg'
import { config } from '../config/index'
import { Effect } from 'effect'
import { knex } from '../knex'
import { EtapeId } from 'camino-common/src/etape'
import { CaminoError } from 'camino-common/src/zod-tools'
import { getPerimetreInfosInternal, PerimetreInfos } from '../api/rest/etapes'
import { titreEtapeUpdationValidate } from '../business/validations/titre-etape-updation-validate'
import { titreDemarcheGet } from '../database/queries/titres-demarches'
import { isNullOrUndefined } from 'camino-common/src/typescript-tools'
import { userSuper } from '../database/user-super'
import { titreEtapeGet } from '../database/queries/titres-etapes'
import { iTitreEtapeToFlattenEtape } from '../api/_format/titres-etapes'
import { FlattenEtape } from 'camino-common/src/etape-form'
import TitresDemarches from '../database/models/titres-demarches'
import { TitreTypeId } from 'camino-common/src/static/titresTypes'
// Le pool ne doit être qu'aux entrypoints : le daily, le monthly, et l'application.
const pool = new pg.Pool({
host: config().PGHOST,
user: config().PGUSER,
password: config().PGPASSWORD,
database: config().PGDATABASE,
})
type QueryError = "Échec de récupération des ids d'étapes en BDD"
type PerimetreError = 'Certaines étapes ont des périmètres invalides'
type EtapeValidationError = "Échec de validation d'une étape"
type EtapeFetchError = "Échec de récupération d'une étape complète"
type DemarcheFetchError = "Échec de récupération d'une démarche"
type PipelineError = CaminoError<QueryError | PerimetreError | EtapeValidationError | EtapeFetchError | DemarcheFetchError>
function getAllEtapeIds(): Effect.Effect<{ id: EtapeId; titreTypeId: TitreTypeId }[], CaminoError<QueryError>, never> {
return Effect.gen(function* () {
const { rows } = yield* Effect.tryPromise({
try: () =>
knex.raw<{ rows: { id: EtapeId; titreTypeId: TitreTypeId }[] }>(`
SELECT
te.id,
t.type_id AS "titreTypeId"
FROM titres_etapes te
JOIN titres_demarches td ON td.id = te.titre_demarche_id
JOIN titres t ON t.id = td.titre_id
WHERE te.geojson4326_perimetre IS NOT NULL
LIMIT 100
`), // FIXME: retirer le LIMIT 100
catch: error => ({
message: "Échec de récupération des ids d'étapes en BDD" as const,
extra: { error },
}),
})
return rows
})
}
type InvalidEtape = { id: EtapeId; errors: string[] }
function filterInvalidEtapes(rows: { id: EtapeId; titreTypeId: TitreTypeId }[]): Effect.Effect<InvalidEtape[], CaminoError<GetEtapeErrorsErrors>, never> {
return Effect.gen(function* (_) {
const invalidEtapes: InvalidEtape[] = []
for (let i = 0; i < rows.length; i += 1) {
const errors = yield* _(getEtapeErrors(rows[i]))
if (errors.length > 0) {
invalidEtapes.push({ id: rows[i].id, errors })
console.error(`${rows[i].id} : ${errors.join(', ')}\n`)
}
}
return invalidEtapes
})
}
type FetchFullEtapeErrors = EtapeFetchError | DemarcheFetchError
function fetchFullEtape(etapeId: EtapeId): Effect.Effect<{ etape: FlattenEtape; demarche: TitresDemarches }, CaminoError<FetchFullEtapeErrors>, never> {
return Effect.gen(function* () {
const etape = yield* Effect.tryPromise({
try: async () => {
const titreEtape = await titreEtapeGet(etapeId, { fields: { demarche: { titre: { pointsEtape: { id: {} } } } }, fetchHeritage: true }, userSuper)
return iTitreEtapeToFlattenEtape(titreEtape)
},
catch: error => ({
message: "Échec de récupération d'une étape complète" as const,
extra: {
etapeId,
error: error instanceof Error ? error.message : error,
},
}),
})
const demarche = yield* Effect.tryPromise({
try: () => titreDemarcheGet(etape.titreDemarcheId, { fields: {} }, userSuper),
catch: error => ({
message: "Échec de récupération d'une démarche" as const,
error: error instanceof Error ? error.message : error,
}),
})
if (isNullOrUndefined(demarche) || isNullOrUndefined(demarche.titre)) {
return yield* Effect.fail({
message: "Échec de récupération d'une démarche" as const,
error: 'La démarche (ou son titre) est undefined',
})
}
return { etape, demarche }
})
}
type GetEtapeErrorsErrors = FetchFullEtapeErrors | EtapeValidationError
function getEtapeErrors(row: { id: EtapeId; titreTypeId: TitreTypeId }): Effect.Effect<string[], CaminoError<GetEtapeErrorsErrors>, never> {
return Effect.gen(function* () {
const { etape, demarche } = yield* fetchFullEtape(row.id)
return yield* Effect.tryPromise({
try: async () => {
if (isNullOrUndefined(etape.perimetre.value?.geojson4326Perimetre)) {
console.error(etape.perimetre.value)
throw new Error('Périmètre manquant')
}
if (isNullOrUndefined(demarche.titre)) {
throw new Error('Titre de la démarche manquant')
}
let perimetreInfos: PerimetreInfos
try {
perimetreInfos = await getPerimetreInfosInternal(
pool,
etape.perimetre.value.geojson4326Perimetre,
etape.perimetre.value.geojsonOriginePerimetre,
etape.perimetre.value.geojsonOriginePoints,
row.titreTypeId,
etape.perimetre.value.geojsonOrigineGeoSystemeId,
etape.perimetre.value.geojsonOrigineForages
)
} catch (error) {
if (error instanceof Error) {
return [error.message]
} else if (typeof error === 'string') {
return [error]
} else {
return ['Périmètre invalide (raison inconnue)']
}
}
return titreEtapeUpdationValidate(
etape,
demarche,
demarche.titre,
[],
[],
[],
perimetreInfos.sdomZones,
perimetreInfos.communes.map(({ id }) => id),
userSuper,
null,
null
)
},
catch: error => ({
message: "Échec de validation d'une étape" as const,
extra: {
etapeId: row.id,
error: error instanceof Error ? error.message : error,
},
}),
})
})
}
const pipeline: Effect.Effect<void, PipelineError, never> = Effect.gen(function* () {
const rows = yield* getAllEtapeIds()
const invalidEtapes = yield* filterInvalidEtapes(rows)
if (invalidEtapes.length > 0) {
yield* Effect.fail({
message: 'Certaines étapes ont des périmètres invalides' as const,
extra: invalidEtapes.map(({ id, errors }) => `${id} : ${errors.join(', ')}`).join('\n'),
})
}
})
try {
await Effect.runPromise(pipeline)
console.info('Script terminé : aucune erreur détectée')
process.exit()
} catch (error) {
process.exit(1)
}
Loading