| """ |
| run.py |
| ====== |
| CLI entry point. Demonstrates the full pipeline and prints a QA summary. |
| |
| Usage: |
| # Extraction + validation only (no API key needed): |
| python run.py path/to/Master_Sheet.xlsx --no-llm |
| |
| # Full run with narratives (requires GOOGLE_API_KEY): |
| python run.py path/to/Master_Sheet.xlsx --developer "Green Edge Consortium" |
| |
| The mini-grid capacities are an external input (developer-supplied, not in the |
| survey data). Pass them with --solar-pv / --battery / --annual-consumption, or |
| fill them in afterwards on the returned object before rendering. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import argparse |
|
|
| from pue_report_agent import extract_master_sheet, validate, recompute |
| from pue_report_agent.schema import EnergySystemSpec |
|
|
|
|
| def main() -> None: |
| ap = argparse.ArgumentParser(description="PUE report builder") |
| ap.add_argument("master_sheet") |
| ap.add_argument("--developer", default="the mini-grid developer") |
| ap.add_argument("--no-llm", action="store_true", |
| help="skip narrative generation (no API key required)") |
| ap.add_argument("--solar-pv", default="") |
| ap.add_argument("--battery", default="") |
| ap.add_argument("--annual-consumption", default="") |
| ap.add_argument("--date", default="", help="report date for the title page") |
| ap.add_argument("--docx-out", default="", |
| help="render a template-faithful .docx to this path") |
| ap.add_argument("--json-out", default="", |
| help="write the full PUEReportData JSON to this path") |
| ap.add_argument("--developer-logo", default="", |
| help="optional path to developer logo image " |
| "(placed on the left of the page header)") |
| ap.add_argument("--dreef-logo", default="", |
| help="optional path to DREEF logo image " |
| "(placed on the right of the page header)") |
| args = ap.parse_args() |
|
|
| |
| data = extract_master_sheet(args.master_sheet) |
|
|
| |
| data = recompute(data) |
|
|
| |
| |
| |
| if args.solar_pv: |
| data.minigrid.solar_pv = args.solar_pv |
| if args.battery: |
| data.minigrid.battery_storage = args.battery |
| if args.annual_consumption: |
| data.minigrid.annual_consumption = args.annual_consumption |
|
|
| |
| data = validate(data) |
|
|
| |
| if not args.no_llm: |
| from pue_report_agent import generate_narratives |
| if generate_narratives is None: |
| print("[warn] langchain not installed; skipping narratives.") |
| else: |
| data = generate_narratives(data) |
|
|
| |
| _print_summary(data) |
|
|
| |
| if args.docx_out: |
| from pue_report_agent.render import render |
| render(data, args.docx_out, developer=args.developer, |
| report_date=args.date, |
| developer_logo_path=args.developer_logo or None, |
| dreef_logo_path=args.dreef_logo or None) |
| print(f"\nWrote {args.docx_out}") |
|
|
| if args.json_out: |
| with open(args.json_out, "w") as fh: |
| fh.write(data.model_dump_json(indent=2)) |
| print(f"Wrote {args.json_out}") |
|
|
|
|
| def _print_summary(data) -> None: |
| c = data.community |
| print("=" * 64) |
| print(f" {c.name} ({c.lga}, {c.state})") |
| print("=" * 64) |
| print(f" Interviewed: {data.interviews.total} " |
| f"(farmers {data.interviews.farmers}, processors " |
| f"{data.interviews.agroprocessors}, SME {data.interviews.sme}, " |
| f"mobility {data.interviews.mobility})") |
| print(f" Farmers: {data.farmers.male}M / {data.farmers.female}F, " |
| f"{len(data.farmers.crops)} crops") |
| print(f" Processors: {data.processors.male}M / {data.processors.female}F, " |
| f"{len(data.processors.machinery)} machines on record") |
| print(f" SME types: {len(data.sme.types)} Mobility: {data.mobility.total}") |
| print(f" Pilot energy: {data.equipment_pilot.projection_total_kwh} kWh " |
| f"ScaleUp: {data.equipment_scaleup.projection_total_kwh} kWh") |
| print(f" Budget grand total: ₦{(data.budget.grand_total or 0):,.0f}") |
| print(f" Financial models: {[m.model_name for m in data.financial_models]}") |
| print() |
| if data.warnings: |
| print(f" ⚠ {len(data.warnings)} item(s) need review:") |
| for w in data.warnings: |
| print(f" • {w}") |
| else: |
| print(" ✓ no warnings") |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|