fix(paths): 옛 자료 폴더 이동 뒤 서버 불러오기 복구 - 경로 상수만 old/ref 새 자리로

B05·B06·B08·B09·Z01·common_util 의 data_* 폴더·파일 이름 상수를 master_data/old · ref 의 새 이름으로 돌림. 로직은 그대로.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016WRFJmmhPi4exAzHgPZHMT
This commit is contained in:
2026-09-19 19:09:02 +09:00
co-authored by Claude Sonnet 5
parent cbb60ecb6e
commit ebd148c4e6
37 changed files with 109 additions and 84 deletions
+2 -2
View File
@@ -24,8 +24,8 @@ from typing import Any
from B09_Estimation.B09_Estimation_PriceBook import PriceKind
from B09_Estimation.B09_Estimation_ResourceAxis_UnitBasis import similar_occupation_note
_MANIFEST = ("resources", "data_cost_input_value", "_manifest.json")
_MASTER_DIR = ("resources", "data_work_item_master")
_MANIFEST = ("resources", "master_data", "old", "_manifest.json")
_MASTER_DIR = ("resources", "master_data", "old")
SHEET_NOTE = (
"산출기초는 새로 세는 장이 아니라 **줄에 달린 근거를 한 장으로 모은 것**입니다 —"
@@ -131,6 +131,6 @@ class CostInput:
enabled_items: tuple[str, ...] | str = DEFAULT_ITEMS
#: 요율 데이터 파일명. **연도를 갈아끼우는 자리.**
rate_file_name: str = "rates_2026.json"
rate_file_name: str = "1_기초_제비율_토목_조달청_2026-04-13.json"
#: 매니페스트 밖 요율 파일(옛 연도 재현 검산 전용). 주면 이쪽이 우선.
rate_file_path: str | None = None
@@ -32,7 +32,7 @@ from decimal import Decimal
from functools import lru_cache
from typing import Any
_DATASET = ("resources", "data_cost_input_value", "coef_2026.json")
_DATASET = ("resources", "master_data", "old", "3_품셈_산림_계수_2026-01-01.json")
_FOREST_SPEC = (
"resources",
"knowledge",
+4 -2
View File
@@ -34,7 +34,7 @@ from B09_Estimation.B09_Estimation_Guards import (
)
from B09_Estimation.B09_Estimation_PriceBook import Money3
_CATALOG_SUBPATH = ("resources", "data_cost_input_value")
_CATALOG_SUBPATH = ("resources", "master_data", "old")
_THOUSAND = Decimal(1000)
@@ -224,7 +224,9 @@ class MachineCatalog:
raise MachineCostError(f"기종 카탈로그에 없는 코드입니다: {machine_code}") from exc
def load_machine_catalog(file_name: str = "mach_base_2026.json") -> MachineCatalog:
def load_machine_catalog(
file_name: str = "3_품셈_건설_기계경비기준_8장_2026-01-01.json",
) -> MachineCatalog:
"""취득가 613건에 손료계수 387건을 붙여 카탈로그 한 벌을 만든다."""
variables = _read_json(file_name)["variables"]
coefficients = {
@@ -33,7 +33,7 @@ from B09_Estimation.B09_Estimation_PriceBook import PriceKind
#: 조합 사용(굴착기+부착장비)일 때의 본체 잡재료비율 — 품셈 제8장 [주]⑤.
COMBINED_MISC_PERCENT = 16
_CATALOG_SUBPATH = ("resources", "data_cost_input_value")
_CATALOG_SUBPATH = ("resources", "master_data", "old")
_THOUSAND = Decimal(1000)
_ZERO = Decimal(0)
@@ -66,7 +66,9 @@ def _project_root() -> str:
@lru_cache(maxsize=1)
def _loss_records() -> dict[str, dict[str, Any]]:
"""기종코드 → 손료계수 원문 줄(상각·정비·관리 계수까지)."""
path = os.path.join(_project_root(), *_CATALOG_SUBPATH, "mach_base_2026.json")
path = os.path.join(
_project_root(), *_CATALOG_SUBPATH, "3_품셈_건설_기계경비기준_8장_2026-01-01.json"
)
with open(path, encoding="utf-8") as handle:
payload = json.load(handle)
records = payload["variables"]["mach_loss_coef"]["records"]
@@ -27,8 +27,8 @@ from dataclasses import dataclass, field
from decimal import Decimal
from typing import Any
_CATALOG_SUBPATH = ("resources", "data_cost_input_value")
_OUTPUT_SUBPATH = ("resources", "data_cost_machine_operating")
_CATALOG_SUBPATH = ("resources", "master_data", "old")
_OUTPUT_SUBPATH = ("resources", "master_data", "old")
#: 품셈 8-4 운전경비 표의 머리글 — 이 여섯이 다 있어야 그 표로 본다.
_REQUIRED_HEADERS = ("분류번호", "기계명", "규격", "주연료", "잡재료", "조종원")
@@ -359,8 +359,8 @@ def enrich_with_catalog(
def load_operating_records(
pum_file: str = "pum_const_2026.json",
labor_file: str = "labor_const_2026-01-01.json",
pum_file: str = "3_품셈_건설_표전체_2026-01-01.json",
labor_file: str = "1_기초_노임_건설업_대한건설협회_2026-01-01.json",
) -> OperatingParseResult:
from B09_Estimation.B09_Estimation_MachineCost import load_machine_catalog
@@ -394,7 +394,7 @@ def write_operating_records(
"""파생 파일로 낸다. **어느 기준자료 판에서 파생됐는지**를 함께 적는다."""
directory = output_dir or os.path.join(_project_root(), *_OUTPUT_SUBPATH)
os.makedirs(directory, exist_ok=True)
path = os.path.join(directory, "machine_operating_2026.json")
path = os.path.join(directory, "3_품셈_건설_기계운전경비_8-4_2026-01-01.json")
payload = {
"schema_version": "1.0",
@@ -419,7 +419,7 @@ def write_operating_records(
#: 시도별 유가 판 — 품셈 8-1-7 5호 「유류가격은 **해당지역의 가격**으로 한다」.
#: ⚠ **파일이 있을 때만 지역을 고를 수 있다** — 없으면 전국평균 한 벌로 돈다(코드로 막지 않음).
REGIONAL_OIL_FILE = "oil_regional_2026-09-09.json"
REGIONAL_OIL_FILE = "1_기초_유가_시도별_오피넷_2026-09-09.json"
#: 운전경비표 연료 종류 → 유가 판 변수. ⚠ 종류를 안 읽고 경유로 때우면 휘발유 기계가 싸게 섬
#: (2026-09-14 661 뒤 ② — 플레이트 콤팩터·진동기·믹서·래머·커터가 경유값이었음).
FUEL_VARIABLES = {"경유": "oil_diesel", "휘발유": "oil_gasoline"}
@@ -455,7 +455,9 @@ def load_regional_fuel_table(
def load_fuel_price(
oil_file: str = "oil_2026-08-14.json", region: str | None = None, kind: str = "경유"
oil_file: str = "1_기초_유가_전국평균_오피넷_2026-08-14.json",
region: str | None = None,
kind: str = "경유",
) -> tuple[Decimal, dict[str, str]]:
"""경유 단가와 그 판의 신원. `region`(시도코드)을 주면 **그 지역 값**으로 선다.
@@ -490,7 +492,9 @@ def load_fuel_price(
}
def load_operator_wages(labor_file: str = "labor_const_2026-01-01.json") -> dict[str, Decimal]:
def load_operator_wages(
labor_file: str = "1_기초_노임_건설업_대한건설협회_2026-01-01.json",
) -> dict[str, Decimal]:
"""직종코드 → 일당."""
records = _read_json(*_CATALOG_SUBPATH, labor_file)["variables"]["labor_rate"]["records"]
return {
@@ -513,7 +517,9 @@ LABOR_RELIABILITY_LABEL = {
}
def load_labor_reliability(labor_file: str = "labor_const_2026-01-01.json") -> dict[str, str]:
def load_labor_reliability(
labor_file: str = "1_기초_노임_건설업_대한건설협회_2026-01-01.json",
) -> dict[str, str]:
"""직종코드 → 신뢰도 기호(`*`·`**`). 정상 공표 직종은 아예 안 담는다."""
records = _read_json(*_CATALOG_SUBPATH, labor_file)["variables"]["labor_rate"]["records"]
return {
@@ -17,8 +17,8 @@ from functools import lru_cache
from pathlib import Path
from typing import Any
DATA_DIR = Path(__file__).resolve().parent.parent / "resources" / "data_machine_productivity"
PREFIX = "machine_productivity_"
DATA_DIR = Path(__file__).resolve().parent.parent / "resources" / "master_data" / "old"
PREFIX = "3_품셈_산림건설_기계작업량_"
class MachineProductivityDataError(FileNotFoundError):
@@ -27,7 +27,7 @@ from dataclasses import dataclass, field
from decimal import Decimal
from typing import Any
_CATALOG_SUBPATH = ("resources", "data_cost_input_value")
_CATALOG_SUBPATH = ("resources", "master_data", "old")
#: 두 창이 맞춘 구분 이름 — 값을 바꾸면 B08 자재총괄과 안 맞는다.
SUPPLY_OWNER = "owner_supplied"
@@ -108,7 +108,7 @@ class MaterialCatalog:
def load_material_catalog(
public_file: str = "mat_price_public_2026-08-14.json",
public_file: str = "1_기초_자재_시설공통_나라장터_2026-08-14.json",
) -> MaterialCatalog:
"""관급 자재를 읽고, 사급은 **없다는 사실을 목록으로** 남긴다."""
payload = _read_json(public_file)
@@ -119,7 +119,7 @@ def material_catalog_rows() -> dict[str, dict[str, str]]:
from B09_Estimation.B09_Estimation_ResourceAxis_Join import EXT_CATALOG_FILE
root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
path = os.path.join(root, "resources", "data_resource_catalog", EXT_CATALOG_FILE)
path = os.path.join(root, "resources", "master_data", "old", EXT_CATALOG_FILE)
if not os.path.isfile(path):
return {}
with open(path, encoding="utf-8") as handle:
+2 -2
View File
@@ -27,7 +27,7 @@ _UNIT_MULTIPLIER: dict[str, int] = {
"billion": 1_000_000_000,
}
_RESOURCE_SUBPATH = ("resources", "data_cost_input_value")
_RESOURCE_SUBPATH = ("resources", "master_data", "old")
# 라벨 문법 — 숫자 구간만 해석한다. 그 밖(`turnkey_or_alternative` 등)은 명시 선택자로 고른다.
_RE_LT = re.compile(r"^lt_(\d+(?:\.\d+)?)_(million|billion)$")
@@ -89,7 +89,7 @@ def _manifest_entry(file_name: str) -> dict[str, Any]:
@lru_cache(maxsize=8)
def load_rate_dataset(file_name: str = "rates_2026.json") -> RateDataset:
def load_rate_dataset(file_name: str = "1_기초_제비율_토목_조달청_2026-04-13.json") -> RateDataset:
"""요율 파일 한 벌을 읽는다. 매니페스트의 지문·적용일을 함께 실어 재현성을 남긴다."""
entry = _manifest_entry(file_name)
with open(os.path.join(_dataset_dir(), file_name), encoding="utf-8") as handle:
@@ -44,8 +44,8 @@ NON_WORK_ITEM_FORMS = frozenset({"coefficient", "reference"})
#: 형태 판정이 안 된 표 — 값을 쓰지 않는다.
UNUSABLE_FORMS = frozenset({"undetermined"})
_MASTER_SUBPATH = ("resources", "data_work_item_master")
_CATALOG_SUBPATH = ("resources", "data_cost_input_value")
_MASTER_SUBPATH = ("resources", "master_data", "old")
_CATALOG_SUBPATH = ("resources", "master_data", "old")
#: 규격이 이름 안에 붙어 있는 흔한 모양 — 「굴착기(0.7㎥)」·「덤프트럭 15톤」.
_RE_SPEC = re.compile(r"[((]([^))]+)[))]|(\d+(?:\.\d+)?\s*(?:톤|ton|㎥|m3|㎡|㎜|mm|HP|kW))")
@@ -22,7 +22,7 @@ from B09_Estimation.B09_Estimation_ResourceAxis import (
#: 자원 축 산출물이 나가는 자리 — **메인의 `data_work_item_master/` 안에 넣지 않는다.**
#: 메인이 품셈을 다시 돌리면 그 폴더가 덮이므로 섞으면 사라진다.
OUTPUT_SUBPATH = ("resources", "data_cost_resource_axis")
OUTPUT_SUBPATH = ("resources", "master_data", "old")
def _master_file_fingerprint(master: dict[str, Any]) -> dict[str, str]:
@@ -36,8 +36,8 @@ from B09_Estimation.B09_Estimation_ResourceAxis_Sources import (
)
from common_util.common_util_aliases import AliasError, in_scope, load_aliases, parse_aliases
_EXT_SUBPATH = ("resources", "data_resource_catalog")
EXT_CATALOG_FILE = "resource_catalog_ext_2026-01-01.json"
_EXT_SUBPATH = ("resources", "master_data", "old")
EXT_CATALOG_FILE = "3_품셈_산림_자원목록보충_2026-01-01.json"
#: 명세 §2 ③ — `AR-<M|L|X>-<8자리 소문자 16진>`. 종류 글자는 PriceKind 글자 그대로.
_RE_AR_CODE = re.compile(r"^AR-([MLX])-[0-9a-f]{8}$")
@@ -29,11 +29,13 @@ from B09_Estimation.B09_Estimation_ResourceAxis import (
_RE_ALTERNATIVE,
)
_CATALOG_SUBPATH = ("resources", "data_cost_input_value")
_MASTER_SUBPATH = ("resources", "data_work_item_master")
_CATALOG_SUBPATH = ("resources", "master_data", "old")
_MASTER_SUBPATH = ("resources", "master_data", "old")
def load_labor_catalog(file_name: str = "labor_const_2026-01-01.json") -> ResourceCatalog:
def load_labor_catalog(
file_name: str = "1_기초_노임_건설업_대한건설협회_2026-01-01.json",
) -> ResourceCatalog:
"""노임 카탈로그 132직종 + `aliases`. 코드는 `occupation_code`."""
payload = _read_json(*_CATALOG_SUBPATH, file_name)
variables = payload["variables"]
@@ -45,7 +47,9 @@ def load_labor_catalog(file_name: str = "labor_const_2026-01-01.json") -> Resour
return ResourceCatalog(entries=entries, aliases=dict(variables.get("aliases", {})))
def load_machine_catalog_entries(file_name: str = "mach_base_2026.json") -> list[CatalogEntry]:
def load_machine_catalog_entries(
file_name: str = "3_품셈_건설_기계경비기준_8장_2026-01-01.json",
) -> list[CatalogEntry]:
"""기종 카탈로그 613건을 매칭용 항목으로 편다.
⚠ **규격이 매칭의 일부**다 — 「굴착기(무한궤도)」만 23 규격이라 이름만으로는
@@ -61,7 +65,7 @@ def load_machine_catalog_entries(file_name: str = "mach_base_2026.json") -> list
def load_material_catalog_entries(
file_name: str = "mat_price_public_2026-08-14.json",
file_name: str = "1_기초_자재_시설공통_나라장터_2026-08-14.json",
) -> list[CatalogEntry]:
"""관급 자재 6,999건을 매칭용 항목으로 편다.
@@ -102,7 +106,7 @@ def load_combined_catalog() -> ResourceCatalog:
def load_work_item_master(
file_name: str = "work_item_master_2026-01-01.json",
file_name: str = "2_공종_산림_마스터_2026-01-01.json",
) -> dict[str, Any]:
"""메인 창 산출물 — **읽기 전용**."""
return _read_json(*_MASTER_SUBPATH, file_name)
+1 -1
View File
@@ -38,7 +38,7 @@ def _sources() -> tuple[dict[str, dict[str, Any]], dict[str, int]]:
"""(기계 코드 → 손료 성분 레코드, 규칙 이름 → 암석 가산 %)."""
from B09_Estimation.B09_Estimation_MachineCost import _read_json
variables = _read_json("mach_base_2026.json")["variables"]
variables = _read_json("3_품셈_건설_기계경비기준_8장_2026-01-01.json")["variables"]
records = {r["machine_code"]: r for r in variables["mach_loss_coef"]["records"]}
rules = {r["machine_group"]: int(r["rock_work"]) for r in variables["mach_rock_adj"]["rules"]}
return records, rules
+1 -1
View File
@@ -92,7 +92,7 @@ class CostRequest(BaseModel):
equipment_guarantee_work_type: str = "civil_general"
subcontract_guarantee_variant: str = "integrated_civil_or_industrial"
rate_file_name: str = "rates_2026.json"
rate_file_name: str = "1_기초_제비율_토목_조달청_2026-04-13.json"
#: 공종별 수량 `{공종코드: 수량}`. 주면 **직접비 3분할을 여기서 만들어** 쓴다.
#: ⚠ 일위대가 합계를 뭉쳐 넣지 않는다 — 밑수가 항목마다 갈린다(PLAN 8-9 규칙 2).
+2 -2
View File
@@ -500,7 +500,7 @@ def _add_machine_layers(
@lru_cache(maxsize=1)
def load_basis_missing(
file_name: str = "basis_missing_2026-01-01.json",
file_name: str = "2_공종_산림_밑수없음_2026-01-01.json",
) -> dict[str, str]:
"""B08 이 낸 **밑수 못 찾은 표** 목록 — `{표 번호: 절 이름}`.
@@ -512,7 +512,7 @@ def load_basis_missing(
import os
root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
path = os.path.join(root, "resources", "data_work_item_master", file_name)
path = os.path.join(root, "resources", "master_data", "old", file_name)
if not os.path.exists(path):
return {}
with open(path, encoding="utf-8") as handle:
@@ -57,7 +57,9 @@ def operating_record(node: dict[str, Any] | None = None):
values = _table_values(node)
if values is None:
return None
aliases = _read_json(*_CATALOG_SUBPATH, "labor_const_2026-01-01.json")["variables"]["aliases"]
aliases = _read_json(*_CATALOG_SUBPATH, "1_기초_노임_건설업_대한건설협회_2026-01-01.json")[
"variables"
]["aliases"]
return OperatingRecord(
machine_code=MACHINE,
machine_name="이동식 임목파쇄기",