Files
Aislo/M02_MasterTemplete/M02_Area_Table.py
T

147 lines
6.6 KiB
Python

"""면적산출 범위 → 도면 표(PLAN 41-1) — 변환은 Node 번들 한 벌(`common_util_spreadsheet_node.ts`
`areaTable` · 화면 표시 글과 같은 코드) · 여기는 부르기 · 도면 표 맞춤만(파이썬 이식 없음).
도면 문서(`structure/<열>.json`) 맨 위 `연결표` = `[{연결, 시트, 범위, 글자크기, 값}]` — 도면 표
`shapeData.style.연결` 이 `연결` 과 같은 표를 면적 [저장] 때 칸 글로 다시 적음(자리 · 크기 그대로 ·
칸 수가 바뀌면 열 너비 · 행 높이도 새로) · `값` = 마지막 칸 글(`{시트id!A1: 글}` · 다름 알림용).
도면에 그 표가 없으면 기록은 그대로 둠. 층 = `store.at(층 폴더)` · 권한은 부르는 쪽.
여러 장(82-3) — 연결표 줄 `장` = 표가 놓인 장id(1장 = 빈 값) · 맞춤은 모든 장(안 열린 장도 서버가).
"""
from __future__ import annotations
import json
import logging
from typing import Any
from common_util import common_util_spreadsheet as sheet_node
from common_util.common_util_json import atomic_write_json
from M02_MasterTemplete import M02_MasterTemplete_Store as store
from M02_MasterTemplete.M02_Template_Layers import drawing_sheets
logger = logging.getLogger(__name__)
LINKS = "연결표"
DROPPED = "지운연결"
# common_util_spreadsheet_node.ts `NAME_GONE` 과 같은 글자 — 이름이 지워졌을 때 까닭
NAME_GONE = "이름없음"
def to_table(
column: str, sheet: str, rng: str, font: float | None = None, name: str | None = None
) -> dict[str, Any]:
"""그 열 면적산출(저장본) 시트 · 범위(또는 `name` 이 있으면 그 이름이 지금 가리키는 범위) →
`{표, 시트, 범위, 값}` — 저장 안 함 · 못 바꾸면 400(이름이 지워졌으면 「이름없음」)."""
book = store.read("area", column)["문서"]
try:
got = sheet_node.area_tables(
[{"book": book, "시트": sheet, "범위": rng, "이름": name, "글자크기": font}]
)
except sheet_node.RecalcError as e:
raise store.StoreError(503, str(e)) from e
if not got[0].get("ok"):
raise store.StoreError(400, got[0].get("까닭"))
return {k: got[0][k] for k in ("표", "시트", "범위", "값")}
def define_name(column: str, sheet: str, rng: str, name: str) -> None:
"""처음 도면으로 보낼 때(T 번호 정해진 뒤) 면적 시트에 보낸 범위 이름을 더함/갱신(104-1) —
`applyLinked`(행열 넣기 · 지우기)가 이 이름을 그대로 따라가게 함."""
book = store.read("area", column)["문서"]
try:
got = sheet_node.define_names([{"book": book, "시트": sheet, "범위": rng, "이름": name}])
except sheet_node.RecalcError as e:
raise store.StoreError(503, str(e)) from e
if not got[0].get("ok"):
raise store.StoreError(400, got[0].get("까닭"))
path = store._path("area", column)
with store._LOCK:
atomic_write_json(path, got[0]["book"]) # 디스크 글 자체가 책(`store.read` 의 "문서")
def unlink(column: str, 연결: str) -> None:
"""이름표 메뉴 [보내기 취소](104-3) — 연결표에서 그 줄만 빼고 저장 · 도면 표(CAD 엔티티)는 그대로 남음
(지우기는 도면에서 — `withLinks` 가 다음 저장 때 표 없는 기록을 거둠)."""
path = store._path("structure", column)
if not path.is_file():
return
with store._LOCK:
doc = json.loads(path.read_bytes())
rows = doc.get(LINKS) or []
kept = [x for x in rows if x.get("연결") != 연결]
if len(kept) == len(rows):
return
doc[LINKS] = kept
atomic_write_json(path, doc)
def _style_link(entity: Any) -> Any:
if not isinstance(entity, dict) or entity.get("type") != "Table":
return None
return ((entity.get("shapeData") or {}).get("style") or {}).get("연결")
def refresh_drawing(column: str) -> list[str]:
"""지금 층 그 열 도면(모든 장)의 연결표마다 칸 글 다시 · 마지막 값 갱신 → 칸을 간 표의 `연결` 목록.
이름(104-1)이 있으면 그 이름이 지금 가리키는 범위로 `범위` 도 갱신 — 이름이 지워졌으면(범위가
통째로 지워짐) 그 연결을 끊고(도면 표는 남음) `지운연결` 목록에 적어 다음에 열 때 알림."""
try:
drawing, area = store._path("structure", column), store._path("area", column)
except store.StoreError:
return []
if not drawing.is_file() or not area.is_file():
return []
doc = json.loads(drawing.read_bytes())
links = [x for x in doc.get(LINKS) or [] if isinstance(x, dict) and x.get("연결")]
if not links:
return []
book = json.loads(area.read_bytes())
asks = [
{
"book": book,
"시트": x.get("시트"),
"범위": x.get("범위"),
"이름": x.get("이름"),
"글자크기": x.get("글자크기"),
}
for x in links
]
try:
results = sheet_node.area_tables(asks)
except sheet_node.RecalcError as e:
logger.warning("도면 표 맞춤 건너뜀 %s — %s", column, e)
return []
entities = [e for _, sheet in drawing_sheets(doc) for e in sheet.get("entities") or []]
done: list[str] = []
kept: list[dict[str, Any]] = []
dropped: list[str] = []
for link, got in zip(links, results):
if not got.get("ok"):
if link.get("이름") and got.get("까닭") == NAME_GONE:
dropped.append(link["연결"]) # 이름이 통째로 지워짐 — 연결만 끊음(도면 표는 남김)
else:
kept.append(link) # 그 밖 실패(시트 지움 등)는 다음 저장 때 다시 시도
continue
kept.append(link)
link["범위"] = got["범위"] # 이름이 지금 가리키는 범위로(104-1)
link["값"] = got["값"]
new = got["표"]["shapeData"]
for entity in entities:
if _style_link(entity) != link["연결"]:
continue
shape = entity["shapeData"]
if (len(shape.get("rowHeights") or []), len(shape.get("columnWidths") or [])) != (
len(new["rowHeights"]),
len(new["columnWidths"]),
):
shape["rowHeights"], shape["columnWidths"] = new["rowHeights"], new["columnWidths"]
shape["cells"] = new["cells"]
done.append(link["연결"])
if dropped:
doc[LINKS] = kept
doc[DROPPED] = sorted(set(doc.get(DROPPED) or []) | set(dropped))
elif len(kept) != len(links):
doc[LINKS] = kept
with store._LOCK:
atomic_write_json(drawing, doc)
return done