#!/usr/bin/env python3 """판례 JSONL 스키마와 사건번호/출처 중복을 검증한다.""" from __future__ import annotations import argparse import json import re import sys from pathlib import Path if __package__ in (None, ""): sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from app.engine.legal_risk import load_precedents CASE_ID_RE = re.compile(r"^(?:19|20)?\d{2}[가-힣]{1,4}\d+$") ALLOWED_WORK_TYPES = {"literary", "visual", "musical"} ALLOWED_LEGAL_TAGS = { "reproduction", "derivative_work", "public_transmission", "distribution", "publication", "attribution", "integrity", "citation_missing", "false_authorship", } def validate_engine_labels(path: Path) -> None: for lineno, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1): if not line.strip(): continue row = json.loads(line) case_id = str(row.get("case_id", "")) if not CASE_ID_RE.fullmatch(case_id): raise ValueError(f"{path}:{lineno} 사건번호 형식 오류: {case_id}") work_types = set(row.get("work_types", [])) legal_tags = set(row.get("legal_tags", [])) criteria = row.get("criteria", []) if unknown := work_types - ALLOWED_WORK_TYPES: raise ValueError(f"{path}:{lineno} 미지원 저작물 유형: {sorted(unknown)}") if unknown := legal_tags - ALLOWED_LEGAL_TAGS: raise ValueError(f"{path}:{lineno} 미지원 법적 태그: {sorted(unknown)}") if not legal_tags: raise ValueError(f"{path}:{lineno} 법적 태그 없음: {case_id}") if not work_types and not criteria: raise ValueError(f"{path}:{lineno} 저작물 유형·판단 기준 없음: {case_id}") def main() -> int: p = argparse.ArgumentParser(description=__doc__) p.add_argument("jsonl", type=Path) args = p.parse_args() precedents = load_precedents(args.jsonl) validate_engine_labels(args.jsonl) print(json.dumps({"valid": True, "precedent_count": len(precedents)}, ensure_ascii=False)) return 0 if __name__ == "__main__": raise SystemExit(main())