Merge remote-tracking branch 'origin/sub_laptop_1' into sub_laptop_4
This commit is contained in:
@@ -70,6 +70,11 @@ def _hit(row: dict, q: str) -> bool:
|
||||
)
|
||||
|
||||
|
||||
def _book(data: dict) -> str | None:
|
||||
"""머리 원문 이름 — 한 테이블 파일(기계)은 머리 「원문」 이 갈래 묶음이라 이름이 묶음 안에."""
|
||||
return data.get("원문") if isinstance(data.get("원문"), str) else None
|
||||
|
||||
|
||||
def groups() -> list[dict]:
|
||||
out = {g: {"group": g, "files": 0, "rows": 0} for g in cm.mf.GROUPS}
|
||||
for data in cm.load(folder=FOLDER).values():
|
||||
@@ -89,7 +94,7 @@ def files_of(group: str) -> list[dict]:
|
||||
out.append(
|
||||
{
|
||||
"file": path.name,
|
||||
"book": data.get("원문"),
|
||||
"book": _book(data),
|
||||
"chapter": chapter_of(path.name, data),
|
||||
"edition": data.get("판"),
|
||||
"rows": len(data.get(items_key(data)) or []),
|
||||
@@ -130,7 +135,7 @@ _STATES = ("공표", "산정", "미공표", "미확보", "추정") # 인력 「
|
||||
|
||||
|
||||
def subs(file: str = "", group: str = "") -> dict:
|
||||
"""하위 거름 목록 — 한 테이블 파일은 머리 묶음(인력 「구분」 · 기계 「세부분류」) ·
|
||||
"""하위 거름 목록 — 한 테이블 파일은 머리 묶음(인력 「구분」 · 기계 「원문」) ·
|
||||
장 그룹(소요량 · 계수 · 로직)은 장 파일에서 모음(구분 = 원문 + 부문 · 상세구분 = 장 · 차례대로)."""
|
||||
if group:
|
||||
if group not in cm.mf.GROUPS:
|
||||
@@ -146,7 +151,7 @@ def subs(file: str = "", group: str = "") -> dict:
|
||||
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)), "")
|
||||
slot = next((s for s in cm.MERGED.values() if isinstance(data.get(s), dict)), "")
|
||||
lines = data.get(items_key(data)) or []
|
||||
has_state = any("상태" in r for r in lines)
|
||||
|
||||
@@ -184,8 +189,8 @@ def rows(
|
||||
) -> dict:
|
||||
data, version = read(file)
|
||||
hits = [r for r in data.get(items_key(data)) or [] if _hit(r, q)]
|
||||
if sub: # 인력 구분 · 기계 세부분류
|
||||
hits = [r for r in hits if sub in (r.get("구분"), r.get("세부분류"))]
|
||||
if sub: # 인력 구분 · 기계 원문
|
||||
hits = [r for r in hits if sub in {r.get(s) for s in cm.MERGED.values()}]
|
||||
if detail: # 인력 상세구분
|
||||
hits = [r for r in hits if r.get("상세구분") == detail]
|
||||
if state: # 인력 상태
|
||||
@@ -527,7 +532,7 @@ def _follow(row: dict, items: list[dict], file: str) -> dict:
|
||||
def _apply(data: dict, changes: list[dict], file: str, book: dict) -> None:
|
||||
"""고침 · 더함(키는 대장의 다음 번호 · 원문번호 없으면 빈 글) · 지움 — 키는 바꾸지 않음."""
|
||||
items = data.setdefault(items_key(data), [])
|
||||
tid = mk.table_id(data.get("그룹"), data.get("원문"))
|
||||
tid = mk.table_id(data.get("그룹"), _book(data))
|
||||
for ch in changes:
|
||||
op, key, row = ch.get("op"), ch.get("key"), _dec(ch.get("row"))
|
||||
if op not in ("edit", "add", "delete"):
|
||||
|
||||
@@ -68,9 +68,9 @@
|
||||
| 빌더 | 빌더 없음 |
|
||||
| 검사 | `check_master.py 로직` (로직 재료 줄 고르기 조건) · `pytest resources/tester/test_자재품목_분류.py -q` (품셈재료가 부르는 재료의 묶음) |
|
||||
|
||||
## 기계 (`기계.json` · 세부분류 건설품셈 · 산림품셈)
|
||||
## 기계 (`기계.json` · 원문 건설품셈 · 산림품셈)
|
||||
|
||||
| 세부분류 | 출처 · 지식DB 자리 | 받는 법 | 주기 | 빌더 | 검사 |
|
||||
| 원문 | 출처 · 지식DB 자리 | 받는 법 | 주기 | 빌더 | 검사 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| 건설품셈 | 건설품셈 공통 8-3 손료 · 8-4 운전경비 · 8-5 기계가격 · `원가계산/건설공사_표준품셈/본문/01_공통부문/제08장_건설기계/` | 한국표준품셈정보원 PDF — `collect_cost_sources.py docs` → md | 연 1회(품셈 개정) | `scripts/build_기계_건설품셈.py [--dry]` | `check_master.py 틀 기계.json` |
|
||||
| 산림품셈 | 산림품셈 2장 기계손료 · `원가계산/산림_표준품셈/본문/제02장_소요재료_및_기계손료/` | 산림청 고시 PDF · md | 고시 개정 때 | 빌더 없음 | 같음 |
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
|
||||
| 그룹 | 파일 이름 |
|
||||
| -------------------- | --------------------------------------------------------------------------------------- |
|
||||
| 인력 | `인력.json` 하나 — 줄마다 `조사` |
|
||||
| 기계 | `기계.json` 하나 — 줄마다 `세부분류` |
|
||||
| 인력 | `인력.json` 하나 — 줄마다 `구분` |
|
||||
| 기계 | `기계.json` 하나 — 줄마다 `원문` |
|
||||
| 재료 | `재료_<원문>.json` — 자재품목 · 유가전력 · 품셈재료 |
|
||||
| 환율 · 요율 | `환율_한국은행환율.json` · `요율_조달청제비율.json` |
|
||||
| 소요량 · 계수 · 로직 | `<그룹>_<원문>_<NN>장_<장 제목>.json` — 예 `소요량_건설품셈_10장_창호_및_유리공사.json` |
|
||||
@@ -94,7 +94,9 @@
|
||||
- `상세구분` = 제조업 업종 · 엔지니어링 부문 · 측량 기술계/기능계 분야 — 없으면 `null` · 목록은 머리에.
|
||||
- `상태` = 공표 · 산정(미공표 직종의 원문 4-라 값을 `값` 에 넣음 · 근거는 `비고` 한 줄) · 미공표(원문에 값 없음) · 미확보(공표 노임에 같은 직종 없음) · 추정(원문이 이름을 맞대지 않아 작업 범위로 짐작한 대응 — `준용` 에 대응 직종 키 · `구분` 은 관리자가 정함 · `값`·`단위`·`일시간` 은 빌더가 준용 직종에서 채움 · 근거 문서는 `출처` 짧게).
|
||||
- `일시간` = 1일 노임 기준 시간 — 8 · 잠수부만 6(원문 4-가).
|
||||
- 기계 `세부분류` = 건설품셈 · 산림품셈 · 세부분류별 원문·판·주는 머리 `세부분류` 묶음.
|
||||
- 기계 `원문` = 건설품셈 · 산림품셈 · 원문별 판·주는 머리 `원문` 묶음(한 테이블 파일은 머리 `원문` 이 갈래 묶음 · 이름은 묶음 열쇠).
|
||||
- 기계 `구분`·`상세구분` = 원문 기계 분류 그대로(`[00]토공기계` … `임업기계` 열하나 · 상세구분은 장비 이름) · 목록은 `ref/_묶음_기계.md`.
|
||||
- 기계 `값원문` = 원문 글이 수 하나로 안 떨어지는 값의 원문 글 묶음 — `{ "8-4-2 주연료": "휘발유0.7" }`(있는 줄만).
|
||||
- 값이 여럿인 요소(기계)는 `값` 을 묶음으로 — `{ "가격": …, "상각비계수": …, "정비비계수": …, "관리비계수": …, "손료계수": …, "주연료": …, "주연료종류": …, "잡재료율": …, "조종원": …, "조종원직종": … }` · `손료계수` = 원문 「계」.
|
||||
- 묶음 요소는 `단위` · `출처` 도 같은 모양의 묶음.
|
||||
- 원문에 없는 값을 지어 넣지 않음. 원문 「-」 = 칸을 비움(`null`).
|
||||
@@ -111,7 +113,7 @@
|
||||
- 값 열 다섯 = 물가지 넷(물가자료 · 유통물가 · 물가정보 · 거래가격) · `관급`(조달) — 채택값·기본가·견적가 두지 않음.
|
||||
- `관급` = 원문 「가격정보」 칸의 조달청 관급 단가(철근 · 레미콘 · 아스콘 · H형강 등) · 기간은 `비고` 「조달 26.7」 · 「조달 ~29.3.18」.
|
||||
- 그 밖의 줄에 남은 「가격정보」 값은 출처가 조달인지 물가지인지 원문이 안 가름 — 어느 값 열에도 넣지 않고 `비고` 「가격정보 1,485 · 2026.7」 로만(67 줄).
|
||||
- `구분` · `상세구분` = 원문(ESTX)에 대·중분류 칸이 없어 비움 — 지어내지 않음.
|
||||
- `구분` · `상세구분` = 원문(ESTX)에 대·중분류 칸이 없어 품명 글자 규칙으로 채움(`scripts/build_자재품목_분류.py`) · 묶음 목록은 `ref/_묶음_자재품목.md` · 못 가른 줄은 「미분류」.
|
||||
- `면수` = 물가지 값의 원문 면수 묶음 `{ 물가자료, 유통물가, 물가정보, 거래가격 }` · 없으면 `null` · 화면에는 접어 둠.
|
||||
- **원문번호 칸 없음** — 원문(ESTX)이 준 품번 `M0005250505` 는 남의 번호라 이름 몫을 못 함 · 대조는 `_키대장.json` 에만 둠(조달청에서 새로 더한 줄의 조달청 번호도 같음). 로직·연결은 키로 적음.
|
||||
- 유류·전력은 뗌 — 유가·전기요금은 `재료_유가전력`(유류는 값이 비고 글뿐 · 중유는 받지 않음).
|
||||
@@ -136,6 +138,7 @@
|
||||
"이름": "메쌓기(인력)",
|
||||
"기준": "1㎡",
|
||||
"출처": "산림품셈 13-4-1",
|
||||
"용도": { "공종": "돌쌓기", "대상": ["인력"], "로직키": ["GF000123"] },
|
||||
"조건": { "뒷길이": "수", "돌": "고르기", "쌓기": "고르기" },
|
||||
"값칸": { "석공": "인", "보통인부": "인" },
|
||||
"줄": [{ "뒷길이": 35, "돌": "견치돌", "쌓기": "골쌓기", "석공": 0.5, "보통인부": 0.4 }],
|
||||
@@ -144,7 +147,7 @@
|
||||
```
|
||||
|
||||
- 장 파일 줄은 갈래를 스스로 지님 — `구분` = 원문 + 부문(「산림품셈」 · 「건설품셈 공통」 · 「건설품셈 토목」 …) · `상세구분` = 「NN장 장 제목」(파일 이름 그대로 · 「13장 구조물」). 파일 머리의 `부문` · `차례` 는 목록 차례로 남김.
|
||||
- 열 차례 — 표는 키 · 원문번호 · 구분 · 상세구분 · 이름 · 기준 · 출처 · 비고 · 조건 · 범위규칙 · 값칸 · 줄 · 주 · `그룹`(소요량 파일 안의 계수 표 표시 · 맨 뒤) · 로직은 키 · 원문번호 · 구분 · 상세구분 · 이름 · 결과단위 · 출처 · 소유 · 비고 · 입력 · 중간 · 호표 · 덧줄 · 결과 · 끝수.
|
||||
- 열 차례 — 표는 키 · 원문번호 · 구분 · 상세구분 · 이름 · 기준 · 출처 · 비고 · 용도 · 조건 · 범위규칙 · 값칸 · 줄 · 주 · `그룹`(소요량 파일 안의 계수 표 표시 · 맨 뒤) · 로직은 키 · 원문번호 · 구분 · 상세구분 · 이름 · 결과단위 · 출처 · 소유 · 비고 · 입력 · 중간 · 호표 · 덧줄 · 결과 · 끝수.
|
||||
- 갈래 목록은 서버가 장 파일에서 모아 줌(`GET /subs?group=…` · 장 차례대로) — 화면이 이름을 품지 않음.
|
||||
- 건설품셈 부문은 `구분` 칸 — 「건설품셈 건축」 · 원문번호는 절 번호만(「1-1-1」).
|
||||
- 조건 종류 — `수`(같은 값) · `고르기`(글자) · `범위`(줄에 `[아래, 위]` · 끝 열림은 `null` · 포함 여부는 `범위규칙` 에 「초과·이하」처럼).
|
||||
@@ -157,6 +160,8 @@
|
||||
- 분수·식으로 적힌 값(「L/V×2×60」)은 값 칸에 식 글자 그대로 · `<값 칸>원문` 칸에 원문.
|
||||
- 원문에 짝 표시 없이 나란히 놓인 줄은 `짝` 칸으로 묶음.
|
||||
- 수는 원문 그대로(쉼표만 뺌). 단위 환산하지 않음.
|
||||
- `용도` = 그 표를 어디에 쓰는지 — `공종`(원문 항 제목) · `대상`(인력 · 기계 · 재료 · 작업량 조건 …) · `로직키`(그 표를 부르는 로직 키 목록 · 없으면 빈 목록).
|
||||
채우기 = `scripts/build_용도.py`(로직 식에서 표 키를 거꾸로 모음 · 다시 돌리면 같은 결과) · 표 3,029개 모두 있음(로직이 부르는 것 1,852).
|
||||
- `주` 는 원문 글 그대로 — 계산에 쓰는 수가 들어 있으면 따로 계수 표로도 올림.
|
||||
|
||||
## 7. 로직 파일
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
# 기계 묶음 (구분 · 상세구분)
|
||||
|
||||
`기계.json` 줄에서 뽑은 표 — 손으로 고치지 않음.
|
||||
|
||||
- 구분 11 · 상세구분 174 (전체 654 줄)
|
||||
|
||||
## 규칙
|
||||
|
||||
- 구분 = 원문 기계 분류 코드 그대로(건설품셈 8-3 `[00]`~`[90]` · 산림품셈은 「임업기계」).
|
||||
- 상세구분 = 장비 이름 묶음 — 규격·톤수는 `규격` 칸으로 가름.
|
||||
- 줄의 `원문`(건설품셈 · 산림품셈)은 갈래 칸 — 구분과 다름.
|
||||
|
||||
## 구분별 상세구분 (줄 수)
|
||||
|
||||
### [00]토공기계 (80)
|
||||
|
||||
로더 17 · 굴착기 15 · 불도저 10 · 스크레이퍼 9 · 덤프트럭 9 · 브레이커 6 · 유압식 리퍼 5 · 덤프트럭 자동덮개시설 3 · 압쇄기 2 · 모터그레이더 2 · 유압식 진동콤팩터 1 · 트랜처 1
|
||||
|
||||
### [10]다짐기계 (32)
|
||||
|
||||
탠덤롤러 10 · 양족식 롤러 8 · 진동롤러 6 · 머캐덤 롤러 3 · 타이어 롤러 3 · 래 머 1 · 플레이트 콤팩터 1
|
||||
|
||||
### [20]운반 및 하역기계 (74)
|
||||
|
||||
크레인 34 · 트럭탑재형 크레인 6 · 리더 5 · 타워크레인 5 · 지게차 5 · 트랙터 4 · 트럭 트랙터 및 평판트레일러 4 · 고소작업차 3 · 케이싱 2 · 디젤 기관차 2 · 터널용고소작업차 1 · 스킵버킷 1 · 건설용리프트 1 · 경운기 1
|
||||
|
||||
### [30]포장기계 (29)
|
||||
|
||||
콘크리트 피니셔 6 · 아스팔트 믹싱플랜트 5 · 아스팔트 디스트리뷰터 4 · 아스팔트 피니셔 2 · 아스팔트 스프레어 2 · 아스팔트 스프레이어 2 · 스테이빌라이저 2 · 콘크리트 조면 마무리기 2 · 현장가열 표층재생기 1 · 콘크리트 스프레더 1 · 콘크리트 롤러페이버 1 · 슬러리실 기계 1
|
||||
|
||||
### [40]콘크리트기계 (33)
|
||||
|
||||
콘크리트 펌프차 8 · 콘크리트 배치플랜트 6 · 콘크리트 믹서 6 · 사일로 4 · 콘크리트 믹서트럭 2 · 콘크리트 펌프 2 · 초고압펌프 2 · 콘크리트 진동기 2 · 커터 1
|
||||
|
||||
### [50]골재생산기계 등 (103)
|
||||
|
||||
스크린 16 · 죠 크러셔 11 · 롤 크러셔 8 · 파이프추진기 8 · 아그리게이트빈 7 · 공기압축기 6 · 벨트 컨베이어 5 · 에이프런 피더 5 · 유압잭 5 · 브레이커 5 · 크러셔 4 · 콘 크러셔 4 · 크롤러드릴 4 · 터널전단면 굴착기 4 · 노면파쇄기 3 · 코아드릴 3 · 점보드릴 2 · 골재세척설비 1 · 드릴웨곤 1 · 유압식할암기 1
|
||||
|
||||
### [60]기초공사용 기계 (67)
|
||||
|
||||
보링 기계 7 · 진동파일 해머 7 · 파일천공전용장비 6 · 오거 5 · 오실레이터, 로테이터 5 · 리버스서큘레이션드릴 5 · 전회전식천공기 5 · 유압 파일 해머 5 · 디젤 파일 해머 4 · 강연선인장기 4 · 그라우팅 펌프 3 · 그라우팅 믹서 2 · PBD천공기 2 · 다짐말뚝 전용장비 2 · 유압파워팩 1 · 워터젯트 1 · 유압식 압입 인발기 1 · 고압분사전용장비 1 · 자동화 믹서플랜트 1
|
||||
|
||||
### [70]기타기계 (117)
|
||||
|
||||
엔진 17 · 모터 15 · 발전기 11 · 윈치 9 · 용접기 7 · 용착기 6 · 물탱크 5 · 건설용펌프 5 · 이동식 임목파쇄기 3 · 부착용 집게 3 · 수중모터 펌프 3 · 모르타르 펌프 3 · 진공흡입 준설차 2 · 자동세륜기 2 · 차선 제거기 2 · 절단기 2 · 취부기 2 · 고성능 착정기 1 · 하수관 천공기 1 · 상수도관 천공기 1 · 골재 살포기 1 · 버킷식준설기 1 · 동력분무기 1 · 라인 마커 1 · 알곤 용접기 1 · 프라즈마 절단기 1 · 실사출기 1 · 우레탄폼 분사용기구 1 · 레일천공기 1 · 파워렌치 1 · 침목천공기 1 · 타이템퍼 1 · 양로기 1 · 모르타르 믹서 1 · 양수기 1 · POWER TLOWEL 1 · 배관파이프 1
|
||||
|
||||
### [80]스마트 건설장비 (5)
|
||||
|
||||
3D GNSS 머신 가이던스 2 · 3D GNSS 머신 컨트롤 2 · 지능형 다짐공 시스템 1
|
||||
|
||||
### [90]해상장비 (79)
|
||||
|
||||
대선 16 · 펌프 준설선 13 · 양묘선 11 · 그래브 준설선 10 · 예 선 9 · 하천골재채취선 7 · 토운선 6 · 기중기선 5 · 이우선 2
|
||||
|
||||
### 임업기계 (35)
|
||||
|
||||
굴착기 3 · 포워더 2 · 체인톱 1 · 예취기 1 · 아키아윈치 1 · 2드럼 케이블윈치 1 · 파르미윈치 1 · 타이푼윈치 1 · 우드피싱 1 · 임내차 1 · 스마트집재기 1 · HAM300 1 · 콜라타워야더 1 · 임업용 동력집재기 1 · HAM200, 춘천집재기, 스마트집재기 1 · 하베스터 1 · 동력상하차기, 임업용 굴착기 1 · 타워야더 1 · 스윙야더 1 · 트럭 1 · 배부식분무기 1 · 천공기 1 · 양수기 1 · 드론 무인 헬리콥터 1 · 드론 무인 멀티콥터 1 · 리튬폴리머 배터리 1 · 1톤 1 · 동력분무기 1 · 파쇄 1 · 부착용집게 1 · 트럭탑재형크레인 1 · 타이어형크레인 1
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
본문 md(확정) 표를 읽어 기계 한 줄로 합침. 값은 md 에서만 · 계산·추정 없음.
|
||||
실행: python resources/master_data/scripts/build_기계_건설품셈.py [--dry]
|
||||
옛 모양(열쇠)으로 쓰고 `master_keys.adopt` 가 기계.json(키 · 세부분류 건설품셈)으로 받음.
|
||||
옛 모양(열쇠)으로 쓰고 `master_keys.adopt` 가 기계.json(키 · 원문 건설품셈)으로 받음.
|
||||
"""
|
||||
|
||||
import json
|
||||
@@ -504,7 +504,7 @@ def compose(rows):
|
||||
org = dict(v.get("원문83") or {})
|
||||
org.update({f"{v['sec84']} {a}": b for a, b in (v.get("원문84") or {}).items()})
|
||||
if org:
|
||||
row["원문"] = org
|
||||
row["값원문"] = org
|
||||
out.append(row)
|
||||
return out
|
||||
|
||||
@@ -512,7 +512,7 @@ def compose(rows):
|
||||
def dumps_row(r):
|
||||
j = lambda o: json.dumps(o, ensure_ascii=False)
|
||||
head = f'{{ "열쇠": {j(r["열쇠"])}, "이름": {j(r["이름"])}, "규격": {j(r["규격"])},'
|
||||
tail = [k for k in ("비고", "원문") if k in r]
|
||||
tail = [k for k in ("비고", "값원문") if k in r]
|
||||
body = [f' "단위": {j(r["단위"])},', f' "값": {j(r["값"])},']
|
||||
body.append(f' "출처": {j(r["출처"])}' + ("," if tail else " }"))
|
||||
for n, k in enumerate(tail):
|
||||
|
||||
@@ -94,8 +94,8 @@ ROW_KEYS = {
|
||||
),
|
||||
"품셈재료": ("키", "원문번호", "이름", "규격", "단위", "요구절", "연결", "고르기"),
|
||||
}
|
||||
# 한 테이블 파일 — 줄의 갈래 칸 · 머리의 갈래 묶음
|
||||
MERGED = {"인력": "구분", "기계": "세부분류"}
|
||||
# 한 테이블 파일 — 줄의 갈래 칸 · 머리의 갈래 묶음 (칸 이름은 `master_keys.MERGED` 한 곳에서)
|
||||
MERGED = {group: slot for group, (_, slot, _) in mk.MERGED.items()}
|
||||
# 인력 줄 — 모든 줄이 같은 열 한 벌 · 상태 값
|
||||
LABOR_COLS = (
|
||||
"키",
|
||||
@@ -222,25 +222,23 @@ def check_form(name: str, data: dict) -> list[str]:
|
||||
f"{name} · 파일 이름이 「그룹_원문[_NN장_장 제목].json」 · 「인력.json」 · 「기계.json」 아님"
|
||||
]
|
||||
out = _head(name, data, m)
|
||||
# 한 테이블 파일(기계)은 머리 「원문」 이 갈래 묶음 — 원문 이름은 묶음 안에
|
||||
book = data.get("원문") if isinstance(data.get("원문"), str) else None
|
||||
try:
|
||||
tid = mk.table_id(data.get("그룹"), data.get("원문"))
|
||||
tid = mk.table_id(data.get("그룹"), book)
|
||||
except KeyError:
|
||||
return out + [f"{name} · 테이블ID 없는 그룹·원문"]
|
||||
merged = MERGED.get(data.get("그룹"))
|
||||
table = data.get("그룹") in mf.TABLE_GROUPS
|
||||
kind = "표" if table else "로직" if data.get("그룹") == "로직" else "요소"
|
||||
if data.get("원문") == "품셈재료":
|
||||
if book == "품셈재료":
|
||||
kind = "품셈재료"
|
||||
rows = data.get("표" if table else "줄")
|
||||
if not isinstance(rows, list):
|
||||
return out + [f"{name} · 머리 「{'표' if table else '줄'}」 목록 없음"]
|
||||
division = data.get("부문") if kind != "요소" else None
|
||||
# 장 파일 줄의 갈래 — 구분 = 원문 + 부문 · 상세구분 = 「NN장 장 제목」(파일 이름 그대로)
|
||||
want = (
|
||||
(" ".join(x for x in (data.get("원문"), division) if x), _detail(name))
|
||||
if m.group(4)
|
||||
else None
|
||||
)
|
||||
want = (" ".join(x for x in (book, division) if x), _detail(name)) if m.group(4) else None
|
||||
for row in rows:
|
||||
key, number = str(row.get("키")), str(row.get("원문번호") or "")
|
||||
where = f"{name} · {key} {row.get('이름') or number}"
|
||||
@@ -257,7 +255,7 @@ def check_form(name: str, data: dict) -> list[str]:
|
||||
f"{where} · 구분·상세구분 「{row.get('구분')} · {row.get('상세구분')}」"
|
||||
f" 이 파일 머리·이름 「{want[0]} · {want[1]}」 과 다름"
|
||||
)
|
||||
cols = COLS.get(data.get("원문"))
|
||||
cols = COLS.get(book)
|
||||
for need in cols or ROW_KEYS[kind]:
|
||||
if need not in row:
|
||||
out.append(f"{where} · 칸 「{need}」 없음")
|
||||
|
||||
@@ -269,7 +269,7 @@ def machine(estx: Estx, files: dict, master: mf.Master):
|
||||
for r in estx.table("MachTitleDat"):
|
||||
if r.get("Unite") == "hr" and r.get("TC"):
|
||||
est.setdefault(mach_key(r.get("Name"), r.get("Size")), r)
|
||||
ours = [m for m in files["기계.json"]["줄"] if m["세부분류"] == "건설품셈"]
|
||||
ours = [m for m in files["기계.json"]["줄"] if m["원문"] == "건설품셈"]
|
||||
pairs = [(m, est[k]) for m in ours if (k := mach_key(m["이름"], m["규격"])) in est]
|
||||
print(f"## 기계 — 우리 {len(ours)} · ESTX 시간당 {len(est)} · 기종·규격 짝 {len(pairs)}")
|
||||
out = []
|
||||
|
||||
@@ -134,7 +134,7 @@ LABOR = {
|
||||
}
|
||||
MERGED = {
|
||||
"인력": ("인력.json", "구분", LABOR),
|
||||
"기계": ("기계.json", "세부분류", {"건설품셈": "건설품셈", "산림품셈": "산림품셈"}),
|
||||
"기계": ("기계.json", "원문", {"건설품셈": "건설품셈", "산림품셈": "산림품셈"}),
|
||||
}
|
||||
DIVISIONS = ("공통", "토목", "건축", "기계설비", "유지관리")
|
||||
# 장 파일 줄의 열 차례 — 앞 넷은 한 줄에 · 「그룹」(소요량 파일 안의 계수 표 표시)은 맨 뒤
|
||||
|
||||
+689
-689
File diff suppressed because it is too large
Load Diff
@@ -332,7 +332,7 @@ def test_구분_목록(client: TestClient) -> None:
|
||||
survey = next(s for s in got["subs"] if s["name"] == "측량")
|
||||
assert survey["book"] == "측량노임" and "기능계 도화" in survey["details"]
|
||||
machine = _get(client, "/api/m01/subs", file="기계.json")
|
||||
assert machine["slot"] == "세부분류" and [s["name"] for s in machine["subs"]] == [
|
||||
assert machine["slot"] == "원문" and [s["name"] for s in machine["subs"]] == [
|
||||
"건설품셈",
|
||||
"산림품셈",
|
||||
]
|
||||
@@ -368,7 +368,7 @@ def test_하위_거름과_참조_이름(client: TestClient) -> None:
|
||||
assert 0 < field["total"] < survey["total"]
|
||||
assert {r["상세구분"] for r in field["rows"]} == {"기능계 도화"}
|
||||
machine = _get(client, "/api/m01/rows", file="기계.json", size=500, sub="산림품셈")
|
||||
assert 0 < machine["total"] < 100 and {r["세부분류"] for r in machine["rows"]} == {"산림품셈"}
|
||||
assert 0 < machine["total"] < 100 and {r["원문"] for r in machine["rows"]} == {"산림품셈"}
|
||||
linked = _get(client, "/api/m01/rows", file="재료_품셈재료.json", size=500)
|
||||
keyed = [r for r in linked["rows"] if isinstance(r["연결"], str)]
|
||||
assert keyed and all(linked["refs"][r["연결"]] for r in keyed)
|
||||
|
||||
Reference in New Issue
Block a user