"""One-shot seeder: inject blocks into pumps.xml from the legacy KlineIDs comma-string at old_source/Herlic2.0/App.config. Reads canonical -> [aliases] map (hard-coded below from the legacy default), finds each in pumps.xml, and inserts an ... block as the first child of that element. Idempotent: if a already has an child, the script merges new aliases into it without creating duplicates. """ import sys from pathlib import Path import xml.etree.ElementTree as ET # Source: old_source/Herlic2.0/App.config -> KlineIDs setting (default value). # Format: "klineID:canonicalPumpID,..." LEGACY_KLINEIDS = ( "0470004007:0470004002,0470004008:0470004006,0470004012:0470004004," "1093412060:0470504028,1093412050:0470504012,1093424041:0470504034," "0986444003:0470504004,1093412071:0470504033,1093423001:0470504027," "0986444012:0470504011,1093412070:0470504033,0986444005:0470504009," "1093424051:0470504046,1093424025:1093424026,0986444002:0470504003," "1093424027:1093424026,1093423002:0470504027,1093424024:1093424026," "0986444006:0470506002,0986444011:0470504010,0986444044:0470506037," "1093421004:0470504026,1093412072:0470504033,1093411003:0470504030," "1093424040:0470504034,0986444019:0470504020,0986444004:0470504005," "1093411022:0470504037,0986444042:0470506017,0986444014:0470504015," "1093421006:0470504026,1093421005:0470504026,1093424080:0470504046," "0986444026:0470506030,1093411004:0470504030,0986444010:0470506009," "0986444008:0470506006," ) PUMPS_XML = Path.home() / ".HC_APTBS" / "config" / "pumps.xml" def parse_legacy(s: str) -> dict[str, list[str]]: """Group legacy entries by canonical pump ID.""" aliases_by_canonical: dict[str, list[str]] = {} for entry in s.split(","): entry = entry.strip() if not entry or ":" not in entry: continue kline_id, canonical = entry.split(":", 1) kline_id = kline_id.strip() canonical = canonical.strip() if not kline_id or not canonical: continue bucket = aliases_by_canonical.setdefault(canonical, []) if kline_id not in bucket: bucket.append(kline_id) return aliases_by_canonical def main() -> int: aliases_by_canonical = parse_legacy(LEGACY_KLINEIDS) print(f"Parsed {sum(len(v) for v in aliases_by_canonical.values())} aliases " f"across {len(aliases_by_canonical)} canonical pumps.") if not PUMPS_XML.exists(): print(f"ERROR: {PUMPS_XML} does not exist.", file=sys.stderr) return 1 tree = ET.parse(PUMPS_XML) root = tree.getroot() pumps_by_id: dict[str, ET.Element] = {} for pump in root.iter("Pump"): pid = pump.attrib.get("id") if pid: pumps_by_id[pid] = pump missing = sorted(set(aliases_by_canonical) - set(pumps_by_id)) if missing: print(f"WARNING: {len(missing)} canonical pumps from legacy data not in " f"pumps.xml — skipping: {missing}", file=sys.stderr) injected_pumps = 0 injected_aliases = 0 skipped_existing = 0 for canonical, kline_ids in sorted(aliases_by_canonical.items()): pump = pumps_by_id.get(canonical) if pump is None: continue existing = pump.find("Aliases") if existing is None: existing = ET.Element("Aliases") pump.insert(0, existing) existing.text = "\n " existing.tail = "\n " already = {(c.tag, (c.text or "").strip()) for c in existing if c.tag in ("KlineId", "ModelRef")} added_here = 0 for k in kline_ids: key = ("KlineId", k) if key in already: skipped_existing += 1 continue kid = ET.SubElement(existing, "KlineId") kid.text = k kid.tail = "\n " already.add(key) added_here += 1 injected_aliases += 1 if added_here: injected_pumps += 1 # Fix the tail of the last child so the closing tag is on its own line. last = list(existing)[-1] last.tail = "\n " tree.write(PUMPS_XML, encoding="utf-8", xml_declaration=True) print(f"Injected {injected_aliases} aliases into {injected_pumps} pumps.") if skipped_existing: print(f" ({skipped_existing} aliases were already present, skipped.)") return 0 if __name__ == "__main__": sys.exit(main())