system-prompts-and-models-o.../dealix/auto_client_acquisition/compliance_os/ropa.py
2026-05-01 14:03:52 +03:00

191 lines
7.8 KiB
Python

"""
RoPA — Records of Processing Activities.
PDPL requires controllers + processors to maintain a RoPA. This module
generates one programmatically per customer based on what Dealix actually
does with their data.
Output is exportable as JSON + CSV for SDAIA inspection.
"""
from __future__ import annotations
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Any
@dataclass
class ProcessingActivity:
"""One processing activity per PDPL Art. 14 / equivalent."""
activity_id: str
name_ar: str
name_en: str
purpose_ar: str
data_categories: list[str] # business_contact / behavioral / sensitive
data_subject_categories: list[str] # decision_makers / leads / customers
recipients: list[str] # who else sees this data
international_transfers: list[str] # GCC only / no transfers / specific
retention_period_days: int
security_measures: list[str]
lawful_basis: str
# The canonical list of activities Dealix performs
DEFAULT_ACTIVITIES: tuple[ProcessingActivity, ...] = (
ProcessingActivity(
activity_id="discovery",
name_ar="اكتشاف الشركات والـ leads",
name_en="Lead discovery",
purpose_ar="إيجاد شركات سعودية تطابق ICP العميل من المصادر العامة",
data_categories=["business_contact", "company_metadata"],
data_subject_categories=["business_decision_makers"],
recipients=["customer_internal_use_only"],
international_transfers=[],
retention_period_days=730,
security_measures=["TLS_1.3", "AES_256_at_rest", "RBAC", "audit_log"],
lawful_basis="legitimate_interest",
),
ProcessingActivity(
activity_id="enrichment",
name_ar="تكميل بيانات الشركات",
name_en="Lead enrichment",
purpose_ar="إكمال بيانات الشركة + DM من Apollo / ZoomInfo / LinkedIn",
data_categories=["business_contact"],
data_subject_categories=["business_decision_makers"],
recipients=["customer_internal_use_only", "enrichment_subprocessors"],
international_transfers=["enrichment_provider_us_eu"],
retention_period_days=730,
security_measures=["TLS_1.3", "AES_256_at_rest", "RBAC", "audit_log"],
lawful_basis="legitimate_interest",
),
ProcessingActivity(
activity_id="outreach",
name_ar="التواصل عبر القنوات (إيميل/واتساب/LinkedIn)",
name_en="B2B outreach",
purpose_ar="إرسال رسائل تجارية مخصصة بناءً على lawful basis مسجل",
data_categories=["business_contact", "behavioral"],
data_subject_categories=["business_decision_makers"],
recipients=["whatsapp_provider", "email_provider"],
international_transfers=["whatsapp_global", "gmail"],
retention_period_days=1095,
security_measures=["consent_check_pre_send", "opt_out_honored", "list_unsubscribe", "audit_log"],
lawful_basis="consent_or_legitimate_interest",
),
ProcessingActivity(
activity_id="reply_classification",
name_ar="تصنيف ردود العملاء",
name_en="Reply classification",
purpose_ar="فهم نية الرد وتحديد next action",
data_categories=["behavioral", "communication_content"],
data_subject_categories=["business_decision_makers"],
recipients=["customer_internal_use_only", "llm_provider_for_classification"],
international_transfers=["anthropic_groq_us"],
retention_period_days=1095,
security_measures=["TLS", "PII_redaction_for_LLM", "no_training_use"],
lawful_basis="legitimate_interest",
),
ProcessingActivity(
activity_id="customer_success",
name_ar="مراقبة صحة العميل + توليد QBR",
name_en="Customer health + QBR",
purpose_ar="قياس النجاح + تنبيه القادة + توصيات upsell",
data_categories=["behavioral", "business_contact"],
data_subject_categories=["customer_internal_users"],
recipients=["customer_internal_only"],
international_transfers=[],
retention_period_days=2555, # 7 years
security_measures=["TLS", "audit_log", "encrypted_at_rest"],
lawful_basis="contract",
),
ProcessingActivity(
activity_id="anonymized_benchmarks",
name_ar="benchmarks مجهولة بين العملاء",
name_en="Anonymized cross-customer benchmarks",
purpose_ar="نشر Pulse الشهري + benchmarks للقطاعات بمعايير privacy (≥5 شركات)",
data_categories=["aggregated_only"],
data_subject_categories=["aggregated_no_individual"],
recipients=["public_pulse_subscribers"],
international_transfers=[],
retention_period_days=3650, # 10 years for anonymized aggregates
security_measures=["min_cohort_5", "no_re_identification_possible", "linear_interpolation_only"],
lawful_basis="legitimate_interest",
),
)
@dataclass
class RoPAExporter:
"""Generates and exports the RoPA bundle."""
customer_id: str
customer_name: str
activities: tuple[ProcessingActivity, ...] = DEFAULT_ACTIVITIES
dpo_name: str | None = None
dpo_email: str | None = None
generated_at: datetime = field(
default_factory=lambda: datetime.now(timezone.utc).replace(tzinfo=None)
)
def to_json(self) -> dict[str, Any]:
return {
"customer_id": self.customer_id,
"customer_name": self.customer_name,
"dpo": {"name": self.dpo_name, "email": self.dpo_email},
"generated_at": self.generated_at.isoformat(),
"n_activities": len(self.activities),
"activities": [
{
"activity_id": a.activity_id,
"name_ar": a.name_ar,
"name_en": a.name_en,
"purpose_ar": a.purpose_ar,
"data_categories": a.data_categories,
"data_subject_categories": a.data_subject_categories,
"recipients": a.recipients,
"international_transfers": a.international_transfers,
"retention_period_days": a.retention_period_days,
"security_measures": a.security_measures,
"lawful_basis": a.lawful_basis,
}
for a in self.activities
],
}
def to_csv_rows(self) -> list[dict[str, Any]]:
"""For Excel-style export to compliance teams."""
rows: list[dict[str, Any]] = []
for a in self.activities:
rows.append({
"activity_id": a.activity_id,
"name_ar": a.name_ar,
"purpose_ar": a.purpose_ar,
"data_categories": "; ".join(a.data_categories),
"data_subject_categories": "; ".join(a.data_subject_categories),
"recipients": "; ".join(a.recipients),
"international_transfers": "; ".join(a.international_transfers),
"retention_period_days": a.retention_period_days,
"lawful_basis": a.lawful_basis,
"security_measures": "; ".join(a.security_measures),
})
return rows
def build_ropa(
*,
customer_id: str,
customer_name: str,
dpo_name: str | None = None,
dpo_email: str | None = None,
additional_activities: tuple[ProcessingActivity, ...] = (),
) -> RoPAExporter:
"""Build a RoPA exporter customized for one customer."""
return RoPAExporter(
customer_id=customer_id,
customer_name=customer_name,
dpo_name=dpo_name,
dpo_email=dpo_email,
activities=DEFAULT_ACTIVITIES + additional_activities,
)