Files
Aislo/resources/knowledge/original/_pipeline/fix_fences.py
T
eomsangdonandClaude Fable 5 6df07f60a1 fix(knowledge): 검토내역 8건 반영 — 상한 제거·행정규칙 부칙 복원·회귀검사 신설
타 AI 검토내역(tmp/기술문서DB_검토내역_260814.md) 전수 검증 후 동의분 적용.

파이프라인:
- fix_fences.py units[:20] / collect_law.py 별표 limit=80·변경이력 [:40] 상한 제거
- gen_md.py 기준목록 출력을 운영본(04_참조_법령기준_목록.md) 경로로 정정
- README 구경로(docs/raw/law) 3곳 현행화, ruff format 일괄 적용
- verify_structure.py 신설: XML↔md 부칙·목·별표·CHANGELOG 행수 회귀검사

신규 발견 결함 2건 정정 (검토내역에 없던 것):
- 행정규칙 XML은 부칙내용이 부칙 직속(부칙단위 래퍼 없음) — 변환기가 미처리해
  행정규칙 md 22건에 부칙 전무. patch_law_md 확장 후 부칙 138건 복원
- patch_law_md 부칙 dedup 키가 괄호형 타법개정 헤더 미매칭 — 재실행 시
  기존 부칙 1,724건 중복 추가되는 비멱등 결함. 정규식 보강

재수집 (law.go.kr):
- 대기환경보전법 시행규칙 별표 PDF 78건 추가 확보 (80→158, 전수)
- CHANGELOG 절단 3건 재생성 (고용보험법 시행령 42·국가계약법 시행령 41·
  대기환경 45회 — API 연혁과 전건 일치 확인)

기술문서·데이터:
- _최종검증로그: 55문서 스냅샷 범위 명시 (현재 65문서, 신설 10건 미검증 주지)
- 제비율.md frontmatter에 조달청 제비율(2026.04.13)·국민연금법 출처 보강
- 원가_입력변수_사전: 장비대금 지급보증을 공사종류별 표로 정정, csv→json 표기
- labor_const JSON: 원문 노임 118건 전건 일치 확인 후 해시만 재동기화
  (원문 변경분 = 신뢰도 플래그 컬럼뿐, 단가 무변)

검증: verify_structure 결함 0건, patch_law_md 재실행 +0 (멱등),
원가 JSON 해시 56건 중 잔여 불일치 1건(coef_2026 — 사용자 협의 대상)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-15 07:47:36 +09:00

95 lines
3.1 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""기존 법령 md의 코드펜스(``` 부칙·개정문)를 인용블록으로 전환.
- 부칙: 같은 폴더 XML에서 절단 없이 다시 읽어 인용블록으로 교체(2500자 절단 복구)
- 개정문·제개정이유(CHANGELOG): 코드펜스 → 인용블록
본문·이미지 로컬화 결과는 건드리지 않는다(부칙/개정문 섹션만 치환).
"""
import re, sys
import xml.etree.ElementTree as ET
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
FENCE = "```"
def clean(t):
t = (t or "").replace(" ", " ")
return re.sub(r"[ \t]+", " ", t).strip()
def to_quote(text):
return "\n".join(f"> {ln}" if ln.strip() else ">" for ln in text.split("\n"))
def buchik_from_xml(xml_path):
"""XML에서 부칙 인용블록 md(절단 없음) 생성. '## 부칙' 섹션 문자열 반환."""
root = ET.parse(xml_path).getroot()
bc = root.find("부칙")
if bc is None:
return None
units = bc.findall("부칙단위")
if not units:
return None
out = ["## 부칙", ""]
# 상한 없음 — 부칙 전건 출력 (상한을 두면 부칙 절단 결함 재발, 2026-08-15)
for b in units:
body = clean(b.findtext("부칙내용") or "")
out.append(to_quote(body))
out.append("")
return "\n".join(out).rstrip() + "\n"
def fix_law_md(md_path, xml_path):
"""현행/교본시점 md: '## 부칙' ~ 다음 '## ' 사이를 XML 부칙으로 교체."""
text = md_path.read_text(encoding="utf-8")
if "## 부칙" not in text:
return 0
new_buchik = buchik_from_xml(xml_path)
if not new_buchik:
return 0
# '## 부칙' 부터 다음 '## ' 헤딩 전까지 잘라 교체
m = re.search(r"^## 부칙\s*$", text, re.M)
if not m:
return 0
start = m.start()
nxt = re.search(r"^## (?!부칙)", text[m.end() :], re.M)
end = m.end() + nxt.start() if nxt else len(text)
new = text[:start] + new_buchik + ("\n" + text[end:].lstrip("\n") if text[end:].strip() else "")
if new != text:
md_path.write_text(new, encoding="utf-8")
return 1
return 0
def fix_changelog(md_path):
"""CHANGELOG: 개정문/제개정이유 코드펜스 → 인용블록."""
text = md_path.read_text(encoding="utf-8")
if FENCE not in text:
return 0
def repl(m):
inner = m.group(1).strip()
return to_quote(inner)
new = re.sub(FENCE + r"\w*\n(.*?)\n?" + FENCE, repl, text, flags=re.S)
if new != text:
md_path.write_text(new, encoding="utf-8")
return 1
return 0
if __name__ == "__main__":
n_law = n_cl = 0
for md in ROOT.rglob("*.md"):
if "임도기술교본" in str(md) or "_pipeline" in str(md):
continue
if md.name.startswith(("현행_", "교본시점_")):
xml = md.with_suffix(".xml")
if xml.exists():
n_law += fix_law_md(md, xml)
elif md.name == "CHANGELOG.md":
n_cl += fix_changelog(md)
print(f"부칙 인용블록 전환 {n_law}개 / CHANGELOG {n_cl}개")