""" Dry-run: for every template, count how many JSON files in one or more data directories are compatible (per the same check used by infographics_generator). Usage: # default (read pools from config.data_resource_dirs) python scripts/match_templates_to_data.py --out scripts/_template_match.csv # explicit pools python scripts/match_templates_to_data.py \ --data /data/liduan/resources/claude_data_v2 /data/liduan/resources/claude_new \ --out scripts/_template_match.csv """ import argparse import csv import json import os import sys from pathlib import Path from collections import defaultdict ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(ROOT)) from config import data_resource_dirs as CFG_DATA_DIRS from modules.chart_engine.template.template_registry import scan_templates from modules.infographics_generator.template_utils import ( analyze_templates, check_template_compatibility, ) def parse_args(): p = argparse.ArgumentParser() p.add_argument("--data", nargs="+", default=None, help="One or more directories of input JSONs " "(default: config.data_resource_dirs)") p.add_argument("--out", default="scripts/_template_match.csv", help="CSV report path") p.add_argument("--engines", nargs="+", default=["d3-js", "echarts-js", "echarts_py"], help="Which engines to consider") p.add_argument("--sample", type=int, default=0, help="If >0, randomly subsample this many files (per --seed)") p.add_argument("--seed", type=int, default=42) return p.parse_args() def main(): args = parse_args() data_dirs = [Path(p) for p in (args.data or CFG_DATA_DIRS)] for d in data_dirs: if not d.is_dir(): raise SystemExit(f"--data path is not a directory: {d}") json_files = [] for d in data_dirs: files = sorted(d.glob("*.json")) json_files.extend(files) print(f" {d}: {len(files)} json files") print(f"Total: {len(json_files)} json files across {len(data_dirs)} dir(s)") if args.sample and args.sample < len(json_files): import random random.Random(args.seed).shuffle(json_files) json_files = json_files[:args.sample] print(f"Sub-sampled to {len(json_files)} files (seed={args.seed})") datas = [] for f in json_files: with open(f, "r") as fh: d = json.load(fh) d["name"] = str(f) # Disambiguate filenames when the same basename exists in both pools: # use "/" so picker / driver can map back uniquely. rel = f"{f.parent.name}/{f.name}" datas.append((rel, d)) templates = scan_templates() _, template_requirements = analyze_templates(templates) # Enumerate ALL templates (engine/chart_type/chart_name) keys all_template_keys = [] for engine, t_dict in templates.items(): if engine not in args.engines: continue for chart_type, c_dict in t_dict.items(): for chart_name in c_dict: if "base" in chart_name: continue all_template_keys.append((engine, chart_type, chart_name)) print(f"Total templates (engines={args.engines}): {len(all_template_keys)}") # For each data, compute compatible templates ONCE (it returns the full list) # then we tally per template. counter = defaultdict(int) matched_files = defaultdict(list) for name, data in datas: compat = check_template_compatibility(data, templates, None) for tpl_key, _ordered_fields in compat: counter[tpl_key] += 1 matched_files[tpl_key].append(name) os.makedirs(os.path.dirname(args.out) or ".", exist_ok=True) with open(args.out, "w", newline="") as fh: w = csv.writer(fh) w.writerow(["engine", "chart_type", "chart_name", "num_compatible_data"]) for engine, chart_type, chart_name in all_template_keys: tpl_key = f"{engine}/{chart_type}/{chart_name}" w.writerow([engine, chart_type, chart_name, counter.get(tpl_key, 0)]) print(f"Wrote {args.out}") # Distribution histogram buckets = [0, 1, 5, 10, 20, 30, 50, 100, 10**9] bucket_labels = ["0", "1-4", "5-9", "10-19", "20-29", "30-49", "50-99", "100+"] bucket_counts = [0] * len(bucket_labels) for engine, chart_type, chart_name in all_template_keys: n = counter.get(f"{engine}/{chart_type}/{chart_name}", 0) for i in range(len(bucket_labels)): if buckets[i] <= n < buckets[i + 1]: bucket_counts[i] += 1 break print() print("Templates by number of compatible data files in", ", ".join(str(d) for d in data_dirs)) for lab, c in zip(bucket_labels, bucket_counts): print(f" {lab:>8s}: {c}") n_geq_20 = sum(1 for k in all_template_keys if counter.get("/".join(k), 0) >= 20) n_geq_5 = sum(1 for k in all_template_keys if counter.get("/".join(k), 0) >= 5) print() print(f"Templates with >= 20 compatible data: {n_geq_20}") print(f"Templates with >= 5 compatible data: {n_geq_5}") if __name__ == "__main__": main()