main_laptop_1 -> main byeonghap (4 hwangyeong 585 commits) #12
@@ -314,6 +314,38 @@ SHARE_TAG_RE = re.compile(
|
||||
CAPACITY_SYMBOLS = {"K", "k", "f", "E", "Cm", "㎝(sec)", "q", "qo", "Q"}
|
||||
|
||||
|
||||
# ⚠ **값 자리에 숫자가 아니라 식이 적힌 칸** (2026-09-07 서브 창 제보로 확인).
|
||||
# `0.2 × 30%`(기초잡석 소할) · `(0.2+0.26)/2` · `1/1.3` 처럼 계산이 그대로 적혀 있다.
|
||||
# 형태 판정은 통과하는데 **값이 숫자로 안 읽혀 그 성분이 조용히 빠진다** —
|
||||
# `partial_ratio` 와 같은 병인데 배분율 딱지가 없어 아무 검사에도 안 걸렸다.
|
||||
# ⚠ 여기서 **식을 계산하지 않는다.** 뜻을 잘못 읽으면 조용히 틀리므로 **드러내기만** 한다.
|
||||
_PLAIN_NUMBER_RE = re.compile(r"^[\d,]+(?:\.\d+)?$")
|
||||
_CALC_CELL_RE = re.compile(r"^[\d,.\s()×xX*/÷+\-%]+$")
|
||||
_CALC_MAX_LEN = 22
|
||||
|
||||
|
||||
def expression_cells(table: dict[str, Any]) -> list[str]:
|
||||
"""값 자리에 식이 적힌 칸 목록. 없으면 빈 목록."""
|
||||
found: list[str] = []
|
||||
for row in table.get("rows", []):
|
||||
for cell in row:
|
||||
text = norm(cell)
|
||||
if not text or len(text) > _CALC_MAX_LEN or _PLAIN_NUMBER_RE.match(text):
|
||||
continue
|
||||
if (
|
||||
_CALC_CELL_RE.match(text)
|
||||
and re.search(r"\d", text)
|
||||
and re.search(r"[×xX*/÷+]", text)
|
||||
):
|
||||
found.append(text)
|
||||
# 순서를 지키되 중복은 지운다 — 같은 식이 여러 줄에 반복된다.
|
||||
seen: list[str] = []
|
||||
for item in found:
|
||||
if item not in seen:
|
||||
seen.append(item)
|
||||
return seen
|
||||
|
||||
|
||||
def resource_shares(table: dict[str, Any]) -> dict[str, float]:
|
||||
"""`{인력: 10.0, 장비: 90.0}` — 딱지에 붙은 몫. 없으면 빈 칸."""
|
||||
shares: dict[str, float] = {}
|
||||
@@ -421,6 +453,8 @@ def build() -> dict[str, Any]:
|
||||
"basis_source": basis_source,
|
||||
"resource_shares": shares,
|
||||
"partial_ratio": partial,
|
||||
# ⚠ 값 자리에 식이 적힌 칸 — 그 성분은 숫자로 안 읽히므로 **금액을 만들면 안 된다**.
|
||||
"expression_cells": expression_cells(table),
|
||||
# 공식 기호가 이 표에 직접 있는가 — 없으면 앞 표에서 물려받는 모양이다.
|
||||
"capacity_formula_here": capacity_formula_pending(table),
|
||||
"variant_key": variant_axis(table),
|
||||
|
||||
@@ -31,6 +31,7 @@ from B08_Quantity.B08_Quantity_Engine_EarthworkSummary import SummaryInput
|
||||
from B08_Quantity.B08_Quantity_Engine_EarthworkSummary import build_table as build_summary_table
|
||||
from B08_Quantity.B08_Quantity_Engine_EarthworkTable import StationArea, build_table
|
||||
from B08_Quantity.B08_Quantity_Engine_HaulSummary import build_table as build_haul_table
|
||||
from B08_Quantity.B08_Quantity_Engine_Handoff import load_mapping
|
||||
from B08_Quantity.B08_Quantity_Engine_Preparation import build_table as build_preparation_table
|
||||
from B08_Quantity.B08_Quantity_Engine_HaulSummary import summary_input_rows
|
||||
from B08_Quantity.B08_Quantity_Engine_SlopeArea import build_table as build_slope_table
|
||||
@@ -103,7 +104,14 @@ async def get_earthwork_table(project_id: UUID, route_id: int) -> JSONResponse:
|
||||
)
|
||||
method, method_is_default = concrete_placing_method(settings)
|
||||
# ⚠ 금액에 바로 걸리는 값이라 「기본값으로 돌고 있음」을 응답에 실어 화면이 띄우게 한다.
|
||||
table["concrete_placing"] = {"method": method, "is_default": method_is_default}
|
||||
table["concrete_placing"] = {
|
||||
"method": method,
|
||||
"is_default": method_is_default,
|
||||
# ⚠ **표시 전용 참고값** — 「무엇을 정해야 하는지」만으로는 부족하고
|
||||
# 「정하면 얼마나 달라지는지」가 보여야 사용자가 판단한다(2026-09-07 조율 창).
|
||||
# B08 의 어떤 계산에도 안 들어간다.
|
||||
"price_hint": (load_mapping().concrete_placing or {}).get("price_hint_krw_per_m3"),
|
||||
}
|
||||
table["settings"] = settings
|
||||
table["project_root_known"] = project_root is not None
|
||||
table["route_id"] = route_id
|
||||
|
||||
@@ -134,6 +134,13 @@ function numberField(label: string, value: number, onInput: (value: number) => v
|
||||
return row;
|
||||
}
|
||||
|
||||
/** 타설 방식 표기 — 코드가 아니라 사람이 읽는 이름으로 보인다. */
|
||||
const PLACING_LABELS: Record<string, string> = {
|
||||
ready_mixed: "레디믹스트",
|
||||
machine_mixed: "기계비빔",
|
||||
hand_mixed: "인력비빔",
|
||||
};
|
||||
|
||||
/** 고르는 칸. 첫 보기는 **「안 정함」**이고 그것이 기본이다 — 찍으면 값이 조용히 틀린다. */
|
||||
function selectField(
|
||||
label: string,
|
||||
@@ -274,8 +281,37 @@ function buildQuantitySidePanel(
|
||||
),
|
||||
);
|
||||
const placing = (
|
||||
table as unknown as { concrete_placing?: { method: string; is_default: boolean } }
|
||||
table as unknown as {
|
||||
concrete_placing?: {
|
||||
method: string;
|
||||
is_default: boolean;
|
||||
price_hint?: { basis?: string; values?: Record<string, number> };
|
||||
};
|
||||
}
|
||||
).concrete_placing;
|
||||
if (placing) {
|
||||
// ⚠ 방식 이름을 **늘** 값 옆에 보인다 — 코드(`12-01-01`)만으로는 무엇을 쓰는지 모른다.
|
||||
const label = PLACING_LABELS[placing.method] ?? placing.method;
|
||||
panel.append(
|
||||
field(
|
||||
L("B08_Quantity_Placing_Current"),
|
||||
placing.is_default ? `${label} (${L("B08_Quantity_Placing_Default_Tag")})` : label,
|
||||
),
|
||||
);
|
||||
}
|
||||
// ⚠ 「정하면 얼마나 달라지는지」까지 보여야 사용자가 판단한다. 이 값은 **참고 표시 전용**이고
|
||||
// B08 의 어떤 계산에도 안 들어간다(금액은 B09 몫).
|
||||
const hint = placing?.price_hint;
|
||||
if (hint?.values) {
|
||||
const line = document.createElement("p");
|
||||
line.className = "b08-quantity__notice";
|
||||
const parts = Object.entries(hint.values).map(
|
||||
([key, value]) =>
|
||||
`${PLACING_LABELS[key] ?? key} ${Math.round(value).toLocaleString("ko-KR")}원`,
|
||||
);
|
||||
line.textContent = `${L("B08_Quantity_Placing_Hint")} ${hint.basis ?? ""} — ${parts.join(" · ")}`;
|
||||
panel.append(line);
|
||||
}
|
||||
if (placing?.is_default) {
|
||||
// 「확인 필요」만 있으면 무엇을 정해야 하는지 모른다 — **지금 무엇으로 돌고 있는지**를 함께 적는다.
|
||||
const notice = document.createElement("p");
|
||||
|
||||
@@ -142,19 +142,12 @@
|
||||
"items": [
|
||||
{
|
||||
"group": "지장목제거",
|
||||
"candidates": [
|
||||
"FP-04-01 수확베기",
|
||||
"FP-04-02 단목베기",
|
||||
"FP-04-03 위험목 베기"
|
||||
],
|
||||
"candidates": ["FP-04-01 수확베기", "FP-04-02 단목베기", "FP-04-03 위험목 베기"],
|
||||
"why": "품셈 4장은 벌목을 목적별로 가르는데 임도 지장목이 어느 쪽인지 원본이 말하지 않음"
|
||||
},
|
||||
{
|
||||
"group": "흙깎기/측구터파기 암",
|
||||
"candidates": [
|
||||
"FP-09-04 암절취(리핑)",
|
||||
"FP-09-05 발파암"
|
||||
],
|
||||
"candidates": ["FP-09-04 암절취(리핑)", "FP-09-05 발파암"],
|
||||
"why": "설계자가 넣는 암 갈래 이름(풍화암·연암·보통암·경암)이 리핑이냐 발파냐를 말하지 않음. 갈래마다 시공법을 지정하는 칸이 필요함"
|
||||
}
|
||||
]
|
||||
@@ -173,10 +166,7 @@
|
||||
"why": "품셈 12장에 「옹벽」 공종이 없음. 실무 내역은 「반중력식옹벽 H=2.0」 한 줄이고 그 일위대가가 위 공종을 묶음.",
|
||||
"needs": "일위대가 조립은 B09 몫 — B08 은 물량과 묶음만 넘김",
|
||||
"placing_note": "타설 코드는 프로젝트 설정의 타설 방식으로 갈림(기본 레디믹스트). 철근구조물 판정은 원단위의 D13·D16 에서 자동으로 나옴.",
|
||||
"not_ready": [
|
||||
"FP-12-03",
|
||||
"FP-12-25"
|
||||
],
|
||||
"not_ready": ["FP-12-03", "FP-12-25"],
|
||||
"not_ready_why": "B09 일위대가가 아직 안 섬 — 지금 세우면 절반짜리가 됨(2026-09-07 조율 창)"
|
||||
}
|
||||
]
|
||||
@@ -190,11 +180,18 @@
|
||||
},
|
||||
"default_method": "ready_mixed",
|
||||
"default_is_provisional": true,
|
||||
"structure_kinds": [
|
||||
"무근구조물",
|
||||
"철근구조물",
|
||||
"소형구조물"
|
||||
],
|
||||
"kind_rule": "원단위 성분에 철근(이형철근·원형철근)이 있으면 철근구조물, 없으면 무근구조물. 소형구조물 판정 기준은 미확보."
|
||||
"structure_kinds": ["무근구조물", "철근구조물", "소형구조물"],
|
||||
"kind_rule": "원단위 성분에 철근(이형철근·원형철근)이 있으면 철근구조물, 없으면 무근구조물. 소형구조물 판정 기준은 미확보.",
|
||||
"price_hint_krw_per_m3": {
|
||||
"note": "⚠ **표시 전용.** 사용자가 타설 방식을 고를 때 「정하면 얼마나 달라지는지」를 보이려고 둔 값이며 B08 의 어떤 계산에도 들어가지 않는다(금액은 B09 몫 — 8-2 경계). 값은 B09 가 2026-09-07 에 낸 철근구조물 기준 단가이고, 요율·노임이 바뀌면 어긋난다 — 화면이 「참고」임을 함께 적는다.",
|
||||
"basis": "철근구조물 콘크리트 타설 (원/㎥)",
|
||||
"computed_by": "B09",
|
||||
"computed_on": "2026-09-07",
|
||||
"values": {
|
||||
"ready_mixed": 65826,
|
||||
"machine_mixed": 163508,
|
||||
"hand_mixed": 408327
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"schema_version": "1.0",
|
||||
"dataset_id": "data_work_item_master_manifest",
|
||||
"generated_at": "2026-09-08T01:00:54+09:00",
|
||||
"generated_at": "2026-09-08T01:18:06+09:00",
|
||||
"built_by": "B08_Quantity/B08_Quantity_Build_WorkItemMaster.py",
|
||||
"source": {
|
||||
"dataset_id": "pum_forest",
|
||||
@@ -12,8 +12,8 @@
|
||||
"files": [
|
||||
{
|
||||
"file": "work_item_master_2026-01-01.json",
|
||||
"sha256": "fe454c56c9dc01ad7dae04a8f90d776d08c5ce33badeadb2606b35234bfce7eb",
|
||||
"size_bytes": 785034
|
||||
"sha256": "086441e1380441259e860620d1e81b886019f11838dea601c0a3797f6f1a3d3e",
|
||||
"size_bytes": 800211
|
||||
},
|
||||
{
|
||||
"file": "form_undetermined_2026-01-01.json",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -631,6 +631,9 @@ export const ui_locales_b2 = {
|
||||
B08_Quantity_Side_Placing: ["콘크리트 타설", "Concrete Placing"],
|
||||
B08_Quantity_Side_Placing_Label: ["타설 방식", "Method"],
|
||||
B08_Quantity_Placing_Unset: ["안 정함(기본값 사용)", "Not set (default)"],
|
||||
B08_Quantity_Placing_Current: ["적용 중", "In use"],
|
||||
B08_Quantity_Placing_Default_Tag: ["기본값 — 확인 필요", "default — needs review"],
|
||||
B08_Quantity_Placing_Hint: ["참고 단가(B09 산출):", "Reference unit price (from B09):"],
|
||||
B08_Quantity_Placing_Ready: ["레디믹스트", "Ready-mixed"],
|
||||
B08_Quantity_Placing_Machine: ["기계비빔", "Machine-mixed"],
|
||||
B08_Quantity_Placing_Hand: ["인력비빔", "Hand-mixed"],
|
||||
|
||||
Reference in New Issue
Block a user