File size: 3,832 Bytes
8bd39c0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
"""
์ •์ฑ… yaml ๋‹ค์ค‘ ๋กœ๋” + ์ •์ฑ…๋ณ„ ํ‰๊ฐ€ + ํ•ฉ์„ฑ.

ยง50 ์šฐ์„ ์ˆœ์œ„: opt_out > no_consent(6mo ์˜ˆ์™ธ) > night > ALLOWED
ยง15ยทยง16 ์šฐ์„ ์ˆœ์œ„: purpose_violation > retention_expired > ALLOWED
ํ•ฉ์„ฑ: ๋ชจ๋‘ ALLOWED์—ฌ์•ผ ALLOWED, ํ•˜๋‚˜๋ผ๋„ BLOCK์ด๋ฉด ์ •์ฑ…๋ณ„ ์‚ฌ์œ  ๋ˆ„์ (๊ฐ€์žฅ ๊ฐ•ํ•œ BLOCK ์ฒซ ๋ฒˆ์งธ).
"""
from __future__ import annotations

import pathlib
import yaml

_BLOCK_PRIORITY = {
    "BLOCKED_OPT_OUT": 0,
    "BLOCKED_PURPOSE": 1,
    "BLOCKED_RETENTION": 2,
    "BLOCKED_NO_CONSENT": 3,
    "BLOCKED_NIGHT": 4,
}


def load_policies() -> dict[str, dict]:
    """`policies/*.yaml` ๋ชจ๋‘ ๋กœ๋“œ โ†’ {name: policy_dict}."""
    base = pathlib.Path(__file__).parent / "policies"
    out: dict[str, dict] = {}
    for path in sorted(base.glob("*.yaml")):
        with open(path, encoding="utf-8") as f:
            data = yaml.safe_load(f)
        out[data["name"]] = data
    return out


def evaluate_infomatics(r: dict, *, send_hour: int, policy: dict) -> dict:
    """ยง50: opt_out > no_consent > night > ALLOWED."""
    citation = policy["citation"]
    rid = r.get("id")

    if r["opt_out"]:
        return {"status": "BLOCKED_OPT_OUT", "citation": citation, "id": rid, "policy": "infomatics"}

    if not r["marketing_consent"]:
        last = r.get("last_same_product_days")
        if last is None or last > policy["same_product_exception_days"]:
            return {"status": "BLOCKED_NO_CONSENT", "citation": citation, "id": rid, "policy": "infomatics"}
        return {"status": "ALLOWED_EXCEPTION", "citation": citation, "id": rid, "policy": "infomatics"}

    start = policy["night_window"]["start_hour"]
    end = policy["night_window"]["end_hour"]
    is_night = send_hour >= start or send_hour < end
    if is_night and not r["night_consent"]:
        return {"status": "BLOCKED_NIGHT", "citation": citation, "id": rid, "policy": "infomatics"}

    return {"status": "ALLOWED", "citation": citation, "id": rid, "policy": "infomatics"}


def evaluate_pipa(r: dict, policy: dict) -> dict:
    """ยง15ยทยง16: purpose_violation > retention_expired > ALLOWED."""
    citation = policy["citation"]
    rid = r.get("id")
    purpose = r.get("collected_purpose", "marketing")
    if purpose not in policy["allowed_purposes"]:
        return {"status": "BLOCKED_PURPOSE", "citation": citation, "id": rid, "policy": "pipa"}
    days = r.get("collected_days_ago", 0)
    if days > policy["retention_max_days"]:
        return {"status": "BLOCKED_RETENTION", "citation": citation, "id": rid, "policy": "pipa"}
    return {"status": "ALLOWED", "citation": citation, "id": rid, "policy": "pipa"}


def evaluate_recipient(r: dict, *, send_hour: int, policies: dict[str, dict]) -> dict:
    """๋‹ค์ค‘ ์ •์ฑ… ํ•ฉ์„ฑ. ALLOWED iff ๋ชจ๋“  ์ •์ฑ… ALLOWED. ์•„๋‹ˆ๋ฉด ๊ฐ€์žฅ ๊ฐ•ํ•œ BLOCK ์ฑ„ํƒ."""
    results = []
    if "infomatics" in policies:
        results.append(evaluate_infomatics(r, send_hour=send_hour, policy=policies["infomatics"]))
    if "pipa" in policies:
        results.append(evaluate_pipa(r, policies["pipa"]))

    blocks = [x for x in results if x["status"].startswith("BLOCKED_")]
    if not blocks:
        # ALLOWED ๋˜๋Š” ALLOWED_EXCEPTION ์ค‘ ๊ฐ€์žฅ ๊ฐ•ํ•œ ๋ผ๋ฒจ ์ฑ„ํƒ(EXCEPTION ์šฐ์„  ํ‘œ๊ธฐ)
        if any(x["status"] == "ALLOWED_EXCEPTION" for x in results):
            return next(x for x in results if x["status"] == "ALLOWED_EXCEPTION")
        return results[0]  # ALLOWED

    blocks.sort(key=lambda x: _BLOCK_PRIORITY.get(x["status"], 999))
    primary = blocks[0]
    primary["all_blocks"] = blocks  # ๋””๋ฒ„๊น…ยทreport ์šฉ
    return primary


def evaluate_recipient_legacy(r: dict, send_hour: int, policy: dict) -> dict:
    """03 ํ˜ธํ™˜ wrapper โ€” ๋‹จ์ผ infomatics ์ •์ฑ… ์‹œ๊ทธ๋‹ˆ์ฒ˜."""
    return evaluate_infomatics(r, send_hour=send_hour, policy=policy)