Merge remote-tracking branch 'origin/sub_laptop_2' into sub_laptop_4

This commit is contained in:
2026-09-25 09:49:11 +09:00
23 changed files with 1121 additions and 410 deletions
@@ -1,6 +1,7 @@
{
"format": 6,
"source": "00_templete_A1.dxf (남의 프로젝트 자료 제거 · 플레이스홀더화)",
"drawing_area": [42, 47, 812, 567],
"entities": [
{
"id": "4afa84ae-9c15-50ec-8a76-db87d04d6311",
+2 -4
View File
@@ -12,7 +12,7 @@ import pytest
from B07_DesignDetail.B07_DesignDetail_Engine_Cad import DRAWING_FORMAT, FRAME_LAYER_ID
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Cover import build_cover_drawing
TEMPLATE = Path("resources/template_2dDrawing/00_template_cover.json")
TEMPLATE = Path("resources/master_template/drawing/00_template_cover.json")
# A1 도각 템플릿과 같은 재단 표식 원점 — 두 도면이 같은 종이 좌표계를 써야 한다.
PAPER = (-5.05, -5.04, 834.95, 588.96)
@@ -62,9 +62,7 @@ def test_도면은_잠금_도각_레이어로_나온다():
assert {e["layerId"] for e in drawing["entities"]} == {FRAME_LAYER_ID}
layers = {layer["id"]: layer for layer in drawing["layers"]}
assert layers[FRAME_LAYER_ID]["isLocked"] is True
assert any(not layer["isLocked"] for layer in drawing["layers"]), (
"덧그릴 비잠금 도면층이 없다"
)
assert any(not layer["isLocked"] for layer in drawing["layers"]), "덧그릴 비잠금 도면층이 없다"
@pytest.mark.parametrize(
+106
View File
@@ -0,0 +1,106 @@
"""M02 도면 양식 — 작도 영역 칸 · B07 도각 층(프로젝트 작업본 → 시스템) (PLAN 10-3)."""
import json
from B07_DesignDetail import B07_DesignDetail_Engine_Template as engine
from M02_MasterTemplete import M02_Template_Layers as layers
A1 = engine.A1_TEMPLATE
def _project_template(project_root, document):
path = engine.project_template_path(project_root)
path.parent.mkdir(parents=True)
path.write_text(json.dumps(document), encoding="utf-8")
return path
def _entities(project_root):
engine.use_project_templates(project_root)
try:
return engine.template_entities()
finally:
engine.use_project_templates(None)
def test_system_a1_reads_drawing_area():
engine.use_project_templates(None)
assert engine.drawing_area() == (42.0, 47.0, 812.0, 567.0)
assert len(engine.template_entities()) == 51
def test_missing_area_falls_back_to_a1(tmp_path):
_project_template(tmp_path, {"format": 6, "entities": [], "layers": []})
engine.use_project_templates(tmp_path)
try:
assert engine.drawing_area() == engine._A1_INNER
finally:
engine.use_project_templates(None)
def test_template_area_moves_content(tmp_path):
line = {
"id": "a",
"type": "Line",
"shapeData": {"startPoint": {"x": 0, "y": 0}, "endPoint": {"x": 10, "y": 0}},
}
_project_template(
tmp_path,
{"format": 6, "drawing_area": [0, 0, 400, 200], "entities": [line], "layers": []},
)
engine.use_project_templates(tmp_path)
try:
assert engine.drawing_area() == (0.0, 0.0, 400.0, 200.0)
assert engine.usable_area() == (400 * 0.96, 200 * 0.96)
# 콘텐츠 중심(500, 500) 이 작도 영역 중심(200, 100) 에 온다 — 이동량 (300, 400).
placed = engine.frame_entities("t", (490.0, 490.0, 510.0, 510.0), fit=False)
assert placed[0]["shapeData"]["startPoint"] == {"x": 300.0, "y": 400.0}
finally:
engine.use_project_templates(None)
def test_bad_area_falls_back(tmp_path):
_project_template(tmp_path, {"drawing_area": [10, 10, 5, 5], "entities": []})
engine.use_project_templates(tmp_path)
try:
assert engine.drawing_area() == engine._A1_INNER
finally:
engine.use_project_templates(None)
def test_old_project_reads_system(tmp_path):
# 작업본이 없는 옛 프로젝트 — 시스템 양식으로 떨어진다.
assert len(_entities(tmp_path)) == 51
assert engine.is_project_template_customized(tmp_path) is False
def test_save_reset_on_seeded_project(tmp_path):
layers.seed_project(tmp_path)
assert engine.project_template_path(tmp_path).is_file()
assert engine.is_project_template_customized(tmp_path) is False
# [완료] — 작업본에만 쓴다 · 작도 영역은 이어 받는다 · `_initial/` · 시스템은 그대로.
path = engine.save_project_template(tmp_path, [])
saved = json.loads(path.read_text(encoding="utf-8"))
assert saved["drawing_area"] == [42, 47, 812, 567]
assert _entities(tmp_path) == []
assert engine.is_project_template_customized(tmp_path) is True
initial = layers.template_path(layers.initial_dir(tmp_path), "drawing", A1)
assert len(json.loads(initial.read_text(encoding="utf-8"))["entities"]) == 51
assert (
len(json.loads(engine.system_template_path().read_text(encoding="utf-8"))["entities"]) == 51
)
# [기본 도각으로] — `_initial/` 을 작업본에 덮어쓴다.
assert engine.reset_project_template(tmp_path) is True
assert len(_entities(tmp_path)) == 51
assert engine.is_project_template_customized(tmp_path) is False
def test_reset_on_old_project_drops_to_system(tmp_path):
engine.save_project_template(tmp_path, [])
assert _entities(tmp_path) == []
assert engine.reset_project_template(tmp_path) is True
assert not engine.project_template_path(tmp_path).exists()
assert len(_entities(tmp_path)) == 51
assert engine.reset_project_template(tmp_path) is False
@@ -0,0 +1,58 @@
"""M02 도면 양식 서버 길 — 자리표 키 목록 · 도각 파일 불러오기 (PLAN 10-3)."""
import io
import json
import re
from pathlib import Path
from fastapi import FastAPI
from fastapi.testclient import TestClient
from M02_MasterTemplete.M02_MasterTemplete_Router_Drawing import DRAWING_FIELDS, router
app = FastAPI()
app.include_router(router)
client = TestClient(app)
def test_fields_cover_system_placeholders():
# 시스템 도면 양식에 박힌 자리표는 전부 키 목록에 있다 — 없으면 화면이 모르는 칸이 생긴다.
keys = {key for key, _, _ in DRAWING_FIELDS}
used = set()
for path in Path("resources/master_template/drawing").glob("00_*.json"):
text = json.dumps(json.loads(path.read_text(encoding="utf-8")), ensure_ascii=False)
used |= {match.strip() for match in re.findall(r"\{\{\s*([^}]+?)\s*\}\}", text)}
assert used and used <= keys, used - keys
def test_fields_without_project():
response = client.get("/api/m02/drawing-fields")
assert response.status_code == 200
body = response.json()
assert [item["key"] for item in body["fields"]] == [key for key, _, _ in DRAWING_FIELDS]
assert body["values"] == {}
def test_import_rejects_other_files():
response = client.post(
"/api/m02/drawing-import", files={"file": ("a.txt", b"hello", "text/plain")}
)
assert response.status_code == 400
assert "DXF" in response.json()["message"]
def test_import_dxf():
import ezdxf
document = ezdxf.new()
document.modelspace().add_line((0, 0), (100, 50))
stream = io.StringIO()
document.write(stream)
response = client.post(
"/api/m02/drawing-import",
files={"file": ("frame.dxf", stream.getvalue().encode("utf-8"), "application/dxf")},
)
assert response.status_code == 200, response.text
body = response.json()
assert body["entity_count"] == 1
assert body["drawing"]["entities"][0]["type"] == "Line"