#!/usr/bin/env python3 """ Generate all document permutations (DOCX + PDF) for a sample company and email them to ops@performancewest.net for review. Usage: python -m scripts.generate_all_permutations Environment variables: SMTP_HOST – outbound mail server (default: co.carrierone.com) SMTP_PORT – mail port (default: 587) SMTP_USER – SMTP login SMTP_PASS – SMTP password """ from __future__ import annotations import logging import os import smtplib import subprocess import sys from email.mime.application import MIMEApplication from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from pathlib import Path logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(name)s] %(message)s") LOG = logging.getLogger("generate_all_permutations") # ── SMTP config ────────────────────────────────────────────────────────────── SMTP_HOST = os.environ.get("SMTP_HOST", "co.carrierone.com") SMTP_PORT = int(os.environ.get("SMTP_PORT", "587")) SMTP_USER = os.environ.get("SMTP_USER", "") SMTP_PASS = os.environ.get("SMTP_PASS", "") FROM_EMAIL = os.environ.get("FROM_EMAIL", "noreply@performancewest.net") TO_EMAIL = "ops@performancewest.net" # ── Sample company data used across all documents ─────────────────────────── COMPANY = { "entity_name": "Falcon Broadband LLC", "dba_name": "Falcon Communications", "frn": "0027160886", "rmd_number": "RMD-00123456", "filer_id_499": "831234", "address_street": "1234 Telecom Drive, Suite 400", "address_city": "Denver", "address_state": "CO", "address_zip": "80202", "contact_name": "Sarah Mitchell", "contact_title": "VP of Regulatory Affairs", "contact_email": "regulatory@falconbroadband.com", "contact_phone": "+1 (303) 555-0142", "ceo_name": "James R. Falcon", "ceo_title": "Chief Executive Officer", } OUTPUT_DIR = Path("/tmp/permutation_output") def _convert_to_pdf(docx_path: Path) -> Path | None: """Convert DOCX to PDF via LibreOffice headless (local fallback).""" pdf_path = docx_path.with_suffix(".pdf") try: result = subprocess.run( ["libreoffice", "--headless", "--convert-to", "pdf", "--outdir", str(docx_path.parent), str(docx_path)], capture_output=True, text=True, timeout=60, ) if result.returncode == 0 and pdf_path.exists(): LOG.info(" PDF: %s", pdf_path.name) return pdf_path else: LOG.warning(" LibreOffice conversion failed for %s: %s", docx_path.name, result.stderr[:200]) except FileNotFoundError: LOG.warning(" LibreOffice not installed — skipping PDF for %s", docx_path.name) except Exception as exc: LOG.warning(" PDF conversion error for %s: %s", docx_path.name, exc) return None def generate_rmd_letters() -> list[Path]: """Generate RMD certification letters for all classification × STIR/SHAKEN permutations.""" from scripts.document_gen.templates.rmd_letter_generator import generate_rmd_letter # Map to actual FCC provider classifications per 47 CFR § 64.6305 # Each with a realistic switch/ucaas combo to test auto-fill classifications = [ { "label": "voice_svc_oasis", "provider_classification": "voice_service_provider", "infra_type": "facilities", "is_gateway_provider": False, "is_wholesale": False, "switch_platform": "Oasis", }, { "label": "voice_svc_bcmone", "provider_classification": "voice_service_provider", "infra_type": "reseller", "is_gateway_provider": False, "is_wholesale": False, "ucaas_host": "BCM One", }, { "label": "voice_svc_metaswitch", "provider_classification": "voice_service_provider", "infra_type": "facilities", "is_gateway_provider": False, "is_wholesale": False, "switch_platform": "Metaswitch", }, { "label": "gateway", "provider_classification": "gateway_provider", "infra_type": "facilities", "is_gateway_provider": True, "is_wholesale": True, "switch_platform": "Ribbon (OASIS SBC)", }, { "label": "intermediate", "provider_classification": "intermediate_provider", "infra_type": "facilities", "is_gateway_provider": False, "is_wholesale": True, "switch_platform": "FreeSWITCH", }, ] stir_shaken_statuses = [ "complete_implementation", "partial_implementation", "robocall_mitigation_only", "exempt_small_carrier", "not_applicable", ] carrier_categories = ["interconnected_voip", "clec", "ixc", "cmrs"] files: list[Path] = [] for cls in classifications: for ss_status in stir_shaken_statuses: cat = carrier_categories[classifications.index(cls) % len(carrier_categories)] label = cls["label"] filename = f"rmd_letter_{label}_{ss_status}.docx" out = str(OUTPUT_DIR / filename) LOG.info("RMD Letter: classification=%s, stir_shaken=%s", label, ss_status) result = generate_rmd_letter( **COMPANY, ocn="8765", former_names=["Falcon Voice Services LLC"], is_foreign_provider=(cls["label"] == "gateway"), provider_classification=cls["provider_classification"], carrier_category=cat, infra_type=cls["infra_type"], is_wholesale=cls.get("is_wholesale", False), is_gateway_provider=cls.get("is_gateway_provider", False), switch_platform=cls.get("switch_platform", ""), ucaas_host=cls.get("ucaas_host", ""), stir_shaken_status=ss_status, stir_shaken_cert_authority="TransNexus", stir_shaken_extension_type="non_ip_network" if ss_status == "partial_implementation" else "", stir_shaken_extension_basis="TDM portions of the network cannot support SIP-based STIR/SHAKEN signing." if ss_status == "partial_implementation" else "", carrier_metadata={ "gateway_countries": ["Canada", "Mexico", "United Kingdom"], "wholesale_customer_types": ["CLEC", "VoIP", "CMRS"], "downstream_count_approx": 47, }, output_path=out, ) if result: files.append(Path(result)) return files def generate_exhibit_a_docs() -> list[Path]: """Generate Exhibit A (robocall mitigation program) for each carrier role.""" from scripts.document_gen.templates.rmd_exhibit_a_generator import generate_exhibit_a roles = ["facilities", "reseller", "gateway", "ucaas", "wholesale_domestic", "international_only"] files: list[Path] = [] for role in roles: filename = f"exhibit_a_{role}.docx" out = str(OUTPUT_DIR / filename) # Vary the intake answers per role to exercise auto-fill role_extras = { "facilities": {"switch_platform": "Oasis"}, "reseller": {"ucaas_host": "BCM One"}, "gateway": {"switch_platform": "Ribbon SBC"}, "ucaas": {"ucaas_host": "RingCentral"}, "wholesale_domestic": {"switch_platform": "Cataleya"}, "international_only": {"switch_platform": "46Labs"}, } LOG.info("Exhibit A: role=%s (auto-fill from intake)", role) result = generate_exhibit_a( entity_name=COMPANY["entity_name"], frn=COMPANY["frn"], carrier_role=role, carrier_metadata={ "gateway_countries": ["Canada", "Mexico", "United Kingdom"], "wholesale_customer_types": ["CLEC", "VoIP", "CMRS"], }, **role_extras.get(role, {}), output_path=out, ) if result: files.append(Path(result)) return files def generate_cpni_letters() -> list[Path]: """Generate CPNI cert letters: retail vs wholesale, complaints vs no complaints.""" from scripts.document_gen.templates.cpni_cert_letter_generator import generate_cpni_cert_letter permutations = [ { "label": "retail_clean", "is_wholesale": False, "complaints_count": 0, }, { "label": "retail_complaints_breaches", "is_wholesale": False, "complaints_count": 3, "complaints_description": "Three complaints were received regarding third-party marketing access to CPNI. Each was investigated within 5 business days and resolved by reaffirming customer opt-out preferences.", "breaches": [ { "description": "Employee inadvertently disclosed call detail records for 12 accounts to an unauthorized third party during a vendor integration.", "date": "2025-06-22", "customers_affected": 12, "response_actions": "Affected customers notified within 30 days per 47 CFR § 64.2011. Employee received corrective training. Vendor access controls tightened.", }, ], "disciplinary_actions_taken": True, "disciplinary_actions_description": "One employee received a written warning and mandatory retraining for the June 2025 inadvertent CPNI disclosure.", "uses_cpni_for_marketing": True, "cpni_approval_method": "opt_in", }, { "label": "wholesale_clean", "is_wholesale": True, "complaints_count": 0, }, { "label": "wholesale_breach", "is_wholesale": True, "complaints_count": 1, "complaints_description": "One complaint regarding unauthorized disclosure of wholesale traffic data to a third party.", "breaches": [ { "description": "Wholesale traffic routing data for 3 downstream carriers was inadvertently exposed via a misconfigured API endpoint.", "date": "2025-09-10", "customers_affected": 3, "response_actions": "API endpoint secured within 2 hours. Affected carriers notified same day. FCC notified within 30 days per § 64.2011.", }, ], }, ] files: list[Path] = [] for perm in permutations: label = perm.pop("label") filename = f"cpni_cert_{label}.docx" out = str(OUTPUT_DIR / filename) LOG.info("CPNI Letter: %s", label) result = generate_cpni_cert_letter( entity_name=COMPANY["entity_name"], frn=COMPANY["frn"], filer_id_499=COMPANY["filer_id_499"], address_street=COMPANY["address_street"], address_city=COMPANY["address_city"], address_state=COMPANY["address_state"], address_zip=COMPANY["address_zip"], officer_name=COMPANY["ceo_name"], officer_title=COMPANY["ceo_title"], contact_email=COMPANY["contact_email"], contact_phone=COMPANY["contact_phone"], reporting_year=2025, output_path=out, **perm, ) if result: files.append(Path(result)) return files def generate_499a_checklists() -> list[Path]: """Generate 499-A checklists for various filer/infra/status combinations.""" from scripts.document_gen.templates.fcc_499a_checklist_generator import generate_499a_checklist permutations = [ { "label": "voip_facilities_full", "filer_type": "interconnected_voip", "infra_type": "facilities", "service_categories": ["interconnected_voip", "long_distance"], "is_deminimis": False, "is_lire": False, }, { "label": "voip_reseller_deminimis", "filer_type": "interconnected_voip", "infra_type": "reseller", "service_categories": ["interconnected_voip"], "is_deminimis": True, "is_lire": False, }, { "label": "clec_facilities_lire", "filer_type": "clec", "infra_type": "facilities", "service_categories": ["local_exchange", "long_distance", "toll_free"], "is_deminimis": False, "is_lire": True, }, { "label": "ixc_both_full", "filer_type": "ixc", "infra_type": "both", "service_categories": ["long_distance", "dedicated_line", "toll_free"], "is_deminimis": False, "is_lire": False, "total_revenue_cents": 125000000, "interstate_pct": 72.5, "international_pct": 8.3, "last_filing_year": 2024, "contribution_factor_pct": 35.8, "uncollectible_revenue_cents": 2500000, "resold_service_costs_cents": 15000000, }, { "label": "cmrs_facilities_deminimis", "filer_type": "cmrs", "infra_type": "facilities", "service_categories": ["wireless", "interconnected_voip"], "is_deminimis": True, "is_lire": False, }, { "label": "voip_safe_harbor", "filer_type": "interconnected_voip", "infra_type": "facilities", "service_categories": ["interconnected_voip"], "is_deminimis": False, "is_lire": False, "uses_voip_safe_harbor": True, "voip_safe_harbor_pct": 64.9, "total_revenue_cents": 80000000, "contribution_factor_pct": 35.8, }, { "label": "reseller_stale_filing", "filer_type": "interconnected_voip", "infra_type": "reseller", "service_categories": ["interconnected_voip", "resale"], "is_deminimis": False, "is_lire": False, "last_filing_year": 2022, }, { "label": "red_light_debt", "filer_type": "interconnected_voip", "infra_type": "facilities", "service_categories": ["interconnected_voip"], "is_deminimis": False, "is_lire": False, "has_outstanding_fcc_debt": True, "total_revenue_cents": 50000000, "contribution_factor_pct": 35.8, }, ] files: list[Path] = [] for perm in permutations: label = perm.pop("label") filename = f"499a_checklist_{label}.docx" out = str(OUTPUT_DIR / filename) LOG.info("499-A Checklist: %s", label) result = generate_499a_checklist( entity_name=COMPANY["entity_name"], frn=COMPANY["frn"], filer_id_499=COMPANY["filer_id_499"], address_street=COMPANY["address_street"], address_city=COMPANY["address_city"], address_state=COMPANY["address_state"], address_zip=COMPANY["address_zip"], output_path=out, **perm, ) if result: files.append(Path(result)) return files def generate_crtc_letters() -> list[Path]: """Generate CRTC notification letters: with and without BITS.""" from scripts.document_gen.templates.crtc_letter_generator import generate_crtc_letter permutations = [ {"label": "bc_with_bits", "include_bits": True}, {"label": "bc_without_bits", "include_bits": False}, ] files: list[Path] = [] for perm in permutations: label = perm.pop("label") filename = f"crtc_letter_{label}.docx" out = str(OUTPUT_DIR / filename) LOG.info("CRTC Letter: %s", label) result = generate_crtc_letter( entity_name="1234567 B.C. Ltd.", incorporation_number="BC1234567", registered_office="329 Howe St, Suite 200, Vancouver, BC V6C 3N2", services_description=( "Resale of local and long distance voice services, data services, " "and wireless services to business and residential customers across Canada." ), geographic_coverage="Canada-wide", regulatory_contact_name="Regulatory Director", regulatory_contact_email="regulatory@1234567bc.ca", regulatory_contact_phone="+1 (604) 555-0199", director_name="James R. Falcon", ca_domain="1234567bc.ca", output_path=out, **perm, ) if result: files.append(Path(result)) return files def generate_operating_agreements() -> list[Path]: """Generate operating agreements: member-managed vs manager-managed.""" from scripts.formation.base import EntityType, FormationOrder, Member from scripts.formation.operating_agreement import generate_operating_agreement members = [ Member( name="James R. Falcon", address="1234 Telecom Drive, Suite 400", city="Denver", state="CO", zip_code="80202", title="Managing Member", ownership_pct=60.0, is_organizer=True, ), Member( name="Sarah Mitchell", address="5678 Signal Blvd", city="Aurora", state="CO", zip_code="80012", title="Member", ownership_pct=40.0, ), ] permutations = [ {"label": "member_managed", "management_type": "member_managed"}, {"label": "manager_managed", "management_type": "manager_managed"}, ] files: list[Path] = [] for perm in permutations: label = perm["label"] LOG.info("Operating Agreement: %s", label) order = FormationOrder( order_id=f"PERM-OA-{label.upper()}", state_code="CO", entity_type=EntityType.LLC, entity_name="Falcon Broadband LLC", management_type=perm["management_type"], purpose="Telecommunications services, including the provision of voice, data, and internet services", members=members, registered_agent_name="Northwest Registered Agent", registered_agent_address="7700 E Arapahoe Rd, Suite 220, Centennial, CO 80112", principal_address="1234 Telecom Drive, Suite 400", principal_city="Denver", principal_state="CO", principal_zip="80202", fiscal_year_end="12/31", filed_at="2026-03-15", ) docx_path, pdf_path = generate_operating_agreement(order) if docx_path: import shutil src = Path(docx_path) dst = OUTPUT_DIR / f"operating_agreement_{label}.docx" shutil.move(str(src), str(dst)) files.append(dst) if pdf_path and pdf_path != "" and Path(pdf_path).is_file(): pdf_dst = OUTPUT_DIR / f"operating_agreement_{label}.pdf" shutil.move(str(pdf_path), str(pdf_dst)) files.append(pdf_dst) return files def send_email(all_files: list[Path]) -> None: """Send all generated files as attachments to ops@performancewest.net.""" if not SMTP_USER or not SMTP_PASS: LOG.error("SMTP_USER / SMTP_PASS not set — cannot send email") LOG.info("Generated %d files in %s — attach and send manually", len(all_files), OUTPUT_DIR) return # Split into batches to avoid oversized emails (max ~20 attachments per email) BATCH_SIZE = 20 batches = [all_files[i:i + BATCH_SIZE] for i in range(0, len(all_files), BATCH_SIZE)] for batch_num, batch in enumerate(batches, 1): msg = MIMEMultipart() msg["From"] = FROM_EMAIL msg["To"] = TO_EMAIL msg["Subject"] = f"Document Permutations — All Templates (Batch {batch_num}/{len(batches)})" # Build summary summary_lines = [f"
  • {f.name} ({f.stat().st_size / 1024:.0f} KB)
  • " for f in batch] body_html = f"""

    Document Permutation Output — Batch {batch_num}/{len(batches)}

    {len(batch)} files attached. {len(all_files)} total across all batches.

    Files in this batch:

    Generated from scripts/generate_all_permutations.py

    """ msg.attach(MIMEText(body_html, "html")) for filepath in batch: with open(filepath, "rb") as f: part = MIMEApplication(f.read(), Name=filepath.name) part["Content-Disposition"] = f'attachment; filename="{filepath.name}"' msg.attach(part) try: with smtplib.SMTP(SMTP_HOST, SMTP_PORT, timeout=30) as server: server.ehlo() server.starttls() server.ehlo() server.login(SMTP_USER, SMTP_PASS) server.sendmail(FROM_EMAIL, [TO_EMAIL], msg.as_string()) LOG.info("Email batch %d/%d sent to %s (%d files)", batch_num, len(batches), TO_EMAIL, len(batch)) except Exception: LOG.exception("Failed to send email batch %d to %s", batch_num, TO_EMAIL) def main() -> None: OUTPUT_DIR.mkdir(parents=True, exist_ok=True) all_files: list[Path] = [] # 1. RMD Certification Letters (6 roles × 5 STIR/SHAKEN statuses = 30 DOCX) LOG.info("=" * 60) LOG.info("GENERATING RMD CERTIFICATION LETTERS") LOG.info("=" * 60) all_files.extend(generate_rmd_letters()) # 2. Exhibit A — Robocall Mitigation Program (6 roles = 6 DOCX) LOG.info("=" * 60) LOG.info("GENERATING EXHIBIT A DOCUMENTS") LOG.info("=" * 60) all_files.extend(generate_exhibit_a_docs()) # 3. CPNI Certification Letters (4 permutations) LOG.info("=" * 60) LOG.info("GENERATING CPNI CERTIFICATION LETTERS") LOG.info("=" * 60) all_files.extend(generate_cpni_letters()) # 4. FCC 499-A Filing Checklists (6 permutations) LOG.info("=" * 60) LOG.info("GENERATING 499-A CHECKLISTS") LOG.info("=" * 60) all_files.extend(generate_499a_checklists()) # 5. CRTC Notification Letters (2 permutations) LOG.info("=" * 60) LOG.info("GENERATING CRTC LETTERS") LOG.info("=" * 60) all_files.extend(generate_crtc_letters()) # 6. LLC Operating Agreements (2 permutations) LOG.info("=" * 60) LOG.info("GENERATING OPERATING AGREEMENTS") LOG.info("=" * 60) all_files.extend(generate_operating_agreements()) # Convert all DOCX to PDF LOG.info("=" * 60) LOG.info("CONVERTING DOCX → PDF") LOG.info("=" * 60) docx_files = [f for f in all_files if f.suffix == ".docx"] for docx in docx_files: pdf = _convert_to_pdf(docx) if pdf: all_files.append(pdf) LOG.info("=" * 60) LOG.info("TOTAL FILES GENERATED: %d", len(all_files)) LOG.info(" DOCX: %d", sum(1 for f in all_files if f.suffix == ".docx")) LOG.info(" PDF: %d", sum(1 for f in all_files if f.suffix == ".pdf")) LOG.info("=" * 60) # Email everything send_email(all_files) LOG.info("Done. Output directory: %s", OUTPUT_DIR) if __name__ == "__main__": main()