IMPORT-POLISH: 4 Importer-Fixes nach Re-Import #2
FIX-1 decision-matching: apply_conflict_decisions/apply_dob_remaps nutzen jetzt canon_pair(name)[0] als Match-Key (Dedup-Identitaet: call-name ohne Zucht, v.d.<->von den gefaltet). Workaround-Spelling v.d. in Victoria Welbys Decision bleibt erhalten; beide Formen matchen jetzt. Kommentar im decision-Eintrag aktualisiert. FIX-2 specific-wins: _alleles_compatible aendert '? vs x = False' -> '? vs x = True' (spezifischer Wert gewinnt). C- vs CC, G- vs Gg, P? vs PP sind kein Konflikt mehr. Echte Wert-Widersprueche (DD vs Dd, Ee vs ee, PP vs Pp) bleiben Konflikte. Loest Enya, Ella, Zac automatisch (Konflikte 8->5 erwartet). 2 bestehende Tests angepasst, 7 neue Tests. FIX-3 parent-FK backfill: nach dem Wurfchronik-Rueckverknuepfungs- Block iteriert ImportService.RunAsync ueber bereits importierte Wuerfe mit null Father/MotherId und setzt fehlende FKs wenn das Elterntier jetzt ladbar ist. Trockenlauf zaehlt, Execute schreibt. LitterSummary.ParentFksBackfilled + 2 neue C#-Tests (SQLite). FIX-4 Skarlett-Artefakt: parse_detail() strippt trailing / +YEAR aus dem Genotyp-Tail (re.sub). Sterbejahr bleibt als death-Date erhalten -> Skarlett erscheint als reiner Sterbedatum-Konflikt. 2 neue Python-Tests. Gate: 124/124 C#-Tests, Python test_extract/test_genotype ALL PASS, has-pending-model-changes = No. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -102,6 +102,39 @@ check("apply_conflict_decisions returns resolved count", n == 2)
|
||||
check("missing decisions file tolerated (returns 0)",
|
||||
e.apply_conflict_decisions([], [], os.path.join(tempfile.gettempdir(), "does-not-exist.json")) == 0)
|
||||
|
||||
# FIX-1: decision matching uses canon_pair identity -> 'von den' decision matches 'v.d.' record
|
||||
dec_vd = os.path.join(tempfile.gettempdir(), "decisions-vd.json")
|
||||
_json.dump({"resolutions": [
|
||||
{"name": "Victoria Welby gen. Welby von den Kleinen Chaoten", # written with 'von den'
|
||||
"dob": "16.01.2023", "decision": "E-locus = ee[f]",
|
||||
"genotype": "Aa CC D- ee[f] Gg pp Spsp", "source": "test"},
|
||||
]}, open(dec_vd, "w", encoding="utf-8"))
|
||||
merged_vd = [
|
||||
{"id": "vw", "name": "Victoria Welby gen. Welby v.d. Kleinen Chaoten", # record has 'v.d.'
|
||||
"dob": "16.01.2023", "conflict": True, "farbschlag": "", "death": "",
|
||||
"genotype": {"mapped8locus": {}, "rawGenotype": "", "unmappedTokens": []}},
|
||||
]
|
||||
conflicts_vd = [{"id": "vw"}]
|
||||
n_vd = e.apply_conflict_decisions(merged_vd, conflicts_vd, dec_vd)
|
||||
check("FIX-1: 'von den' decision matches 'v.d.' record (canon_pair identity)", n_vd == 1)
|
||||
check("FIX-1: conflict cleared for v.d. record", merged_vd[0]["conflict"] is False)
|
||||
# Also verify the workaround spelling (v.d. in decision) matches a 'von den' record
|
||||
_json.dump({"resolutions": [
|
||||
{"name": "Victoria Welby gen. Welby v.d. Kleinen Chaoten", # workaround: v.d. in decision
|
||||
"dob": "16.01.2023", "decision": "E-locus = ee[f]",
|
||||
"genotype": "Aa CC D- ee[f] Gg pp Spsp", "source": "test"},
|
||||
]}, open(dec_vd, "w", encoding="utf-8"))
|
||||
merged_vd2 = [
|
||||
{"id": "vw2", "name": "Victoria Welby gen. Welby von den Kleinen Chaoten", # record 'von den'
|
||||
"dob": "16.01.2023", "conflict": True, "farbschlag": "", "death": "",
|
||||
"genotype": {"mapped8locus": {}, "rawGenotype": "", "unmappedTokens": []}},
|
||||
]
|
||||
conflicts_vd2 = [{"id": "vw2"}]
|
||||
n_vd2 = e.apply_conflict_decisions(merged_vd2, conflicts_vd2, dec_vd)
|
||||
check("FIX-1: v.d. decision also matches 'von den' record (both spellings match)", n_vd2 == 1)
|
||||
try: os.remove(dec_vd)
|
||||
except OSError: pass
|
||||
|
||||
# --- correctDob: a wrong-birthdate duplicate is remapped BEFORE dedup so it merges ---
|
||||
dec2 = os.path.join(tempfile.gettempdir(), "decisions-dob.json")
|
||||
_json.dump({"resolutions": [
|
||||
@@ -128,23 +161,52 @@ except OSError: pass
|
||||
try: os.remove(dec_path)
|
||||
except OSError: pass
|
||||
|
||||
# --- "presence wins" conflict rule (Julian) ---
|
||||
# present-vs-absent (whole locus or [f] modifier) is NOT a conflict; differing filled values are.
|
||||
# --- "presence wins" + "specific wins" conflict rules (Julian) ---
|
||||
# present-vs-absent (whole locus or [f] modifier) is NOT a conflict; differing FILLED values are.
|
||||
# FIX-2 (specific-wins): unknown allele '?' vs any specified value is also NOT a conflict —
|
||||
# the specific value wins (C- vs CC -> CC; G- vs Gg -> Gg; P? vs PP -> PP).
|
||||
check("spsp present vs locus absent -> no conflict",
|
||||
not e._genotype_conflict([{"Sp": ["sp", "sp"]}, {}]))
|
||||
check("ee[f] vs ee ([f] modifier present/absent) -> no conflict",
|
||||
not e._genotype_conflict([{"E": ["e", "e^f"]}, {"E": ["e", "e"]}]))
|
||||
check("DD vs D- (unknown vs filled) -> conflict",
|
||||
e._genotype_conflict([{"D": ["D", "D"]}, {"D": ["D", "?"]}]))
|
||||
# FIX-2: '?' vs specified = specific wins (was: contradiction)
|
||||
check("FIX-2: DD vs D- (specific wins: DD wins) -> NOT conflict",
|
||||
not e._genotype_conflict([{"D": ["D", "D"]}, {"D": ["D", "?"]}]))
|
||||
check("FIX-2: C- vs Cc[h] (specific wins: c^h wins) -> NOT conflict",
|
||||
not e._genotype_conflict([{"C": ["C", "?"]}, {"C": ["C", "c^h"]}]))
|
||||
check("FIX-2: C- vs CC (specific wins: CC) -> NOT conflict",
|
||||
not e._genotype_conflict([{"C": ["C", "?"]}, {"C": ["C", "C"]}]))
|
||||
check("FIX-2: G- vs Gg (specific wins) -> NOT conflict",
|
||||
not e._genotype_conflict([{"G": ["G", "?"]}, {"G": ["G", "g"]}]))
|
||||
check("FIX-2: PP vs P? (specific wins: PP) -> NOT conflict",
|
||||
not e._genotype_conflict([{"P": ["P", "P"]}, {"P": ["P", "?"]}]))
|
||||
# Genuine value contradictions (both alleles specified but different) still quarantine
|
||||
check("Ee vs ee (different base allele) -> conflict",
|
||||
e._genotype_conflict([{"E": ["E", "e"]}, {"E": ["e", "e"]}]))
|
||||
check("C- vs Cc[h] -> conflict",
|
||||
e._genotype_conflict([{"C": ["C", "?"]}, {"C": ["C", "c^h"]}]))
|
||||
check("c[h] vs c[chm] (different modifiers) -> conflict",
|
||||
check("DD vs Dd (both specified, D vs d) -> conflict",
|
||||
e._genotype_conflict([{"D": ["D", "D"]}, {"D": ["D", "d"]}]))
|
||||
check("PP vs Pp (both specified) -> conflict",
|
||||
e._genotype_conflict([{"P": ["P", "P"]}, {"P": ["P", "p"]}]))
|
||||
check("c[h] vs c[chm] (different modifiers, both specified) -> conflict",
|
||||
not e._alleles_compatible("c^h", "c^chm"))
|
||||
check("identical genotypes -> no conflict",
|
||||
not e._genotype_conflict([{"A": ["A", "a"]}, {"A": ["A", "a"]}]))
|
||||
|
||||
# --- FIX-4: Skarlett parse artifact — trailing "/ +YEAR" stripped from geno, death captured ---
|
||||
dob4, death4, geno4 = e.parse_detail("Skarlett,*17.04.2016, aa C- DD ee Gg PP spsp rere / +2018")
|
||||
check("FIX-4: '/ +YEAR' artifact stripped from geno tail",
|
||||
geno4 == "aa C- DD ee Gg PP spsp rere")
|
||||
check("FIX-4: death year still captured from full cell text",
|
||||
death4 == "2018")
|
||||
check("FIX-4: DOB still correct",
|
||||
dob4 == "17.04.2016")
|
||||
# Without artifact — must be unchanged
|
||||
dob5, death5, geno5 = e.parse_detail("*01.01.2020, aa C- DD ee Gg PP spsp rere")
|
||||
check("FIX-4: no artifact -> geno unchanged",
|
||||
geno5 == "aa C- DD ee Gg PP spsp rere")
|
||||
check("FIX-4: no artifact -> no spurious death",
|
||||
death5 == "")
|
||||
|
||||
# --- name-bleed guard (a parent name is not a Farbschlag) ---
|
||||
check("v.d. name rejected", e.looks_like_animal_name("Tennessee von den Kleinen Chaoten"))
|
||||
check("gen.+v.d. name rejected", e.looks_like_animal_name("Victoria Welby gen. Welby v.d. Kleinen Chaoten"))
|
||||
|
||||
Reference in New Issue
Block a user