Skip to content

Commit

Permalink
Added ban ids generate mechanism on assemblage
Browse files Browse the repository at this point in the history
  • Loading branch information
antoineludeau committed Jul 19, 2024
1 parent 013cf56 commit eda3481
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 96 deletions.
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,4 @@ DISTRICT_TO_SNAPSHOT= # Comma separated list of district to snapshot (used only
MATOMO_URL=
MATOMO_SITE_ID=
MATOMO_TOKEN_AUTH=
IS_GENERATE_BANID_ON_ASSEMBLY= # Set to true to generate banId on assembly
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ services:
- MATOMO_TOKEN_AUTH=${MATOMO_TOKEN_AUTH}
- FORCE_DOWNLOAD_CONTOUR=
- FORCE_DOWNLOAD_DATASETS=
- IS_GENERATE_BANID_ON_ASSEMBLY=${IS_GENERATE_BANID_ON_ASSEMBLY}
ports:
- "${PORT:-5000}:5000"
volumes:
Expand Down
205 changes: 110 additions & 95 deletions lib/compose/index.cjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable complexity */
const {chain, omit, pick, keyBy} = require('lodash')
const bluebird = require('bluebird')
const {v4: uuidv4} = require('uuid')

const {getCommune: getCommuneCOG, getRegion, getDepartement} = require('../util/cog.cjs')

