mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-06-18 15:29:36 +00:00
191 lines
7.8 KiB
Python
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,
|
|
)
|