115 lines
5.0 KiB
Python
115 lines
5.0 KiB
Python
import sys, os, re, json
|
||
import fitz # PyMuPDF
|
||
sys.stdout.reconfigure(encoding='utf-8')
|
||
|
||
# Load target items
|
||
with open('scratch/located_const_sections.json', 'r', encoding='utf-8') as f:
|
||
located_items = json.load(f)
|
||
|
||
md_map = {
|
||
"공통_제1장": "resources/knowledge/original/원가계산/건설공사_표준품셈/01_공통부문/제1장_적용기준.md",
|
||
"공통_제3장": "resources/knowledge/original/원가계산/건설공사_표준품셈/01_공통부문/제3장_토공사.md",
|
||
"공통_제4장": "resources/knowledge/original/원가계산/건설공사_표준품셈/01_공통부문/제4장_조경공사.md",
|
||
"공통_제6장": "resources/knowledge/original/원가계산/건설공사_표준품셈/01_공통부문/제6장_철근콘크리트공사.md",
|
||
"토목_제1장": "resources/knowledge/original/원가계산/건설공사_표준품셈/02_토목부문/제1장_도로포장공사.md",
|
||
"토목_제6장": "resources/knowledge/original/원가계산/건설공사_표준품셈/02_토목부문/제6장_관부설및접합공사.md",
|
||
"건축_제9장": "resources/knowledge/original/원가계산/건설공사_표준품셈/03_건축부문/제9장_미장공사.md",
|
||
"유지관리_제1장": "resources/knowledge/original/원가계산/건설공사_표준품셈/05_유지관리부문/제1장_공통.md",
|
||
"유지관리_제3장": "resources/knowledge/original/원가계산/건설공사_표준품셈/05_유지관리부문/제3장_건축.md",
|
||
}
|
||
|
||
md_texts = {}
|
||
for k, p in md_map.items():
|
||
if os.path.exists(p):
|
||
with open(p, 'r', encoding='utf-8') as f:
|
||
md_texts[k] = f.read()
|
||
|
||
pdf_path = "resources/knowledge/original/원가계산/건설공사_표준품셈/2026년_건설공사_표준품셈.pdf"
|
||
doc = fitz.open(pdf_path)
|
||
|
||
audit_details = []
|
||
|
||
for item in located_items:
|
||
k = item['key']
|
||
num = item['number']
|
||
name = item['name']
|
||
pn = item['path_name']
|
||
pdf_pages = item.get('pdf_pages', [])
|
||
|
||
# Exclude chapter 8
|
||
if "공통부문 › 건설기계" in pn:
|
||
continue
|
||
|
||
# Determine chapter
|
||
md_key = None
|
||
if "공통부문 › 적용기준" in pn: md_key = "공통_제1장"
|
||
elif "공통부문 › 토공사" in pn: md_key = "공통_제3장"
|
||
elif "공통부문 › 조경공사" in pn: md_key = "공통_제4장"
|
||
elif "공통부문 › 철근콘크리트공사" in pn: md_key = "공통_제6장"
|
||
elif "토목부문 › 도로포장공사" in pn: md_key = "토목_제1장"
|
||
elif "토목부문 › 관부설" in pn: md_key = "토목_제6장"
|
||
elif "건축부문 › 미장공사" in pn: md_key = "건축_제9장"
|
||
elif "유지관리부문 › 공 통" in pn: md_key = "유지관리_제1장"
|
||
elif "유지관리부문 › 건 축" in pn: md_key = "유지관리_제3장"
|
||
|
||
raw_md = md_texts.get(md_key, "")
|
||
|
||
# Find how this section starts in raw_md
|
||
# Look for num in raw_md
|
||
escaped_num = re.escape(num)
|
||
# Search with regex
|
||
m = re.search(rf'([^\n]{{0,50}})({escaped_num}\s+[^\n]+)', raw_md)
|
||
header_status = "정상 분리"
|
||
preceding_context = ""
|
||
if m:
|
||
preceding = m.group(1)
|
||
header_line = m.group(2)
|
||
if preceding.strip():
|
||
header_status = "⚠ 앞 문장과 경계 붙음 (줄바꿈 누락)"
|
||
preceding_context = preceding.strip()[-30:]
|
||
else:
|
||
# Check if number appears at all
|
||
if num in raw_md:
|
||
header_status = "본문 중 인라인 출현"
|
||
else:
|
||
header_status = "❌ MD 본문 완전 부재"
|
||
|
||
# Find PDF text for relevant pages
|
||
actual_pages = [p for p in pdf_pages if p > 50]
|
||
pdf_text = ""
|
||
for p in actual_pages:
|
||
pdf_text += doc[p - 1].get_text()
|
||
|
||
# Check table collapse in this section's neighborhood
|
||
# Grab 50 lines around the match
|
||
section_flaws = []
|
||
if m:
|
||
start_pos = m.start()
|
||
# look forward 3000 characters
|
||
chunk = raw_md[start_pos:start_pos + 4000]
|
||
# check collapsed table rows in chunk
|
||
for line in chunk.split('\n'):
|
||
if line.strip().startswith('|') and line.strip().endswith('|'):
|
||
tokens = line.split()
|
||
if len(tokens) >= 15:
|
||
section_flaws.append(f"표 한 줄 뭉침 ({len(tokens)}개 단어): {line.strip()[:60]}...")
|
||
if '→' in line or '제1장' in line or '공통부문' in line:
|
||
if re.search(r'→\d+|\d+제\d+장', line):
|
||
section_flaws.append(f"페이지 전이 찌꺼기 삽입: {line.strip()[:60]}")
|
||
|
||
audit_entry = {
|
||
"key": k,
|
||
"section": num,
|
||
"name": name,
|
||
"chapter": md_key,
|
||
"pdf_pages": actual_pages,
|
||
"header_separation": header_status,
|
||
"preceding_context": preceding_context,
|
||
"flaws_detected": section_flaws
|
||
}
|
||
audit_details.append(audit_entry)
|
||
print(f"[{k}] {num} {name}: {header_status} | flaws={len(section_flaws)}")
|
||
|
||
with open('scratch/audit_42_detailed_breakdown.json', 'w', encoding='utf-8') as f:
|
||
json.dump(audit_details, f, ensure_ascii=False, indent=2)
|