46:def classify_error(msg: str) -> str:
57:def now():
61:def _effective_invoice_date(invoice_date_str: str) -> date:
69:class SendTask(Task):
70:    def on_failure(self, exc, task_id, args, kwargs, einfo):
74:@celery_app.task(bind=True, base=SendTask, name="app.tasks.send_tasks.send_one",
76:def send_one(self, campaign_id: int, campaign_recipient_id: int):
300:def _recipient_dict(r) -> dict:
306:def _plain_fallback(html: str) -> str:
313:def _unsubscribe_url(recipient, campaign) -> str:
320:def _sanitize_response(resp) -> str:
327:def _rate_window(key: str, interval: float):
349:def _reschedule(db, task, campaign_id, cr, job, delay: timedelta, reason: str):
360:def _handle_send_error(db, task, campaign, cr, job, account, attempt, error: str):
361:    err_class = classify_error(error)
369:    if err_class == "permanent":
372:        attempt.error_class = err_class
392:        attempt.error_class = err_class
408:    attempt.error_class = err_class
416:def _push_dlq(campaign_id, cr_id, error):
425:def _publish_control(campaign_id, action, reason=""):
434:def _publish_progress(campaign_id):
