Release v0.1.2

This commit is contained in:
2026-06-25 19:58:20 +02:00
parent 39ad3500e2
commit 23318c709a
98 changed files with 3432 additions and 2339 deletions

View File

@@ -28,6 +28,7 @@ from govoplan_campaign.backend.schemas import (
CampaignResponse,
CampaignVersionDetailResponse,
CampaignVersionResponse,
CampaignWorkspaceResponse,
CampaignVersionSetStepRequest,
CampaignReviewStateRequest,
CampaignVersionUpdateRequest,
@@ -50,8 +51,7 @@ from govoplan_campaign.backend.persistence.campaigns import (
load_campaign_config_from_json,
validate_campaign_version,
)
from govoplan_files.backend.storage.files import current_version_and_blob
from govoplan_files.backend.storage.campaign_attachments import managed_match_payloads, prepared_campaign_snapshot
from govoplan_campaign.backend.integrations import files_integration
from govoplan_campaign.backend.campaign.loader import load_campaign_config, load_campaign_json
from govoplan_campaign.backend.attachments.resolver import resolve_campaign_attachments
from govoplan_core.security.time import utc_now
@@ -355,6 +355,62 @@ def get_campaign(
return CampaignResponse.model_validate(_get_campaign_for_principal(session, campaign_id, principal))
@router.get("/{campaign_id}/workspace", response_model=CampaignWorkspaceResponse)
def get_campaign_workspace(
campaign_id: str,
version_id: str | None = None,
include_current_version: bool = True,
include_summary: bool = False,
include_versions: bool = True,
session: Session = Depends(get_session),
principal: ApiPrincipal = Depends(require_scope("campaigns:campaign:read")),
):
campaign = _get_campaign_for_principal(session, campaign_id, principal)
versions: list[CampaignVersion] = []
if include_versions or include_current_version:
versions = (
session.query(CampaignVersion)
.filter(CampaignVersion.campaign_id == campaign.id)
.order_by(CampaignVersion.version_number.desc())
.all()
)
selected_version_id = version_id or campaign.current_version_id or (versions[0].id if versions else None)
current_version: CampaignVersion | None = None
if include_current_version and selected_version_id:
_require_permission(principal, "campaigns:recipient:read")
current_version = (
session.query(CampaignVersion)
.filter(
CampaignVersion.id == selected_version_id,
CampaignVersion.campaign_id == campaign.id,
)
.one_or_none()
)
summary_payload: dict[str, object] | None = None
if include_summary:
try:
summary_payload = generate_campaign_report(
session,
tenant_id=principal.tenant_id,
campaign_id=campaign_id,
version_id=selected_version_id,
include_jobs=False,
)
except CampaignReportError:
summary_payload = None
return CampaignWorkspaceResponse(
campaign=CampaignResponse.model_validate(campaign),
versions=[CampaignVersionResponse.model_validate(item) for item in versions] if include_versions else [],
current_version=CampaignVersionDetailResponse.model_validate(current_version) if current_version is not None else None,
summary=summary_payload,
selected_version_id=selected_version_id,
)
@router.put("/{campaign_id}", response_model=CampaignResponse)
def update_campaign_metadata_endpoint(
campaign_id: str,
@@ -1811,6 +1867,7 @@ def append_sent(
tenant_id=principal.tenant_id,
campaign_id=campaign_id,
enqueue_celery=payload.enqueue_celery,
run_inline=payload.run_inline,
dry_run=payload.dry_run,
)
audit_from_principal(
@@ -1841,7 +1898,7 @@ class CampaignAttachmentPreviewResponse(BaseModel):
def _file_preview(session: Session, asset) -> dict[str, object]:
version, blob = current_version_and_blob(session, asset)
version, blob = files_integration().current_version_and_blob(session, asset)
return {
"id": asset.id,
"version_id": version.id,
@@ -1876,7 +1933,8 @@ def preview_campaign_attachments(
raw = raw if isinstance(raw, dict) else {}
_require_mail_profile_use_if_needed(principal, raw)
with prepared_campaign_snapshot(
files = files_integration()
with files.prepared_campaign_snapshot(
session,
tenant_id=principal.tenant_id,
campaign_id=campaign.id,
@@ -1892,7 +1950,7 @@ def preview_campaign_attachments(
for entry in report.entries:
for attachment in entry.attachments:
managed_matches = managed_match_payloads(attachment.matches, prepared.managed_files_by_local_path)
managed_matches = files.managed_match_payloads(attachment.matches, prepared.managed_files_by_local_path)
matched_asset_ids.update(str(item["asset_id"]) for item in managed_matches)
matches: list[dict[str, object]] = [
{