Merge remote-tracking branch 'origin/dev' into sub_desktop_1

This commit is contained in:
2026-09-20 22:02:22 +09:00
10 changed files with 1252 additions and 952 deletions
@@ -165,6 +165,14 @@ export const searchElements = (
): Promise<{ total: number; items: ElementBrief[] }> =>
request(`/elements?${query({ group, q, limit: "100" })}`);
export const fetchMaterials = (cond: {
sub: string;
detail?: string;
spec?: string;
region?: string;
}): Promise<{ total: number; 기본: string | null; items: ElementBrief[] }> =>
request(`/materials?${query({ ...cond, limit: "100" })}`);
export const runCalc = (body: {
key: string;
inputs: Record<string, unknown>;
+4 -2
View File
@@ -8,6 +8,8 @@
import { createSelectField, el, showToast } from "@ui/ui_template_elements";
import { fetchLogics } from "./M01_MasterData_UI_Logic_Api";
import { render as renderA } from "./M01_MasterData_UI_Test_A";
import { render as renderB } from "./M01_MasterData_UI_Test_B";
import { render as renderC } from "./M01_MasterData_UI_Test_C";
import { tt } from "./M01_MasterData_UI_Test_Text";
import "./M01_MasterData_UI_Test_Style.css";
@@ -31,8 +33,8 @@ type Render = (host: HTMLElement, logicKey: string) => void;
/** 방식 파일이 오면 여기 한 줄씩 잇는다 — null 은 준비 중 */
const METHODS: { id: string; label: () => string; render: Render | null }[] = [
{ id: "A", label: () => tt("Tab_A"), render: renderA },
{ id: "B", label: () => tt("Tab_B"), render: null },
{ id: "C", label: () => tt("Tab_C"), render: null },
{ id: "B", label: () => tt("Tab_B"), render: renderB },
{ id: "C", label: () => tt("Tab_C"), render: renderC },
];
export function mountM01Test(host: HTMLElement): void {
+13 -1
View File
@@ -7,6 +7,7 @@
import { createButton, createSelectField, el, showToast } from "@ui/ui_template_elements";
import {
fetchLogic,
fetchMaterials,
runCalc,
searchElements,
type CalcAnswer,
@@ -147,7 +148,18 @@ function wizard(host: HTMLElement, one: LogicOne): void {
else swaps.delete(i);
},
});
void searchElements(line., line. ?? "")
// 재료 고르기 조건({구분, 상세구분, 규격}) 이면 새 API 로 조건 안 후보 · 아니면 이름 찾기
const cond = line. as unknown;
const found_ =
cond && typeof cond === "object"
? fetchMaterials({
sub: String((cond as Record<string, unknown>)["구분"] ?? ""),
detail: String((cond as Record<string, unknown>)["상세구분"] ?? ""),
spec: String((cond as Record<string, unknown>)["규격"] ?? ""),
region: "울진",
})
: searchElements(line., line. ?? "");
void found_
.then((r) => {
r.items.forEach((it) => found.set(it.ref, it));
select.setOptions(
+137
View File
@@ -0,0 +1,137 @@
# 마스터 갱신 절차
테이블마다 한 줄씩. 명령은 저장소 루트에서 `./venv/Scripts/python.exe …` (표기는 `scripts/…` = `resources/master_data/scripts/…`).
`빌더 없음` = 구멍. 돌린 뒤 검사는 언제나 `check_master.py`(틀 · 본문 · 로직)를 더함.
공통:
- 원문 받기 = 지식DB 폴더에 원본을 두고 md 로 옮긴 뒤(`resources/knowledge/original/_pipeline/`) 빌더를 돌림.
- 빌더는 옛 모양으로 쓰고 끝에 `master_keys.adopt` 가 키 · 파일 이름으로 받음. 새 키는 `_키대장.json` 이 냄.
- API 키는 `resources/knowledge/.secrets.local.md` 에서 읽음 — 출력·커밋 금지.
- 공통 검사 — `check_master.py 틀` · `pytest resources/tester/test_masterdata_check.py -q`.
## 인력 (`인력.json` · 구분별)
| 구분 | 출처 원문 · 지식DB 자리 | 받는 법 | 주기 | 빌더 | 검사 |
| --- | --- | --- | --- | --- | --- |
| 건설업 | 건설노임 · `원가계산/노임단가_건설업_대한건설협회/` | 협회 사이트 PDF — `_pipeline/collect_cost_sources.py docs` (게시글 uuid 를 먼저 갱신) → `_pipeline/split_cost_docs.py`(md) | 반기(12월 말→1/1 · 8월 말→9/1) | `scripts/build_인력_노임.py` | 빌더가 md 와 줄 수·값 자체 대조 · `check_master.py 틀 인력.json` |
| 제조업 | 제조노임 · `원가계산/노임단가_제조업_중소기업중앙회/` | 중기중앙회 게시판 PDF — `collect_cost_sources.py docs` (게시글 seq 갱신) → `split_cost_docs.py kbiz` | 반기(6월 말→7/1 · 11월 말→1/1) | `scripts/build_인력_노임.py` | 같음 |
| 엔지니어링 | 엔지니어링노임 · `원가계산/노임단가_엔지니어링_한국엔지니어링협회/` | 협회 게시물 첨부 손으로 내려받음 (`_meta.md` 의 게시물 번호) | 연 1회(12월 공표→1/1) | `scripts/build_인력_노임.py` | 같음 |
| 측량 | 측량노임 · `원가계산/노임단가_측량_한국공간정보산업협회/` | 협회 게시물 첨부 손으로 | 연 1회 | `scripts/build_인력_노임.py` | 같음 |
| 건설사업관리 | 건설사업관리노임 · `원가계산/노임단가_건설사업관리_한국건설엔지니어링협회/` | 협회 공표 PDF 손으로 | 연 1회 | `scripts/build_인력_노임.py` | 같음 |
| SW | SW노임 · `원가계산/노임단가_SW기술자_한국인공지능소프트웨어산업협회/` | 협회 사이트 XLSX(`_meta.md` 주소) 손으로 | 연 1회 | `scripts/build_인력_노임.py` | 같음 |
| 산림 | 산림노임 · `원가계산/노임단가_산림_산림청/` | 산림청 게시글 HTML 손으로 | 수시 | `scripts/build_인력_노임.py` | 같음 |
- 건설·제조 미공표 직종은 빌더가 산정. 추정 줄은 준용 직종에서 되채움.
## 자재품목 · 물가지 (`재료_자재품목.json`)
| 항목 | 값 |
| --- | --- |
| 출처 | 물가지 네 열 — 상용 적산 자료(ESTX) · `원가계산/다산소프트/2026-07-main.estx` / 나라장터 가격정보 — `원가계산/자재단가/나라장터_시설공통자재_*.json` |
| 받는 법 | ESTX = 구매 파일(배포 전 판권 구매 뒤 갱신) · 나라장터 = `collect_cost_sources.py values` (API) · 유료 물가지 직접 구독은 미정 |
| 주기 | ESTX 반기 · 나라장터 연 2회+수시 |
| 빌더 | `scripts/build_재료_자재품목.py``scripts/build_자재품목_분류.py`(구분·상세구분 채움 · `ref/_묶음_자재품목.md` 새로 씀) |
| 검사 | `scripts/compare_estx.py 자재` · `pytest resources/tester/test_자재품목_분류.py -q` · `check_master.py 틀 재료_자재품목.json` |
- 나라장터 스냅샷에서 마스터 줄을 뽑는 빌더 없음 — 구멍.
## 관급 (자재품목 「관급」 열)
| 항목 | 값 |
| --- | --- |
| 출처 | 조달청 종합쇼핑몰 OpenAPI(15129471) · 원본 `원가계산/자재단가/관급/<받은 날>/*.json.gz` · 설계 `ref/_설계_관급단가_갱신.md` |
| 받는 법 | `scripts/update_관급단가.py fetch` (API · 일일 호출 한도) |
| 주기 | 계약 기간 단위 · 견적 기준일마다 |
| 빌더 | `scripts/update_관급단가.py update` (받기 + 정본 관급 열 갱신 · 바뀐 줄 `tmp/관급_바뀐줄.json`) · 보강 후보 = `scripts/update_관급단가_보강.py fetch``build` (`ref/_관급_보강_후보.json`) |
| 검사 | `scripts/update_관급단가.py test` (정본 안 덮음 · `tmp/관급_대조.json`) · `pytest resources/tester/test_masterdata_procure.py -q` |
## 유가전력 (`재료_유가전력.json`)
| 항목 | 값 |
| --- | --- |
| 출처 | 오피넷 유가 · `원가계산/유가_오피넷/` / 한전 전기요금표 별표1(C) · `원가계산/전기요금_한국전력/20250101/` |
| 받는 법 | 유가 = `collect_cost_sources.py values` (오피넷 API) · 전기요금 = 한전 요금표 손으로 |
| 주기 | 유가 주간 · 견적 기준일마다 · 전기요금 개정 때 |
| 빌더 | `scripts/build_재료_유가전력.py` |
| 검사 | `check_master.py 틀 재료_유가전력.json` |
- 휘발유·경유는 옛 판 값 그대로 지님 — 새로 받아도 갱신 안 됨(빌더 안 새 판 반영 없음) — 구멍.
## 품셈재료 (`재료_품셈재료.json`)
| 항목 | 값 |
| --- | --- |
| 출처 | 품셈이 부르는 재료 — 산림품셈 · 건설품셈 본문 · `원가계산/산림_표준품셈/본문/` · `원가계산/건설공사_표준품셈/본문/` |
| 받는 법 | 손으로 줄 추가 · 연결(자재품목 키)은 M01 화면에서 사람이 이음 |
| 주기 | 품셈 개정 때 |
| 빌더 | 빌더 없음 |
| 검사 | `check_master.py 로직` (로직 재료 줄 고르기 조건) · `pytest resources/tester/test_자재품목_분류.py -q` (품셈재료가 부르는 재료의 묶음) |
## 기계 (`기계.json` · 세부분류 건설품셈 · 산림품셈)
| 세부분류 | 출처 · 지식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 | 고시 개정 때 | 빌더 없음 | 같음 |
- 운전경비의 환율·유가는 마스터가 아니라 로직이 환율·유가전력 줄을 부름.
## 소요량 (`소요량_<원문>_<NN>장_….json`)
| 범위 | 출처 · 지식DB 자리 | 받는 법 | 주기 | 빌더 | 검사 |
| --- | --- | --- | --- | --- | --- |
| 건설 공통 2장 | 건설품셈 본문 `01_공통부문/제02장_가설공사` | PDF → md | 연 1회 | `scripts/build_소요량_건설품셈_공통2장.py` | `check_master.py 본문 소요량_건설품셈_02장_가설공사.json` |
| 건설 공통 3장 | 건설품셈 본문 `01_공통부문/제03장_토공사` | 같음 | 같음 | `scripts/build_소요량_건설품셈_공통3장.py` | `check_master.py 본문 소요량_건설품셈_03장_토공사.json` |
| 건설 토목 | 건설품셈 본문 `02_토목부문` 1·2·6장 | 같음 | 같음 | `scripts/build_소요량_건설품셈_토목.py` | `check_master.py 본문 <해당 장 파일>` |
| 건설 기계설비 7~13장 | 건설품셈 본문 `04_기계설비부문` | 같음 | 같음 | `scripts/build_소요량_건설품셈_기계설비.py [7 8 …]` | `check_master.py 본문 <해당 장 파일>` |
| 건설 건축 · 유지관리 | 건설품셈 본문 `03_건축부문` · `05_유지관리부문` | 같음 | 같음 | 빌더 없음 | — |
| 산림 1~14장 | 산림품셈 본문 `원가계산/산림_표준품셈/본문/제NN장_…/` | 산림청 고시 PDF → md(절 하나 = md 하나) | 고시 개정 때 | 빌더 없음 | `check_master.py 본문 <해당 장 파일>` |
## 계수 (`계수_<원문>_<NN>장_….json`)
| 범위 | 출처 · 지식DB 자리 | 받는 법 | 주기 | 빌더 | 검사 |
| --- | --- | --- | --- | --- | --- |
| 산림 1장 추가(1-1-3 · 1-2 · 1-6 · 1-7) | 산림품셈 본문 `제01장_적용기준` | 산림청 고시 md | 고시 개정 때 | `scripts/build_계수_산림품셈_01장_추가.py` (이미 넣었으면 멈춤 — 한 번 돌림) | `check_master.py 본문 계수_산림품셈_01장_적용기준.json` |
| 산림 그 밖 장 | 산림품셈 본문 각 장 | 같음 | 같음 | 빌더 없음 | 같음 |
| 건설 전 장 | 건설품셈 본문 각 부문 | 같음 | 연 1회 | 빌더 없음(소요량 빌더 안 계수 손 표는 소요량에 들어감) | 같음 |
## 환율 (`환율_한국은행환율.json`)
| 항목 | 값 |
| --- | --- |
| 출처 | 한국은행환율 · `원가계산/환율_한국은행ECOS/환율_일별_*.csv` (ECOS 731Y001 · 5통화) |
| 받는 법 | `collect_cost_sources.py values` (ECOS API) |
| 주기 | 일별 · 견적 기준일마다 |
| 빌더 | 빌더 없음 (`scripts/_migrate_base.py` 는 옛 파일 한 번 옮기기) |
| 검사 | `check_master.py 틀 환율_한국은행환율.json` |
## 요율 (`요율_조달청제비율.json`)
| 항목 | 값 |
| --- | --- |
| 출처 | 조달청제비율 · `행정규칙/토목공사원가계산 제비율 적용기준/첨부/*조달청_토목공사_제비율.xlsx` + 국민연금법 `법률/국민연금법/현행_*.xml` |
| 받는 법 | 조달청 고시 첨부 XLSX · 법령 XML 손으로 (`_pipeline/collect_law.py` 는 법령 쪽) |
| 주기 | 고시 개정 때(연 1~2회) · 국민연금 요율은 매년 |
| 빌더 | 빌더 없음 (`scripts/_migrate_base.py` 는 옛 파일 한 번 옮기기) |
| 검사 | `check_master.py 틀 요율_조달청제비율.json` |
## 로직 (`로직_<원문>_<NN>장_….json`)
| 항목 | 값 |
| --- | --- |
| 출처 | 품셈 본문 절 + 위 요소·표 파일(키로 끌어옴) · 자체 로직은 원문 「자체」 |
| 받는 법 | M01 화면에서 편집·저장 (정본 파일 직접 편집도 가능) |
| 주기 | 요소 갱신 때 · 품셈 개정 때 |
| 빌더 | 빌더 없음 |
| 검사 | `check_master.py 로직` (끊긴 키 · 돌고 도는 참조 · 찾기 조건) · `check_master.py 계산 <로직키> 이름=값 …` (견본 계산) · `pytest resources/tester/test_masterdata_check.py resources/tester/test_m01_api.py -q` |
| 복잡도 | `scripts/로직_복잡도_재기.py [상위 N]` (읽기 전용) |
## 구멍 목록
- 환율 · 요율 · 품셈재료 · 로직 — 빌더 없음.
- 기계 산림품셈 · 소요량 건설 건축·유지관리 · 소요량 산림 전 장 · 계수 건설 전 장 · 계수 산림 1장 밖 — 빌더 없음.
- 자재품목 — 나라장터 스냅샷 → 마스터 줄 빌더 없음 · 유료 물가지 받는 길 미정.
- 유가전력 — 휘발유·경유 새 판 반영 없음.
- 원본 받기 — 문서 URL(게시글 uuid · seq)을 반기마다 `collect_cost_sources.py` 상수에 손으로 갱신.
- 판 최신 여부 표 없음 — `resources/tester/test_master_sources.py` 가 가리키는 `resources/data_master_sources/` 가 이 저장소에 없음.
+2
View File
@@ -25,6 +25,7 @@
| `GET /logics` | `sub` · `detail` · `q` · `blocked`(0/1) | `{logics: [{file, 키, 원문번호, 구분, 상세구분, 이름, 결과단위, 출처, blocked, reasons}]}` — 장 차례대로 |
| `GET /logic` | `key` | `{file, version, logic: {로직 한 줄 통째}, blocked, reasons, prices: {요소: 요약 \| null}}` |
| `GET /elements` | `group` · `q` · `limit`(50 · 200까지) | `{total, items: [{ref, file, 원문번호, 이름, 규격, 단위, 값, 값칸}]}` |
| `GET /materials` | `sub`(구분) · `detail` · `spec` · `region`(자재지역) · `limit`(50 · 200까지) | `{total, 기본, items: [{ref, 구분, 상세구분, 이름, 규격, 단위, 값, 관급}]}` — 재료 고르기 조건 안 후보 · `기본` = 시험 계산이 쓸 줄 |
- `rows` 는 요소·로직 파일은 `줄`, 표형 파일(소요량·계수)은 `표` 를 줄로 봄.
- `q` = 찾기 — `키`·`원문번호`·`이름`·`옛이름` 에 든 글(대소문자 무시).
@@ -33,6 +34,7 @@
- `blocked` = 로직 검사(`check_master.py 로직`)에 걸림 · `reasons` = 걸린 까닭 글.
- `prices` = 호표 `요소``{ref, 이름, 규격, 단위, 값}`(단가 자동 칸) · 없는 요소는 `null` · `{이름}` 이 낀 원문번호·하위 로직 줄은 빠짐(계산 때 정해짐). 품셈재료는 `값` = 연결을 따라간 낮은 값(시험 계산용) · `출처` = 그 값의 열(`<자재품목 키>.<열>`) · 자재지역 후보는 `값` null(계산 때 정해짐).
- `elements` = 요소 찾기 창 — 그룹 전체에서 `q` 찾기 · `ref` = 식에 넣는 키 · 표형 그룹은 `값칸` 이 옴.
- `materials` = 재료 고르기 단계 — 로직 재료 줄 `요소` 가 조건 `{구분, 상세구분, 규격}` 이면 이 API 로 후보(방식 A). `값` 이 비어 있어도 후보로 둠.
- `pick` = 고르기 창 — `price` = `재료_자재품목.json`(값 = 값 열 다섯 가운데 낮은 값 · `관급` = 관급 값 있음) ref 키 · `job` = `인력.json` 값 있는 공표 직종(구분 「미확보」 빼고) ref 키.
## 3. 시험 계산
@@ -2,7 +2,7 @@
`scripts/build_자재품목_분류.py` 가 만든 표 — 손으로 고치지 않음.
- 구분 23 · 상세구분 96 · 미분류 1063 줄 (전체 30592 줄)
- 구분 23 · 상세구분 97 · 미분류 169 줄 (전체 30592 줄)
## 규칙
@@ -14,73 +14,77 @@
## 구분별 상세구분 (줄 수)
### 관·이음관 (7044)
### 관·이음관 (7045)
PE관 2906 · 이음쇠·부속 1003 · 주철관 828 · 강관 705 · PVC관 461 · 파형강관 417 · 유리섬유·FRP관 398 · 동관·스텐관 132 · 흄관 117 · 단열이중관 77
PE관 2906 · 이음쇠·부속 1003 · 주철관 828 · 강관 705 · PVC관 461 · 파형강관 417 · 유리섬유·FRP관 398 · 동관·스텐관 133 · 흄관 117 · 단열이중관 77
### 식생·종자 (5869)
### 식생·종자 (5989)
수목 5433 · 수목지주·보호 181 · 종자 105 · 잔디·지피 97 · 비료·토양개량 25 · 농약·약제 21 · 비료 7
수목 5532 · 수목지주·보호 181 · 종자 105 · 잔디·지피 97 · 농약·약제 42 · 비료·토양개량 25 · 비료 7
### 레미콘 (3203)
레미콘 3203
### 철근·강재 (1857)
### 철근·강재 (1870)
형강 703 · 철근 602 · 강판 262 · 구조용강관 147 · 철선 92 · 철망·와이어메쉬 51
형강 716 · 철근 602 · 강판 262 · 구조용강관 147 · 철선 92 · 철망·와이어메쉬 51
### 아스콘·포장 (1705)
아스콘 1479 · 포장블록 173 · 특수포장 30 · 아스팔트 13 · 투수포장재 10
### 안전시설 (1505)
### 안전시설 (1536)
표지판·지주 933 · 가드레일 427 · 낙석방지책 63 · 낙석방지·휀스 59 · 차선·방호 23
표지판·지주 933 · 가드레일 458 · 낙석방지책 63 · 낙석방지·휀스 59 · 차선·방호 23
### 볼트·앵커·철물 (1001)
볼트·너트 525 · 와이어로프·클램프 208 · 앵커 189 · 못 41 · 철근커플러 38
### 전기·통신 (799)
### 전기·통신 (900)
케이블·전선 312 · 판넬·차단기 163 · 전선관·트레이 134 · 조명·전등 133 · 부속·기타 57
케이블·전선 319 · 판넬·차단기 163 · 전선관·트레이 139 · 조명·전등 133 · 제어·계측기기 89 · 부속·기타 57
### 밸브·수도자재 (761)
밸브 597 · 수도계량기·유량계 79 · 스프링클러·호스 51 · 펌프 34
### 매트·토목섬유 (678)
돌망태 372 · 부직포·토목섬유 113 · 식생매트 64 · 돌망태·사방 63 · 매트 54 · 부직포 8 · 잔디보호매트 4
### 운송·용역 (675)
화물운임 552 · 시험·검사 62 · 폐기물 61
### 매트·토목섬유 (641)
### 기계·설비 (606)
돌망태 372 · 부직포·토목섬유 76 · 식생매트 64 · 돌망태·사방 63 · 매트 54 · 부직포 8 · 잔디보호매트 4
기계류 255 · 보일러·공조 172 · 놀이·운동·시설물 109 · 측정·시험기기 60 · 소방·가스 10
### 맨홀·수로·그레이팅 (599)
수로관 283 · 그레이팅 166 · 맨홀 150
### 재 (525)
### 시멘트·골재 (582)
경계석 401 · 조경석·자연석 124
골재 323 · 모르타르 188 · 시멘트 71
### 도료·방수·접착 (522)
### 도료·방수·접착 (557)
도료 249 · 접착·혼화 138 · 방수·실링 135
도료 249 · 접착·혼화 173 · 방수·실링 135
### 콘크리트제품 (484)
### 석재 (534)
말뚝·파일 223 · 경계석·블록 171 · PC구조물 90
경계석 410 · 조경석·자연석 124
### 시멘트·골재 (483)
### 콘크리트제품 (496)
골재 323 · 모르타르 101 · 시멘트 59
말뚝·파일 223 · 경계석·블록 171 · PC구조물 102
### 기계·설비 (462)
### 건축마감 (450)
보일러·공조 172 · 기계류 118 · 놀이·운동·시설물 109 · 측정·시험기기 53 · 소방·가스 10
천장·바닥·마감재 246 · 문·창·유리 123 · 타일·기와 66 · 위생기구 15
### 목재 (430)
@@ -90,13 +94,9 @@ PE관 2906 · 이음쇠·부속 1003 · 주철관 828 · 강관 705 · PVC관 46
단열재 282
### 건축마감 (279)
### 잡재료 (263)
문·창·유리 123 · 천장·바닥·마감재 75 · 타일·기와 66 · 위생기구 15
### 잡재료 (142)
소모품 92 · 용접·가스 28 · 고철·기타금속 11 · 화약 9 · 용수 2
소모품 189 · 고철·기타금속 31 · 용접·가스 28 · 화약 13 · 용수 2
### 교량·구조부속 (133)
@@ -367,6 +367,120 @@ R = [
"소모품",
r"장갑|걸레|비닐|페인트붓|^붓$|사포|브러[시쉬]|톱날|연마|숫돌|기름|오일|윤활|그리스|목탄|비누|현수막|끈|밧줄|포대|마대|볏짚|양생포|필름|테이프|와이어",
),
# 보강 — 미분류 꼬리 (기존 규칙 뒤에 붙임 · 앞 규칙 결과는 그대로)
(
"식생·종자",
"수목",
r"^(가우라|가랑코에|덴드롱|디기탈리스|루피너스|리아트리스|만데빌라|밀렛|블루세이지|비덴스|산파첸스|스토크|시클라멘|아로우카리아|아르메리아|안젤로니아|알리움|애니시다|임파첸스|이포메아|쥬리안|캄파놀라|크로톤|클레오메|토레니아|펜타스|포인세티아|한련화|장비와인핑크|꿀풀|좁쌀풀|향토초본류|향토목본류|초본류)$",
),
(
"식생·종자",
"농약·약제",
r"발근촉진제|수목도포제|전착제|카바액제|푸른솔|에코마인|증산억제제|황산동|질산칼[륨슘]|제1인산칼륨|요소|용과린|입단형성제|토양보습제|생명분|대지생명",
),
(
"식생·종자",
"수목",
r"녹매토|GP토|부엽토|배합토|인공토|복합토|에코토|생태복원토양|바크|세라소일|부엽식물|론생백|씨앗부착거적|피복재|법면안정재|천연섬유망",
),
("식생·종자", "수목", r"화분|그린폴|그린엣지|드립엣지|가로수보호판"),
("시멘트·골재", "시멘트", r"시멘트|슬래그|코우크스|SMT기층용결합재|프롬"),
(
"시멘트·골재",
"모르타르",
r"줄눈(샌드|채움)|PFM|고경도줄눈|알[칼카]리(성)?회복제|침투형알칼리|중성화|탄산화|염해|탄성보수재|방오코팅|RA-|MA(코트|콘|프라이몰)|폴리(코트|콘)|Recoh|균열(방지|저감)|바탕조정재|급결제|표면보호용|하이브리드섬유보강|나선형보강재|타스|워터스탑|몰[트더]|본크론|코타스|믹스|하이루|아루텍|에스몰|새지마|메토셀|비안새|뉴스타|탑스톤|HiCon|HI-PAT|VES|E-005S|JSM|고능축완결재|광촉매에폭시|에멀젼|파우더|필러|F-Tape|CERACOTE|포시몰|KL-화인|바니쉬|S\.R",
),
(
"도료·방수·접착",
"접착·혼화",
r"규산소다|염화칼슘|생석회|소석회|친환경수용성녹제거제|착색제|GRPS활성제",
),
(
"잡재료",
"소모품",
r"유압유|절삭유|LPG|무연분탄|복사용지|인쇄잉크|모조지|영상인화지|저장매체|USB메모리|건전지|배터리|축전지|에칠알콜|밀가루|요소수|카바이트|질소|탄산칼슘|산$|염산|인산|수산|알루미늄분말|알루미늄괴|넝마|새끼|빗자루|FILMCASSETTE|COUPLANT|형광자분|자분|침투통|비산먼지방지망|분진망|차광망|손전등",
),
(
"잡재료",
"고철·기타금속",
r"주물|동합금선|황동선|골함석|압착그립|연결체인|파일용접밴드|암타이밴드|천연밴드|나일론밴드|후크|지압판|웨지핀|케이지",
),
("잡재료", "화약", r"도폭선|미진동파쇄제|발파전석"),
("석재", "경계석", r"전석|해미석|인조석|게비온석|디딤석"),
("콘크리트제품", "PC구조물", r"비탈면보호블럭|블럭조임재|생태목틀|SSM|무소음트랜치|고무블럭"),
(
"안전시설",
"가드레일",
r"표지|칼라콘|슈퍼콘|제설함|윙카등|디자인형울타리|울타리|핸드레일|방충재|방현|외장재\(교량|휴먼보강캡",
),
(
"매트·토목섬유",
"부직포·토목섬유",
r"Geogrid|지오드레인|직포|BSA시트|프로텍션|펠트|RS강화|SRR고정구|부착망|고무판|고무바|라인벨트|완충고무",
),
(
"건축마감",
"천장·바닥·마감재",
r"판넬|PANEL|캐노피|CANOPY|ROOFSHEET|지붕판|슁글|합지벽지|폴리카보네이트|아크릴|포멕스|썬라이트|FRP|알루미늄(판|캡)|STS(헤어라인|원형봉)|강재판넬|T챤넬|엘브엣지|베이스(커버|찬넬)|보호금구|상부캡|워셔캡|주주|SS높이조절|QMR|K-SPAN|L형고정|스프라이스|보호캡|연결핀|JACK-BOLT|완충브래킷|Plate|PPP|평상|테이블|책상|야외|분리수거장|축대목|외부|내부|미끄럼방지",
),
(
"전기·통신",
"제어·계측기기",
r"콘덴서|UPS|MONITOR|TOUCHPANEL|LSUBoard|하이넬|USB|운전화면|DB$|자동탈착|MT자외선|ECTPROBE|FIELDINDICATOR|SURVEYMETER|MAGNETICCHUCK|MAGNETIC|JISA형시편|초음파탐촉자",
),
(
"기계·설비",
"기계류",
r"치즐|백호우티스|BIT|비트|TAPER|핸들패커|코어리프터|유압할암봉|에어타카|에어튜브|모듈러잭|로내대차|배기가스|재연소로|집진장치|필터백|유해가스촉매|건조기|캐스터블|래크|DUCT|FLEXIBLE|오수처리|미생물담체|다공성여재|정화침투|공드럼|홈메우기|파일안전덮개|일반고무판|데리네이트|폴리에스터펠트|내마모",
),
(
"건축마감",
"천장·바닥·마감재",
r"벽지|한지장판지|도배지|운용지|롤스크린|큐비클|세면대|캐비넷|APRON|수건|휴지걸이|컵대|옷걸이|거울|국기꽂이|베너기|차광막|오메가레일",
),
(
"도료·방수·접착",
"접착·혼화",
r"ERH-|SLC-|SC604A|레팔트|BACKUPROD|패칭재|알카리회복재|부식방지제|함침재|씰링용에폭시|전해질저감재|탄소저감재|재생첨가제|블랙터|원타임|ㄷ-보강재|주제$",
),
(
"잡재료",
"소모품",
r"방진|보안경|리어카|바퀴|쟈키|와샤|Screw|그라인더날|건축용주사기|거울원판|가루분|^풀$|호차|취부밴드|CruzerBlade|CM01-|프린터|COMPUTER|서버용PC",
),
(
"기계·설비",
"기계류",
r"에어햄머|롯드페카|샘풀|SPT|리밍셀|패커|P\.C콘|파일캡|연결판|고정판|분수용기기|고압분무|쿨링포그|정수필터|압력게이지|송관대차|관운반차|수골|대차|냉각장치|집진기|펄스유니트|인젝트|예열기|잔재처리|배풍기|배기관|유골|백연방지|다이옥신|단열문|제어함열처리",
),
(
"전기·통신",
"제어·계측기기",
r"Board|CPU|ROM교체|MMI|감전보호기|화재주수신기|점멸기|음향신호기|위성시보기|낙뢰|TIMER|전력량계|계전기|중앙감시반|조작반|모니터장치|안내시스템|재실감지|Modem|단자함|전용회선|CONT\(SCU\)",
),
(
"전기·통신",
"케이블·전선",
r"경동|연동연선|지선롯드|경완금|FC통신관|팔각테파관강주|근가밴드|랙크|Y캡",
),
("철근·강재", "형강", r"I\"형행가|열녀목|STSSR캡"),
("관·이음관", "동관·스텐관", r"^배관$"),
("전기·통신", "전선관·트레이", r"RACEWAY|PULLBOX|ELBOW|C-찬넬"),
(
"전기·통신",
"제어·계측기기",
r"카메라|렌즈|검지|제어장치|전원장치|서브랙|단자대|제어함체|철구조물\((신호|과속)",
),
(
"시멘트·골재",
"모르타르",
r"모르터|폴리머(콘크리트|결합재)|SS섬유재|충전재|SMT표층|스틸화이바|네오스톤",
),
("기계·설비", "측정·시험기기", r"압력계|ALARMDOSIMETER|UVMETER|TEMPILSTIK|MEGA-CHECK"),
("기계·설비", "기계류", r"권양기|공기주입기|벨트\(|악취방지|UV살균기|예취기|스핀틀|공기배기구"),
("식생·종자", "수목", r"촉촉소일|나무여과상자|만병초"),
("석재", "경계석", r"빗물머금석"),
("잡재료", "소모품", r"마킹펜|크레용|세척제|WHITEPAINT|보조통|암등|이단철개|열수축시트"),
]
RX = [(a, b, re.compile(c, re.I)) for a, b, c in R]
@@ -389,6 +503,7 @@ NAME = re.compile(r'"이름": "((?:[^"\\]|\\.)*)"')
SRC_ = re.compile(r'"출처": "((?:[^"\\]|\\.)*)"')
NOTE_OLD = "구분 · 상세구분 = 원문에 대·중분류 칸이 없어 비움(지어내지 않음)"
NOTE_NEW = "구분 · 상세구분 = 품명 글자 규칙으로 채움(scripts/build_자재품목_분류.py · 묶음 목록 ref/_묶음_자재품목.md) · 조달청 줄은 조달청 품명을 따름 · 못 가린 줄은 「미분류」"
NOTE_EMPTY = "이름 없음 — 원문 품명 칸이 빈 줄이라 분류하지 않음"
def q(s):
@@ -406,6 +521,8 @@ def main():
sc = json.loads('"' + SRC_.search(ln).group(1) + '"')
a, b = 분류(nm, sc)
ln = LINE.sub(lambda _: f'"구분": {q(a)}, "상세구분": {q(b)}', ln, count=1)
if not nm:
ln = ln.replace('"비고": null', f'"비고": "{NOTE_EMPTY}"', 1)
res.append((a, b, nm))
out.append(ln.replace(NOTE_OLD, NOTE_NEW))
assert len(res) == len(rows), (len(res), len(rows))
@@ -444,6 +561,14 @@ def 묶음_md(res):
return "\n".join(L)
def apply():
"""빌더가 JSON 을 새로 쓴 뒤 부름 — 두 칸 · 머리 비고를 다시 채움."""
new, res = main()
SRC.write_text(new, encoding="utf-8", newline="\n")
OUT_MD.write_text(묶음_md(res), encoding="utf-8", newline="\n")
return res
if __name__ == "__main__":
new, res = main()
if "--미분류" in sys.argv:
@@ -6,7 +6,7 @@ Cost2..5 = 물가지 넷 · Page2..5 = 면수. TC(채택) · Cost6(견적가)는
품명에 관급 줄의 Cost1(가격정보) = 조달청 관급 단가 관급 · 기간은 비고 조달 26.7.
밖의 줄에 남은 Cost1 출처가 조달인지 물가지인지 원문이 가름 열에 넣지 않고 비고 가격정보 로만.
0 · 미저장 = 없음(null). 이름·규격은 원문 그대로(띄어쓰기·오자 포함). 코드 없는 줄은 .
ESTX ·중분류 칸이 없어 구분·상세구분은 비움(지어내지 않음).
ESTX ·중분류 칸이 없어 구분·상세구분은 build_자재품목_분류.py 품명 글자 규칙으로 채움(빌더 끝에서 부름).
쓰기: ./venv/Scripts/python.exe resources/master_data/scripts/build_재료_자재품목.py
원본과 대조.
@@ -140,12 +140,15 @@ def main():
"관급 = 원문 「가격정보」 칸의 조달청 관급 단가(철근 · 레미콘 · 아스콘 · H형강 등) · 기간은 비고 「조달 26.7」",
"조달청에만 있는 품목 380 줄은 관급 열만 채움 — 출처 「자재품목 조달청 …」 · 원문번호는 _키대장.json 에만",
"원문 「가격정보」 칸에 시중 값이 남은 줄은 값 열에 넣지 않고 비고 「가격정보 ○○」 로만 둠 — 출처가 조달인지 물가지인지 원문이 안 가름",
"구분 · 상세구분 = 원문에 대·중분류 칸이 없어 비움(지어내지 않음)",
"구분 · 상세구분 = 품명 글자 규칙으로 채움(scripts/build_자재품목_분류.py · 묶음 목록 ref/_묶음_자재품목.md) · 조달청 줄은 조달청 품명을 따름 · 못 가린 줄은 「미분류」",
"면수 = 물가지 값의 원문 면수 · 화면에는 접어 둠",
],
}
dump_master(OUT, head, build(rows))
check(rows)
import build_자재품목_분류 as cls # noqa: E402
print("구분·상세구분 채움", len(cls.apply()), "")
print("새 틀로 받음", mk.adopt()) # 열쇠 → 키
File diff suppressed because it is too large Load Diff
@@ -119,3 +119,14 @@ def test_품셈재료_이름과_같은_품명_줄이_한_묶음():
p["이름"],
collections.Counter(x["구분"] for x in hit),
)
def test_이름_빈_줄은_비고에_까닭():
for x in ROWS:
if not x["이름"]:
assert x["구분"] == "미분류" and "이름 없음" in (x["비고"] or ""), x[""]
def test_빌더가_분류를_부름():
src = (MD / "scripts" / "build_재료_자재품목.py").read_text(encoding="utf-8")
assert "cls.apply()" in src