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:
@@ -88,6 +88,13 @@ export interface GerbilProvenance {
|
||||
parentConfidence?: string
|
||||
/** Menschlich lesbare Herkunftshinweise (deutsch). */
|
||||
notes: string[]
|
||||
/**
|
||||
* Chronologischer, dateibezogener Verlauf (deutsch): liest sich wie ein
|
||||
* Protokoll, WELCHE Quelldatei WELCHE Angabe beigetragen hat (z. B.
|
||||
* „Geburtsdatum (27.03.2022) aus ‚Stammbaum von X.xlsx'."). Primärinhalt des
|
||||
* Datenherkunft-Dialogs.
|
||||
*/
|
||||
history: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,6 +33,7 @@ function parseProvenance(raw?: string | null): EntityProvenance | null {
|
||||
parentMethod: p.parentMethod,
|
||||
parentConfidence: p.parentConfidence,
|
||||
notes: Array.isArray(p.notes) ? p.notes : [],
|
||||
history: Array.isArray(p.history) ? p.history : [],
|
||||
}
|
||||
} catch {
|
||||
return null
|
||||
@@ -88,6 +89,19 @@ export default function ProvenanceDialog({ open, onClose, provenance, entityLabe
|
||||
<>
|
||||
<p className="provenance__intro">{entityLabel ? t.introFor(entityLabel) : t.intro}</p>
|
||||
|
||||
{p.history.length > 0 && (
|
||||
<section className="provenance__section">
|
||||
<div className="provenance__section-title">{t.historyTitle}</div>
|
||||
<ol className="provenance__history">
|
||||
{p.history.map((step, i) => (
|
||||
<li key={`${i}-${step}`} className="provenance__history-step">
|
||||
{step}
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<section className="provenance__section">
|
||||
<div className="provenance__section-title">
|
||||
{t.mergedTitle}
|
||||
|
||||
@@ -149,6 +149,20 @@
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* Chronologischer Verlauf: liest sich wie ein Protokoll der Datenherkunft. */
|
||||
.provenance__history {
|
||||
margin: 0;
|
||||
padding-left: 1.25rem;
|
||||
display: grid;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.provenance__history-step {
|
||||
font-size: 0.88rem;
|
||||
line-height: 1.35;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.provenance__actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
@@ -894,6 +894,8 @@ export const de = {
|
||||
contact: 'dieses Kontakts',
|
||||
litter: 'dieses Wurfs',
|
||||
},
|
||||
/** Überschrift des chronologischen, dateibezogenen Verlaufs (Primärinhalt). */
|
||||
historyTitle: 'Verlauf',
|
||||
/** Überschriften / Feldbeschriftungen. */
|
||||
sourceFilesTitle: 'Quelldateien',
|
||||
sourceFilesCount: (n: number) => (n === 1 ? 'aus 1 Quelle' : `aus ${n} Quellen`),
|
||||
|
||||
Reference in New Issue
Block a user