feat: harden campaign delivery and editing

This commit is contained in:
2026-07-30 14:27:04 +02:00
parent 4a120e8009
commit cd223cbb95
34 changed files with 3370 additions and 165 deletions
@@ -23,10 +23,13 @@ from govoplan_campaign.backend.change_tracking import (
)
from govoplan_campaign.backend.db.models import (
CampaignJob,
CampaignMessageAction,
CampaignMessageActionAttempt,
ImapAppendAttempt,
PostboxDeliveryAttempt,
SendAttempt,
)
from govoplan_campaign.backend.integrations import postbox_integration
from govoplan_core.db.session import get_session
@@ -49,6 +52,27 @@ from govoplan_campaign.backend.services.job_queries import (
router = APIRouter(prefix="/campaigns", tags=["campaigns"])
def _postbox_receipts_for_attempts(
session: Session,
*,
tenant_id: str,
attempts: list[PostboxDeliveryAttempt],
):
integration = postbox_integration()
if not integration.receipt_evidence_available:
return None
delivery_ids = [
attempt.provider_delivery_id
for attempt in attempts
if attempt.provider_delivery_id
]
return integration.delivery_receipt_summaries(
session,
tenant_id=tenant_id,
delivery_ids=delivery_ids,
)
@router.get("/{campaign_id}/jobs", response_model=CampaignJobsResponse)
def list_jobs(
campaign_id: str,
@@ -373,12 +397,36 @@ def get_job_detail(
),
label="Postbox attempts for this campaign job",
)
message_actions = _job_attempt_rows(
session.query(CampaignMessageAction)
.filter(CampaignMessageAction.job_id == job.id)
.order_by(CampaignMessageAction.created_at.asc()),
label="Single-message actions for this campaign job",
)
action_ids = [action.id for action in message_actions]
message_action_attempts = (
_job_attempt_rows(
session.query(CampaignMessageActionAttempt)
.filter(CampaignMessageActionAttempt.action_id.in_(action_ids))
.order_by(CampaignMessageActionAttempt.started_at.asc()),
label="Single-message action attempts for this campaign job",
)
if action_ids
else []
)
return CampaignJobDetailResponse(
job=_job_detail_payload(job),
attempts=_job_attempts_payload(
send_attempts,
imap_attempts,
postbox_attempts,
message_actions,
message_action_attempts,
postbox_receipts=_postbox_receipts_for_attempts(
session,
tenant_id=principal.tenant_id,
attempts=postbox_attempts,
),
),
)
@@ -428,9 +476,33 @@ def get_job_diagnostics(
),
label="Postbox diagnostics for this campaign job",
)
message_actions = _job_attempt_rows(
session.query(CampaignMessageAction)
.filter(CampaignMessageAction.job_id == job.id)
.order_by(CampaignMessageAction.created_at.asc()),
label="Single-message action diagnostics for this campaign job",
)
action_ids = [action.id for action in message_actions]
message_action_attempts = (
_job_attempt_rows(
session.query(CampaignMessageActionAttempt)
.filter(CampaignMessageActionAttempt.action_id.in_(action_ids))
.order_by(CampaignMessageActionAttempt.started_at.asc()),
label="Single-message action-attempt diagnostics for this campaign job",
)
if action_ids
else []
)
return _job_diagnostics_payload(
job,
send_attempts,
imap_attempts,
postbox_attempts,
message_actions,
message_action_attempts,
postbox_receipts=_postbox_receipts_for_attempts(
session,
tenant_id=principal.tenant_id,
attempts=postbox_attempts,
),
)