diff --git a/tools/import/README.md b/tools/import/README.md index 2b99236..537d319 100644 --- a/tools/import/README.md +++ b/tools/import/README.md @@ -19,12 +19,29 @@ See the format analysis in `FEAT-8a-format-spec.md` (Pam's hive workspace). - Embedded photos (`xl/media`) → `output/photos//`, mapped to the animal by drawing anchor position. 2. **Dedup + review (stage 2)** - - Merge animals on `normalise(name) + DOB` (corroborated by DOB+genotype). + - Merge animals on `normalise(call-name) + DOB`, with the **Zucht as + discriminator** (Julian's ruling: Wurfchronik `[brackets]` ≡ Stammbaum + `of/von ` suffix — both are the breeding line; same name+DOB but + different Zucht stays two animals). + - Match animals onto Wurfchronik litters (`litterRef`) via DOB + (Vater, + Mutter) — the Pam-validated build order (chronicle litters are canonical). - Emit a German-language `output/review-report.md` for the breeder to verify - (merges, **conflicts**, ambiguous/incomplete entries, unmapped genotype tokens). + (merges, **conflicts**, ambiguous/incomplete entries, unmapped genotype + tokens, litter data-quality warnings). - **Nothing is loaded into the database** — stage 3 (API load) is separate and waits on DATA-2 + FEAT-1b phase 2. +### Wurfchronik column semantics (Julian, authoritative) + +`A` Wurfbezeichnung · `B` Geburtsdatum · `C` Mutter · `D` Vater (`[…]` = Zucht, +`&` = multiple sires) · `E` **survivedToGoHome** (Tabelle1 only, unlabeled — +detected positionally) · `F` Wurfstärke → `totalBorn` · `G` breakdown +`Männchen,Weibchen,TG,s` → `males/females/stillborn/diedLater` (`s` = died +after birth, before Abgabe) · last column → `note`. Validation: `E` should +equal `F − TG − s`; mismatches become German warnings in the review report +(data-quality signal, not an import blocker). A few Tabelle2 rows shift these +columns — they are read value-adaptively and flagged with a warning. + Genotypes are mapped to the frozen 8-locus contract (A C D E G P Sp Re) while preserving everything: `genotype.mapped8locus`, `genotype.rawGenotype` (verbatim), `genotype.unmappedTokens` (e.g. the `Uw` locus, markers `WFNZ/WP/DP`). A `-` diff --git a/tools/import/extract.py b/tools/import/extract.py index 5b3b885..9221b8c 100644 --- a/tools/import/extract.py +++ b/tools/import/extract.py @@ -83,6 +83,53 @@ def clean_name(raw): return n +# --- (name, Zucht) canonicalisation ------------------------------------------- +# Julian (FEAT-8 ruling): the [brackets] in Wurfchronik names ARE the Zucht +# (breeding line) and are equivalent to the Stammbaum "of/von " suffix. +# Both fold into one canonical (call-name, zucht) pair; the Zucht acts as a +# dedup DISCRIMINATOR (same name+DOB but different Zucht = different animal). +_BRACKET_ZUCHT = re.compile(r"^(.*?)\s*\[([^\]]+)\]\s*$") +_SUFFIX_ZUCHT = re.compile( + r"^(.+?)\s+(?:of|von\s+den|von\s+der|v\.\s?d\.|von)\s+(.+)$", re.IGNORECASE) +# canonical values are post-norm_zucht (word-final 'n' folded: "kleinechaote") +ZUCHT_ALIASES = { + "zdkc": "kleinechaote", # "Zucht der kleinen Chaoten" (home cattery shorthand) +} + + +def split_name_zucht(raw): + """'Luna [ZdkC]' -> ('Luna','ZdkC'); 'Pikachu of Black Forest' -> + ('Pikachu','Black Forest'); plain names -> (name, '').""" + n = (raw or "").strip() + m = _BRACKET_ZUCHT.match(n) + if m: + return m.group(1).strip(), m.group(2).strip() + m = _SUFFIX_ZUCHT.match(n) + if m: + return m.group(1).strip(), m.group(2).strip() + return n, "" + + +def norm_zucht(z): + """Canonical Zucht key: drops Zucht/von/der fillers, folds declension + ('kleinen Chaoten' == 'kleine Chaoten'), resolves known shorthands.""" + if not z: + return "" + n = z.lower() + n = re.sub(r"\bv\.\s?d\.\b", " ", n) + n = re.sub(r"\b(zucht|privatzucht|der|die|den|des|dem|von|of)\b", " ", n) + n = re.sub(r"[^a-z0-9äöüß ]", " ", n) + words = [w[:-1] if len(w) > 4 and w.endswith("n") else w for w in n.split()] + key = "".join(words) + return ZUCHT_ALIASES.get(key, key) + + +def canon_pair(raw): + """Full raw name -> (normalised call-name, canonical zucht).""" + name, zucht = split_name_zucht(raw) + return norm_name(name), norm_zucht(zucht) + + # --------------------------------------------------- Stammbaum extraction ---- def parse_detail(text): """From a string that contains *DOB and/or genotype, pull (dob, death, geno_str). @@ -167,6 +214,10 @@ def extract_stammbaum(path): g = parse_detail(t) if compact else (dob, death, geno) genodict = gt.parse(geno) + # Zucht: from the name's [tag]/of-von suffix, else from the breeder line + _, zraw = split_name_zucht(name) + if not zraw and breeder: + zraw = breeder animals.append({ "id": None, # assigned in dedup "name": name, @@ -177,6 +228,7 @@ def extract_stammbaum(path): "farbschlag": farbschlag, "genotype": genodict, "breeder": breeder, + "zucht": zraw, "parentRefs": [], "photos": [], "sourceFiles": [fname], @@ -184,6 +236,7 @@ def extract_stammbaum(path): "_col": c, "_row": r, "_file": fname, + "_zucht": norm_zucht(zraw), }) # name-pair cells "X & Y" (deepest generation, names only) @@ -194,12 +247,14 @@ def extract_stammbaum(path): for part in t.split(" & "): part = clean_name(part) if part: + _, zraw = split_name_zucht(part) animals.append({ "id": None, "name": part, "nameVariants": [], "dob": "", "death": "", "gender": None, "farbschlag": "", - "genotype": gt.parse(""), "breeder": "", "parentRefs": [], - "photos": [], "sourceFiles": [fname], + "genotype": gt.parse(""), "breeder": "", "zucht": zraw, + "parentRefs": [], "photos": [], "sourceFiles": [fname], "_gen": gen_of(c), "_col": c, "_row": r, "_file": fname, + "_zucht": norm_zucht(zraw), }) _reconstruct_parents(animals) @@ -297,7 +352,19 @@ def extract_wurfchronik(path): col_ws = find("ws", "wurfstärke", "wurfstaerke") col_breakdown = find("männchen", "maennchen", "weibchen") col_zn = find("zuchtnummer") - col_note = find("bemerkung") + col_note = find("bemerkung", "anmerkung") + # Julian (authoritative): Tabelle1 has an UNLABELED numeric column between + # Vater and Wurfstärke = "Überlebende bis zum Abgabedatum" (survivors to + # go-home). Tabelle2 dropped it. Header-based find() cannot see it, so + # detect it positionally. + col_survived = None + if col_sire and col_ws and col_ws - col_sire > 1: + mapped = {col_id, col_date, col_dam, col_sire, col_ws, + col_breakdown, col_zn, col_note} + for c in range(col_sire + 1, col_ws): + if c not in mapped: + col_survived = c + break sheet_name = os.path.basename(sp) for r in sorted(rows): if r == hdr_row: # skip the header row itself @@ -310,25 +377,86 @@ def extract_wurfchronik(path): if "jahr" in " ".join(row.values()).lower() and not DOB.search(txt_b): continue dob = DOB.search(txt_b) - bd = row.get(col_breakdown, "") if col_breakdown else "" - m = re.findall(r"\d+", bd) - breakdown = {} - if len(m) >= 1: - keys = ["maennchen", "weibchen", "totgeburt", "s"] - for k, val in zip(keys, m): - breakdown[k] = int(val) lid = row.get(col_id, "") datestr = dob.group(1) if dob else "" + dam_raw = row.get(col_dam, "") if col_dam else "" + sire_raw = row.get(col_sire, "") if col_sire else "" + _, dam_zucht = split_name_zucht(dam_raw) + # '&' = multiple sires possible (litter with uncertain/dual sire) + sire_names = [s.strip() for s in sire_raw.split("&") if s.strip()] + _, sire_zucht = split_name_zucht(sire_names[0] if sire_names else "") + + # Numeric layout is read VALUE-ADAPTIVELY per row: a handful of + # Tabelle2 rows insert an extra numeric column (Überlebende, T1 + # order E,F) before WS and shift the breakdown right of its header. + warnings = [] + scan_end = (col_note or (col_sire or 4) + 6) + 1 + bd_col = None + for c in range((col_sire or 4) + 1, scan_end): + if re.fullmatch(r"\d+(?:[,;]\d+){1,3}", row.get(c, "")): + bd_col = c + break + if bd_col and col_breakdown and bd_col != col_breakdown: + warnings.append( + f"Spaltenschema-Abweichung: Geschlechter-Aufschlüsselung in " + f"Spalte {xu.num_to_col(bd_col)} statt " + f"{xu.num_to_col(col_breakdown)} gefunden") + # single numeric cells between Vater and the breakdown: E (Überlebende) + # and/or F (Wurfstärke), in T1 order + singles = [] + for c in range((col_sire or 4) + 1, bd_col or scan_end): + v = row.get(c, "") + if re.fullmatch(r"\d+", v): + singles.append(int(v)) + if len(singles) >= 2: + survived, total_born = singles[0], singles[1] + if not col_survived: + warnings.append( + "Spaltenschema-Abweichung: zusätzliche Zahlenspalte als " + "„Überlebende“ interpretiert (bitte prüfen)") + elif len(singles) == 1: + total_born = singles[0] + survived = None + else: + total_born = _to_int(row.get(col_ws)) if col_ws else None + survived = _to_int(row.get(col_survived)) if col_survived else None + + # breakdown "2,0,2,0" = Männchen, Weibchen, Totgeburt, später + # verstorben ('s' = died after birth, before Abgabe — Julian). + bd = row.get(bd_col, "") if bd_col else "" + m = re.findall(r"\d+", bd) + males = females = stillborn = died_later = None + if m: + vals = [int(x) for x in m] + [None] * 4 + males, females, stillborn, died_later = vals[:4] + # Validation (Julian): Überlebende E should equal F − TG − s. + # Mismatch = data-quality signal for the review report, NOT a blocker. + if survived is not None and total_born is not None: + expected = total_born - (stillborn or 0) - (died_later or 0) + if survived != expected: + warnings.append( + f"Überlebende ({survived}) ≠ Wurfstärke ({total_born}) " + f"− Totgeburten ({stillborn or 0}) − später verstorben " + f"({died_later or 0}) = {expected}") litters.append({ "id": f"{sheet_name.replace('.xml','')}-{lid}-{norm_dob(datestr)}", "litterId": lid, "date": datestr, - "damName": row.get(col_dam, "") if col_dam else "", - "sireName": row.get(col_sire, "") if col_sire else "", - "wurfstaerke": _to_int(row.get(col_ws)) if col_ws else None, - "sexBreakdown": breakdown, + "damName": dam_raw, + "damZucht": dam_zucht, + "sireName": sire_raw, + "sireNames": sire_names, + "sireZucht": sire_zucht, + "totalBorn": total_born, + "survivedToGoHome": survived, + "males": males, + "females": females, + "stillborn": stillborn, + "diedLater": died_later, + "breakdownRaw": bd, "zuchtnummer": row.get(col_zn, "") if col_zn else "", "note": row.get(col_note, "") if col_note else "", + "warnings": warnings, "sourceFile": fname, "sheet": sheet_name, "row": r, @@ -345,20 +473,48 @@ def _to_int(s): # ------------------------------------------------------------- stage 2: dedup def dedup(animals): - """Merge by normalise(name)+DOB. Returns (merged, conflicts, orphans).""" + """Merge by normalise(call-name)+DOB, with the canonical Zucht as + DISCRIMINATOR (Julian: same name+DOB but different Zucht = different + animal). Returns (merged, conflicts, orphans, zucht_splits).""" groups = {} orphans = [] for a in animals: - key = (norm_name(a["name"]), norm_dob(a["dob"])) + call, _ = split_name_zucht(a["name"]) + key = (norm_name(call), norm_dob(a["dob"])) if not key[0] or not key[1]: orphans.append(a) # orphans still get a stable id but are not merged key = ("__orphan__", id(a)) groups.setdefault(key, []).append(a) + # split groups whose members carry DIFFERENT (non-empty) canonical Zuchten; + # members without a Zucht merge into the group only if it is unambiguous. + final_groups = [] + zucht_splits = [] + for key, grp in groups.items(): + by_zucht = {} + for a in grp: + by_zucht.setdefault(a.get("_zucht", ""), []).append(a) + nonempty = sorted(z for z in by_zucht if z) + if len(nonempty) <= 1: + final_groups.append(grp) + continue + # genuine split: same call-name+DOB, different Zucht + for z in nonempty: + final_groups.append(by_zucht[z]) + if "" in by_zucht: + # Zucht-less mentions cannot be attributed -> own (flagged) entry + final_groups.append(by_zucht[""]) + zucht_splits.append({ + "name": grp[0]["name"], + "dob": norm_dob(grp[0]["dob"]), + "zuechte": sorted(set(a["zucht"] for a in grp if a.get("zucht"))), + "files": sorted(set(f for a in grp for f in a["sourceFiles"])), + }) + merged = [] conflicts = [] - for key, grp in groups.items(): + for grp in final_groups: base = dict(grp[0]) variants = set([base["name"]]) files = set(base["sourceFiles"]) @@ -392,6 +548,8 @@ def dedup(animals): "farbschlagVariants": sorted(farb), "genotype": best, "breeder": next((a["breeder"] for a in grp if a["breeder"]), ""), + "zucht": next((a["zucht"] for a in grp if a.get("zucht")), ""), + "zuchtCanon": next((a["_zucht"] for a in grp if a.get("_zucht")), ""), "parentRefs": _dedup_parentrefs(parent_refs), "photos": sorted(set(photos)), "sourceFiles": sorted(files), @@ -406,7 +564,64 @@ def dedup(animals): "deaths": sorted(deaths), "files": sorted(files), }) merged.sort(key=lambda a: (a["dob"], a["name"])) - return merged, conflicts, orphans + # ids must stay unique even when a Zucht split duplicates name+DOB slugs + seen_ids = {} + for a in merged: + if a["id"] in seen_ids: + seen_ids[a["id"]] += 1 + a["id"] = f"{a['id']}-{seen_ids[a['id']]}" + else: + seen_ids[a["id"]] = 1 + return merged, conflicts, orphans, zucht_splits + + +# ------------------------------------------- animal <-> litter matching ------ +def match_litters(merged, litters): + """Attach each animal to its Wurfchronik litter (Pam-validated build order: + litters are canonical, animals match onto them via DOB + (Vater, Mutter)). + Sets a['litterRef']; returns match statistics for the report.""" + by_date = {} + for l in litters: + d = norm_dob(l["date"]) + if d: + by_date.setdefault(d, []).append(l) + + stats = {"parents": 0, "dateOnly": 0, "ambiguous": 0} + for a in merged: + if not a["dob"]: + continue + cands = by_date.get(a["dob"]) + if not cands: + continue + a_parents = set() + for ref in a["parentRefs"]: + cn, _ = canon_pair(ref["name"]) + if cn: + a_parents.add(cn) + + def score(l): + s = 0 + for nm in [l["damName"]] + l.get("sireNames", []): + cn, _ = canon_pair(nm) + if cn and cn in a_parents: + s += 1 + return s + + scored = sorted(((score(l), l["id"]) for l in cands), reverse=True) + best_score, best_id = scored[0] + if best_score > 0 and (len(scored) == 1 or scored[1][0] < best_score): + a["litterRef"] = {"litterId": best_id, + "method": "geburtsdatum+eltern", "confidence": "hoch"} + stats["parents"] += 1 + elif len(cands) == 1: + a["litterRef"] = {"litterId": cands[0]["id"], + "method": "nur-geburtsdatum", "confidence": "niedrig"} + stats["dateOnly"] += 1 + else: + a["litterRef"] = {"litterId": None, "method": "mehrdeutig", + "candidates": [l["id"] for l in cands]} + stats["ambiguous"] += 1 + return stats def _dedup_parentrefs(refs): @@ -419,12 +634,13 @@ def _dedup_parentrefs(refs): # ------------------------------------------------------------------ reporting -def write_report(merged, conflicts, orphans, raw_count, litters, photo_count): - keyset = set((a["dob"], norm_name(a["name"])) for a in merged) +def write_report(merged, conflicts, orphans, raw_count, litters, photo_count, + zucht_splits, match_stats): multi = [a for a in merged if a["mentions"] > 1] with_dob = [a for a in merged if a["dob"]] - lit_dates = set(norm_dob(l["date"]) for l in litters if l["date"]) - joinable = [a for a in merged if a["dob"] and norm_dob(a["dob"]) in lit_dates] + val_warn = [l for l in litters if any("≠" in w for w in l["warnings"])] + schema_warn = [l for l in litters if any("Abweichung" in w for w in l["warnings"])] + matched = match_stats["parents"] + match_stats["dateOnly"] L = [] L.append("# FEAT-8b — Import-Vorschau & Prüfbericht (Stammbäume + Wurfchronik)\n") @@ -439,12 +655,32 @@ def write_report(merged, conflicts, orphans, raw_count, litters, photo_count): L.append(f"- Mehrdeutige / unvollständige Einträge (ohne Name+Datum): **{len(orphans)}**") L.append(f"- Fotos zugeordnet: **{photo_count}**") L.append(f"- Würfe aus der Wurfchronik: **{len(litters)}**") - L.append(f" - Tiere, deren Geburtsdatum zu einem Wurf passt (verknüpfbar): {len(joinable)}\n") + L.append(f" - Tiere mit Wurf verknüpft: **{matched}** " + f"(davon über Geburtsdatum **und** Eltern: {match_stats['parents']}, " + f"nur über Geburtsdatum: {match_stats['dateOnly']}; " + f"mehrdeutig: {match_stats['ambiguous']})") + L.append(f" - Würfe mit Datenqualitäts-Hinweisen: {len(val_warn)} " + f"(+ {len(schema_warn)} Zeilen mit abweichendem Spaltenschema)\n") L.append("## Zusammenführungs-Schlüssel\n") - L.append("Tiere wurden zusammengeführt über **normalisierter Name + Geburtsdatum**. " - "Namensvarianten (z. B. `v.d.` ↔ `von den`, `gen.`-Spitznamen, Zuchtsuffixe) " - "werden als `nameVariants` erhalten.\n") + L.append("Tiere wurden zusammengeführt über **normalisierter Rufname + Geburtsdatum**, " + "mit der **Zucht als Unterscheidungsmerkmal** (Julians Regel: die `[Klammern]` " + "in der Wurfchronik und das `of/von `-Suffix der Stammbäume bezeichnen " + "beide die Zucht und werden zusammengeführt — z. B. `[ZdkC]` ≙ " + "`von den Kleinen Chaoten`). Namensvarianten (z. B. `v.d.` ↔ `von den`, " + "`gen.`-Spitznamen) werden als `nameVariants` erhalten.\n") + + if zucht_splits: + L.append("### Gleicher Name + Geburtsdatum, aber unterschiedliche Zucht " + "(NICHT zusammengeführt — bitte prüfen)\n") + L.append("| Tier | Geburtsdatum | Zuchten | Dateien |") + L.append("|---|---|---|---|") + for s in zucht_splits[:50]: + L.append("| {} | {} | {} | {} |".format( + split_name_zucht(s["name"])[0], s["dob"], + " // ".join(s["zuechte"]), + ", ".join(os.path.splitext(f)[0] for f in s["files"]))) + L.append("") L.append("## ⚠️ Konflikte (bitte prüfen)\n") if conflicts: @@ -506,6 +742,37 @@ def write_report(merged, conflicts, orphans, raw_count, litters, photo_count): for t, n in tok.most_common(25): L.append(f"| `{t}` | {n} | {hint.get(t, '?')} |") + # Wurfchronik data quality (Julian: E sollte = F − TG − s sein) + L.append("\n## Wurfchronik — Datenqualitäts-Hinweise\n") + L.append("Julians Spaltenregel: **Überlebende bis Abgabe (E) = Wurfstärke (F) − " + "Totgeburten (TG) − später verstorben (s)**. Bei diesen Würfen geht die " + "Rechnung nicht auf — kein Import-Hindernis, aber ein Hinweis auf " + "Tippfehler oder fehlende Einträge:\n") + if val_warn: + L.append("| Wurf | Datum | Mutter × Vater | Hinweis |") + L.append("|---|---|---|---|") + for l in val_warn[:120]: + L.append("| {} | {} | {} × {} | {} |".format( + l["litterId"], l["date"], l["damName"], l["sireName"], + "; ".join(w for w in l["warnings"] if "≠" in w))) + if len(val_warn) > 120: + L.append(f"\n… und {len(val_warn) - 120} weitere (siehe `litters.json`).") + else: + L.append("_Keine — alle Würfe sind rechnerisch konsistent._") + + L.append("\n### Zeilen mit abweichendem Spaltenschema (automatisch interpretiert)\n") + L.append(f"{len(schema_warn)} Zeilen (überwiegend Tabelle2 ab 2014) tragen eine " + "zusätzliche Zahlenspalte vor der Wurfstärke bzw. eine verschobene " + "Geschlechter-Aufschlüsselung. Sie wurden nach dem Muster von Tabelle1 " + "gelesen (**Überlebende, Wurfstärke, Aufschlüsselung**) — bei " + f"{sum(1 for l in schema_warn if not any('≠' in w for w in l['warnings']))} " + "davon geht die Rechnung E = F − TG − s damit exakt auf, was die Lesart " + "bestätigt. Alle betroffenen Zeilen sind in `litters.json` mit " + "`warnings` markiert. Beispiele:\n") + for l in schema_warn[:8]: + L.append(f"- Wurf {l['litterId']} ({l['date']}): E={l['survivedToGoHome']}, " + f"F={l['totalBorn']}, Aufschlüsselung `{l['breakdownRaw']}`") + L.append("\n## Hinweise für den Import (Stufe 3, später)\n") L.append("- **Wurfchronik = Quelle der Würfe** (Datum, Wurfstärke, Eltern, Zuchtnummer); " "**Stammbäume = Abstammung + Genotyp + Fotos**. Verknüpfung über Geburtsdatum + Elternnamen.") @@ -550,7 +817,8 @@ def main(): litters = extract_wurfchronik(args.wurfchronik) print(f"Wurfchronik: {len(litters)} Würfe") - merged, conflicts, orphans = dedup(raw_animals) + merged, conflicts, orphans, zucht_splits = dedup(raw_animals) + match_stats = match_litters(merged, litters) photo_count = sum(len(a["photos"]) for a in merged) # strip private (_) fields from the JSON output @@ -562,10 +830,15 @@ def main(): with open(os.path.join(OUT, "litters.json"), "w", encoding="utf-8") as f: json.dump(litters, f, ensure_ascii=False, indent=2) - write_report(merged, conflicts, orphans, len(raw_animals), litters, photo_count) + write_report(merged, conflicts, orphans, len(raw_animals), litters, photo_count, + zucht_splits, match_stats) print(f"\nRoh: {len(raw_animals)} → eindeutig: {len(merged)} " - f"| Konflikte: {len(conflicts)} | Orphans: {len(orphans)} | Fotos: {photo_count}") + f"| Konflikte: {len(conflicts)} | Zucht-Splits: {len(zucht_splits)} " + f"| Orphans: {len(orphans)} | Fotos: {photo_count}") + print(f"Wurf-Verknüpfung: {match_stats['parents']} (Datum+Eltern), " + f"{match_stats['dateOnly']} (nur Datum), {match_stats['ambiguous']} mehrdeutig " + f"| Wurf-Warnungen: {sum(1 for l in litters if l['warnings'])}") print(f"Ausgabe in {OUT}") diff --git a/tools/import/output/review-report.md b/tools/import/output/review-report.md index 9fc00e3..89f06d6 100644 --- a/tools/import/output/review-report.md +++ b/tools/import/output/review-report.md @@ -5,18 +5,19 @@ _Automatisch erzeugt von `tools/import/extract.py` — **noch nichts in die Date ## Überblick - Rohe Tier-Einträge aus den Stammbäumen: **889** -- Nach Zusammenführung (eindeutige Tiere): **587** - - davon mit Geburtsdatum: 292 - - in mehreren Dateien gefunden (Dubletten zusammengeführt): 142 -- Konflikte zur Klärung: **24** +- Nach Zusammenführung (eindeutige Tiere): **574** + - davon mit Geburtsdatum: 279 + - in mehreren Dateien gefunden (Dubletten zusammengeführt): 146 +- Konflikte zur Klärung: **32** - Mehrdeutige / unvollständige Einträge (ohne Name+Datum): **310** - Fotos zugeordnet: **123** - Würfe aus der Wurfchronik: **752** - - Tiere, deren Geburtsdatum zu einem Wurf passt (verknüpfbar): 152 + - Tiere mit Wurf verknüpft: **135** (davon über Geburtsdatum **und** Eltern: 95, nur über Geburtsdatum: 40; mehrdeutig: 9) + - Würfe mit Datenqualitäts-Hinweisen: 113 (+ 138 Zeilen mit abweichendem Spaltenschema) ## Zusammenführungs-Schlüssel -Tiere wurden zusammengeführt über **normalisierter Name + Geburtsdatum**. Namensvarianten (z. B. `v.d.` ↔ `von den`, `gen.`-Spitznamen, Zuchtsuffixe) werden als `nameVariants` erhalten. +Tiere wurden zusammengeführt über **normalisierter Rufname + Geburtsdatum**, mit der **Zucht als Unterscheidungsmerkmal** (Julians Regel: die `[Klammern]` in der Wurfchronik und das `of/von `-Suffix der Stammbäume bezeichnen beide die Zucht und werden zusammengeführt — z. B. `[ZdkC]` ≙ `von den Kleinen Chaoten`). Namensvarianten (z. B. `v.d.` ↔ `von den`, `gen.`-Spitznamen) werden als `nameVariants` erhalten. ## ⚠️ Konflikte (bitte prüfen) @@ -24,13 +25,17 @@ Gleiches Tier (Name+Datum), aber widersprüchliche Angaben in verschiedenen Date | Tier | Geburtsdatum | abweichende Genotypen | abweichende Farbschläge | Sterbedaten | Dateien | |---|---|---|---|---|---| +| Ella | 10.06.2019 | Aa C D- ee[f] GG P- spsp // Aa Cc[chm] D- ee[f] UwUw P- spsp | Algierfuchsschimmel, hell | 03.02.2023 | Stammbaum von Akio Kids, Stammbaum von CP-Fuchs, CP-Sa Sp von Unity, Stammbaum von Valentino Firehearts Kids | +| ZoneFire | 07.12.2020 | Aa c[chm]c[chm] D- Ee Gg P- Spsp | CP-Agouti Kragenschecke // Kalea von den Kleinen Chaoten | — | Stammbaum von Akio Kids | | Louis von den Kleinen Chaoten | 15.07.2017 | Aa Cc[] D- Ee Gg P- spsp // Aa Cc[chm] D- Ee Uwuw[d] P- spsp | Roswitha von den Kleinen Chaoten | 01.07.2020 | Stammbaum von Akio Kids, Stammbaum von CP-Fuchs, CP-Sa Sp von Unity | | Roswitha von den Kleinen Chaoten | 10.09.2018 | aa CC D- ee[f] Gg P- spsp // aa CC D- ee[f] Uwuw[d] P- spsp | — | 05.08.2021 | Stammbaum von Akio Kids, Stammbaum von CP-Fuchs, CP-Sa Sp von Unity | | Firefly von den Kleinen Chaoten | 18.12.2019 | /+, Aa c[chm]c[chm] D- Ee Gg PP Spsp // Aa c[chm]c[chm] DD Ee Gg PP Spsp | — | 2024 | Stammbaum von Akio Kids, Stammbaum von CP-Fuchs, CP-Sa Sp von Unity, Stammbaum von Fire Kids, Stammbaum von Valentino Firehearts Kids | | Zuleika von den Kleinen Chaoten | 24.10.2015 | aa c[chm]c[h] D- E G P- spsp // aa c[chm]c[h] D- Ee Gg P- spsp // aa c[chm]c[h] DD Ee Gg P- spsp | — | 24.02.2019 | Stammbaum von Akio Kids, Stammbaum von CP-Fuchs, CP-Sa Sp von Unity, Stammbaum von Valentino Firehearts Kids | | WildFire von den Kleinen Chaoten | 05.10.2017 | aa c[chm]c[chm] D- Ee gg P- spsp // aa c[chm]c[chm] D- Ee gg PP spsp | — | — | Stammbaum von Akio Kids, Stammbaum von CP-Fuchs, CP-Sa Sp von Unity, Stammbaum von Fire Kids, Stammbaum von Valentino Firehearts Kids | +| Vestra von den Schlossmäusen | 08.02.2019 | Aa Cc[chm] D- EE GG PP Spsp [WP] // Aa Cc[chm] DD EE GG PP Spsp [WP] | — | 26.05.2023 | Stammbaum von Akio Kids, Stammbaum von CP-Fuchs, CP-Sa Sp von Unity, Stammbaum von Fire Kids, Stammbaum von Valentino Firehearts Kids | | Flint von den Kleinen Chaoten | 23.12.2017 | aa Cc[chm] D- ee Gg P- spsp | — | 10.05.2021 // 10.05.2022 | Stammbaum von Akio Kids, Stammbaum von CP-Fuchs, CP-Sa Sp von Unity | | Silenos gen. Adonis v.d. Kleinen Chaoten | 11.10.2015 | aa Cc[chm] D- Ee Gg PP spsp // aa Cc[chm] D- Ee Uwuw[d] PP spsp | — | 18.07.2019 | Stammbaum von Akio Kids, Stammbaum von Goldfuchs Sp (Pikachu) Kids, Stammbaum von Watarus Kids | +| Bruno of Black Forest | 01.06.2022 | aa C- dd Ee Gg P- spsp | Blau // Mystique of Black Forest | — | Stammbaum von Alberto Kids, Stammbaum von Fire Kids, Stammbaum von Stella Kids | | Milka of LennyLengo | 09.12.2018 | aa C- dd E- Gg P- Spsp // aa Cc[h] dd EE Gg P- Spsp | — | 22.12.2021 | Stammbaum von Alberto Kids, Stammbaum von Stella Kids | | Hedwig of BGB | 30.10.2019 | aa CC DD E- G- P- Spsp WP // aa CC DD E- G- P- Spsp WP DP (hörend) | — | 30.08.2023 | Stammbaum von Alberto Kids, Stammbaum von Fire Kids, Stammbaum von Stella Kids | | Silvain von den Kleinen Chaoten | 27.03.2022 | aa c[chm]c[chm] Dd Ee[-] Gg P- Spsp // aa c[chm]c[chm] Dd ee[-] Gg Pp Spsp | — | 31.12.2024 | Stammbaum von Alberto Kids, Stammbaum von CP-Fuchs, CP-Sa Sp von Unity | @@ -41,10 +46,14 @@ Gleiches Tier (Name+Datum), aber widersprüchliche Angaben in verschiedenen Date | Molly of Black Forest | 13.09.2021 | /+, Aa Cc[chm] D- Ee gg P- spsp // Aa Cc[chm] Dd Ee gg Pp spsp | — | 03.05.2021 | Stammbaum von Alberto Kids, Stammbaum von CP-Fuchs, CP-Sa Sp von Unity | | Little Runner's Big Ben | 03.02.2020 | Aa Cc[chm] DD Ee Gg PP Spsp // Aa Cc[chm] DD Ee Gg Pp Spsp | Daja of Little Rose | 14.10.2023 | Stammbaum von CP-Fuchs, CP-Sa Sp von Unity, Stammbaum von Fire Kids, Stammbaum von Goldfuchs Sp (Pikachu) Kids, Stammbaum von Kohlief, Goldfuchsef Sp von Chrissi, Stammbaum von Valentino Firehearts Kids, Stammbaum von Watarus Kids | | Daja of Little Rose | 16.05.2021 | aa chmchm D- EE Gg P- // aa chmchm D- EE Gg P- spsp | — | — | Stammbaum von CP-Fuchs, CP-Sa Sp von Unity, Stammbaum von Fire Kids, Stammbaum von Valentino Firehearts Kids | +| Vance Jr. von den Kleinen Chaoten | 10.04.2022 | aa Cc[hm] Dd Ee gg P- Spsp // aa Cc[hm] Dd Ee gg P- spsp | Kohlfuchs, hell // Velvet von den Kleinen Chaoten | — | Stammbaum von Fire Kids, Stammbaum von Stella Kids | | Ichika von den Kleinen Chaoten | 19.04.2020 | aa CC D- ee Gg pp spsp // aa CC D- ee[f] Gg pp spsp | — | 27.11.2023 | Stammbaum von Goldfuchs Sp (Pikachu) Kids, Stammbaum von Watarus Kids | -| Victoria Welby gen. Welby v.d. Kleinen Chaoten | 16.01.2023 | Aa CC D- Ee[f] Gg pp Spsp [DP] // Aa CC D- ee[f] Gg pp Spsp [DP] | — | 17.02.2026 | Stammbaum von Goldfuchs Sp (Pikachu) Kids, Stammbaum von Watarus Kids | +| Trogir von den Kleinen Chaoten | 21.03.2022 | Aa CC DD EE GG pp Spsp | Gold Ansatzschecke // Mahima von den Kleinen Chaoten | 29.05.2024 | Stammbaum von Goldfuchs Sp (Pikachu) Kids, Stammbaum von Kohlief, Goldfuchsef Sp von Chrissi, Stammbaum von Watarus Kids | +| Chayton v.d. Kleinen Chaoten (extern SC) | 04.02.2022 | aa Cc[-] D- e[f]e[f] Gg Pp spsp | Orangeschimmel, hell // Victoria Welby gen. Welby v.d. Kleinen Chaoten | 30.04.2024 | Stammbaum von Goldfuchs Sp (Pikachu) Kids, Stammbaum von Kohlief, Goldfuchsef Sp von Chrissi, Stammbaum von Watarus Kids | +| Victoria Welby gen. Welby v.d. Kleinen Chaoten | 16.01.2023 | Aa CC D- Ee[f] Gg pp Spsp [DP] // Aa CC D- ee[f] Gg pp Spsp [DP] | Goldfuchsschimmel Punktschecke DP | 17.02.2026 | Stammbaum von Goldfuchs Sp (Pikachu) Kids, Stammbaum von Kohlief, Goldfuchsef Sp von Chrissi, Stammbaum von Watarus Kids | | Zac gen. Action von den Kleinen Chaoten | 25.12.2020 | aa C- D- Ee G- Pp Spsp [DP] // aa CC D- Ee G- Pp Spsp [DP] | Belica gen. Emi von den Kleinen Chaoten | 31.01.2025 | Stammbaum von Goldfuchs Sp (Pikachu) Kids, Stammbaum von Kohlief, Goldfuchsef Sp von Chrissi, Stammbaum von Watarus Kids | | Chelsea von den Kleinen Chaoten | 02.04.2021 | /+, Aa CC Dd ee gg Pp spsp // Aa CC Dd ee gg Pp spsp | — | — | Stammbaum von Goldfuchs Sp (Pikachu) Kids, Stammbaum von Valentino Firehearts Kids | +| Chesnut | 13.11.2019 | aa C- D- ee[f] GG PP spsp | Kohlfuchsschimmel // Tennessee von den Kleinen Chaoten | 22.11.2023 | Stammbaum von Kentucky | | Ethan von den Kleinen Chaoten | 09.07.2020 | Aa Cc[chm] D- ee[f] Gg Pp Spsp | Ichika von den Kleinen Chaoten // Orangeschimmel, hell Kragenschecke | 30.07.2024 | Stammbaum von Kentucky, Stammbaum von Watarus Kids | | Quied Soldier of Black Forest | 07.06.2018 | /+, Aa C- D- ee[f] GG Pp Spsp [DP] // Aa C- D- ee[f] GG Pp Spsp DP | Hoshi von den Kleinen Chaoten | — | Stammbaum von Kentucky | | Hanami von den Kleinen Chaoten | 10.09.2015 | aa Cc[chm] D- Ee gg P- spsp | — | 12.12.2019 // 14.01.2020 | Stammbaum von Kentucky, Stammbaum von Stella Kids | @@ -143,20 +152,20 @@ Diese Tokens stehen weiter in `rawGenotype`/`unmappedTokens` — Entscheidung (M | Token | Vorkommen | Bedeutung (Vermutung) | |---|---|---| -| `[DP]` | 16 | Marker (Dunkelpigment?) | +| `[DP]` | 15 | Marker (Dunkelpigment?) | | `[WFNZ]` | 13 | Marker | -| `DP` | 10 | Marker | -| `WP` | 8 | Marker | +| `DP` | 9 | Marker | | `/+` | 8 | ? | +| `WP` | 7 | Marker | | `Uwuw[d]` | 4 | 9. Locus Uw (nicht im Modell) | -| `[WP]` | 3 | Marker | | `-g` | 2 | ? | | `C(C)` | 2 | Schreibweise (C trägt c) | -| `UwUw` | 2 | 9. Locus Uw | +| `[WP]` | 2 | Marker | | `chmchm` | 2 | Schreibweise (c[chm]c[chm]) | | `Cc[]` | 1 | ? | | `-psp` | 1 | ? | | `G(G)` | 1 | ? | +| `UwUw` | 1 | 9. Locus Uw | | `uw[d]uw[d]` | 1 | ? | | `[DP` | 1 | ? | | `Dea/dea]` | 1 | ? | @@ -168,6 +177,139 @@ Diese Tokens stehen weiter in `rawGenotype`/`unmappedTokens` — Entscheidung (M | `(hörend)` | 1 | ? | | `-DD` | 1 | ? | +## Wurfchronik — Datenqualitäts-Hinweise + +Julians Spaltenregel: **Überlebende bis Abgabe (E) = Wurfstärke (F) − Totgeburten (TG) − später verstorben (s)**. Bei diesen Würfen geht die Rechnung nicht auf — kein Import-Hindernis, aber ein Hinweis auf Tippfehler oder fehlende Einträge: + +| Wurf | Datum | Mutter × Vater | Hinweis | +|---|---|---|---| +| J | 27.10.2010 | Jana [little longnoses] × Danny [Privatzucht Maintal] | Überlebende (4) ≠ Wurfstärke (4) − Totgeburten (0) − später verstorben (1) = 3 | +| S | 13.10.2011 | Beatrice [Kleine Chaoten] × Alvin [Kleine Chaoten] | Überlebende (3) ≠ Wurfstärke (5) − Totgeburten (0) − später verstorben (0) = 5 | +| C1 | 24.03.2012 | Gina [Mauzis Renner] × Heaven [Privatzucht Maintal] | Überlebende (1) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| A2 | 27.01.2013 | Gwen gen. Sakura [little Angels] × Jamie [Kleine Chaoten] | Überlebende (6) ≠ Wurfstärke (6) − Totgeburten (0) − später verstorben (1) = 5 | +| P2 | 16.06.2013 | Mina [Strelitzie] × Feivel [Kleine Chaoten] | Überlebende (1) ≠ Wurfstärke (5) − Totgeburten (0) − später verstorben (0) = 5 | +| T2 | 14.07.2013 | Bea [Kleine Chaoten] × Silver [Kleine Chaoten] | Überlebende (2) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| O3 | 27.05.2014 | Kallie [Kleine Chaoten] × Digimon [Kleine Chaoten] | Überlebende (3) ≠ Wurfstärke (5) − Totgeburten (3) − später verstorben (0) = 2 | +| R3 | 26.06.2014 | Akina [Kleine Chaoten] × Paul [Privatzucht Gießen] | Überlebende (3) ≠ Wurfstärke (4) − Totgeburten (0) − später verstorben (0) = 4 | +| S3 | 04.07.2014 | Mira [Kleine Chaoten] × JD [Kleine Chaoten] | Überlebende (4) ≠ Wurfstärke (5) − Totgeburten (0) − später verstorben (0) = 5 | +| H4 | 25.10.2014 | Milena [Kleine Chaoten] × Benny [Kleine Chaoten] | Überlebende (2) ≠ Wurfstärke (2) − Totgeburten (1) − später verstorben (0) = 1 | +| I4 | 31.10.2014 | Lynn [Lenny Lengo] × Derek [Kleine Chaoten] | Überlebende (4) ≠ Wurfstärke (5) − Totgeburten (0) − später verstorben (0) = 5 | +| W4 | 14.01.2015 | Nelly [Kleine Chaoten] × Earl [Kleine Chaoten] | Überlebende (2) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| E6 | 12.05.2015 | Isa [golden lights] × Dastan | Überlebende (2) ≠ Wurfstärke (3) − Totgeburten (2) − später verstorben (0) = 1 | +| O6 | 21.06.2015 | Elena [KK-Chaos] × Cash [Kleine Chaoten] | Überlebende (2) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| Q6 | 28.06.2015 | Luisa × Cash [Kleine Chaoten] | Überlebende (3) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (1) = 2 | +| U6 | 12.07.2015 | Reika [Kleine Chaoten] × Ringo [Kleine Chaoten] | Überlebende (1) ≠ Wurfstärke (5) − Totgeburten (1) − später verstorben (0) = 4 | +| A7 | 07.08.2015 | Arizona [Kleine Chaoten] × Marc [Kleine Chaoten] | Überlebende (1) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| I7 | 11.09.2015 | Zora [Kleine Chaoten] × BlackFire [Kleine Chaoten] | Überlebende (3) ≠ Wurfstärke (4) − Totgeburten (0) − später verstorben (0) = 4 | +| J7 | 12.09.2015 | Yuzuki (Onelia (Algieref)) × Milon [Kleine Chaoten] | Überlebende (2) ≠ Wurfstärke (5) − Totgeburten (2) − später verstorben (0) = 3 | +| P7 | 11.10.2015 | Gin [Kleine Chaoten] × Pan [Kleine Chaoten] | Überlebende (3) ≠ Wurfstärke (5) − Totgeburten (1) − später verstorben (0) = 4 | +| Q7 | 13.10.2015 | Cristina [Kleine Chaoten] × Gino [Kleine Chaoten] | Überlebende (6) ≠ Wurfstärke (6) − Totgeburten (1) − später verstorben (0) = 5 | +| R7 | 15.10.2015 | Sheila × Blue | Überlebende (1) ≠ Wurfstärke (2) − Totgeburten (0) − später verstorben (0) = 2 | +| Y7 | 24.10.2015 | Xtra Bounty [Schlossmaus] × Ruby [Kleine Chaoten] | Überlebende (4) ≠ Wurfstärke (4) − Totgeburten (2) − später verstorben (0) = 2 | +| Z7 | 25.10.2015 | Villow [Kleine Chaoten] × Flocke [Kleine Chaoten] | Überlebende (4) ≠ Wurfstärke (6) − Totgeburten (0) − später verstorben (3) = 3 | +| F8 | 25.11.2015 | Alice [Chalfont Stud] × D’Artagnan gen. Dante [Hanse Renner] | Überlebende (3) ≠ Wurfstärke (3) − Totgeburten (1) − später verstorben (0) = 2 | +| H8 | 01.12.2015 | Isa [golden lights] × Arrow [Privatzucht Gießen] | Überlebende (4) ≠ Wurfstärke (4) − Totgeburten (0) − später verstorben (1) = 3 | +| R8 | 14.12.2015 | Xylona [Kleine Chaoten] × Joghurt [Privat Frankfurt] | Überlebende (6) ≠ Wurfstärke (7) − Totgeburten (0) − später verstorben (0) = 7 | +| W8 | 27.12.2015 | Cristina [Kleine Chaoten] × Gino [Kleine Chaoten] | Überlebende (5) ≠ Wurfstärke (6) − Totgeburten (0) − später verstorben (0) = 6 | +| X8 | 30.12.2015 | Alice [Chalfont Stud] × D’Artagnan gen. Dante [Hanse Renner] | Überlebende (4) ≠ Wurfstärke (5) − Totgeburten (0) − später verstorben (0) = 5 | +| B9 | 03.01.2016 | Elly [Kleine Chaoten] × George gen. Bounty [Kleine Chaoten] | Überlebende (2) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| C9 | 09.01.2016 | Ruby [Kleine Chaoten] × Gino [Kleine Chaoten] | Überlebende (5) ≠ Wurfstärke (5) − Totgeburten (0) − später verstorben (1) = 4 | +| F9 | 22.01.2016 | Lotta [Kleine Chaoten] × Wade [Kleine Chaoten] | Überlebende (4) ≠ Wurfstärke (6) − Totgeburten (0) − später verstorben (0) = 6 | +| H9 | 29.01.2016 | Viven [Kleine Chaoten] × Spike [Kleine Chaoten] | Überlebende (2) ≠ Wurfstärke (7) − Totgeburten (2) − später verstorben (0) = 5 | +| L9 | 06.02.2016 | Gin [Kleine Chaoten] × Pan [Kleine Chaoten] | Überlebende (3) ≠ Wurfstärke (4) − Totgeburten (2) − später verstorben (0) = 2 | +| M9 | 07.02.2016 | Princess [Kleine Chaoten] × Pudlov [Kleine Chaoten] | Überlebende (4) ≠ Wurfstärke (6) − Totgeburten (0) − später verstorben (1) = 5 | +| X9 | 03.03.2016 | Zora [Kleine Chaoten] × Sammy [Kleine Chaoten] | Überlebende (2) ≠ Wurfstärke (3) − Totgeburten (1) − später verstorben (1) = 1 | +| Z9 | 08.03.2016 | Viven [Kleine Chaoten] × Spikie [Kleine Chaoten] | Überlebende (3) ≠ Wurfstärke (5) − Totgeburten (3) − später verstorben (0) = 2 | +| B10 | 12.03.2016 | Nancy [little Hobbits] × Milon [Kleine Chaoten] | Überlebende (1) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (1) = 2 | +| J10 | 28.05.2016 | Kokiri [Samsimar] × Sammy [Kleine Chaoten] | Überlebende (2) ≠ Wurfstärke (4) − Totgeburten (0) − später verstorben (0) = 4 | +| L10 | 19.06.2016 | Violetta [Kleine Chaoten] × Piet [Kleine Chaoten] | Überlebende (3) ≠ Wurfstärke (4) − Totgeburten (0) − später verstorben (0) = 4 | +| S10 | 06.08.2016 | Lisa [Kleine Chaoten] × Logan [Kleine Chaoten] | Überlebende (4) ≠ Wurfstärke (5) − Totgeburten (2) − später verstorben (0) = 3 | +| U10 | 16.08.2016 | Hamster (C*C) [Kleine Chaoten] × Agouti (Mala [Zeko i ptica, Croatia] & Lenny) [Kleine Chaoten] | Überlebende (5) ≠ Wurfstärke (6) − Totgeburten (0) − später verstorben (0) = 6 | +| A11 | 15.11.2016 | Kimiko [Kleine Chaoten] × Xavier [Black forest] | Überlebende (5) ≠ Wurfstärke (6) − Totgeburten (3) − später verstorben (0) = 3 | +| B11 | 17.11.2016 | Leyla [colourful furry gerbils] × BlackFire [Kleine Chaoten] | Überlebende (3) ≠ Wurfstärke (4) − Totgeburten (0) − später verstorben (0) = 4 | +| D11 | 20.12.2016 | Amira [Kleine Chaoten] × Golek [Kleine Chaoten] | Überlebende (3) ≠ Wurfstärke (4) − Totgeburten (0) − später verstorben (0) = 4 | +| F11 | | Zarina [Kleine Chaoten] × Sloan [Kleine Chaoten] | Überlebende (1) ≠ Wurfstärke (2) − Totgeburten (0) − später verstorben (0) = 2 | +| H11 | 08.01.2017 | Isa [golden lights] × Jack II [Kleine Chaoten] | Überlebende (2) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| J11 | 17.01.2017 | Catelyn Stark [Kleine Chaoten] × Ayron [Kleine Chaoten] [Kleine Chaoten] | Überlebende (1) ≠ Wurfstärke (4) − Totgeburten (2) − später verstorben (0) = 2 | +| O11 | 24.02.2017 | Hanami [Kleine Chaoten] × Xavier [Black Forest] | Überlebende (4) ≠ Wurfstärke (4) − Totgeburten (0) − später verstorben (1) = 3 | +| Q11 | 02.03.2017 | Leyla [colourful furry gerbils] × BlackFire [Kleine Chaoten] | Überlebende (3) ≠ Wurfstärke (3) − Totgeburten (1) − später verstorben (0) = 2 | +| R11 | 07.03.2017 | Little Lilly [Kleine Chaoten] × Edward [Kleine Chaoten] | Überlebende (3) ≠ Wurfstärke (3) − Totgeburten (2) − später verstorben (0) = 1 | +| B12 | 30.04.2017 | Aki [Kleine Chaoten] × Athmos [Kleine Chaoten] | Überlebende (5) ≠ Wurfstärke (5) − Totgeburten (2) − später verstorben (0) = 3 | +| H12 | 10.07.2017 | Jela [Kleine Chaoten] × Maxi King [Lenny lengo] | Überlebende (2) ≠ Wurfstärke (6) − Totgeburten (0) − später verstorben (2) = 4 | +| O12 | 24.08.2017 | Varda [Kleine Chaoten] × Lenny [Kleine Chaoten] | Überlebende (1) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| Y12 | 13.12.2017 | Hanami [Kleine Chaoten] × Robb Stark [Kleine Chaoten] | Überlebende (2) ≠ Wurfstärke (3) − Totgeburten (2) − später verstorben (0) = 1 | +| A13 | 17.12.2017 | Montana [Kleine Chaoten] × Eric [Kleine Chaoten] | Überlebende (1) ≠ Wurfstärke (2) − Totgeburten (0) − später verstorben (0) = 2 | +| G13 | 20.02.2018 | Sam [Kleine Chaoten] × Polly [Diepholzer Fellnasen] | Überlebende (3) ≠ Wurfstärke (3) − Totgeburten (1) − später verstorben (0) = 2 | +| L13 | 28.03.2018 | Yella [Kleine Chaoten] × Quambo [Kleine Chaoten] | Überlebende (4) ≠ Wurfstärke (6) − Totgeburten (0) − später verstorben (0) = 6 | +| T14 | 09.04.2019 | Enya [Kleine Chaoten] × Brandon Stark [Kleine Chaoten] | Überlebende (4) ≠ Wurfstärke (5) − Totgeburten (2) − später verstorben (0) = 3 | +| Z14 | 02.06.2019 | Vandana [Kleine Chaoten] × Laito [Kleine Chaoten] | Überlebende (1) ≠ Wurfstärke (3) − Totgeburten (1) − später verstorben (0) = 2 | +| H15 | 09.07.2019 | Mina [Kleine Chaoten] × Adonis [Kleine Chaoten] | Überlebende (3) ≠ Wurfstärke (4) − Totgeburten (0) − später verstorben (0) = 4 | +| I15 | 14.07.2019 | Naho [Kleine Chaoten] × Wilbur [Kleine Chaoten] | Überlebende (3) ≠ Wurfstärke (4) − Totgeburten (0) − später verstorben (0) = 4 | +| J15 | 16.07.2019 | Chevrolet Corvette [Topolino] × South Dakoto [Kleine Chaoten] | Überlebende (1) ≠ Wurfstärke (4) − Totgeburten (0) − später verstorben (0) = 4 | +| Y15 | 25.10.2019 | Nisha [Black Forest] × Gus [Kleine Chaoten] | Überlebende (3) ≠ Wurfstärke (4) − Totgeburten (0) − später verstorben (0) = 4 | +| F16 | 12.12.2019 | Jill [Black Forest] × John Dewey [Kleine Chaoten] | Überlebende (4) ≠ Wurfstärke (5) − Totgeburten (0) − später verstorben (0) = 5 | +| R16 | 05.02.2020 | Naho [Kleine Chaoten] × Oscar [Black Forest] | Überlebende (4) ≠ Wurfstärke (5) − Totgeburten (0) − später verstorben (0) = 5 | +| U16 | 13.02.2020 | Oyama [Kleine Chaoten] × Ramzi [Kleine Chaoten] | Überlebende (3) ≠ Wurfstärke (4) − Totgeburten (1) − später verstorben (1) = 2 | +| Z16 | 28.02.2020 | Hana [Kleine Chaoten] × Zadar [Zeko i ptica, Croatia] | Überlebende (2) ≠ Wurfstärke (2) − Totgeburten (0) − später verstorben (1) = 1 | +| O17 | 09.05.2020 | Nisha [Black Forest] × Zenon von Elea [Kleine Chaoten] | Überlebende (1) ≠ Wurfstärke (1) − Totgeburten (1) − später verstorben (0) = 0 | +| W17 | 07.06.2020 | Inory [Kleine Chaoten] × Jeremy [Kleine Chaoten] | Überlebende (4) ≠ Wurfstärke (5) − Totgeburten (2) − später verstorben (0) = 3 | +| Y17 | 20.06.2020 | Honshu [golden lights] × Baiko [Kleine Chaoten] | Überlebende (2) ≠ Wurfstärke (4) − Totgeburten (1) − später verstorben (0) = 3 | +| F18 | 06.07.2020 | Nisha [Black Forest] × Zenon von Elea [Kleine Chaoten] | Überlebende (3) ≠ Wurfstärke (3) − Totgeburten (1) − später verstorben (0) = 2 | +| W18 | 24.12.2020 | Belica gen. Emi [Kleine Chaoten] × Martin [Kleine Chaoten] | Überlebende (6) ≠ Wurfstärke (7) − Totgeburten (0) − später verstorben (0) = 7 | +| N19 | 13.05.2021 | Tennesee [Kleine Chaoten] × Chesnut [Kleine Chaoten] | Überlebende (1) ≠ Wurfstärke (4) − Totgeburten (0) − später verstorben (0) = 4 | +| 10 | 23.02.2015 | Kia [Kleine Chaoten] × Arrow [Privatzucht Gießen] | Überlebende (0) ≠ Wurfstärke (1) − Totgeburten (0) − später verstorben (0) = 1 | +| 11 | 25.02.2015 | Raya [bunten Fellnasen] × Hanse Renner's D’Artagnan gen. Dante | Überlebende (0) ≠ Wurfstärke (4) − Totgeburten (0) − später verstorben (0) = 4 | +| 12 | 01.03.2015 | Bleach [Kleine Chaoten] [Kleine Chaoten] × JD [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (2) − Totgeburten (0) − später verstorben (0) = 2 | +| 14 | 02.04.2015 | Nancy [Kleine Chaoten] × Zorro [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (1) − Totgeburten (0) − später verstorben (0) = 1 | +| 15 | 03.04.2015 | Eliza [Kleine Chaoten] × Jack Jr. [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (2) − Totgeburten (0) − später verstorben (0) = 2 | +| 17 | 05.04.2015 | Skarlett [Kleine Chaoten] × Hiro [golden lights] | Überlebende (0) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| 18 | 07.04.2015 | Olympia [Kleine Chaoten] × Shinichi [Privatzucht Mücke] | Überlebende (0) ≠ Wurfstärke (6) − Totgeburten (0) − später verstorben (0) = 6 | +| 21 | 01.05.2015 | Milena [Kleine Chaoten] × Browny [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (2) − Totgeburten (0) − später verstorben (0) = 2 | +| 28 | 29.06.2015 | Dusty × Harumi [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (7) − Totgeburten (0) − später verstorben (0) = 7 | +| 29 | 30.06.2015 | Meredith [Kleine Chaoten] × Bailey [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (2) − Totgeburten (0) − später verstorben (0) = 2 | +| 30 | 30.06.2015 | Qamikaze Queen gen. Quenny [Schlossmaus] × Sammy [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| 31 | 01.07.2015 | Kazumi [Kleine Chaoten] × Kazu [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (1) − Totgeburten (0) − später verstorben (0) = 1 | +| 33 | 15.07.2015 | Mia [bunte Fellnasen] × Dargonet [Hanse Renner] | Überlebende (0) ≠ Wurfstärke (4) − Totgeburten (0) − später verstorben (0) = 4 | +| 37 | 28.08.2015 | Genna [golden lights] × Arrow [Privatzucht Gießen] | Überlebende (0) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| 38 | 15.09.2015 | Sheila [of Lenny Lengo] × Blue [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (2) − Totgeburten (0) − später verstorben (0) = 2 | +| 39 | 15.09.2015 | Schwarz?? [Kleine Chaoten] × Poseidon [Hanse Renner] | Überlebende (0) ≠ Wurfstärke (2) − Totgeburten (0) − später verstorben (0) = 2 | +| 40 | 31.10.2015 | Abiya [Kleine Chaoten] × Dastan [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (6) − Totgeburten (0) − später verstorben (0) = 6 | +| 41 | 02.11.2015 | A Sp (Zobel) × Janosch [Black forest] | Überlebende (0) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| 43 | 22.12.2015 | Zoey (CP-Sa) [Kleine Chaoten] × Janosch [Black forest] | Überlebende (2) ≠ Wurfstärke (4) − Totgeburten (0) − später verstorben (4) = 0 | +| 44 | 12.01.2016 | Akina [Kleine Chaoten] × Poseidon [Hanse Renner | Überlebende (0) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| 46 | 05.02.2016 | Zora [Kleine Chaoten] × Sammy [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| 47 | 06.02.2016 | Zuleika [Kleine Chaoten] × Xtra Bounty [Schlossmaus] | Überlebende (0) ≠ Wurfstärke (1) − Totgeburten (0) − später verstorben (0) = 1 | +| 53 | 03.06.2016 | Sa Sp × Rudi | Überlebende (0) ≠ Wurfstärke (1) − Totgeburten (0) − später verstorben (0) = 1 | +| 54 | 11.06.2016 | Nella [Kleine Chaoten] × Arrow [Privatzucht Gießen] | Überlebende (0) ≠ Wurfstärke (1) − Totgeburten (0) − später verstorben (0) = 1 | +| 58 | 27.11.2016 | Villow [Kleine Chaoten] × Theodore [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| 61 | 31.12.2016 | Nina [Skylers Rennmausclan] × Henry [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (2) − Totgeburten (0) − später verstorben (0) = 2 | +| 62 | 07.01.2017 | Villow [Kleine Chaoten] × Theodore [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (1) − Totgeburten (0) − später verstorben (0) = 1 | +| 65 | 13.02.2017 | Princess Jr. [Kleine Chaoten] × Pudlov [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| 67 | 14.03.2017 | Lenni [Kleine Chaoten] × BlackFire [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (6) − Totgeburten (0) − später verstorben (0) = 6 | +| 68 | 17.04.2017 | Elfenbein/Apricot SP [Kleine Chaoten] × Agouti [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (4) − Totgeburten (0) − später verstorben (0) = 4 | +| 75 | 01.06.2017 | Sherry(Marlou&Zelos) [Kleine Chaoten] × Luke [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| 81 | 21.05.2018 | Umi [Kleine Chaoten] × Theodore [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| 83 | 30.07.2018 | Pirinna [Kleine Chaoten] × Iain [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (4) − Totgeburten (0) − später verstorben (0) = 4 | +| 85 | 21.10.2018 | Sherry [Kleine Chaoten] × Yoshua [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| 101 | 18.02.2020 | Oria [Kleine Chaoten] × Opheus [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (2) − Totgeburten (0) − später verstorben (1) = 1 | +| 108 | 22.03.2020 | Oyama [Kleine Chaoten] × Ramzi [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (3) − Totgeburten (0) − später verstorben (0) = 3 | +| 110 | 25.03.2020 | Hollie gen. Panini [Kleine Chaoten] × Kazuya [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (5) − Totgeburten (4) − später verstorben (0) = 1 | +| 115 | 13.05.2020 | Lacey [Kleine Chaoten] × Ares [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (2) − Totgeburten (0) − später verstorben (0) = 2 | +| 123 | 27.07.2020 | Yasna [Kleine Chaoten] × Ramzi [Kleine Chaoten] | Überlebende (0) ≠ Wurfstärke (2) − Totgeburten (0) − später verstorben (0) = 2 | + +### Zeilen mit abweichendem Spaltenschema (automatisch interpretiert) + +138 Zeilen (überwiegend Tabelle2 ab 2014) tragen eine zusätzliche Zahlenspalte vor der Wurfstärke bzw. eine verschobene Geschlechter-Aufschlüsselung. Sie wurden nach dem Muster von Tabelle1 gelesen (**Überlebende, Wurfstärke, Aufschlüsselung**) — bei 99 davon geht die Rechnung E = F − TG − s damit exakt auf, was die Lesart bestätigt. Alle betroffenen Zeilen sind in `litters.json` mit `warnings` markiert. Beispiele: + +- Wurf 6 (03.11.2014): E=2, F=4, Aufschlüsselung `1,3,2,0` +- Wurf 9 (04.02.2015): E=0, F=5, Aufschlüsselung `2,3,5,0` +- Wurf 10 (23.02.2015): E=0, F=1, Aufschlüsselung `` +- Wurf 11 (25.02.2015): E=0, F=4, Aufschlüsselung `` +- Wurf 12 (01.03.2015): E=0, F=2, Aufschlüsselung `` +- Wurf 13 (29.03.2015): E=0, F=6, Aufschlüsselung `2,4,6,0` +- Wurf 14 (02.04.2015): E=0, F=1, Aufschlüsselung `` +- Wurf 15 (03.04.2015): E=0, F=2, Aufschlüsselung `` + ## Hinweise für den Import (Stufe 3, später) - **Wurfchronik = Quelle der Würfe** (Datum, Wurfstärke, Eltern, Zuchtnummer); **Stammbäume = Abstammung + Genotyp + Fotos**. Verknüpfung über Geburtsdatum + Elternnamen.