new-site/scripts/document_gen/templates/cdr_traffic_study_generator.py
justin f8cd37ac8c Initial commit — Performance West telecom compliance platform
Includes: API (Express/TypeScript), Astro site, Python workers,
document generators, FCC compliance tools, Canada CRTC formation,
Ansible infrastructure, and deployment scripts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 06:54:22 -05:00

270 lines
9.8 KiB
Python

"""Traffic Study generator — PDF + XLSX deliverable.
Takes a fully-rolled ``cdr_traffic_studies`` row and produces:
* a signed-ready DOCX (converted to PDF downstream) for the customer's
audit file, with methodology statement + both Block 5 regional
tables + revenue-vs-minutes cross-check
* an XLSX "working doc" with per-period rollups and the same cells
that will drop into the 499-A E-File session
Produced by ``CDRAnalysisHandler`` at the end of a reporting period.
Pre-existing infrastructure reused:
* python-docx for the DOCX
* openpyxl for the XLSX
* scripts.document_gen.templates.base_handler pattern for styling
No classification happens here — this module only formats numbers that
the ingester + classifier already wrote into cdr_calls + cdr_traffic_studies.
"""
from __future__ import annotations
import logging
from datetime import datetime
from decimal import Decimal
from pathlib import Path
from typing import Optional
LOG = logging.getLogger("document_gen.cdr_traffic_study")
try:
from docx import Document
from docx.shared import Pt, Inches, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
except ImportError:
LOG.warning("python-docx not installed — traffic study generation unavailable")
Document = None # type: ignore[assignment,misc]
try:
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Border, Side
except ImportError:
LOG.warning("openpyxl not installed — xlsx export unavailable")
Workbook = None # type: ignore[assignment,misc]
NAVY = RGBColor(0x1A, 0x27, 0x44) if Document else None
def _pct(value) -> str:
if value is None:
return ""
return f"{float(value):.2f}%"
def _dollars(cents: Optional[int]) -> str:
if cents is None:
return ""
return f"${cents/100:,.2f}"
def _minutes(seconds: Optional[int]) -> str:
if seconds is None:
return ""
return f"{seconds/60:,.0f}"
# ─── DOCX ───────────────────────────────────────────────────────────────
def generate_traffic_study_docx(
*,
study: dict,
entity_name: str,
frn: str = "",
filer_id_499: str = "",
output_path: str,
) -> Optional[str]:
if Document is None:
LOG.error("python-docx not installed")
return None
doc = Document()
for section in doc.sections:
section.top_margin = Inches(1)
section.bottom_margin = Inches(1)
section.left_margin = Inches(1.25)
section.right_margin = Inches(1.25)
# Title
title = doc.add_paragraph()
title.alignment = WD_ALIGN_PARAGRAPH.CENTER
r = title.add_run(f"Telecommunications Traffic Study — {study['reporting_year']} {study['reporting_period']}")
r.font.size = Pt(14)
r.bold = True
r.font.color.rgb = NAVY
sub = doc.add_paragraph()
sub.alignment = WD_ALIGN_PARAGRAPH.CENTER
sub_r = sub.add_run(entity_name)
sub_r.font.size = Pt(12)
sub_r.bold = True
info = doc.add_paragraph()
info_r = info.add_run(
f"FRN: {frn or 'N/A'} | 499 Filer ID: {filer_id_499 or 'N/A'} | "
f"Generated: {datetime.now().strftime('%B %d, %Y')}"
)
info_r.font.size = Pt(9)
info_r.font.color.rgb = RGBColor(0x55, 0x55, 0x55)
doc.add_paragraph()
# Methodology
doc.add_heading("Methodology", level=1)
method_paragraphs = [
(
f"This study analyzes {study.get('total_calls', 0):,} call detail records "
f"covering {study['reporting_year']} {study['reporting_period']}. "
"Each call was classified by endpoint geography using NANP area-code "
"records and FCC country-code assignments (47 CFR § 54.706 definitions). "
"Jurisdictional buckets are: interstate, intrastate, international, and "
"indeterminate (records where one or both endpoints could not be "
"resolved to a country/state)."
),
(
"Revenue-based weighting is used where the source CDR carries per-call "
"billing amounts. Minutes-weighted percentages are provided as a "
"cross-check. Records are five-year retained per 47 CFR § 54.711(a) "
"and available for USAC audit on request."
),
(study.get("methodology") or ""),
]
for text in method_paragraphs:
if text:
doc.add_paragraph(text)
# Jurisdictional table
doc.add_heading("Jurisdictional Breakdown", level=1)
juris_table = doc.add_table(rows=1, cols=3)
juris_table.style = "Table Grid"
hdr = juris_table.rows[0].cells
hdr[0].text = "Category"
hdr[1].text = "Revenue-weighted"
hdr[2].text = "Minutes-weighted"
for label, key_rev, key_min in [
("Interstate", "interstate_pct", "interstate_pct_minutes"),
("Intrastate", "intrastate_pct", "intrastate_pct_minutes"),
("International", "international_pct", "international_pct_minutes"),
("Indeterminate", "indeterminate_pct", "indeterminate_pct_minutes"),
]:
row = juris_table.add_row().cells
row[0].text = label
row[1].text = _pct(study.get(key_rev))
row[2].text = _pct(study.get(key_min))
# Wholesale vs retail
doc.add_heading("Block 3 vs. Block 4-A Allocation", level=1)
w_min = study.get("wholesale_minutes") or 0
r_min = study.get("retail_minutes") or 0
doc.add_paragraph(
f"Wholesale (carrier-to-carrier, Block 3): {w_min/60:,.0f} minutes\n"
f"Retail (end-user, Block 4-A): {r_min/60:,.0f} minutes"
)
# Block 5 regional — BOTH reports
for label, key in [
("Block 5 — by originating state of caller", "orig_state_regions_json"),
("Block 5 — by customer billing-address state", "billing_state_regions_json"),
]:
doc.add_heading(label, level=1)
regions = (study.get(key) or {})
if not regions:
doc.add_paragraph("(no data for this view)")
continue
table = doc.add_table(rows=1, cols=2)
table.style = "Table Grid"
h = table.rows[0].cells
h[0].text = "Region"
h[1].text = "% of Total"
for region_name, pct_val in sorted(regions.items()):
row = table.add_row().cells
row[0].text = region_name
row[1].text = _pct(pct_val)
doc.add_heading("Certification", level=1)
doc.add_paragraph(
f"I certify that this traffic study accurately reflects the "
f"telecommunications usage of {entity_name} during the reporting "
f"period. The underlying CDRs are retained for five years and "
f"available on request."
)
for _ in range(2):
doc.add_paragraph()
doc.add_paragraph("_" * 45)
doc.add_paragraph("Authorized Officer")
doc.add_paragraph(entity_name)
doc.add_paragraph(f"Date: {datetime.now().strftime('%B %d, %Y')}")
out = Path(output_path)
out.parent.mkdir(parents=True, exist_ok=True)
doc.save(str(out))
return str(out)
# ─── XLSX (admin working doc) ───────────────────────────────────────────
def generate_traffic_study_xlsx(
*,
study: dict,
entity_name: str,
output_path: str,
) -> Optional[str]:
if Workbook is None:
LOG.error("openpyxl not installed")
return None
wb = Workbook()
default = wb.active
wb.remove(default)
# Summary
ws = wb.create_sheet("Summary")
ws["A1"] = f"Traffic Study — {entity_name}"
ws["A1"].font = Font(bold=True, size=14, color="1A2744")
ws["A2"] = f"{study['reporting_year']} {study['reporting_period']}"
rows = [
("Total calls", study.get("total_calls") or 0),
("Total minutes", (study.get("total_minutes") or 0)),
("Total revenue (cents)", study.get("total_revenue_cents") or 0),
("", ""),
("Interstate % (revenue-weighted)", study.get("interstate_pct")),
("Intrastate % (revenue-weighted)", study.get("intrastate_pct")),
("International % (revenue-weighted)", study.get("international_pct")),
("Indeterminate % (revenue-weighted)", study.get("indeterminate_pct")),
("", ""),
("Interstate % (minutes-weighted)", study.get("interstate_pct_minutes")),
("Intrastate % (minutes-weighted)", study.get("intrastate_pct_minutes")),
("International % (minutes-weighted)", study.get("international_pct_minutes")),
("Indeterminate % (minutes-weighted)", study.get("indeterminate_pct_minutes")),
("", ""),
("Wholesale minutes (Block 3)", (study.get("wholesale_minutes") or 0) / 60),
("Retail minutes (Block 4-A)", (study.get("retail_minutes") or 0) / 60),
]
for i, (label, value) in enumerate(rows, start=4):
ws.cell(row=i, column=1, value=label)
ws.cell(row=i, column=2, value=value)
ws.column_dimensions["A"].width = 45
ws.column_dimensions["B"].width = 22
# Regional breakdowns
for sheet_name, key in [
("Block 5 — Orig State", "orig_state_regions_json"),
("Block 5 — Billing State", "billing_state_regions_json"),
]:
rs = wb.create_sheet(sheet_name)
rs.cell(row=1, column=1, value="Region").font = Font(bold=True)
rs.cell(row=1, column=2, value="% of Total").font = Font(bold=True)
regions = study.get(key) or {}
for i, (name, pct) in enumerate(sorted(regions.items()), start=2):
rs.cell(row=i, column=1, value=name)
rs.cell(row=i, column=2, value=float(pct) if pct is not None else None)
rs.cell(row=i, column=2).number_format = '0.00"%"'
rs.column_dimensions["A"].width = 25
rs.column_dimensions["B"].width = 15
out = Path(output_path)
out.parent.mkdir(parents=True, exist_ok=True)
wb.save(str(out))
return str(out)