diff --git a/backend/src/routes/gedcom.ts b/backend/src/routes/gedcom.ts index 036a92e..7fd8c37 100644 --- a/backend/src/routes/gedcom.ts +++ b/backend/src/routes/gedcom.ts @@ -37,6 +37,22 @@ function clip(value: string | null, max: number): string | null { return trimmed ? trimmed.slice(0, max) : null } +// Lieu d'un GEDCOM : les logiciels remplissent toutes les colonnes de leur format de lieu, même vides, et y +// glissent des codes. Heredis écrit « La Roche-sur-Yon,85191,Vendée,Pays de la Loire,France, » (ville, code +// INSEE, département, région, pays, subdivision). On garde les noms, sans case vide, sans code INSEE ni +// postal, sans répétition : « La Roche-sur-Yon, Vendée, Pays de la Loire, France ». +export function cleanGedcomPlace(value: string | null | undefined): string | null { + if (!value) return null + const parts: string[] = [] + for (const raw of value.split(',')) { + const part = raw.trim() + if (!part || /^(\d{4,5}|2[AB]\d{3})$/i.test(part)) continue + if (parts.length && parts[parts.length - 1].toLowerCase() === part.toLowerCase()) continue + parts.push(part) + } + return parts.length ? parts.join(', ') : null +} + // Export : un champ sur une ligne ne doit jamais en créer une autre (enregistrement injecté) function oneLine(value: string | null | undefined): string { return String(value ?? '').replace(/[\r\n]+/g, ' ') @@ -527,7 +543,7 @@ export const gedcomRoutes: FastifyPluginAsync = async (app) => { const birtNode = findChild(indi, 'BIRT') const birthDateIso = gedcomDateToIso(findChild(birtNode, 'DATE')?.value ?? '') - const birthPlace = clip(findChild(birtNode, 'PLAC')?.value ?? null, 200) + const birthPlace = clip(cleanGedcomPlace(findChild(birtNode, 'PLAC')?.value), 200) const deatNode = findChild(indi, 'DEAT') const deathDateIso = gedcomDateToIso(findChild(deatNode, 'DATE')?.value ?? '') diff --git a/backend/test/gedcom-place.test.ts b/backend/test/gedcom-place.test.ts new file mode 100644 index 0000000..d82b0bc --- /dev/null +++ b/backend/test/gedcom-place.test.ts @@ -0,0 +1,26 @@ +import { describe, expect, it } from 'vitest' + +import { cleanGedcomPlace } from '../src/routes/gedcom' + +describe('lieu importé d’un GEDCOM', () => { + it('Heredis : colonnes vides et code INSEE retirés', () => { + expect(cleanGedcomPlace('La Roche-sur-Yon,85191,Vendée,Pays de la Loire,France,')).toBe('La Roche-sur-Yon, Vendée, Pays de la Loire, France') + expect(cleanGedcomPlace('Ménéac,56129,,,,')).toBe('Ménéac') + expect(cleanGedcomPlace('Ajaccio,2A004,Corse-du-Sud,Corse,France,')).toBe('Ajaccio, Corse-du-Sud, Corse, France') + }) + + it('Gramps, Geneanet : un lieu déjà propre ne change pas', () => { + expect(cleanGedcomPlace('Lille, Nord, Hauts-de-France, France')).toBe('Lille, Nord, Hauts-de-France, France') + expect(cleanGedcomPlace('Paris')).toBe('Paris') + }) + + it('code postal et répétitions écartés', () => { + expect(cleanGedcomPlace('Caen, 14000, Calvados, Calvados, France')).toBe('Caen, Calvados, France') + }) + + it('rien de lisible : pas de lieu', () => { + expect(cleanGedcomPlace(',,,,')).toBeNull() + expect(cleanGedcomPlace('')).toBeNull() + expect(cleanGedcomPlace(undefined)).toBeNull() + }) +})