Merge remote-tracking branch 'origin/main_laptop_1' into main_desktop_1
This commit is contained in:
@@ -50,7 +50,45 @@ from common_util.common_util_excavation import (
|
||||
SCALE_MM_PER_M = 40.0
|
||||
|
||||
#: 이 그림을 그리는 종류. 옹벽·집수정처럼 단면이 다른 것은 아직 그리지 않는다.
|
||||
FIGURE_TYPE_IDS: frozenset[str] = frozenset({"masonry_wet", "masonry_dry", "boulder_masonry"})
|
||||
#: ⚠ **큰돌쌓기(`boulder_masonry`)를 뺐다**(2026-09-09). 여기 있었지만 그림이 두께를
|
||||
#: **뒷길이**에서 내는데 큰돌쌓기는 규격 축이 **직경**(`stone_cm`)이라 **영영 못 그렸다** —
|
||||
#: 목록에만 있고 결과는 늘 빈 그림이었다. 품셈 **13-6(직경) ↔ 13-4(뒷길이)** 로 축이
|
||||
#: 갈리는 자리이고 수량 쪽에서 이미 갈라 둔 그것이다(`EXPANDERS` 주석).
|
||||
#: ⇒ 「직경에서 두께를 내는 식」은 **도메인 판단이라 사용자 몫** — 계획서 4-12 답 대기 줄.
|
||||
FIGURE_TYPE_IDS: frozenset[str] = frozenset({"masonry_wet", "masonry_dry"})
|
||||
|
||||
#: **전면 기울기 판정** 대상 — 그림 대상과 **다르다**. 큰돌쌓기는 그림은 못 그려도
|
||||
#: 기울기는 판정된다(품셈 13-6 「1:0.3 이상」). 한 목록으로 묶어 두었더니 큰돌쌓기를
|
||||
#: 그림에서 뺄 때 **장 제목의 「1:0.3」까지 사라졌다**(2026-09-09 화면 실측에서 잡음).
|
||||
SLOPE_TYPE_IDS: frozenset[str] = frozenset({"masonry_wet", "masonry_dry", "boulder_masonry"})
|
||||
|
||||
#: 그림이 못 서는 장에 **까닭을 적는다** — 빈 자리를 그냥 두면 사용자가 「고장」으로 읽는다
|
||||
#: (2026-09-09 사용자 지시). 「무엇을 받아야 서는지」까지 적는다.
|
||||
_NO_FIGURE_REASONS: dict[str, str] = {
|
||||
"boulder_masonry": (
|
||||
"그림 없음 — 큰돌쌓기는 규격이 **직경**이라 두께를 낼 식이 아직 없습니다"
|
||||
"(뒷길이로 내는 돌쌓기 식과 축이 다름 · 사용자 확정 대기)"
|
||||
),
|
||||
"retaining_wall": "그림 없음 — 옹벽은 단면이 달라 아직 그리지 않습니다(표준도 3단계)",
|
||||
}
|
||||
_NO_FIGURE_DEFAULT = "그림 없음 — 이 형식은 아직 단면을 그리지 않습니다"
|
||||
|
||||
|
||||
def figure_reason(sheet: dict[str, Any]) -> str | None:
|
||||
"""그림이 **안 서는 까닭** 한 줄. 서는 장이면 `None`.
|
||||
|
||||
⚠ 「무엇을 받아야 서는지」까지 적는다 — 뒷길이만 고르면 서는 장과, 식 자체가 없는 장은
|
||||
사용자가 할 일이 다르다.
|
||||
"""
|
||||
type_id = str(sheet.get("type_id") or "")
|
||||
if type_id not in FIGURE_TYPE_IDS:
|
||||
return _NO_FIGURE_REASONS.get(type_id, _NO_FIGURE_DEFAULT)
|
||||
if float(sheet.get("height_m") or 0.0) <= 0:
|
||||
return "그림 없음 — 높이가 없습니다(제원에서 높이를 넣으면 그림이 섭니다)"
|
||||
if back_length_cm(sheet) is None:
|
||||
return "그림 없음 — **뒷길이를 고르면 그림이 섭니다**(두께가 뒷길이에서 나옵니다)"
|
||||
return None
|
||||
|
||||
|
||||
_LABEL_FONT = 4.0
|
||||
_DIM_FONT = 3.4
|
||||
|
||||
@@ -22,6 +22,7 @@ from typing import Any
|
||||
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad import (
|
||||
DRAWING_FORMAT,
|
||||
_text_entity,
|
||||
TABLE_LABEL_COLOR,
|
||||
TABLE_LINE_COLOR,
|
||||
TABLE_VALUE_COLOR,
|
||||
@@ -32,7 +33,7 @@ from B07_DesignDetail.B07_DesignDetail_Engine_Cad import (
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_StandardFigure import (
|
||||
SCALE_MM_PER_M as FIGURE_SCALE_MM_PER_M,
|
||||
)
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_StandardFigure import build_figure
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_StandardFigure import build_figure, figure_reason
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Template import usable_bbox
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -58,6 +59,8 @@ def sheet_drawing_id(number: int, total: int) -> str:
|
||||
_COLUMN_WIDTHS: tuple[float, ...] = (90.0, 535.0, 70.0, 40.0)
|
||||
_ROW_HEIGHT = 9.0
|
||||
_FONT = 4.2
|
||||
#: 그림이 없는 장에 까닭을 적는 글자 크기(㎜) — 표 글자보다 살짝 작게.
|
||||
_NOTE_FONT = 4.0
|
||||
#: 표 사이 간격 — 장이 여럿일 때 서로 붙어 보이지 않게.
|
||||
_GAP = 14.0
|
||||
#: 그림을 왼쪽 여백에서 조금 들여 놓는다 — 치수 글자가 왼쪽으로 나가기 때문.
|
||||
@@ -181,6 +184,24 @@ def build_standard_drawing(drawing_id: str, label: str, payload: dict[str, Any])
|
||||
if figure:
|
||||
entities.extend(figure)
|
||||
top -= used + _GAP
|
||||
else:
|
||||
# ⚠ **빈 자리를 그냥 두지 않는다** — 사용자가 「고장인가」로 읽는다(2026-09-09
|
||||
# 사용자 지시). 왜 안 서는지와 **무엇을 받아야 서는지**를 표 위에 한 줄 적는다.
|
||||
reason = figure_reason(sheet)
|
||||
if reason:
|
||||
entities.append(
|
||||
_text_entity(
|
||||
f"{drawing_id}:sheet:{index}:nofigure",
|
||||
reason.replace("**", ""),
|
||||
x0,
|
||||
top - _NOTE_FONT,
|
||||
STANDARD_LAYER_ID,
|
||||
_NOTE_FONT,
|
||||
TABLE_LABEL_COLOR,
|
||||
"left",
|
||||
)
|
||||
)
|
||||
top -= _NOTE_FONT + _GAP
|
||||
grid = _sheet_rows(sheet)
|
||||
entities.append(
|
||||
table_entity(
|
||||
|
||||
@@ -79,11 +79,12 @@ def sheet_key(structure: dict[str, Any]) -> str:
|
||||
def _slope_or_none(structure: dict[str, Any]) -> float | None:
|
||||
"""돌쌓기 계열이면 판정된 전면 기울기, 아니면 `None`. 판정은 그림 모듈이 가진 한 벌."""
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_StandardFigure import (
|
||||
FIGURE_TYPE_IDS,
|
||||
SLOPE_TYPE_IDS,
|
||||
slope_of,
|
||||
)
|
||||
|
||||
if str(structure.get("type_id") or "") not in FIGURE_TYPE_IDS:
|
||||
# ⚠ **그림 대상 목록을 쓰지 않는다** — 큰돌쌓기는 그림은 못 그려도 기울기는 판정된다.
|
||||
if str(structure.get("type_id") or "") not in SLOPE_TYPE_IDS:
|
||||
return None
|
||||
return slope_of(structure)[0]
|
||||
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
# 계획서 0·3·4장 완료분 근거 (이관 2026-09-09)
|
||||
|
||||
> 계획서에는 결과 한두 줄만 남기고 **조사·검증 원문을 여기로 통째로** 옮긴 것
|
||||
> (2026-09-09 사용자 확정 규칙). 원문 그대로이며 손대지 않았음.
|
||||
|
||||
- [x] **재계산 중간 취소 — 만들지 않기로 닫음** (2026-09-09 밤 재판정, 커밋 `5d4473b4`)
|
||||
**잰 값** — 서버 계측 로그 네 번: **87.3 · 90.0 · 93.9 · 95.4초**(평균 91.7초).
|
||||
그중 **배수·관·측점 재생성이 80~86초(90 % 넘음)**, 노선 풀이는 3초대임.
|
||||
293초 → 90초로 이미 3분의 1이 됐음(0-11).
|
||||
|
||||
**왜 안 만드나 — 취소가 「반쯤 부순 상태」를 만드는 단추가 되기 때문임.**
|
||||
· 체인이 돌기 **전에** 관 지점을 이미 지움(`clear_pipe_points`) — 취소해도 안 돌아옴
|
||||
· 체인이 끊기면 **노선만 새것 · 종횡단은 옛것**으로 어긋남(2026-09-06 실측 사고:
|
||||
배수관 측점 9 → 0). 지금 코드도 그때는 계획노선을 되돌리라고 사유를 올려 보냄
|
||||
· 90초는 기다릴 수 있는 길이고, 급하면 **취소보다 그 80초 한 곳을 줄이는 것**이 이득임
|
||||
|
||||
**대신 한 것** — 대기 안내가 「몇 분 걸립니다」라는 **옛 문구**였음. **「1분 반쯤 걸립니다
|
||||
(N초 지남)」** 로 고쳐 **경과 시간이 1초마다 도는 것**을 보임(멈춘 것인지 도는 것인지 알 수 있게).
|
||||
⚠ **다시 열 조건** — 배수·관·측점 재생성이 더 커져 **3분을 넘거나**, 체인을 단계마다
|
||||
되돌릴 수 있게 되면 그때 다시 볼 것.
|
||||
|
||||
---
|
||||
|
||||
- [x] **직행 `/files` 가드 — 이미 붙어 있었음** (2026-09-09 확인)
|
||||
`B03_FileInput_Router.py:212` 가 분석중 차단, `:249` 가 같은 이름 옛 행 내림(`supersede`)을
|
||||
부름. 주석에 「이 갈래만 빠져 있어 분석 2개가 같은 산출물 경로에서 부딪혔다(2026-09-08)」로
|
||||
까닭까지 적혀 있음. **세 갈래가 같아졌음.**
|
||||
~~[잔손질] 두 줄이면 세~~
|
||||
갈래가 같아짐. **가장 싸고 효과 큼.**
|
||||
|
||||
---
|
||||
|
||||
- [x] **purge 전 확인 단계 — 이미 있었음** (2026-09-09 확인)
|
||||
`B03_FileInput_Router_Helpers.py:69` `_confirm_replace_response` 가 **409** 로 「지우고
|
||||
진행할까?」를 묻고 **무엇이 지워지는지 함께 알림**. 사람이 고르면 `confirm_replace=True`.
|
||||
⇒ 0-8 은 셋 중 둘이 닫혔고 **남은 것은 업로드 잠금 표시 하나**임.
|
||||
~~[반나절] 「이 프로젝트에 설계 산출물이 있음. 지우고 새~~
|
||||
자료로 갈까?」. 지금은 말없이 지움.
|
||||
|
||||
---
|
||||
|
||||
- [x] **프로젝트 단위 업로드 잠금 표시** — **2026-09-09 밤 끝냄**(커밋 `018ebe7f`).
|
||||
막는 것은 이미 있었고(`is_analysis_running`) 없던 것은 **막힌 까닭을 사람에게 보이는 것**이었음.
|
||||
- 분석을 띄우는 **경로 넷 모두** 시작한 사람을 `params.started_by` 에 담음
|
||||
- `analysis_lock_owner` 가 그 id 로 이름을 붙임 — 옛 자료는 **이름 없이 잠금만** 돎(막는 것이 먼저)
|
||||
- `/upload-overview` 에 `analysis_lock` 을 실어 B03 화면이 띠로 보임
|
||||
|
||||
| 잰 것 | 결과 |
|
||||
|---|---|
|
||||
| API 칸이 서나 | ⭕ `analysis_lock` — 도는 분석이 없어 지금은 `null` (두 프로젝트 200 확인) |
|
||||
| 화면 띠가 뜨나 | ⭕ 「엄상돈 님이 올린 자료를 분석하는 중입니다 — 끝난 뒤에 새 자료를 올릴 수 있습니다」 경고색 |
|
||||
| 이름이 없으면 | ⭕ 「다른 자리에서 올린 자료를…」 로 뜸(잠금은 그대로) |
|
||||
|
||||
⚠ **실제 분석이 도는 화면은 아직 못 봄** — 지금 도는 분석이 없어 응답을 잠깐 가로채 확인하고
|
||||
곧바로 되돌렸음. **다음 업로드 때 실물로 볼 것.** 시험 넷 추가, 전체 **1211 통과 · 실패 0**.
|
||||
|
||||
|
||||
---
|
||||
|
||||
- [x] **[답 나옴 · 보류 확정 — 2026-09-09 밤 닫음]** **도수로·절토사면 배수로는 품셈에 그 공종이 없음** (2026-09-08).
|
||||
「수로」로 전수 검색해도 나오는 것이 위 셋뿐임. 지장목제거와 같은 모양 —
|
||||
**수량은 서는데 단가 공종이 없는 것**.
|
||||
⇒ **2026-09-09 사용자 확정 5차 3번 — 「나중에 실무 문서 첨부. 임의로 만드는 건 지금은
|
||||
무의미」로 보류.** B06 은 **배치만 받고** 단가·일위대가를 만들지 않음. 지금처럼
|
||||
사유(`pending_user`)로 두는 것이 확정된 동작임 — **더 할 일 없음. 문서가 오면 그때 엶.**
|
||||
⇒ **미체크로 둘 까닭이 없어 닫음**(2026-09-09 밤 재훑기). 문서가 오면 4-12 쪽에서 새로 열 것.
|
||||
|
||||
|
||||
---
|
||||
|
||||
- [x] **[잔손질]** 카드에서 측구를 **껐다 켰다** 해서 방금 가른 선택/결과가 화면에서 도는지.
|
||||
**2026-09-09 밤 화면 실측으로 끝냄 — 안 돌던 까닭을 찾아 고쳤음.**
|
||||
|
||||
**⚠ 왜 안 돌았나 — 「칸 하나」가 통로에서 빠져 있었음**
|
||||
|
||||
측구 선택(`ditch_choice`)이 **캐시(`CrossDesignChoice`)에 없어** 눌린 값이 그 자리에서
|
||||
버려졌음. 브라우저 재계산은 저장분 결과만 읽었고, [저장] patch·서버 스키마에도 칸이 없었음.
|
||||
⇒ 통로 넷 + 스키마를 이었음(`5907de85`). 화면 코드는 리로드, 스키마는 백엔드 재시작으로 반영.
|
||||
|
||||
**잰 것 (측점 1+0.0 = 20.0m, 프로젝트 랩탑_보조 route 182)**
|
||||
|
||||
| 잰 것 | 고치기 전 | 고친 뒤 |
|
||||
|---|---|---|
|
||||
| 캐시에 담기나 | ❌ `ditch_choice` 칸 자체가 없음 | ⭕ `"ditch_choice": false` 로 담김 |
|
||||
| 끄면 사라지나 | ❌ `aria-pressed` 가 `true` 그대로 | ⭕ `false` · **절토 계 3.33 → 1.78㎡** |
|
||||
| 다시 누르면 켜지나 | ❌ 안 돎 | ⭕ `true` · **절토 계 1.78 → 3.90㎡** |
|
||||
| 「자동」으로 풀리나 | — | ⭕ **엔진이 자동값과 같은 선택은 `null` 로 되돌림**(시험 `test_자동과_같은_선택은_자동으로_푼다`) |
|
||||
| 계획고를 바꾸면 자동이 다시 도나 | — | ⭕ **구조로 잠김** — 안 만진 측점은 캐시에 선택이 없어 자동 판정을 그대로 탐 |
|
||||
|
||||
시험 `test_b06_ditch_choice.py` 에 통로 넷 + patch 병합 잠금 한 건 추가(전체 **1198 통과 · 실패 0**).
|
||||
조작으로 바꾼 값은 **캐시를 걷고 리로드해 되돌렸음** — 절토 계 3.33㎡ 로 복귀 확인, [저장]·[확정] 안 눌렀음.
|
||||
|
||||
**✅ 곁들여 본 것 — 카드를 만졌다 되돌리면 값이 저장분과 다름 (2026-09-09 밤 끝냄, 커밋 `2eb2c972`)**
|
||||
|
||||
**거울 문제가 아니었음.** 같은 입력을 주면 파이썬·TS 가 같은 값을 냄 —
|
||||
측점 20.0m 에서 **표준단면 측구 상폭 0.69 기준 절토 3.328 · 0.9 기준 3.899** 로 둘 다 같음.
|
||||
갈린 것은 산식이 아니라 **입력(표준단면)** 이었음.
|
||||
|
||||
⚠⚠ **더 큰 것이 나왔음 — 저장분 한 줄 안에 두 표준단면이 섞여 있었음.**
|
||||
|
||||
| 잰 것 (측점 20.0m) | 값 |
|
||||
|---|---|
|
||||
| 저장분 절토 계 | 3.328㎡ — **옛 상폭 0.69 기준** |
|
||||
| 저장분 측구 기하·면적 | 상폭 **0.9** · 0.18㎡ — **새 상폭 기준** |
|
||||
| 저장분 옵션의 표준단면 | 상폭 **0.9** (사용자가 고쳐 저장한 값) |
|
||||
|
||||
까닭 — 브라우저는 **만진 측점만** 다시 계산하는데 [저장]은 **전 측점 면적**을 실어 보냄.
|
||||
그래서 표준단면을 고쳐도 안 만진 측점은 **옛 면적 그대로** 정본이 됨(수량이 갈리는 자리).
|
||||
|
||||
고친 것 — ① [저장]·[확정]이 면적을 모으기 **전에** 전 측점을 다시 계산함
|
||||
② 표준단면 패널에 **「고친 값이 측점에 아직 안 실렸음」 알림**(언제 따라오는지 함께 적음).
|
||||
화면 실측 — 패널 값을 저장분과 다르게 두면 알림이 뜨고, 같으면 사라짐(초안은 원래대로 되돌려 놓았음).
|
||||
시험 둘 추가, 전체 **1205 통과 · 실패 0**.
|
||||
⚠ **[저장] 실측은 아직 못 했음** — 사용자가 표토 축 확정 전까지 저장을 멈춰 둔 상태라
|
||||
저장 버튼을 안 눌렀음. 잠금이 풀리면 「표준단면 고치고 저장 → 전 측점 면적이 따라오나」를 볼 것.
|
||||
|
||||
|
||||
---
|
||||
|
||||
- [x] **[잔손질]** 확정 ② 후반 「사용자가 값을 바꿀 수 있게」 — 표준도 입력 칸에 **상부·하부 두께**를
|
||||
더할 것(비우면 식). ⑨ 야면석 계수 · ⑩ 채움 강도와 같은 자리.
|
||||
**2026-09-09 밤 끝냄** — 등록부 돌쌓기 계열 다섯에 칸을 두고 표준도 제원 폼·저장·수량까지 이었음.
|
||||
큰돌쌓기는 두께 식이 달라 뺐음(그 장에는 칸이 안 뜸).
|
||||
|
||||
| 잰 것 (랩탑_보조 · 표준도 3장 돌쌓기(찰) H=2.5) | 결과 |
|
||||
|---|---|
|
||||
| 칸이 뜨나 | ⭕ 「상부 두께(m)」·「하부 두께(m)」 — 자리표시 「비우면 식」 |
|
||||
| 저장되나 | ⭕ 상부 0.60 저장 → 「2개소에 반영했습니다」 · 정본 `thickness_top_m: 0.6` |
|
||||
| 수량에 닿나 | ⭕ **입적 24.375 → 20.625㎥** · 근거 「상부 0.60(사용자 입력) · 하부 1.05(상부 + 0.3×(H−1))」 |
|
||||
| 비우면 식으로 돌아오나 | ⭕ 칸을 비워 저장 → 정본에서 칸이 지워지고 **입적 24.375㎥ 복귀** |
|
||||
|
||||
⚠ 빈 칸은 「안 정함」임 — 자동값을 저장에 박지 않음. 상부만 넣으면 하부는 그 위에서 식을 이음.
|
||||
시험 다섯 추가(`test_b08_coeff_and_strength.py`), 전체 **1203 통과 · 실패 0**. 커밋 `3a64bb5b`.
|
||||
|
||||
|
||||
---
|
||||
|
||||
- [x] **끝남(2026-09-09, `0ff26d43`)** — `spoil_bank` 두 줄(`side`·`extra_distance_m`)을 뺐음.
|
||||
**덮여 있었음** — 정본에 `default_basis` 로 까닭이 적혀 있어 목록 없이도 통과.
|
||||
- 자체검증 — 두 줄을 지우고 `resources/tester/` 전체 실행: **1183 통과 / 22 건너뜀 / 실패 0**.
|
||||
그 자리에 「앞으로도 목록에 넣지 말 것」을 적어 둠.
|
||||
- ⚠ **같은 상태가 13 개 더 있음** — `revetment`·`soil_guard`·C군 `side` 계열
|
||||
(`revetment.tiers`·`lift_m`·`shift_m`·`side` · `soil_guard.tiers`·`lift_m`·`shift_m`·
|
||||
`stone_supply`·`side` · `masonry_wet/dry`·`boulder_masonry`·`retaining_wall`.`side`).
|
||||
빼도 통과하나 **주석 덩어리가 얽혀 있어** 이번 잔손질 범위를 넘음. 다음에 그 파일을
|
||||
손댈 때 함께 걷을 자리.
|
||||
- ⚠ 「혼자 지우지 말 것」 단서는 **풀렸음** — 시험이 `resources/tester`(git 안)로 옮겨져
|
||||
창끼리 함께 건너감.
|
||||
|
||||
|
||||
---
|
||||
|
||||
- [x] **날개벽·집수정 원단위** — **2026-09-09 밤 끝냄**(커밋 `0747979c`).
|
||||
ⓘ **집수정 Ø800 은 이미 표에 있었음**(`pipe_inlet_basin` □형·콘크리트·800) — 다시 안 만듦.
|
||||
날개벽 다섯 형식을 원본 탭 소계 그대로 옮겼고, **고르는 칸**까지 이었음.
|
||||
|
||||
| 형식 | 터파기 | 되메우기 | 잔토 | 기초잡석 | 콘크리트(저판+벽체) | 거푸집(저판+벽체) |
|
||||
|---|---|---|---|---|---|---|
|
||||
| Ø800 A-TYPE | 13.104 | 6.504 | 6.61 | 1.152 | 2.0595 | 유로폼 12.5376 |
|
||||
| Ø800 C-TYPE | 7.7269 | 4.7148 | 3.0122 | 0.6821 | 1.109 | 유로폼 6.2309 |
|
||||
| Ø1000 A-TYPE | 24.39 | 10.478 | 13.912 | 1.6 | 2.891 | 합판(4회) 18.255 |
|
||||
| Ø1000 C-TYPE | 13.7179 | 7.9134 | 5.8046 | 1.0271 | 1.9109 | 합판(4회) 10.7220 |
|
||||
| Ø800 A+집수정 | 16.256 | 8.44 | 7.816 | 1.152 | 2.6395 | 합판(4회) 18.3376 |
|
||||
|
||||
- **거푸집 종류를 뭉개지 않았음** — 원본 탭에서 직접 읽어 Ø800 A·C 는 유로폼,
|
||||
Ø1000 과 A+집수정은 합판(4회). 단가가 갈리는 자리라 이름을 그대로 둠.
|
||||
- ⚠ **철근·면목은 원본 날개벽 탭에 없음** — 지어내지 않았고 시험이 그것을 지킴.
|
||||
- ⚠ **겹쳐 세는 자리** — 「A-TYPE+집수정」은 집수정을 품은 형식이라 집수정 형식도 차 있으면
|
||||
**사유로 알림**(값은 안 고침). 원단위표 `double_count_rules` 에도 규칙으로 적었음.
|
||||
- 등록부·배치 폼에 「관보호공 날개벽」 칸(A-TYPE·C-TYPE·A-TYPE+집수정, 비우면 안 셈).
|
||||
|
||||
검증 — 화면에서 배수관 폼에 칸이 뜨고 선택지 셋 확인. 엔진을 돌려 **날개벽 줄이 따로 서고**
|
||||
터파기 13.104 · 콘크리트 2.0595 · 유로폼 12.5376 이 나오는 것까지 봄. 시험 일곱 추가,
|
||||
전체 **1218 통과 · 실패 0**. ⚠ 정본 저장까지는 [저장]을 눌러야 하는데 지금 멈춰 둔 상태라
|
||||
**화면에서 고른 값이 수량에 실리는 것은 잠금 풀린 뒤에 볼 것.**
|
||||
|
||||
---
|
||||
|
||||
**⭐ 2026-09-09 밤 — 지금 무엇이 서 있나 (도면 알맹이로 전수 확인, 랩탑 메인)**
|
||||
|
||||
| 장 | 표(하단표) | 그림(상단) | 까닭 |
|
||||
|---|---|---|---|
|
||||
| 1장 큰돌쌓기 H=2.5 | ⭕ | ❌ | **규격 축이 직경**(`stone_cm` 60~80)인데 그림이 **뒷길이**(`back_len_cm`)를 찾음 |
|
||||
| 2장 돌쌓기(메) H=2 뒷길이 35㎝ | ⭕ | ⭕ **섬** — 폴리선 2 · 선 1 · 글자 11 | 뒷길이가 정해져 있음 |
|
||||
| 3장 돌쌓기(찰) H=2.5 | ⭕ | ❌ | **뒷길이를 아직 안 골랐음**(제원이 `기초유`·H 뿐) |
|
||||
| 4장 옹벽 H=2 | ⭕ | ❌ | 그림 대상 아님 — `FIGURE_TYPE_IDS` 는 돌쌓기 계열 셋뿐 |
|
||||
|
||||
⇒ **1단계(하단표)는 네 장 다 섬** — V-9 를 ⭕ 로 닫았음(7장 참조).
|
||||
⇒ **2단계(그림)는 이미 절반 돌고 있음** — 코드(`..._Cad_StandardFigure.build_figure`)가 있고
|
||||
**뒷길이가 정해진 장에서는 실제로 그려짐.** 안 그려지는 것은 코드가 없어서가 아니라
|
||||
**제원이 없어서**임(뒷길이를 고르면 그 장도 바로 섬).
|
||||
|
||||
**⚠ 하나는 코드 자리가 어긋나 있음 — 큰돌쌓기.** `FIGURE_TYPE_IDS` 에는 들어 있는데
|
||||
그림이 뒷길이를 찾으므로 **직경만 있는 큰돌쌓기는 영영 못 그림**(품셈 13-6 은 직경 축,
|
||||
13-4 는 뒷길이 축 — 수량 쪽에서 이미 갈라 둔 그 자리와 같음). ⇒ **큰돌쌓기 그림은
|
||||
직경에서 두께를 내는 다른 식이 필요**하거나, 아니면 목록에서 빼고 사유를 적어야 함.
|
||||
**만들지 않고 드러내만 둠**(2단계에서 다룰 자리).
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
"""표준도 — 그림이 **안 서는 까닭을 화면에 드러낸다** (2026-09-09 사용자 지시).
|
||||
|
||||
빈 자리를 그냥 두면 사용자가 「고장인가」로 읽는다. 장마다 **왜 그림이 없는지와 무엇을
|
||||
받아야 서는지**를 표 위에 한 줄 적는다.
|
||||
|
||||
⚠ **큰돌쌓기를 그림 대상에서 뺐다** — 그림은 두께를 **뒷길이**에서 내는데 큰돌쌓기는 규격이
|
||||
**직경**이라 영영 못 그렸다(품셈 13-6 직경 ↔ 13-4 뒷길이). 식은 만들지 않는다 —
|
||||
「직경에서 두께를 내는 법」은 도메인 판단이라 사용자 몫이다.
|
||||
⚠ 그림 대상과 **기울기 판정 대상은 다른 목록**이다 — 한 벌로 묶었더니 큰돌쌓기를 그림에서
|
||||
뺄 때 **장 제목의 「1:0.3」까지 사라졌다**(화면 실측에서 잡음).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
if str(ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_StandardFigure import ( # noqa: E402
|
||||
FIGURE_TYPE_IDS,
|
||||
SLOPE_TYPE_IDS,
|
||||
figure_reason,
|
||||
)
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_StandardSheet import ( # noqa: E402
|
||||
build_standard_drawing,
|
||||
)
|
||||
from B07_DesignDetail.B07_DesignDetail_Engine_Standard_Sheet import sheet_title # noqa: E402
|
||||
|
||||
|
||||
def _sheet(type_id: str, **options) -> dict:
|
||||
return {
|
||||
"key": type_id,
|
||||
"title": type_id,
|
||||
"type_id": type_id,
|
||||
"height_m": 2.5,
|
||||
"options": {"height_m": 2.5, **options},
|
||||
"rows": [{"name": "콘크리트", "unit": "㎥", "amount": 1.0, "basis": "시험"}],
|
||||
"member_count": 1,
|
||||
"unit_label": "m당",
|
||||
}
|
||||
|
||||
|
||||
def test_뒷길이가_없으면_무엇을_고르면_되는지_적는다() -> None:
|
||||
reason = figure_reason(_sheet("masonry_wet"))
|
||||
assert reason is not None and "뒷길이" in reason and "섭니다" in reason
|
||||
|
||||
|
||||
def test_뒷길이가_있으면_까닭이_없다() -> None:
|
||||
assert figure_reason(_sheet("masonry_dry", back_len_cm=35)) is None
|
||||
|
||||
|
||||
def test_큰돌쌓기는_그림_대상에서_빠지고_까닭이_적힌다() -> None:
|
||||
assert "boulder_masonry" not in FIGURE_TYPE_IDS
|
||||
reason = figure_reason(_sheet("boulder_masonry", stone_cm="60~80"))
|
||||
assert reason is not None and "직경" in reason
|
||||
|
||||
|
||||
def test_기울기_판정은_큰돌쌓기에도_남는다() -> None:
|
||||
"""그림 대상 목록으로 판정하면 장 제목에서 「1:0.3」이 사라진다 — 목록을 갈라 둔 자리."""
|
||||
assert "boulder_masonry" in SLOPE_TYPE_IDS
|
||||
title = sheet_title(
|
||||
{"name": "큰돌쌓기", "type_id": "boulder_masonry", "height_m": 2.5, "options": {}}
|
||||
)
|
||||
assert "1:0.3" in title
|
||||
|
||||
|
||||
def test_까닭이_도면에_한_줄로_실린다() -> None:
|
||||
drawing = build_standard_drawing(
|
||||
"standard_sheet_1",
|
||||
"표준도 1장",
|
||||
{"sheets": [_sheet("retaining_wall")], "structure_count": 1},
|
||||
)
|
||||
texts = [
|
||||
(entity.get("shapeData") or {}).get("label", "")
|
||||
for entity in drawing["entities"]
|
||||
if entity.get("type") == "Text"
|
||||
]
|
||||
assert any("그림 없음" in text for text in texts), texts
|
||||
Reference in New Issue
Block a user