From 2868aad36923810d3e17c6fce0f4578b76d76122 Mon Sep 17 00:00:00 2001 From: Gulum Date: Mon, 22 Jun 2026 15:02:19 +0200 Subject: [PATCH] feat(stammbaum): gleiches Geburtsdatum der Eltern als Geschwister-Indiz werten MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ein identisches Geburtsdatum bei Vater und Mutter ist ein starkes Indiz für eine Vollgeschwister-Verpaarung — ein gemeinsamer Wurf ist praktisch der einzige Grund dafür, und die Eltern-Würfe sind in den importierten Daten nicht immer verknüpft (gleiche litterId fehlt dann). Die Geschwister-Erkennung (Diagramm-Zusammenführung in build.ts + Akte-Chip in GerbilDetailPage) greift jetzt bei gleicher litterId ODER gleichem, gesetztem Geburtsdatum beider Eltern (distinkte Tiere vorausgesetzt). Tests: gleiches DOB bei fehlender/verschiedener litterId → erkannt; unterschied- liches DOB → nicht erkannt. Co-Authored-By: Claude Opus 4.8 --- .../src/pages/GerbilDetailPage.tsx | 10 ++++--- .../src/pedigree/__tests__/build.test.ts | 28 +++++++++++++++++++ gerbil-manager-web/src/pedigree/build.ts | 14 ++++++---- 3 files changed, 43 insertions(+), 9 deletions(-) diff --git a/gerbil-manager-web/src/pages/GerbilDetailPage.tsx b/gerbil-manager-web/src/pages/GerbilDetailPage.tsx index ad7511e..e554e12 100644 --- a/gerbil-manager-web/src/pages/GerbilDetailPage.tsx +++ b/gerbil-manager-web/src/pages/GerbilDetailPage.tsx @@ -137,14 +137,16 @@ export default function GerbilDetailPage() { const lookup = (map: Map, key: string | null) => (key ? (map.get(key) ?? '—') : '—') const storedColorName = g.colorVarietyId ? (colorName.get(g.colorVarietyId) ?? null) : null - // Geschwisterverpaarung: Vater und Mutter dieses Tiers stammen aus demselben - // Wurf (Vollgeschwister) — erkennbar an gemeinsamer litterId der Eltern. + // Geschwisterverpaarung: Vater und Mutter dieses Tiers sind Vollgeschwister — + // erkennbar an gemeinsamer litterId ODER gleichem Geburtsdatum (starkes Indiz: + // ein identisches Datum entsteht praktisch nur durch einen gemeinsamen Wurf, + // und die Eltern-Würfe sind in den Daten nicht immer verknüpft). const isSiblingPairing = !!father.data && !!mother.data && father.data.id !== mother.data.id && - !!father.data.litterId && - father.data.litterId === mother.data.litterId + ((!!father.data.litterId && father.data.litterId === mother.data.litterId) || + (!!father.data.dateOfBirth && father.data.dateOfBirth === mother.data.dateOfBirth)) const parentLink = ( pid: string | null, diff --git a/gerbil-manager-web/src/pedigree/__tests__/build.test.ts b/gerbil-manager-web/src/pedigree/__tests__/build.test.ts index baef796..aabc9ba 100644 --- a/gerbil-manager-web/src/pedigree/__tests__/build.test.ts +++ b/gerbil-manager-web/src/pedigree/__tests__/build.test.ts @@ -208,6 +208,34 @@ describe('buildPedigree', () => { expect(animal(mutter.mother).gerbil.name).toBe('Oma') }) + it('erkennt Geschwister-Verpaarung am gleichen Geburtsdatum (litterId fehlt/verschieden)', async () => { + // Vater und Mutter haben verschiedene litterId, aber dasselbe Geburtsdatum + // (starkes Indiz: gemeinsamer Wurf, der in den Daten nicht verknüpft ist). + const source = makeSource( + [ + makeGerbil('kind', 'Kind', 'w-kind'), + { ...makeGerbil('vater', 'Vater', 'w-v'), dateOfBirth: '2018-09-22' }, + { ...makeGerbil('mutter', 'Mutter', 'w-m'), dateOfBirth: '2018-09-22' }, + ], + [makeLitter('w-kind', 'vater', 'mutter')], + ) + const root = await buildPedigree(source, 'kind') + expect(animal(root!.mother).siblingPairing).toEqual({ siblingName: 'Vater', siblingPath: 'v' }) + }) + + it('gleiches Geburtsdatum nur bei BEIDEN Eltern (unterschiedliches DOB → keine Verpaarung)', async () => { + const source = makeSource( + [ + makeGerbil('kind', 'Kind', 'w-kind'), + { ...makeGerbil('vater', 'Vater', 'w-v'), dateOfBirth: '2018-09-22' }, + { ...makeGerbil('mutter', 'Mutter', 'w-m'), dateOfBirth: '2019-05-15' }, + ], + [makeLitter('w-kind', 'vater', 'mutter')], + ) + const root = await buildPedigree(source, 'kind') + expect(animal(root!.mother).siblingPairing).toBeUndefined() + }) + it('verlangt für Geschwister-Verpaarung dieselbe litterId (Halbgeschwister zählen nicht)', async () => { // Gemeinsamer Vater (Opa), aber verschiedene Würfe → keine Verpaarung-Marke. const source = makeSource( diff --git a/gerbil-manager-web/src/pedigree/build.ts b/gerbil-manager-web/src/pedigree/build.ts index bc6c91e..824c6db 100644 --- a/gerbil-manager-web/src/pedigree/build.ts +++ b/gerbil-manager-web/src/pedigree/build.ts @@ -71,16 +71,20 @@ async function buildAnimal( parentNode(source, litter?.motherId, path + 'm', generationsLeft - 1, nextAncestors), ]) - // Geschwister-Verpaarung: Stammen Vater und Mutter aus DEMSELBEN Wurf - // (gleiche litterId), sind sie Vollgeschwister. Die Vorfahren-Linie ist dann - // auf beiden Seiten identisch — wir markieren die Mutter, damit das Diagramm + // Geschwister-Verpaarung: Vater und Mutter sind Vollgeschwister, wenn sie aus + // DEMSELBEN Wurf stammen (gleiche litterId) ODER am selben Tag geboren sind + // (gleiches Geburtsdatum ist ein starkes Indiz — Würfe sind praktisch der + // einzige Grund für ein identisches Datum, und die Daten verknüpfen die + // Eltern-Würfe nicht immer). Wir markieren dann die Mutter, damit das Diagramm // ihren Ast zu einem Verweis auf die Vaterlinie zusammenführt. Die echten // Vorfahren bleiben im Modell erhalten (Druck-Ahnentafel nutzt sie weiter). if ( father.kind === 'animal' && mother.kind === 'animal' && - father.gerbil.litterId && - father.gerbil.litterId === mother.gerbil.litterId + father.gerbil.id !== mother.gerbil.id && + ((father.gerbil.litterId && father.gerbil.litterId === mother.gerbil.litterId) || + (father.gerbil.dateOfBirth != null && + father.gerbil.dateOfBirth === mother.gerbil.dateOfBirth)) ) { const markedMother: AnimalNode = { ...mother,