"""Post-completion email flow.
Sends two emails after an order is marked completed:
1. Immediately: "Your filing is complete!" with documents
2. 24 hours later: Exit survey + referral program + review ask
Run via cron every 15 minutes:
python -m scripts.workers.completion_emails
"""
import json
import logging
import os
import smtplib
import sys
import urllib.request
from datetime import datetime, timedelta, timezone
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from scripts._email_plaintext import html_to_text
import psycopg2
LOG = logging.getLogger("workers.completion_emails")
DB_URL = os.getenv("DATABASE_URL", "")
SMTP_FROM = "noreply@performancewest.net"
SITE_URL = os.getenv("SITE_URL", "https://performancewest.net")
API_URL = os.getenv("API_URL", "https://api.performancewest.net")
GOOGLE_REVIEW_URL = os.getenv("GOOGLE_REVIEW_URL", "")
TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN", "")
TELEGRAM_CHAT_ID = os.getenv("TELEGRAM_CHAT_ID", "")
def send_email(to: str, subject: str, html: str):
"""Send an HTML email via local Postfix."""
msg = MIMEMultipart("alternative")
msg["From"] = f"Performance West <{SMTP_FROM}>"
msg["To"] = to
msg["Subject"] = subject
msg.attach(MIMEText(html_to_text(html), "plain"))
msg.attach(MIMEText(html, "html"))
import os as _smtp_os
with smtplib.SMTP(_smtp_os.getenv("SMTP_HOST", "co.carrierone.com"), int(_smtp_os.getenv("SMTP_PORT", "587")), timeout=30) as s:
s.starttls()
_u, _p = _smtp_os.getenv("SMTP_USER", ""), _smtp_os.getenv("SMTP_PASS", "")
if _u and _p:
s.login(_u, _p)
s.sendmail(SMTP_FROM, [to], msg.as_string())
def get_or_create_referral_code(conn, email: str, name: str) -> str:
"""Get or create a referral code for a customer."""
cur = conn.cursor()
cur.execute("SELECT code FROM referral_codes WHERE customer_email = %s", (email,))
row = cur.fetchone()
if row:
return row[0]
clean = "".join(c for c in name.upper() if c.isalpha())[:12]
code = f"REF-{clean or 'CUSTOMER'}"
# Ensure unique
cur.execute("SELECT 1 FROM referral_codes WHERE code = %s", (code,))
if cur.fetchone():
import random
code = f"{code}{random.randint(10, 99)}"
try:
cur.execute(
"INSERT INTO referral_codes (code, customer_email, customer_name) VALUES (%s, %s, %s) ON CONFLICT DO NOTHING",
(code, email, name),
)
conn.commit()
except Exception:
conn.rollback()
return code
def completion_email_html(order_number: str, service_name: str, customer_name: str) -> str:
"""Generate the completion email HTML."""
first = customer_name.split()[0] if customer_name else "there"
return f"""
✅
Your {service_name} Is Complete!
Hi {first},
Great news — your {service_name} has been completed and filed successfully.
Order reference: {order_number}.
You can view your documents and track all your filings in your client portal:
If you have any questions about your filing, reply to this email or call us at
(888) 411-0383.
— The Performance West Team
Performance West Inc. · (888) 411-0383 · performancewest.net
"""
def followup_email_html(
order_number: str,
service_name: str,
customer_name: str,
referral_code: str,
) -> str:
"""Generate the 24h follow-up email with survey + referral."""
first = customer_name.split()[0] if customer_name else "there"
survey_url = f"{SITE_URL}/survey?order={order_number}"
return f"""
How did we do, {first}?
Your {service_name} was completed yesterday. We'd love your feedback — it takes 30 seconds.
Rate your experience:
Click a star to rate
Know another trucker?
Share your referral code and earn $25 credit for each order they place.
Your referral code:
{referral_code}
They can enter this code at checkout. You earn $25 credit per order — no limit.
Thank you for choosing Performance West. We're here whenever you need us.
— The Performance West Team
Performance West Inc. · (888) 411-0383 · performancewest.net
"""
def process_completions():
"""Find completed orders and send emails."""
conn = psycopg2.connect(DB_URL)
cur = conn.cursor()
now = datetime.now(timezone.utc)
# 1. Send completion emails (order completed, email not sent yet)
cur.execute("""
SELECT order_number, customer_email, customer_name, service_name, updated_at
FROM compliance_orders
WHERE payment_status = 'paid'
AND completion_email_sent_at IS NULL
AND updated_at < NOW() - interval '5 minutes'
AND (
intake_data->>'status' = 'completed'
OR intake_data->>'status' = 'delivered'
OR intake_data->>'status' = 'filed'
)
ORDER BY updated_at
LIMIT 20
""")
completions = cur.fetchall()
for row in completions:
order_number, email, name, service_name, updated_at = row
if not email:
continue
try:
html = completion_email_html(order_number, service_name or "compliance filing", name or "")
send_email(email, f"✅ Your {service_name or 'filing'} is complete — {order_number}", html)
cur.execute(
"UPDATE compliance_orders SET completion_email_sent_at = NOW() WHERE order_number = %s",
(order_number,),
)
conn.commit()
LOG.info("[completion] Sent completion email for %s to %s", order_number, email)
except Exception as exc:
LOG.error("[completion] Failed for %s: %s", order_number, exc)
conn.rollback()
# 2. Send 24h follow-up emails (completion email sent 24h+ ago, follow-up not sent)
cur.execute("""
SELECT order_number, customer_email, customer_name, service_name
FROM compliance_orders
WHERE payment_status = 'paid'
AND completion_email_sent_at IS NOT NULL
AND completion_email_sent_at < NOW() - interval '24 hours'
AND followup_email_sent_at IS NULL
ORDER BY completion_email_sent_at
LIMIT 20
""")
followups = cur.fetchall()
for row in followups:
order_number, email, name, service_name = row
if not email:
continue
try:
referral_code = get_or_create_referral_code(conn, email, name or "")
html = followup_email_html(order_number, service_name or "compliance filing", name or "", referral_code)
send_email(email, f"How was your experience? + Earn $25 referrals", html)
cur.execute(
"UPDATE compliance_orders SET followup_email_sent_at = NOW(), referral_code = %s WHERE order_number = %s",
(referral_code, order_number),
)
conn.commit()
LOG.info("[followup] Sent follow-up email for %s to %s (ref: %s)", order_number, email, referral_code)
except Exception as exc:
LOG.error("[followup] Failed for %s: %s", order_number, exc)
conn.rollback()
conn.close()
total = len(completions) + len(followups)
if total:
LOG.info("[completion_emails] Processed %d completion + %d follow-up emails", len(completions), len(followups))
def main():
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(name)s] %(levelname)s %(message)s",
)
process_completions()
if __name__ == "__main__":
main()