#!/usr/bin/env python3 """ process-wiki-system-changes Propagates wiki changes to star-map.json and SQLite. Wiki pages (wiki/star-systems/*/index.md) are the single source of truth. This script: 1. Reads all wiki frontmatter 2. Updates star-map.json nodes (preserving edges/topology) 3. Syncs all data into SQLite star_systems table 4. Reports completion stats Usage: tooling/process-wiki-system-changes Run full sync tooling/process-wiki-system-changes --dry-run Show what would change tooling/process-wiki-system-changes --stats Just show fill rates """ import argparse import json import sys from pathlib import Path SCRIPT_DIR = Path(__file__).resolve().parent WORKTREE_ROOT = (SCRIPT_DIR / "..").resolve() STAR_MAP_PATH = WORKTREE_ROOT / "docs" / "design" / "star-map.json" # Import wiki_sync for DB operations sys.path.insert(0, str(SCRIPT_DIR / "db")) import wiki_sync # --------------------------------------------------------------------------- # Star-map.json update # --------------------------------------------------------------------------- # Fields that star-map.json nodes carry (topology + join keys) STAR_MAP_NODE_FIELDS = [ "system_id", "astronomical_id", "gate_topology", "aperture_count", "gate_connections", "hop_distance_from_gateway", ] def update_star_map(wiki_systems: dict) -> dict: """Update star-map.json node fields from wiki data. Returns summary.""" with open(STAR_MAP_PATH, "r") as f: data = json.load(f) nodes = data["nodes"] updated = 0 added = 0 wiki_sids = set(wiki_systems.keys()) map_sids = {n["system_id"] for n in nodes} # Update existing nodes for node in nodes: sid = node["system_id"] if sid not in wiki_systems: continue wiki = wiki_systems[sid] changed = False for field in STAR_MAP_NODE_FIELDS: if field in wiki and wiki[field] != "": val = wiki[field] # Coerce integers if field in ("aperture_count", "gate_connections", "hop_distance_from_gateway"): try: val = int(val) except (ValueError, TypeError): continue if node.get(field) != val: node[field] = val changed = True # Preserve is_gateway flag if changed: updated += 1 # Add nodes for wiki pages that aren't in star-map yet for sid in wiki_sids - map_sids: wiki = wiki_systems[sid] new_node = {"system_id": sid} for field in STAR_MAP_NODE_FIELDS: if field in wiki and wiki[field] != "": val = wiki[field] if field in ("aperture_count", "gate_connections", "hop_distance_from_gateway"): try: val = int(val) except (ValueError, TypeError): continue new_node[field] = val nodes.append(new_node) added += 1 with open(STAR_MAP_PATH, "w") as f: json.dump(data, f, indent=2) f.write("\n") return { "nodes": len(nodes), "edges": len(data["edges"]), "updated": updated, "added": added, } def dry_run(wiki_systems: dict) -> dict: """Show what would change in star-map.json without writing.""" with open(STAR_MAP_PATH, "r") as f: data = json.load(f) changes = [] for node in data["nodes"]: sid = node["system_id"] if sid not in wiki_systems: continue wiki = wiki_systems[sid] for field in STAR_MAP_NODE_FIELDS: if field in wiki and wiki[field] != "": val = wiki[field] if field in ("aperture_count", "gate_connections", "hop_distance_from_gateway"): try: val = int(val) except (ValueError, TypeError): continue if node.get(field) != val: changes.append(f" {sid}.{field}: {node.get(field)} -> {val}") return {"changes": changes} # --------------------------------------------------------------------------- # Main # --------------------------------------------------------------------------- def main(): parser = argparse.ArgumentParser( description="Propagate wiki changes to star-map.json and SQLite" ) parser.add_argument("--dry-run", action="store_true", help="Show changes without writing") parser.add_argument("--stats", action="store_true", help="Just show fill rates") args = parser.parse_args() cfg = wiki_sync.load_config() if args.stats: wiki_sync.stats(cfg) return # Load wiki wiki_systems, warnings, body_count = wiki_sync.load_wiki_pages() if not wiki_systems: print("ERROR: No wiki pages found") for w in warnings: print(f" {w}") sys.exit(1) print(f"Loaded {len(wiki_systems)} wiki pages ({body_count} body sections filled)") if warnings: print(f"Warnings: {len(warnings)}") for w in warnings[:5]: print(f" {w}") if len(warnings) > 5: print(f" ... and {len(warnings) - 5} more") if args.dry_run: result = dry_run(wiki_systems) if result["changes"]: print(f"\nstar-map.json changes ({len(result['changes'])}):") for c in result["changes"]: print(c) else: print("\nstar-map.json: no changes") print("\n(dry run — nothing written)") return # Step 1: Update star-map.json print("\n--- Updating star-map.json ---") map_result = update_star_map(wiki_systems) print(f"Nodes: {map_result['nodes']}, Edges: {map_result['edges']}") print(f"Updated: {map_result['updated']}, Added: {map_result['added']}") # Step 2: Sync to SQLite print("\n--- Syncing to SQLite ---") db_result = wiki_sync.sync(cfg) if db_result["ok"]: print(db_result["summary"]) else: print(f"ERROR: {db_result.get('error')}") sys.exit(1) # Step 3: Quick stats print("\n--- Completion ---") wiki_sync.stats(cfg) if __name__ == "__main__": main()