forked from ChelseaKR/permit-bearings
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscreening.py
More file actions
111 lines (92 loc) · 3.84 KB
/
Copy pathscreening.py
File metadata and controls
111 lines (92 loc) · 3.84 KB
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
"""Deterministic pathway screening.
Rules are data, not code: each rule is a JSON record carrying its own
citation and verification status. The engine never emits a result whose
rule lacks a citation — an uncited rule is a schema error, not a softer
answer.
"""
from __future__ import annotations
import json
from dataclasses import dataclass, field
from datetime import date
from pathlib import Path
from typing import Any
ROUTE_CLASSES = ("ministerial", "discretionary", "mixed")
_OPS = {
"eq": lambda a, b: a == b,
"lte": lambda a, b: a is not None and a <= b,
"gte": lambda a, b: a is not None and a >= b,
"in": lambda a, b: a in b,
}
@dataclass(frozen=True)
class Citation:
"""The source a rule encodes. `verified_on` records the date attached to
its source evidence; it does not identify a reviewer or jurisdiction
approval. The harness reports records without a date separately."""
source: str # e.g. "Gov. Code § 66321(b)(3)" or an HCD document title
url: str
excerpt: str | None = None # supporting source text recorded for the rule
excerpt_sha256: str | None = None
verified_on: str | None = None # ISO date of last verification against source
@property
def is_verified(self) -> bool:
return self.verified_on is not None
def is_stale(self, max_age_days: int, today: date) -> bool:
if not self.is_verified:
return True
verified = date.fromisoformat(self.verified_on)
return (today - verified).days > max_age_days
@dataclass(frozen=True)
class Rule:
rule_id: str
pathway: str # e.g. "ADU ministerial approval"
route_class: str # ministerial | discretionary | mixed
jurisdiction_scope: str # "statewide" or a jurisdiction slug
criteria: list[dict[str, Any]] # [{"field", "op", "value"}, ...]
citation: Citation
required_documents: list[str] = field(default_factory=list)
notes: str = ""
def matches(self, intake: dict[str, Any]) -> bool:
for c in self.criteria:
op = _OPS[c["op"]]
if not op(intake.get(c["field"]), c["value"]):
return False
return True
@dataclass(frozen=True)
class PathwayResult:
rule: Rule
verified: bool
def summary(self) -> str:
badge = ("dated source record" if self.verified
else "NO DATED SOURCE RECORD")
return (
f"{self.rule.pathway} ({self.rule.route_class}) — "
f"{self.rule.citation.source} [{badge}]"
)
def load_rules(path: Path) -> list[Rule]:
"""Load rules from a JSON file, or from every *.json file in a
directory (sorted by filename: statewide plus per-jurisdiction files)."""
files = sorted(path.glob("*.json")) if path.is_dir() else [path]
rules = []
for record in (r for f in files for r in json.loads(f.read_text())):
citation = Citation(**record.pop("citation"))
rule = Rule(citation=citation, **record)
if rule.route_class not in ROUTE_CLASSES:
raise ValueError(f"{rule.rule_id}: unknown route_class {rule.route_class!r}")
if not rule.citation.source or not rule.citation.url:
raise ValueError(f"{rule.rule_id}: rule has no citation")
rules.append(rule)
return rules
def screen(intake: dict[str, Any], rules: list[Rule]) -> list[PathwayResult]:
"""Return candidate pathways for a structured intake. Results from
rules without dated source evidence are still returned — flagged, never
hidden — because hiding them would misrepresent coverage."""
jurisdiction = intake.get("jurisdiction")
applicable = [
r for r in rules
if r.jurisdiction_scope in ("statewide", jurisdiction)
]
return [
PathwayResult(rule=r, verified=r.citation.is_verified)
for r in applicable
if r.matches(intake)
]