135 lines
5.1 KiB
Markdown
135 lines
5.1 KiB
Markdown
# HNS 물질 Import 파이프라인
|
||
|
||
`C:\Projects\MeterialDB\유해물질 화물적부도 검색툴.xlsm` 외부 자료를 `HNS_SUBSTANCE` DB로 변환하는 1회성 파이프라인.
|
||
|
||
## 파이프라인 구조
|
||
|
||
```
|
||
[Excel xlsm]
|
||
├─ (1) extract-excel.py → out/base.json (1,345종 기본 정보)
|
||
└─ (2) extract-images.py → out/images/*.png (225종 상세 카드 이미지)
|
||
↓
|
||
(3) ocr-images.ts → out/ocr.json (Claude Vision → 물성/위험도/EmS JSON)
|
||
↓
|
||
(4) merge-data.ts → frontend/src/data/hnsSubstanceData.json
|
||
↓
|
||
(5) tsx src/db/seedHns.ts → HNS_SUBSTANCE 테이블
|
||
```
|
||
|
||
## 전제 조건
|
||
|
||
- Python 3.9+ with `openpyxl`
|
||
- Node.js 20
|
||
- `ANTHROPIC_API_KEY` 환경변수 (Claude Vision API)
|
||
- Excel 원본 파일: `C:\Projects\MeterialDB\유해물질 화물적부도 검색툴.xlsm`
|
||
|
||
## 실행 순서
|
||
|
||
### 1) Excel 메타 시트 파싱
|
||
|
||
```bash
|
||
cd backend
|
||
python scripts/hns-import/extract-excel.py
|
||
```
|
||
|
||
- 입력: `C:\Projects\MeterialDB\유해물질 화물적부도 검색툴.xlsm`
|
||
- 처리 시트: `화물적부도 화물코드`(1,345개), `동의어`(215개), `IBC CODE`(분류)
|
||
- 출력: `scripts/hns-import/out/base.json`
|
||
|
||
### 2) 이미지 225개 추출
|
||
|
||
```bash
|
||
python scripts/hns-import/extract-images.py
|
||
```
|
||
|
||
- 출력:
|
||
- `scripts/hns-import/out/images/{nameKr}.png`
|
||
- `scripts/hns-import/out/image-map.json` (파일명↔시트명 매핑)
|
||
|
||
### 3) Claude Vision OCR
|
||
|
||
```bash
|
||
export ANTHROPIC_API_KEY="sk-ant-..."
|
||
cd backend
|
||
npx tsx scripts/hns-import/ocr-images.ts
|
||
```
|
||
|
||
- 이미지 한 장당 Claude API 1회 호출
|
||
- 동시 5개 병렬, 실패 시 3회 재시도
|
||
- 출력: `scripts/hns-import/out/ocr.json` `{ [nameKr]: OcrResult }`
|
||
|
||
### 4) 최종 JSON 병합
|
||
|
||
```bash
|
||
npx tsx scripts/hns-import/merge-data.ts
|
||
```
|
||
|
||
- 입력: `out/base.json` + `out/ocr.json`
|
||
- 출력: `frontend/src/data/hnsSubstanceData.json` (전량 덮어쓰기)
|
||
|
||
### 5) DB 재시드
|
||
|
||
```bash
|
||
cd backend
|
||
npx tsx src/db/seedHns.ts
|
||
```
|
||
|
||
- 기존 `DELETE FROM HNS_SUBSTANCE` → 새 1,345종 INSERT
|
||
|
||
## 재실행 안내
|
||
|
||
- `out/` 디렉토리는 `.gitignore` 처리되어 커밋되지 않음
|
||
- OCR 결과는 비결정적이므로 재실행 시 약간 달라질 수 있음
|
||
- 비용 절감을 위해 OCR 결과는 보존하고 `ocr-images.ts --resume` 로 실패 항목만 재시도 가능
|
||
|
||
## 비용/시간 가이드
|
||
|
||
- 이미지 225장 × Claude Sonnet 4.6 기준 약 $3~10
|
||
- 전체 파이프라인: Excel 파싱 ~30초, 이미지 추출 ~10초, OCR ~10~20분, 병합/시드 ~1분
|
||
|
||
## 알려진 이슈 (후속 작업 필요)
|
||
|
||
### 1) OCR ↔ base.json 국문명 매칭 실패 (136건)
|
||
|
||
`merge-data.ts` 실행 시 base.json의 국문명과 `ocr.json` 키 표기가 달라 상세정보가 연결되지 않는 물질이 다수 존재. 실제 514종 중 **상세 정보 보유는 73종** 수준.
|
||
|
||
**예시:**
|
||
| base.json 국문명 | ocr.json 키 |
|
||
|---|---|
|
||
| `1메톡시2프로판올` | `1-메톡시-2프로판올` |
|
||
| `c810이소알코올` | `(C8-10)이소 알코올` |
|
||
| `메탄올` | (OCR 없음, 이미지 누락) |
|
||
| `가솔린` | `휘발유` (동의어) |
|
||
|
||
**원인:**
|
||
- 하이픈/공백/괄호 제거 수준 차이 (Excel 시트명과 이미지 파일명 추출 로직 불일치)
|
||
- 동의어 처리 미흡 (동의어 시트 215개 활용 필요)
|
||
- OCR 대상 225종 외 나머지 1,120종은 기본정보만 존재
|
||
|
||
**권장 해결 방향:**
|
||
- `merge-data.ts` 에 정규화 함수 추가 (공백/하이픈/괄호 제거 후 매칭)
|
||
- `base.json` 의 동의어(synonyms) 배열을 역인덱스로 활용해 OCR 키와 교차 매칭
|
||
- 매칭 실패 목록을 `out/merge-unmatched.json` 으로 출력하여 수동 검토
|
||
|
||
### 2) SEBC/CAS/UN 번호 varchar 길이 초과
|
||
|
||
`base.json` 생성 시 Excel에서 복수 CAS/UN 번호를 줄바꿈으로 결합해 저장하여, `HNS_SUBSTANCE` 테이블의 `VARCHAR(20)` 등 제약을 초과했음. 현재는 [`seedHns.ts`](../../../backend/src/db/seedHns.ts) 의 `firstToken()` 헬퍼로 첫 토큰만 검색 컬럼에 저장하고 원본 전체는 `DATA` JSONB에 보존.
|
||
|
||
**영향:**
|
||
- `CAS_NO`, `UN_NO` 검색 시 두 번째 이후 번호로는 매칭 불가
|
||
- 프론트엔드 표시 시 JSONB의 원본 값을 참조해야 함
|
||
|
||
**권장 해결 방향:**
|
||
- `extract-excel.py` 에서 복수 CAS/UN 을 배열로 분리 저장
|
||
- 스키마에 `CAS_NO_LIST TEXT[]`, `UN_NO_LIST TEXT[]` 추가 후 GIN 인덱스 구성
|
||
|
||
### 3) MSDS 요약시트 포맷 이미지 (약 5건)
|
||
|
||
원본 xlsm 일부 시트는 표준 HNS 카드가 아닌 KOSHA MSDS 요약시트 포맷으로, 저해상도 + 필드 구조 상이로 OCR 정확도가 떨어짐.
|
||
|
||
**해당 물질:** 프로필벤젠, 프르푸필 알콜, 프로필렌 클리콜 알긴산, 휘발유, 헥사메틸렌 디이소시안산
|
||
|
||
**권장 해결 방향:**
|
||
- `ANTHROPIC_API_KEY` 설정 후 `HNS_OCR_ONLY` 로 해당 5종만 재OCR
|
||
- 이미지 누락 필드는 화학 문헌값(ICSC, PubChem)으로 보강
|