""" CDR unlock nudge — quarterly cron. Emails customers whose ingestion is healthy but whose current reporting year has no `cdr_study_access_grants` row. Upsell to the 499-A or standalone `cdr-analysis` service so the classified study unlocks. One nudge per profile per quarter. Tracks last_nudge_at on a simple per-quarter key to prevent spam. Usage: python -m scripts.workers.cdr_unlock_nudge CRON: 0 10 1 */3 * (10am on the 1st day of every third month) """ from __future__ import annotations import logging import os import smtplib import sys from datetime import datetime, timedelta from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText import psycopg2 import psycopg2.extras log = logging.getLogger("cdr_unlock_nudge") logging.basicConfig( level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s", handlers=[logging.StreamHandler(sys.stdout)], ) DATABASE_URL = os.environ.get("DATABASE_URL", "") 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", "notifications@performancewest.net") ADMIN_EMAIL = os.environ.get("ADMIN_EMAIL", "ops@performancewest.net") SITE_URL = os.environ.get("SITE_URL", "https://performancewest.net") MIN_ROWS_TO_NUDGE = 10_000 # don't pester customers with no real data yet _EMAIL_HTML = """\
""" def _send(to_email: str, subject: str, body_html: str) -> bool: if not SMTP_USER or not SMTP_PASS: log.warning("SMTP unconfigured — would email %s: %s", to_email, subject) return False msg = MIMEMultipart("alternative") msg["Subject"] = subject msg["From"] = FROM_EMAIL msg["To"] = to_email msg["Bcc"] = ADMIN_EMAIL msg.attach(MIMEText(body_html, "html")) try: with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as server: if SMTP_PORT != 25: server.starttls() server.login(SMTP_USER, SMTP_PASS) server.sendmail(FROM_EMAIL, [to_email, ADMIN_EMAIL], msg.as_string()) return True except Exception as exc: log.error("send to %s failed: %s", to_email, exc) return False def run() -> dict: if not DATABASE_URL: return {"error": "no DATABASE_URL"} year = datetime.utcnow().year quarter = (datetime.utcnow().month - 1) // 3 + 1 period_key = f"{year}Q{quarter}" conn = psycopg2.connect(DATABASE_URL) sent = 0 skipped = 0 try: with conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) as cur: # Profiles with healthy ingestion + no grant for this year cur.execute( """ SELECT p.id AS profile_id, p.customer_id, p.telecom_entity_id, te.legal_name AS entity_name, m.rows_ingested, co_email.customer_email, co_email.customer_name FROM cdr_ingestion_profiles p JOIN telecom_entities te ON te.id = p.telecom_entity_id LEFT JOIN cdr_usage_meters m ON m.profile_id=p.id AND m.reporting_year=%s LEFT JOIN LATERAL ( SELECT customer_email, customer_name FROM compliance_orders WHERE telecom_entity_id = p.telecom_entity_id AND payment_status='paid' ORDER BY created_at DESC LIMIT 1 ) co_email ON TRUE WHERE m.rows_ingested >= %s AND NOT EXISTS ( SELECT 1 FROM cdr_study_access_grants g WHERE g.profile_id=p.id AND g.reporting_year=%s ) """, (year, MIN_ROWS_TO_NUDGE, year), ) candidates = list(cur.fetchall()) for c in candidates: if not c.get("customer_email"): skipped += 1 continue unlock_url = ( f"{SITE_URL.rstrip('/')}/order/fcc-499a" f"?entity={c['telecom_entity_id']}&year={year}&source=nudge:{period_key}" ) html = _EMAIL_HTML.format( customer_name=c.get("customer_name") or "there", reporting_year=year, row_count=c["rows_ingested"] or 0, unlock_url=unlock_url, ) subject = f"Unlock your {year} traffic study — {c['entity_name']}" if _send(c["customer_email"], subject, html): sent += 1 else: skipped += 1 finally: conn.close() log.info("unlock nudge run: sent=%s skipped=%s period=%s", sent, skipped, period_key) return {"sent": sent, "skipped": skipped, "period": period_key} def main() -> None: print(run()) if __name__ == "__main__": main()