feat(b09,b08): 9-19-1 성토면 기계 = 굴착기 0.6㎥ 형식 두 갈래(무한궤도 947원/㎡ · 타이어 1,014원/㎡) · 제안 무한궤도(영월 실무 「성토사면고르기 06M3 B/H」 · 브레인 ②) — 칸 곁 제안만, 스스로 안 고름 · 마스터 갈래 키 재생성(바뀐 공종 9-19-1 하나)
일위대가 나머지 460 제목 변화 0 · 9-19-1 못 붙은 줄(굴착기 0.6㎥) 걷힘 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
This commit is contained in:
@@ -31,7 +31,7 @@ GROUND_SPLIT_GROUPS = ("흙깎기", "측구터파기", "구조물터파기", "
|
|||||||
|
|
||||||
#: 면고르기 갈래 — 품셈 9-19-1 원문 표 두 벌 그대로(L5414 절토면 토질 6 · L5430 성토면 시공·토질).
|
#: 면고르기 갈래 — 품셈 9-19-1 원문 표 두 벌 그대로(L5414 절토면 토질 6 · L5430 성토면 시공·토질).
|
||||||
#: B09 표 읽기(`_ResourceAxis_JudgedTable`)가 같은 이름으로 갈래를 세움 — 짝 시험이 마스터와 대조.
|
#: B09 표 읽기(`_ResourceAxis_JudgedTable`)가 같은 이름으로 갈래를 세움 — 짝 시험이 마스터와 대조.
|
||||||
#: ⚠ 제안값 없음(판정 Ⓒ) · 「성토면 · 기계」 는 굴착기 0.6㎥ 형식 둘이라 B09 가 안 세움(Ⓓ 사유).
|
#: ⚠ 토질·시공은 제안값 없음(판정 Ⓒ) · 성토면 기계는 굴착기 0.6㎥ 형식 둘로 갈래(브레인 ②).
|
||||||
FACE_DRESSING_CUT_CLASSES = (
|
FACE_DRESSING_CUT_CLASSES = (
|
||||||
"절토면 · 모래ㆍ사질토ㆍ점토ㆍ점질토",
|
"절토면 · 모래ㆍ사질토ㆍ점토ㆍ점질토",
|
||||||
"절토면 · 연질토ㆍ불순자갈",
|
"절토면 · 연질토ㆍ불순자갈",
|
||||||
@@ -43,7 +43,14 @@ FACE_DRESSING_CUT_CLASSES = (
|
|||||||
FACE_DRESSING_FILL_CLASSES = (
|
FACE_DRESSING_FILL_CLASSES = (
|
||||||
"성토면 · 인력 · 점토 또는 점질토",
|
"성토면 · 인력 · 점토 또는 점질토",
|
||||||
"성토면 · 인력 · 모래 또는 사질토",
|
"성토면 · 인력 · 모래 또는 사질토",
|
||||||
"성토면 · 기계",
|
"성토면 · 기계 · 무한궤도",
|
||||||
|
"성토면 · 기계 · 타이어",
|
||||||
|
)
|
||||||
|
#: 성토면 제안 — 칸 곁 회색 제안일 뿐 스스로 고르지 않음(「기본값을 몰래 확정으로 안 바꿈」).
|
||||||
|
FACE_DRESSING_FILL_SUGGESTED = (
|
||||||
|
"성토면 · 기계 · 무한궤도",
|
||||||
|
"영월 실무 설계내역 「성토사면고르기 06M3 B/H」(백호 = 무한궤도 굴착기) · 임도는 산지라"
|
||||||
|
" 타이어식이 잘 안 들어감 — 타이어식도 고를 수 있음(2026-09-14 브레인 ②)",
|
||||||
)
|
)
|
||||||
#: ⭐ 2026-09-14 브레인 판정 Ⓐ — 밑수 = 초류종자살포(파종) 면적. 반영률 칸을 따로 두면 같은 값을
|
#: ⭐ 2026-09-14 브레인 판정 Ⓐ — 밑수 = 초류종자살포(파종) 면적. 반영률 칸을 따로 두면 같은 값을
|
||||||
#: 두 곳에서 관리하게 됨. 실무도 그렇게 움직임(거창 파종 성토 50%·절토 100% · 영월 성토 50%).
|
#: 두 곳에서 관리하게 됨. 실무도 그렇게 움직임(거창 파종 성토 50%·절토 100% · 영월 성토 50%).
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ from B06_Section.B06_Section_Repository import (
|
|||||||
from B08_Quantity.B08_Quantity_Engine_EarthworkSummary import (
|
from B08_Quantity.B08_Quantity_Engine_EarthworkSummary import (
|
||||||
FACE_DRESSING_CUT_CLASSES,
|
FACE_DRESSING_CUT_CLASSES,
|
||||||
FACE_DRESSING_FILL_CLASSES,
|
FACE_DRESSING_FILL_CLASSES,
|
||||||
|
FACE_DRESSING_FILL_SUGGESTED,
|
||||||
SummaryInput,
|
SummaryInput,
|
||||||
)
|
)
|
||||||
from B08_Quantity.B08_Quantity_Engine_EarthworkSummary import build_table as build_summary_table
|
from B08_Quantity.B08_Quantity_Engine_EarthworkSummary import build_table as build_summary_table
|
||||||
@@ -166,6 +167,11 @@ async def get_earthwork_table(project_id: UUID, route_id: int) -> JSONResponse:
|
|||||||
table["face_dressing_choices"] = {
|
table["face_dressing_choices"] = {
|
||||||
"cut": list(FACE_DRESSING_CUT_CLASSES),
|
"cut": list(FACE_DRESSING_CUT_CLASSES),
|
||||||
"fill": list(FACE_DRESSING_FILL_CLASSES),
|
"fill": list(FACE_DRESSING_FILL_CLASSES),
|
||||||
|
# 성토면 제안(회색 · [제안값 넣기]) — 비워 두면 여전히 「안 정함」(브레인 ②).
|
||||||
|
"fill_suggested": {
|
||||||
|
"value": FACE_DRESSING_FILL_SUGGESTED[0],
|
||||||
|
"basis": FACE_DRESSING_FILL_SUGGESTED[1],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
# 준비공·사방공 — 못 서는 줄도 사유와 함께 남긴다(빈 표는 「빠뜨림」과 구별이 안 됨).
|
# 준비공·사방공 — 못 서는 줄도 사유와 함께 남긴다(빈 표는 「빠뜨림」과 구별이 안 됨).
|
||||||
structures = await _route_structures(project_id)
|
structures = await _route_structures(project_id)
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ KNOWN_GAPS: dict[str, tuple[str, str]] = {
|
|||||||
" — 표에 착암공 수량이 없어 줄을 안 세움(보통인부는 표 그대로). "
|
" — 표에 착암공 수량이 없어 줄을 안 세움(보통인부는 표 그대로). "
|
||||||
"ⓘ 절토면 풍화암: [주]② 「소형브레이커를 사용할 시는 연암 고르기 품을 **준용할 수 있다**」"
|
"ⓘ 절토면 풍화암: [주]② 「소형브레이커를 사용할 시는 연암 고르기 품을 **준용할 수 있다**」"
|
||||||
" — 선택지라 따로 갈래를 안 만듦(브레이커를 쓰면 연암 갈래를 고를 것). "
|
" — 선택지라 따로 갈래를 안 만듦(브레이커를 쓰면 연암 갈래를 고를 것). "
|
||||||
"ⓘ 성토면 · 기계: 굴착기 0.6㎥ 가 무한궤도·타이어 둘이라 **규격 미정** — 이 갈래는 안 섬"
|
"ⓘ 성토면 · 기계: 원문이 굴착기 0.6㎥ 형식을 안 적어 무한궤도·타이어 두 갈래로 세움"
|
||||||
"(굴착기 규격 칸과 함께 풀 자리 · 2026-09-14 판정 Ⓓ).",
|
"(제안 무한궤도 — 영월 실무 「06M3 B/H」 · 2026-09-14 브레인 ②).",
|
||||||
),
|
),
|
||||||
"FP-12-25": (
|
"FP-12-25": (
|
||||||
"운반거리 미정",
|
"운반거리 미정",
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ JUDGED_TABLES: dict[str, dict[str, Any]] = {
|
|||||||
"prefix": "성토면",
|
"prefix": "성토면",
|
||||||
# 원문 L5430 표 — 「인력시공」 칸이 두 줄 병합이라 둘째 줄(모래 또는 사질토)이 앞당겨 옴.
|
# 원문 L5430 표 — 「인력시공」 칸이 두 줄 병합이라 둘째 줄(모래 또는 사질토)이 앞당겨 옴.
|
||||||
"merged_rows": (1,),
|
"merged_rows": (1,),
|
||||||
|
# 기계시공 「굴착기 | 0.6㎥」 — 원문이 형식을 안 적음. 카탈로그 0.6 은 무한궤도·타이어
|
||||||
|
# 둘이라 형식마다 갈래 · 고르기는 B08 칸(제안 무한궤도 — 영월 「06M3 B/H」 · 브레인 ②).
|
||||||
|
"form_split": {"굴착기": ("무한궤도", "타이어")},
|
||||||
"why": "원문 L5430 「2. 성토면 고르기 (단위: 10㎡당)」 — 시공 칸 병합으로 둘째 줄 앞당김",
|
"why": "원문 L5430 「2. 성토면 고르기 (단위: 10㎡당)」 — 시공 칸 병합으로 둘째 줄 앞당김",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -160,12 +163,16 @@ def _merged_first(code, table, judged, rows, catalog, basis, unit) -> list | str
|
|||||||
variant = " · ".join(
|
variant = " · ".join(
|
||||||
[judged["prefix"], short, *([soil] if methods.count(method) > 1 else [])]
|
[judged["prefix"], short, *([soil] if methods.count(method) > 1 else [])]
|
||||||
)
|
)
|
||||||
entry = _entry(catalog, name, code, (size,) if size else ())
|
side = (size,) if size else ()
|
||||||
if entry is None:
|
forms = judged.get("form_split", {}).get(name)
|
||||||
# 그 갈래만 안 섬(자원이 하나뿐) — 사유는 못 맞춘 줄로(Ⓑ 굴착기 0.6㎥ 형식 둘).
|
for form in forms or (None,):
|
||||||
label = f"{name} {size}".strip()
|
entry = _entry(catalog, f"{name}({form})" if form else name, code, side)
|
||||||
reason = unmatched_reason(catalog, name)
|
if entry is None:
|
||||||
staged.append(UnmatchedRow(code, str(table.get("pum_table_id", "")), label, reason))
|
# 그 갈래만 안 섬(자원이 하나뿐) — 사유는 못 맞춘 줄로.
|
||||||
continue
|
label = f"{name} {size}".strip()
|
||||||
staged.append(_row(code, table, entry, amount / basis, unit, index, variant))
|
reason = unmatched_reason(catalog, name)
|
||||||
|
staged.append(UnmatchedRow(code, str(table.get("pum_table_id", "")), label, reason))
|
||||||
|
continue
|
||||||
|
named = f"{variant} · {form}" if form else variant
|
||||||
|
staged.append(_row(code, table, entry, amount / basis, unit, index, named))
|
||||||
return staged
|
return staged
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"schema_version": "1.0",
|
"schema_version": "1.0",
|
||||||
"dataset_id": "data_work_item_master_manifest",
|
"dataset_id": "data_work_item_master_manifest",
|
||||||
"generated_at": "2026-09-14T14:28:19+09:00",
|
"generated_at": "2026-09-14T16:09:37+09:00",
|
||||||
"built_by": "B08_Quantity/B08_Quantity_Build_WorkItemMaster.py",
|
"built_by": "B08_Quantity/B08_Quantity_Build_WorkItemMaster.py",
|
||||||
"source": {
|
"source": {
|
||||||
"dataset_id": "pum_forest",
|
"dataset_id": "pum_forest",
|
||||||
@@ -12,8 +12,8 @@
|
|||||||
"files": [
|
"files": [
|
||||||
{
|
{
|
||||||
"file": "work_item_master_2026-01-01.json",
|
"file": "work_item_master_2026-01-01.json",
|
||||||
"sha256": "f3e4367188b209bcb064f99825e55ecf5b9c7caafb6d6a37c7d6b179cd7fb91c",
|
"sha256": "a942f7ad412441ba0c7ad3f8149fd143b10502dae5d20053e5d98029ab31c6b4",
|
||||||
"size_bytes": 838069
|
"size_bytes": 838243
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"file": "form_undetermined_2026-01-01.json",
|
"file": "form_undetermined_2026-01-01.json",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"dataset_id": "work_item_master_forest",
|
"dataset_id": "work_item_master_forest",
|
||||||
"effective_date": "2026-01-01",
|
"effective_date": "2026-01-01",
|
||||||
"pum_edition": "2026-01-01",
|
"pum_edition": "2026-01-01",
|
||||||
"generated_at": "2026-09-14T14:28:19+09:00",
|
"generated_at": "2026-09-14T16:09:37+09:00",
|
||||||
"dataset_version": {
|
"dataset_version": {
|
||||||
"dataset_id": "pum_forest",
|
"dataset_id": "pum_forest",
|
||||||
"effective_date": "2026-01-01",
|
"effective_date": "2026-01-01",
|
||||||
@@ -28434,7 +28434,9 @@
|
|||||||
"capacity_formula_here": false,
|
"capacity_formula_here": false,
|
||||||
"variant_key": [
|
"variant_key": [
|
||||||
"성토면 · 인력 · 점토 또는 점질토",
|
"성토면 · 인력 · 점토 또는 점질토",
|
||||||
"성토면 · 인력 · 모래 또는 사질토"
|
"성토면 · 인력 · 모래 또는 사질토",
|
||||||
|
"성토면 · 기계 · 무한궤도",
|
||||||
|
"성토면 · 기계 · 타이어"
|
||||||
],
|
],
|
||||||
"condition_note": [
|
"condition_note": [
|
||||||
"시 공",
|
"시 공",
|
||||||
@@ -28480,7 +28482,9 @@
|
|||||||
"절토면 · 연암",
|
"절토면 · 연암",
|
||||||
"절토면 · 보통암ㆍ경암",
|
"절토면 · 보통암ㆍ경암",
|
||||||
"성토면 · 인력 · 점토 또는 점질토",
|
"성토면 · 인력 · 점토 또는 점질토",
|
||||||
"성토면 · 인력 · 모래 또는 사질토"
|
"성토면 · 인력 · 모래 또는 사질토",
|
||||||
|
"성토면 · 기계 · 무한궤도",
|
||||||
|
"성토면 · 기계 · 타이어"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -174,7 +174,7 @@ def test_토질_시공_칸이_비면_금액_없이_입력_사유() -> None:
|
|||||||
|
|
||||||
|
|
||||||
def test_고르는_갈래는_원문_표_두_벌과_마스터_갈래_키가_같음() -> None:
|
def test_고르는_갈래는_원문_표_두_벌과_마스터_갈래_키가_같음() -> None:
|
||||||
"""B08 칸의 선택지와 B09 표 읽기가 세운 갈래 — 두 벌이 갈리면 빨강(성토면·기계는 Ⓓ 로 안 섬)."""
|
"""B08 칸의 선택지와 B09 표 읽기가 세운 갈래 — 두 벌이 갈리면 빨강."""
|
||||||
from B09_Estimation.B09_Estimation_ResourceAxis import load_work_item_master
|
from B09_Estimation.B09_Estimation_ResourceAxis import load_work_item_master
|
||||||
from B09_Estimation.B09_Estimation_UnitPrice import normalize_variant_key
|
from B09_Estimation.B09_Estimation_UnitPrice import normalize_variant_key
|
||||||
|
|
||||||
@@ -185,17 +185,17 @@ def test_고르는_갈래는_원문_표_두_벌과_마스터_갈래_키가_같
|
|||||||
choices = {
|
choices = {
|
||||||
normalize_variant_key(k) for k in FACE_DRESSING_CUT_CLASSES + FACE_DRESSING_FILL_CLASSES
|
normalize_variant_key(k) for k in FACE_DRESSING_CUT_CLASSES + FACE_DRESSING_FILL_CLASSES
|
||||||
}
|
}
|
||||||
assert keys == choices - {normalize_variant_key("성토면 · 기계")}
|
assert keys == choices
|
||||||
|
|
||||||
|
|
||||||
# ── 내역 — 갈래가 서면 금액 · 기계는 사유 ─────────────────────────────
|
# ── 내역 — 갈래가 서면 금액 · 기계는 사유 ─────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
def test_내역은_갈래가_서면_금액이_서고_성토면_기계는_규격_미정_사유() -> None:
|
def test_내역은_갈래가_서면_금액이_서고_성토면_기계는_무한궤도_제안() -> None:
|
||||||
"""9-19-2(연암 · 리핑) · 9-19-1(성토면 인력 점토 500㎡ × 3,269원) 금액이 섬.
|
"""9-19-2(연암 · 리핑) · 9-19-1(성토면 인력 점토 500㎡ × 3,269원) 금액이 섬.
|
||||||
ⓘ 9-19-1 은 첫 커밋(ee9368ec) 때 공기압축기 3.5 손료 미확보(공종 단위 「일부만」)에 막혀 있었고
|
ⓘ 9-19-1 은 첫 커밋(ee9368ec) 때 공기압축기 3.5 손료 미확보(공종 단위 「일부만」)에 막혀 있었고
|
||||||
운전경비 표 괄호 딸림값 파서 고침으로 풀림(같은 날 · `test_b09_operating_paren_spec`).
|
운전경비 표 괄호 딸림값 파서 고침으로 풀림(같은 날 · `test_b09_operating_paren_spec`).
|
||||||
성토면·기계는 [주]·규격 미정 사유가 후보 목록 곁에 붙음(Ⓓ)."""
|
성토면·기계는 굴착기 형식 두 갈래 · 제안 무한궤도(영월 실무 「06M3 B/H」 · 브레인 ②)."""
|
||||||
from B09_Estimation.B09_Estimation_BillOfQuantities import build_bill
|
from B09_Estimation.B09_Estimation_BillOfQuantities import build_bill
|
||||||
|
|
||||||
handoff = _handoff(face_dressing_cut_class=CUT_SAND, face_dressing_fill_class=FILL_CLAY)
|
handoff = _handoff(face_dressing_cut_class=CUT_SAND, face_dressing_fill_class=FILL_CLAY)
|
||||||
@@ -204,8 +204,11 @@ def test_내역은_갈래가_서면_금액이_서고_성토면_기계는_규격_
|
|||||||
assert ripping.amount_krw and int(ripping.amount_krw) > 0, ripping.note
|
assert ripping.amount_krw and int(ripping.amount_krw) > 0, ripping.note
|
||||||
fill = next(r for r in rows if r.code == "FP-09-19-01" and r.spec == FILL_CLAY)
|
fill = next(r for r in rows if r.code == "FP-09-19-01" and r.spec == FILL_CLAY)
|
||||||
assert fill.amount_krw == 500 * 3269, fill.note
|
assert fill.amount_krw == 500 * 3269, fill.note
|
||||||
machine = _handoff(face_dressing_cut_class=CUT_SAND, face_dressing_fill_class="성토면 · 기계")
|
track = "성토면 · 기계 · 무한궤도"
|
||||||
row = next(
|
machine = _handoff(face_dressing_cut_class=CUT_SAND, face_dressing_fill_class=track)
|
||||||
r for r in build_bill(machine).rows if r.code == "FP-09-19-01" and r.spec == "성토면"
|
row = next(r for r in build_bill(machine).rows if r.code == "FP-09-19-01" and r.spec == track)
|
||||||
)
|
assert row.amount_krw and int(row.amount_krw) > 0, row.note
|
||||||
assert row.amount_krw is None and "규격 미정" in row.note, row.note
|
from B08_Quantity.B08_Quantity_Engine_EarthworkSummary import FACE_DRESSING_FILL_SUGGESTED
|
||||||
|
|
||||||
|
value, basis = FACE_DRESSING_FILL_SUGGESTED
|
||||||
|
assert value == track and "06M3 B/H" in basis and "타이어" in basis
|
||||||
|
|||||||
@@ -106,15 +106,17 @@ def test_토사면_고르기_절토면은_토질_여섯_갈래로_규격까지_
|
|||||||
assert rock == {("1002", 0.046), ("5205-0035", 0.125), ("5210-0010", 0.245)}, rock
|
assert rock == {("1002", 0.046), ("5205-0035", 0.125), ("5210-0010", 0.245)}, rock
|
||||||
|
|
||||||
|
|
||||||
def test_토사면_고르기_성토면_인력_두_갈래_기계는_규격_미정으로_막힘() -> None:
|
def test_토사면_고르기_성토면_인력_두_갈래_기계는_형식_두_갈래() -> None:
|
||||||
"""Ⓒ 둘째 줄이 병합 칸 탓에 한 칸 밀려 옴 — 사람 판정표로만 읽음(일반 보정 금지 · 목재틀흙막이
|
"""Ⓒ 둘째 줄이 병합 칸 탓에 한 칸 밀려 옴 — 사람 판정표로만 읽음(일반 보정 금지 · 목재틀흙막이
|
||||||
503만원 전례). Ⓑ 굴착기 0.6㎥ 는 무한궤도·타이어 둘이라 안 고름 — 그 갈래는 안 섬."""
|
503만원 전례). 굴착기 0.6㎥ 는 원문이 형식을 안 적음 — 처음(Ⓑ)엔 근거가 없어 안 세웠고,
|
||||||
|
2026-09-14 브레인 ② 로 무한궤도·타이어 두 갈래를 세움(고르기는 B08 칸 · 제안 무한궤도)."""
|
||||||
build = cached_build()
|
build = cached_build()
|
||||||
assert _per_m2(FILL + "인력·점토또는점질토") == {"1002": 0.019}
|
assert _per_m2(FILL + "인력·점토또는점질토") == {"1002": 0.019}
|
||||||
assert _per_m2(FILL + "인력·모래또는사질토") == {"1002": 0.017}
|
assert _per_m2(FILL + "인력·모래또는사질토") == {"1002": 0.017}
|
||||||
assert not [code for code in build.book.titles if code.startswith(FILL + "기계")]
|
assert _per_m2(FILL + "기계·무한궤도") == {"X-0201-0060": 0.009}
|
||||||
|
assert _per_m2(FILL + "기계·타이어") == {"X-0211-0060": 0.009}
|
||||||
left = build.unattached.get("FP-09-19-01", [])
|
left = build.unattached.get("FP-09-19-01", [])
|
||||||
assert any("굴착기" in label for label in left), left
|
assert not any("굴착기" in label for label in left), left
|
||||||
|
|
||||||
|
|
||||||
def test_범위_별칭은_칸이_규격을_적었으면_덮지_않음() -> None:
|
def test_범위_별칭은_칸이_규격을_적었으면_덮지_않음() -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user