new-site/scripts/workers/services/cdr_storage_tier.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

134 lines
4.8 KiB
Python

"""CDR Storage Tier handler.
Storage tier purchases (tier1/2/3) are not filings — they're subscription
allocations that bump the customer's `cdr_ingestion_profiles.storage_plan`
quota. No Playwright, no external portal, no artifacts generated.
Tiers correspond to (storage bytes, classified calls) caps:
tier1: 50 GB / 50M calls
tier2: 250 GB / 250M calls
tier3: 1,000 GB / 1,000M calls
A new purchase replaces the existing tier (doesn't stack). Subsequent
ingestion enforces the new cap via `scripts/workers/cdr_ingester.py`.
"""
from __future__ import annotations
import logging
import os
from typing import Optional
import psycopg2
from .base_handler import BaseServiceHandler
logger = logging.getLogger(__name__)
class CDRStorageTierHandler(BaseServiceHandler):
SERVICE_SLUG = "cdr-storage-tier" # set per subclass
SERVICE_NAME = "CDR Storage Tier"
REQUIRES_LLM = False
TIER_LABEL = "" # set per subclass: 'tier1'|'tier2'|'tier3'
async def process(self, order_data: dict) -> list[str]:
order_number = order_data["name"]
entity = order_data.get("entity", {}) or {}
entity_id = entity.get("id")
if not entity_id:
self._create_admin_todo(
order_number,
f"{self.SERVICE_NAME}: no telecom_entity_id on order. "
"Link the entity + re-dispatch, or bump the storage plan "
"manually in cdr_ingestion_profiles.storage_plan.",
)
return []
try:
conn = psycopg2.connect(os.environ.get("DATABASE_URL", ""))
with conn.cursor() as cur:
cur.execute(
"""
UPDATE cdr_ingestion_profiles
SET storage_plan = %s,
storage_plan_purchased_at = NOW(),
storage_plan_order_ref = %s
WHERE telecom_entity_id = %s
RETURNING id
""",
(self.TIER_LABEL, order_number, entity_id),
)
row = cur.fetchone()
conn.commit()
conn.close()
except psycopg2.errors.UndefinedColumn:
# Columns don't exist yet — fall back to setting just storage_plan
try:
conn = psycopg2.connect(os.environ.get("DATABASE_URL", ""))
with conn.cursor() as cur:
cur.execute(
"UPDATE cdr_ingestion_profiles SET storage_plan = %s "
"WHERE telecom_entity_id = %s RETURNING id",
(self.TIER_LABEL, entity_id),
)
row = cur.fetchone()
conn.commit()
conn.close()
except Exception as exc:
logger.warning(
"%s: storage_plan column missing + fallback failed: %s",
self.SERVICE_SLUG, exc,
)
row = None
except Exception as exc:
logger.exception("%s: DB error: %s", self.SERVICE_SLUG, exc)
row = None
if not row:
self._create_admin_todo(
order_number,
f"{self.SERVICE_NAME} for entity {entity_id}: no CDR profile "
"found. Customer needs to enable CDR ingestion first, OR "
"admin should provision the profile manually.",
)
return []
logger.info(
"%s: set storage_plan=%s on profile for entity %s (order %s)",
self.SERVICE_SLUG, self.TIER_LABEL, entity_id, order_number,
)
return [] # no artifacts — tier updates are quiet
def _create_admin_todo(self, order_number: str, description: str) -> None:
try:
from scripts.workers.erpnext_client import ERPNextClient
ERPNextClient().create_resource(
"ToDo",
{
"description": f"[{self.SERVICE_SLUG}] {order_number}\n\n{description}",
"priority": "Low",
"role": "Accounting Advisor",
},
)
except Exception as exc:
logger.error("Could not create admin ToDo: %s", exc)
class CDRStorageTier1Handler(CDRStorageTierHandler):
SERVICE_SLUG = "cdr-storage-tier1"
SERVICE_NAME = "CDR Storage Tier 1 (50 GB / 50M calls)"
TIER_LABEL = "tier1"
class CDRStorageTier2Handler(CDRStorageTierHandler):
SERVICE_SLUG = "cdr-storage-tier2"
SERVICE_NAME = "CDR Storage Tier 2 (250 GB / 250M calls)"
TIER_LABEL = "tier2"
class CDRStorageTier3Handler(CDRStorageTierHandler):
SERVICE_SLUG = "cdr-storage-tier3"
SERVICE_NAME = "CDR Storage Tier 3 (1 TB / 1B calls)"
TIER_LABEL = "tier3"