From 99a47bec03f86e6a3eb2bd133a28dc66841fb3c2 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Thu, 1 Oct 2026 23:25:20 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat(M01):=20=EB=A7=88=EC=8A=A4=ED=84=B0=20?= =?UTF-8?q?=EC=82=AC=EB=B3=B8=20=ED=8F=B4=EB=8D=94=20=EA=B8=B8=20=E2=80=94?= =?UTF-8?q?=20AISLO=5FMASTER=5FDIR=20=EA=B0=80=20=EC=9E=88=EC=9C=BC?= =?UTF-8?q?=EB=A9=B4=20=EA=B7=B8=20=ED=8F=B4=EB=8D=94=20=C2=B7=20=EC=97=86?= =?UTF-8?q?=EC=9C=BC=EB=A9=B4=20=EC=A0=95=EB=B3=B8(=ED=9D=90=EB=A6=84?= =?UTF-8?q?=EB=8F=84=20=EC=A0=81=EC=9A=A9=20=EC=A0=95=ED=95=A8=2013=20?= =?UTF-8?q?=C2=B7=20PLAN=2036-4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01KmELpdXFDRZHhfbGuRhSmL --- M01_MasterData/M01_MasterData_Store.py | 3 +- .../tester/test_m01_logic_flow_folder.py | 38 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 resources/tester/test_m01_logic_flow_folder.py diff --git a/M01_MasterData/M01_MasterData_Store.py b/M01_MasterData/M01_MasterData_Store.py index 165086730..8f7b2d789 100644 --- a/M01_MasterData/M01_MasterData_Store.py +++ b/M01_MasterData/M01_MasterData_Store.py @@ -28,7 +28,8 @@ import master_text as mtx # noqa: E402 from M01_MasterData import M01_MasterData_Store_Cache as cache # noqa: E402 from M01_MasterData.M01_MasterData_Store_Shape import dump, keep_shape # noqa: E402 -FOLDER: Path = cm.MASTER # 시험은 사본 폴더로 바꿈 +_COPY = os.environ.get("AISLO_MASTER_DIR") # 화면 저장 시험 사본 폴더(정함 13) · 없으면 정본 +FOLDER: Path = Path(_COPY).resolve() if _COPY else cm.MASTER # 시험은 사본 폴더로 바꿈 DRAFT = "_초안.json" # 저장 안 한 새 로직을 얹는 메모리 자리 — 파일로 쓰지 않음 _CHAPTER = re.compile(r"_(\d\d)장_(.+)\.json$") # 「_03장_토공사.json」 # ponytail: 저장은 한 번에 하나(프로세스 안 잠금) · 서버를 여럿 띄우면 파일 잠금으로 diff --git a/resources/tester/test_m01_logic_flow_folder.py b/resources/tester/test_m01_logic_flow_folder.py new file mode 100644 index 000000000..bd624e2c1 --- /dev/null +++ b/resources/tester/test_m01_logic_flow_folder.py @@ -0,0 +1,38 @@ +"""흐름도 적용(PLAN 36-4 · 정함 13) — 마스터 사본 폴더 길 시험. + +· 환경 변수 AISLO_MASTER_DIR 가 있으면 M01 저장소 마스터 폴더 = 그 폴더 · 없으면 정본 +""" + +from __future__ import annotations + +import os +import subprocess +import sys +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] +PROBE = "from M01_MasterData import M01_MasterData_Store as s; print(s.FOLDER)" + + +def _folder(env_dir: str | None) -> Path: + env = {k: v for k, v in os.environ.items() if k != "AISLO_MASTER_DIR"} + if env_dir: + env["AISLO_MASTER_DIR"] = env_dir + got = subprocess.run( + [sys.executable, "-c", PROBE], + cwd=ROOT, + env=env, + capture_output=True, + text=True, + timeout=120, + ) + assert got.returncode == 0, got.stderr + return Path(got.stdout.strip().splitlines()[-1]) + + +def test_사본_폴더_변수가_있으면_그_폴더(tmp_path: Path) -> None: + assert _folder(str(tmp_path)) == tmp_path.resolve() + + +def test_변수가_없으면_정본() -> None: + assert _folder(None) == (ROOT / "resources" / "master_data").resolve() From 4697db29d3c318d3dc303e488cb9e93b0085c34f Mon Sep 17 00:00:00 2001 From: umsangdon Date: Thu, 1 Oct 2026 23:25:37 +0900 Subject: [PATCH 2/2] auto: 2026-10-01 23:25 (ESD_LAPTOP) --- docs/raw/M01_쉬운단가/흐름도_적용_정함.md | 37 +++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 docs/raw/M01_쉬운단가/흐름도_적용_정함.md diff --git a/docs/raw/M01_쉬운단가/흐름도_적용_정함.md b/docs/raw/M01_쉬운단가/흐름도_적용_정함.md new file mode 100644 index 000000000..2536a147b --- /dev/null +++ b/docs/raw/M01_쉬운단가/흐름도_적용_정함.md @@ -0,0 +1,37 @@ +# 흐름도 본 프로그램 적용 — 정함 (PLAN 36 · 브레인 · 10-01 23시) + +사용자 지시 · 결정은 PLAN.md 36장 머리 줄(그대로 인용)이 기준. 아래는 창끼리 맞출 약속. + +## 목적 + +「단가산출 로직」 화면에서 로직을 열면 왼쪽 = 지금 내용(보기) · 오른쪽 = 같은 로직의 흐름도(고치는 곳). 한 로직 · 두 보기 · 고칠 곳은 흐름도 한 곳. 옛 로직은 열 때 흐름도로 자동 변환(파일은 안 바뀜) · 흐름도로 [저장]하면 그 로직은 흐름도가 정본. + +## 약속 + +1. **로직 줄 「흐름」 칸** — 마스터 로직 줄(`resources/master_data/로직_*.json` 의 `줄[]`)에 `흐름`(CfLogic JSON · DataFlow Model 꼴 그대로). 있으면 흐름도가 정본. 쓰는 때 = 화면 [저장](기존 LogicLab `POST /save` 의 줄에 실어 보냄)뿐 · 자동 변환 결과를 파일에 쓰지 않음. 서버는 저장 때 흐름을 검사(Model 검사 꼴)하고 서버 셈으로 다시 셈. +2. **흐름 받기 길** — `GET /api/m01/logic/flow?file=&key=` → `{flow: CfLogic, converted: bool, notes: string[]}` · 줄에 `흐름` 이 있으면 그대로(converted false) · 없으면 변환(converted true · notes = 못 옮긴 곳 쉬운 말). 변환 모듈 = `M02_MasterTemplete/DataFlow/M02_DF_Convert.py`(sub7 · 서버 단독 · 옛 식 파서는 지금 서버가 쓰는 것을 그대로 씀). +3. **변환 규칙** — 입력 → 입력 상자(변수 · 고르기 = choice · 범위) · 중간 식 → 함수 상자(식 하나 = 결과 하나) · 호표 줄 → 요소 알약(단가) + 수량 함수 + 출력 상자(비목별: 노무비 · 재료비 · 경비) · 결과 식 · 덧줄 · 끝수 → 함수 · 끝수 · 옛 식에 나온 이름마다 알약을 칸에 연결하고 식은 칸 주소로(M02 방식) · 만약 → IF · 또는 → OR · 최대/최소 → MAX/MIN · 올림/반올림/버림 → ROUNDUP/ROUND/ROUNDDOWN · 찾기(표, 조건=값).칸 → 표 알약 + 선 규칙 「표에서 찾기」 · 로직(…) → 다른 로직 입력 상자 · 자동 배치(입력 왼쪽 → 함수 가운데 → 출력 오른쪽 · 겹침 없음). 전수 대조 = 1,472 로직마다 옛 셈 값(기본 입력 · 시험입력) = 변환 흐름도 셈 값 · 맞는 비율 · 안 맞는 까닭 목록(읽기만 · 파일 안 씀). +4. **계산 길** — 기존 M01 계산(`/calc` 꼴 · 시험 계산 · 다른 화면이 부르는 셈)이 줄에 `흐름` 이 있으면 DataFlow 서버 셈(`M02_DF_Compute.py`)으로 · 없으면 지금 셈 그대로. 입력 이름 = 흐름의 「설계 때 정할 것」(변수 이름) 그대로. +5. **오른쪽 패널** — `M02_MasterTemplete/DataFlow/M02_DF_Panel.ts`(sub8) `mountFlowPanel(host, opts) → handle` · opts = { logic: CfLogic; readOnly?: boolean; title: string; onChange(lg: CfLogic): void; logics?(key) } · handle = { set(lg), destroy(), expand() } · 다이어그램 · 상세 내용 · 원가 · 검증 칸을 패널 안에(세로로) · 너비 끌어 조절 · 접기 · 「크게 보기」(전체 화면 · 닫으면 패널로) · 저장 단추는 패널에 없음(페이지 [저장] 하나) · 실행 취소는 패널 안. +6. **페이지 저장 하나** — 패널 조작 = 페이지 캐시(drafts · sessionStorage)에 쌓임 · [저장] 한 번에 줄(흐름 포함) 보냄 · [고친 것 버리기] = 흐름도 고친 것도 버림 · 자동저장 없음. +7. **왼쪽 보기(흐름 로직)** — 기본정보(지금대로) · 단가산출 상세 = 흐름도 출력 상자를 비용 종류별 줄(이름 · 단위 · 값) · 소계 · 계 · 텍스트 수식 = 함수 상자마다 「식 보기」 두 줄(이름 식 · 값 식) · 시험 계산 = 계산 길(4) 그대로. 흐름 없는 옛 로직의 왼쪽은 지금 그대로. +8. **식 보기 · 조건 만들기** — `M02_DF_FormulaView.ts`(sub2) `formulaLines(box: CfBox, values) → { name: string; named: string; valued: string; branches?: {when: string; then: string; now: boolean}[] }[]` (칸 주소 → 연결 알약 이름 · ×, ÷ · IF 는 만약 / 아니면 줄) — 상세 내용 함수 판과 왼쪽 텍스트 수식(sub5)이 함께 씀. 「조건 만들기」 = 엑셀 시트 안 기능(`A00_Common/spreadsheet/spreadsheet_condition.ts` 새 파일 · 편집기 우클릭 「조건 만들기」 · 부르는 쪽이 `conditions` 선택을 줄 때만 보임 → M02 화면은 그대로) · 만약 [칸 · 알약] [비교] [값] → [칸 · 값] 줄 · 아니면 만약 · 아니면 → 고른 칸에 IF 식 · 값 목록은 부르는 쪽 `valuesFor(a1)` 이 줌. +9. **선 조건(조건 통과)** — 웹앱 모양 그대로 · CfRule gate = `{ kind: "gate"; conds: { from: CfPort; op; value }[]; join: "그리고" | "또는" }`(옛 한 줄 꼴도 읽음) · Model `valueChoices(lg, port) → (string | number)[] | null`(변수 고르기 · 표 칸 값 · 재료 규격 · 없으면 null = 숫자 칸 + 범위 검사) · 규칙 → 받는 칸 엑셀 식 · 서버 Compute 같은 뜻 · 선 이름표 「지반 = 토사 그리고 높이 ≥ 3 일 때만」. +10. **단추** — 「식으로 새로 만들기」 지움 · 「신규 로직」(빈 흐름도 + 기본정보 칸 이름 · 원문번호 · 결과 단위 · 출처 · 구분 자체 → drafts 새 줄 → [저장] 때 서버가 GX 새 키) · 「본떠 만들기」 → 「로직 복사/생성」(신규 로직 오른쪽 · 흐름도 있으면 함께 복사) · 「시험판 ▾」 과 시험판 단추 둘 지움. +11. **다른 로직 입력** — 고를 대상 = 마스터 로직 전체(기존 로직 목록 찾기) · 내보내는 알약 = 그 로직 출력(흐름이면 출력 상자 · 옛 로직이면 노무비 · 재료비 · 경비 · 계) · 받는 알약 = 그 로직 입력 · 값 = 서버 계산 길(4). +12. **권한** — 흐름도 고치기 · 저장 = 시스템 관리자(공용 로직 포함) · 그 밖은 보기만. +13. **안전** — 서버는 환경 변수 `AISLO_MASTER_DIR` 가 있으면 그 폴더를 마스터 폴더로(없으면 지금대로 · sub4). 화면 저장 시험은 자기 폴더 `tmp/master_copy_36/`(resources/master_data 사본) + 그 변수로 띄운 자기 서버에서만 · 끝나면 git status 로 resources/master_data 안 바뀜 확인. 시험(pytest)은 사본 폴더 · 마스터 내용에 기대지 않음. +14. **정리** — DataFlow · CostFlow 폴더 그대로 · 흐름도 시작 화면 견본 목록 지움 · 시험이 쓰는 견본은 `resources/tester/fixtures_df/` 로 옮기고 그 밖 견본 지움 · 시험판 입구 지움. + +## 창 + +| 창 | 맡음 | +| --- | --- | +| sub7 | 36-0b 선 조건 틀(gate conds · join · valueChoices · Rule 식 · 시험) 먼저 → 36-2 변환(M02_DF_Convert.py · 자동 배치 · 전수 대조) | +| sub2 | 36-0 상세 내용 함수 판 「식 보기」 · 칸 연결 표 · 칸 색 · M02_DF_FormulaView.ts · 엑셀 「조건 만들기」(spreadsheet_condition.ts) · LinkPanel 조건 여러 줄 · 값 목록 고르기 | +| sub6 | 36-1 조사(옛 식 문법 전수 · 파서 위치 · 못 옮기는 꼴 · 계산 길을 부르는 곳) → sub7 에 넘김 → 36-7 정리(견본 · 시험 옮김) | +| sub8 | 36-3 M02_DF_Panel.ts(패널 꼴 · 너비 · 접기 · 크게 보기) · 선 이름표 여러 조건 | +| sub4 | 36-4 서버 — 「흐름」 칸 저장 · 검사 · /api/m01/logic/flow · 계산 길 갈래 · 신규 GX · 복사에 흐름 · 권한 · AISLO_MASTER_DIR · Compute 여러 조건 | +| sub5 | 36-4 왼쪽 보기(흐름 로직의 단가산출 상세 · 텍스트 수식 · 시험 계산) | +| sub3 | 36-6 다른 로직 입력 = 마스터 로직 고르기 · 값 | +| sub1 | 36-5 단추 · 시험판 지움 · LogicLab 두 칸 배치 · 패널 잇기 · drafts 저장 하나 → 36-8 끝 검증 |