Release v0.1.2
This commit is contained in:
@@ -28,11 +28,15 @@ from govoplan_campaign.backend.db.models import (
|
||||
SendAttempt,
|
||||
)
|
||||
from govoplan_campaign.backend.sending.execution import ExecutionSnapshot, ExecutionSnapshotError, ensure_execution_snapshot, runtime_imap_config, runtime_smtp_config
|
||||
from govoplan_mail.backend.mail_profiles import MailProfileError, assert_mail_policy_allows_send
|
||||
from govoplan_mail.backend.sending.rate_limit import wait_for_rate_limit
|
||||
from govoplan_mail.backend.sending.smtp import SmtpConfigurationError, SmtpSendError, send_email_bytes
|
||||
from govoplan_mail.backend.sending.imap import ImapAppendError, ImapConfigurationError, append_message_to_sent
|
||||
from govoplan_files.backend.storage.services import mark_job_attachment_uses_sent
|
||||
from govoplan_campaign.backend.integrations import (
|
||||
ImapAppendError,
|
||||
ImapConfigurationError,
|
||||
MailProfileError,
|
||||
SmtpConfigurationError,
|
||||
SmtpSendError,
|
||||
files_integration,
|
||||
mail_integration,
|
||||
)
|
||||
|
||||
|
||||
class QueueingError(RuntimeError):
|
||||
@@ -709,7 +713,7 @@ def reconcile_job_outcome(
|
||||
job.claim_token = None
|
||||
job.last_error = note or "Operator confirmed SMTP acceptance after an uncertain outcome."
|
||||
job.imap_status = JobImapStatus.PENDING.value if snapshot.delivery.imap_append_sent.enabled else JobImapStatus.NOT_REQUESTED.value
|
||||
mark_job_attachment_uses_sent(session, job)
|
||||
files_integration().mark_job_attachment_uses_sent(session, job)
|
||||
attempt_status = "reconciled_smtp_accepted"
|
||||
elif decision == "not_sent":
|
||||
job.send_status = JobSendStatus.FAILED_TEMPORARY.value
|
||||
@@ -1054,7 +1058,7 @@ def send_campaign_job(
|
||||
|
||||
job = session.get(CampaignJob, job.id)
|
||||
assert job is not None
|
||||
wait_for_rate_limit(
|
||||
mail_integration().wait_for_rate_limit(
|
||||
key=f"tenant:{job.tenant_id}:campaign:{job.campaign_id}",
|
||||
messages_per_minute=snapshot.delivery.rate_limit.messages_per_minute,
|
||||
enabled=use_rate_limit,
|
||||
@@ -1062,7 +1066,7 @@ def send_campaign_job(
|
||||
attempt = _record_attempt_start(session, job, claim_token)
|
||||
try:
|
||||
smtp_config = runtime_smtp_config(session, version, snapshot)
|
||||
assert_mail_policy_allows_send(
|
||||
mail_integration().assert_mail_policy_allows_send(
|
||||
session,
|
||||
tenant_id=job.tenant_id,
|
||||
campaign_id=job.campaign_id,
|
||||
@@ -1071,7 +1075,7 @@ def send_campaign_job(
|
||||
from_header=_from_header_from_job(job, snapshot),
|
||||
recipients=envelope_recipients,
|
||||
)
|
||||
result = send_email_bytes(
|
||||
result = mail_integration().send_email_bytes(
|
||||
message_bytes,
|
||||
smtp_config=smtp_config,
|
||||
envelope_from=envelope_from,
|
||||
@@ -1098,7 +1102,7 @@ def send_campaign_job(
|
||||
else:
|
||||
job.imap_status = JobImapStatus.NOT_REQUESTED.value
|
||||
job.last_error = refused_warning
|
||||
mark_job_attachment_uses_sent(session, job)
|
||||
files_integration().mark_job_attachment_uses_sent(session, job)
|
||||
session.add(attempt)
|
||||
session.add(job)
|
||||
_update_campaign_after_job(session, job.campaign_id, job.campaign_version_id)
|
||||
@@ -1208,9 +1212,9 @@ def append_sent_for_job(session: Session, *, job_id: str, dry_run: bool = False)
|
||||
session.commit()
|
||||
return AppendSentResult(job_id=job.id, status="not_requested", attempt_number=0, dry_run=dry_run)
|
||||
imap_config = runtime_imap_config(session, version, snapshot)
|
||||
if not imap_config or not imap_config.enabled:
|
||||
if not imap_config:
|
||||
job.imap_status = JobImapStatus.SKIPPED.value
|
||||
job.last_error = "IMAP append requested, but the execution snapshot has no enabled IMAP configuration"
|
||||
job.last_error = "IMAP append requested, but the execution snapshot has no IMAP configuration"
|
||||
session.add(job)
|
||||
session.commit()
|
||||
return AppendSentResult(job_id=job.id, status="skipped", attempt_number=0, dry_run=dry_run, message=job.last_error)
|
||||
@@ -1230,14 +1234,14 @@ def append_sent_for_job(session: Session, *, job_id: str, dry_run: bool = False)
|
||||
|
||||
attempt = _record_imap_attempt_start(session, job)
|
||||
try:
|
||||
assert_mail_policy_allows_send(
|
||||
mail_integration().assert_mail_policy_allows_send(
|
||||
session,
|
||||
tenant_id=job.tenant_id,
|
||||
campaign_id=job.campaign_id,
|
||||
smtp=snapshot.smtp,
|
||||
imap=imap_config,
|
||||
)
|
||||
result = append_message_to_sent(
|
||||
result = mail_integration().append_message_to_sent(
|
||||
message_bytes,
|
||||
imap_config=imap_config,
|
||||
folder=None if folder == "auto" else folder,
|
||||
@@ -1246,7 +1250,7 @@ def append_sent_for_job(session: Session, *, job_id: str, dry_run: bool = False)
|
||||
attempt.folder = result.folder
|
||||
job.imap_status = JobImapStatus.APPENDED.value
|
||||
job.last_error = None
|
||||
mark_job_attachment_uses_sent(session, job)
|
||||
files_integration().mark_job_attachment_uses_sent(session, job)
|
||||
session.add(attempt)
|
||||
session.add(job)
|
||||
session.commit()
|
||||
@@ -1263,7 +1267,15 @@ def append_sent_for_job(session: Session, *, job_id: str, dry_run: bool = False)
|
||||
raise
|
||||
|
||||
|
||||
def enqueue_pending_imap_appends(session: Session, *, tenant_id: str, campaign_id: str, enqueue_celery: bool = True, dry_run: bool = False) -> dict[str, Any]:
|
||||
def enqueue_pending_imap_appends(
|
||||
session: Session,
|
||||
*,
|
||||
tenant_id: str,
|
||||
campaign_id: str,
|
||||
enqueue_celery: bool = True,
|
||||
run_inline: bool = False,
|
||||
dry_run: bool = False,
|
||||
) -> dict[str, Any]:
|
||||
campaign = _get_campaign_for_tenant(session, campaign_id=campaign_id, tenant_id=tenant_id)
|
||||
jobs = (
|
||||
session.query(CampaignJob)
|
||||
@@ -1276,15 +1288,39 @@ def enqueue_pending_imap_appends(session: Session, *, tenant_id: str, campaign_i
|
||||
.order_by(CampaignJob.entry_index.asc())
|
||||
.all()
|
||||
)
|
||||
should_enqueue = _should_enqueue_celery(enqueue_celery) and not dry_run
|
||||
if should_enqueue:
|
||||
should_enqueue = _should_enqueue_celery(enqueue_celery) and not dry_run and not run_inline
|
||||
results: list[dict[str, Any]] = []
|
||||
appended_count = 0
|
||||
failed_count = 0
|
||||
skipped_count = 0
|
||||
if run_inline or dry_run:
|
||||
for job in jobs:
|
||||
try:
|
||||
result = append_sent_for_job(session, job_id=job.id, dry_run=dry_run)
|
||||
payload = result.as_dict()
|
||||
results.append(payload)
|
||||
if result.status == JobImapStatus.APPENDED.value:
|
||||
appended_count += 1
|
||||
elif result.status in {"skipped", "not_requested", "not_sent", "already_appended", "dry_run"}:
|
||||
skipped_count += 1
|
||||
except Exception as exc: # keep processing later jobs and expose per-job details
|
||||
failed_count += 1
|
||||
results.append({"job_id": job.id, "status": "failed", "message": str(exc)})
|
||||
elif should_enqueue:
|
||||
for job in jobs:
|
||||
_celery_enqueue_append_sent_job(job.id)
|
||||
|
||||
return {
|
||||
"campaign_id": campaign.id,
|
||||
"pending_count": len(jobs),
|
||||
"enqueued_count": len(jobs) if should_enqueue else 0,
|
||||
"processed_count": len(results) if run_inline and not dry_run else 0,
|
||||
"appended_count": appended_count,
|
||||
"failed_count": failed_count,
|
||||
"skipped_count": skipped_count,
|
||||
"dry_run": dry_run,
|
||||
"run_inline": run_inline,
|
||||
"results": results,
|
||||
}
|
||||
|
||||
def next_retry_delay(snapshot: ExecutionSnapshot, attempt_count: int) -> int:
|
||||
|
||||
Reference in New Issue
Block a user