Merge remote-tracking branches 'origin/main_laptop_1' and 'origin/sub_desktop_1' into sub_laptop_1
This commit is contained in:
@@ -403,6 +403,37 @@ def spec(kind: str, columns: list[str]) -> dict[str, Any]:
|
||||
}
|
||||
|
||||
|
||||
#: 갈래 둘 — 기초단가(원가 = 얼마인가) · 품셈 기준(수량 = 얼마나 드나). 화면은 이것으로 상자를 세움.
|
||||
BASE_PRICE_GROUP, PUMSEM_GROUP = "base_price", "pumsem_basis"
|
||||
|
||||
|
||||
def kind_group(kind: str) -> str:
|
||||
return PUMSEM_GROUP if kind in base_tables.SPEC else BASE_PRICE_GROUP
|
||||
|
||||
|
||||
def kind_label(kind: str) -> str:
|
||||
"""kind 한글 이름 — 이름표 `merged_tables` 에서만(코드에 박지 않음 · 없으면 영문 그대로)."""
|
||||
merged = tables.load_labels().get("merged_tables") or []
|
||||
named = next((t for t in merged if t.get("key") in (kind, f"{kind}s")), {})
|
||||
return named.get("name_ko") or kind
|
||||
|
||||
|
||||
def kinds() -> list[dict[str, Any]]:
|
||||
"""화면이 상자를 세울 목록 — **서버가 냄**(화면이 제 코드에 들면 새 kind 가 조용히 안 뜸).
|
||||
|
||||
갈래 · kind · 한글 이름(이름표) · 줄 수.
|
||||
"""
|
||||
return [
|
||||
{
|
||||
"group": kind_group(kind),
|
||||
"kind": kind,
|
||||
"label": kind_label(kind),
|
||||
"rows": len(rows(kind)),
|
||||
}
|
||||
for kind in KINDS
|
||||
]
|
||||
|
||||
|
||||
def row_label(kind: str, row: dict[str, Any] | None) -> str:
|
||||
if row is None:
|
||||
return ""
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
GET /api/master-data/tree 갈래 → 파일 → 표
|
||||
GET /api/master-data/rows?file=&table=&page=&size=&q= 표 줄(쪽 나누기 · 검색)
|
||||
GET /api/master-data/base-prices/{kind}?page=&size=&q=&sort=&desc= 기초단가 한 표(labor|machine|material|oil|rate)
|
||||
GET /api/master-data/base-prices 표 목록(갈래·kind·이름·줄 수)
|
||||
GET /api/master-data/base-prices/{kind}?page=&size=&q=&sort=&desc= 한 표(기초단가 다섯 · 품셈 기준 열)
|
||||
PUT /api/master-data/base-prices/{kind}/{row_id} {values:{열:값}} → 덮개에만 씀 · null = 되돌리기
|
||||
GET /api/master-data/overrides 고친 것·원본 바뀐 것·주인 없는 것(서버 정렬)
|
||||
⚠ 권한은 등록하는 쪽(`main.py` · 랩탑 서브)이 `dependencies=[verify_session, require_system_admin]` 로 붙임.
|
||||
@@ -48,6 +49,12 @@ def _kind(kind: str) -> str:
|
||||
return kind
|
||||
|
||||
|
||||
@router.get("/base-prices")
|
||||
def get_base_price_kinds() -> dict:
|
||||
"""어떤 표가 있나 — 갈래·kind·한글 이름·줄 수. 화면이 목록을 제 코드에 들지 않게 서버가 냄."""
|
||||
return {"kinds": base_prices.kinds()}
|
||||
|
||||
|
||||
@router.get("/base-prices/{kind}")
|
||||
def get_base_prices(
|
||||
kind: str,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"schema_version": "1.0",
|
||||
"dataset_id": "data_master_labels",
|
||||
"effective_date": "2026-01-01",
|
||||
"generated_at": "2026-09-16T13:11:45+09:00",
|
||||
"generated_at": "2026-09-16T15:44:59+09:00",
|
||||
"note": "마스터 자료의 **사람이 읽을 이름표**. 값이 아니라 이름만 담는다 — 여기를 고쳐도 계산은 안 바뀐다. Z01 마스터 관리 화면이 표·열 이름을 여기서 읽는다.",
|
||||
"policy": {
|
||||
"labels_only": "값·수식·단가를 담지 않는다. 마스터 파일은 손대지 않는다.",
|
||||
@@ -1197,6 +1197,153 @@
|
||||
"note": "형식마다 정해진 값. 코드 기본 0.3 이 여기서 왔다."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "machine_productivity",
|
||||
"name_ko": "기계 시공능력",
|
||||
"summary": "불도저 속도·삽날 용량과 덤프 운반 계수. 코드에 박혀 있던 표를 꺼낸 벌이다.",
|
||||
"sources": [
|
||||
"B09_Estimation_MachineProductivity_Dozer.py (건설품셈 8-2-1)",
|
||||
"B09_Estimation_MachineProductivity_Dump.py (산림품셈 10-12)"
|
||||
],
|
||||
"rows_note": "불도저 속도 · 삽날 용량 · 덤프 운반 낱값 · 재료별 계수 = 47 줄",
|
||||
"columns": [
|
||||
{
|
||||
"key": "table",
|
||||
"name_ko": "어느 표",
|
||||
"unit": "",
|
||||
"visible": true,
|
||||
"note": "`dozer_speed`(불도저 속도) · `dozer_blade`(삽날 용량) · `dump_haul`(덤프 운반 낱값) · `dump_material`(재료별 계수). ⚠ 줄을 가리는 축 — 못 고침. 바꾸면 고친 값이 주인을 잃는다."
|
||||
},
|
||||
{
|
||||
"key": "track",
|
||||
"name_ko": "주행 장치",
|
||||
"unit": "",
|
||||
"visible": true,
|
||||
"note": "무한궤도 · 타이어. ⚠ 줄을 가리는 축 — 못 고침. 바꾸면 고친 값이 주인을 잃는다."
|
||||
},
|
||||
{
|
||||
"key": "tonnage_ton",
|
||||
"name_ko": "기계 규격",
|
||||
"unit": "ton",
|
||||
"visible": true,
|
||||
"note": "⚠ 줄을 가리는 축 — 못 고침. 바꾸면 고친 값이 주인을 잃는다."
|
||||
},
|
||||
{
|
||||
"key": "gear",
|
||||
"name_ko": "변속 단",
|
||||
"unit": "",
|
||||
"visible": true,
|
||||
"note": "1단·2단… 속도가 단마다 다르다. ⚠ 줄을 가리는 축 — 못 고침. 바꾸면 고친 값이 주인을 잃는다."
|
||||
},
|
||||
{
|
||||
"key": "forward_m_per_min",
|
||||
"name_ko": "전진 속도",
|
||||
"unit": "m/분",
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"key": "reverse_m_per_min",
|
||||
"name_ko": "후진 속도",
|
||||
"unit": "m/분",
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"key": "blade_m3",
|
||||
"name_ko": "삽날 용량",
|
||||
"unit": "㎥",
|
||||
"visible": true,
|
||||
"note": "1회에 밀 수 있는 흐트러진 상태 부피."
|
||||
},
|
||||
{
|
||||
"key": "key",
|
||||
"name_ko": "낱값 키",
|
||||
"unit": "",
|
||||
"visible": true,
|
||||
"note": "`truck_ton` 처럼 한 값짜리를 가리키는 이름. ⚠ 줄을 가리는 축 — 못 고침. 바꾸면 고친 값이 주인을 잃는다."
|
||||
},
|
||||
{
|
||||
"key": "value",
|
||||
"name_ko": "값",
|
||||
"unit": "",
|
||||
"visible": true,
|
||||
"note": "낱값 표에서만 선다 — 단위는 곁 칸이 말한다."
|
||||
},
|
||||
{
|
||||
"key": "unit",
|
||||
"name_ko": "단위",
|
||||
"unit": "",
|
||||
"visible": true,
|
||||
"note": "위 낱값의 단위."
|
||||
},
|
||||
{
|
||||
"key": "meaning",
|
||||
"name_ko": "무엇인가",
|
||||
"unit": "",
|
||||
"visible": true,
|
||||
"note": "낱값이 품셈 식에서 어느 기호인지 풀어 적은 자리."
|
||||
},
|
||||
{
|
||||
"key": "work_item_code",
|
||||
"name_ko": "공종코드",
|
||||
"unit": "",
|
||||
"visible": true,
|
||||
"note": "덤프 운반 재료 갈래(토사·암절취·발파암). ⚠ 줄을 가리는 축 — 못 고침. 바꾸면 고친 값이 주인을 잃는다."
|
||||
},
|
||||
{
|
||||
"key": "label",
|
||||
"name_ko": "재료 이름",
|
||||
"unit": "",
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"key": "unit_weight_ton_per_m3",
|
||||
"name_ko": "단위중량",
|
||||
"unit": "ton/㎥",
|
||||
"visible": true,
|
||||
"note": "자연 상태 토석의 습윤밀도 γt — 적재량을 무게로 묶는 값."
|
||||
},
|
||||
{
|
||||
"key": "loose_factor",
|
||||
"name_ko": "체적변화율",
|
||||
"unit": "L",
|
||||
"visible": true,
|
||||
"note": "흐트러진 상태 ÷ 자연 상태. 토량환산계수 표와 같은 축의 값이다."
|
||||
},
|
||||
{
|
||||
"key": "bucket_factor",
|
||||
"name_ko": "버킷계수",
|
||||
"unit": "K",
|
||||
"visible": true,
|
||||
"note": "적재 장비가 한 번에 뜨는 몫."
|
||||
},
|
||||
{
|
||||
"key": "loader_efficiency",
|
||||
"name_ko": "적재 작업효율",
|
||||
"unit": "E0",
|
||||
"visible": true,
|
||||
"note": "⚠ 운반 식의 `Es` 와 **다른 기호**다 — 섞으면 작업량이 갈린다."
|
||||
},
|
||||
{
|
||||
"key": "truck_efficiency",
|
||||
"name_ko": "운반 작업효율",
|
||||
"unit": "Es",
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"key": "loading_efficiency",
|
||||
"name_ko": "적재 효율",
|
||||
"unit": "E",
|
||||
"visible": true
|
||||
},
|
||||
{
|
||||
"key": "loading_note",
|
||||
"name_ko": "적재 비고",
|
||||
"unit": "",
|
||||
"visible": true,
|
||||
"note": "원문이 값을 안 준 자리와 우리가 고른 까닭."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"machine_cost_chain": {
|
||||
@@ -1517,6 +1664,20 @@
|
||||
"type_id"
|
||||
],
|
||||
"checked": "id 칸이 없다. 뒷길이·돌 직경 갈래는 `table+class`, 형식별 기울기는 `table+type_id`. ⚠ 돌 직경 갈래 이름은 원문 표기 `40~60` 꼴 그대로다."
|
||||
},
|
||||
{
|
||||
"table": "machine_productivity",
|
||||
"name_ko": "기계 시공능력",
|
||||
"verdict": "없음 — 축으로만 가려짐",
|
||||
"key": [
|
||||
"table",
|
||||
"track",
|
||||
"tonnage_ton",
|
||||
"gear",
|
||||
"key",
|
||||
"work_item_code"
|
||||
],
|
||||
"checked": "id 칸이 없다. **표마다 축이 다르다** — 속도표는 `table+track+tonnage_ton+gear`, 삽날표는 `table+track+tonnage_ton`, 낱값은 `table+key`, 재료별 계수는 `table+work_item_code`. 서버가 줄마다 `@axis` 로 무엇이 축인지 알려 준다."
|
||||
}
|
||||
],
|
||||
"null_is_real": {
|
||||
@@ -1610,8 +1771,8 @@
|
||||
}
|
||||
},
|
||||
"counts": {
|
||||
"merged_tables": 14,
|
||||
"merged_columns": 136,
|
||||
"merged_tables": 15,
|
||||
"merged_columns": 156,
|
||||
"files": 34,
|
||||
"tables": 91,
|
||||
"columns": 462,
|
||||
|
||||
@@ -452,6 +452,7 @@ def test_미결은_미판정으로_표시된다(labels):
|
||||
|
||||
|
||||
#: 2026-09-16 사용자 판정 — 기초데이터 아홉이 더해져 열넷.
|
||||
#: 곧이어 코드에 박혀 있던 기계 시공능력을 꺼내 열다섯(브레인 b6fb3725).
|
||||
#: 잣대 = 계산되는 값이 아니라 **그냥 값만으로 정의되는 데이터셋**.
|
||||
MERGED_KEYS = (
|
||||
"labor",
|
||||
@@ -468,6 +469,7 @@ MERGED_KEYS = (
|
||||
"masonry_back_length",
|
||||
"stone_kind",
|
||||
"masonry_class",
|
||||
"machine_productivity",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -271,3 +271,34 @@ def test_같은_기울기가_두_자리인_까닭을_표가_말함(client: TestC
|
||||
assert any("축과 근거가 다름" in line for line in notice)
|
||||
slope = _get(client, "masonry_slope", size=1)["notice"]
|
||||
assert any("횡단 도면" in line for line in slope) # 표준경사 쪽 알림은 그대로
|
||||
|
||||
|
||||
def test_kind_목록을_서버가_줌_새_kind_도_저절로(
|
||||
client: TestClient, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
"""화면이 kind 목록을 제 코드에 들면 **새 kind 가 조용히 안 뜸**(기계 작업량이 그랬음 · 브레인).
|
||||
|
||||
⇒ 갈래·kind·한글 이름·줄 수를 서버가 냄. 화면은 상자만 세운다.
|
||||
"""
|
||||
listed = client.get("/api/master-data/base-prices")
|
||||
assert listed.status_code == 200, listed.text
|
||||
items = listed.json()["kinds"]
|
||||
assert [i["kind"] for i in items] == list(base_prices.KINDS)
|
||||
groups = {i["kind"]: i["group"] for i in items}
|
||||
assert groups["labor"] == "base_price" and groups["machine_productivity"] == "pumsem_basis"
|
||||
assert {i["group"] for i in items} == {"base_price", "pumsem_basis"}
|
||||
labor = next(i for i in items if i["kind"] == "labor")
|
||||
assert labor["label"] == "노임" and labor["rows"] == 261 # 이름은 이름표에서
|
||||
assert next(i for i in items if i["kind"] == "machine_productivity")["rows"] == 47
|
||||
|
||||
from Z01_MasterData import Z01_MasterData_BasePrices_Tables as base_tables
|
||||
|
||||
fake = dict(base_tables.SPEC)
|
||||
fake["coef_fake"] = dict(base_tables.SPEC["coef"])
|
||||
monkeypatch.setattr(base_tables, "SPEC", fake)
|
||||
monkeypatch.setattr(base_prices, "KINDS", (*base_prices.KINDS, "coef_fake"))
|
||||
grown = client.get("/api/master-data/base-prices").json()["kinds"]
|
||||
assert [i["kind"] for i in grown][-1] == "coef_fake" # 더하면 목록이 저절로 늚
|
||||
assert (
|
||||
next(i for i in grown if i["kind"] == "coef_fake")["label"] == "coef_fake"
|
||||
) # 이름표 전엔 영문
|
||||
|
||||
Reference in New Issue
Block a user