feat(b08): 도자 한계거리를 산출 조건 칸으로 — 기본 60 m 와 근거 셋을 화면에 같이 보임
유토곡선 장비 경계의 도자 한계거리가 config 붙박이였고 설정 칸 haul_limits_m_override 는 아무도 안 읽었음. dozer_haul_limit_m 칸으로 갈음해 B06 유토곡선 문맥·배분·B08 산출 조건이 같은 값을 읽음(common_util 한 곳). 화면에 기본값 60 m · 근거(품셈 8-1-1 · 산림과임업기술 5장 · 실무 EARTH.DAT 전수)를 보이고, 종무대 20 m 는 규정이라 값만 보임. 종무대 이하 값은 저장에서 막음 · 비우면 기본값. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BW6Jdsh18WPtYUR6THZJqn
This commit is contained in:
@@ -73,14 +73,13 @@ from B06_Section.B06_Section_Schema import (
|
||||
SectionRegenerateRequest,
|
||||
SectionSummaryResponse,
|
||||
)
|
||||
from B06_Section.B06_Section_Server_Calc_Prebuild import conversion_factors_for
|
||||
from B06_Section.B06_Section_Server_Calc_Prebuild import conversion_factors_for, haul_limits_for
|
||||
from common_util.common_util_auth import verify_session
|
||||
from common_util.common_util_storage import resolve_stored_project_path
|
||||
from common_util.common_util_surface_confirmation import get_surface_confirmation_params
|
||||
from common_util.common_util_workflow_state import get_workflow_state
|
||||
from config.config_db import get_db_pool, run_with_connection
|
||||
from config.config_system import (
|
||||
EARTHWORK_HAUL_EQUIPMENT_LIMITS_M,
|
||||
FOREST_ROAD_MIN_WIDTH_M,
|
||||
NATURAL_SPOIL_MIN_GROUND_SLOPE,
|
||||
SECTION_VERTICAL_EXAGGERATION,
|
||||
@@ -149,7 +148,7 @@ async def get_section_context(project_id: UUID) -> SectionContextResponse | JSON
|
||||
earthwork_conversion=await conversion_factors_for(project_id),
|
||||
haul_equipment_limits=[
|
||||
HaulEquipmentLimit(key=key, max_distance_m=limit)
|
||||
for key, limit in EARTHWORK_HAUL_EQUIPMENT_LIMITS_M
|
||||
for key, limit in await haul_limits_for(project_id)
|
||||
],
|
||||
natural_spoil_min_ground_slope=NATURAL_SPOIL_MIN_GROUND_SLOPE,
|
||||
)
|
||||
|
||||
@@ -27,6 +27,7 @@ from B06_Section.B06_Section_Server_Calc_Prebuild import (
|
||||
_mass_haul_context,
|
||||
conversion_factors_for,
|
||||
haul_inputs_for,
|
||||
haul_limits_for,
|
||||
)
|
||||
from common_util.common_util_node_bundle import run_bundle_json
|
||||
|
||||
@@ -66,6 +67,8 @@ async def compute_haul_plan(
|
||||
haul_inputs = await haul_inputs_for(project_id)
|
||||
# 곡선이 쓰는 계수도 프로젝트가 고른 값으로 — 토적표·운반표와 같은 값이어야 한다.
|
||||
factors = await conversion_factors_for(project_id)
|
||||
# 장비 거리 경계도 프로젝트 값으로 — 도쟈 한계거리를 고쳤으면 배분이 그 값으로 선다.
|
||||
limits = await haul_limits_for(project_id)
|
||||
try:
|
||||
output = await asyncio.to_thread(
|
||||
run_bundle_json,
|
||||
@@ -73,7 +76,7 @@ async def compute_haul_plan(
|
||||
_NPM_SCRIPT,
|
||||
{
|
||||
"haul_plan_for": result,
|
||||
"context": _mass_haul_context(haul_inputs, factors),
|
||||
"context": _mass_haul_context(haul_inputs, factors, limits),
|
||||
},
|
||||
)
|
||||
except Exception:
|
||||
|
||||
@@ -40,6 +40,7 @@ from B06_Section.B06_Section_Repository_Bulk import merge_cross_section_designs
|
||||
from common_util.common_util_node_bundle import run_bundle_json
|
||||
from common_util.common_util_project_settings import (
|
||||
earthwork_conversion_factors,
|
||||
haul_equipment_limits,
|
||||
quantity_settings,
|
||||
)
|
||||
from common_util.common_util_storage import resolve_stored_project_path
|
||||
@@ -98,9 +99,21 @@ async def conversion_factors_for(project_id: Any) -> dict[str, dict[str, float]]
|
||||
return earthwork_conversion_factors(quantity_settings(root))
|
||||
|
||||
|
||||
async def haul_limits_for(project_id: Any) -> list[tuple[str, float | None]]:
|
||||
"""이 프로젝트가 쓸 운반장비 거리 경계(도쟈 한계거리를 고쳤으면 그 값). 못 읽으면 기본값."""
|
||||
try:
|
||||
stored_path = await run_with_connection(get_project_storage_relative_path, project_id)
|
||||
root = resolve_stored_project_path(stored_path)
|
||||
except Exception:
|
||||
logger.warning("B06 프로젝트 경로를 못 찾음 — 기본 경계로 진행: project_id=%s", project_id)
|
||||
return list(EARTHWORK_HAUL_EQUIPMENT_LIMITS_M)
|
||||
return haul_equipment_limits(quantity_settings(root))
|
||||
|
||||
|
||||
def _mass_haul_context(
|
||||
haul_inputs: dict[str, Any] | None = None,
|
||||
factors: dict[str, dict[str, float]] | None = None,
|
||||
limits: list[tuple[str, float | None]] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""유토곡선 계산에 필요한 값 — 화면이 `sections/context`로 받는 것과 같은 상수다.
|
||||
|
||||
@@ -118,9 +131,10 @@ def _mass_haul_context(
|
||||
# 프로젝트가 고른 계수가 있으면 그것, 없으면 정본 기본값.
|
||||
"earthwork_conversion": factors or EARTHWORK_CONVERSION_FACTORS,
|
||||
"natural_spoil_min_ground_slope": NATURAL_SPOIL_MIN_GROUND_SLOPE,
|
||||
# 프로젝트가 도쟈 한계거리를 고쳤으면 그 값, 없으면 정본 기본값.
|
||||
"haul_equipment_limits": [
|
||||
{"key": key, "max_distance_m": limit}
|
||||
for key, limit in EARTHWORK_HAUL_EQUIPMENT_LIMITS_M
|
||||
for key, limit in (limits or EARTHWORK_HAUL_EQUIPMENT_LIMITS_M)
|
||||
],
|
||||
# ⚠ B08 이 아직 이 값을 내지 않는다(2026-09-09) — 그때까지 `None`(아직 안 옴)이다.
|
||||
# 값을 내기 시작하면 여기에 실어 주기만 하면 통로가 이어진다.
|
||||
@@ -220,7 +234,9 @@ async def _recompute(project_id: UUID | str, route_id: int) -> int:
|
||||
{
|
||||
"detail": detail,
|
||||
"context": _mass_haul_context(
|
||||
haul_inputs, earthwork_conversion_factors(quantity_settings(project_root))
|
||||
haul_inputs,
|
||||
earthwork_conversion_factors(quantity_settings(project_root)),
|
||||
haul_equipment_limits(quantity_settings(project_root)),
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -48,6 +48,7 @@ from common_util.common_util_project_settings import (
|
||||
concrete_placing_method,
|
||||
earthwork_conversion_choices,
|
||||
earthwork_conversion_factors,
|
||||
haul_limit_choice,
|
||||
quantity_settings,
|
||||
rock_classes,
|
||||
save_section,
|
||||
@@ -57,6 +58,7 @@ from config.config_db import run_with_connection
|
||||
from config.config_system_design import (
|
||||
EARTHWORK_CONVERSION_FACTORS,
|
||||
EARTHWORK_CONVERSION_PUMSEM_C_RANGES,
|
||||
EARTHWORK_HAUL_EQUIPMENT_LIMITS_M,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -90,6 +92,8 @@ async def get_earthwork_table(project_id: UUID, route_id: int) -> JSONResponse:
|
||||
table = build_table(_stations(designs), factors)
|
||||
# 화면이 「무엇을 골랐나 · 품셈 범위 안인가」를 보이는 데 쓴다. 계산에는 안 들어간다.
|
||||
table["conversion_factor_choices"] = earthwork_conversion_choices(settings)
|
||||
# 도쟈 한계거리 — 지금 값·기본값·근거를 함께 보인다(유토곡선 장비 경계, 2026-09-13 판정).
|
||||
table["haul_limit_choice"] = haul_limit_choice(settings)
|
||||
# 품셈 암종별 범위 — **화면 안내용**이다. 정의처가 서버 한 곳이라 내려보내 쓴다
|
||||
# (프론트에 다시 적으면 두 벌이 되어 갈린다).
|
||||
table["conversion_factor_pumsem_ranges"] = [
|
||||
@@ -445,6 +449,8 @@ class QuantitySettingsBody(BaseModel):
|
||||
# ⚠ **기본값을 복사해 넣지 않는다** — 안 고른 갈래는 키가 없어야 정본이 선다.
|
||||
# 빈 dict 는 「전부 기본값으로 되돌림」이라 통째로 갈아 끼운다.
|
||||
conversion_factors_override: dict[str, Any] | None = None
|
||||
# 도쟈 한계거리(m) — `None` 은 기본값(60 m). 종무대 20 m 보다 커야 한다(도쟈 몫이 사라짐).
|
||||
dozer_haul_limit_m: float | None = None
|
||||
|
||||
|
||||
#: `None` 이 「안 정함」을 뜻하는 칸 — 저장에서 **버리지 않고 그대로 덮어쓴다**.
|
||||
@@ -456,6 +462,7 @@ NULLABLE_SETTING_KEYS = (
|
||||
"rubble_base_thickness_m",
|
||||
"topsoil_haul_distance_m",
|
||||
"wood_chipping_volume_m3",
|
||||
"dozer_haul_limit_m",
|
||||
)
|
||||
|
||||
|
||||
@@ -482,6 +489,17 @@ async def save_quantity_settings(project_id: UUID, body: QuantitySettingsBody) -
|
||||
for key in NULLABLE_SETTING_KEYS:
|
||||
if key in body.model_fields_set:
|
||||
values[key] = getattr(body, key)
|
||||
dozer_limit = values.get("dozer_haul_limit_m")
|
||||
free_haul = dict(EARTHWORK_HAUL_EQUIPMENT_LIMITS_M)["free_haul"] or 0.0
|
||||
if dozer_limit is not None and dozer_limit <= free_haul:
|
||||
# 조용히 기본값으로 돌리지 않는다 — 넣은 값이 안 쓰이는 줄 모른다.
|
||||
return JSONResponse(
|
||||
status_code=400,
|
||||
content={
|
||||
"status": "error",
|
||||
"message": f"도쟈 한계거리는 종무대 {free_haul:g} m 보다 커야 합니다.",
|
||||
},
|
||||
)
|
||||
if "concrete_placing_method" in values:
|
||||
method = values["concrete_placing_method"]
|
||||
# 「안 정함」으로 되돌릴 수 있어야 한다 — 빈 값이면 지운다(8-22 ② 와 같은 자리).
|
||||
|
||||
@@ -92,6 +92,8 @@ export interface QuantitySettings {
|
||||
structure_trench_water?: string | null;
|
||||
/** 표토 운반거리(m) — 별표2 가 요구하는 운반·적치의 밑수. 비면 그 줄이 막힌다. */
|
||||
topsoil_haul_distance_m?: number | null;
|
||||
/** 도쟈 한계거리(m) — `null`·없음이면 기본값(서버 정본 60 m)이 선다. */
|
||||
dozer_haul_limit_m?: number | null;
|
||||
/** 부대시설 개소 — `{항목키: 개소}`. ⚠ 산식으로 만들지 않는다(확정 13). */
|
||||
ancillary_counts?: Record<string, number | null>;
|
||||
/** 임목축적 등급 — `"소림"`·`"중림"`·`"밀림"`(품셈 9-21 [주]①). 본수가 아니라 축적이다. */
|
||||
@@ -141,6 +143,15 @@ export interface EarthworkTable {
|
||||
conversion_factor_choices?: Record<string, ConversionFactorChoice>;
|
||||
/** 품셈 암종별 범위(안내용). 정의처가 서버라 내려받아 보인다. */
|
||||
conversion_factor_pumsem_ranges?: PumsemRange[];
|
||||
/** 도쟈 한계거리 — 지금 값·기본값·근거(서버가 정본). 종무대 20 m 는 규정이라 값만 보인다. */
|
||||
haul_limit_choice?: {
|
||||
value: number;
|
||||
default: number;
|
||||
chosen: boolean;
|
||||
basis: string;
|
||||
free_haul_m: number;
|
||||
free_haul_basis: string;
|
||||
};
|
||||
/** 근거 사전 — ⚠ **개발환경에서만** 실려 온다. 운영에서는 칸 자체가 없다. */
|
||||
provenance?: ProvenancePayload;
|
||||
}
|
||||
|
||||
@@ -110,6 +110,8 @@ async function saveQuantitySettings(projectId: string, draft: DraftSettings): Pr
|
||||
ancillary_counts: draft.ancillary_counts,
|
||||
// 토량환산계수 — 고른 갈래만 담긴다. 빈 dict 는 「전부 기본값으로 되돌림」이다.
|
||||
conversion_factors_override: conversionOverridePayload(draft.conversion_factors),
|
||||
// 도쟈 한계거리 — `null` 도 보낸다(기본값으로 되돌리는 길).
|
||||
dozer_haul_limit_m: draft.dozer_haul_limit_m,
|
||||
}),
|
||||
},
|
||||
);
|
||||
@@ -295,6 +297,8 @@ interface DraftSettings {
|
||||
material_supply: Record<string, SupplyChoice>;
|
||||
// 갈래별 토량환산계수(다짐) — `compacted` 가 `null` 이면 「안 고름」이라 기본값이 선다.
|
||||
conversion_factors: Record<string, FactorDraft>;
|
||||
// 도쟈 한계거리(m) — `null` 은 기본값(서버 정본 60 m)이 선다.
|
||||
dozer_haul_limit_m: number | null;
|
||||
dirty: boolean;
|
||||
}
|
||||
|
||||
@@ -403,6 +407,30 @@ function buildQuantitySidePanel(
|
||||
);
|
||||
if (factorFields) panel.append(factorFields);
|
||||
|
||||
// ── 운반장비 거리 경계 — 도쟈 한계거리는 설계 조건이라 칸으로(2026-09-13 판정) ──
|
||||
// ⚠ 기본값과 근거를 **서버가 준 그대로** 옆에 보인다 — 「이 값이 어디서 왔나」가 화면에 있어야 함.
|
||||
const haul = table?.haul_limit_choice;
|
||||
if (haul) {
|
||||
panel.append(field(L("B08_Quantity_Side_HaulLimits"), ""));
|
||||
const dozer = optionalNumberField(
|
||||
L("B08_Quantity_Side_DozerLimit_Label"),
|
||||
draft.dozer_haul_limit_m,
|
||||
"5",
|
||||
(value) => {
|
||||
draft.dozer_haul_limit_m = value;
|
||||
draft.dirty = true;
|
||||
},
|
||||
);
|
||||
const input = dozer.querySelector("input");
|
||||
if (input) input.placeholder = String(haul.default);
|
||||
panel.append(dozer);
|
||||
panel.append(hintRow(`${L("B08_Quantity_Factor_Default")} ${haul.default} m — ${haul.basis}`));
|
||||
panel.append(
|
||||
hintRow(`${L("B08_Quantity_Side_FreeHaul")} ${haul.free_haul_m} m — ${haul.free_haul_basis}`),
|
||||
);
|
||||
panel.append(hintRow(L("B08_Quantity_Side_HaulLimits_Reach")));
|
||||
}
|
||||
|
||||
// ── 지반 구성비 — 갈래 수는 프로젝트 세트가 정한다(코드에 안 박음, PLAN 8-13) ──
|
||||
const classes = [...(table?.summary?.rock_classes ?? [])];
|
||||
// ⚠ 비율을 아직 안 넣었으면 집계가 **「암」 한 줄**로 나온다(갈래로 안 갈림). 그 줄에도
|
||||
@@ -940,6 +968,7 @@ export async function renderB08Quantity(root: HTMLElement): Promise<void> {
|
||||
},
|
||||
]),
|
||||
),
|
||||
dozer_haul_limit_m: (stored.dozer_haul_limit_m as number | null) ?? null,
|
||||
dirty: false,
|
||||
};
|
||||
const reload = (): void => {
|
||||
|
||||
@@ -39,6 +39,8 @@ from common_util.common_util_json import atomic_write_json
|
||||
from config.config_system_design import (
|
||||
EARTHWORK_CONVERSION_C_RANGES,
|
||||
EARTHWORK_CONVERSION_FACTORS,
|
||||
EARTHWORK_HAUL_EQUIPMENT_LIMITS_M,
|
||||
EARTHWORK_HAUL_LIMIT_BASIS,
|
||||
)
|
||||
|
||||
SETTINGS_FILENAME = "project_settings.json"
|
||||
@@ -90,7 +92,10 @@ def default_settings() -> dict[str, Any]:
|
||||
# 「시공법 미지정」으로 드러난다(2026-09-07 일감 9 에서 드러난 자리).
|
||||
"rock_methods": {},
|
||||
"conversion_factors_override": None,
|
||||
"haul_limits_m_override": None,
|
||||
# 도쟈 한계거리(m) — `None` 은 기본값(config 60 m, 근거 셋 일치)이 선다.
|
||||
# 설계 조건이라 바꿀 수 있다(2026-09-13 판정). 종무대 20 m 는 규정이라 칸이 없다.
|
||||
# ⚠ 종전 `haul_limits_m_override` 는 아무도 안 읽던 칸이라 이것으로 갈음했다.
|
||||
"dozer_haul_limit_m": None,
|
||||
"application_ratios_pct": {key: 100 for key in APPLICATION_RATIO_KEYS},
|
||||
# 자재총괄의 관급/사급 구분 — `{자재명: "owner_supplied"|"contractor_supplied"}`
|
||||
# 또는 `{자재명: {"supply": …, "install_by": "contractor"|"owner"}}`.
|
||||
@@ -318,6 +323,38 @@ def earthwork_conversion_choices(settings: dict[str, Any]) -> dict[str, dict[str
|
||||
return choices
|
||||
|
||||
|
||||
def haul_equipment_limits(settings: dict[str, Any]) -> list[tuple[str, float | None]]:
|
||||
"""이 프로젝트가 쓸 운반장비 거리 경계 — 기본값 위에 **도쟈 한계거리만** 얹는다.
|
||||
|
||||
⚠ 유토곡선(B06)·운반표가 같은 값으로 서야 한다 — 읽는 자리마다 상수를 들지 말고
|
||||
이 함수를 거친다.
|
||||
⚠ 종무대 경계 이하 값은 받지 않는다(도쟈 몫이 사라짐) — 기본값이 선다.
|
||||
"""
|
||||
limits = dict(EARTHWORK_HAUL_EQUIPMENT_LIMITS_M)
|
||||
value = settings.get("dozer_haul_limit_m")
|
||||
if (
|
||||
isinstance(value, (int, float))
|
||||
and not isinstance(value, bool)
|
||||
and float(value) > float(limits["free_haul"] or 0.0)
|
||||
):
|
||||
limits["dozer"] = float(value)
|
||||
return [(key, limits[key]) for key, _ in EARTHWORK_HAUL_EQUIPMENT_LIMITS_M]
|
||||
|
||||
|
||||
def haul_limit_choice(settings: dict[str, Any]) -> dict[str, Any]:
|
||||
"""도쟈 한계거리 칸 — 화면이 지금 값·기본값·근거를 함께 보인다."""
|
||||
default = dict(EARTHWORK_HAUL_EQUIPMENT_LIMITS_M)["dozer"]
|
||||
value = dict(haul_equipment_limits(settings))["dozer"]
|
||||
return {
|
||||
"value": value,
|
||||
"default": default,
|
||||
"chosen": value != default,
|
||||
"basis": EARTHWORK_HAUL_LIMIT_BASIS["dozer"],
|
||||
"free_haul_m": dict(EARTHWORK_HAUL_EQUIPMENT_LIMITS_M)["free_haul"],
|
||||
"free_haul_basis": EARTHWORK_HAUL_LIMIT_BASIS["free_haul"],
|
||||
}
|
||||
|
||||
|
||||
def application_ratio(settings: dict[str, Any], key: str) -> float:
|
||||
"""반영률을 0~1 로. 없으면 100 %(=1.0) — 실무 관측치를 기본값으로 쓰지 않는다."""
|
||||
raw = (settings.get("application_ratios_pct") or {}).get(key, 100)
|
||||
|
||||
@@ -422,6 +422,16 @@ EARTHWORK_HAUL_EQUIPMENT_LIMITS_M = (
|
||||
("dozer", 60.0),
|
||||
("dump_truck", None),
|
||||
)
|
||||
# 경계값 근거 — **화면이 기본값 옆에 그대로 보인다**(2026-09-13 브레인 판정 · PLAN 10장).
|
||||
# ⚠ 성격이 다르다: 종무대 20 m 는 품셈 규정이라 칸을 안 두고, 도쟈 60 m 는 설계 조건이라
|
||||
# 프로젝트 설정 `dozer_haul_limit_m` 으로 바꿀 수 있다.
|
||||
EARTHWORK_HAUL_LIMIT_BASIS = {
|
||||
"free_haul": "품셈 1-2-7 「소운반 20 m 이내는 품에 포함」 — 규정이라 안 바꿈",
|
||||
"dozer": (
|
||||
"근거 셋 일치 — 표준품셈 8-1-1(60 m 이하 도저) · 산림과임업기술 5장 p.441·478"
|
||||
"(60 m 이하 도저 / 초과 덤프) · 실무 오솔길 EARTH.DAT 6개 공사지 전부 60 m"
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -239,7 +239,8 @@ def test_기본설정_모양(tmp_path: Path) -> None:
|
||||
quantity = settings["quantity"]
|
||||
# override 는 기본이 None — 「안 정했으면 config 정본을 쓴다」는 뜻.
|
||||
assert quantity["conversion_factors_override"] is None
|
||||
assert quantity["haul_limits_m_override"] is None
|
||||
# 도쟈 한계거리 — 안 읽히던 `haul_limits_m_override` 를 갈음(2026-09-13). None 이면 기본 60 m.
|
||||
assert quantity["dozer_haul_limit_m"] is None
|
||||
# 반영률 기본 100. 실무 관측 80/50/80 을 넣지 않는다.
|
||||
assert quantity["application_ratios_pct"] == {key: 100 for key in APPLICATION_RATIO_KEYS}
|
||||
# estimation 은 자리만 — 채우는 것은 B09 몫.
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
"""도쟈 한계거리 칸 (PLAN 5장 · 2026-09-13 판정 「도자 60 m 확정 — 설계 조건이라 칸으로」).
|
||||
|
||||
지키는 것
|
||||
① 안 넣으면 정본 60 m · 넣으면 유토곡선 장비 경계가 그 값 · 종무대 20 m 는 규정이라 안 바뀜
|
||||
② 화면이 기본값과 근거(근거 셋 일치)를 함께 받음
|
||||
③ 종무대 이하 값은 저장에서 막음(도쟈 몫이 사라짐) · null 로 기본값 되돌림
|
||||
④ B06 유토곡선 계산 문맥이 프로젝트 값을 실음
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
import B08_Quantity.B08_Quantity_Router_Earthwork as earthwork_router # noqa: E402
|
||||
from B06_Section.B06_Section_Server_Calc_Prebuild import _mass_haul_context # noqa: E402
|
||||
from common_util.common_util_project_settings import ( # noqa: E402
|
||||
haul_equipment_limits,
|
||||
haul_limit_choice,
|
||||
load_settings,
|
||||
)
|
||||
|
||||
PROJECT_ID = "55555555-5555-5555-5555-555555555555"
|
||||
|
||||
|
||||
def test_기본은_60m_넣으면_그_값_종무대는_그대로() -> None:
|
||||
assert haul_equipment_limits({}) == [("free_haul", 20.0), ("dozer", 60.0), ("dump_truck", None)]
|
||||
assert dict(haul_equipment_limits({"dozer_haul_limit_m": 70}))["dozer"] == 70.0
|
||||
assert dict(haul_equipment_limits({"dozer_haul_limit_m": 15}))["dozer"] == 60.0 # 종무대 이하
|
||||
choice = haul_limit_choice({"dozer_haul_limit_m": 70})
|
||||
assert (choice["value"], choice["default"], choice["chosen"]) == (70.0, 60.0, True)
|
||||
assert "8-1-1" in choice["basis"] and "EARTH.DAT" in choice["basis"]
|
||||
assert "1-2-7" in choice["free_haul_basis"]
|
||||
|
||||
|
||||
def test_유토곡선_문맥이_프로젝트_경계를_싣는다() -> None:
|
||||
limits = haul_equipment_limits({"dozer_haul_limit_m": 80})
|
||||
context = _mass_haul_context(None, None, limits)
|
||||
assert {row["key"]: row["max_distance_m"] for row in context["haul_equipment_limits"]} == {
|
||||
"free_haul": 20.0,
|
||||
"dozer": 80.0,
|
||||
"dump_truck": None,
|
||||
}
|
||||
assert _mass_haul_context()["haul_equipment_limits"][1]["max_distance_m"] == 60.0
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def client(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> TestClient:
|
||||
async def fake_run(func, *args):
|
||||
return "project"
|
||||
|
||||
monkeypatch.setattr(earthwork_router, "run_with_connection", fake_run)
|
||||
monkeypatch.setattr(earthwork_router, "resolve_stored_project_path", lambda _p: str(tmp_path))
|
||||
app = FastAPI()
|
||||
app.include_router(earthwork_router.router)
|
||||
return TestClient(app)
|
||||
|
||||
|
||||
def test_저장은_종무대_이하를_막고_null_로_되돌린다(client: TestClient, tmp_path: Path) -> None:
|
||||
url = f"/api/projects/{PROJECT_ID}/quantity/settings"
|
||||
assert client.put(url, json={"dozer_haul_limit_m": 20}).status_code == 400
|
||||
assert client.put(url, json={"dozer_haul_limit_m": 70}).status_code == 200
|
||||
assert load_settings(tmp_path)["quantity"]["dozer_haul_limit_m"] == 70
|
||||
assert client.put(url, json={"dozer_haul_limit_m": None}).status_code == 200
|
||||
assert load_settings(tmp_path)["quantity"]["dozer_haul_limit_m"] is None
|
||||
@@ -765,6 +765,13 @@ export const ui_locales_b2 = {
|
||||
"These factors also feed the mass-haul curve, haul table and basis units.",
|
||||
],
|
||||
B08_Quantity_Factor_Default: ["기본값", "Default"],
|
||||
B08_Quantity_Side_HaulLimits: ["운반장비 거리 경계", "Haul equipment distance limits"],
|
||||
B08_Quantity_Side_DozerLimit_Label: ["도쟈 한계거리(m)", "Dozer haul limit (m)"],
|
||||
B08_Quantity_Side_FreeHaul: ["종무대(바꾸지 않음)", "Free haul (fixed)"],
|
||||
B08_Quantity_Side_HaulLimits_Reach: [
|
||||
"유토곡선 운반 배분(무대·도쟈·덤프)에 닿습니다 — 바꾼 뒤 종횡단에서 다시 저장해야 운반표가 따라옵니다.",
|
||||
"Feeds the mass-haul split (free/dozer/dump) — re-save the profile/cross sections for the haul table to follow.",
|
||||
],
|
||||
B08_Quantity_Factor_Range: ["품셈 범위", "Standard range"],
|
||||
B08_Quantity_Factor_OutOfRange: [
|
||||
"품셈 범위 밖입니다 — 사유를 적어 주세요(토질시험 값일 수 있습니다).",
|
||||
|
||||
Reference in New Issue
Block a user