feat(provenance): Datenherkunft als chronologischer, datei-attribuierter Verlauf
Die Herkunft liest sich jetzt wie eine History: pro Feld wird genannt, aus welcher Quelldatei der Wert kam, plus Merge-/Entscheidungs-/Wurfchronik-Schritte. Beispiel: „In ‚X.xlsx' gefunden." → „Geburtsdatum (…) aus ‚X.xlsx'." → „Auch in ‚Y.docx' gefunden → zusammengeführt." → „Eltern über Position erkannt (Quelle …)." Import: build_entity_provenance() um history[] erweitert; ein field_source- Tracking im Gerbil-Dedup hält fest, welcher Datensatz/welche Datei jedes Feld (DOB/Genotyp/Geschlecht/Farbschlag/Sterbedatum) lieferte — auch über Empty-Fill und Mehrheitsentscheid hinweg. Kontakte und Würfe erhalten analoge, datei- attribuierte Verläufe. Kein DB-Migration nötig (history liegt im Provenance-JSON). Frontend: ProvenanceDialog rendert den Verlauf als nummerierte Timeline. Tests: history wird erzeugt, nennt Dateien, überlebt Ingest (dotnet 212, vitest 129, playwright 17, python merge/extract grün). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -130,6 +130,14 @@ export function seedDb(): MockDb {
|
||||
parentMethod: 'chart-position',
|
||||
parentConfidence: 'medium',
|
||||
notes: ['aus 2 Datensätzen zusammengeführt'],
|
||||
history: [
|
||||
'In „Stammbaum von Krümel.xlsx“ gefunden.',
|
||||
'Geburtsdatum (12.03.2025) aus „Stammbaum von Krümel.xlsx“.',
|
||||
'Genotyp aus „Stammbaum von Krümel.xlsx“.',
|
||||
'Auch in „Wurfchronik-Detail.docx“ gefunden → Datensätze zusammengeführt.',
|
||||
'Eltern über Position im Stammbaum erkannt (Quelle: „Stammbaum von Krümel.xlsx“).',
|
||||
'Angaben aus der Wurfchronik übernommen.',
|
||||
],
|
||||
}),
|
||||
},
|
||||
{ ...gerbil('fridolin', 'Fridolin', 'male', '2023-05-01', 'w-fridolin', 'cv-schwarz', 'aa CC DD EE GG PP spsp rere'), enclosureId: 'enc-gross', originBreeder: 'Zoohandlung Meier', profilePhotoUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==' },
|
||||
@@ -203,6 +211,11 @@ export function seedDb(): MockDb {
|
||||
mergedRecordCount: 2,
|
||||
fromWurfchronik: true,
|
||||
notes: ['aus Wurfchronik', 'aus 2 Datensätzen zusammengeführt', 'Geschwister-Würfe zusammengeführt'],
|
||||
history: [
|
||||
'Wurf aus Wurfchronik „Wurfchronik Teil 1_page_0009.md“.',
|
||||
'Auch in „Wurfchronik Teil 1_page_0028.md“ gefunden → Datensätze zusammengeführt.',
|
||||
'Geschwister-Würfe zusammengeführt.',
|
||||
],
|
||||
}),
|
||||
},
|
||||
{ id: 'w-fridolin', name: 'Wurf F', date: '2023-05-01', totalBorn: 4, expectedGoHomeDate: null, notes: null, fatherId: 'balu', motherId: 'maja' },
|
||||
@@ -229,6 +242,11 @@ export function seedDb(): MockDb {
|
||||
mergedRecordCount: 2,
|
||||
fromWurfchronik: true,
|
||||
notes: ['aus 2 Datensätzen zusammengeführt', 'als Züchter erkannt', 'als Abnehmer erkannt'],
|
||||
history: [
|
||||
'In „Stammbaum von Krümel.xlsx“ als Züchter erkannt.',
|
||||
'Auch in „Wurfchronik Teil 1_page_0001.md“ gefunden → Datensätze zusammengeführt.',
|
||||
'Sowohl als Züchter als auch als Abnehmer geführt.',
|
||||
],
|
||||
}),
|
||||
},
|
||||
{ id: 'con-huber', name: 'Familie Huber', email: null, phone: '0151 2345678', address: null, notes: null, isBreeder: false, isReceiver: true },
|
||||
|
||||
@@ -13,6 +13,14 @@ test('Tierakte: "Datenherkunft"-Button öffnet den Nachverfolgungs-Dialog', asyn
|
||||
const dialog = page.getByRole('dialog', { name: p.dialogTitle })
|
||||
await expect(dialog).toBeVisible()
|
||||
|
||||
// VERLAUF: chronologischer, dateibezogener Verlauf als Primärinhalt.
|
||||
await expect(dialog).toContainText(p.historyTitle)
|
||||
await expect(dialog).toContainText('In „Stammbaum von Krümel.xlsx“ gefunden.')
|
||||
await expect(dialog).toContainText('Geburtsdatum (12.03.2025) aus „Stammbaum von Krümel.xlsx“.')
|
||||
await expect(dialog).toContainText(
|
||||
'Auch in „Wurfchronik-Detail.docx“ gefunden → Datensätze zusammengeführt.',
|
||||
)
|
||||
|
||||
// Quelldateien werden angezeigt.
|
||||
await expect(dialog).toContainText(p.sourceFilesTitle)
|
||||
await expect(dialog).toContainText('Stammbaum von Krümel.xlsx')
|
||||
@@ -53,6 +61,10 @@ test('Kontakt: "Datenherkunft"-Button öffnet den Nachverfolgungs-Dialog', async
|
||||
const dialog = page.getByRole('dialog', { name: p.dialogTitle })
|
||||
await expect(dialog).toBeVisible()
|
||||
|
||||
// VERLAUF: dateibezogene Schritte des Kontakts.
|
||||
await expect(dialog).toContainText(p.historyTitle)
|
||||
await expect(dialog).toContainText('In „Stammbaum von Krümel.xlsx“ als Züchter erkannt.')
|
||||
|
||||
// Quelldateien + Zusammenführung + Kontakt-Rolle-Hinweise.
|
||||
await expect(dialog).toContainText(p.sourceFilesTitle)
|
||||
await expect(dialog).toContainText('Wurfchronik Teil 1_page_0001.md')
|
||||
@@ -73,6 +85,10 @@ test('Wurf: "Datenherkunft"-Button öffnet den Nachverfolgungs-Dialog', async ({
|
||||
const dialog = page.getByRole('dialog', { name: p.dialogTitle })
|
||||
await expect(dialog).toBeVisible()
|
||||
|
||||
// VERLAUF: dateibezogene Schritte des Wurfs.
|
||||
await expect(dialog).toContainText(p.historyTitle)
|
||||
await expect(dialog).toContainText('Wurf aus Wurfchronik „Wurfchronik Teil 1_page_0009.md“.')
|
||||
|
||||
await expect(dialog).toContainText(p.sourceFilesTitle)
|
||||
await expect(dialog).toContainText('Wurfchronik Teil 1_page_0009.md')
|
||||
await expect(dialog).toContainText(p.mergedCount(2))
|
||||
|
||||
Reference in New Issue
Block a user