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:
2026-06-23 10:08:13 +02:00
parent 26977d73ff
commit 6b264a9b71
6 changed files with 532 additions and 31 deletions

View File

@@ -1099,9 +1099,19 @@ def apply_conflict_decisions(merged, conflicts, path):
Returns the number of conflicts resolved. (god/HUMANQUESTION D.)"""
decisions_full = {} # (nameCanon, zuchtCanon, dob) -> r — when decision carries a Zucht
decisions_name = {} # (nameCanon, dob) -> r — fallback, decision has no Zucht
decisions_ref = {} # externalRef (merged-animal id) -> r — for NAMELESS animals whose
# (name="" + dob) key is shared by several records: the externalRef
# (the dedup slug, e.g. „unbekannt-13082025-3") pins exactly one.
try:
with open(path, encoding="utf-8") as fh:
for r in (json.load(fh).get("resolutions") or []):
ref = r.get("externalRef")
if ref:
decisions_ref[ref] = r
# An externalRef-only decision (no name) must NOT register a name/dob
# key — a ("", "") key would match every nameless, dateless animal.
if not r.get("name"):
continue
nc, zc = canon_pair(r.get("name", ""))
dob = norm_dob(r.get("dob", ""))
if zc:
@@ -1110,14 +1120,17 @@ def apply_conflict_decisions(merged, conflicts, path):
decisions_name[(nc, dob)] = r
except (OSError, ValueError):
return 0
if not decisions_full and not decisions_name:
if not decisions_full and not decisions_name and not decisions_ref:
return 0
resolved = 0
for a in merged:
nc, zc = canon_pair(a["name"])
dob = norm_dob(a["dob"])
d = decisions_full.get((nc, zc, dob)) or decisions_name.get((nc, dob))
# externalRef (the dedup id) wins — it is the most specific key and the only
# way to address one of several same-(name,dob) nameless animals.
d = decisions_ref.get(a.get("id")) or decisions_full.get((nc, zc, dob)) \
or decisions_name.get((nc, dob))
if not d:
continue
a["resolvedByDecision"] = True