MEGA-BOQ-WEBAPP/run.py

77 lines
2.5 KiB
Python
Raw Normal View History

#!/usr/bin/env python3
"""
BOQ PDF -> Excel + CSV
python3 run.py tender.pdf # writes out/BOQ_<name>.xlsx + .csv
python3 run.py tender.pdf --report # also prints the validation report
"""
from __future__ import annotations
import argparse
import json
import os
import re
import sys
from boq_engine.extract import extract
from boq_engine.populate import flatten, populate_template, to_csv
from boq_engine.validate import validate
def safe(name: str) -> str:
return re.sub(r"[^A-Za-z0-9]+", "_", name).strip("_")[:60]
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("pdf")
ap.add_argument("-o", "--outdir", default="out")
ap.add_argument("--contract", default="")
ap.add_argument("--title", default="BILL OF QUANTITIES")
ap.add_argument("--vat", type=float, default=15.0)
ap.add_argument("--report", action="store_true")
ap.add_argument("--json", dest="json_out")
args = ap.parse_args()
base = safe(os.path.splitext(os.path.basename(args.pdf))[0])
os.makedirs(args.outdir, exist_ok=True)
xlsx = os.path.join(args.outdir, f"{base}.xlsx")
csvp = os.path.join(args.outdir, f"{base}.csv")
jsonp = args.json_out or os.path.join(args.outdir, f"{base}.json")
print(f"reading {args.pdf}")
tree = extract(args.pdf)
rep = validate(tree)
stats = populate_template(tree, xlsx, title=args.title,
contract=args.contract, vat_pct=args.vat)
nrows = to_csv(tree, csvp)
with open(jsonp, "w", encoding="utf-8") as fh:
json.dump({"tree": tree, "validation": rep.as_dict()}, fh,
indent=1, default=str)
print(f"\n priced lines {stats['lines']}")
print(f" groups {stats['groups']}")
print(f" 'Rate only' {stats['rate_only']}")
print(f" odd units {stats['unknown_units']} (highlighted in the workbook)")
print(f" csv rows {nrows}")
print(f" validation {len(rep.errors)} error(s), {len(rep.warnings)} warning(s)")
if rep.errors or args.report:
print("\nVALIDATION")
for i in rep.issues:
if i.severity == "info" and not args.report:
continue
mark = {"error": "ERROR", "warn": "WARN ", "info": "INFO "}[i.severity]
print(f" {mark} {i.code}")
print(f" {i.where}")
print(f" {i.detail}")
print(f"\nwrote {xlsx}\nwrote {csvp}\nwrote {jsonp}")
return 1 if rep.errors else 0
if __name__ == "__main__":
sys.exit(main())