Add bulk calendar invitation delivery
This commit is contained in:
@@ -30,7 +30,7 @@ from govoplan_campaign.backend.db.models import (
|
||||
PostboxDeliveryAttempt,
|
||||
SendAttempt,
|
||||
)
|
||||
from govoplan_campaign.backend.integrations import postbox_integration
|
||||
from govoplan_campaign.backend.integrations import calendar_integration, postbox_integration
|
||||
from govoplan_campaign.backend.runtime import capability
|
||||
from govoplan_campaign.backend.sending.execution import ExecutionSnapshot
|
||||
from govoplan_campaign.backend.response_security import (
|
||||
@@ -38,6 +38,10 @@ from govoplan_campaign.backend.response_security import (
|
||||
public_delivery_result_message,
|
||||
public_source_filename,
|
||||
)
|
||||
from govoplan_campaign.backend.services.job_queries import (
|
||||
_calendar_invitation_state_from_provenance,
|
||||
_calendar_invitations_for_jobs,
|
||||
)
|
||||
|
||||
|
||||
class CampaignReportError(RuntimeError):
|
||||
@@ -586,7 +590,9 @@ def _job_row(
|
||||
*,
|
||||
include_diagnostics: bool = False,
|
||||
review_decision: dict[str, Any] | None = None,
|
||||
calendar_invitation: dict[str, object] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
invitation = calendar_invitation or _calendar_invitation_state_from_provenance(job)
|
||||
row = {
|
||||
"job_id": job.id,
|
||||
"entry_index": job.entry_index,
|
||||
@@ -639,6 +645,10 @@ def _job_row(
|
||||
review_decision,
|
||||
include_diagnostics=include_diagnostics,
|
||||
),
|
||||
"calendar_invitation": invitation,
|
||||
"calendar_rsvp_status": (
|
||||
invitation.get("rsvp_status") if invitation is not None else None
|
||||
),
|
||||
}
|
||||
if include_diagnostics:
|
||||
row.update(
|
||||
@@ -713,13 +723,16 @@ def _job_evidence_row(
|
||||
latest_message_action: CampaignMessageAction | None = None,
|
||||
include_diagnostics: bool = False,
|
||||
review_decision: dict[str, Any] | None = None,
|
||||
calendar_invitation: dict[str, object] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
row = _job_row(
|
||||
job,
|
||||
include_diagnostics=include_diagnostics,
|
||||
review_decision=review_decision,
|
||||
calendar_invitation=calendar_invitation,
|
||||
)
|
||||
public_decision = row.pop("review_decision") or {}
|
||||
invitation = row.pop("calendar_invitation") or {}
|
||||
recipients = job.resolved_recipients or {}
|
||||
row.update({
|
||||
"campaign_id": job.campaign_id,
|
||||
@@ -752,6 +765,13 @@ def _job_evidence_row(
|
||||
str(code)
|
||||
for code in public_decision.get("issue_codes") or []
|
||||
),
|
||||
"calendar_invitation_state": invitation.get("state"),
|
||||
"calendar_rsvp_status": invitation.get("rsvp_status"),
|
||||
"calendar_event_id": invitation.get("event_id"),
|
||||
"calendar_id": invitation.get("calendar_id"),
|
||||
"calendar_uid": invitation.get("uid"),
|
||||
"calendar_external_state": invitation.get("external_state"),
|
||||
"calendar_reply_ingress": invitation.get("reply_ingress"),
|
||||
})
|
||||
if include_diagnostics:
|
||||
row.update(
|
||||
@@ -870,6 +890,7 @@ def generate_campaign_report(
|
||||
)
|
||||
aggregate = _JobReportAggregate()
|
||||
job_rows: list[dict[str, Any]] = []
|
||||
included_jobs: list[CampaignJob] = []
|
||||
review_decisions = _review_decisions_by_job(version)
|
||||
retry_max_attempts = _retry_max_attempts(version)
|
||||
iterator = jobs.yield_per(500) if hasattr(jobs, "yield_per") else jobs
|
||||
@@ -880,19 +901,13 @@ def generate_campaign_report(
|
||||
include_recent_failures=include_recent_failures,
|
||||
)
|
||||
if include_jobs:
|
||||
if len(job_rows) >= CAMPAIGN_JSON_JOB_LIMIT:
|
||||
if len(included_jobs) >= CAMPAIGN_JSON_JOB_LIMIT:
|
||||
raise CampaignReportError(
|
||||
"The recipient-level JSON report exceeds the safe row "
|
||||
f"limit of {CAMPAIGN_JSON_JOB_LIMIT}; use the CSV export "
|
||||
"or the paginated recipient table."
|
||||
)
|
||||
job_rows.append(
|
||||
_job_row(
|
||||
job,
|
||||
include_diagnostics=include_diagnostics,
|
||||
review_decision=review_decisions.get(job.id),
|
||||
)
|
||||
)
|
||||
included_jobs.append(job)
|
||||
report = _campaign_report_payload(
|
||||
session,
|
||||
campaign=campaign,
|
||||
@@ -904,6 +919,19 @@ def generate_campaign_report(
|
||||
review_decisions=review_decisions,
|
||||
)
|
||||
if include_jobs:
|
||||
calendar_invitations = _calendar_invitations_for_jobs(
|
||||
session,
|
||||
included_jobs,
|
||||
)
|
||||
job_rows.extend(
|
||||
_job_row(
|
||||
job,
|
||||
include_diagnostics=include_diagnostics,
|
||||
review_decision=review_decisions.get(job.id),
|
||||
calendar_invitation=calendar_invitations.get(job.id),
|
||||
)
|
||||
for job in included_jobs
|
||||
)
|
||||
report["jobs"] = job_rows
|
||||
return report
|
||||
|
||||
@@ -943,6 +971,11 @@ def _campaign_report_payload(
|
||||
campaign_id=campaign.id,
|
||||
version=version,
|
||||
)
|
||||
calendar_invitations = calendar_integration().summarize_invitations(
|
||||
session,
|
||||
tenant_id=tenant_id,
|
||||
source_resource_id=version.id if version else None,
|
||||
)
|
||||
report = {
|
||||
"generated_at": _utcnow_iso(),
|
||||
"campaign": _campaign_report_campaign_payload(campaign),
|
||||
@@ -982,6 +1015,7 @@ def _campaign_report_payload(
|
||||
version=version,
|
||||
),
|
||||
"postbox_receipts": postbox_receipts,
|
||||
"calendar_invitations": calendar_invitations,
|
||||
"message_actions": _campaign_message_action_summary(
|
||||
session,
|
||||
tenant_id=tenant_id,
|
||||
@@ -1620,6 +1654,7 @@ def generate_jobs_csv(
|
||||
job_ids,
|
||||
)
|
||||
review_decisions = _review_decisions_by_job(version)
|
||||
calendar_invitations = _calendar_invitations_for_jobs(session, jobs)
|
||||
rows = [
|
||||
_job_evidence_row(
|
||||
job,
|
||||
@@ -1628,6 +1663,7 @@ def generate_jobs_csv(
|
||||
latest_message_action=latest_message_actions.get(job.id),
|
||||
include_diagnostics=include_diagnostics,
|
||||
review_decision=review_decisions.get(job.id),
|
||||
calendar_invitation=calendar_invitations.get(job.id),
|
||||
)
|
||||
for job in jobs
|
||||
]
|
||||
@@ -1705,6 +1741,13 @@ def generate_jobs_csv(
|
||||
"review_actor_user_id",
|
||||
"review_decided_at",
|
||||
"review_issue_codes",
|
||||
"calendar_invitation_state",
|
||||
"calendar_rsvp_status",
|
||||
"calendar_event_id",
|
||||
"calendar_id",
|
||||
"calendar_uid",
|
||||
"calendar_external_state",
|
||||
"calendar_reply_ingress",
|
||||
]
|
||||
if include_diagnostics:
|
||||
sent_at_index = fieldnames.index("outcome_unknown_at")
|
||||
|
||||
Reference in New Issue
Block a user