feat(triage): Ticket-Fixes (Daten + Code) + prod-fähige Triage
Daten-Fixes (conflict-decisions.json, re-ingest-stabil) für ~30 Tickets:
Merges (Jamie/Hiro/Mino/Jana/Blacky/Sakura/Malou/Socke→Marty), Eltern-Korrekturen
(Jacky/Idefix/Ichika/Roni/Ethan), Kruke→Kuke (+ Todesdatum), Targa-Wurf R14 + Druna,
Stacy/Merle/Domi/Eliza; Joghurt-Phantomwurf entfernt.
Code-Fixes:
- Gaida & alle Verstorbenen: Status wird aus Todesdatum/Abgabe abgeleitet
(Program.cs Startup-Sweep heilt Altfälle; IngestResolved re-derived nach Freeze).
- CoCo: Scheckungsart wird bei jeder Schecke angezeigt (Platzhalter wenn leer).
- M-Wurf/Gale: über-gemergte Fremdtiere via neuem litterChildren-Override entfernt.
- renameTo eltern-verknüpfungssicher (Quell-Name im Index); dateOfDeath als Override.
Prod-fähige Triage (API):
- GET /feedback/{id} + GET /feedback?status= (kein 2-MB-Dump).
- POST /import/ingest-resolved/upload (multipart) → Ingest gegen Prod ohne SSH.
Tests: 280 Backend, 149 Frontend, alle Python, betroffene Playwright grün.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3209,7 +3209,7 @@ def main():
|
||||
_ovr_by_namedob = {}
|
||||
for d in _decisions:
|
||||
has_ovr = (("isResident" in d) or d.get("notes") or d.get("receiver")
|
||||
or d.get("renameTo") or d.get("deceased")
|
||||
or d.get("renameTo") or d.get("deceased") or d.get("dateOfDeath")
|
||||
or d.get("correctDob") or d.get("genotype") or d.get("farbschlag")
|
||||
or d.get("gender") or d.get("father") or d.get("mother")
|
||||
or d.get("originBreeder"))
|
||||
@@ -3274,6 +3274,10 @@ def main():
|
||||
# erst HIER wird der Name überschrieben.
|
||||
rename_to = (d.get("renameTo") or "").strip()
|
||||
if rename_to and g.get("Name") != rename_to:
|
||||
# Quell-Namen merken, damit spätere Eltern-Verknüpfungen, die noch den
|
||||
# alten Namen referenzieren, weiter auflösen (siehe Namens-Index unten,
|
||||
# Ticket e0a0c304 Kruke→Kuke: sonst verlieren Kukes Würfe die Mutter).
|
||||
g["_pre_rename_name"] = g.get("Name")
|
||||
g["Name"] = rename_to
|
||||
applied = True
|
||||
# deceased: markiert ein bestehendes Tier als verstorben, falls noch
|
||||
@@ -3281,6 +3285,14 @@ def main():
|
||||
if d.get("deceased") and g.get("Status") != "Deceased":
|
||||
g["Status"] = "Deceased"
|
||||
applied = True
|
||||
# dateOfDeath: setzt das Todesdatum (falls noch keins) + Status Deceased.
|
||||
# rpro3 hat keine Sterbespalte, daher gibt die Züchterin/das Ticket das Datum vor.
|
||||
if d.get("dateOfDeath") and not g.get("DateOfDeath"):
|
||||
_dod = parse_date(d["dateOfDeath"])
|
||||
if _dod:
|
||||
g["DateOfDeath"] = _dod
|
||||
g["Status"] = "Deceased"
|
||||
applied = True
|
||||
if d.get("correctDob") and not g.get("DateOfBirth"):
|
||||
dob_iso = parse_date(d["correctDob"])
|
||||
g["DateOfBirth"] = dob_iso
|
||||
@@ -3414,6 +3426,13 @@ def main():
|
||||
# …and by the connector-folding canon key so abbreviation variants match
|
||||
# (e.g. „BlackFire v.d. Kleinen Chaoten“ vs „… von den …“ — ticket #30).
|
||||
keys.add(canon_name_key(g["Name"]))
|
||||
# Umbenannte Tiere (renameTo) auch unter ihrem Quell-Namen indexieren, damit
|
||||
# Eltern-Verknüpfungen, die noch den alten Namen referenzieren, weiter auflösen.
|
||||
pre = g.get("_pre_rename_name")
|
||||
if pre:
|
||||
keys.add(normalize_name(pre))
|
||||
keys.add(normalize_name(get_call_name(pre)))
|
||||
keys.add(canon_name_key(pre))
|
||||
for k in keys:
|
||||
if k:
|
||||
gerbil_by_norm_name.setdefault(k, []).append(g)
|
||||
@@ -4140,6 +4159,42 @@ def main():
|
||||
if _lo_applied:
|
||||
print(f"litterOrder: {_lo_applied} Jungtier-Reihenfolgen gesetzt")
|
||||
|
||||
# ── litterChildren: autoritative Kinder-Whitelist eines Wurfs (Mensch-Entscheidung) ──
|
||||
# Für Fälle, in denen der Wurf-Merge fremde Tiere (z. B. Abnehmer-Sammelseiten der
|
||||
# Wurfchronik) in einen datierten Wurf gezogen hat (Tickets 4b9f49fb M-Wurf, 8d259edf Gale).
|
||||
# Match per date + `matchChild` (ein aktuell zugeordnetes Kind, disambiguiert gleich-datierte
|
||||
# Würfe). Alle Kinder, deren Ruf-Name NICHT in `keep` steht, werden aus dem Wurf gelöst
|
||||
# (LitterId=None) — sie verschwinden aus dem Wurf, bleiben aber als Tier erhalten. Optional
|
||||
# `renameTo` setzt den kanonischen Wurf-Namen (z. B. 'Ungeklärt' -> 'M-Wurf').
|
||||
try:
|
||||
with open(CONFLICT_DECISIONS_PATH, encoding="utf-8") as _lcfh:
|
||||
_litter_children = (json.load(_lcfh).get("litterChildren") or [])
|
||||
except Exception:
|
||||
_litter_children = []
|
||||
_lc_removed = 0
|
||||
for lc in _litter_children:
|
||||
lc_date = parse_date(lc["date"]) if lc.get("date") else None
|
||||
match_child = normalize_name(get_call_name(lc.get("matchChild") or ""))
|
||||
keep = {normalize_name(get_call_name(n)) for n in (lc.get("keep") or [])}
|
||||
if not keep:
|
||||
continue
|
||||
for l in resolved_litters:
|
||||
if lc_date and l.get("Date") != lc_date:
|
||||
continue
|
||||
kids = [g for g in resolved_gerbils if g.get("LitterId") == l["Id"]]
|
||||
names = {normalize_name(get_call_name(g.get("Name") or "")) for g in kids}
|
||||
if match_child and match_child not in names:
|
||||
continue # falscher, nur zufällig gleich-datierter Wurf
|
||||
for g in kids:
|
||||
if normalize_name(get_call_name(g.get("Name") or "")) not in keep:
|
||||
g["LitterId"] = None
|
||||
_lc_removed += 1
|
||||
if lc.get("renameTo"):
|
||||
l["Name"] = lc["renameTo"]
|
||||
break
|
||||
if _lc_removed:
|
||||
print(f"litterChildren: {_lc_removed} fälschlich zugeordnete Jungtiere aus Würfen gelöst")
|
||||
|
||||
# ── isResident-Sweep (Ticket 381f7e51, freigegeben) ───────────────────────
|
||||
# Entscheidung der Züchterin: „Nimm wirklich ALLE Tiere aus dem Bestand heraus,
|
||||
# MIT AUSNAHME der, die bereits ein ELTERNTIER sind." Datengetrieben + re-ingest-
|
||||
|
||||
Reference in New Issue
Block a user