knowledge(마스터): 소요량·계수·로직에 구분·상세구분 — 표·줄마다 칸 · 갈래 목록은 서버가 줌
- 장 파일 137 개의 표 3,028 · 로직 1,351 줄에 구분(원문 + 부문 · 「건설품셈 공통」) · 상세구분(「03장 토공사」) 칸을 직접 둠 · 파일 머리의 부문·차례는 목록 차례로 남김 - 열 차례 — 표는 키 · 원문번호 · 구분 · 상세구분 · 이름 · 기준 · 출처 · 비고 · 조건 · 범위규칙 · 값칸 · 줄 · 주 · 그룹(맨 뒤) · 로직은 소유 다음에 비고 - 서버 — GET /subs?group= 이 구분·상세구분 목록(장 차례대로) · GET /tables 가 파일을 가로질러 구분·상세구분·찾기로 거르고 쪽 나눔 · GET /logics 도 구분·상세구분으로 거름 · 새 줄은 서버가 갈래를 채움 · 죽은 book 칸 걷어냄 - check_master 가 구분·상세구분이 파일 머리·이름과 맞는지 봄 · adopt 가 빌더 줄에 갈래를 찍어 되돌리기 시험 글자까지 같음 · _틀.md · 화면 계약 · 화면 트리 · 시험 같이 고침 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JgUhN55Z3BCYhUJTjwTNfj
This commit is contained in:
@@ -127,8 +127,22 @@ def refs_of(rows_: list[dict]) -> dict[str, str]:
|
||||
return out
|
||||
|
||||
|
||||
def subs(file: str) -> dict:
|
||||
"""하위 거름 목록 — 인력 「구분」 · 기계 「세부분류」 묶음(머리에 등록된 것) · 그 밖은 빈 목록."""
|
||||
def subs(file: str = "", group: str = "") -> dict:
|
||||
"""하위 거름 목록 — 한 테이블 파일은 머리 묶음(인력 「구분」 · 기계 「세부분류」) ·
|
||||
장 그룹(소요량 · 계수 · 로직)은 장 파일에서 모음(구분 = 원문 + 부문 · 상세구분 = 장 · 차례대로)."""
|
||||
if group:
|
||||
if group not in cm.mf.GROUPS:
|
||||
raise StoreError(404, f"없는 그룹 「{group}」")
|
||||
found: dict[str, dict] = {}
|
||||
for info in files_of(group):
|
||||
name = mk.CHAPTER.match(info["file"])
|
||||
if not name:
|
||||
continue
|
||||
data = read(info["file"])[0]
|
||||
kind = " ".join(x for x in (data.get("원문"), data.get("부문")) if x)
|
||||
slot = found.setdefault(kind, {"name": kind, "book": data.get("원문"), "details": []})
|
||||
slot["details"].append(mk.detail_of(info["file"]))
|
||||
return {"file": "", "version": "", "slot": "구분", "subs": list(found.values())}
|
||||
data, version = read(file)
|
||||
slot = next((s for s in ("구분", "세부분류") if isinstance(data.get(s), dict)), "")
|
||||
return {
|
||||
@@ -172,19 +186,51 @@ def rows(
|
||||
}
|
||||
|
||||
|
||||
def tables(file: str, q: str) -> dict:
|
||||
data, version = read(file)
|
||||
if items_key(data) != "표":
|
||||
raise StoreError(400, f"표형 파일 아님 「{file}」")
|
||||
heads = ("키", "원문번호", "이름", "기준", "출처", "조건", "값칸")
|
||||
_TABLE_HEADS = ("키", "원문번호", "구분", "상세구분", "이름", "기준", "출처", "조건", "값칸")
|
||||
|
||||
|
||||
def tables(
|
||||
file: str = "",
|
||||
group: str = "",
|
||||
sub: str = "",
|
||||
detail: str = "",
|
||||
q: str = "",
|
||||
page: int = 1,
|
||||
size: int = 30,
|
||||
) -> dict:
|
||||
"""표 목록 — 파일 하나 또는 그룹 전체(구분 · 상세구분 · 찾기로 거름 · 장 차례대로 · 쪽 나눔)."""
|
||||
if file:
|
||||
names = [file]
|
||||
elif group in cm.mf.TABLE_GROUPS:
|
||||
names = [f["file"] for f in files_of(group)]
|
||||
else:
|
||||
raise StoreError(404, f"없는 표형 그룹 「{group}」")
|
||||
hits, version = [], ""
|
||||
for name in names:
|
||||
data, version = read(name)
|
||||
if items_key(data) != "표":
|
||||
raise StoreError(400, f"표형 파일 아님 「{name}」")
|
||||
for t in data["표"]:
|
||||
if sub and t.get("구분") != sub:
|
||||
continue
|
||||
if detail and t.get("상세구분") != detail:
|
||||
continue
|
||||
if _hit(t, q):
|
||||
hits.append(
|
||||
{
|
||||
"file": name,
|
||||
**{k: t.get(k) for k in _TABLE_HEADS},
|
||||
"count": len(t.get("줄") or []),
|
||||
}
|
||||
)
|
||||
page, size = max(page, 1), min(max(size, 1), 500)
|
||||
return {
|
||||
"file": file,
|
||||
"version": version,
|
||||
"tables": [
|
||||
{**{k: t.get(k) for k in heads}, "count": len(t.get("줄") or [])}
|
||||
for t in data["표"]
|
||||
if _hit(t, q)
|
||||
],
|
||||
"version": version if file else "",
|
||||
"total": len(hits),
|
||||
"page": page,
|
||||
"size": size,
|
||||
"tables": hits[(page - 1) * size : page * size],
|
||||
}
|
||||
|
||||
|
||||
@@ -206,14 +252,15 @@ def _logic_files(files: dict[str, dict]):
|
||||
yield name, data
|
||||
|
||||
|
||||
def logics(book: str, chapter: str, q: str, blocked: int | None) -> list[dict]:
|
||||
def logics(sub: str, detail: str, q: str, blocked: int | None) -> list[dict]:
|
||||
"""로직 목록 — 구분 · 상세구분 · 찾기로 거름(장 차례대로 · 파일을 가로지름)."""
|
||||
files = cm.load(folder=FOLDER)
|
||||
whole = cm.mf.Master(files)
|
||||
out = []
|
||||
for name, data in sorted(_logic_files(files), key=lambda f: f[1].get("차례") or 0):
|
||||
if (book and data.get("원문") != book) or (chapter and chapter_of(name, data) != chapter):
|
||||
continue
|
||||
for row in data.get("줄") or []:
|
||||
if (sub and row.get("구분") != sub) or (detail and row.get("상세구분") != detail):
|
||||
continue
|
||||
if not _hit(row, q):
|
||||
continue
|
||||
reasons = cm.mf.check_logic(whole, row)[0]
|
||||
@@ -222,9 +269,10 @@ def logics(book: str, chapter: str, q: str, blocked: int | None) -> list[dict]:
|
||||
out.append(
|
||||
{
|
||||
"file": name,
|
||||
"book": data.get("원문"),
|
||||
"chapter": chapter_of(name, data),
|
||||
**{k: row.get(k) for k in ("키", "원문번호", "이름", "결과단위", "출처")},
|
||||
**{
|
||||
k: row.get(k)
|
||||
for k in ("키", "원문번호", "구분", "상세구분", "이름", "결과단위", "출처")
|
||||
},
|
||||
"blocked": bool(reasons),
|
||||
"reasons": reasons,
|
||||
}
|
||||
@@ -232,8 +280,8 @@ def logics(book: str, chapter: str, q: str, blocked: int | None) -> list[dict]:
|
||||
return out
|
||||
|
||||
|
||||
def logic(book: str, key: str) -> dict:
|
||||
"""로직 하나 — 키는 전체에서 하나(`book` 은 화면 호환으로만 받음)."""
|
||||
def logic(key: str) -> dict:
|
||||
"""로직 하나 — 키는 전체에서 하나."""
|
||||
files = cm.load(folder=FOLDER)
|
||||
for name, data in _logic_files(files):
|
||||
for row in data.get("줄") or []:
|
||||
@@ -349,7 +397,7 @@ def _swap(files: dict, key: str, row: dict, file: str | None) -> str:
|
||||
return new
|
||||
|
||||
|
||||
def calc(book: str, key: str, inputs: dict, row: dict | None = None, file: str | None = None):
|
||||
def calc(key: str, inputs: dict, row: dict | None = None, file: str | None = None):
|
||||
given = {k: _dec(v) for k, v in inputs.items()}
|
||||
files = cm.load(folder=FOLDER)
|
||||
if row is not None:
|
||||
@@ -382,6 +430,16 @@ def _dec(v):
|
||||
return v
|
||||
|
||||
|
||||
def _chapter_cols(file: str, data: dict) -> dict:
|
||||
"""장 파일에 더하는 줄의 갈래 — 구분 = 원문 + 부문 · 상세구분 = 「NN장 장 제목」."""
|
||||
if not mk.CHAPTER.match(file):
|
||||
return {}
|
||||
return {
|
||||
"구분": " ".join(x for x in (data.get("원문"), data.get("부문")) if x),
|
||||
"상세구분": mk.detail_of(file),
|
||||
}
|
||||
|
||||
|
||||
def _apply(data: dict, changes: list[dict], file: str, book: dict) -> None:
|
||||
"""고침 · 더함(키는 대장의 다음 번호 · 원문번호 없으면 빈 글) · 지움 — 키는 바꾸지 않음."""
|
||||
items = data.setdefault(items_key(data), [])
|
||||
@@ -398,8 +456,12 @@ def _apply(data: dict, changes: list[dict], file: str, book: dict) -> None:
|
||||
raise StoreError(400, f"{file} · row 없음")
|
||||
if op == "add":
|
||||
number = str(row.get("원문번호") or "")
|
||||
rest = {k: v for k, v in row.items() if k not in ("키", "원문번호")}
|
||||
items.append({"키": mk.issue(book, tid, number, file), "원문번호": number, **rest})
|
||||
cols = _chapter_cols(file, data)
|
||||
drop = ("키", "원문번호", *cols)
|
||||
rest = {k: v for k, v in row.items() if k not in drop}
|
||||
items.append(
|
||||
{"키": mk.issue(book, tid, number, file), "원문번호": number, **cols, **rest}
|
||||
)
|
||||
elif op == "edit":
|
||||
if str(row.get("키")) != key:
|
||||
raise StoreError(400, f"{file} · 키는 바꾸지 않음 「{key}」")
|
||||
|
||||
Reference in New Issue
Block a user