Coverage for src/lexigram/admin/gdpr/models.py: 0%
41 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-24 23:18 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-24 23:18 +0800
1"""GDPR data models — anonymization rules, SAR, and consent records."""
3from __future__ import annotations
5from dataclasses import dataclass, field
6from datetime import UTC, datetime
7from enum import StrEnum
8from typing import Any
11class AnonymizationStrategy(StrEnum):
12 """Strategy for anonymizing a single field.
14 * ``HASH`` — SHA-256 hex digest (irreversible, retains uniqueness)
15 * ``CLEAR`` — Replace with empty string
16 * ``REDACT`` — Replace with ``"[REDACTED]"``
17 * ``ZERO`` — Replace with ``0`` (for numeric fields)
18 * ``NULLIFY`` — Replace with ``None``
19 * ``FAKE_EMAIL`` — Replace with ``"anonymized@example.com"``
20 """
22 HASH = "hash"
23 CLEAR = "clear"
24 REDACT = "redact"
25 ZERO = "zero"
26 NULLIFY = "nullify"
27 FAKE_EMAIL = "fake_email"
30@dataclass
31class AnonymizationRule:
32 """Per-resource-type anonymization configuration.
34 Attributes:
35 resource_type: Resource name this rule applies to (e.g. ``"user"``).
36 fields: Mapping of field name → :class:`AnonymizationStrategy` (or
37 string shorthand).
38 preserve_id: When ``True``, the ``id`` field is always kept as-is.
39 """
41 resource_type: str
42 fields: dict[str, str | AnonymizationStrategy] = field(default_factory=dict)
43 preserve_id: bool = True
46class SARStatus(StrEnum):
47 """Subject Access Request lifecycle status."""
49 PENDING = "pending"
50 IN_PROGRESS = "in_progress"
51 COMPLETED = "completed"
52 REJECTED = "rejected"
55@dataclass
56class SubjectAccessRequest:
57 """A GDPR Subject Access Request.
59 Attributes:
60 sar_id: Unique identifier.
61 subject_id: User/subject identifier who made the request.
62 subject_email: Contact email for the response.
63 status: Current lifecycle status.
64 requested_at: UTC timestamp when the request was submitted.
65 completed_at: UTC timestamp when the request was fulfilled.
66 data_snapshot: Exported data (set when completed).
67 notes: Internal notes.
68 """
70 sar_id: str
71 subject_id: str
72 subject_email: str
73 status: SARStatus = SARStatus.PENDING
74 requested_at: datetime = field(default_factory=lambda: datetime.now(UTC))
75 completed_at: datetime | None = None
76 data_snapshot: dict[str, Any] = field(default_factory=dict)
77 notes: str = ""
80@dataclass
81class ConsentRecord:
82 """A single consent event for a subject.
84 Attributes:
85 consent_id: Unique ID.
86 subject_id: Who gave or withdrew consent.
87 purpose: Consent purpose slug (e.g. ``"marketing_email"``).
88 granted: ``True`` for grant, ``False`` for withdrawal.
89 recorded_at: UTC timestamp of the event.
90 metadata: Additional context (IP, user-agent, etc.).
91 """
93 consent_id: str
94 subject_id: str
95 purpose: str
96 granted: bool
97 recorded_at: datetime = field(default_factory=lambda: datetime.now(UTC))
98 metadata: dict[str, Any] = field(default_factory=dict)
101__all__ = [
102 "AnonymizationRule",
103 "AnonymizationStrategy",
104 "ConsentRecord",
105 "SARStatus",
106 "SubjectAccessRequest",
107]