"""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
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, "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()