Expand All @@ -26,6 +27,8 @@ const communesLocauxAdresses = require(`../../${communesLocauxAdressesDataPath}`

const locauxAdressesIndex = keyBy(communesLocauxAdresses, 'codeCommune')

const IS_GENERATE_BANID_ON_ASSEMBLY = process.env.IS_GENERATE_BANID_ON_ASSEMBLY === 'true'

async function getSourceData(sourceName, codeCommune) {
const adresses = await source(sourceName).getAdresses(codeCommune)
const prepareData = require(`./sources/${sourceName}.cjs`)
Expand All @@ -51,123 +54,135 @@ async function getBalData(codeCommune, revision) {
}

async function composeCommune(codeCommune, ignoreIdConfig) {
const communeCOG = getCommuneCOG(codeCommune)
const commune = await getCommune(codeCommune)

if (!ignoreIdConfig && commune?.withBanId) {
console.info(`La commune ${codeCommune} est gérée avec Ban ID => composition ignorée`)
return false
}

const compositionOptions = commune?.compositionOptions || {}

if (!communeCOG) {
throw new Error(`La commune ${codeCommune} n’existe pas.`)
}
try {
const communeCOG = getCommuneCOG(codeCommune)
const commune = await getCommune(codeCommune)

const currentRevision = await getCurrentRevision(codeCommune)
if (!ignoreIdConfig && commune?.withBanId) {
console.info(`La commune ${codeCommune} est gérée avec Ban ID => composition ignorée`)
return false
}

if (!compositionOptions.force && currentRevision && commune?.idRevision === currentRevision?._id) {
console.log(`${codeCommune} | révision source inchangée => composition ignorée`)
return false
}
const compositionOptions = commune?.compositionOptions || {}

const isBAL = Boolean(currentRevision)
if (!communeCOG) {
throw new Error(`La commune ${codeCommune} n’existe pas.`)
}

// La bloc suivant garantit qu'il n'y a pas de retour en arrière.
// Ne sera plus nécessaire quand l'API de dépôt contiendra 100% des BAL contenues dans la BAN
if (!isBAL && commune?.typeComposition === 'bal') {
console.log(`${codeCommune} | passage de 'bal' à 'assemblage' interdit => composition ignorée`)
return false
}
const currentRevision = await getCurrentRevision(codeCommune)

const balData = isBAL && await getBalData(codeCommune, currentRevision)
const multiSourcesData = !isBAL && await getMultiSourcesData(codeCommune)
if (!compositionOptions.force && currentRevision && commune?.idRevision === currentRevision?._id) {
console.log(`${codeCommune} | révision source inchangée => composition ignorée`)
return false
}

const pseudoCodeVoieGenerator = await createPseudoCodeVoieGenerator(codeCommune)
const isBAL = Boolean(currentRevision)

const composeVoiesOptions = {
codeCommune,
pseudoCodeVoieGenerator,
forceCertification: commune?.forceCertification
}
// La bloc suivant garantit qu'il n'y a pas de retour en arrière.
// Ne sera plus nécessaire quand l'API de dépôt contiendra 100% des BAL contenues dans la BAN
if (!isBAL && commune?.typeComposition === 'bal') {
console.log(`${codeCommune} | passage de 'bal' à 'assemblage' interdit => composition ignorée`)
return false
}

const composedVoies = isBAL
? await BAL.buildVoies(balData, composeVoiesOptions)
: await MS.buildVoies(multiSourcesData, composeVoiesOptions)
const balData = isBAL && await getBalData(codeCommune, currentRevision)
const multiSourcesData = !isBAL && await getMultiSourcesData(codeCommune)

const voies = composedVoies.map(v => ({
type: 'voie',
...omit(v, 'numeros')
}))
const pseudoCodeVoieGenerator = await createPseudoCodeVoieGenerator(codeCommune)

const numeros = chain(composedVoies)
.map(v => v.numeros.map(n => ({
const composeVoiesOptions = {
codeCommune,
idVoie: v.idVoie,
...n
})))
.flatten()
.value()
pseudoCodeVoieGenerator,
forceCertification: commune?.forceCertification
}

const existingVoiesIds = new Set(chain(composedVoies).map('idVoie').uniq().value())
const buildLieuxDitsOptions = {codeCommune, pseudoCodeVoieGenerator, existingVoiesIds}
const composedVoies = isBAL
? await BAL.buildVoies(balData, composeVoiesOptions)
: await MS.buildVoies(multiSourcesData, composeVoiesOptions)

const lieuxDits = isBAL
? await BAL.buildLieuxDits(balData, buildLieuxDitsOptions)
: await MS.buildLieuxDits(multiSourcesData, buildLieuxDitsOptions)
const voies = composedVoies.map(v => ({
type: 'voie',
...omit(v, 'numeros')
}))

const voiesToPersist = [...voies, ...lieuxDits]
const existingVoiesIds = new Set(chain(composedVoies).map('idVoie').uniq().value())
const buildLieuxDitsOptions = {codeCommune, pseudoCodeVoieGenerator, existingVoiesIds}

const nbNumeros = voies.reduce((acc, voie) => acc + voie.nbNumeros, 0)
const nbNumerosCertifies = numeros.filter(n => n.certifie).length
const lieuxDits = isBAL
? await BAL.buildLieuxDits(balData, buildLieuxDitsOptions)
: await MS.buildLieuxDits(multiSourcesData, buildLieuxDitsOptions)

let districtID
if (isBAL) {
const balAddressVersion = getBalAddressVersion(balData.adresses[0])
districtID = digestIDsFromBalAddress(balData.adresses[0], balAddressVersion)?.districtID
}

if (!geo[codeCommune]) {
console.warn(`La commune ${codeCommune} n'a pas de données géographiques associées dans le fichier geo.json`)
}
let districtID
if (isBAL) {
const balAddressVersion = getBalAddressVersion(balData.adresses[0])
districtID = digestIDsFromBalAddress(balData.adresses[0], balAddressVersion)?.districtID
} else {
districtID = IS_GENERATE_BANID_ON_ASSEMBLY ? uuidv4() : undefined
}

const communeRecord = {
banId: districtID,
nomCommune: communeCOG.nom,
population: communeCOG.population,
departement: pick(getDepartement(communeCOG.departement), 'nom', 'code'),
region: pick(getRegion(communeCOG.region), 'nom', 'code'),
codesPostaux: communeCOG.codesPostaux || [],
displayBBox: geo[codeCommune]?.bbox,
typeCommune: communeCOG.type,
nbNumeros,
nbNumerosCertifies,
nbVoies: voies.length,
nbLieuxDits: lieuxDits.length,
typeComposition: isBAL ? 'bal' : 'assemblage',
idRevision: currentRevision?._id,
dateRevision: currentRevision?.publishedAt,
withBanId: false
}
const voiesToPersist = !isBAL && IS_GENERATE_BANID_ON_ASSEMBLY
? [...voies.map(v => ({banIdDistrict: districtID, ...v})), ...lieuxDits.map(ld => ({banIdDistrict: districtID, ...ld}))]
: [...voies, ...lieuxDits]

const numeros = chain(composedVoies)
.map(v => v.numeros.map(n => ({
codeCommune,
idVoie: v.idVoie,
...(!isBAL && IS_GENERATE_BANID_ON_ASSEMBLY ? {banId: uuidv4()} : {}),
...(!isBAL && IS_GENERATE_BANID_ON_ASSEMBLY ? {banIdMainCommonToponym: v.banId,} : {}),
...(!isBAL && IS_GENERATE_BANID_ON_ASSEMBLY ? {banIdDistrict: districtID} : {}),
...n
})))
.flatten()
.value()

const nbNumeros = voies.reduce((acc, voie) => acc + voie.nbNumeros, 0)
const nbNumerosCertifies = numeros.filter(n => n.certifie).length

if (!geo[codeCommune]) {
console.warn(`La commune ${codeCommune} n'a pas de données géographiques associées dans le fichier geo.json`)
}

if (codeCommune in locauxAdressesIndex) {
const nbAdressesAttendues = locauxAdressesIndex[codeCommune].nbAdressesLocaux
const ratio = Math.round((nbNumeros / nbAdressesAttendues) * 100)
const deficitAdresses = (communeCOG.population < 2000 && communeCOG.population > 0)
? ratio < 50 : undefined
const communeRecord = {
banId: districtID,
nomCommune: communeCOG.nom,
population: communeCOG.population,
departement: pick(getDepartement(communeCOG.departement), 'nom', 'code'),
region: pick(getRegion(communeCOG.region), 'nom', 'code'),
codesPostaux: communeCOG.codesPostaux || [],
displayBBox: geo[codeCommune]?.bbox,
typeCommune: communeCOG.type,
nbNumeros,
nbNumerosCertifies,
nbVoies: voies.length,
nbLieuxDits: lieuxDits.length,
typeComposition: isBAL ? 'bal' : 'assemblage',
idRevision: currentRevision?._id,
dateRevision: currentRevision?.publishedAt,
withBanId: false
}

communeRecord.analyseAdressage = {
nbAdressesAttendues,
ratio,
deficitAdresses
if (codeCommune in locauxAdressesIndex) {
const nbAdressesAttendues = locauxAdressesIndex[codeCommune].nbAdressesLocaux
const ratio = Math.round((nbNumeros / nbAdressesAttendues) * 100)
const deficitAdresses = (communeCOG.population < 2000 && communeCOG.population > 0)
? ratio < 50 : undefined

communeRecord.analyseAdressage = {
nbAdressesAttendues,
ratio,
deficitAdresses
}
}
}

await pseudoCodeVoieGenerator.save()
await pseudoCodeVoieGenerator.save()

await saveCommuneData(codeCommune, {commune: communeRecord, voies: voiesToPersist, numeros})
return true
await saveCommuneData(codeCommune, {commune: communeRecord, voies: voiesToPersist, numeros})
return true
} catch (error) {
console.error(`Erreur lors de la composition de la commune ${codeCommune}: ${error.message}`)
return false
}
}

module.exports = composeCommune
11 changes: 10 additions & 1 deletion lib/compose/strategies/multi-sources/lieux-dits.cjs
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
const {v4: uuidv4} = require('uuid')

const IS_GENERATE_BANID_ON_ASSEMBLY = process.env.IS_GENERATE_BANID_ON_ASSEMBLY === 'true'

async function buildLieuxDits(sourcesData, {existingVoiesIds}) {
const cadastreData = sourcesData.find(d => d.source === 'cadastre')

if (cadastreData) {
return cadastreData.lieuxDits.filter(ld => !existingVoiesIds.has(ld.idVoie))
const lieuxDitsData = cadastreData.lieuxDits.filter(ld => !existingVoiesIds.has(ld.idVoie))
const banId = IS_GENERATE_BANID_ON_ASSEMBLY ? uuidv4() : undefined
return lieuxDitsData.map(ld => ({
banId,
...ld
}))
}

return []
Expand Down
6 changes: 6 additions & 0 deletions lib/compose/strategies/multi-sources/voies.cjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const {chain, first, flatten} = require('lodash')
const {feature} = require('@turf/turf')
const {v4: uuidv4} = require('uuid')
const {rewriteSuffixes} = require('@etalab/adresses-util/lib/numeros')

const {computeBufferedBbox, getCenterFromPoints, derivePositionProps} = require('../../../util/geo.cjs')
Expand All @@ -11,6 +12,8 @@ const computeGroups = require('../../processors/compute-groups.cjs')

const {buildNumero} = require('./numero.cjs')

const IS_GENERATE_BANID_ON_ASSEMBLY = process.env.IS_GENERATE_BANID_ON_ASSEMBLY === 'true'

function normalizeSuffixeKey(suffixe) {
if (!suffixe) {
return ''
Expand Down Expand Up @@ -113,7 +116,10 @@ function buildVoies(multiSourcesData, {codeCommune, pseudoCodeVoieGenerator}) {
const centroid = getCenterFromPoints(positions)
const {lon, lat, x, y, tiles} = derivePositionProps(centroid, 10, 14)

const banId = IS_GENERATE_BANID_ON_ASSEMBLY ? uuidv4() : undefined

return {
banId,
groupId,
idVoie: idVoieFantoir,
idVoieFantoir,
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
"pumpify": "^2.0.1",
"rev-hash": "^3.0.0",
"sequelize": "^6.31.1",
"uuid": "^10.0.0",
"vt-pbf": "^3.1.3",
"worker-farm": "^1.7.0",
"yup": "^1.0.2"
Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8416,6 +8416,11 @@ [email protected], uuid@^8.3.0, uuid@^8.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==

uuid@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294"
integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==

uuidv4@^6.2.13:
version "6.2.13"
resolved "https://registry.yarnpkg.com/uuidv4/-/uuidv4-6.2.13.tgz#8f95ec5ef22d1f92c8e5d4c70b735d1c89572cb7"
Expand Down

0 comments on commit eda3481

Please sign in to comment.