Files
Aislo/common_util/common_util_calc_sites.py
T
eomsangdonandClaude Opus 5.5 f15ca04bb0 merge(511-B · 511-C 합치기): origin/sub_laptop_4(511-C 유토 배분 E2 · 판 haul 줄) 받음 — 공유 파일 충돌 풂
- Node 입구 design_calc_node.ts 등록부 밖 id = 시험 길 · 단계 · 화면 길(PAGE_IDS) · haul.view
- calc_runner = PAGE_IDS(cross.refresh) · VIEW_IDS(haul.view) 둘 다 받음
- calc_sites.py 머리 글 = B-4 cross · 511-C haul(기본 server) 함께 · 묶음 13 줄
- 시험 309_2 · 309_2_browser · 511_b1 = 열린 줄에 haul 더함 · 화면 시험 「지금 서버」 = haul 기본 server 로 1 줄(바꾼 뒤 2 줄)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 15:55:17 +09:00

141 lines
5.9 KiB
Python

"""계산 자리 판(PLAN 504-2 · 요구 309-2) — 시스템 관리자 「계산 자리」 창이 읽고 쓰는 판 하나.
판 = `resources/master_template/calc_sites.json` `{version, sites: {묶음: "both" | "server"}}`.
both = 조작 중 브라우저 셈 + [저장] 서버 정본(지금 등록부 자리) · server = 조작 중에도 서버 길
(G1 · D3 = 504-2 · ③ 토량 · ④ 이월 · ⑧ 고정 브라우저(시험용 · fixed 는 정본 칸 표시) = 511-A 창구 ·
cross = 511-B B-4 묶음 cross 단계 · 저장 노선 다시 셈).
판이 없거나 깨지면 기본값(줄 `default` · 없으면 both = 등록부 그대로 · haul = server).
haul(E2 · 511-C · 요구 321) both = 브라우저가 배분 모듈을 그때만 불러 셈 · server = /haul-plan.
[저장] · [확정] 서버 정본 길은 판을 안 읽음.
묶음 13 줄 = 등록부(`design_calc_registry*.ts`) # 전부 · 같은지 시험 `test_309_2_calc_sites.py`.
"""
from __future__ import annotations
import json
import os
from pathlib import Path
from typing import Any
ROOT = Path(__file__).resolve().parents[1]
PATH = ROOT / "resources" / "master_template" / "calc_sites.json"
VALUES = ("both", "server")
# 묶음 · 등록부 # · 바꿀 수 있음 · 잠금 줄의 고정 자리(both · server · browser) · 까닭 열쇠
GROUPS: tuple[dict[str, Any], ...] = (
{"key": "ground_sample", "nos": ["G1"], "switchable": True, "fixed": None, "why": None},
{"key": "corridor_mesh", "nos": ["D3"], "switchable": True, "fixed": None, "why": None},
{
"key": "quantity",
"nos": ["E1", "F7", "F8", "C4"],
"switchable": True,
"fixed": None,
"why": "quantity",
},
{"key": "carry", "nos": ["R9", "F10"], "switchable": True, "fixed": None, "why": "carry"},
# 묶음 단계(511-B · 단계째 서버 Node `stage.*`) — 짝 모으기 B-3 ~ B-5 끝난 줄부터 엶
# 노선(511-B B-5 · 노선 저장 폴리라인 = Node `route.polyline` · 조작 중 「서버」 = 같은 id)
{"key": "route", "nos": ["R1", "R3"], "switchable": True, "fixed": None, "why": None},
{
"key": "follow",
"nos": ["P5", "P6", "F1", "F2"],
"switchable": False,
"fixed": "both",
"why": "pair_B5",
},
# 측점(511-B B-2 · 서버 측점 생성 = Node `stations.sections` · 조작 중 「서버」 = `stations.list`)
{
"key": "stations",
"nos": ["R5", "R6", "R7", "F3"],
"switchable": True,
"fixed": None,
"why": None,
},
# 종단 = 짝 모음 끝(B-3 · 서버 Node `profile.base` · `profile.alignment`) — 엶
{
"key": "profile",
"nos": ["P1", "P2", "P3", "P4", "P8"],
"switchable": True,
"fixed": None,
"why": None,
},
# cross = 짝 모음 끝(B-4) — server = 묶음 cross 를 서버 Node 가 같은 TS 로(DTM 아닌 지표면 = 브라우저)
{
"key": "cross",
"nos": ["G2", "G4", "C1", "C2", "C3", "C5", "C6", "F4", "F5", "F6"],
"switchable": True,
"fixed": None,
"why": "cross",
},
# 폼 기본값 도구(B04 · B08 파이썬 · 고를 것 없음)
{"key": "form_default", "nos": ["F9"], "switchable": False, "fixed": "both", "why": None},
# 유토 배분 — 기본 server(지금 /haul-plan 그대로) · both = 브라우저 셈(511-C)
{
"key": "haul",
"nos": ["E2"],
"switchable": True,
"fixed": None,
"why": "haul",
"default": "server",
},
# 서버만 — 줄마다 까닭(`whys` · 511-C)
{
"key": "fixed_server",
"nos": ["G3", "D1", "D2", "N1"],
"switchable": False,
"fixed": "server",
"why": "server",
"whys": {n: f"server_{n}" for n in ("G3", "D1", "D2", "N1")},
},
{
"key": "fixed_browser",
"nos": ["R2", "R4", "R8", "P7", "C7", "D4"],
"switchable": True,
"fixed": "browser",
"why": "browser",
},
)
# 판 줄 차례 = 묶음 차례(파일 늘 같은 꼴)
ORDER = tuple(g["key"] for g in GROUPS if g["switchable"])
SWITCHABLE = frozenset(ORDER)
LOCKED = frozenset(g["key"] for g in GROUPS if not g["switchable"])
DEFAULTS = {g["key"]: g.get("default", "both") for g in GROUPS if g["switchable"]}
def read_sites() -> dict[str, Any]:
"""판 — 없음 · 깨짐 · 모르는 값 = 그 줄 기본값(`DEFAULTS`)."""
try:
raw = json.loads(PATH.read_text(encoding="utf-8"))
except (OSError, ValueError):
raw = {}
stored = raw.get("sites") if isinstance(raw, dict) else None
stored = stored if isinstance(stored, dict) else {}
sites = {k: stored.get(k) if stored.get(k) in VALUES else DEFAULTS[k] for k in ORDER}
version = raw.get("version") if isinstance(raw, dict) else None
return {"version": version if isinstance(version, int) else 0, "sites": sites}
def check_changes(changes: dict[str, str]) -> None:
"""잠금 줄 · 모르는 줄 · 모르는 값 = ValueError(창구가 400)."""
for key, value in changes.items():
if key in LOCKED:
raise ValueError(f"잠긴 줄 — {key}")
if key not in SWITCHABLE:
raise ValueError(f"모르는 줄 — {key}")
if value not in VALUES:
raise ValueError(f"모르는 값 — {key}={value}")
def write_sites(changes: dict[str, str]) -> tuple[dict[str, Any], list[str]]:
"""검사 → 바뀐 줄만 얹어 판 번호 +1 · 통째 바꿔 씀(반쯤 쓴 판 없음). 돌려줌 = 새 판 · 바뀐 줄."""
check_changes(changes)
current = read_sites()
changed = sorted(k for k, v in changes.items() if current["sites"][k] != v)
if not changed:
return current, []
board = {"version": current["version"] + 1, "sites": {**current["sites"], **changes}}
temp = PATH.with_suffix(".json.tmp")
temp.write_text(json.dumps(board, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
os.replace(temp, PATH)
return board, changed