feat(M01): 끝수 계산 붙임 · 절 목록·절 거르기·표 조건 값·소유 거름·자체 로직 API

- 엔진 — 로직 줄의 묶음 `끝수`{대상, 자리, 방법}를 계산 끝에 붙임(비목 합마다 끊고 계 = 그 합 · 돈 아닌 로직은 결과) · 글자 끝수는 설명 그대로 · 검사가 묶음 모양을 봄
- 서버 — `GET /sections`(원문 본문 절 목록 + 그 절의 표·로직 수) · `/tables` 의 `section`(13-4 가 13-40 에 안 걸림) · `GET /table/options`(조건 값만) · `/logics` 의 `owner`
- 자체 로직 — `POST /logic/new|copy|edit|delete` · 파일 `로직_자체.json` · 키 GX 서버 발급(원문번호 같아도 줄마다 다른 키) · 정본은 403 · 저장 안 한 초안은 `/calc` 에 `key` "" 로
- 저장소가 700줄을 넘어 쓰기 모양(`keep_shape`·`dump`)을 `M01_MasterData_Store_Shape.py` 로 가름
- 시험 `resources/tester/test_m01_make.py` 8건 통과 · 기존 M01 시험 25건 통과 · 일괄 시험 계산 1,291 그대로

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JgUhN55Z3BCYhUJTjwTNfj
This commit is contained in:
2026-09-21 09:31:45 +09:00
co-authored by Claude Opus 5
parent 964552c2ba
commit aa86e02b8f
8 changed files with 678 additions and 109 deletions
+64 -3
View File
@@ -12,6 +12,7 @@ from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from M01_MasterData import M01_MasterData_Store as store
from M01_MasterData import M01_MasterData_Store_Make as make
router = APIRouter(prefix="/api/m01", tags=["M01 MasterData"])
@@ -39,6 +40,29 @@ class SaveBody(BaseModel):
files: list[FileChanges]
class NewLogic(BaseModel):
logic: dict[str, Any]
owner: str = make.OWNERS[0]
class CopyLogic(BaseModel):
key: str
이름: str = ""
owner: str = make.OWNERS[0]
class EditLogic(BaseModel):
key: str
version: str
logic: dict[str, Any]
owner: str = ""
class DeleteLogic(BaseModel):
key: str
version: str
def _num(v):
"""Decimal → JSON 수(그냥 두면 글자로 나감)."""
if isinstance(v, Decimal):
@@ -96,8 +120,9 @@ def get_tables(
page: int = 1,
size: int = 30,
usage: str = "",
section: str = "",
) -> dict:
return _call(store.tables, file, group, sub, detail, q, page, size, usage)
return _call(store.tables, file, group, sub, detail, q, page, size, usage, section)
@router.get("/table")
@@ -105,9 +130,25 @@ def get_table(file: str, key: str) -> dict:
return _call(store.table, file, key)
@router.get("/table/options")
def get_table_options(file: str, key: str) -> dict:
"""표 조건 칸의 값만 — 줄을 통째로 받지 않음."""
return _call(make.table_options, file, key)
@router.get("/sections")
def get_sections(
book: str, division: str = "", chapter: str = "", q: str = "", limit: int = 200
) -> dict:
"""원문 절 목록 — 만들기 첫 걸음."""
return _call(make.sections, book, division, chapter, q, limit)
@router.get("/logics")
def get_logics(sub: str = "", detail: str = "", q: str = "", blocked: int | None = None) -> dict:
return {"logics": _call(store.logics, sub, detail, q, blocked)}
def get_logics(
sub: str = "", detail: str = "", q: str = "", blocked: int | None = None, owner: str = ""
) -> dict:
return {"logics": _call(store.logics, sub, detail, q, blocked, owner)}
@router.get("/logic")
@@ -147,3 +188,23 @@ def post_calc(body: CalcBody) -> dict:
@router.post("/save")
def post_save(body: SaveBody) -> dict:
return {"files": _call(store.save, [f.model_dump() for f in body.files])}
@router.post("/logic/new")
def post_logic_new(body: NewLogic) -> dict:
return _call(make.logic_new, body.logic, body.owner)
@router.post("/logic/copy")
def post_logic_copy(body: CopyLogic) -> dict:
return _call(make.logic_copy, body.key, body.이름, body.owner)
@router.post("/logic/edit")
def post_logic_edit(body: EditLogic) -> dict:
return _call(make.logic_edit, body.key, body.version, body.logic, body.owner)
@router.post("/logic/delete")
def post_logic_delete(body: DeleteLogic) -> dict:
return _call(make.logic_delete, body.key, body.version)