fix(import): Farbschlag aus Genotyp ableiten + Mamta-Eltern — Ticket-Triage
genotype.py: Python-Port von genotypeToFarbschlag (0 Abw. über 3402 Genotypen). resolve_color_and_genotype: bei vorhandenem Genotyp gewinnt der berechnete Farbschlag (Goldfuchs≠Gold, Dilute Agouti/Anthrazit, Blaufuchs statt -schimmel bei (schimmel), spsp statt Schecke). Mamta Mini: Ee + Eltern Geely×Gaida am Wurf. Regressionstests je Fall. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -432,6 +432,61 @@ check("contracts: animal-less record has empty Animals list",
|
||||
_sale3 and _sale3[0]["Animals"] == [])
|
||||
|
||||
|
||||
# ── resolve_color_and_genotype + clean_color_name (genetics-farbschlag cluster) ──
|
||||
# A tiny synthetic variety_map (name->id) with the keys these cases need.
|
||||
_VM = {
|
||||
"gold": "ID-gold", "goldfuchs": "ID-goldfuchs", "goldfuchsschimmel": "ID-gfs",
|
||||
"agouti": "ID-agouti", "dilute agouti": "ID-dagouti",
|
||||
"anthrazit": "ID-anthrazit", "dilute anthrazit": "ID-danthrazit",
|
||||
"blaufuchs": "ID-blaufuchs", "blaufuchsschimmel": "ID-bfs",
|
||||
"kohlfuchsschimmel": "ID-kfs", "marder": "ID-marder", "schwarz": "ID-schwarz",
|
||||
"orangeschimmel": "ID-orange",
|
||||
}
|
||||
_VG = {}
|
||||
|
||||
|
||||
def _rc(color, geno):
|
||||
return m.resolve_color_and_genotype(color, geno, _VM, _VG)[0]
|
||||
|
||||
|
||||
# Ticket 3f5942a2 — specificity: „Goldfuchs"-label must NOT collapse to „Gold".
|
||||
check("3f5942a2 label: 'Goldfuchs' -> goldfuchs (not gold)",
|
||||
m._match_color_label("goldfuchs", _VM) == "ID-goldfuchs")
|
||||
# Genotype wins: ee fox genotype overrides a stale „Gold" label.
|
||||
check("3f5942a2 genotype wins: ee -> Goldfuchs over 'Gold' label",
|
||||
_rc("Gold", "AA CC DD ee GG pp spsp") == "ID-goldfuchs")
|
||||
# Ticket 998087e2 — dd ignored by label: genotype gives Dilute Agouti.
|
||||
check("998087e2: dd genotype -> Dilute Agouti over 'Agouti' label",
|
||||
_rc("Agouti", "AA CC dd EE GG PP spsp") == "ID-dagouti")
|
||||
# Ticket 06217eb3 — Dilute Anthrazit.
|
||||
check("06217eb3: dd genotype -> Dilute Anthrazit over 'Anthrazit'",
|
||||
_rc("Anthrazit", "aa CC dd Ee gg P- spsp") == "ID-danthrazit")
|
||||
# Ticket 1aac054f — Kohlfuchsschimmel over a stale 'Gold' label.
|
||||
check("1aac054f: ee[f] genotype -> Kohlfuchsschimmel over 'Gold'",
|
||||
_rc("Gold", "aa Cc[chm] D- ee[f] Gg Pp Spsp") == "ID-kfs")
|
||||
# Ticket e22764aa — „Blaufuchs(schimmel)" parenthetical is NOT definitive; the
|
||||
# cleaned label is „blaufuchs" and the ee[-] genotype confirms Blaufuchs.
|
||||
_cn, _sc = m.clean_color_name("Blaufuchs(schimmel)")
|
||||
check("e22764aa: '(schimmel)' stripped, not promoted -> 'blaufuchs'", _cn == "blaufuchs")
|
||||
check("e22764aa: ee[-] genotype -> Blaufuchs (not Blaufuchsschimmel)",
|
||||
_rc("Blaufuchs(schimmel)", "aa C- D- ee[-] gg P- spsp") == "ID-blaufuchs")
|
||||
# Ticket e09d6f22 — a Schecke-looking LABEL must not flip an explicit source spsp
|
||||
# to Spsp (the source genotype is authoritative for the Sp-locus).
|
||||
_, _g_spsp = m.resolve_color_and_genotype("Kohlfuchsschimmel, hell",
|
||||
"aa Cc[chm] D- ee[f] Gg Pp spsp", _VM, _VG)
|
||||
check("e09d6f22: explicit spsp kept (label-Schecke does not force Spsp)",
|
||||
"Spsp" not in _g_spsp and "spsp" in _g_spsp)
|
||||
# VORSICHTIG guard: a COMPACT-notation genotype (cchmcchm/efef) the parser can't
|
||||
# read must fall back to the text label, NOT mis-recolour (e.g. Marder->Schwarz).
|
||||
check("guard: compact 'cchmcchm' unparsable -> keep label 'Marder'",
|
||||
_rc("Marder", "aa cchmcchm DD EE GG PP spsp rere") == "ID-marder")
|
||||
check("guard: compact 'efef' unparsable -> keep label 'Orangeschimmel'",
|
||||
_rc("Orangeschimmel", "AA CC DD efef GG PP spsp rere") == "ID-orange")
|
||||
# A genuinely Schecke label with no Sp in the genotype still appends Spsp.
|
||||
_, _g_add = m.resolve_color_and_genotype("Agouti Schecke", "AA CC DD EE GG PP", _VM, _VG)
|
||||
check("schecke label + no Sp token -> appends Spsp", "Spsp" in _g_add)
|
||||
|
||||
|
||||
# ── Integration: assert the resolved_import.json output reflects the ticket fixes ──
|
||||
# (Only when the pipeline has already been run; tolerant if the file is absent.)
|
||||
import os as _os, json as _json
|
||||
@@ -534,6 +589,59 @@ if _os.path.exists(_resolved):
|
||||
and "unbekannt" in (g.get("ExternalRef") or "").lower()]
|
||||
check("Duplicate-merge: nameless buck *15.02.2024 deduped to one record",
|
||||
len(_bucks) == 1)
|
||||
|
||||
# ── genetics-farbschlag cluster: the STORED colorVarietyId is now genotype-
|
||||
# correct for the ticket animals. Build the id→name map from the authoritative
|
||||
# ApplicationContext.cs catalog (same source the pipeline uses for the ids).
|
||||
import re as _re
|
||||
_app = _os.path.abspath(_os.path.join(_os.path.dirname(__file__),
|
||||
"../../GerbilManagerWebAPI/ApplicationContext.cs"))
|
||||
_idname = {}
|
||||
if _os.path.exists(_app):
|
||||
_cm = _re.search(r"catalog\s*=\s*\{(.*?)\};", open(_app, encoding="utf-8").read(), _re.DOTALL)
|
||||
if _cm:
|
||||
for _i, (_n, _g, _so) in enumerate(_re.findall(
|
||||
r'\(\s*"([^"]+)"\s*,\s*"([^"]+)"\s*,\s*(\d+)\s*\)', _cm.group(1))):
|
||||
_idname[f"00000000-0000-0000-0000-{_i + 1:012d}"] = _n
|
||||
|
||||
def _by_ref(ref):
|
||||
return next((g for g in _d["gerbils"] if g.get("ExternalRef") == ref), None)
|
||||
|
||||
def _cv_name(g):
|
||||
return _idname.get(g.get("ColorVarietyId")) if g else None
|
||||
|
||||
if _idname:
|
||||
# Ticket 1aac054f — namenloses Weibchen *13.08.2025 -> Kohlfuchsschimmel.
|
||||
_t1 = _by_ref("stammbaum-unbekannt-13082025-2")
|
||||
check("1aac054f: nameless *13.08.2025 stored as Kohlfuchsschimmel",
|
||||
_cv_name(_t1) == "Kohlfuchsschimmel")
|
||||
# Ticket e09d6f22 — same litter, *-3: spsp (NOT Schecke) + Kohlfuchsschimmel.
|
||||
_t2 = _by_ref("stammbaum-unbekannt-13082025-3")
|
||||
check("e09d6f22: Sp-locus is spsp (no Schecke)",
|
||||
_t2 is not None and "Spsp" not in (_t2.get("Genotype") or "")
|
||||
and "spsp" in (_t2.get("Genotype") or ""))
|
||||
check("e09d6f22: stored as Kohlfuchsschimmel", _cv_name(_t2) == "Kohlfuchsschimmel")
|
||||
# Ticket 06217eb3 — Dilute Anthrazit (dd).
|
||||
_t3 = _by_ref("stammbaum-unbekannt-27052025")
|
||||
check("06217eb3: nameless dd-Weibchen stored as Dilute Anthrazit",
|
||||
_cv_name(_t3) == "Dilute Anthrazit")
|
||||
# Ticket e22764aa — Blaufuchs (NOT Blaufuchsschimmel).
|
||||
_t4 = _by_ref("stammbaum-unbekannt-16012026")
|
||||
check("e22764aa: '(schimmel)' animal stored as Blaufuchs",
|
||||
_cv_name(_t4) == "Blaufuchs")
|
||||
# Ticket 3f5942a2 — named fox animals are Goldfuchs (ee), not Gold (EE).
|
||||
_banjo = _find("Banjo of Fiomi")
|
||||
check("3f5942a2: Banjo of Fiomi stored as Goldfuchs",
|
||||
_cv_name(_banjo) == "Goldfuchs")
|
||||
|
||||
# ── Mamta Mini (cc9ea3fe / 1a508c04): Ee[-] resolved to Ee + parents linked. ──
|
||||
_mamta = _find("Mamta Mini")
|
||||
check("Mamta Mini: E-locus resolved to Ee (no unknown [-])",
|
||||
_mamta is not None and "Ee[-]" not in (_mamta.get("Genotype") or "")
|
||||
and "Ee" in (_mamta.get("Genotype") or ""))
|
||||
_mf, _mm = _parents(_mamta)
|
||||
check("Mamta Mini: father Geely, mother Gaida linked at the litter",
|
||||
(_mf or "").startswith("Geely") and (_mm or "").startswith("Gaida"))
|
||||
else:
|
||||
print("note: output/resolved_import.json not present — skipped integration assertions")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user