New data flow: wiki → star-map.json + SQLite. - tooling/process-wiki-system-changes: unified script that reads wiki, updates star-map.json topology, syncs to SQLite - tooling/db/wiki_sync.py: wiki frontmatter parser + SQLite upsert - generate-stubs.py: updated for full-frontmatter wiki format - Removed extract-csv.py (replaced by SQLite queries) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
202 lines
6.2 KiB
Python
Executable File
202 lines
6.2 KiB
Python
Executable File
#!/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()
|