# -*- coding: utf-8 -*- """Parser für RennmausPro-III `.mxp`-Exporte (aus dem Backup `C:\\Users\\gulum\\dev\\RennmausPro IIIb 06.06.26.backup`). Eine `.mxp` ist ein latin-1-XML-artiges Format mit drei Abschnitten: das Tier selbst (name, clan=Herkunft, sex, geb=Julian Day Number, color="Farbschlag$$Gencode$$...", herk, gew) Ahnentafel: -Zeilen, Felder per <#> getrennt: [_, pos(v/m/vv/vm/...), name, clan/adresse, geb, _, _, color, ...] eigene Würfe: -Blöcke mit , , -Zeilen Nutzung: python -X utf8 rpro3_mxp.py [Name ...] # ohne Namen: alle Tiere als kompakte Übersicht """ import sys, os, zipfile, json, datetime BACKUP = r"C:\Users\gulum\dev\RennmausPro IIIb 06.06.26.backup" def jdn_to_iso(jdn): try: n = int(str(jdn).strip()) except Exception: return None if n <= 0: return None try: return datetime.date.fromordinal(n - 1721425).isoformat() except Exception: return None def _split_color(raw): # "Silberagouti-Schecke$$Aa C- D- E- gg Pp Spsp$$$$$$$$schwarz" parts = (raw or "").split("$$") farbschlag = parts[0].strip() if parts else "" gencode = parts[1].strip() if len(parts) > 1 else "" return farbschlag or None, gencode or None def _tagval(block, tag): key = "<" + tag + ">" for line in block.splitlines(): s = line.strip() if s.startswith(key): return s[len(key):].strip() return None def parse_mxp(text): # Split into sections by top-level tags. stamm = {} if "" in text: seg = text.split("", 1)[1].split("")[0] if "" in text else text.split("", 1)[1] seg = seg.split("")[0].split("")[0] name = _tagval(seg, "name") clan = _tagval(seg, "clan") sex = _tagval(seg, "sex") geb = _tagval(seg, "geb") color = _tagval(seg, "color") fs, gc = _split_color(color) stamm = {"name": name, "clan": clan, "sex": sex, "dob": jdn_to_iso(geb), "farbschlag": fs, "gencode": gc, "zbnr": _tagval(seg, "zbnr"), "gew": _tagval(seg, "gew")} # Ahnentafel: direct parents (pos == 'v' / 'm') parents = {} for line in text.splitlines(): s = line.strip() if not s.startswith(""): continue f = s.split("<#>") if len(f) < 3: continue pos = f[1].strip() if pos not in ("v", "m"): continue nm = f[2].strip() clan = f[3].strip() if len(f) > 3 else "" geb = f[4].strip() if len(f) > 4 else "" color = next((x for x in f if "$$" in x), "") fs, gc = _split_color(color) parents["Vater" if pos == "v" else "Mutter"] = { "name": nm, "clan": clan.split("##")[0] if clan else clan, "dob": jdn_to_iso(geb), "farbschlag": fs, "gencode": gc} # Würfe litters = [] if "" in text: lit = text.split("", 1)[1] for wblock in lit.split("")[1:]: date = jdn_to_iso(_tagval(wblock, "date")) co = _tagval(wblock, "coparent") or "" coname = co.split("$$")[0].strip() if co else "" children = [] for line in wblock.splitlines(): s = line.strip() if s.startswith(""): cf = s.split("<#>") csex = cf[2].strip() if len(cf) > 2 else "" ccolor = next((x for x in cf if "$$" in x), "") cfs, _ = _split_color(ccolor) # child name is usually empty in export; keep sex+color children.append({"sex": csex, "farbschlag": cfs}) litters.append({"date": date, "coparent": coname, "children": len(children), "child_details": children}) return {"stamm": stamm, "parents": parents, "litters": litters} def load_all(src=BACKUP): out = {} if os.path.isfile(src) and zipfile.is_zipfile(src): z = zipfile.ZipFile(src) for n in z.namelist(): if n.lower().endswith(".mxp"): txt = z.read(n).decode("latin-1", "replace") out[os.path.splitext(os.path.basename(n))[0]] = parse_mxp(txt) else: for n in os.listdir(src): if n.lower().endswith(".mxp"): txt = open(os.path.join(src, n), encoding="latin-1").read() out[os.path.splitext(n)[0]] = parse_mxp(txt) return out if __name__ == "__main__": src = sys.argv[1] if len(sys.argv) > 1 else BACKUP want = [a.lower() for a in sys.argv[2:]] data = load_all(src) for key in sorted(data): if want and key.lower() not in want: continue d = data[key] st = d["stamm"] print(f"\n### {key} ({st.get('name')}, {st.get('sex')}, *{st.get('dob')})") print(f" Herkunft: {st.get('clan')} | Farbe: {st.get('farbschlag')} [{st.get('gencode')}]") for role, p in d["parents"].items(): print(f" {role}: {p['name']} ({p.get('clan')}, *{p.get('dob')}) {p.get('farbschlag')} [{p.get('gencode')}]") for l in d["litters"]: print(f" Wurf {l['date']} x {l['coparent']} -> {l['children']} Junge")