#!/bin/sh
# Claude Code Skill 설치 스크립트
# 생성: 2026-08-11 · Skill 1개 · 파일 1개
#
#   ./install-skills.sh            ~/.claude/skills/ 에 설치 (모든 프로젝트에서 사용)
#   ./install-skills.sh ./myrepo   ./myrepo/.claude/skills/ 에 설치 (그 저장소 전용)
set -e

DEST="${1:+$1/.claude/skills}"
DEST="${DEST:-$HOME/.claude/skills}"
mkdir -p "$DEST"
echo "설치 위치: $DEST"


mkdir -p "$DEST/ctrial-collect"
cat > "$DEST/ctrial-collect/SKILL.md" <<'SKILL_PAYLOAD_EOF'
---
name: ctrial-collect
description: 공공데이터포털(식약처 MFDS) 임상시험 API로 시험 목록·상세를 수집해 DB/Excel로 적재하고, 회차 간 변경분(신규·상태변경·종료)을 비교 산출한다. 목록+상세 2단 API, 재시도, 증분 갱신, 날짜 기반 파일 세대 관리. TRIGGER - "임상시험 수집", "임상시험 목록 긁기", "MFDS/식약처 API", "공공데이터포털", "임상시험 데이터 갱신", 특정 암종의 진행 중 임상시험을 찾아야 할 때.
---

# 임상시험 데이터 수집

## 언제 쓰나

식약처 승인 임상시험을 주기적으로 긁어 "지난번 대비 뭐가 새로 생겼고 뭐가 끝났는지"를 내야 할 때. 특정 암종의 현재 모집 중 시험을 찾는 작업의 입력을 만든다.

조합: 이 skill로 수집 → `[[cancer-type-classify]]`로 암종 부착 → 담도암만 필터.

## 정본 코드

```
~/Documents/IMOK/ctrial-auto/app/
  services/api_download_service.py    388줄  ★ 공공데이터 API 수집 (정본)
  services/data_download_service.py   242줄  구버전 Playwright 크롤링 (사용 안 함)
  services/excel_compare_service.py   539줄  회차 간 변경 비교
  services/url_update_service.py       83줄  MFDS 상세 URL 역추적
  services/export_excel_service.py     70줄  내보내기
  pipeline/pipeline_manager.py        246줄  단계 진행 상태 관리
  prompt/                                    분류 프롬프트 (cancer-type-classify skill 참조)
```

`ctrial-auto` 계열은 6벌(`_2월`, `_4월`, `_api`, `CTrial_AUTO`, `ctrial_auto_module`)이 있다. **`IMOK/ctrial-auto`가 최신(2026-07-30)이고 이것이 정본이다.**

## 수집: 목록 + 상세 2단 API

```
목록 API  getMdcinClincTestInfoList02   → 최근 CLNC_TEST_SN(시험번호) 확보 + 실시기관명
상세 API  getClncExamPlanDtlInq2        → 시험번호별 STATUS·대상질환·영문제목·연구목적·성분명
```

**목록만으로는 분류에 쓸 정보가 부족하다.** 상세 API가 주는 대상질환명/카테고리/영문제목/연구목적/성분명 5개 컬럼이 `[[cancer-type-classify]]`의 입력 품질을 결정한다. 목록에서 얻은 실시기관은 `_lab_map: {CLNC_TEST_SN: 실시기관}`에 담아 상세 결과와 합친다 — 상세 API에는 실시기관이 없다.

호출 사이에 `call_delay`를 둔다. 공공데이터포털은 연속 호출을 차단한다. `max_retries`로 재시도.

### 웹 크롤링에서 API로 갈아탄 이유

원래 Playwright로 긁었다(`data_download_service.py`). API로 바꾸면서 **출력 엑셀 형식은 완전히 동일하게 유지하고 컬럼 5개만 추가**했다. 이게 중요하다 — 다운스트림(비교·분류·내보내기)을 하나도 안 고치고 수집 방식만 교체할 수 있었다. 수집기를 바꿀 때는 출력 스키마를 먼저 고정해라.

`url_update_service.py`의 Playwright는 아직 필요하다. MFDS 상세 페이지 URL은 API로 안 나와서 검색 URL로 역추적한다.

## 로컬 필터링

API가 아니라 받은 뒤에 나눈다:

```
모집중  + 승인일 3년 이내   → recruiting_trials.xlsx
승인완료 + 승인일 6개월 이내 → completed_trials.xlsx
```

기준 기간은 용도마다 다르니 상수로 빼둬라.

## 변경 비교 (회차 간 증분)

`excel_compare_service.py`가 핵심이자 가장 큰 파일이다.

```
excel_old/ 에서 파일명 날짜로 '가장 최근' 1개 선택
  → excel_output/ 신규 파일과 비교
  → step1_output/ 에 비교결과 + updated 파일 생성
```

**파일명에 날짜를 박아 세대를 관리한다**(`extract_date_from_filename`, `find_latest_file_by_date`). DB에도 `20260403.db`, `20260618.db`, `20260701.db` 식으로 스냅샷을 남긴다. 임상시험은 상태가 바뀌므로 "그때 이 시험이 뭐였는지"를 복원할 수 있어야 한다.

비교 전에 양쪽 다 중복 제거를 한다 — 신규는 시험번호 기준(`dedup_file2_by_seq`), 기존은 라벨을 붙이며(`dedup_and_label_file1`). 제목은 `norm_title()`로 정규화해서 비교한다. 공백·괄호 차이로 같은 시험이 신규로 잡히는 사고를 막는다.

`find_previous_statuses`(`db_history`)로 이전 상태 이력을 끌어와 "모집중 → 종료" 같은 전이를 잡는다.

## 파이프라인 진행 상태

`PipelineManager`는 싱글턴이고 파이프라인 종류별로 상태를 따로 들고 있다:

```
current_step / total_steps / progress(%) / completed_steps / 실패 시 error
is_pipeline_running()  중복 실행 방지
```

수집은 수십 분 걸리므로 **웹 UI가 진행률을 물어볼 창구가 필요하다**. 동기 서비스는 `asyncio.to_thread`로 감싸 백그라운드에서 돈다(`imok_pipeline.py`).

## 함정

- **`mfds_updater_service.py`의 `DEFAULT_DB_MODE = "local"`** 에 `⚠️ 테스트용` 주석이 달려 있다. 운영에 쓸 때 반드시 확인해라.
- 외부 Gradle 프로젝트(`MfdsUpdater2`)를 `subprocess`로 부른다. 타임아웃 300초. 이 의존성이 있는지 먼저 확인.
- Elasticsearch 색인(`elasticsearch_service.py`)은 선택 단계다. ES 없이도 수집·비교는 돈다.

## 유사 프로젝트

`IMOK/iqvia`가 같은 구조(`excel_input`/`excel_old`/`excel_output`/`db`)로 IQVIA 데이터를 처리한다. `IMOK/ctrial-mapper`는 수집물을 PI(연구자) 중심으로 재구성한다.

## 연관 skill

`[[cancer-type-classify]]`, `[[excel-case-validator]]`, `[[regimen-extract]]`
SKILL_PAYLOAD_EOF

echo ""
echo "완료 — Skill 1개를 설치했습니다."
echo "Claude Code를 다시 시작하면 인식됩니다."
