Monitor Project Deadlines and Send Alerts


Missing deadlines costs client relationships. This script checks all active projects and tasks daily and sends an alert when anything is at risk.


The Script


python
#!/usr/bin/env python3
# deadline-monitor.py

import subprocess, json, datetime, urllib.request

SLACK_WEBHOOK = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
ALERT_DAYS    = 7  # warn when deadline is within this many days

def ao(cmd):
    r = subprocess.run(["ao"] + cmd.split() + ["--json"], capture_output=True, text=True)
    return json.loads(r.stdout).get("data", [])

def slack(text: str):
    data = json.dumps({"text": text}).encode()
    urllib.request.urlopen(urllib.request.Request(
        SLACK_WEBHOOK, data=data,
        headers={"Content-Type": "application/json"}
    ))

today    = datetime.date.today()
warnings = []
critical = []

# Check project deadlines
for proj in ao("projects list --status active"):
    due = proj.get("due_date")
    if not due:
        continue
    due_date = datetime.date.fromisoformat(due)
    days     = (due_date - today).days
    name     = proj["name"]

    if days < 0:
        critical.append(f":red_circle: *{name}* — OVERDUE by {-days} days (due {due})")
    elif days <= ALERT_DAYS:
        warnings.append(f":yellow_circle: *{name}* — due in {days} days ({due})")

# Check critical tasks
for proj in ao("projects list --status active"):
    pid   = proj["id"]
    pname = proj["name"]
    tasks = ao(f"projects task list {pid} --priority critical")

    for task in tasks:
        if task.get("status") in ("done", "cancelled"):
            continue
        due = task.get("due_date")
        if due:
            due_date = datetime.date.fromisoformat(due)
            days     = (due_date - today).days
            if days < 0:
                critical.append(f":red_circle: *[{pname}]* {task['title']} — OVERDUE by {-days} days")
            elif days <= ALERT_DAYS:
                warnings.append(f":yellow_circle: *[{pname}]* {task['title']} — due in {days} days")

# Send alerts
if not critical and not warnings:
    print("All clear — no deadlines at risk.")
else:
    lines = [f"*Deadline Monitor — {today}*"]
    if critical:
        lines.append("\n*OVERDUE:*")
        lines.extend(critical)
    if warnings:
        lines.append(f"\n*Due within {ALERT_DAYS} days:*")
        lines.extend(warnings)
    message = "\n".join(lines)
    print(message)
    slack(message)
    print(f"\nAlert sent: {len(critical)} overdue, {len(warnings)} upcoming")

Cron Schedule


bash
# Every morning at 7:30 AM
30 7 * * * python3 /path/to/deadline-monitor.py

Email Version


python
import smtplib
from email.mime.text import MIMEText

def email_alert(subject: str, body: str):
    msg = MIMEText(body)
    msg["Subject"] = subject
    msg["From"]    = "alerts@yourcompany.com"
    msg["To"]      = "team@yourcompany.com"
    with smtplib.SMTP("smtp.gmail.com", 587) as s:
        s.starttls()
        s.login("alerts@yourcompany.com", "your-app-password")
        s.send_message(msg)

if critical or warnings:
    email_alert(
        f"⚠️ Deadline Alert — {len(critical)} overdue, {len(warnings)} upcoming",
        "\n".join(critical + warnings)
    )

Extend: Check Invoices Too


python
# Add to the script
for inv in ao("invoice list --status overdue --limit 50"):
    amount = inv.get("total_amount") or inv.get("amount") or 0
    critical.append(f":moneybag: Invoice #{inv.get('invoice_number','?')} — ${amount:,.0f} overdue since {inv.get('due_date','?')} ")

Tip: Adjust ALERT_DAYS per project type — you might want 3 days for internal projects and 14 days for client-facing ones.