feat(regulatory): 增加法规规则版本检查
This commit is contained in:
106
review_agent/regulatory_review/services/rule_loader.py
Normal file
106
review_agent/regulatory_review/services/rule_loader.py
Normal file
@@ -0,0 +1,106 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
from django.conf import settings
|
||||
|
||||
from review_agent.models import RegulatoryRuleVersion
|
||||
|
||||
|
||||
DEFAULT_RULE_CODE = "nmpa_ivd_registration_v1"
|
||||
DEFAULT_RULE_PATH = (
|
||||
Path(settings.BASE_DIR)
|
||||
/ "review_agent"
|
||||
/ "regulatory_review"
|
||||
/ "rules"
|
||||
/ "nmpa_ivd_registration_v1.yaml"
|
||||
)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RuleVersionCheck:
|
||||
status: str
|
||||
code: str
|
||||
path: Path
|
||||
current_hash: str
|
||||
database_hash: str = ""
|
||||
record: RegulatoryRuleVersion | None = None
|
||||
|
||||
|
||||
def compute_file_sha256(path: str | Path) -> str:
|
||||
file_path = Path(path)
|
||||
digest = hashlib.sha256()
|
||||
with file_path.open("rb") as handle:
|
||||
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
|
||||
digest.update(chunk)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def load_rule_file(path: str | Path | None = None) -> dict:
|
||||
rule_path = Path(path) if path else DEFAULT_RULE_PATH
|
||||
with rule_path.open("r", encoding="utf-8") as handle:
|
||||
payload = yaml.safe_load(handle) or {}
|
||||
if payload.get("code") != DEFAULT_RULE_CODE:
|
||||
raise ValueError(f"规则 code 必须为 {DEFAULT_RULE_CODE}")
|
||||
if not isinstance(payload.get("requirements"), list) or not payload["requirements"]:
|
||||
raise ValueError("规则文件必须包含 requirements 列表。")
|
||||
return payload
|
||||
|
||||
|
||||
def check_rule_version(
|
||||
*,
|
||||
path: str | Path | None = None,
|
||||
update_missing: bool = True,
|
||||
) -> RuleVersionCheck:
|
||||
rule_path = Path(path) if path else DEFAULT_RULE_PATH
|
||||
rule_set = load_rule_file(rule_path)
|
||||
current_hash = compute_file_sha256(rule_path)
|
||||
record = RegulatoryRuleVersion.objects.filter(code=rule_set["code"]).first()
|
||||
yaml_path = str(rule_path.relative_to(settings.BASE_DIR))
|
||||
|
||||
if record is None:
|
||||
if not update_missing:
|
||||
return RuleVersionCheck(
|
||||
status="missing",
|
||||
code=rule_set["code"],
|
||||
path=rule_path,
|
||||
current_hash=current_hash,
|
||||
)
|
||||
record = RegulatoryRuleVersion.objects.create(
|
||||
code=rule_set["code"],
|
||||
name=rule_set.get("name") or rule_set["code"],
|
||||
yaml_path=yaml_path,
|
||||
yaml_hash=current_hash,
|
||||
rag_collection=rule_set.get("rag_collection", ""),
|
||||
status=RegulatoryRuleVersion.Status.ACTIVE,
|
||||
)
|
||||
return RuleVersionCheck(
|
||||
status="created",
|
||||
code=record.code,
|
||||
path=rule_path,
|
||||
current_hash=current_hash,
|
||||
database_hash=record.yaml_hash,
|
||||
record=record,
|
||||
)
|
||||
|
||||
if record.yaml_hash != current_hash:
|
||||
return RuleVersionCheck(
|
||||
status="mismatch",
|
||||
code=record.code,
|
||||
path=rule_path,
|
||||
current_hash=current_hash,
|
||||
database_hash=record.yaml_hash,
|
||||
record=record,
|
||||
)
|
||||
|
||||
return RuleVersionCheck(
|
||||
status="ok",
|
||||
code=record.code,
|
||||
path=rule_path,
|
||||
current_hash=current_hash,
|
||||
database_hash=record.yaml_hash,
|
||||
record=record,
|
||||
)
|
||||
Reference in New Issue
Block a user