From 4e30dc7fe374fd254f767ea9adfee560dce74400 Mon Sep 17 00:00:00 2001 From: Gulum Date: Mon, 22 Jun 2026 22:38:24 +0200 Subject: [PATCH] =?UTF-8?q?feat(import):=20RennmausPro-III-Analyse=20?= =?UTF-8?q?=E2=80=94=20Abgleich,=20Dedup,=20Feature-Gap,=20Ticket-Generier?= =?UTF-8?q?ung?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - compare_rpro3.py: RPRO3-SQLite ↔ aktueller Stand (Dedup Name+DOB+Farbe+Herkunft) - rpro3-vergleich.md / rpro3-feature-gap.md: erzeugte Reports - rpro3_tickets.py / rpro3_post_tickets.py: 413 Rückfrage-Tickets mit Herkunft + Links Co-Authored-By: Claude Opus 4.8 --- tools/import/compare_rpro3.py | 474 +++++++++++++++++++++++++++++ tools/import/rpro3-feature-gap.md | 90 ++++++ tools/import/rpro3-vergleich.md | 449 +++++++++++++++++++++++++++ tools/import/rpro3_post_tickets.py | 64 ++++ tools/import/rpro3_tickets.py | 162 ++++++++++ 5 files changed, 1239 insertions(+) create mode 100644 tools/import/compare_rpro3.py create mode 100644 tools/import/rpro3-feature-gap.md create mode 100644 tools/import/rpro3-vergleich.md create mode 100644 tools/import/rpro3_post_tickets.py create mode 100644 tools/import/rpro3_tickets.py diff --git a/tools/import/compare_rpro3.py b/tools/import/compare_rpro3.py new file mode 100644 index 0000000..446356f --- /dev/null +++ b/tools/import/compare_rpro3.py @@ -0,0 +1,474 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +RennmausPro III (SQLite `_rpro3.db`) <-> aktueller GerbilManager-Stand: Abgleich + Dedup-Analyse. + +Erzeugt NUR einen Report (schreibt NICHTS in die DB). + +Zwei Teile: + (A) RPRO3-INTERNE DUBLETTEN: RennmausPro erkennt beim Import vorhandene Tiere nicht + -> dasselbe (v.a. externe) Tier kommt mehrfach vor. Zusammenlegung anhand + Name + Geburtsdatum (DOB) + Farbe + Herkunft. Sichere Cluster werden gemerged, + unsichere Faelle landen im Report. + (B) ABGLEICH vs. aktueller Stand (API-JSON): eigene Tiere (Eltern/Genotyp/fehlend), + sowie Zaehlungen fuer Wuerfe/Kontakte/externe Ahnen. + +Aufruf: + python compare_rpro3.py [report.md] +""" +from __future__ import annotations +import sqlite3, sys, json, datetime, re, unicodedata +from collections import Counter, defaultdict + +# ---------- Helpers ---------- +PLACEHOLDER_NAMES = {"", "-", "n", "unbekannt", "unbekannt?", "?", "nn", "n.n.", "na"} +# Herkunft/Farbe-Werte, die "unbekannt" bedeuten -> wie leer behandeln (kompatibel mit allem) +UNKNOWN_VALUES = {"", "unbekannt", "unbek.", "unbek", "unbekannte zucht", "?", "n.n.", + "unbekannter farbschlag", "keine angabe", "k.a.", "na"} + +def smart_decode(b): + """RPRO3-DB ist gemischt kodiert: neuere Felder UTF-8, ältere Latin-1/CP1252. + Erst UTF-8 versuchen (valid -> nehmen), sonst CP1252.""" + try: + return b.decode("utf-8") + except UnicodeDecodeError: + return b.decode("cp1252", "replace") + +def jdn_to_date(v): + """RPRO3 speichert Daten als astronomische Julianische Tageszahl (REAL). 0/None = unbekannt.""" + if v is None: + return None + try: + v = float(v) + except (TypeError, ValueError): + return None + if v <= 0: + return None + try: + return datetime.date.fromordinal(int(round(v)) - 1721425) + except (ValueError, OverflowError): + return None + +def norm_name(s): + if s is None: + return "" + s = unicodedata.normalize("NFKC", str(s)).strip().casefold() + s = re.sub(r"\s+", " ", s) + return s + +def norm_geno(s): + if not s: + return "" + return re.sub(r"\s+", " ", str(s).strip()) + +def norm_farbe(s): + v = norm_name(s) + return "" if v in UNKNOWN_VALUES else v + +def norm_origin(s): + v = norm_name(s) + return "" if v in UNKNOWN_VALUES else v + +def iso(d): + return d.isoformat() if d else "—" + +# ---------- RPRO3 laden ---------- +def load_rpro3(db_path): + c = sqlite3.connect(db_path) + c.text_factory = smart_decode + c.row_factory = sqlite3.Row + cur = c.cursor() + + herk = {r["id"]: (r["_BEZ"] or r["_CLAN"] or f'{r["_VNAME"]} {r["_NNAME"]}'.strip()) + for r in cur.execute("SELECT * FROM herk_tb")} + + color_stamm = {str(r["id"]): r for r in cur.execute("SELECT * FROM color_tb")} + color_pup = {str(r["id"]): r for r in cur.execute("SELECT * FROM wurfcolor_tb")} + color_ext = {str(r["id"]): r for r in cur.execute("SELECT * FROM fremdcolor_tb")} + + baum = {str(r["id"]): (r["_MID"], r["_PID"]) for r in cur.execute("SELECT * FROM baum_tb")} + + stamm = {int(r["id"]): r for r in cur.execute("SELECT * FROM stamm_tb")} + fremd = {int(r["id"]): r for r in cur.execute("SELECT * FROM fremd_tb")} + wurftier = {str(r["id"]): r for r in cur.execute("SELECT * FROM wurftier_tb")} + wurf = {int(r["id"]): r for r in cur.execute("SELECT * FROM wurf_tb")} + + def color_for(rid): + rid = str(rid) + if rid.startswith("u"): + row = color_ext.get(rid) + elif "j" in rid: + row = color_pup.get(rid) + else: + row = color_stamm.get(rid) + if not row: + return ("", "") + return (row["_FARBE"] or "", row["_FCODE"] or "") + + def name_for(rid): + if rid in (None, "", "n", "NULL"): + return None + rid = str(rid) + if rid.startswith("u"): + try: + r = fremd.get(int(rid[1:])) + except ValueError: + return None + return r["_NAME"] if r else None + if "j" in rid: + r = wurftier.get(rid) + return r["_NAME"] if r else None + try: + r = stamm.get(int(rid)) + except ValueError: + return None + return r["_NAME"] if r else None + + animals = [] # einheitliche Records + + # eigene Tiere (stamm) + for sid, r in stamm.items(): + farbe, fcode = color_for(sid) + mid, pid = baum.get(str(sid), (None, None)) + animals.append({ + "rid": str(sid), "src": "stamm", "name": r["_NAME"], + "sex": r["_SEX"], "dob": jdn_to_date(r["_BIRTH"]), + "farbe": farbe, "fcode": fcode, + "origin": herk.get(r["_HERKUNFT"], ""), + "zb": r["_ZB"], "mid_raw": mid, "pid_raw": pid, + "mother": name_for(mid), "father": name_for(pid), + "wid": r["_WID"], + }) + + # externe Tiere (fremd) + for fid, r in fremd.items(): + rid = f"u{fid}" + farbe, fcode = color_for(rid) + animals.append({ + "rid": rid, "src": "fremd", "name": r["_NAME"], + "sex": r["_SEX"], "dob": jdn_to_date(r["_BIRTH"]), + "farbe": farbe, "fcode": fcode, + "origin": herk.get(r["_HERK"], ""), + "zb": r["_ZB"], "mid_raw": r["_MID"], "pid_raw": r["_PID"], + "mother": name_for(r["_MID"]), "father": name_for(r["_PID"]), + "wid": None, + }) + + return { + "animals": animals, "stamm": stamm, "fremd": fremd, + "wurf": wurf, "wurftier": wurftier, "herk": herk, + "n_color_stamm": len(color_stamm), "n_color_ext": len(color_ext), + "name_for": name_for, + } + +# ---------- Dedup (Teil A) ---------- +def dedup(animals): + """Union-Find innerhalb gleicher (normalisierter) Namen. + merge(a,b) wenn DOB/Farbe/Herkunft kompatibel (gleich oder eine Seite unbekannt) + UND mindestens ein bekanntes Feld positiv uebereinstimmt. + Konflikt in einem bekannten Feld -> NICHT mergen (unsicher).""" + def compat(x, y): + return (not x) or (not y) or (x == y) + + def positive(a, b): + agree = 0 + if a["dobk"] and b["dobk"] and a["dobk"] == b["dobk"]: + agree += 1 + if a["farbek"] and b["farbek"] and a["farbek"] == b["farbek"]: + agree += 1 + if a["origink"] and b["origink"] and a["origink"] == b["origink"]: + agree += 1 + return agree + + def conflict(a, b): + c = 0 + if a["dobk"] and b["dobk"] and a["dobk"] != b["dobk"]: + c += 1 + if a["farbek"] and b["farbek"] and a["farbek"] != b["farbek"]: + c += 1 + if a["origink"] and b["origink"] and a["origink"] != b["origink"]: + c += 1 + return c + + for a in animals: + a["namek"] = norm_name(a["name"]) + a["dobk"] = a["dob"] + a["farbek"] = norm_farbe(a["farbe"]) + a["origink"] = norm_origin(a["origin"]) + + by_name = defaultdict(list) + placeholders = [] + for a in animals: + if a["namek"] in PLACEHOLDER_NAMES: + placeholders.append(a) + else: + by_name[a["namek"]].append(a) + + parent = {} + def find(x): + while parent[x] != x: + parent[x] = parent[parent[x]] + x = parent[x] + return x + def union(x, y): + parent.setdefault(x, x); parent.setdefault(y, y) + parent[find(x)] = find(y) + + for name, group in by_name.items(): + for a in group: + parent.setdefault(a["rid"], a["rid"]) + n = len(group) + for i in range(n): + for j in range(i + 1, n): + a, b = group[i], group[j] + pos = positive(a, b) + con = conflict(a, b) + comp = compat(a["dobk"], b["dobk"]) and compat(a["farbek"], b["farbek"]) and compat(a["origink"], b["origink"]) + if comp and pos >= 1 and con == 0: + union(a["rid"], b["rid"]) # sicher zusammenlegen + + # Cluster je Wurzel sammeln + clusters = defaultdict(list) + for a in animals: + if a["namek"] in PLACEHOLDER_NAMES: + continue + clusters[find(a["rid"])].append(a) + merge_clusters = {k: v for k, v in clusters.items() if len(v) > 1} + + # Mehrdeutige Namen: Name löst sich nach sicherem Merge in >1 Cluster auf + name_to_roots = defaultdict(set) + for a in animals: + if a["namek"] in PLACEHOLDER_NAMES: + continue + name_to_roots[a["namek"]].add(find(a["rid"])) + ambiguous = [] # (anzeigename, [variant_sig,...]) + for nm, roots in name_to_roots.items(): + if len(roots) <= 1: + continue + variants = [] + for root in roots: + recs = clusters[root] + r0 = recs[0] + variants.append({ + "n": len(recs), + "rids": [r["rid"] for r in recs][:4], + "dob": sorted({iso(r["dob"]) for r in recs if r["dob"]}), + "farbe": sorted({r["farbe"] for r in recs if r["farbe"]}), + "origin": sorted({r["origin"] for r in recs if norm_origin(r["origin"])}), + "is_own": any(r["src"] == "stamm" for r in recs), + }) + variants.sort(key=lambda v: -v["n"]) + ambiguous.append((recs[0]["name"] if False else nm, variants)) + # Anzeigenamen schöner: ersten Originalnamen je Gruppe nehmen + disp = {} + for a in animals: + disp.setdefault(a["namek"], a["name"]) + ambiguous = [(disp.get(nm, nm), v) for nm, v in ambiguous] + ambiguous.sort(key=lambda x: -len(x[1])) + return merge_clusters, ambiguous, placeholders + +# ---------- aktueller Stand ---------- +def load_current(scratch): + def load(ep): + d = json.load(open(f"{scratch}\\cur_{ep}.json", encoding="utf-8")) + return d["items"] if isinstance(d, dict) else d + g = load("gerbils"); lit = load("litters"); con = load("contacts") + gid = {x["id"]: x for x in g} + litmap = {x["id"]: x for x in lit} + by_name = defaultdict(list) + for x in g: + by_name[norm_name(x["name"])].append(x) + def parents(x): + lid = x.get("litterId") + if not lid or lid not in litmap: + return (None, None) + l = litmap[lid] + f = gid.get(l.get("fatherId"), {}).get("name") + m = gid.get(l.get("motherId"), {}).get("name") + return (m, f) + return {"gerbils": g, "litters": lit, "contacts": con, + "by_name": by_name, "parents": parents} + +def parse_dob(s): + if not s: + return None + try: + return datetime.date.fromisoformat(str(s)[:10]) + except ValueError: + return None + +# ---------- Report ---------- +def main(): + db = sys.argv[1] + scratch = sys.argv[2] + out = sys.argv[3] if len(sys.argv) > 3 else None + + R = load_rpro3(db) + animals = R["animals"] + own = [a for a in animals if a["src"] == "stamm"] + ext = [a for a in animals if a["src"] == "fremd"] + + merge_clusters, ambiguous, placeholders = dedup(animals) + cur = load_current(scratch) + + L = [] + w = L.append + w("# RennmausPro III — Abgleich- & Dedup-Report\n") + w("> Erzeugt von `tools/import/compare_rpro3.py`. **Schreibt nichts in die DB** — reine Analyse.\n") + + # Überblick + w("## 1. Überblick\n") + w("| Datensatz | RennmausPro III | Aktueller Stand (DB) |") + w("|---|---:|---:|") + w(f"| Eigene Tiere (stamm) | {len(own)} | {sum(1 for x in cur['gerbils'] if x.get('isResident'))} (isResident) / {len(cur['gerbils'])} gesamt |") + w(f"| Externe Ahnen (fremd, roh) | {len(ext)} | — |") + w(f"| Würfe | {len(R['wurf'])} | {len(cur['litters'])} |") + w(f"| Kontakte (Herkunft+Abnehmer) | {len(R['herk'])} (+Abnehmer) | {len(cur['contacts'])} |") + w(f"| Genotypen hinterlegt | {R['n_color_stamm']} eigen / {R['n_color_ext']} extern | {sum(1 for x in cur['gerbils'] if x.get('genotype'))} |") + w("") + + # Teil A: Dedup + collapsed = sum(len(v) for v in merge_clusters.values()) + unique_after = collapsed - len(merge_clusters) + w("## 2. RPRO3-interne Dubletten (Teil A)\n") + w("RennmausPro erkennt beim Import vorhandene Tiere nicht → dasselbe Tier kommt mehrfach vor. " + "Zusammenlegung anhand **Name + Geburtsdatum + Farbe + Herkunft**.\n") + w(f"- **{len(merge_clusters)} sichere Cluster** fassen **{collapsed} Datensätze** zu **{len(merge_clusters)} Tieren** zusammen " + f"→ **{unique_after} Dubletten** fallen weg.") + w(f"- **{len(ambiguous)} mehrdeutige Namen** (gleicher Name → mehrere Varianten, manuelle Entscheidung nötig, s. u.).") + ph = Counter(norm_name(a['name']) for a in placeholders) + w(f"- **{len(placeholders)} Platzhalter-Namen** ohne Identität (nicht dedupbar): {dict(ph.most_common(6))} …") + w("") + + # größte sichere Cluster + w("### 2a. Größte sichere Zusammenlegungen (Top 25)\n") + w("| Tier | #Datensätze | DOB | Farbe | Herkunft |") + w("|---|---:|---|---|---|") + big = sorted(merge_clusters.values(), key=lambda v: -len(v))[:25] + for v in big: + a = v[0] + dobs = sorted({iso(x["dob"]) for x in v if x["dob"]}) + farben = sorted({x["farbe"] for x in v if x["farbe"]}) + orig = sorted({x["origin"] for x in v if x["origin"]}) + w(f"| {a['name']} | {len(v)} | {', '.join(dobs) or '—'} | {', '.join(farben)[:40] or '—'} | {', '.join(orig)[:40] or '—'} |") + w("") + + # mehrdeutige Namen + w("### 2b. Mehrdeutige Namen — bitte entscheiden (gleicher Name, mehrere Varianten)\n") + w("Pro Name die nach sicherem Merge verbliebenen **Varianten**. Frage je Zeile: *Ist das dasselbe Tier " + "(→ zusammenlegen) oder verschiedene Tiere gleichen Namens (→ getrennt lassen)?* " + "★ = eigenes Tier (stamm). Top 60 nach Variantenzahl.\n") + w("Merkmalslose Datensätze (kein DOB, keine Farbe, keine Herkunft) sind als „**N× nur Name**\" " + "zusammengefasst. Sortiert nach Anzahl **informativer** Varianten.\n") + w("| Name | Informative Varianten (Anzahl · DOB · Farbe · Herkunft) | nur Name |") + w("|---|---|---:|") + # nach Zahl informativer Varianten sortieren + def split_variants(variants): + info, bare = [], 0 + for v in variants: + if v["dob"] or v["farbe"] or v["origin"]: + info.append(v) + else: + bare += v["n"] + return info, bare + enriched = [(nm, *split_variants(vs)) for nm, vs in ambiguous] + enriched.sort(key=lambda x: (-len(x[1]), -x[2])) + shown = [e for e in enriched if len(e[1]) >= 1][:60] + for nm, info, bare in shown: + parts = [] + for v in info: + star = "★" if v["is_own"] else "" + dob = ", ".join(v["dob"]) or "—" + farbe = ", ".join(v["farbe"])[:30] or "—" + orig = ", ".join(v["origin"])[:30] or "—" + parts.append(f"{star}{v['n']}× · {dob} · {farbe} · {orig}") + w(f"| {nm} | {'
'.join(parts) or '—'} | {bare or ''} |") + only_bare = sum(1 for e in enriched if len(e[1]) == 0) + w(f"\n_Zusätzlich {only_bare} Namen, bei denen **alle** Varianten merkmalslos sind " + f"(reine Namens-Dubletten ohne Unterscheidungsmerkmale)._") + w("") + + # Teil B: Abgleich eigene Tiere + w("## 3. Eigene Tiere — Abgleich gegen aktuellen Stand (Teil B)\n") + missing, parent_diffs, geno_diffs, dob_sex_diffs, matched = [], [], [], [], 0 + for a in own: + cands = cur["by_name"].get(norm_name(a["name"]), []) + if not cands: + missing.append(a); continue + # besten Kandidaten nach DOB wählen + best = cands[0] + if a["dob"]: + scored = sorted(cands, key=lambda x: abs(((parse_dob(x.get("dateOfBirth")) or datetime.date(1900,1,1)) - a["dob"]).days)) + best = scored[0] + matched += 1 + m_cur, f_cur = cur["parents"](best) + if (norm_name(a["mother"]) != norm_name(m_cur)) or (norm_name(a["father"]) != norm_name(f_cur)): + parent_diffs.append((a, m_cur, f_cur)) + cg = norm_geno(best.get("genotype")) + rg = norm_geno(a["fcode"]) + if rg and cg != rg: + geno_diffs.append((a, best.get("genotype"))) + cdob = parse_dob(best.get("dateOfBirth")) + if a["dob"] and cdob and abs((a["dob"]-cdob).days) > 14: + dob_sex_diffs.append((a, cdob)) + + w(f"- Gematcht (Name): **{matched}/{len(own)}**") + w(f"- **Fehlen im aktuellen Stand: {len(missing)}**") + w(f"- **Eltern unterschiedlich: {len(parent_diffs)}**") + w(f"- **Genotyp unterschiedlich/aktuell ohne: {len(geno_diffs)}**") + w(f"- **Geburtsdatum weicht >14 Tage ab: {len(dob_sex_diffs)}**") + w("") + + w("### 3a. Eltern unterschiedlich (RPRO3 = autoritativ)\n") + w("| Tier | DOB | RPRO3 Mutter × Vater | Aktuell Mutter × Vater |") + w("|---|---|---|---|") + for a, mc, fc in parent_diffs[:120]: + w(f"| {a['name']} | {iso(a['dob'])} | {a['mother'] or '—'} × {a['father'] or '—'} | {mc or '—'} × {fc or '—'} |") + if len(parent_diffs) > 120: + w(f"\n_… und {len(parent_diffs)-120} weitere._") + w("") + + w("### 3b. Eigene Tiere, die im aktuellen Stand fehlen\n") + w("| Tier | DOB | Geschlecht | Farbe | Herkunft | ZB |") + w("|---|---|---|---|---|---|") + for a in missing[:120]: + w(f"| {a['name']} | {iso(a['dob'])} | {a['sex']} | {a['farbe'] or '—'} | {a['origin'] or '—'} | {a['zb'] or '—'} |") + if len(missing) > 120: + w(f"\n_… und {len(missing)-120} weitere._") + w("") + + w("### 3c. Genotyp unterschiedlich / aktuell nicht vorhanden (Top 60)\n") + w("| Tier | RPRO3 Genotyp (autoritativ) | Aktuell |") + w("|---|---|---|") + for a, cg in geno_diffs[:60]: + w(f"| {a['name']} | `{a['fcode']}` | {('`'+cg+'`') if cg else '—'} |") + if len(geno_diffs) > 60: + w(f"\n_… und {len(geno_diffs)-60} weitere._") + w("") + + # Caveat + Empfehlung + w("## 4. Hinweise zum Matching & Empfehlung\n") + w("**Matching-Vorbehalt:** Der Abgleich in Abschnitt 3 vergleicht über den **Namen**. Der aktuelle " + "Stand führt Tiere oft mit Zucht-Suffix („… von den Kleinen Chaoten\", „of …\"), RPRO3 nur den " + "Kurznamen. Ein Teil der **180 „fehlend\"** ist daher nur ein Suffix-Mismatch, kein echtes Fehlen. " + "Der spätere Importer normalisiert Namen (Suffix abtrennen) und matcht zusätzlich über DOB+Farbe.\n") + w("**Kernbefund:** RPRO3 ist die **autoritative Quelle** — die echten Zuchtdaten der Software. Die " + "Eltern-Abweichungen (Abschnitt 3a) zeigen klar die bekannten xlsx-Heuristik-Fehler (z. B. Blacky = " + "*Bella × Snoopy* statt fälschlich *Yumi × Flori*). Ein Import aus RPRO3 behebt viele dieser Fehler " + "an der Wurzel.\n") + w("**Empfohlenes Vorgehen für den Import:**") + w("1. RPRO3-interne Dubletten **vor** dem Laden zusammenlegen (Abschnitt 2; merkmalslose Namens-Dubletten zu 1 Tier).") + w("2. Mehrdeutige Namen (2b) der Züchterin zur Entscheidung vorlegen.") + w("3. Eigene Tiere + Eltern + Genotypen + Würfe + Kontakte aus RPRO3 als **führend** übernehmen.") + w("4. Externe Ahnen (nach Dedup) für tiefe Stammbäume ergänzen.") + w("") + + text = "\n".join(L) + if out: + open(out, "w", encoding="utf-8").write(text) + print(f"Report geschrieben: {out} ({len(text)} Zeichen)") + else: + print(text) + +if __name__ == "__main__": + main() diff --git a/tools/import/rpro3-feature-gap.md b/tools/import/rpro3-feature-gap.md new file mode 100644 index 0000000..90baf24 --- /dev/null +++ b/tools/import/rpro3-feature-gap.md @@ -0,0 +1,90 @@ +# RennmausPro III — Feature-Gap-Analyse vs. GerbilManager + +> Read-only-Untersuchung. Quelle: RPRO3-Backup `_rpro3.db` (SQLite, 43 Tabellen) vs. +> GerbilManager-IST (laufende API-Routen, `Models/`, `Endpoints/`, `src/pages/`). +> **Schreibt nichts in die DB** — reine Analyse. Datumsfelder in RPRO3 = Julianische Tageszahlen. + +## Management-Summary — die wichtigsten Lücken + +- **Verpaarungs-/Deck-Management (`zucht_tb`, 148 Zeilen)** fehlt komplett. RPRO3 dokumentiert je + Verpaarung Zusammensetzen → Decken → Trennen + Notiz und verknüpft sie mit dem entstandenen Wurf. + Das ist die zentrale aktive Zuchtarbeit, die wir gar nicht abbilden — **Top-Priorität**. +- **Welpen-Wiegekurven (`jungwaage_tb`, 1022 Zeilen!)** sind der mit Abstand am intensivsten genutzte + Datenbestand. Wir haben Gewicht je adultem Tier (`waage_tb`-Äquivalent), aber **keine wurfweisen + Welpen-Wiegekurven**. Größter Daten-Hebel, mittlerer Aufwand. +- **Krankheits-/Behandlungs-Historie ist bei uns generisch, RPRO3 deutlich reicher**: `krank_tb` + (Diagnose + Medikament je Termin), `dauerkrank_tb` (Dauermedikation mit Dosierung), `tod_tb` + (Todesursache strukturiert, 51 Zeilen), plus Tierarzt-Stammdaten (`arzt_tb`) und ein + Medikamenten-/Behandlungs-Nachschlagewerk (`notiz_tb`). +- **Gehege-Reinigungszyklus (`becken_tb` `_CLEANED/_CYCLUS/_NEXT`)** und **Tagebuch je Tier + (`diary_tb`)** fehlen — kleine Features mit hohem Alltagsnutzen (Quick Wins). +- **Erwerb/Preis je Tier (`herktier_tb`, 251 Zeilen)** und **Abgabe-/Reservierungs-Status + (`abgeben_tb`/`abstat_tb`/`getback_tb`)** sind in RPRO3 strukturiert; bei uns nur teilweise über + Verträge/Kontakte abgedeckt. **Rücknahmen** (`getback_tb`, Tier kommt zurück) fehlen ganz. +- **Vorhanden, aber von dieser Züchterin ungenutzt** (in RPRO3 leer): Finanzen + (`einnahme_/ausgabe_/artikel_/bestand_tb`), Ausstellungen/Auszeichnungen (`ausz_tb`), + Termin-Kalender (`termin_tb`). Niedrige Priorität — keine Migrationsdaten, kein belegter Bedarf. + +--- + +## Feature-Gap-Tabelle (sortiert: Quick Wins / hoher Nutzen zuerst) + +| # | Feature (RPRO3) | Was RPRO3 macht | Status bei uns | RPRO3-Tabelle(n) (Zeilen) | Nutzen | Aufwand | Empfehlung | +|---|---|---|---|---|---|---|---| +| 1 | **Welpen-Wiegekurven** | Gewichtsverlauf je **Welpe im Wurf**, viele Messpunkte über die Aufzucht | **Teilweise** — wir haben `WeightRecord` je Tier, aber keine wurfweise Welpen-Wiegung/-Kurve vor der Vergabe | `jungwaage_tb` (1022), `juwa_tb` (129, Sammeldaten) | Hoch — meistgenutzter Datentyp, Aufzuchtkontrolle | M | **Hoch** — WeightRecord auf Welpen/Litter erweitern + Kurven-Ansicht im Wurf | +| 2 | **Verpaarungs-/Deck-Management** | Je Verpaarung: Zusammensetzen (`_ZUS`), Decken (`_DECK`), Trennen (`_TRENN`), Notiz (`_BEM`), Verknüpfung zum Wurf (`_WID`) | **Fehlt** — wir haben nur den fertigen Wurf, nicht den Paarungsprozess | `zucht_tb` (148) | Hoch — Kern der aktiven Zuchtarbeit, Planung & Nachverfolgung | M–L | **Hoch** — neue Entity „Verpaarung/Pairing" mit Status-Zeitachse, an Litter gekoppelt | +| 3 | **Gehege-Reinigungszyklus** | Becken hat Reinigungsintervall (`_CYCLUS` Tage), letzte Reinigung (`_CLEANED`), nächste fällige (`_NEXT`) → Erinnerung; dazu Maße (`_SIZE`) + Kapazität (`_MENGE`) | **Teilweise** — `Enclosure` hat nur Name + Notes | `becken_tb` (6) | Hoch — konkreter Alltagsnutzen, wenig Daten | S | **Quick Win** — Felder Größe/Kapazität/Reinigungszyklus + „nächste Reinigung"-Hinweis auf Becken | +| 4 | **Tagebuch je Tier** | Datierte Freitext-Einträge je Tier (Verpaarungen, Beobachtungen, Vorfälle) | **Fehlt** — Gerbil hat nur ein `Notes`-Freitextfeld (kein datierter Verlauf) | `diary_tb` (14) | Hoch — laufende Beobachtungsdoku, ergänzt Provenance/Gesundheit | S | **Quick Win** — datierte Diary-Einträge je Gerbil (loses nullable GerbilId, wie Feedback) | +| 5 | **Todesursache strukturiert** | Eigene Tabelle: Todesdatum (`_AM`) + Ursache (`_WARUM`) je Tier | **Teilweise** — `Gerbil.DateOfDeath` + `CauseOfDeath` als Freitext vorhanden | `tod_tb` (51) | Mittel — schon weitgehend abgedeckt; ggf. Statistik nach Ursache | S | **Niedrig/Teilweise** — Felder existieren; optional Ursachen-Statistik | +| 6 | **Erwerb/Kauf je Tier** | Kaufdatum (`_DATE`) + Preis (`_PRICE`) + Notiz je zugekauftem Tier | **Fehlt** — Herkunft (Kontakt/Breeder) ja, aber kein Erwerbsdatum/-preis | `herktier_tb` (251) | Mittel — Anschaffungskosten, Belege | S | **Quick Win** — Felder PurchaseDate/PurchasePrice auf Gerbil | +| 7 | **Krankheits-Historie (reich)** | Je Eintrag: Datum, Diagnose (`_BEM`), Medikament+Kosten (`_MED`), Tierarzt-Ref (`_AZID`) | **Teilweise** — `HealthRecord` hat Date/Type/Description/Veterinarian, aber **kein Medikament/Dosis-Feld, kein Tierarzt als Stammdatensatz** | `krank_tb` (27) | Mittel-Hoch — vollständige Behandlungsdoku | M | **Mittel** — HealthRecord um Medikament/Kosten + Arzt-FK erweitern | +| 8 | **Dauermedikation** | Laufende Medikation mit Dosierung (`_DOS`, z. B. „1× täglich · 0,1 ml"), Dauer (`_DAUER`), Wirkstoff (`_ARZN`), Enddatum (`_END`) | **Fehlt** | `dauerkrank_tb` (27) | Mittel — chronisch kranke Tiere, Behandlungsplan | M | **Mittel** — eigener HealthRecord-Typ „Dauermedikation" mit Dosierung/Dauer | +| 9 | **Tierarzt-Stammdaten** | Adressbuch für Tierärzte (eigene Tabelle, Schema wie Kontakte) | **Fehlt** (Tabelle leer, aber Schema da; `krank_tb._AZID` referenziert) | `arzt_tb` (0, vorhanden-ungenutzt) | Mittel — referenzierbar in Gesundheitseinträgen | S | **Mittel** — Kontakt-Rolle „Tierarzt" wiederverwenden statt eigener Tabelle | +| 10 | **Abgabe-/Reservierungs-Status** | Reservierung je (Jung-)Tier (`abgeben_tb`: reserviert `_RES`, Termin, Abnehmer); Abgabe-Status mit Preis/Datum/Notiz (`abstat_tb`) | **Teilweise** — `GoHomeDate`, ReceiverContact, Verträge vorhanden; aber **kein expliziter Reservierungs-Workflow vor Abgabe** | `abgeben_tb` (21), `abstat_tb` (23) | Mittel-Hoch — Vergabe-Pipeline (reserviert→abgegeben) | M | **Mittel** — Status „reserviert" + Reservierungs-Felder, ggf. an Anfragen koppeln | +| 11 | **Rücknahmen** | Tier kommt zurück: Rückgabedatum (`_ZAM`), Rückgabepreis, ursprüngl. Abgabe, Abnehmer, Grund (`_BEM`) | **Fehlt** | `getback_tb` (17) | Mittel — Wiederaufnahme ins Bestand, Historie | S–M | **Mittel** — Rücknahme-Event je Gerbil (Datum, Grund, Abnehmer) | +| 12 | **Nachfrage/Warteliste** | Interessenten-Vormerkung: gewünschte Farbe/Geschlecht/Anzahl, Abnehmer, Datum, Status | **Teilweise** — Anfragen-Inbox (`/api/requests`) vorhanden, aber **keine strukturierte Warteliste nach Wunschkriterien** | `nachfrage_tb` (2) | Mittel — Matching Wurf ↔ Interessent | M | **Mittel** — Wunschkriterien an Anfragen/Kontakt, Match-Vorschlag bei neuem Wurf | +| 13 | **Medikamenten-/Behandlungs-Nachschlagewerk** | Notizbuch: Medikament + Anwendungsart (`_KAT`, z. B. „oral", „eintropfen") + datierte Notiz (HTML) | **Fehlt** | `notiz_tb` (13) | Niedrig-Mittel — Wissensspeicher | S | **Niedrig** — ggf. als statische Hilfeseite/Notizen | +| 14 | **Verpaarungs-Vorschau / Genotyp-Erwartung** | Hinterlegte Verpaarungs-Regel: erwartete Nachkommen-Farben aus Eltern-Genotyp (`zugru_tb`: Bezeichnung, erwartete Farben-Liste, Bemerkung) | **Teilweise** — wir haben Genetik-Seite + Inbreeding-Rechner + `test-inbreeding`; **erwartete Farbverteilung einer Verpaarung** nicht als gespeicherte Vorschau | `zugru_tb` (1) | Mittel — Zuchtplanung | M | **Mittel** — Punnett/Farb-Erwartung in Genetik-Seite zur Verpaarung anzeigen | +| 15 | **Ausstellungen/Auszeichnungen** | Je Tier: Veranstaltung, Datum, Ort, Platzierung, Auszeichnung, Juror | **Fehlt** | `ausz_tb` (0, vorhanden-ungenutzt) | Niedrig — von dieser Züchterin nicht genutzt | M | **Niedrig** — nur bei konkretem Bedarf | +| 16 | **Termin-/Kalender** | Termine mit Priorität, Kategorie, Datum/Zeit, Tier-Ref | **Fehlt** | `termin_tb` (0, vorhanden-ungenutzt) | Niedrig-Mittel — Würfe/Reinigung könnten Termine speisen | M | **Niedrig** — erst sinnvoll mit Verpaarung+Reinigung als Quelle | +| 17 | **Finanzen (Einnahmen/Ausgaben/Artikel/Bestand)** | Einnahmen, Ausgaben, Artikel-Katalog mit Nachbestell-/Mindestmenge, Lagerbestand | **Fehlt** | `einnahme_tb`/`ausgabe_tb`/`artikel_tb`/`bestand_tb` (alle 0, vorhanden-ungenutzt) | Niedrig — kein belegter Bedarf | L | **Niedrig** — bewusst nicht genutzt; nicht bauen | +| 18 | **Übersetzungs-/Lokalisierungstabelle** | RPRO3-interne Mehrsprachen-Strings | n/a — wir nutzen `strings/de.ts` | `trans_tb` (0) | — | — | **Nicht relevant** | +| 19 | **Favoriten-Farbschläge / Zuchtziel-Farben** | Markierte Wunsch-/Zielfarben (`fav_tb`, Schema wie `color_tb`) | **Fehlt** (ungenutzt) | `fav_tb` (0, vorhanden-ungenutzt) | Niedrig | S | **Niedrig** — optional Farbkatalog-Favoriten | + +### Bereits abgedeckt (kein Gap) + +| Bereich | RPRO3 | GerbilManager | +|---|---|---| +| Tiere (eigen/extern) | `stamm_tb` (303), `fremd_tb` (7427) | Gerbil + IsResident, `/gerbils` | +| Würfe | `wurf_tb` (127), `wurftier_tb` (562) | Litter, `/litters`, Wurf-Seiten | +| Stammbaum/Eltern | `baum_tb` (436) | Stammbaum-Viewer, Litter→Vater/Mutter | +| Genotyp/Farbschlag | `color_tb`/`wurfcolor_tb`/`fremdcolor_tb` | ColorVariety, Genotype-String, Farbkatalog, Genetik | +| Inzucht | (nicht explizit) | `/gerbils/{id}/inbreeding-coefficient`, `/genetics/test-inbreeding` — **wir können hier mehr** | +| Kontakte (Herkunft/Abnehmer) | `herk_tb` (645), `abn_tb` (105) | Contact, `/contacts` | +| Verträge | (RPRO3: nur `_VERTRAG`-Flag in `not_tb`) | SaleContract, Generator, PDF — **wir können hier mehr** | +| Fotos | `photo_tb` (111) | GerbilPhoto/EnclosurePhoto | +| Gewicht adult | `waage_tb` (229) | WeightRecord | +| Gehege (Grunddaten) | `becken_tb` | Enclosure (ohne Reinigungszyklus, s. #3) | +| Statistik | `prop_tb` (Wurf-Stats 2,1 etc.) | StatistikPage | +| Öffentliche Webseite/CMS | — (RPRO3 kann das nicht) | CMS, `/api/pages`, Publish — **wir können hier mehr** | +| Feedback/Tickets | — | Feedback, TicketsPage — **wir können hier mehr** | + +--- + +## Anmerkungen zur Datendeutung + +- `not_tb` (12) = **Notfall-/Pflegetiere** (Tiere nur vorübergehend in Obhut: `_NOTRUF`, `_BIS`, + `_VERTRAG`, `_ZUBEHOER`). Grenzfall zu Abgabe; bei Bedarf als Gerbil-Status „Pflege/Notfall" abbildbar. +- `prop_tb` (136) = Wurfgrößen-Statistik je Wurf (z. B. „3 (2,1)" = 3 Welpen, 2♀/1♂). Statistik haben wir. +- `info_tb` (19) = App-Konfiguration (Default-Abnehmer/Arzt/Clan-Name) → bei uns BreederSettings. +- `juwa_tb` (129) vs. `jungwaage_tb` (1022): `juwa_tb` ist die ältere Sammel-/Aggregatform + (Datums-Liste in einem Feld), `jungwaage_tb` die normalisierte Einzelmessung. Für #1 ist `jungwaage_tb` führend. +- `herk_tb` enthält **Züchter (Herkunft)**, `abn_tb` **Abnehmer** — bei uns ein vereinheitlichter Contact mit Rolle. + +## Quick-Win-Empfehlung (Reihenfolge) + +1. **Gehege-Reinigungszyklus** (#3, S) — wenig Daten, sofortiger Alltagsnutzen. +2. **Tagebuch je Tier** (#4, S) — Muster „loses nullable GerbilId" wie Feedback. +3. **Erwerb/Preis je Tier** (#6, S) — 2 Felder auf Gerbil + 251 importierbare Zeilen. +4. **Welpen-Wiegekurven** (#1, M) — größter Datenbestand (1022 Zeilen), hoher Nutzen. +5. **Verpaarungs-Management** (#2, M–L) — strategisch wichtigste Lücke. diff --git a/tools/import/rpro3-vergleich.md b/tools/import/rpro3-vergleich.md new file mode 100644 index 0000000..f485f5e --- /dev/null +++ b/tools/import/rpro3-vergleich.md @@ -0,0 +1,449 @@ +# RennmausPro III — Abgleich- & Dedup-Report + +> Erzeugt von `tools/import/compare_rpro3.py`. **Schreibt nichts in die DB** — reine Analyse. + +## 1. Überblick + +| Datensatz | RennmausPro III | Aktueller Stand (DB) | +|---|---:|---:| +| Eigene Tiere (stamm) | 303 | 1712 (isResident) / 2352 gesamt | +| Externe Ahnen (fremd, roh) | 7427 | — | +| Würfe | 127 | 904 | +| Kontakte (Herkunft+Abnehmer) | 645 (+Abnehmer) | 878 | +| Genotypen hinterlegt | 302 eigen / 7430 extern | 944 | + +## 2. RPRO3-interne Dubletten (Teil A) + +RennmausPro erkennt beim Import vorhandene Tiere nicht → dasselbe Tier kommt mehrfach vor. Zusammenlegung anhand **Name + Geburtsdatum + Farbe + Herkunft**. + +- **803 sichere Cluster** fassen **4947 Datensätze** zu **803 Tieren** zusammen → **4144 Dubletten** fallen weg. +- **428 mehrdeutige Namen** (gleicher Name → mehrere Varianten, manuelle Entscheidung nötig, s. u.). +- **169 Platzhalter-Namen** ohne Identität (nicht dedupbar): {'unbekannt': 128, '-': 33, '': 5, '?': 3} … + +### 2a. Größte sichere Zusammenlegungen (Top 25) + +| Tier | #Datensätze | DOB | Farbe | Herkunft | +|---|---:|---|---|---| +| Alice | 30 | 2010-07-19 | Schimmel | Clan of Kleine Füchse, unbek. | +| BlackEye | 28 | — | Silberschimmel | Clan of Desert, unbek. | +| Gwendolin | 27 | — | Schwarz-Schecke | Kieger mit Krallen, unbek. | +| Panda of Queensland [Sls] | 26 | — | Schwarz [Sls] | unbek. | +| Nosey of Queensland | 26 | — | Agouti-Schecke | unbek. | +| Never ever Summer | 26 | — | Colourpoint-Agouti | unbek. | +| Waterproof Wizzard of old Birch | 26 | — | Schwarz [HW] | unbek. | +| Devil wears Prada of Burrow | 26 | — | Schwarz-Schecke [Sls] | unbek. | +| Clyde | 26 | — | Agouti | unbek. | +| Schneewittchen | 26 | — | Orangeschimmel | Clan of KleineFüchse, unbek. | +| Samuel | 26 | — | Orangeschimmel | Privatzüchter Marcel T., unbek. | +| Ghost's Hermes [DP] | 25 | — | Anthrazit-Schecke [DP] | unbek. | +| Ghost's Faith [DP] | 25 | — | Anthrazit-Schecke [DP] | unbek. | +| Ghost's Chupacabra | 25 | — | Anthrazit-Schecke | unbek. | +| Ghost's Ying Yang [DP] | 25 | — | Anthrazit-Schecke [DP] | unbek. | +| Ghost's Herne | 25 | — | Zobel-Schecke | unbek. | +| Ghost's Lamia | 25 | — | Zobel | unbek. | +| Infinity of Vitality | 25 | 2009-07-07 | Zobel-Schecke | unbek. | +| Hot Chili Pepper of old Birch [HW] | 25 | — | Colourpoint-Agouti [HW] | unbek. | +| Alinghi of Burrow | 25 | 2007-05-05 | Agouti [HW] | unbek. | +| Burbon Whiskey of Pocket Fox | 25 | — | Marder | unbek. | +| Grizzly of Vitality | 25 | — | Agouti | unbek. | +| Mirja of Vitality | 25 | — | Schwarz | unbek. | +| Yukon of Desert | 25 | 2007-03-16 | Polarfuchsschimmel | unbek. | +| Bonnie | 25 | — | Agouti | unbek. | + +### 2b. Mehrdeutige Namen — bitte entscheiden (gleicher Name, mehrere Varianten) + +Pro Name die nach sicherem Merge verbliebenen **Varianten**. Frage je Zeile: *Ist das dasselbe Tier (→ zusammenlegen) oder verschiedene Tiere gleichen Namens (→ getrennt lassen)?* ★ = eigenes Tier (stamm). Top 60 nach Variantenzahl. + +Merkmalslose Datensätze (kein DOB, keine Farbe, keine Herkunft) sind als „**N× nur Name**" zusammengefasst. Sortiert nach Anzahl **informativer** Varianten. + +| Name | Informative Varianten (Anzahl · DOB · Farbe · Herkunft) | nur Name | +|---|---|---:| +| Merlin | 4× · 2007-08-22 · Blaufuchs · Clan of the Yankees
3× · — · Platin-Schecke · —
2× · 2004-06-26 · Marder-Schecke · Fernwehspatz
2× · — · Platin-Hell-Schecke · von Steffilein
1× · 2021-02-10 · Silberagouti-Kragenschecke · Clan of little Angels
1× · — · Colourpoint-Algierfuchs · Ulmer Strolche
1× · 2000-04-09 · Schwarz · Clan of Cherokee
1× · 2012-12-01 · Platin-Schecke DP · Zoohandlung
1× · 2012-08-07 · Silberagouti-Kragenschecke · Clan of little Angels | | +| Bella | 2× · 2012-06-21 · Blaufuchs · Clan of Sunset Glow
1× · 2012-04-10 · Blaufuchs · Svenja Müller
1× · — · Schwarz · Seligenstädter Privatzucht
1× · 2012-04-10 · Blaufuchs · Clan of Samsimar
★1× · 2013-03-20 · Algierfuchs, hell · eigene Zucht
1× · 2012-06-21 · Blaufuchs · Anja Neumann
1× · 2004-02-03 · Algierfuchs, hell · von Knuffeline
1× · — · Colourpoint-Kohlfuchs-Schecke · — | | +| Silver | 5× · — · Colourpoint-Silberagouti-Schec · —
1× · — · Dilute Silberagouti · Moonlight/Piskomilo
1× · 2003-07-19 · Colourpoint-Algierfuchs-Hell · —
1× · — · Colourpoint-Silberagouti · Clan of little Soldiers
★1× · 2013-03-20 · Silberagouti · eigene Zucht
1× · 2012-03-12 · Colourpoint-Silberagouti-Hell · Svenja Müller
1× · 2012-03-12 · Colourpoint-Silberagouti · Svenja Müller
1× · 2005-03-02 · Polarfuchs · Clan of vitamine s | | +| Charly | 20× · — · Colourpoint-Silberagouti · —
5× · 2009-09-27 · Agouti · Clan von den Rastatter Fellnas
2× · — · Apricot · Rat(t)fred
2× · 2011-11-22 · Apricot · Clan of Ratfrett
2× · 2009-09-27 · WFNZ-Agouti · Christine Enders
1× · 2016-04-05 · Marder-Schecke · —
1× · — · Colourpoint-Blaufuchs · —
1× · 2015-06-05 · Agouti-Schecke · Kleinen Chaoten | | +| Ruby | 3× · 2009-03-19 · Marder · —
2× · 2011-11-05 · Marder-Hell · Stefpanie Knötsch
1× · 2012-01-24 · Schwarz · —
1× · 2013-01-24 · Schwarz · Anja Neumann
1× · 2009-03-10 · Marder · Clan of little Beauties
1× · — · Apricot-Hell-Schecke · Privatzüchterin Ashanti
1× · 2015-03-27 · Kohlfuchs, hell · Kleinen Chaoten
1× · 2011-11-05 · Marder-Hell · Clan of KK Chaos | | +| Sam | 3× · — · Agouti · Clan of Desert
3× · — · Marder-Hell · Clan of Gerbils
1× · — · Schwarz · Privatzüchterin Sabine H.
1× · 2006-08-19 · Agouti · Privatzucht
★1× · 2011-10-13 · Schwarz-Schecke · eigene Zucht
1× · — · Marder-Hell · Privatzüchterin Andrea T.
1× · 2013-01-07 · Blau-Schecke · Zucht der bunten Fellnasen | | +| Felix | 4× · 2009-04-21 · Marder-Schecke · Clan of Avalon
4× · — · Kohlfuchsschimmel · von den Kriegern mit Krallen
1× · — · Algierfuchs-Hell · —
1× · 2011-04-30 · Kohlfuchsschimmel · Kieger mit Krallen
1× · 2009-02-21 · Marder-Schecke · Clan of Happy Mice
1× · — · Schwarz · Gründauerer Privatzucht
1× · — · Schwarz · Privatzüchterin Sabine H. | | +| Sunny | 4× · — · Schwarz-Schecke · Privat
3× · — · Schwarz · —
2× · 2004-04-30 · Kohlfuchs, hell · —
2× · 2004-09-30 · Blaufuchsschimmel-Schecke · —
1× · 2004-09-27 · Blaufuchsschimmel-Schecke · Clan of Sarima
★1× · — · Gold · Privat | | +| . | 1× · — · Agouti-Schecke · —
1× · — · Schwarz · —
1× · — · Marder · —
1× · — · Zobel · —
1× · — · Kohlfuchs, hell · —
1× · — · Colourpoint-Algierfuchs-Hell · — | | +| Luna | 6× · — · Colourpoint-Kohlfuchs-Hell · Privatzucht Mettmann
2× · 2011-06-01 · Silberagouti-Schecke · Rennmauszucht kleine Renner
1× · — · Marder · Privatzüchterin Andrea T.
1× · 2010-04-23 · Marder · Privat
1× · — · Zobel · Clan of Cervinorum
1× · 2002-07-05 · Marder-Schecke · Clan of Desert | | +| Namenlos | 6× · 2009-05-18 · Agouti · —
4× · — · Schwarz · —
4× · — · Marder-Schecke · —
3× · 2009-03-10 · Schwarz-Schecke · —
2× · — · Marder-Hell-Schecke · —
2× · — · Marder · — | | +| Blacky | 1× · — · Schwarz · Privatzüchterin Andrea T.
1× · — · Schwarz · Erfurter Privatzucht
1× · 2013-02-01 · Schwarz Sls [WP] · v. Sabine P.
★1× · 2009-07-23 · Schwarz · Seligenstädter Privatzucht
1× · — · Schwarz-Starkschecke DP · Privatzüchter Jürgen K. | | +| Gina | 4× · 2009-10-18 · Agouti · Clan of Strelitzie
★1× · 2011-09-09 · Marder-Schecke · Zucht der bunten Fellnasen
1× · 2010-01-01 · Kohlfuchs · —
1× · 2012-05-05 · Marder · Clan of Samsimar
1× · 2010-01-01 · Kohlfuchs, hell · — | | +| Sky | ★2× · 2013-08-15 · Blaufuchs · Chalfont Stud
1× · 2004-01-27 · Polarfuchs · von Steffi
1× · 2008-05-25 · Blaufuchs · —
1× · — · Agouti · Privatzucht
1× · 2015-01-19 · Blau-Schecke · Privatzüchter Jürgen K. | | +| Snow | 6× · 2014-12-21 · Polarfuchs · Michael Lengowski
5× · — · unbekannte Farbe · —
1× · 2011-07-12 · Colourpoint-Kohlfuchs · Svenja Müller
★1× · 2018-10-26 · Silberagouti-Schecke · Kleinen Chaoten
1× · 2011-07-12 · Colourpoint-Kohlfuchs-Hell · Svenja Müller | | +| Maja | 2× · 2013-05-19 · Silberagouti · Freddy Braun
2× · — · Algierfuchs-Schecke · —
1× · 2013-05-19 · Silberagouti · Chalfont Stud
1× · — · — · Privat aus München
1× · 2006-09-19 · Agouti-Starkschecke · Clan of Glen Coe | | +| Balu | 1× · 2008-03-20 · Marder-Hell · —
1× · 2012-03-26 · Schwarz · Svenja Müller
1× · 2007-01-10 · Blaufuchs · Züchter aus NL
1× · 2007-03-29 · Marder-Hell-Kragenschecke · Rennmauszucht von 1Anja1 in FF
1× · 2007-03-20 · Marder-Hell · — | | +| Fee | 6× · 2004-05-02 · Marder-Hell · —
4× · 2008-08-23 · Polarfuchsschimmel-Schecke · Clan of Vitality
1× · 2006-08-06 · Silberagouti · Clan of Kinghouse (Schwester)
1× · 2012-06-04 · dilute-Blaufuchs · Clan of Nagenasen
1× · — · Agouti · Clan of Curious Furs | | +| Paula | 2× · 2007-08-25 · Anthrazit-Schecke · —
1× · 2004-08-01 · Schwarz-Schecke · —
1× · 2007-08-25 · Anthrazit · Clan of Happy Mice
1× · — · Kohlfuchs, hell · —
1× · — · Agouti · Züchter aus NL | | +| Kira | 2× · 2009-06-14 · Algierfuchs · Clan of Happy Mice
2× · 2008-07-14 · Colourpoint-Agouti-Hell · private Zucht
1× · — · Kohlfuchs · —
1× · 2008-07-14 · Colourpoint-Agouti-Hell · Clan of Fiomi
1× · — · Agouti-Schecke · Clan of Vitality | | +| Filou | 1× · — · Blaufuchs-Schecke · —
1× · — · Agouti · —
1× · 2014-11-24 · Silberagouti · Kleinen Chaoten
1× · 2009-01-29 · Schwarz-Schecke · The little warrior with Claws
1× · 2012-08-31 · Blau · Fam.Wendt | | +| Pünktchen | 8× · — · Rotaugen-Weiß · The little warrior with Claws
5× · — · unbekannte Farbe · —
1× · 2006-05-12 · Anthrazit · —
1× · 2010-10-12 · Anthrazit-Starkschecke DP · Clan of Braveheart
1× · 2006-05-12 · Marder-Schecke · Baru of Lienehma | | +| Pegasus | 12× · — · Goldfuchs, hell-Schecke · agouti(s) sind net(t)
4× · 2008-02-27 · Algierfuchs Hell · —
3× · 2008-01-28 · Goldfuchs, hell Schimmel-Schec · Estra Dragon (DE)
1× · — · Goldfuchs Schecke · of Estra Dragon
1× · 2008-01-28 · Goldfuchs, hell-Schecke · Ag(o)uti('s sind) net(t) | | +| Anton | 5× · — · unbekannte Farbe · —
4× · — · Anthrazit-Schecke · —
3× · 2007-07-31 · Agouti · Clan of Ben und Lulu
1× · 2007-07-30 · Agouti · Clan of Ben und Lulu
1× · 2007-03-21 · WFNZ Agouti · Clan of Ben und Lulu | | +| Fiona | 1× · — · — · Privat
1× · — · Agouti · Gründauerer Privatzucht
1× · 2007-07-01 · Polarfuchs · Clan of Glen Coe
1× · — · Kohlfuchsschimmel · Privatzucht | 23 | +| Elvis | 1× · — · — · Privatzucht
1× · — · Marder · Clan of sweet little mouse
1× · — · Algierfuchs · von Sylvia Ljubic
1× · — · Agouti-Schecke · — | 20 | +| Jamie | 3× · 2002-06-26 · Algierfuchs · Clan of Sorceress
2× · 2010-01-02 · Agouti-Schecke · Clan of Happy Mice
1× · 2006-07-26 · Zobel · Clan of Silvermoon
★1× · 2010-12-27 · Zobel · eigene Zucht | | +| Mary | 2× · 2011-03-14 · Silberagouti · Anja Neumann
★1× · 2011-09-13 · Agouti-Schecke · Seligenstädter Privatzucht
1× · 2006-06-05 · Blau · —
1× · 2011-03-14 · Silberagouti · Clan of Sunset Glow | | +| Jack | 4× · 2006-09-26 · Schwarz · Privatzucht
1× · 2013-09-05 · Blaufuchs · Chalfont Stud
1× · 2009-01-01 · Gold-Starkschecke · Privat
★1× · 2012-03-20 · Colourpoint-Agouti-Hell · Maintaler Privatzucht | | +| Yuki | 1× · 2009-09-06 · dilute-Polarfuchs · Clan of little fellows
★1× · 2012-12-17 · Algierfuchsschimmel · eigene Zucht
1× · — · Colourpoint-Agouti-Schecke · —
1× · 2008-11-18 · Polarfuchs · Clan of Tamyna | | +| Raya | 1× · 2009-08-28 · Colourpoint-Blaufuchs · Clan of Sunset Glow
1× · — · CP Fuchs · Anja Neumann
★1× · 2013-02-02 · Schimmel · Zucht der bunten Fellnasen
1× · — · Colourpoint-Agouti-Hell-Scheck · Clan of blue eyes | | +| Amy | 3× · 2011-08-17 · Anthrazit · Clan of Vitality
3× · 2012-01-02 · Algierfuchs · Svenja Müller
1× · 2011-08-17 · Anthrazit · Clan of Vitality (CH)
★1× · 2013-02-16 · Algierfuchsschimmel · eigene Zucht | | +| Lilly | 1× · 2007-09-28 · Polarfuchs-Schecke · Clan of little Soldiers
1× · 2015-05-15 · Blau · Ulmer Strolche
★1× · 2012-09-22 · Dilute-Kohlfuchs-Schecke · Clan of Ulmer Strolche
1× · 2009-07-06 · Schwarz · Miris Renner | | +| Yuna | 2× · — · Schwarz-Schecke · of Camelot
★1× · 2013-08-23 · Schimmel · eigene Zucht
1× · — · Schwarz-Schecke · Clan of Camelot
1× · 2014-03-15 · Marder-Hell-Schecke · Clan of sweet little mouse | | +| Blue | 4× · 2008-06-14 · Blaufuchs · Clan of Vitality
1× · 2011-01-01 · Blau · —
★1× · 2014-01-14 · Blaufuchsschimmel-hell-Schecke · eigene Zucht
1× · 2015-05-07 · Blau, meliert · Sarah Wörz | | +| Sheila | 4× · 2008-02-06 · C-Separator-Schecke · Estra Dragon (DE)
1× · — · Schwarz-Schecke · Clan of Ulmer Strolche
★1× · 2014-01-08 · Algierfuchs · Clan of Lennylengo
1× · 2011-11-10 · Algierfuchs · Clan of Lennylengo | | +| Zora | 4× · 2005-03-28 · Zobel · —
1× · 2014-10-12 · Blaufuchs-Kragenschecke · Kleinen Chaoten
★1× · 2015-11-06 · Blaufuchs-Schecke · eigene Zucht
1× · — · Agouti WFNZ · Clan of Lennylengo | | +| Clyde | 26× · — · Agouti · —
1× · — · Goldfuchs-Hell-Schecke · Privatzüchter Riko
1× · — · Schwarz-Schecke · Privat
1× · 2010-02-28 · Polarfuchs · — | | +| Bonny | 1× · — · Blaufuchs · Chalfont Stud
1× · 2006-01-19 · Agouti-Schecke · Clan of little Soldiers
1× · 2006-04-19 · Blaufuchs-Schecke · Clan of little Soldiers
1× · 2008-06-25 · Marder-Schecke · — | | +| Pearl | 1× · 2008-02-11 · dilute-Marder · Clan of Tamyna
1× · 2009-06-10 · Dilute Marder · Clan of Zauberwald
1× · — · Platin-Hell-Schecke · Fanny Van Rysselberghe
1× · — · Goldfuchs-Schecke · — | | +| Chap | 5× · — · unbekannte Farbe · —
4× · — · Agouti · —
1× · 2011-06-03 · Anthrazit-Schecke · Clan of little Angels
1× · 2011-07-26 · WFNZ-Agouti · Jessica Waldesbühl | | +| Fips | 3× · — · Schwarz-Schecke · Clan of Blue Balu
2× · 2010-02-07 · Marder · Clan of little Snugglers
1× · 2008-03-01 · Blaufuchs-Schecke · Clan of Kinghouse
1× · 2010-11-03 · dilute-Agouti · Clan of little fellows | | +| Amadeus | 3× · 2007-10-06 · Zobel-Hell · —
3× · 2007-03-05 · Marder-Schecke · —
1× · 2012-03-26 · Marder · Clan of sweet little mouse
1× · 2008-11-26 · Zobel-Hell · Clan of Westwind | | +| Aisha | 23× · — · WFNZ-Agouti · Christine Enders
6× · 2005-01-05 · Blau · von Alicia
5× · 2008-09-16 · Agouti · Clan von den Rastatter Fellnas
1× · 2011-03-27 · Marder-Hell-Schecke DP · Clan of little Snugglers | | +| Daisy | 1× · 2006-08-27 · Colourpoint-Polarfuchs · Clan of little Soldiers
1× · 2010-02-15 · Platin-Hell · Privatzucht
1× · — · Colourpoint-Agouti-Hell · Privatzucht
1× · 2010-02-15 · Platin · — | | +| Odin | 2× · — · Schwarz · —
2× · — · Zobel-Hell · Clan of little Beauties
1× · 2013-06-26 · Algierfuchs Schecke · Clan of Vitality
1× · 2008-01-09 · Colourpoint-Agouti-Schecke · Clan of Vitality | | +| Male | 2× · — · Polarfuchs · Zoohandlung
1× · — · Colourpoint-Agouti · —
1× · — · Schwarz-Schecke · —
1× · — · Saphir · Lems | | +| Female | 2× · — · Polarfuchs · Zoohandlung
1× · — · Agouti · —
1× · — · Marder · —
1× · — · Marder-Hell · — | | +| Ida | 2× · 2012-12-22 · Anthrazit · Clan of Sunset Glow
1× · 2010-10-01 · Silberagouti-hell · —
1× · 2012-12-22 · Anthrazit · Anja Neumann
1× · 2010-10-01 · Silberagouti · — | | +| Frodo | 3× · — · Anthrazit · Clan of Climbers
1× · 2014-11-28 · Kohlfuchs-Schecke · Berenice Schulz-Gebeltzig
1× · — · Marder · Clan of Tishtrya
1× · — · Anthrazit · Clan of Climbers (CH) | | +| Joy | 2× · 2005-09-05 · Platin · Clan of little Paw
2× · 2010-06-12 · Schwarz-Schecke · —
1× · 2008-12-22 · Marder-Hell-Schecke · Freiberger Rennmauszucht
1× · 2008-12-22 · Marder-Hell-Schecke · Freiberger Rennerzucht | | +| Lola | 1× · 2014-02-10 · Polarfuchs · Freddy Braun
1× · 2014-02-10 · Polarfuchs · Michael Lengowski
1× · 2014-02-10 · Polarfuchs · Chalfont Stud
1× · 2010-07-23 · Schwarz · Privatzüchterin Sandy | | +| Rose | 1× · — · — · Clan of Kleine Füchse
1× · 2006-06-16 · Kohlfuchs · Clan of Amazing (CH)
1× · 2013-01-26 · Marder-Schecke · Clan of little Angels | 25 | +| Cole | 2× · — · Schwarz-Schecke · of Camelot
1× · — · — · Clan of Kleine Füchse
1× · — · Schwarz-Schecke · Clan of Camelot | 25 | +| Yumi | 21× · 2011-02-01 · Colourpoint-Agouti-Hell-Scheck · Privatzüchterin Andrea T.
★1× · 2011-04-30 · Kohlfuchsschimmel · Kieger mit Krallen
1× · 2014-04-08 · Goldfuchs-Starkschecke [DP] · Petra Dos Santos | | +| Melody | 2× · — · Polarfuchs-Schecke · Clan of Sunnydale, Clan of sun
1× · — · Marder-Hell · Clan of Strättlingen
★1× · 2010-11-03 · Agouti · Clan of little fellows | | +| Eddy | 1× · 2017-08-24 · Blau · Clan of Lennylengo
★1× · 2011-06-13 · Kohlfuchsschimmel, hell · eigene Zucht
1× · 2010-08-20 · Schwarz-Schecke · Kleinen Chaoten | | +| Zeus | 4× · 2006-08-23 · Polarfuchs · Barbara Peterhans
3× · — · Blaufuchs · Niederlande
★1× · 2012-01-27 · Agouti · eigene Zucht | | +| Ivy | 1× · 2016-11-07 · Schwarz · Clan of Topolino
★1× · 2012-08-22 · Colourpoint-Agouti · Clan of Harzer-Renner
1× · 2014-01-10 · Schwarz-Schecke · — | | +| Ben | 2× · 2005-02-01 · Agouti · Clan of Ben und Lulu
★1× · 2013-03-04 · Colourpoint-Agouti-Hell · eigene Zucht
1× · 2005-01-01 · Agouti · Privat aus Holland | | + +_Zusätzlich 4 Namen, bei denen **alle** Varianten merkmalslos sind (reine Namens-Dubletten ohne Unterscheidungsmerkmale)._ + +## 3. Eigene Tiere — Abgleich gegen aktuellen Stand (Teil B) + +- Gematcht (Name): **123/303** +- **Fehlen im aktuellen Stand: 180** +- **Eltern unterschiedlich: 108** +- **Genotyp unterschiedlich/aktuell ohne: 120** +- **Geburtsdatum weicht >14 Tage ab: 42** + +### 3a. Eltern unterschiedlich (RPRO3 = autoritativ) + +| Tier | DOB | RPRO3 Mutter × Vater | Aktuell Mutter × Vater | +|---|---|---|---| +| Blacky | 2009-07-23 | Bella × Snoopy | Yumi × Flori von den Kleinen Chaoten | +| Alvin | 2010-02-18 | Kuke × Blacky | — × — | +| Charlie | 2010-05-08 | Evy × Danny | Nancy × Zorro | +| Dante | 2010-05-08 | Evy × Danny | — × Danny von Maintaler PZ | +| Jana | 2009-12-07 | Darina gen. Talina × Milon | — × — | +| Beatrice | 2011-06-13 | Malina × Dante | Bea von den Kleinen Chaoten × Taro von den kleinen Chaoten | +| Lee | 2011-02-16 | Malina × Alvin | Malina of minor diabolus × Alvin von den kleinen Chaoten | +| Liz | 2011-07-18 | — × — | Little Lilly von den Kleinen Chaoten × — | +| Yumi | 2011-04-30 | Alice × Eros | — × — | +| Kim | 2010-11-03 | Yuki × Daco | Naho von den Kleinen Chaoten × Sokrates von den Kleinen Chaoten | +| Dean | 2011-03-24 | Kim × Rocco | — × — | +| Jamie | 2010-12-27 | Jana × Danny | Beatrice von den kleinen Chaoten × Jamie | +| Eddy | 2011-06-13 | Malina × Dante | — × — | +| Kyara | 2011-12-05 | Frau Maus × Hr.Maus | — × — | +| Philo | 2011-06-11 | Jana × Charlie | Jana × Charlie von den Kleinen Chaoten | +| Aiko | 2011-09-17 | Kimba × Fips | Sheila of Lennylengo × Eddy von den Kleinen Chaoten | +| Talia | 2011-12-04 | Liz × — | Liz v. PZ Gelnhausen × Dean of little fellows | +| Jack | 2012-03-20 | Katie × Jason | — × — | +| Riko | 2011-10-08 | Yumi × — | Yumi × Flori von den Kleinen Chaoten | +| Ivy | 2012-08-22 | Gini × Baten Kaidos | — × — | +| Yuri | 2012-02-19 | Doregene × Ong1 | — × — | +| Tay | 2012-04-04 | Eiji × Felix | — × — | +| Kyra | 2012-10-29 | Hazel1 × Blue Cloud | — × — | +| Robin | 2012-10-30 | Trixxy × Tai gen. Karlchen | Trixxy von den Kleinen Chaoten × Tai | +| Stacy | 2012-10-23 | Mutti × Vati | — × — | +| Jd | 2012-06-11 | Delia × Feivel | Delia of cb-animals × Flori von den Kleinen Chaoten | +| Oskar | 2012-06-13 | Sheila × Eddy | — × — | +| Raya | 2013-02-02 | Scarlett × Gismo | — × — | +| Mina | 2012-08-31 | Yam × Tjorvi | Little Lilly von den Kleinen Chaoten × Eddward von den Kleinen Chaoten | +| Amy | 2013-02-16 | Yumi × Yuri | — × — | +| Lilly | 2012-09-22 | Minghan × Ketchup | Trixxy von den Kleinen Chaoten × Jack | +| Ina | 2013-04-14 | Lina × Olly | — × Blacky von PZ Seligenstadt | +| Eddie | 2013-01-27 | Gwen gen. Sakura × Jamie | Sakura × Jamie | +| Bella | 2013-03-20 | Beatrice × Taro | Beatrice von den kleinen Chaoten × — | +| Ben | 2013-03-04 | Kyra × Flippi | Roni × — | +| Mira | 2013-03-04 | Kyra × Flippi | — × Willow | +| Paul | — | Paula × John | — × — | +| Yoko | 2013-07-27 | Yuki × Jamie | — × Jamie | +| Yuna | 2013-08-23 | Kazumi × Tay | — × — | +| Lighting | 2013-10-31 | Sammy 11 × Ben | Sammy von den kleinen Chaoten × Ben von den kleinen Chaoten | +| Nelly | 2013-07-12 | Bella × Silver | Bella × Silver von den kleinen Chaoten | +| Jacky | 2013-07-24 | Lilly × Jack | Whity × Grey | +| Melina | 2014-01-12 | Mia × Kazu | — × Kazu von den Kleinen Chaoten | +| Blue | 2014-01-14 | Lilly × Drake | Holly gen. Lilly of Ulmer Strolche × Drake von den kleinen Chaoten | +| Genna | 2014-06-20 | Reika × Epoxy | — × — | +| Dastan | 2014-05-07 | Jette × Blue Cloud | Kazumi von den Kleinen Chaoten × Flippi von den kleinen Chaoten | +| Browny | 2013-12-02 | Reika × Epoxy | — × Kazu von den Kleinen Chaoten | +| Honey | 2013-04-04 | Milan × Stan | Jule von den Kleinen Chaoten × D'Artagnan gen. Dante | +| Whity | 2013-10-02 | Akina × Mr.Agouti | Akina von den Kleinen Chaoten × Mr. Agouti | +| Scrat | 2012-07-08 | Yumi × Jamie | — × — | +| Lynn | 2013-12-11 | Silvi × Bommel III. | — × — | +| Leila | 2014-04-17 | — × — | Lexy × Blue | +| Berni | 2014-03-19 | Amie × Flash | — × — | +| Flash | 2013-12-01 | — × — | Tennessee von den Kleinen Chaoten × Chesnut | +| Amie | 2014-07-04 | — × — | Katara von den Kleinen Chaoten × BlackFire von den Kleinen Chaoten | +| Cerise | 2014-02-20 | Jarla × Ain al Rami | — × — | +| Kia | 2014-02-21 | Yuna × Yoko | — × — | +| Jayjay | 2013-06-19 | Lilly × Jack | Holly gen. Lilly of Ulmer Strolche × Jack | +| Earl | 2013-04-22 | Vee × Olly | — × — | +| Elena | 2013-12-05 | Skarlett × Ibo | — × — | +| Hiro | 2012-02-13 | Amidala × Wynn | — × — | +| Sunny | — | — × — | Beatrice von den kleinen Chaoten × Alvin von den kleinen Chaoten | +| Grace | 2014-01-28 | Skarlett × Ibo | Nancy × Milon von den Kleinen Chaoten | +| Alex | 2014-02-13 | Sammy 11 × Ben | — × — | +| Lexy | 2014-03-06 | Kazumi × Taro | — × Taro von den kleinen Chaoten | +| Arizona | 2014-05-27 | Kelly × Digimon | — × Digimon von den Kleinen Chaoten | +| Kokiri of Samsimar | 2014-05-12 | Talula × Asterix of Samsimar | Talula of Samsimar × Asterix of Samsimar | +| Persephone | 2014-08-14 | Xana of Golden Lights1 × Eddard Stark of Sunset Glow1 | — × — | +| Qamikaze Queen | 2013-10-15 | Goldlöckli × Einstein | — × — | +| Zora | 2015-11-06 | Leila1 × BlackFire | — × — | +| Ernie | — | Kazumi × Jayjay | Skarlett von den Kleinen Chaoten × Max von Privat | +| Sam | 2011-10-13 | Beatrice × Alvin | — × — | +| Zara | 2015-01-29 | Mira × Sam | Mira von den Kleinen Chaoten × Sam | +| Zoey | 2015-11-10 | Bura × Alon | Isa × Arrow | +| Elieus gen. Eragon | 2016-05-18 | Liana × Xeno | Stadtmaus Liana × Stadtmaus Xeno | +| Nduga | 2017-03-04 | Moon Girl × Thas'dorah | Wilde Ringrenner Moon Girl × Exidra`s Thas'dorah | +| Fast Boy | 2017-03-31 | Ziwa × Charly | — × — | +| Lotta | 2014-12-05 | Dune × Lugdusch | — × Akio | +| Living Force's Idefix | 2016-04-05 | Living Force's Vally × Living Force's Nando | Living Force's Vally × — | +| Obelix | 2017-06-16 | Venus × Shaddok | — × — | +| Olympia | 2018-04-01 | Olia × Obelix | — × Shinichi von PZ Mücke | +| Tinkerbell | 2018-03-13 | Batida of little Runner`s [meliert] × Collin | — × — | +| Nancy | 2014-05-18 | Lumina × Fegur | — × Taro von den kleinen Chaoten | +| Danako | — | Umi × Osamu | Umi × Osamu von den Kleinen Chaoten | +| Poseidon | 2014-08-14 | Xana of Golden Lights1 × Eddard Stark of Sunset Glow1 | Xana of Golden Lights × Eddard Stark of Sunset Glow | +| Sammy | 2012-10-30 | Trixxy × Tai gen. Karlchen | Mira von den Kleinen Chaoten × Id | +| Teddy Altman | 2017-09-13 | Sloan Riley × Charly | SA × Charlie | +| Zarina | 2015-01-29 | Mira × Sam | — × — | +| John | 2018-02-19 | Naho × Sokrates | Naho von den Kleinen Chaoten × Sokrates von den Kleinen Chaoten | +| Rumi | 2017-02-24 | Hanami × Xavier | — × Xavier of Black Forest | +| Lotte | 2017-11-27 | Cho × Remi | — × — | +| Tommy | 2013-03-16 | Stacy × Sam | — × — | +| Ina of Black Forest | 2017-10-25 | Onella × Elieus gen. Eragon | — × — | +| Pepper | — | Jela × Maxi King | — × — | +| Ares | 2018-09-11 | Nalani × Adonis | — × — | +| Isa | 2014-12-24 | Reika × Epoxy | — × Blacky von PZ Seligenstadt | +| Jill | 2017-12-08 | Willow × Maxi King | Willow of Black Forest × Maxi King of LennyLengo | +| J | 2019-07-09 | — × Adonis | Kathlin von den Kleinen Chaoten × Baxter von den Kleinen Chaoten | +| Laito | 2018-03-16 | Rumiko × Lance | Rumiko v.d. Kleinen Chaoten × Lance v.d. Kleinen Chaoten | +| Panini | 2018-10-24 | Addison × Wilbur | — × — | +| Yoshua | 2017-11-14 | Summer × Jack II | Summer v.d. Kleinen Chaoten × Jack II von den Kleinen Chaoten | +| Jeremy | 2019-07-09 | Macey × Adonis | Macey von den Kleinen Chaoten × Silenos gen. Adonis von den Kleinen Chaoten | +| Snickers | 2018-09-21 | Fleurelle gen. Fipsy × Pinto | Zoé von den Kleinen Chaoten × Koji von den Kleinen Chaoten | +| Uriana | 2019-04-02 | Venus × Jack III | Venus von den Kleinen Chaoten × Jack III von den Kleinen Chaoten | +| Lucky | 2020-02-06 | Akatosh [DP, Rex] × Snickers | Rainny von den Kleinen Chaoten × Kazuya von den Kleinen Chaoten | +| Firestorm | 2019-12-18 | Vestra × WildFire | — × — | +| Eliza | 2014-02-13 | Sammy 11 × Ben | — × — | +| Daim | — | Danako × Leibniz | Athos × — | + +### 3b. Eigene Tiere, die im aktuellen Stand fehlen + +| Tier | DOB | Geschlecht | Farbe | Herkunft | ZB | +|---|---|---|---|---|---| +| Kuke | 2009-09-18 | weiblich | Anthrazit-Schecke | M.Knoss GG | ZdKC-B.10.2009 | +| Evy | 2008-09-20 | weiblich | Silberagouti | Gobis Rennmauszucht | ZdKC-B.04.2010 | +| Danny | 2009-10-09 | männlich | Marder-Hell-Schecke | Maintaler Privatzucht | ZdKC-D.01.2010 | +| Charlett | 2010-05-14 | weiblich | Kohlfuchs, hell-Schecke | Clan of little longnoses | ZdKC-C.02.2011 | +| Malina | 2010-03-22 | weiblich | Gold-Hell | Clan of minor diabolus | ZdKC-M.09.2010 | +| Aice | 2009-07-23 | weiblich | Saphir | Seligenstädter Privatzucht | 0 | +| Heaven | 2011-08-08 | männlich | Marder | Maintaler Privatzucht | ZdKC-H.2011 | +| Puschel | 2011-06-13 | weiblich | Rotfuchs | Privatzüchterin Terry M. | ZdKC-P.11.2011 | +| Melody | 2010-11-03 | weiblich | Agouti | Clan of little fellows | — | +| Vee | 2011-08-18 | weiblich | Rotfuchs | Privatzüchterin Terry M. | ZdKC-V.11.2011 | +| Mary | 2011-09-13 | weiblich | Agouti-Schecke | Seligenstädter Privatzucht | ZdKC-M.10.2011 | +| Gina | 2011-09-09 | weiblich | Marder-Schecke | Zucht der bunten Fellnasen | ZdKC-G.11.2011 | +| Gwen gen. Sakura | 2011-12-17 | weiblich | Silberagouti-Schecke | Clan of little Angels | ZdKC-S.05.2012 | +| Tai gen. Karlchen | 2011-10-01 | männlich | Polarfuchs | Clan of Lennylengo | — | +| Zeus | 2012-01-27 | männlich | Agouti | eigene Zucht | — | +| Delia | 2011-12-10 | weiblich | Schwarz-Schecke | Clan of cb-animals | ZdKC-D.03.2012 | +| Teiko | 2011-12-04 | weiblich | Agouti | eigene Zucht | — | +| Olly | 2012-07-16 | männlich | Anthrazit | eigene Zucht | — | +| Drake | 2012-06-06 | männlich | Polarfuchs | eigene Zucht | — | +| Trixxy | 2011-12-04 | weiblich | Rotfuchs-Schecke | eigene Zucht | — | +| Taro | 2012-09-01 | männlich | Agouti | eigene Zucht | — | +| Akina | 2013-04-13 | weiblich | Anthrazit-Kragenschecke | eigene Zucht | — | +| Kazumi | 2013-04-23 | weiblich | Algierfuchsschimmel, hell-Schecke | eigene Zucht | — | +| Mr.Agouti | 2012-12-23 | männlich | Agouti | eigene Zucht | — | +| Bea | 2013-03-20 | weiblich | Colourpoint-Polarfuchs | eigene Zucht | — | +| Silver | 2013-03-20 | männlich | Silberagouti | eigene Zucht | — | +| Sammy 11 | 2013-03-16 | weiblich | Saphir-Schecke | eigene Zucht | — | +| Mia | 2013-03-04 | weiblich | Marder-Hell | eigene Zucht | — | +| Kazu | 2013-04-23 | männlich | Algierfuchsschimmel-Schecke | eigene Zucht | — | +| Milow | 2013-07-29 | männlich | Algierfuchsschimmel, hell | eigene Zucht | — | +| Blue1 | 2013-09-22 | weiblich | Blaufuchs-Schecke | Gießener Privatzucht | — | +| AJ aka Alvin Jr. | 2013-11-22 | männlich | Silberagouti-Schecke | eigene Zucht | — | +| Skarlett | 2013-07-14 | weiblich | Polarfuchs | eigene Zucht | — | +| Amelia | 2013-12-09 | weiblich | Colourpoint-Silberagouti-Hell-Schecke | eigene Zucht | — | +| Samson | 2014-01-12 | männlich | Marder-Schecke | eigene Zucht | — | +| Digimon | 2013-06-19 | männlich | Colourpoint-Agouti-Hell-Schecke | eigene Zucht | — | +| Flippi | 2011-01-12 | männlich | Marder-Hell | eigene Zucht | 0121a K | +| Flori | 2010-09-21 | männlich | Schwarz | eigene Zucht | ZdKC-F2010-02 | +| Kelly | 2013-04-23 | weiblich | Agouti | eigene Zucht | — | +| Sheila | 2014-01-08 | weiblich | Algierfuchs | Clan of Lennylengo | — | +| Leila1 | 2014-04-08 | weiblich | Rotfuchs-Schecke | Privatzüchterin | — | +| Oliver | 2014-01-26 | männlich | Anthrazit | eigene Zucht | — | +| Derek | 2013-12-09 | männlich | Colourpoint-Agouti | eigene Zucht | — | +| Katara | 2014-02-24 | weiblich | Colourpoint-Agouti | eigene Zucht | — | +| Benny | 2012-07-19 | männlich | Agouti | — | — | +| Linus | 2014-04-20 | männlich | Algierfuchsschimmel | Annemarie Knief | — | +| Hanse Renner's Dagonet | 2013-01-26 | männlich | Agouti | Hanse Renner | G02/MIX-FU02-D-13-01 | +| Hanse Renner's D'Artagnan | 2013-01-26 | männlich | — | eigene Zucht | G02/MIX-FU02-D-13-02 | +| Dustin | 2014-08-09 | männlich | Algierfuchs, hell-Schecke | eigene Zucht | — | +| BlackFire | 2013-08-31 | männlich | Schwarz | eigene Zucht | — | +| Dastan1 | 2014-05-08 | männlich | Schwarz | Clan of Golden Lights | — | +| Lumina | 2013-08-18 | weiblich | Schimmel | Michaela Graf | — | +| Hina | 2014-01-14 | weiblich | Blaufuchs | eigene Zucht | — | +| Xtra Bounty | 2014-03-20 | männlich | Hermelin | Clan der Schlossmäuse | — | +| Xtra Bounty h | 2014-03-30 | männlich | Hermelin | Clan der Schlossmäuse | — | +| x | — | weiblich | Agouti | — | — | +| Nestor | 2008-12-07 | männlich | Blaufuchsschimmel | Clan of La Salida Del Sol | — | +| Janosch of Black Forest | 2014-06-18 | männlich | Siam | Michelle Enders | — | +| Danella gen. Nella | 2014-04-15 | weiblich | Orangeschimmel | Clan of sweet little mouse | Danella/eef/1/weiblich/ Orangeschimmel | +| Hanse Renner's Rem | 2014-09-17 | weiblich | Marder-Hell | Hanse Renner | — | +| El Chicco of Sweet Little Mouse | 2013-08-14 | männlich | Dilute Polarfuchs | Clan of sweet little mouse | — | +| Judith | 2014-06-18 | weiblich | Marder | Clan of Black Forest | — | +| Silberagouti | — | weiblich | Silberagouti | — | — | +| Polarfuchs D- | — | weiblich | Polarfuchs | — | — | +| Omara | — | weiblich | Colourpoint-Kohlfuchs | — | — | +| Hanse Renner's Sophia | 2014-09-21 | weiblich | Silberagouti Punktschecke | Hanse Renner | — | +| Xandria | 2016-03-16 | weiblich | Colourpoint-Blaufuchs-Schecke | Clan of Golden Lights | CoGL-X-2016-3 | +| Xenia | 2016-03-16 | weiblich | Marder-Hell-Schecke | Clan of Golden Lights | CoGL-X-2016-4 | +| Mister X | 2015-10-30 | männlich | Orange-Schimmel | Sarah Wörz11 | — | +| Bura | 2015-05-07 | weiblich | Blau | Sarah Wörz111 | — | +| Tuli | 2012-12-18 | weiblich | Schimmel | Ulmer Strolche`s | — | +| Flame | 2015-01-19 | weiblich | Blau-Schecke | Clan of Braveheart | — | +| Fegur | 2013-07-14 | männlich | Agouti | Clan of little Hobbits (alt) | — | +| Latimer | 2017-02-21 | männlich | Colourpoint-Kohlfuchs-Schecke | Berenice Schulz-Gebeltzig | — | +| Koo | 2017-05-17 | männlich | Silberagouti-Schecke | Clan of Golden Lights | CoGL-D-2017-02 | +| Olia | 2016-04-25 | weiblich | Schwarz-Schecke DP | Verena Kaufmann | 279 XYZ | +| Dreamer (DP) | 2016-02-19 | männlich | Blau-Schecke | Verena Kaufmann | 233 XYZ | +| Elia | 2017-07-15 | weiblich | Algierfuchsschimmel, hell-Schecke | Clan of Black Forest | — | +| Fleurelle gen. Fipsy | 2017-03-07 | weiblich | Blau | Clan of Tamyna | — | +| Lumina1 | 2013-08-18 | weiblich | Schimmel | — | — | +| Helge | — | männlich | Marder | Clan of Samsimar | — | +| Collin | 2016-05-06 | männlich | Schwarz-Schecke [DP] | Julia Flume | — | +| Wonderman | 2017-06-14 | männlich | Schwarz-Starkschecke [DP] | Melanie Speck | — | +| Unique | 2017-05-12 | weiblich | Schwarz-Schecke | Melanie Speck | — | +| Shinichi | 2014-01-21 | männlich | Colourpoint-Agouti | Drazena Rimac | ZdkC-J3/-2014-02 | +| Alice (Polarfuchs w) | 2013-11-08 | weiblich | Polarfuchs | Nicole Koch | — | +| Unit | — | weiblich | Kohlfuchs-Schecke | Kleinen Chaoten | — | +| Leibniz | — | männlich | Marder-Hell | Kleinen Chaoten | — | +| Jack III | 2017-11-14 | männlich | Agouti-Schecke | Kleinen Chaoten | — | +| Jack Jr | 2013-06-19 | männlich | Colourpoint-Agouti | eigene Zucht | — | +| Enya | 2017-11-01 | weiblich | Colourpoint-Fuchs | Kleinen Chaoten | — | +| Daichi | 2018-08-20 | männlich | Agouti | Kleinen Chaoten | — | +| Brandon Stark | 2017-12-13 | männlich | Schwarz | Kleinen Chaoten | — | +| WildFire | 2017-10-05 | männlich | Zobel | Kleinen Chaoten | — | +| Piero | 2017-02-16 | männlich | Colourpoint-Agouti | Kleinen Chaoten | — | +| Naho | 2017-07-20 | weiblich | Algierfuchsschimmel | Kleinen Chaoten | — | +| Jin | 2018-02-19 | weiblich | Blaufuchs | Kleinen Chaoten | — | +| Pinja | 2017-08-24 | weiblich | Algierfuchs, hell-Schecke | Kleinen Chaoten | — | +| Flint | 2017-12-23 | männlich | Kohlfuchs, hell | Kleinen Chaoten | — | +| Ramzi | 2018-04-21 | männlich | Marder | Kleinen Chaoten | — | +| Willy | 2017-01-16 | männlich | Agouti | Kleinen Chaoten | — | +| Milena | 2014-03-19 | weiblich | Marder-Kragenschecke | eigene Zucht | — | +| Oyama | 2018-12-26 | weiblich | Colourpoint-Agouti | Kleinen Chaoten | — | +| Zuchtweibchen schwarz | 2019-03-11 | männlich | Schwarz | Clan of little Runner`s | — | +| Rin | 2019-01-01 | weiblich | Rotfuchs | Kleinen Chaoten | — | +| Zera | — | weiblich | Platin-Schecke DP | Kleinen Chaoten | — | +| Inusch | 2017-10-25 | männlich | Platin-Schecke DP | Clan of Black Forest | — | +| Sansa Stark | 2019-04-09 | weiblich | Agouti | Kleinen Chaoten | — | +| Quied Soldier | 2018-06-07 | männlich | Algierfuchsschimmel, hell-Schecke DP | Clan of Black Forest | — | +| Polaris | — | männlich | Polarfuchs | Kleinen Chaoten | — | +| Honshu | 2018-05-31 | weiblich | Platin-Schecke | Clan of Golden Lights | — | +| Adonis | 2015-10-11 | männlich | Schwarz | Kleinen Chaoten | — | +| Vestra | 2019-02-08 | weiblich | Agouti-Ansatzstarkschecke | Clan der Schlossmäuse | — | +| Nalani | 2017-02-10 | weiblich | Colourpoint-Silberagouti-Kragenschecke | Kleinen Chaoten | — | +| Halia | 2018-09-11 | weiblich | Schwarz-Schecke | Kleinen Chaoten | — | +| Kiki | 2018-06-10 | weiblich | Algierfuchs, hell | Kleinen Chaoten | — | +| Opheus | 2018-03-28 | männlich | Silberagouti-Schecke | Kleinen Chaoten | — | +| Uniques Tochter | 2018-07-06 | weiblich | Schwarz-Starkschecke DP | Clan of Fiomi | — | +| Dorie | 2019-05-28 | weiblich | Schwarz-Schecke | Clan of Black Forest | — | +| Marlin | 2019-05-28 | männlich | Agouti | Clan of Black Forest | — | + +_… und 60 weitere._ + +### 3c. Genotyp unterschiedlich / aktuell nicht vorhanden (Top 60) + +| Tier | RPRO3 Genotyp (autoritativ) | Aktuell | +|---|---|---| +| Blacky | `aa Cc[chm] DD EE GG Pp spsp` | — | +| Alvin | `aa Cc[chm] DD EE Gg PP Spsp` | — | +| Charlie | `Aa Cc[chm] DD EE gg Pp spsp` | `AA CC DD EE GG PP Spsp rere` | +| Dante | `Aa c[chm]c[chm] DD Ee[f] gg PP spsp` | — | +| Jana | `aa c[chm]c[h] DD ee[f] Gg Pp spsp` | — | +| Beatrice | `Aa c[chm]c[h] DD Ee Gg Pp spsp` | — | +| Lee | `Aa c[chm]c[h] DD Ee GG Pp spsp` | — | +| Liz | `Aa CC DD ee GG pp Spsp` | — | +| Yumi | `aa CC DD ee[f] GG Pp spsp` | — | +| Kim | `Aa C- Dd Ee Gg Pp Spsp` | — | +| Dean | `aa CC DD ee[f] gg Pp spsp` | — | +| Jamie | `aa c[chm]c[chm] DD Ee gg Pp spsp` | — | +| Eddy | `aa Cc[chm] DD ee[f] Gg Pp spsp` | — | +| Kyara | `Aa Cc[chm] Dd ee[-] GG Pp Spsp` | — | +| Philo | `aa c[chm]c[chm] DD Ee Gg pp spsp` | — | +| Aiko | `Aa CC Dd Ee gg PP spsp` | — | +| Talia | `Aa Cc[chm] DD ee Gg Pp spsp` | — | +| Jack | `Aa c[chm]c[h] DD Ee GG Pp spsp` | — | +| Riko | `aa C- DD Ee GG Pp spsp` | — | +| Ivy | `Aa c[chm]c[chm] DD EE Gg P- spsp` | — | +| Yuri | `AA CC DD Ee[f] GG PP spsp` | — | +| Tay | `Aa CC DD e[f]e[f] G- P- spsp` | — | +| Kyra | `Aa c[chm]c[chm] DD EE gg PP spsp` | — | +| Robin | `Aa CC D- ee gg Pp spsp` | — | +| Stacy | `Aa CC D- EE GG pp Spsp` | — | +| Yuki | `Aa CC DD ee[f] GG Pp spsp` | — | +| Jd | `aa CC DD EE GG PP Spsp` | — | +| Oskar | `Aa CC D- Ee G- P- Spsp` | `AA CC DD EE GG PP Spsp rere` | +| Raya | `Aa Cc[chm] DD e[f]e[f] GG PP spsp` | — | +| Mina | `aa c[chm]c[chm] D- ee Gg PP spsp` | — | +| Amy | `Aa CC DD ee[f] GG PP spsp` | — | +| Lilly | `aa Cc[chm] dd ee Gg PP Spsp` | — | +| Ina | `aa Cc[chm] DD Ee Gg pp spsp` | `aa CC DD EE GG PP spsp rere` | +| Eddie | `aa c[chm]c[chm] DD E- gg P- Spsp` | — | +| Bella | `Aa Cc[chm] DD ee GG Pp spsp` | `AA CC DD ee GG PP spsp rere` | +| Ben | `Aa c[chm]c[h] DD E- Gg Pp spsp` | — | +| Mira | `Aa c[chm]c[chm] DD E- Gg PP spsp` | — | +| Paul | `A- C- D- ee gg P- spsp` | — | +| Yoko | `Aa Cc[chm] DD ee[f] Gg P- spsp` | — | +| Yuna | `AA C- DD e[f]e[f] G- PP spsp` | — | +| Lighting | `aa c[chm]c[chm] DD E- G- Pp spsp` | — | +| Nelly | `aa Cc[chm] DD ee Gg Pp spsp` | — | +| Jacky | `aa c[chm]c[chm] Dd Ee Gg P- spsp` | — | +| Melina | `aa c[chm]c[h] DD Ee[-] G- Pp spsp` | — | +| Max | `aa c[chm]c[chm] DD EE GG P- spsp` | — | +| Blue | `aa Cc[chm] Dd ee[f] gg PP Spsp` | `aa CC DD ee gg PP Spsp rere` | +| Genna | `aa c[chm]c[chm] DD Ee gg Pp spsp` | — | +| Dastan | `aa Cc[chm] DD Ee Gg P- spsp` | `AA CC DD ee GG PP Spsp rere` | +| Browny | `aa c[chm]c[chm] DD Ee gg PP spsp` | — | +| Honey | `Aa C- D- ee[f] Gg Pp spsp` | — | +| Whity | `Aa c[chm]c[chm] DD Ee Gg pp spsp` | — | +| Scrat | `aa Cc[chm] DD Ee Gg pp spsp` | — | +| Lynn | `Aa CC D- E- Gg PP Spsp` | — | +| Leila | `AA CC DD ee GG PP Spsp` | — | +| Berni | `aa C- DD ee G- P- Spsp` | — | +| Flash | `aa C- DD ee Gg P- Spsp` | — | +| Amie | `aa C- D- ee Gg P- spsp` | — | +| Cerise | `AA C- DD Ee gg pp Spsp` | `AA CC DD EE GG pp Spsp rere` | +| Kia | `A- C- DD e[f]e[f] G- PP spsp` | — | +| Sky | `aa C- DD ee gg P- spsp` | `AA cchmcchm DD ee GG PP spsp rere` | + +_… und 60 weitere._ + +## 4. Hinweise zum Matching & Empfehlung + +**Matching-Vorbehalt:** Der Abgleich in Abschnitt 3 vergleicht über den **Namen**. Der aktuelle Stand führt Tiere oft mit Zucht-Suffix („… von den Kleinen Chaoten", „of …"), RPRO3 nur den Kurznamen. Ein Teil der **180 „fehlend"** ist daher nur ein Suffix-Mismatch, kein echtes Fehlen. Der spätere Importer normalisiert Namen (Suffix abtrennen) und matcht zusätzlich über DOB+Farbe. + +**Kernbefund:** RPRO3 ist die **autoritative Quelle** — die echten Zuchtdaten der Software. Die Eltern-Abweichungen (Abschnitt 3a) zeigen klar die bekannten xlsx-Heuristik-Fehler (z. B. Blacky = *Bella × Snoopy* statt fälschlich *Yumi × Flori*). Ein Import aus RPRO3 behebt viele dieser Fehler an der Wurzel. + +**Empfohlenes Vorgehen für den Import:** +1. RPRO3-interne Dubletten **vor** dem Laden zusammenlegen (Abschnitt 2; merkmalslose Namens-Dubletten zu 1 Tier). +2. Mehrdeutige Namen (2b) der Züchterin zur Entscheidung vorlegen. +3. Eigene Tiere + Eltern + Genotypen + Würfe + Kontakte aus RPRO3 als **führend** übernehmen. +4. Externe Ahnen (nach Dedup) für tiefe Stammbäume ergänzen. diff --git a/tools/import/rpro3_post_tickets.py b/tools/import/rpro3_post_tickets.py new file mode 100644 index 0000000..ef1b5af --- /dev/null +++ b/tools/import/rpro3_post_tickets.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Postet die von rpro3_tickets.py erzeugten Rückfrage-Tickets in die laufende WebAPI. +Pro Ticket: POST /feedback (legt „Open" an, mit GerbilId/EntityName) + PUT /feedback/{id} +(setzt Question -> NeedsInfo, und AgentContext). + +Re-runfähig: überspringt Tickets, deren EntityName bereits als Context=rpro3-import existiert. + +Aufruf: python rpro3_post_tickets.py [http://localhost:5179] +""" +from __future__ import annotations +import sys, json, urllib.request, urllib.error + +def req(method, url, body=None): + data = json.dumps(body, ensure_ascii=False).encode("utf-8") if body is not None else None + r = urllib.request.Request(url, data=data, method=method, + headers={"Content-Type": "application/json; charset=utf-8"}) + with urllib.request.urlopen(r, timeout=30) as resp: + raw = resp.read().decode("utf-8") + return json.loads(raw) if raw else None + +def main(): + tickets_path = sys.argv[1] + base = sys.argv[2] if len(sys.argv) > 2 else "http://localhost:5179" + tickets = json.load(open(tickets_path, encoding="utf-8")) + + existing = req("GET", f"{base}/feedback") or [] + seen = {f.get("entityName") for f in existing if f.get("context") == "rpro3-import"} + print(f"Vorhandene rpro3-import-Tickets: {len(seen)}") + + created = skipped = failed = 0 + for i, tk in enumerate(tickets, 1): + if tk["EntityName"] in seen: + skipped += 1 + continue + try: + post_body = { + "message": tk["Message"], + "context": tk["Context"], + "entityName": tk["EntityName"], + "gerbilId": tk.get("GerbilId"), + } + created_dto = req("POST", f"{base}/feedback", post_body) + fid = created_dto["id"] + req("PUT", f"{base}/feedback/{fid}", { + "question": tk["Question"], + "agentContext": tk["AgentContext"], + }) + created += 1 + seen.add(tk["EntityName"]) + if created % 50 == 0: + print(f" … {created} erstellt ({i}/{len(tickets)})") + except urllib.error.HTTPError as e: + failed += 1 + print(f" FEHLER bei „{tk['EntityName']}\": {e.code} {e.read().decode('utf-8','replace')[:160]}") + except Exception as e: # noqa + failed += 1 + print(f" FEHLER bei „{tk['EntityName']}\": {e}") + + print(f"\nFertig: {created} erstellt, {skipped} übersprungen, {failed} Fehler.") + +if __name__ == "__main__": + main() diff --git a/tools/import/rpro3_tickets.py b/tools/import/rpro3_tickets.py new file mode 100644 index 0000000..56026ef --- /dev/null +++ b/tools/import/rpro3_tickets.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Erzeugt aus dem RPRO3-Dedup (compare_rpro3.dedup) Rückfrage-Tickets für die Züchterin: +mehrdeutige Namen mit >=2 informativen Varianten (echte „gleich/verschieden?"-Entscheidungen). + +Pro Variante wird die HERKUNFT hinterlegt (RennmausPro-Datensatz-Nr., eigenes Tier vs. externer +Ahn, Züchter/Herkunft). Wo ein passendes BESTEHENDES Tier im aktuellen Bestand gefunden wird, +kommt ein Hyperlink in die Frage ([Name ansehen](/rennmaeuse/)), den die Tickets-UI klickbar +rendert. Das Ticket bekommt zusätzlich GerbilId (für den „Datensatz ansehen"-Link in der Kopfzeile) +und ein internes AgentContext (Varianten + rids + gefundene Tier-IDs) für den späteren Importer. + +Postet NICHTS — schreibt nur die Kandidaten-JSON. Das Posten macht rpro3_post_tickets.py. + +Aufruf: python rpro3_tickets.py <_rpro3.db> [out.json] +""" +from __future__ import annotations +import sys, json, re, datetime +import compare_rpro3 as C + +LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" +SUFFIX_RE = re.compile(r"\s+(von den |von der |von dem |vom |von |of |gen\.\s|v\.\s|de\s|du\s)", re.IGNORECASE) + +def informative(v): + return bool(v["dob"] or v["farbe"] or v["origin"]) + +def strip_suffix(name): + """Zucht-Suffix abtrennen (z. B. „Blacky von den Kleinen Chaoten" -> „blacky").""" + n = name or "" + m = SUFFIX_RE.search(n) + if m: + n = n[:m.start()] + return C.norm_name(n) + +def parse_dob(s): + if not s: + return None + try: + return datetime.date.fromisoformat(str(s)[:10]) + except ValueError: + return None + +def build_current_index(scratch): + g = json.load(open(f"{scratch}\\cur_gerbils.json", encoding="utf-8")) + g = g["items"] if isinstance(g, dict) else g + idx_full, idx_strip = {}, {} + for x in g: + rec = {"id": x["id"], "name": x["name"], "dob": parse_dob(x.get("dateOfBirth"))} + idx_full.setdefault(C.norm_name(x["name"]), []).append(rec) + idx_strip.setdefault(strip_suffix(x["name"]), []).append(rec) + return idx_full, idx_strip + +def match_current(name, dob, idx_full, idx_strip): + """Eindeutiges bestehendes Tier zu (Name, DOB) finden — sonst None (keine falschen Links).""" + key = C.norm_name(name) + cands = idx_full.get(key) or idx_strip.get(strip_suffix(name)) or [] + if not cands: + return None + if dob: + near = [c for c in cands if c["dob"] and abs((c["dob"] - dob).days) <= 31] + if len(near) == 1: + return near[0] + if near: + return None # mehrdeutig -> nicht verlinken + if len(cands) == 1: + return cands[0] + return None # mehrdeutig -> nicht verlinken + +def source_label(v): + rid = v["rids"][0] if v["rids"] else "?" + if v["is_own"]: + return f"eigenes Tier (RennmausPro-Nr. {rid})" + return f"externer Ahn (RennmausPro-Nr. {rid})" + +def fmt_variant(letter, v, match): + dob = ", ".join(v["dob"]) or "unbekannt" + farbe = ", ".join(v["farbe"]) or "unbekannt" + orig = ", ".join(v["origin"]) or "unbekannt" + cnt = f", {v['n']}× im Import" if v["n"] > 1 else "" + link = f" — [Tier ansehen](/rennmaeuse/{match['id']})" if match else "" + return (f"• **{letter}**: {source_label(v)} — geboren {dob}, Farbe {farbe}, " + f"Herkunft {orig}{cnt}{link}") + +def build_question(name, rows): + lines = [ + f"Beim Import aus RennmausPro gibt es mehrere Tiere mit dem Namen „{name}\". " + "Manchmal hat RennmausPro dasselbe Tier mehrfach gespeichert, manchmal sind es " + "wirklich verschiedene Tiere mit zufällig gleichem Namen.", + "", + "Welche davon sind **dasselbe Tier**, welche sind **verschiedene**? " + "Die Herkunft jeder Variante steht dabei:", + "", + ] + for letter, v, match in rows: + lines.append(fmt_variant(letter, v, match)) + lines += [ + "", + "Antworte am einfachsten mit den Buchstaben, z. B.: " + "„A und B sind dasselbe Tier, C ist ein anderes\" · „alle dasselbe\" · „alle verschieden\".", + ] + return "\n".join(lines) + +def main(): + db = sys.argv[1] + scratch = sys.argv[2] + out = sys.argv[3] if len(sys.argv) > 3 else None + + R = C.load_rpro3(db) + _, ambiguous, _ = C.dedup(R["animals"]) + idx_full, idx_strip = build_current_index(scratch) + + tickets = [] + for name, variants in ambiguous: + info = [v for v in variants if informative(v)] + if len(info) < 2: + continue + info.sort(key=lambda v: (not v["is_own"], -v["n"])) + + rows, matches = [], [] + for i, v in enumerate(info): + dob = parse_dob(v["dob"][0]) if v["dob"] else None + m = match_current(name, dob, idx_full, idx_strip) + rows.append((LETTERS[i], v, m)) + matches.append(m) + + # Ticket-GerbilId: bevorzugt das gematchte eigene Tier, sonst erster Treffer. + primary = next((m for (l, v, m) in rows if v["is_own"] and m), None) or next((m for m in matches if m), None) + + agent_ctx = { + "source": "rpro3-ambiguity", + "name": name, + "variants": [ + {"label": LETTERS[i], "rids": v["rids"], "count": v["n"], + "dob": v["dob"], "farbe": v["farbe"], "origin": v["origin"], + "is_own": v["is_own"], "matched_gerbil_id": (matches[i]["id"] if matches[i] else None)} + for i, v in enumerate(info) + ], + } + tickets.append({ + "Message": f"RennmausPro-Import: Name „{name}\" kommt mehrfach vor " + f"({len(info)} Varianten) — dasselbe Tier oder verschiedene?", + "Context": "rpro3-import", + "EntityName": name, + "GerbilId": primary["id"] if primary else None, + "Question": build_question(name, rows), + "AgentContext": json.dumps(agent_ctx, ensure_ascii=False), + "_own": any(v["is_own"] for v in info), + "_nvars": len(info), + "_linked": sum(1 for m in matches if m), + }) + + tickets.sort(key=lambda t: (not t["_own"], -t["_nvars"])) + linked = sum(t["_linked"] for t in tickets) + print(f"Ticket-Kandidaten: {len(tickets)} (eigenes Tier beteiligt: {sum(1 for t in tickets if t['_own'])})") + print(f"Varianten mit Tier-Hyperlink: {linked}") + print(f"Tickets mit GerbilId (Kopf-Link): {sum(1 for t in tickets if t['GerbilId'])}") + if out: + json.dump(tickets, open(out, "w", encoding="utf-8"), ensure_ascii=False, indent=2) + print(f"geschrieben: {out}") + +if __name__ == "__main__": + main()