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>
134 lines
4.8 KiB
Python
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"
|