feat(import): merge animals with different DOBs if their parent names match
Some checks failed
CI / Backend Tests (.NET) (push) Successful in 1m2s
CI / Frontend Tests (Node/Vite) (push) Successful in 9m35s
CI / Docker Build & Push (push) Failing after 12s

This commit is contained in:
2026-06-21 22:23:59 +02:00
parent d2993418b6
commit 41ff973f9a
3 changed files with 111 additions and 15 deletions

View File

@@ -612,6 +612,49 @@ def dedup(animals):
"files": sorted(set(f for a in grp for f in a["sourceFiles"])),
})
# 2. Merge groups that have different DOBs but same call-name (and Zucht) and matching parents
def get_parent_keys(a):
parent_refs = a.get("parentRefs", [])
f_name = next((p["name"] for p in parent_refs if p.get("roleGuess") == "father"), "")
m_name = next((p["name"] for p in parent_refs if p.get("roleGuess") == "mother"), "")
return norm_name(f_name), norm_name(m_name)
def groups_parents_match(g1, g2):
for a1 in g1:
for a2 in g2:
f1, m1 = get_parent_keys(a1)
f2, m2 = get_parent_keys(a2)
if f1 and f2 and f1 == f2 and m1 and m2 and m1 == m2:
return True
return False
i = 0
while i < len(final_groups):
g1 = final_groups[i]
call1, _ = split_name_zucht(g1[0]["name"])
n_call1 = norm_name(call1)
zucht1 = g1[0].get("_zucht", "")
j = i + 1
merged_any = False
while j < len(final_groups):
g2 = final_groups[j]
call2, _ = split_name_zucht(g2[0]["name"])
n_call2 = norm_name(call2)
zucht2 = g2[0].get("_zucht", "")
if n_call1 == n_call2:
if zucht1 == zucht2 or not zucht1 or not zucht2:
if groups_parents_match(g1, g2):
g1.extend(g2)
final_groups.pop(j)
merged_any = True
continue
j += 1
if merged_any:
continue
i += 1
merged = []
conflicts = []
for grp in final_groups:
@@ -652,11 +695,25 @@ def dedup(animals):
return sum(1 for pair in gd["mapped8locus"].values() for a in pair if a != "?")
best = max((a["genotype"] for a in grp),
key=lambda gd: (len(gd["mapped8locus"]), _specificity(gd), len(gd["rawGenotype"])))
# Find best DOB (proband first)
best_dob = ""
for a in grp:
if a.get("_gen") == 0 and a.get("dob"):
best_dob = a["dob"]
break
if not best_dob:
for a in grp:
if a.get("dob"):
best_dob = a["dob"]
break
chosen_dob = norm_dob(best_dob or base["dob"])
out = {
"id": slug(base["name"], base["dob"]),
"id": slug(base["name"], chosen_dob),
"name": base["name"],
"nameVariants": sorted(v for v in variants if v),
"dob": norm_dob(base["dob"]),
"dob": chosen_dob,
"death": sorted(deaths)[0] if deaths else "",
# box-colour sex (blue=male, white=female): majority across mentions, else None.
"gender": Counter(genders).most_common(1)[0][0] if genders else None,