Files
Aislo/resources/tester/test_m01_logic_source.py
T
eomsangdonandClaude Sonnet 5.5 2d954a14dd feat(M01): 50-2 원문 패널 — 마크다운 기호 벗김 · 출처 항 · 주 강조
- 서버 원문 조각에서 역슬래시 · 인용 머리 · 글 안 주석 벗김(원본 자료 손 안 댐)
- 출처 항 · 주 번호(3-4-2 1 · 9-7-1 주⑥)를 source() 답에 실음 — 그 조각 hit 표시
- 패널이 그 항을 옅은 바탕 + 막대로 강조 · 보이게 스크롤 · 머리에 「1항」 · 「주⑥」
- 시험 3 추가 · 화면 시나리오 50-2

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NPJGtfXo6GMbcpiboQC4uK
2026-10-05 18:42:15 +09:00

107 lines
4.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""M01 로직 원문 — 출처 글 → 품셈 본문 절(44-4). 원본은 읽기만."""
from __future__ import annotations
import json
from fastapi import FastAPI
from fastapi.testclient import TestClient
from M01_MasterData import M01_MasterData_Router as router_module
from M01_MasterData import M01_MasterData_Store as store
from M01_MasterData import M01_MasterData_Store_Make as make
def test_build_civil_section_with_table() -> None:
got = make.source("건설품셈 토목 1-1-2")
assert len(got["found"]) == 1
one = got["found"][0]
assert one["matched"] == "1-1-2"
assert one["file"].startswith("원가계산/건설공사_표준품셈/본문/02_토목부문/제01장_")
assert one["blocks"][0]["h"] == 3 and one["blocks"][0]["text"].startswith("1-1-2 유도선")
table = next(b["table"] for b in one["blocks"] if "table" in b)
assert table[0][0] == "구분" and ["특별인부", "인", "2", "1,350", "1,560"] in table
# 다음 절(1-1-3 …) 앞에서 끊김 · 주석 줄 뺌
assert not any("1-1-3" in b.get("text", "") for b in one["blocks"])
assert not any(b.get("p", "").startswith("<!--") for b in one["blocks"])
def test_forest_and_many_numbers() -> None:
forest = make.source("산림품셈 3-4-2 1")["found"]
assert [x["matched"] for x in forest] == ["3-4-2"]
many = make.source("건설품셈 공통 8-1-5 · 8-1-6")["found"]
assert [x["number"] for x in many] == ["8-1-5", "8-1-6"]
def test_no_link() -> None:
assert make.source("현장 자체 단가")["found"] == []
assert make.source("건설품셈 토목 99-9-9")["found"] == []
def test_most_master_logics_link() -> None:
"""정본 로직 출처 대부분이 원문 절에 닿음."""
hit = total = 0
for path in store.FOLDER.glob("로직_*품셈_*.json"):
for row in json.loads(path.read_text(encoding="utf-8"))["줄"]:
total += 1
hit += bool(make.source(row.get("출처", ""))["found"])
assert total > 1000 and hit / total > 0.9, (hit, total)
def test_route() -> None:
app = FastAPI()
app.include_router(router_module.router)
got = TestClient(app).get("/api/m01/logic/source", params={"src": "산림품셈 3-1"}).json()
assert got["found"][0]["matched"] == "3-1"
def test_tables_split_and_br() -> None:
"""빈 줄 · 주석이 붙은 표 둘을 가름(49-2) · 칸 안 `<br>` = 줄바꿈."""
one = make.source("건설품셈 공통 6-1-1")["found"][0]
tables = [b["table"] for b in one["blocks"] if "table" in b]
assert [len(t[0]) for t in tables] == [7, 2]
assert tables[0][2][0] == "인력운반\n타설"
def test_markdown_stripped() -> None:
"""역슬래시 · 인용 `>` 머리 · 글 안 주석을 벗김(50-2) — 모든 정본 출처 원문에 0."""
bad = 0
for path in store.FOLDER.glob("로직_*품셈_*.json"):
for row in json.loads(path.read_text(encoding="utf-8"))["줄"]:
for one in make.source(row.get("출처", ""))["found"]:
for b in one["blocks"]:
t = b.get("p") or b.get("text") or ""
bad += "\\" in t or "<!--" in t or t.startswith(">")
assert bad == 0
one = make.source("산림품셈 3-4-2 1")["found"][0]
assert any(b.get("p") == "Q = 3,600 × q × K × f × E / Cm" for b in one["blocks"])
def test_item_marked() -> None:
"""출처 항 · 주 번호 → 그 조각만 hit(절은 통째) · 못 찾으면 item 없음."""
def hits(src: str) -> list[str]:
one = make.source(src)["found"][0]
return [(b.get("p") or b.get("text") or "table")[:2] for b in one["blocks"] if b.get("hit")]
one = make.source("산림품셈 3-4-2 1")["found"][0]
assert one["item"] == "1" and one["blocks"][0].get("hit") is None
assert not any(b.get("hit") for b in one["blocks"] if b.get("p", "").startswith("2."))
assert hits("산림품셈 3-4-2 1")[0] == "1."
assert hits("산림품셈 9-7-1 주⑥") == ["⑥ "]
assert make.source("산림품셈 13-10-2 2")["found"][0]["item"] == "2"
assert "item" not in make.source("산림품셈 3-4-2")["found"][0]
def test_item_all_marked() -> None:
"""항 · 주 번호가 달린 정본 출처는 전부 표시가 붙음(전수)."""
total = missing = 0
for path in store.FOLDER.glob("로직_*품셈_*.json"):
for row in json.loads(path.read_text(encoding="utf-8"))["줄"]:
src = row.get("출처", "")
if not any(i for _n, i in make._NUMBER.findall(src)):
continue
total += 1
missing += not any(f.get("item") for f in make.source(src)["found"])
assert total >= 30 and missing == 0, (total, missing)