#!/usr/bin/env python3 from pathlib import Path import argparse import csv BASE_DIR = Path(__file__).resolve().parents[1] DATA_DIR = BASE_DIR / "data" OUTPUT_DIR = BASE_DIR / "output" def clean_value(v): if v is None: return "" return str(v).strip() def bci_escape(v): v = clean_value(v) v = v.replace("\\", "\\\\") v = v.replace("'", "\\'") v = v.replace("\n", "\\n") return v def read_csv(path): with open(path, newline="", encoding="utf-8-sig") as f: return list(csv.DictReader(f)) def dict_to_bci_payload(d): parts = [] for k, v in d.items(): if clean_value(v) != "": parts.append(f"'{bci_escape(k)}':'{bci_escape(v)}'") return "{" + ",".join(parts) + "}" def main(): parser = argparse.ArgumentParser(description="Generate Bluebeam BCI script from Markups CSV + update CSV.") parser.add_argument("--bb-csv", default=str(DATA_DIR / "bluebeam_markups.csv")) parser.add_argument("--updates-csv", default=str(DATA_DIR / "bluebeam_updates.csv")) parser.add_argument("--out", default=str(OUTPUT_DIR / "update_columns.bci")) parser.add_argument("--pdf-path", default=r"C:\PATH\TO\TARGET.pdf") parser.add_argument("--match-column", default="Comment") parser.add_argument("--page-column", default="Page Index") parser.add_argument("--id-column", default="ID") parser.add_argument("--contains", action="store_true") args = parser.parse_args() OUTPUT_DIR.mkdir(exist_ok=True) bb_rows = read_csv(args.bb_csv) update_rows = read_csv(args.updates_csv) if not bb_rows: raise ValueError("Bluebeam CSV is empty.") if not update_rows: raise ValueError("Updates CSV is empty.") required_bb = {args.page_column, args.id_column, args.match_column} missing_bb = required_bb - set(bb_rows[0].keys()) if missing_bb: raise ValueError(f"Bluebeam CSV missing required columns: {sorted(missing_bb)}") if "match_value" not in update_rows[0]: raise ValueError("Updates CSV missing required column: match_value") lines = [f"Open('{args.pdf_path}', '')"] total_matches = 0 for urow in update_rows: match_value = clean_value(urow.get("match_value")) if not match_value: continue col_data = {k: v for k, v in urow.items() if k != "match_value" and clean_value(v) != ""} if not col_data: continue payload = dict_to_bci_payload(col_data) for brow in bb_rows: target = clean_value(brow.get(args.match_column)) matched = match_value in target if args.contains else target == match_value if not matched: continue page_index = clean_value(brow.get(args.page_column)) markup_id = clean_value(brow.get(args.id_column)) if not page_index or not markup_id: continue lines.append(f'ColumnDataSet({page_index},"{markup_id}","{payload}")') total_matches += 1 lines += ["Save()", "Close()"] out_path = Path(args.out) out_path.write_text("\n".join(lines) + "\n", encoding="utf-8") print(f"WROTE: {out_path}") print(f"MATCHED MARKUPS: {total_matches}") if __name__ == "__main__": main()