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)