UNPKG

@gouvfr-anct/mednum

Version:

✨ Permet de transformer une source de données vers le schéma des lieux de mediation numerique

71 lines (70 loc) 5.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.findDuplicates = void 0; const lieux_de_mediation_numerique_1 = require("@gouvfr-anct/lieux-de-mediation-numerique"); const fuzzball_1 = require("fuzzball"); const sameSource = (lieu, lieuToDeduplicate) => lieu.source === lieuToDeduplicate.source; const sameId = (lieu, lieuToDeduplicate) => lieu.id === lieuToDeduplicate.id; const sameCodeInsee = (lieu, lieuToDeduplicate) => lieu.code_insee === lieuToDeduplicate.code_insee; const compatibleTypologies = [[lieux_de_mediation_numerique_1.Typologie.RFS, lieux_de_mediation_numerique_1.Typologie.PIMMS]]; const toTypologies = ({ typologie }) => (0, lieux_de_mediation_numerique_1.Typologies)(typologie?.split('|') ?? []); const allEmpty = (lieuTypologies, lieuToDeduplicateTypologies) => lieuTypologies.length === 0 && lieuToDeduplicateTypologies.length === 0; const hasSameTypologieIn = (lieuToDeduplicateTypologies) => (typologie) => lieuToDeduplicateTypologies.includes(typologie); const hasCompatibleTypologiesFor = (lieuTypologies, lieuToDeduplicateTypologies) => ([typologyA, typologyB]) => (lieuTypologies.includes(typologyA) && lieuToDeduplicateTypologies.includes(typologyB)) || (lieuTypologies.includes(typologyB) && lieuToDeduplicateTypologies.includes(typologyA)); const sameTypologie = (lieuTypologies, lieuToDeduplicateTypologies) => allEmpty(lieuTypologies, lieuToDeduplicateTypologies) || lieuTypologies.some(hasSameTypologieIn(lieuToDeduplicateTypologies)) || compatibleTypologies.some(hasCompatibleTypologiesFor(lieuTypologies, lieuToDeduplicateTypologies)); const onlyPotentialDuplicates = (lieuToDeduplicate, allowInternalMerge) => (lieu) => sameCodeInsee(lieu, lieuToDeduplicate) && !sameId(lieu, lieuToDeduplicate) && (allowInternalMerge || !sameSource(lieu, lieuToDeduplicate)) && sameTypologie(toTypologies(lieu), toTypologies(lieuToDeduplicate)); const MINIMAL_CARTESIAN_DISTANCE = 0.0004; const computeDistanceScore = (cartesianDistance) => 100 / (cartesianDistance * 1000 + 1); const distanceScore = (cartesianDistance) => Math.trunc(cartesianDistance < MINIMAL_CARTESIAN_DISTANCE ? 100 : computeDistanceScore(cartesianDistance)); const hasDefinedCoordinates = (lieu) => lieu.latitude != null && lieu.longitude != null; const pythagore = (x1, x2, y1, y2) => Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2); const cartesianDistanceBetween = (lieu, cible) => hasDefinedCoordinates(lieu) && hasDefinedCoordinates(cible) ? pythagore(lieu.latitude, cible.latitude, lieu.longitude, cible.longitude) : NaN; const duplicatesWithScores = (lieux) => (lieuToDeduplicate, allowInternalMerge) => lieux.filter(onlyPotentialDuplicates(lieuToDeduplicate, allowInternalMerge)).map((lieu) => ({ id: lieu.id, distanceScore: distanceScore(cartesianDistanceBetween(lieu, lieuToDeduplicate)), nomFuzzyScore: (0, fuzzball_1.ratio)(lieu.nom, lieuToDeduplicate.nom), voieFuzzyScore: (0, fuzzball_1.ratio)(lieu.adresse, lieuToDeduplicate.adresse) })); const toLieuDuplications = (lieu, lieux, allowInternalMerge) => ({ id: lieu.id, duplicates: duplicatesWithScores(lieux)(lieu, allowInternalMerge) }); const appendCommuneDuplications = (lieux) => (lieuToDeduplicate, duplications, allowInternalMerge) => lieuToDeduplicate.code_insee != null ? [ ...duplications, { code_insee: lieuToDeduplicate.code_insee, lieux: [toLieuDuplications(lieuToDeduplicate, lieux, allowInternalMerge)] } ] : duplications; const toUpdatedCommuneDuplications = (lieux) => (lieu, duplicationsWithSameCodeInsee, allowInternalMerge) => (communeDuplications) => communeDuplications.code_insee === lieu.code_insee ? { code_insee: lieu.code_insee, lieux: [...duplicationsWithSameCodeInsee.lieux, toLieuDuplications(lieu, lieux, allowInternalMerge)] } : communeDuplications; const withSameCodeInsee = (lieu) => ({ code_insee }) => code_insee === lieu.code_insee; const toCommunesDuplications = (lieux, allowInternalMerge) => (duplications, lieuToDeduplicate) => ((duplicationsWithSameCodeInsee) => duplicationsWithSameCodeInsee == null ? appendCommuneDuplications(lieux)(lieuToDeduplicate, duplications, allowInternalMerge) : duplications.map(toUpdatedCommuneDuplications(lieux)(lieuToDeduplicate, duplicationsWithSameCodeInsee, allowInternalMerge)))(duplications.find(withSameCodeInsee(lieuToDeduplicate))); const onlyWithDuplicates = ({ duplicates }) => duplicates.length > 0; const onlyWithoutDuplicates = ({ duplicates }) => duplicates.length === 0; const toDuplicatesWithout = (noDuplicatesIds) => ({ id, duplicates }) => ({ id, duplicates: duplicates.filter((duplicate) => !noDuplicatesIds.includes(duplicate.id)) }); const invalidDuplicatesIds = ({ lieux }) => lieux.filter(onlyWithoutDuplicates).map((lieu) => lieu.id); const removeLieuxFrom = ({ code_insee, lieux }, ids) => ({ code_insee, lieux: lieux.map(toDuplicatesWithout(ids)).filter(onlyWithDuplicates) }); const toValidDuplicates = (communeDuplications) => removeLieuxFrom(communeDuplications, invalidDuplicatesIds(communeDuplications)); const onlyWithLieux = (communeDuplications) => communeDuplications.lieux.length > 0; const findDuplicates = (lieux, allowInternalMerge, lieuxToDeduplicate = lieux) => lieuxToDeduplicate.reduce(toCommunesDuplications(lieux, allowInternalMerge), []).map(toValidDuplicates).filter(onlyWithLieux); exports.findDuplicates = findDuplicates;