Release v0.1.2
This commit is contained in:
@@ -11,8 +11,8 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from govoplan_core.admin.governance import get_system_settings
|
||||
from govoplan_core.core.optional import reraise_unless_missing_package
|
||||
from govoplan_core.db.models import AuditLog, Group, SystemSettings, Tenant, User
|
||||
from govoplan_campaign.backend.db.models import Campaign, CampaignJob, CampaignVersion, JobImapStatus, JobQueueStatus, JobSendStatus
|
||||
from govoplan_core.settings import settings
|
||||
|
||||
PRIVACY_POLICY_SETTINGS_KEY = "privacy_retention_policy"
|
||||
@@ -57,11 +57,11 @@ FINAL_VERSION_STATES = {
|
||||
"archived",
|
||||
}
|
||||
FINAL_EML_SEND_STATUSES = {
|
||||
JobSendStatus.SMTP_ACCEPTED.value,
|
||||
JobSendStatus.SENT.value,
|
||||
JobSendStatus.FAILED_PERMANENT.value,
|
||||
JobSendStatus.CANCELLED.value,
|
||||
JobSendStatus.OUTCOME_UNKNOWN.value,
|
||||
"smtp_accepted",
|
||||
"sent",
|
||||
"failed_permanent",
|
||||
"cancelled",
|
||||
"outcome_unknown",
|
||||
}
|
||||
AUDIT_DETAIL_REDACTION_KEYS = {
|
||||
"attachments",
|
||||
@@ -148,6 +148,22 @@ class PrivacyPolicyError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
def _campaign_models():
|
||||
try:
|
||||
from govoplan_campaign.backend.db.models import Campaign, CampaignJob, CampaignVersion, JobImapStatus, JobQueueStatus
|
||||
except ModuleNotFoundError as exc:
|
||||
reraise_unless_missing_package(exc, "govoplan_campaign")
|
||||
raise PrivacyPolicyError("Campaign module is not installed") from exc
|
||||
return Campaign, CampaignJob, CampaignVersion, JobImapStatus, JobQueueStatus
|
||||
|
||||
|
||||
def _campaign_models_or_none():
|
||||
try:
|
||||
return _campaign_models()
|
||||
except PrivacyPolicyError:
|
||||
return None
|
||||
|
||||
|
||||
def _utcnow() -> datetime:
|
||||
return datetime.now(timezone.utc)
|
||||
|
||||
@@ -251,8 +267,9 @@ def effective_privacy_policy(
|
||||
campaign_id: str | None = None,
|
||||
) -> PrivacyRetentionPolicy:
|
||||
policy = privacy_policy_from_session(session)
|
||||
campaign: Campaign | None = None
|
||||
campaign: Any | None = None
|
||||
if campaign_id:
|
||||
Campaign, _, _, _, _ = _campaign_models()
|
||||
campaign = session.get(Campaign, campaign_id)
|
||||
if campaign is None:
|
||||
raise PrivacyPolicyError("Campaign not found for privacy policy")
|
||||
@@ -290,6 +307,7 @@ def parent_privacy_policy(session: Session, *, tenant_id: str, scope_type: str,
|
||||
return policy
|
||||
if clean_scope != "campaign" or not scope_id:
|
||||
return policy
|
||||
Campaign, _, _, _, _ = _campaign_models()
|
||||
campaign = session.get(Campaign, scope_id)
|
||||
if campaign is None or campaign.tenant_id != tenant_id:
|
||||
raise PrivacyPolicyError("Campaign not found for privacy policy")
|
||||
@@ -318,11 +336,13 @@ def _retention_policy_source_fields(patch: dict[str, Any], *, baseline: bool = F
|
||||
|
||||
|
||||
def _retention_policy_source_step(scope_type: str, label: str, scope_id: str | None, patch: dict[str, Any], *, baseline: bool = False) -> dict[str, Any]:
|
||||
source_policy = PrivacyRetentionPolicy.model_validate(patch).model_dump(mode="json") if baseline else dict(patch)
|
||||
return {
|
||||
"scope_type": scope_type,
|
||||
"scope_id": scope_id,
|
||||
"label": label,
|
||||
"applied_fields": _retention_policy_source_fields(patch, baseline=baseline),
|
||||
"policy": source_policy,
|
||||
}
|
||||
|
||||
|
||||
@@ -336,8 +356,9 @@ def effective_privacy_policy_sources(
|
||||
) -> list[dict[str, Any]]:
|
||||
system_settings = get_system_settings(session)
|
||||
sources = [_retention_policy_source_step("system", "System", None, _privacy_policy_patch_from_settings(system_settings.settings or {}), baseline=True)]
|
||||
campaign: Campaign | None = None
|
||||
campaign: Any | None = None
|
||||
if campaign_id:
|
||||
Campaign, _, _, _, _ = _campaign_models()
|
||||
campaign = session.get(Campaign, campaign_id)
|
||||
if campaign is None:
|
||||
raise PrivacyPolicyError("Campaign not found for privacy policy")
|
||||
@@ -378,6 +399,7 @@ def parent_privacy_policy_sources(session: Session, *, tenant_id: str, scope_typ
|
||||
return sources
|
||||
if clean_scope != "campaign" or not scope_id:
|
||||
return sources
|
||||
Campaign, _, _, _, _ = _campaign_models()
|
||||
campaign = session.get(Campaign, scope_id)
|
||||
if campaign is None or campaign.tenant_id != tenant_id:
|
||||
raise PrivacyPolicyError("Campaign not found for privacy policy")
|
||||
@@ -413,6 +435,7 @@ def get_privacy_policy_for_scope(session: Session, *, tenant_id: str, scope_type
|
||||
raise PrivacyPolicyError("Group privacy policy not found")
|
||||
return _privacy_policy_patch_from_settings(group.settings or {})
|
||||
if clean_scope == "campaign":
|
||||
Campaign, _, _, _, _ = _campaign_models()
|
||||
campaign = session.get(Campaign, scope_id)
|
||||
if campaign is None or campaign.tenant_id != tenant_id:
|
||||
raise PrivacyPolicyError("Campaign privacy policy not found")
|
||||
@@ -460,6 +483,7 @@ def set_privacy_policy_for_scope(
|
||||
session.add(group)
|
||||
return patch
|
||||
if clean_scope == "campaign":
|
||||
Campaign, _, _, _, _ = _campaign_models()
|
||||
campaign = session.get(Campaign, scope_id)
|
||||
if campaign is None or campaign.tenant_id != tenant_id:
|
||||
raise PrivacyPolicyError("Campaign privacy policy not found")
|
||||
@@ -542,6 +566,10 @@ def _campaign_policy_for_id(session: Session, campaign_id: str | None, cache: di
|
||||
|
||||
def _apply_raw_json_retention(session: Session, *, dry_run: bool, now: datetime) -> dict[str, int]:
|
||||
result = {"eligible": 0, "redacted": 0, "skipped_not_final": 0, "already_redacted": 0}
|
||||
models = _campaign_models_or_none()
|
||||
if models is None:
|
||||
return result
|
||||
_, _, CampaignVersion, _, _ = models
|
||||
policy_cache: dict[str, PrivacyRetentionPolicy] = {}
|
||||
versions = (
|
||||
session.query(CampaignVersion)
|
||||
@@ -579,6 +607,10 @@ def _apply_raw_json_retention(session: Session, *, dry_run: bool, now: datetime)
|
||||
|
||||
def _apply_eml_retention(session: Session, *, dry_run: bool, now: datetime) -> dict[str, int]:
|
||||
result = {"eligible": 0, "metadata_cleared": 0, "files_deleted": 0, "files_missing": 0, "skipped_not_final": 0}
|
||||
models = _campaign_models_or_none()
|
||||
if models is None:
|
||||
return result
|
||||
_, CampaignJob, _, JobImapStatus, JobQueueStatus = models
|
||||
policy_cache: dict[str, PrivacyRetentionPolicy] = {}
|
||||
jobs = (
|
||||
session.query(CampaignJob)
|
||||
@@ -616,6 +648,10 @@ def _apply_eml_retention(session: Session, *, dry_run: bool, now: datetime) -> d
|
||||
|
||||
def _apply_report_detail_retention(session: Session, *, dry_run: bool, now: datetime) -> dict[str, int]:
|
||||
result = {"eligible_versions": 0, "summaries_redacted": 0, "already_redacted": 0}
|
||||
models = _campaign_models_or_none()
|
||||
if models is None:
|
||||
return result
|
||||
_, _, CampaignVersion, _, _ = models
|
||||
policy_cache: dict[str, PrivacyRetentionPolicy] = {}
|
||||
versions = (
|
||||
session.query(CampaignVersion)
|
||||
@@ -708,9 +744,12 @@ def _parse_datetime(value: Any) -> datetime | None:
|
||||
|
||||
def _privacy_policy_for_audit_item(session: Session, item: AuditLog, campaign_cache: dict[str, PrivacyRetentionPolicy], tenant_cache: dict[str, PrivacyRetentionPolicy]) -> PrivacyRetentionPolicy:
|
||||
if item.object_type == "campaign" and item.object_id:
|
||||
campaign = session.get(Campaign, str(item.object_id))
|
||||
if campaign is not None:
|
||||
return _campaign_policy_for_id(session, campaign.id, campaign_cache)
|
||||
models = _campaign_models_or_none()
|
||||
if models is not None:
|
||||
Campaign, _, _, _, _ = models
|
||||
campaign = session.get(Campaign, str(item.object_id))
|
||||
if campaign is not None:
|
||||
return _campaign_policy_for_id(session, campaign.id, campaign_cache)
|
||||
if item.tenant_id:
|
||||
if item.tenant_id not in tenant_cache:
|
||||
tenant_cache[item.tenant_id] = effective_privacy_policy(session, tenant_id=item.tenant_id)
|
||||
|
||||
Reference in New Issue
Block a user