inital commit
This commit is contained in:
78
server/app/mailer/commands/campaign_report.py
Normal file
78
server/app/mailer/commands/campaign_report.py
Normal file
@@ -0,0 +1,78 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from app.db.bootstrap import create_all_tables
|
||||
from app.db.session import SessionLocal
|
||||
from app.mailer.reports.campaigns import CampaignReportError, generate_campaign_report, generate_jobs_csv
|
||||
from app.security.api_keys import authenticate_api_key
|
||||
from app.settings import settings
|
||||
|
||||
|
||||
def _print_text_report(report: dict) -> None:
|
||||
campaign = report["campaign"]
|
||||
cards = report["cards"]
|
||||
delivery = report["delivery"]
|
||||
print(f"Campaign: {campaign['name']} ({campaign['id']})")
|
||||
print(f"Status: {campaign['status']}")
|
||||
print(f"Jobs: {cards['jobs_total']} total | {cards['queueable']} queueable | {cards['needs_attention']} need attention")
|
||||
print(f"Sending: {cards['sent']} sent | {cards['failed']} failed")
|
||||
print(f"IMAP: {cards['imap_appended']} appended | {cards['imap_failed']} failed")
|
||||
if delivery.get("rate_limit", {}).get("messages_per_minute"):
|
||||
print(
|
||||
"Rate: "
|
||||
f"{delivery['rate_limit']['messages_per_minute']}/min, concurrency {delivery['rate_limit']['concurrency']}"
|
||||
)
|
||||
if delivery.get("estimated_remaining_send_human"):
|
||||
print(f"ETA: {delivery['estimated_remaining_send_human']}")
|
||||
print("Validation counts:", report["status_counts"]["validation"])
|
||||
print("Send counts: ", report["status_counts"]["send"])
|
||||
print("Issue codes: ", report["issues"]["by_code"])
|
||||
print("Attachments: ", report["attachments"])
|
||||
failures = report.get("recent_failures") or []
|
||||
if failures:
|
||||
print("Recent failures:")
|
||||
for failure in failures[:10]:
|
||||
print(
|
||||
f" - entry={failure['entry_index']} recipient={failure['recipient_email']} "
|
||||
f"send={failure['send_status']} imap={failure['imap_status']} error={failure['last_error']}"
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Generate a campaign status/report payload.")
|
||||
parser.add_argument("--campaign-id", required=True, help="Database campaign UUID")
|
||||
parser.add_argument("--api-key", default=settings.dev_bootstrap_api_key)
|
||||
parser.add_argument("--json", action="store_true", help="Print machine-readable JSON")
|
||||
parser.add_argument("--include-jobs", action="store_true", help="Include per-job rows in JSON output")
|
||||
parser.add_argument("--jobs-csv", help="Write per-job report CSV to this path")
|
||||
args = parser.parse_args()
|
||||
|
||||
create_all_tables()
|
||||
with SessionLocal() as session:
|
||||
api_key = authenticate_api_key(session, args.api_key)
|
||||
if not api_key:
|
||||
raise SystemExit("Invalid API key")
|
||||
try:
|
||||
report = generate_campaign_report(
|
||||
session,
|
||||
tenant_id=api_key.tenant_id,
|
||||
campaign_id=args.campaign_id,
|
||||
include_jobs=args.include_jobs,
|
||||
)
|
||||
if args.jobs_csv:
|
||||
csv_text = generate_jobs_csv(session, tenant_id=api_key.tenant_id, campaign_id=args.campaign_id)
|
||||
Path(args.jobs_csv).write_text(csv_text, encoding="utf-8")
|
||||
print(f"Wrote {args.jobs_csv}")
|
||||
if args.json:
|
||||
print(json.dumps(report, indent=2, ensure_ascii=False, default=str))
|
||||
else:
|
||||
_print_text_report(report)
|
||||
except CampaignReportError as exc:
|
||||
raise SystemExit(str(exc)) from exc
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user