feat(import): isResident-Override + Vorfahren Bentley/Alexandria/Bugatti
- Neuer isResident-Override im Import (merge_and_resolve, NACH dem Dedup, damit die Zusammenführung ihn nicht überschreibt). Matcht über externalRef (präzise, auch für namenlose Tiere) oder normalize(call-name)+dob. Ein Nicht-Bestandstier verliert den eigenen Zucht-Breeder. - conflict-decisions.json: Bentley, Alexandria, Bugatti = nur Vorfahren (isResident=false), bestätigt von der Züchterin (Tickets 09bcac78 / 45cc501b). - Regressionstests ergänzt/aktualisiert (Bentley/Alexandria/Bugatti isResident; Danielle Vater Makoto; Catelyn Eddard×Milena; Gaida Zhuāngzǐ×Zaibunissa). Alle python test_*.py grün. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -414,6 +414,27 @@
|
||||
"mother": "Zaibunissa von den Kleinen Chaoten",
|
||||
"motherDob": "21.02.2020",
|
||||
"source": "Züchterin 2026-06-23 — Ticket ba63325a"
|
||||
},
|
||||
{
|
||||
"name": "Bentley",
|
||||
"externalRef": "Wurfchronik Teil 1_page_0054.md-50505050-0003-4000-8000-000000000003",
|
||||
"isResident": false,
|
||||
"decision": "Nur Vorfahre (Clan of Black Forest), kein eigenes Zuchttier. Bestätigt von der Züchterin.",
|
||||
"source": "Züchterin 2026-06-23 — Ticket 09bcac78"
|
||||
},
|
||||
{
|
||||
"name": "Alexandria",
|
||||
"externalRef": "Wurfchronik Teil 1_page_0054.md-50505050-0004-4000-8000-000000000004",
|
||||
"isResident": false,
|
||||
"decision": "Nur Vorfahre, kein eigenes Zuchttier (wie Bentley).",
|
||||
"source": "Züchterin 2026-06-23 — Ticket 45cc501b"
|
||||
},
|
||||
{
|
||||
"name": "Bugatti",
|
||||
"externalRef": "Wurfchronik Teil 1_page_0054.md-40404040-0003-4000-8000-000000000003",
|
||||
"isResident": false,
|
||||
"decision": "Bugatti stammt aus Clan of Black Forest, war nie ihr Zuchttier — nur Vorfahre.",
|
||||
"source": "Züchterin 2026-06-23 — Ticket 09bcac78"
|
||||
}
|
||||
],
|
||||
"addAnimals": [
|
||||
|
||||
@@ -3016,6 +3016,51 @@ def main():
|
||||
|
||||
print(f"Deduplicated to {len(resolved_gerbils)} unique gerbil records.")
|
||||
|
||||
# ── isResident-Overrides (Mensch-Entscheidung) ────────────────────────────
|
||||
# NACH dem Dedup anwenden, sonst würde die Dedup-Zusammenführung (IsResident=True
|
||||
# falls eine Variante resident ist) sie wieder überschreiben. Matcht primär über
|
||||
# `externalRef` (präzise, auch für namenlose Tiere) oder sonst über
|
||||
# normalize(call-name)+ISO-dob (leere dob = name-only, für Vorfahren ohne Datum).
|
||||
_isres_by_extref = {}
|
||||
_isres_by_namedob = {}
|
||||
for d in _decisions:
|
||||
if "isResident" not in d:
|
||||
continue
|
||||
val = bool(d["isResident"])
|
||||
er = (d.get("externalRef") or "").strip()
|
||||
if er:
|
||||
_isres_by_extref[er] = val
|
||||
nm = d.get("name")
|
||||
if nm is not None:
|
||||
ck = normalize_name(get_call_name(nm or ""))
|
||||
iso = parse_date(d.get("dob")) if d.get("dob") else ""
|
||||
_isres_by_namedob[(ck, iso or "")] = val
|
||||
_isres_applied = 0
|
||||
if _isres_by_extref or _isres_by_namedob:
|
||||
for g in resolved_gerbils:
|
||||
er = g.get("ExternalRef") or ""
|
||||
ck = normalize_name(get_call_name(g.get("Name") or ""))
|
||||
iso = g.get("DateOfBirth") or ""
|
||||
if er and er in _isres_by_extref:
|
||||
val = _isres_by_extref[er]
|
||||
elif er and any(er.endswith(k) for k in _isres_by_extref):
|
||||
val = next(v for k, v in _isres_by_extref.items() if er.endswith(k))
|
||||
elif (ck, iso) in _isres_by_namedob:
|
||||
val = _isres_by_namedob[(ck, iso)]
|
||||
elif (ck, "") in _isres_by_namedob:
|
||||
val = _isres_by_namedob[(ck, "")]
|
||||
else:
|
||||
continue
|
||||
if g.get("IsResident") != val:
|
||||
g["IsResident"] = val
|
||||
# Ein Nicht-Bestandstier ist kein eigenes Zuchttier → den eigenen
|
||||
# Zucht-Breeder entfernen (nur, wenn er auf die eigene Zucht zeigt).
|
||||
if not val and g.get("OriginBreeder") == "Zucht der kleinen Chaoten":
|
||||
g["OriginBreeder"] = None
|
||||
_isres_applied += 1
|
||||
if _isres_applied:
|
||||
print(f"isResident-Overrides angewandt: {_isres_applied}")
|
||||
|
||||
# Apply age-based death threshold (6.0 years) to all resolved gerbils
|
||||
dt_now = datetime.now()
|
||||
for g in resolved_gerbils:
|
||||
|
||||
@@ -559,17 +559,30 @@ if _os.path.exists(_resolved):
|
||||
# Sunny von PZ Karl: father corrected Hiro → Bill von Privat.
|
||||
_check_parents("Sunny (parents)", "Sunny von PZ Karl", "Bill von Privat", "Melly von Privat")
|
||||
|
||||
# Danielle: mother = Ella *10.06.2019 (the elder one), father unknown.
|
||||
# Danielle: mother = Ella *10.06.2019, father = Makoto (sibling pairing; Ticket 4692fd5c).
|
||||
_dan = _find("Danielle von den Kleinen")
|
||||
_df, _dm = _parents(_dan)
|
||||
check("Danielle: mother is Ella", (_dm or "") == "Ella")
|
||||
check("Danielle: father unknown (sibling pairing)", _df is None)
|
||||
check("Danielle: father is Makoto", (_df or "").startswith("Makoto"))
|
||||
if _dan and _dan.get("LitterId"):
|
||||
_dl = _L.get(_dan["LitterId"])
|
||||
_dmom = _G.get(_dl.get("MotherId")) if _dl else None
|
||||
check("Danielle: mother Ella is the *2019-06-10 one (not the *2023 Ella)",
|
||||
_dmom is not None and _dmom.get("DateOfBirth") == "2019-06-10")
|
||||
|
||||
# Catelyn (Ticket 7bbc045c): father Eddard Stark of Sunset Glow, mother Milena.
|
||||
_check_parents("Catelyn", "Catelyn Stark von den Kleinen", "Eddard Stark", "Milena")
|
||||
|
||||
# Gaida (Ticket ba63325a): Geschwisterverpaarung Zhuāngzǐ × Zaibunissa (beide *2020-02-21).
|
||||
_check_parents("Gaida", "Gaida von den Kleinen Chaoten", "Zhuāngzǐ", "Zaibunissa")
|
||||
|
||||
# Bentley / Alexandria / Bugatti (Ticket 09bcac78 / 45cc501b): nur Vorfahren → isResident False.
|
||||
for _tag, _ref in [("Bentley", "Wurfchronik Teil 1_page_0054.md-50505050-0003-4000-8000-000000000003"),
|
||||
("Alexandria", "Wurfchronik Teil 1_page_0054.md-50505050-0004-4000-8000-000000000004"),
|
||||
("Bugatti", "Wurfchronik Teil 1_page_0054.md-40404040-0003-4000-8000-000000000003")]:
|
||||
_g = next((g for g in _d["gerbils"] if g.get("ExternalRef") == _ref), None)
|
||||
check(f"{_tag}: isResident override == False", _g is not None and _g.get("IsResident") is False)
|
||||
|
||||
# Cherry Berry's Quqquluuruu: gender override female (box colour misread).
|
||||
_cherry = _find("Cherry Berry")
|
||||
check("Cherry Berry: gender override female",
|
||||
|
||||
Reference in New Issue
Block a user