#!/usr/bin/env python3 """Prepare a request packet for LAB-007 grid power hookup estimates.""" from __future__ import annotations import argparse import json from pathlib import Path from typing import Any PRIMARY_UTILITY = { "name": "Eversource", "phone": "800-362-7764", "email": "nhnewservice@eversource.com", "hours": "Mon-Fri, 7 a.m. to 4:30 p.m. ET", "evidence_url": "https://www.eversource.com/residential/services/communities-we-serve", "work_request_url": "https://www.eversource.com/residential/about/doing-business-with-us/builders-contractors/electric-work-order-management", } FALLBACK_UTILITY = { "name": "New Hampshire Electric Co-op", "phone": "800-698-2007", "request_service_url": "https://www.nhec.com/request-service/", "contact_url": "https://www.nhec.com/contact-us/", } REQUIRED_FIELDS = ( "site_address", "pole_distance_feet", "terrain_description", ) ESTIMATE_REQUEST_CHECKLIST = ( "pole/transformer", "overhead line", "meter base", "connection fees", "timeline from deposit to energized service", "monthly base charge", "per-kWh rate", ) TERRITORY_EVIDENCE = ( "Eversource's New Hampshire electric communities-served list includes Lempster, " "so Eversource is the primary utility candidate for the cabin site unless parcel-level data proves otherwise." ) def build_packet(site_details: dict[str, Any]) -> dict[str, Any]: normalized = {key: site_details.get(key) for key in ("site_address", "pole_distance_feet", "terrain_description", "service_size")} normalized.setdefault("service_size", "200A residential service") missing = [field for field in REQUIRED_FIELDS if not normalized.get(field)] ready = not missing pole_distance = normalized.get("pole_distance_feet") if pole_distance is not None: pole_line = f"Estimated pole distance: {pole_distance} feet" else: pole_line = "Estimated pole distance: [measure and fill in]" terrain = normalized.get("terrain_description") or "[describe terrain between nearest pole and cabin site]" site_address = normalized.get("site_address") or "[exact cabin address / parcel identifier]" service_size = normalized.get("service_size") or "200A residential service" estimate_lines = "\n".join(f"- {item}" for item in ESTIMATE_REQUEST_CHECKLIST) email_body = ( f"Hello Eversource New Service Team,\n\n" f"I need a no-obligation estimate for bringing new electric service to a cabin site in Lempster, New Hampshire.\n\n" f"Site address / parcel: {site_address}\n" f"Requested service size: {service_size}\n" f"{pole_line}\n" f"Terrain / access notes: {terrain}\n\n" f"Please include the following in the estimate or site-visit scope:\n" f"{estimate_lines}\n\n" f"I would also like to know the expected timeline from deposit to energized service and any next-step documents you need from me.\n\n" f"Thank you.\n" ) call_script = [ f"Confirm the cabin site is in {PRIMARY_UTILITY['name']}'s New Hampshire territory for Lempster.", "Request a no-obligation new-service estimate and ask whether a site visit is required.", f"Provide the site address, pole distance, terrain, and requested service size ({service_size}).", "Ask for written/email follow-up with total hookup cost, monthly base charge, per-kWh rate, and timeline.", ] return { "primary_utility": PRIMARY_UTILITY, "fallback_utility": FALLBACK_UTILITY, "territory_evidence": TERRITORY_EVIDENCE, "site_details": { "site_address": site_address, "pole_distance_feet": pole_distance, "terrain_description": terrain, "service_size": service_size, }, "missing_fields": missing, "ready_to_contact": ready, "estimate_request_checklist": list(ESTIMATE_REQUEST_CHECKLIST), "call_script": call_script, "email_subject": "Request for new electric service estimate - Lempster, NH cabin site", "email_body": email_body, } def render_markdown(packet: dict[str, Any]) -> str: primary = packet["primary_utility"] fallback = packet["fallback_utility"] site = packet["site_details"] lines = [ "# LAB-007 — Grid Power Hookup Estimate Request Packet", "", "No formal estimate has been received yet.", "This packet turns the issue into a contact-ready request while preserving what is still missing before the utility can quote real numbers.", "", "## Utility identification", "", f"- Primary candidate: {primary['name']}", f"- Evidence: {packet['territory_evidence']}", f"- Primary contact: {primary['phone']} / {primary['email']} ({primary['hours']})", f"- Service-request portal: {primary['work_request_url']}", f"- Fallback if parcel-level service map disproves the territory assumption: {fallback['name']} ({fallback['phone']})", "", "## Site details currently in packet", "", f"- Site address / parcel: {site['site_address']}", f"- Pole distance: {site['pole_distance_feet'] if site['pole_distance_feet'] is not None else '[measure and fill in]'}", f"- Terrain: {site['terrain_description']}", f"- Requested service size: {site['service_size']}", "", "## Missing information before a real estimate request can be completed", "", ] if packet["missing_fields"]: lines.extend(f"- {field}" for field in packet["missing_fields"]) else: lines.append("- none") lines.extend([ "", "## Estimate request checklist", "", ]) lines.extend(f"- {item}" for item in packet["estimate_request_checklist"]) lines.extend([ "", "## Call script", "", ]) lines.extend(f"- {item}" for item in packet["call_script"]) lines.extend([ "", "## Draft email", "", f"Subject: {packet['email_subject']}", "", "```text", packet["email_body"].rstrip(), "```", "", "## Honest next step", "", "Once the exact address / parcel, pole distance, and terrain notes are filled in, this packet is ready for the live Eversource new-service request. The issue should remain open until a written estimate is actually received and uploaded.", ]) return "\n".join(lines).rstrip() + "\n" def main() -> None: parser = argparse.ArgumentParser(description="Prepare the LAB-007 grid power estimate packet") parser.add_argument("--site-address", default=None) parser.add_argument("--pole-distance-feet", type=int, default=None) parser.add_argument("--terrain-description", default=None) parser.add_argument("--service-size", default="200A residential service") parser.add_argument("--output", default=None) parser.add_argument("--json", action="store_true") args = parser.parse_args() packet = build_packet( { "site_address": args.site_address, "pole_distance_feet": args.pole_distance_feet, "terrain_description": args.terrain_description, "service_size": args.service_size, } ) rendered = json.dumps(packet, indent=2) if args.json else render_markdown(packet) if args.output: output_path = Path(args.output).expanduser() output_path.parent.mkdir(parents=True, exist_ok=True) output_path.write_text(rendered, encoding="utf-8") print(f"Grid power packet written to {output_path}") else: print(rendered) if __name__ == "__main__": main()