import json import re from pathlib import Path OLD_APP = Path("/mnt/storage/sftp/mcelwain/repository/word-doc-generator") OLD_CONSTANTS_CANDIDATES = [ OLD_APP / "public" / "constants.js", OLD_APP / "constants.js", ] OUT_DIR = Path("tools/doc_generator/content/excel_maps") OUT_FILE = OUT_DIR / "legacy_excel_maps.json" CELL_RE = re.compile(r"([A-Za-z_][A-Za-z0-9_]*)\s*:\s*['\"]([A-Z]{1,3}[0-9]{1,5})['\"]") def find_constants_file(): for path in OLD_CONSTANTS_CANDIDATES: if path.exists(): return path raise SystemExit("Could not find old constants.js") def extract_object_blocks(text): """ Finds JS object-ish assignment/export blocks that contain Excel cell mappings. This is intentionally simple and robust for the old constants.js style. """ blocks = [] # Match things like: # const fieldToCellMap = { ... }; # export const fieldToCellMap = { ... }; # let someMap = { ... }; pattern = re.compile( r"(?:export\s+)?(?:const|let|var)\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*\{", re.MULTILINE, ) for match in pattern.finditer(text): name = match.group(1) start = match.end() - 1 depth = 0 end = None for i in range(start, len(text)): char = text[i] if char == "{": depth += 1 elif char == "}": depth -= 1 if depth == 0: end = i + 1 break if end: block = text[start:end] cells = dict(CELL_RE.findall(block)) if cells: blocks.append((name, cells)) return blocks def label_from_name(name): label = re.sub(r"([a-z])([A-Z])", r"\1 \2", name) label = label.replace("_", " ").replace("-", " ") label = re.sub(r"\s+", " ", label).strip() return label.title() def normalize_id(name): value = re.sub(r"([a-z])([A-Z])", r"\1_\2", name) value = re.sub(r"[^A-Za-z0-9]+", "_", value).strip("_").lower() return value or "excel_map" def discover_excel_templates(): templates = [] for path in sorted(OLD_APP.rglob("*.xlsx")): if ".git" in path.parts or "node_modules" in path.parts: continue rel = path.relative_to(OLD_APP).as_posix() templates.append({ "label": rel, "legacyPath": str(path), "filename": path.name }) return templates def main(): constants_path = find_constants_file() text = constants_path.read_text(encoding="utf-8", errors="ignore") blocks = extract_object_blocks(text) excel_templates = discover_excel_templates() maps = [] for name, cells in blocks: map_id = normalize_id(name) maps.append({ "id": map_id, "sourceName": name, "label": label_from_name(name), "description": f"Generated from {constants_path.relative_to(OLD_APP)} object {name}.", "template": excel_templates[0]["filename"] if excel_templates else "", "legacyTemplateCandidates": excel_templates, "fields": dict(sorted(cells.items(), key=lambda item: item[1])) }) OUT_DIR.mkdir(parents=True, exist_ok=True) OUT_FILE.write_text(json.dumps({ "id": "legacy_excel_maps", "source": str(constants_path), "maps": maps }, indent=2), encoding="utf-8") print(f"Wrote {OUT_FILE}") print(f"Source: {constants_path}") print(f"Excel templates found: {len(excel_templates)}") for t in excel_templates: print(f"- {t['legacyPath']}") print(f"Maps found: {len(maps)}") for item in maps: print(f"- {item['id']}: {len(item['fields'])} fields") if __name__ == "__main__": main()