From bd3c48c525701d2a0f721ee1314bc12d6901c27c Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 5 Oct 2026 13:14:09 +0900 Subject: [PATCH] =?UTF-8?q?=EF=BB=BFrefactor(M02):=2046-1=20=EA=B5=AC?= =?UTF-8?q?=EC=A1=B0=EB=AC=BC=20=EC=98=B5=EC=85=98=20=EB=B3=B4=EC=9D=B4?= =?UTF-8?q?=EA=B8=B0=20=EC=BD=94=EB=93=9C=EB=A5=BC=20`M02=5FMasterTemplete?= =?UTF-8?q?/StructOptions/`=20=ED=95=9C=20=EA=B3=B3=EC=9C=BC=EB=A1=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 서버 정본 `M02_SO_Options.py` · 길 `M02_SO_Router.py`(고쳐 씀 = 시스템 관리자 · 읽기 = 로그인) - 화면 `M02_SO_Options.ts` · 글자 `M02_SO_Text.ts` · API `M02_SO_Api.ts` · 스타일 `M02_SO_Style.css` - 길 주소 · 동작 그대로 · B05 · 시험은 경로만 고침 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MPcuokNm1ZR2kqgGGDfaJA --- .../B05_Profile_Structures_Repository.py | 2 +- B05_Profile/B05_Profile_Structures_Router.py | 2 +- .../M02_MasterTemplete_Api_Fetch.ts | 27 +------ .../M02_MasterTemplete_Router.py | 18 ----- .../M02_MasterTemplete_Router_Items.py | 8 -- .../M02_MasterTemplete_UI_Side.ts | 2 +- .../M02_MasterTemplete_UI_Style.css | 77 ------------------- .../StructOptions/M02_SO_Api.ts | 29 +++++++ .../M02_SO_Options.py} | 0 .../M02_SO_Options.ts} | 11 +-- .../StructOptions/M02_SO_Router.py | 52 +++++++++++++ .../StructOptions/M02_SO_Style.css | 76 ++++++++++++++++++ .../M02_SO_Text.ts} | 2 +- main.py | 5 ++ resources/tester/test_m02_empty_master.py | 3 +- .../tester/test_m02_structure_options.py | 8 +- resources/tester/test_m02_system_read.py | 2 + 17 files changed, 182 insertions(+), 142 deletions(-) create mode 100644 M02_MasterTemplete/StructOptions/M02_SO_Api.ts rename M02_MasterTemplete/{M02_MasterTemplete_Options.py => StructOptions/M02_SO_Options.py} (100%) rename M02_MasterTemplete/{M02_MasterTemplete_Options.ts => StructOptions/M02_SO_Options.ts} (96%) create mode 100644 M02_MasterTemplete/StructOptions/M02_SO_Router.py create mode 100644 M02_MasterTemplete/StructOptions/M02_SO_Style.css rename M02_MasterTemplete/{M02_MasterTemplete_Options_Text.ts => StructOptions/M02_SO_Text.ts} (98%) diff --git a/B05_Profile/B05_Profile_Structures_Repository.py b/B05_Profile/B05_Profile_Structures_Repository.py index 30ae8a441..273874bef 100644 --- a/B05_Profile/B05_Profile_Structures_Repository.py +++ b/B05_Profile/B05_Profile_Structures_Repository.py @@ -14,7 +14,7 @@ from typing import Any, Iterable from B05_Profile.B05_Profile_Structures_Schema import StructureInstance, structure_type_map from common_util.common_util_json import atomic_write_json -from M02_MasterTemplete.M02_MasterTemplete_Options import hidden_keys +from M02_MasterTemplete.StructOptions.M02_SO_Options import hidden_keys STRUCTURES_FILE_NAME = "structures.json" _STAGE_DIR = os.path.join("B05_Profile", "route") diff --git a/B05_Profile/B05_Profile_Structures_Router.py b/B05_Profile/B05_Profile_Structures_Router.py index 1a18351f3..fcf149b02 100644 --- a/B05_Profile/B05_Profile_Structures_Router.py +++ b/B05_Profile/B05_Profile_Structures_Router.py @@ -42,7 +42,7 @@ from common_util.common_util_drainage_pipes import ( ) from common_util.common_util_storage import resolve_stored_project_path from config.config_db import get_db_pool -from M02_MasterTemplete.M02_MasterTemplete_Options import TypesWithVisible, with_visible +from M02_MasterTemplete.StructOptions.M02_SO_Options import TypesWithVisible, with_visible logger = logging.getLogger(__name__) diff --git a/M02_MasterTemplete/M02_MasterTemplete_Api_Fetch.ts b/M02_MasterTemplete/M02_MasterTemplete_Api_Fetch.ts index 02125837d..5e7f251e8 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Api_Fetch.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_Api_Fetch.ts @@ -32,7 +32,8 @@ export interface TemplateDoc { /** 서버가 판 불일치로 막았을 때 */ export class StaleError extends Error {} -async function call(path: string, init: RequestInit = {}): Promise { +/** `/api/m02` 아래 길 부름 — 409 = StaleError · 그 밖 실패 = 서버 까닭 글(하위 폴더도 씀) */ +export async function call(path: string, init: RequestInit = {}): Promise { const response = await fetch(`${API_BASE_URL}/m02${path}`, { credentials: "include", headers: { "Content-Type": "application/json" }, @@ -192,30 +193,6 @@ export interface StructureCombos { export const fetchStructureCombos = (): Promise => scoped("/structure-combos"); /** 조합 갈라 내기 결과 — `새로만듦` false = 같은 담은 로직의 조합이 이미 있어 그 키를 가리킴 */ -/* --- 구조물 옵션 보이기(마스터 템플릿 `구조물옵션표시.json`) — 좌측 「구조물 옵션 보이기」 줄 --- */ -export interface OptionKind { - type_id: string; - group: string; - name: string; - /** 거짓이면 B05 선택지에 안 나오는 종류 */ - enabled: boolean; - options: { key: string; label: string; phase: "b05" | "detail"; required: boolean }[]; -} - -/** 판 · 표시(숨긴 것만 `{종류: {옵션: false}}`) · 종류(옵션이 있는 것과 그 옵션) */ -export interface OptionsDoc { - 판: string; - 표시: Record>; - 종류: OptionKind[]; -} - -export const fetchStructureOptions = (): Promise => - call("/layers/system/structure-options"); // 읽기 = 로그인 누구나 · 고쳐 씀은 옛 길(시스템 관리자) - -/** 고쳐 씀 — 숨긴 것만 보냄 · 낡은 판이면 StaleError · 돌려받는 것은 새 문서 */ -export const saveStructureOptions = (판: string, 표시: OptionsDoc["표시"]): Promise => - call("/structure-options", { method: "PUT", body: JSON.stringify({ 판, 표시 }) }); - /** 항목 하나 복사해 가져오기(39-5 · 39-1b) — 받는 열(`column`)에 덮어씀 · 번호 · 열은 그대로 · * `source` = 원본 열 id(실무문서면 조합 키) · `from` 없음 = 지금 층 · 층 · 주인 = 다른 층(원본 읽기 권한) · * `practice` = 실무문서 조합 → 일위대가(시스템 관리자만) */ diff --git a/M02_MasterTemplete/M02_MasterTemplete_Router.py b/M02_MasterTemplete/M02_MasterTemplete_Router.py index 50e2e8bda..3094131d5 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Router.py +++ b/M02_MasterTemplete/M02_MasterTemplete_Router.py @@ -10,7 +10,6 @@ from typing import Any from fastapi import APIRouter, HTTPException, Query from pydantic import BaseModel -from M02_MasterTemplete import M02_MasterTemplete_Options as options from M02_MasterTemplete import M02_MasterTemplete_Store as store router = APIRouter(prefix="/api/m02", tags=["M02 MasterTemplete"]) @@ -21,11 +20,6 @@ class SaveBody(BaseModel): 문서: Any -class OptionsBody(BaseModel): - 판: str = "" # 옵션 표시 표 판 — 어긋나면 409 - 표시: dict[str, Any] # {종류: {옵션 키: 참 · 거짓}} — 거짓만 남김 - - def _call(fn, *args): try: return fn(*args) @@ -55,18 +49,6 @@ def post_structure(column: str) -> dict: return _call(store.create_structure, column) -@router.get("/structure-options") -def get_structure_options() -> dict: - """`{판, 표시, 종류}` — 표시 = 숨긴 것만 · 종류 = 옵션 있는 종류와 그 옵션.""" - return _call(options.listing) - - -@router.put("/structure-options") -def put_structure_options(body: OptionsBody) -> dict: - """고쳐 씀(낡은 판이면 409 · 없는 종류 · 키는 422) — 돌려주는 것 = 새 `{판, 표시, 종류}`.""" - return _call(options.save, body.표시, body.판) - - # 복사 가져오기 — 다른 열(`from`) 내용을 받는 열로 · 번호 · 열은 받는 쪽 그대로 @router.post("/structures/{column}/copy") def post_structure_copy(column: str, source: str = Query(alias="from")) -> dict: diff --git a/M02_MasterTemplete/M02_MasterTemplete_Router_Items.py b/M02_MasterTemplete/M02_MasterTemplete_Router_Items.py index 0a0ad7976..d92c53a72 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Router_Items.py +++ b/M02_MasterTemplete/M02_MasterTemplete_Router_Items.py @@ -288,14 +288,6 @@ def get_numbers(layer: Layer, owner: str | None = None, session: Session = Depen return _call(_root(session, layer, owner), store.structure_numbers) -@router.get("/layers/system/structure-options") -def get_structure_options(session: Session = Depends(verify_session)) -> dict: - """구조물 옵션 보이기 읽기 — 로그인 누구나(마스터 한 벌 · 고쳐 씀은 옛 길 시스템 관리자만).""" - from M02_MasterTemplete import M02_MasterTemplete_Options as options - - return _call(_root(session, "system"), options.listing) - - @router.get("/layers/{layer}/structure-combos") def get_combos(layer: Layer, owner: str | None = None, session: Session = Depends(verify_session)): return _call(_root(session, layer, owner), store.read_combos) diff --git a/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts b/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts index ad7e1b396..8504ca93e 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts @@ -28,7 +28,7 @@ import { type TemplateInfo, } from "./M02_MasterTemplete_Api_Fetch"; import { STRUCTURE_TABLE } from "./M02_MasterTemplete_Structure"; -import { buildOptionsSection } from "./M02_MasterTemplete_Options"; +import { buildOptionsSection } from "./StructOptions/M02_SO_Options"; import { buildPracticeSection } from "./M02_MasterTemplete_Practice"; import { canEdit, type Selection } from "./M02_MasterTemplete_UI_Main"; diff --git a/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css b/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css index a5b38543d..730b3775d 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css +++ b/M02_MasterTemplete/M02_MasterTemplete_UI_Style.css @@ -143,13 +143,6 @@ gap: var(--spacing-4); } -/* 구조물 옵션 보이기 — 종류 제목 */ -.m02-options__head { - margin: 0; - color: var(--color-text); - font-size: var(--text-body); -} - /* 층 화면 — 회사용 읽기만 안내 막대 · 회사 없음 안내 */ .m02-layer-bar { display: flex; @@ -172,76 +165,6 @@ color: var(--color-text); } -/* 좌측 「구조물 옵션 보이기」 화면(메인 칸) — 왼쪽 종류(군별) · 오른쪽 옵션 체크 */ -.m02-opts { - display: flex; - flex-direction: column; - gap: var(--spacing-12); - max-width: 64rem; - font-size: var(--text-body-sm); -} - -.m02-opts__panes { - display: grid; - grid-template-columns: minmax(14rem, 20rem) 1fr; - gap: var(--spacing-16); - align-items: start; -} - -.m02-opts__kinds, -.m02-opts__list { - max-height: calc(100vh - 20rem); - overflow-y: auto; - display: flex; - flex-direction: column; - gap: var(--spacing-4); - min-width: 0; -} - -.m02-opts__group { - margin: var(--spacing-8) 0 0; - color: var(--color-text-muted); - font-size: var(--text-body-sm); -} - -.m02-opts__kind { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: var(--spacing-4); - padding: var(--spacing-4) var(--spacing-8); - border: 1px solid transparent; - border-radius: var(--radius-sm); - background: none; - color: var(--color-text); - font: inherit; - text-align: left; - cursor: pointer; -} - -.m02-opts__kind:hover { - background: var(--color-mist-violet); -} - -.m02-opts__kind.is-active { - border-color: var(--color-accent); - background: var(--color-mist-violet); -} - -.m02-opts__option { - display: flex; - flex-wrap: wrap; - align-items: baseline; - gap: var(--spacing-8); - padding: var(--spacing-4) 0; - cursor: pointer; -} - -.m02-opts__key { - color: var(--color-text-muted); - font-size: 11px; -} - .m02-main__head + .m02-tree__search { margin-bottom: var(--spacing-8); } diff --git a/M02_MasterTemplete/StructOptions/M02_SO_Api.ts b/M02_MasterTemplete/StructOptions/M02_SO_Api.ts new file mode 100644 index 000000000..61430d310 --- /dev/null +++ b/M02_MasterTemplete/StructOptions/M02_SO_Api.ts @@ -0,0 +1,29 @@ +/* ============================================================================= + * M02_SO_Api.ts + * 구조물 옵션 보이기 서버 호출 — 서버 = `M02_SO_Router.py`(마스터 템플릿 `구조물옵션표시.json`) + * ========================================================================== */ + +import { call } from "../M02_MasterTemplete_Api_Fetch"; + +export interface OptionKind { + type_id: string; + group: string; + name: string; + /** 거짓이면 B05 선택지에 안 나오는 종류 */ + enabled: boolean; + options: { key: string; label: string; phase: "b05" | "detail"; required: boolean }[]; +} + +/** 판 · 표시(숨긴 것만 `{종류: {옵션: false}}`) · 종류(옵션이 있는 것과 그 옵션) */ +export interface OptionsDoc { + 판: string; + 표시: Record>; + 종류: OptionKind[]; +} + +export const fetchStructureOptions = (): Promise => + call("/layers/system/structure-options"); // 읽기 = 로그인 누구나 · 고쳐 씀은 시스템 관리자 + +/** 고쳐 씀 — 숨긴 것만 보냄 · 낡은 판이면 StaleError · 돌려받는 것은 새 문서 */ +export const saveStructureOptions = (판: string, 표시: OptionsDoc["표시"]): Promise => + call("/structure-options", { method: "PUT", body: JSON.stringify({ 판, 표시 }) }); diff --git a/M02_MasterTemplete/M02_MasterTemplete_Options.py b/M02_MasterTemplete/StructOptions/M02_SO_Options.py similarity index 100% rename from M02_MasterTemplete/M02_MasterTemplete_Options.py rename to M02_MasterTemplete/StructOptions/M02_SO_Options.py diff --git a/M02_MasterTemplete/M02_MasterTemplete_Options.ts b/M02_MasterTemplete/StructOptions/M02_SO_Options.ts similarity index 96% rename from M02_MasterTemplete/M02_MasterTemplete_Options.ts rename to M02_MasterTemplete/StructOptions/M02_SO_Options.ts index 2c7074a06..f14fb6187 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Options.ts +++ b/M02_MasterTemplete/StructOptions/M02_SO_Options.ts @@ -1,20 +1,21 @@ /* ============================================================================= - * M02_MasterTemplete_Options.ts + * M02_SO_Options.ts * 좌측 「구조물 옵션 보이기」 줄(구조물 집계 양식 아래 별도) — 누르면 메인 칸에 * 종류 목록(군별) → 고른 종류의 옵션 체크 목록(라벨 · 키 작은 글 · B06 전용 · 필수 표시) · * [저장] · [모두 보임]. 체크 안 한 옵션은 B05 · B06 구조물 폼에서 숨음(정의 · 값은 그대로). - * 서버 = `M02_MasterTemplete_Options.py`(마스터 템플릿 `구조물옵션표시.json` · 숨긴 것만 저장). + * 서버 = `M02_SO_Options.py`(마스터 템플릿 `구조물옵션표시.json` · 숨긴 것만 저장). * ========================================================================== */ import { createButton, createTag, el, showToast } from "@ui/ui_template_elements"; +import { StaleError } from "../M02_MasterTemplete_Api_Fetch"; import { fetchStructureOptions, saveStructureOptions, - StaleError, type OptionKind, type OptionsDoc, -} from "./M02_MasterTemplete_Api_Fetch"; -import { to } from "./M02_MasterTemplete_Options_Text"; +} from "./M02_SO_Api"; +import "./M02_SO_Style.css"; +import { to } from "./M02_SO_Text"; export interface OptionsSectionOptions { isAdmin: boolean; diff --git a/M02_MasterTemplete/StructOptions/M02_SO_Router.py b/M02_MasterTemplete/StructOptions/M02_SO_Router.py new file mode 100644 index 000000000..56d64ec41 --- /dev/null +++ b/M02_MasterTemplete/StructOptions/M02_SO_Router.py @@ -0,0 +1,52 @@ +"""M02 구조물 옵션 보이기 길 — 정본 `M02_SO_Options.py`. + +`router` = 고쳐 씀 · 관리 읽기(`main.py` 가 `system_admin_only` 로 붙임) · +`read_router` = 읽기만(로그인 누구나 · `main.py` 가 `protected` 로 붙임). +""" + +from __future__ import annotations + +from typing import Any + +from fastapi import APIRouter, Depends, HTTPException +from pydantic import BaseModel + +from common_util.common_util_auth import verify_session +from M02_MasterTemplete import M02_Layer_Access as access +from M02_MasterTemplete import M02_MasterTemplete_Store as store +from M02_MasterTemplete.StructOptions import M02_SO_Options as options + +router = APIRouter(prefix="/api/m02", tags=["M02 MasterTemplete"]) +read_router = APIRouter(prefix="/api/m02", tags=["M02 MasterTemplete"]) + + +class OptionsBody(BaseModel): + 판: str = "" # 옵션 표시 표 판 — 어긋나면 409 + 표시: dict[str, Any] # {종류: {옵션 키: 참 · 거짓}} — 거짓만 남김 + + +def _call(fn, *args): + with store.at(store.FOLDER): + try: + return fn(*args) + except store.StoreError as e: + raise HTTPException(status_code=e.status, detail=e.detail) from e + + +@router.get("/structure-options") +def get_structure_options() -> dict: + """`{판, 표시, 종류}` — 표시 = 숨긴 것만 · 종류 = 옵션 있는 종류와 그 옵션.""" + return _call(options.listing) + + +@router.put("/structure-options") +def put_structure_options(body: OptionsBody) -> dict: + """고쳐 씀(낡은 판이면 409 · 없는 종류 · 키는 422) — 돌려주는 것 = 새 `{판, 표시, 종류}`.""" + return _call(options.save, body.표시, body.판) + + +@read_router.get("/layers/system/structure-options") +def get_structure_options_read(session=Depends(verify_session)) -> dict: + """구조물 옵션 보이기 읽기 — 로그인 누구나(마스터 한 벌 · 고쳐 씀은 시스템 관리자만).""" + access.require(session, "system", None) + return _call(options.listing) diff --git a/M02_MasterTemplete/StructOptions/M02_SO_Style.css b/M02_MasterTemplete/StructOptions/M02_SO_Style.css new file mode 100644 index 000000000..9a508036f --- /dev/null +++ b/M02_MasterTemplete/StructOptions/M02_SO_Style.css @@ -0,0 +1,76 @@ +/* 좌측 「구조물 옵션 보이기」 화면(메인 칸) — 왼쪽 종류(군별) · 오른쪽 옵션 체크 */ +.m02-opts { + display: flex; + flex-direction: column; + gap: var(--spacing-12); + max-width: 64rem; + font-size: var(--text-body-sm); +} + +/* 종류 제목 */ +.m02-options__head { + margin: 0; + color: var(--color-text); + font-size: var(--text-body); +} + +.m02-opts__panes { + display: grid; + grid-template-columns: minmax(14rem, 20rem) 1fr; + gap: var(--spacing-16); + align-items: start; +} + +.m02-opts__kinds, +.m02-opts__list { + max-height: calc(100vh - 20rem); + overflow-y: auto; + display: flex; + flex-direction: column; + gap: var(--spacing-4); + min-width: 0; +} + +.m02-opts__group { + margin: var(--spacing-8) 0 0; + color: var(--color-text-muted); + font-size: var(--text-body-sm); +} + +.m02-opts__kind { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--spacing-4); + padding: var(--spacing-4) var(--spacing-8); + border: 1px solid transparent; + border-radius: var(--radius-sm); + background: none; + color: var(--color-text); + font: inherit; + text-align: left; + cursor: pointer; +} + +.m02-opts__kind:hover { + background: var(--color-mist-violet); +} + +.m02-opts__kind.is-active { + border-color: var(--color-accent); + background: var(--color-mist-violet); +} + +.m02-opts__option { + display: flex; + flex-wrap: wrap; + align-items: baseline; + gap: var(--spacing-8); + padding: var(--spacing-4) 0; + cursor: pointer; +} + +.m02-opts__key { + color: var(--color-text-muted); + font-size: 11px; +} diff --git a/M02_MasterTemplete/M02_MasterTemplete_Options_Text.ts b/M02_MasterTemplete/StructOptions/M02_SO_Text.ts similarity index 98% rename from M02_MasterTemplete/M02_MasterTemplete_Options_Text.ts rename to M02_MasterTemplete/StructOptions/M02_SO_Text.ts index 206a0c352..aaccee04a 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Options_Text.ts +++ b/M02_MasterTemplete/StructOptions/M02_SO_Text.ts @@ -1,5 +1,5 @@ /* ============================================================================= - * M02_MasterTemplete_Options_Text.ts + * M02_SO_Text.ts * 좌측 「구조물 옵션 보이기」 줄 · 상세 화면 글자 — [한국어, 영어] * ========================================================================== */ diff --git a/main.py b/main.py index ed3216512..13dd66cd9 100644 --- a/main.py +++ b/main.py @@ -68,6 +68,8 @@ from M02_MasterTemplete.M02_MasterTemplete_Router_Drawing import router as m02_d from M02_MasterTemplete.M02_MasterTemplete_Router_Items import router as m02_items_router from M02_MasterTemplete.M02_MasterTemplete_Router_Layers import router as m02_layers_router from M02_MasterTemplete.M02_Project_Items_Router import router as m02_project_items_router +from M02_MasterTemplete.StructOptions.M02_SO_Router import read_router as m02_so_read_router +from M02_MasterTemplete.StructOptions.M02_SO_Router import router as m02_so_router from common_util.common_util_audit import note_api_call, record_call_burst from common_util.common_util_auth import ( require_company, @@ -646,6 +648,9 @@ app.include_router( ) # 마스터 템플릿 — 시스템 층은 시스템 관리자만 · 층(회사·개인·프로젝트)·도면 길은 로그인만(권한은 길 안에서). app.include_router(m02_master_template_router, dependencies=system_admin_only) +# 구조물 옵션 보이기 · 그룹(PLAN 46) — 고쳐 씀은 시스템 관리자 · 읽기는 로그인만 +app.include_router(m02_so_router, dependencies=system_admin_only) +app.include_router(m02_so_read_router, dependencies=protected) app.include_router(m02_layers_router, dependencies=protected) app.include_router(m02_items_router, dependencies=protected) app.include_router(m02_combos_router, dependencies=protected) diff --git a/resources/tester/test_m02_empty_master.py b/resources/tester/test_m02_empty_master.py index ea468cf74..7fbae861c 100644 --- a/resources/tester/test_m02_empty_master.py +++ b/resources/tester/test_m02_empty_master.py @@ -25,6 +25,7 @@ from M02_MasterTemplete import M02_MasterTemplete_Router_Layers as layers_router from M02_MasterTemplete import M02_MasterTemplete_Store as store from M02_MasterTemplete import M02_MasterTemplete_Tree as tree from M02_MasterTemplete import M02_Template_Layers as layers +from M02_MasterTemplete.StructOptions import M02_SO_Router as so_router from resources.tester.helper_m02_master_sample import copy_master_sample from resources.tester.test_item_copy_complete import ( # noqa: F401 — 복사본 world 고정물 FOLDER, @@ -64,7 +65,7 @@ def empty(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> dict[str, Any]: gone = empty_master(system) session = {"user_id": 42, "company_id": 7, "role": "SYSTEM_ADMIN", "is_master": True} app = FastAPI() - for module in (main_router, items_router, layers_router): + for module in (main_router, items_router, layers_router, so_router): app.include_router(module.router) app.dependency_overrides[verify_session] = lambda: session return {"client": TestClient(app), "system": system, "gone": gone} diff --git a/resources/tester/test_m02_structure_options.py b/resources/tester/test_m02_structure_options.py index cd525adb7..b5fc71efe 100644 --- a/resources/tester/test_m02_structure_options.py +++ b/resources/tester/test_m02_structure_options.py @@ -18,9 +18,9 @@ from fastapi.testclient import TestClient from B05_Profile.B05_Profile_Structures_Router import router as b05_router from B05_Profile.B05_Profile_Structures_Schema import load_structure_types -from M02_MasterTemplete import M02_MasterTemplete_Options as opt from M02_MasterTemplete import M02_MasterTemplete_Store as store -from M02_MasterTemplete.M02_MasterTemplete_Router import router as m02_router +from M02_MasterTemplete.StructOptions import M02_SO_Options as opt +from M02_MasterTemplete.StructOptions.M02_SO_Router import router as m02_router ROOT = Path(__file__).resolve().parents[2] URL = "/api/m02/structure-options" @@ -131,12 +131,12 @@ def test_좌측_줄은_구조물_집계_양식_아래_한_줄_같은_한_곳에 assert "matset" not in side paint = side.split("function paint(): void {")[1].split("\n }\n")[0] assert "if (opt.getOpen()) options.clear();" in paint # 양식을 열면 이 줄 고름 지움 - api = (ROOT / "M02_MasterTemplete/M02_MasterTemplete_Api_Fetch.ts").read_text("utf-8") + api = (ROOT / "M02_MasterTemplete/StructOptions/M02_SO_Api.ts").read_text("utf-8") assert ( 'call("/layers/system/structure-options")' in api and 'call("/structure-options", { method: "PUT"' in api ) - ui = (ROOT / "M02_MasterTemplete/M02_MasterTemplete_Options.ts").read_text("utf-8") + ui = (ROOT / "M02_MasterTemplete/StructOptions/M02_SO_Options.ts").read_text("utf-8") assert 'o.phase === "detail"' in ui # B06 전용 표시 diff --git a/resources/tester/test_m02_system_read.py b/resources/tester/test_m02_system_read.py index b86aaf039..5af19a703 100644 --- a/resources/tester/test_m02_system_read.py +++ b/resources/tester/test_m02_system_read.py @@ -18,6 +18,7 @@ from M02_MasterTemplete import M02_MasterTemplete_Router_Items as items_router from M02_MasterTemplete import M02_MasterTemplete_Router_Layers as layers_router from M02_MasterTemplete import M02_MasterTemplete_Store as store from M02_MasterTemplete import M02_Template_Layers as layers +from M02_MasterTemplete.StructOptions import M02_SO_Router as so_router from resources.tester.test_m02_structure import write_sample_table SYS = "/api/m02/layers/system" @@ -39,6 +40,7 @@ def world(tmp_path, monkeypatch) -> dict[str, Any]: app = FastAPI() app.include_router(items_router.router) app.include_router(layers_router.router) + app.include_router(so_router.read_router) app.dependency_overrides[verify_session] = lambda: session return {"client": TestClient(app), "session": session}