uspto-oa-cli 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- uspto_oa_cli-0.1.0/.gitignore +24 -0
- uspto_oa_cli-0.1.0/.python-version +1 -0
- uspto_oa_cli-0.1.0/CLAUDE.md +84 -0
- uspto_oa_cli-0.1.0/PKG-INFO +148 -0
- uspto_oa_cli-0.1.0/README.md +126 -0
- uspto_oa_cli-0.1.0/pyproject.toml +53 -0
- uspto_oa_cli-0.1.0/src/oa_cli/__init__.py +3 -0
- uspto_oa_cli-0.1.0/src/oa_cli/__main__.py +4 -0
- uspto_oa_cli-0.1.0/src/oa_cli/cli.py +180 -0
- uspto_oa_cli-0.1.0/src/oa_cli/config.py +103 -0
- uspto_oa_cli-0.1.0/src/oa_cli/prosecution/__init__.py +0 -0
- uspto_oa_cli-0.1.0/src/oa_cli/prosecution/collect.py +198 -0
- uspto_oa_cli-0.1.0/src/oa_cli/prosecution/extract.py +224 -0
- uspto_oa_cli-0.1.0/src/oa_cli/uspto/__init__.py +0 -0
- uspto_oa_cli-0.1.0/src/oa_cli/uspto/client.py +40 -0
- uspto_oa_cli-0.1.0/src/oa_cli/uspto/documents.py +20 -0
- uspto_oa_cli-0.1.0/src/oa_cli/uspto/download.py +80 -0
- uspto_oa_cli-0.1.0/uv.lock +605 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv
|
|
11
|
+
|
|
12
|
+
# data
|
|
13
|
+
file/
|
|
14
|
+
|
|
15
|
+
# AI Coding Assistants configs & logs
|
|
16
|
+
.antigravitycli/
|
|
17
|
+
.claude/
|
|
18
|
+
|
|
19
|
+
# secrets
|
|
20
|
+
.env
|
|
21
|
+
|
|
22
|
+
# temp
|
|
23
|
+
temp/
|
|
24
|
+
test/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## 프로젝트 개요
|
|
6
|
+
|
|
7
|
+
USPTO ODP(Open Data Portal) API를 통해 미국 특허 심사과정 문서를 다운로드하고, XML 파싱을 거쳐 구조화된 Markdown으로 변환하는 CLI 도구. AI 에이전트(Claude Code, Gemini CLI)가 생성된 MD 파일을 읽고 심사 전략을 분석하는 워크플로우를 목표로 한다. LLM API 직접 호출은 최소화하고 AI 코딩 에이전트에게 분석을 위임하는 방식을 지향한다.
|
|
8
|
+
|
|
9
|
+
## 실행 명령
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# 의존성 설치
|
|
13
|
+
uv sync
|
|
14
|
+
|
|
15
|
+
# API 키 설정 (최초 1회) → ~/.oa-cli.toml 에 저장
|
|
16
|
+
uv run uspto-oa configure
|
|
17
|
+
|
|
18
|
+
# 문서 다운로드
|
|
19
|
+
uv run uspto-oa download 16330077
|
|
20
|
+
|
|
21
|
+
# XML 파싱 → MD 생성 (file/{app_num}/{app_num}_prosecution.md)
|
|
22
|
+
uv run uspto-oa extract 16330077
|
|
23
|
+
|
|
24
|
+
# 특허번호 입력 (출원번호 자동 변환)
|
|
25
|
+
uv run uspto-oa download US12228644B2
|
|
26
|
+
|
|
27
|
+
# 특정 문서 코드만 다운로드
|
|
28
|
+
uv run uspto-oa download 16330077 --doc-codes CTNF,CTFR,NOA
|
|
29
|
+
|
|
30
|
+
# 강제 재다운로드
|
|
31
|
+
uv run uspto-oa download 16330077 --force
|
|
32
|
+
|
|
33
|
+
# 상세 로그
|
|
34
|
+
uv run uspto-oa -v download 16330077
|
|
35
|
+
|
|
36
|
+
# PyPI 배포 (빌드 → 업로드)
|
|
37
|
+
uv build && uv run twine upload dist/*
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**API 키 우선순위**: `--api-key` 옵션 > `USPTO_API_KEY` 환경변수 > `~/.oa-cli.toml` (개발 시 `.env` 자동 로드)
|
|
41
|
+
|
|
42
|
+
## 소스 구조
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
src/oa_cli/ # PyPI 배포 패키지 (hatchling wheel 타겟)
|
|
46
|
+
├── __init__.py # __version__
|
|
47
|
+
├── __main__.py # python -m oa_cli 지원
|
|
48
|
+
├── cli.py # click 진입점 (configure / download / extract 커맨드)
|
|
49
|
+
├── config.py # API 키 우선순위 관리, ~/.oa-cli.toml 읽기/쓰기
|
|
50
|
+
├── uspto/ # USPTO ODP API 저수준 클라이언트 (도메인 무관)
|
|
51
|
+
│ ├── client.py # make_api_request() — GET + 재시도/백오프
|
|
52
|
+
│ ├── documents.py # get_application_documents() — 문서 목록 API
|
|
53
|
+
│ └── download.py # download_file() / download_bytes()
|
|
54
|
+
└── prosecution/ # 심사과정 분석 도메인 로직
|
|
55
|
+
├── collect.py # 문서 필터링 + XML 우선 일괄 다운로드
|
|
56
|
+
└── extract.py # XML 파싱 → prosecution.md 생성
|
|
57
|
+
file/
|
|
58
|
+
└── {app_num}/ # 다운로드 파일 + 생성된 MD 저장 위치
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 아키텍처 핵심 사항
|
|
62
|
+
|
|
63
|
+
**레이어 분리**: `src/uspto/`는 API 통신만 담당(도메인 무관), `src/prosecution/`은 심사과정 비즈니스 로직 담당. `cli.py`는 두 레이어를 연결하는 진입점.
|
|
64
|
+
|
|
65
|
+
**서버사이드 필터 미사용**: USPTO API의 `documentCodes` 파라미터가 500 오류를 유발하므로, 전건 조회 후 클라이언트에서 필터링한다(`collect.py`의 `PROSECUTION_CODES_EXACT` / `PROSECUTION_CODE_PREFIXES`).
|
|
66
|
+
|
|
67
|
+
**파일명 규칙**: `{날짜}_{코드}_{문서ID}.{ext}` — 날짜 오름차순 정렬, 확장자는 실제 포맷 반영.
|
|
68
|
+
|
|
69
|
+
**다운로드 우선순위**: XML > PDF (MS_WORD 제외). XML은 tar 아카이브로 전달되므로 압축 해제 후 저장하며, tar 내 `.xml` 없거나 오류 시 PDF로 자동 폴백.
|
|
70
|
+
|
|
71
|
+
**XML 구조 (USPTO ST96 스키마)**: CTNF/CTFR는 `FormParagraph[07-21-aia]`에 거절 항목, NOA/NACT는 `FormParagraph[12-151-07]`에 허여 청구항, `FormParagraph[13-03]`에 Examiner's Statement 포함.
|
|
72
|
+
|
|
73
|
+
**PDF 특이사항**: USPTO OA PDF는 전 페이지 이미지 구성 → pypdf 텍스트 추출 불가. PDF 전용 문서(Amendment 등)는 MD에 경로만 참조로 포함하고 AI 에이전트가 필요 시 직접 읽도록 한다.
|
|
74
|
+
|
|
75
|
+
**생성 MD 구조** (`{app_num}_prosecution.md`):
|
|
76
|
+
- 타임라인 표 (전체 문서, XML/PDF 형식 표시)
|
|
77
|
+
- Office Action 상세 (CTNF/CTFR 거절 항목 전문)
|
|
78
|
+
- Notice of Allowance 상세 (허여 청구항 + Examiner's Statement)
|
|
79
|
+
- PDF 전용 문서 목록 (AI 에이전트 직접 전달용)
|
|
80
|
+
|
|
81
|
+
## 다음 구현 단계
|
|
82
|
+
|
|
83
|
+
- `--step summary/detail/prior-art/strategy` — 생성된 prosecution.md를 AI 에이전트에 넘기는 분석 진입점 (LLM API 직접 호출 없이 Claude Code / Gemini CLI에 위임)
|
|
84
|
+
- `--no-download` — 이미 다운로드된 파일 기준으로 `--extract` 바로 수행
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: uspto-oa-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: USPTO 특허 심사과정 분석 CLI — 문서 다운로드 · XML 파싱 · MD 생성
|
|
5
|
+
Project-URL: Homepage, https://github.com/noaa/uspto-oa-cli
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: cli,office-action,patent,prosecution,uspto
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Intended Audience :: Legal Industry
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
|
|
16
|
+
Requires-Python: >=3.13
|
|
17
|
+
Requires-Dist: click>=8.0
|
|
18
|
+
Requires-Dist: pypdf>=6.11.0
|
|
19
|
+
Requires-Dist: requests>=2.34.2
|
|
20
|
+
Requires-Dist: rich>=15.0.0
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# odp-oa-cli
|
|
24
|
+
|
|
25
|
+
USPTO ODP(Open Data Portal) API를 통해 미국 특허 심사과정 문서를 다운로드하고, XML 파싱을 거쳐 구조화된 Markdown으로 변환하는 CLI 도구.
|
|
26
|
+
|
|
27
|
+
생성된 MD 파일을 Claude Code, Gemini CLI 등 AI 에이전트에게 전달하여 심사 전략 분석을 수행하는 워크플로우를 지원한다.
|
|
28
|
+
|
|
29
|
+
## 요구사항
|
|
30
|
+
|
|
31
|
+
- Python 3.13+
|
|
32
|
+
- [uv](https://docs.astral.sh/uv/)
|
|
33
|
+
- USPTO API 키 ([ODP 포털](https://developer.uspto.gov/)에서 발급)
|
|
34
|
+
|
|
35
|
+
## 설치
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
# 로컬 개발
|
|
39
|
+
uv sync
|
|
40
|
+
|
|
41
|
+
# PyPI에서 설치
|
|
42
|
+
pip install odp-oa-cli
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## API 키 설정
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# 대화형 설정 (권장) — ~/.oa-cli.toml 에 저장
|
|
49
|
+
uspto-oa configure
|
|
50
|
+
|
|
51
|
+
# 현재 설정 확인
|
|
52
|
+
uspto-oa configure --show
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
또는 환경변수로 지정:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
export USPTO_API_KEY=your_api_key_here
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 사용법
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# 1. 문서 다운로드 (file/{app_num}/ 에 저장)
|
|
65
|
+
uspto-oa download 16330077
|
|
66
|
+
|
|
67
|
+
# 2. XML 파싱 → prosecution.md 생성
|
|
68
|
+
uspto-oa extract 16330077
|
|
69
|
+
# 결과: file/16330077/16330077_prosecution.md
|
|
70
|
+
|
|
71
|
+
# 특허번호 입력 (출원번호 자동 변환)
|
|
72
|
+
uspto-oa download US12228644B2
|
|
73
|
+
|
|
74
|
+
# 특정 문서 코드만 다운로드
|
|
75
|
+
uspto-oa download 16330077 --doc-codes CTNF,CTFR,NOA
|
|
76
|
+
|
|
77
|
+
# 강제 재다운로드 (기존 파일 덮어쓰기)
|
|
78
|
+
uspto-oa download 16330077 --force
|
|
79
|
+
|
|
80
|
+
# 상세 로그
|
|
81
|
+
uspto-oa -v download 16330077
|
|
82
|
+
|
|
83
|
+
# 일회성 API 키 지정
|
|
84
|
+
uspto-oa download 16330077 --api-key YOUR_KEY
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 커맨드 옵션
|
|
88
|
+
|
|
89
|
+
**`uspto-oa download <application>`**
|
|
90
|
+
|
|
91
|
+
| 옵션 | 설명 |
|
|
92
|
+
|------|------|
|
|
93
|
+
| `--doc-codes CODES` | 쉼표 구분 문서 코드 (예: `CTNF,CTFR,NOA`). 생략 시 전체 수집 대상 |
|
|
94
|
+
| `--output-dir DIR` | 저장 경로 (기본: `file/{app_num}/`) |
|
|
95
|
+
| `--force` | 기존 파일도 재다운로드 |
|
|
96
|
+
| `--api-key TEXT` | API 키 (설정 파일·환경변수보다 우선) |
|
|
97
|
+
|
|
98
|
+
**`uspto-oa extract <application>`**
|
|
99
|
+
|
|
100
|
+
| 옵션 | 설명 |
|
|
101
|
+
|------|------|
|
|
102
|
+
| `--output-dir DIR` | 파일 디렉토리 (기본: `file/{app_num}/`) |
|
|
103
|
+
|
|
104
|
+
## 워크플로우
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
uspto-oa download {app_num}
|
|
108
|
+
└─ file/{app_num}/ 에 XML / PDF 저장
|
|
109
|
+
|
|
110
|
+
uspto-oa extract {app_num}
|
|
111
|
+
└─ file/{app_num}/{app_num}_prosecution.md 생성
|
|
112
|
+
└─ AI 에이전트 (Claude Code / Gemini CLI)
|
|
113
|
+
└─ 심사 전략 분석, 요약, 질의응답
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## 수집 대상 문서 코드
|
|
117
|
+
|
|
118
|
+
| 코드 | 의미 |
|
|
119
|
+
|------|------|
|
|
120
|
+
| `CTNF` | Non-Final Office Action |
|
|
121
|
+
| `CTFR` | Final Office Action |
|
|
122
|
+
| `NOA` / `NACT` | Notice of Allowance |
|
|
123
|
+
| `REM` | Remarks (의견서) |
|
|
124
|
+
| `ABN` | Abandonment |
|
|
125
|
+
| `SRNT` / `SRFW` | Search Report |
|
|
126
|
+
| `EXIN` | Examiner Interview |
|
|
127
|
+
| `RCE` / `RCEX` | Request for Continued Examination |
|
|
128
|
+
| `CTAV` | Advisory Action |
|
|
129
|
+
| `892` / `1449` / `IDS` | Prior Art / IDS |
|
|
130
|
+
| `A*` | Amendment 계열 전체 |
|
|
131
|
+
|
|
132
|
+
## 생성 파일 구조
|
|
133
|
+
|
|
134
|
+
`file/{app_num}/{app_num}_prosecution.md`:
|
|
135
|
+
|
|
136
|
+
| 섹션 | 내용 |
|
|
137
|
+
|------|------|
|
|
138
|
+
| 타임라인 | 전체 문서 날짜순 표 (XML/PDF 형식 표시) |
|
|
139
|
+
| Office Action 상세 | CTNF/CTFR 거절 항목 전문 |
|
|
140
|
+
| Notice of Allowance 상세 | 허여 청구항 + Examiner's Statement |
|
|
141
|
+
| PDF 전용 문서 | Amendment 등 이미지 PDF 목록 (AI 에이전트 직접 전달용) |
|
|
142
|
+
|
|
143
|
+
## PyPI 배포
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
uv build
|
|
147
|
+
uv run twine upload dist/*
|
|
148
|
+
```
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# odp-oa-cli
|
|
2
|
+
|
|
3
|
+
USPTO ODP(Open Data Portal) API를 통해 미국 특허 심사과정 문서를 다운로드하고, XML 파싱을 거쳐 구조화된 Markdown으로 변환하는 CLI 도구.
|
|
4
|
+
|
|
5
|
+
생성된 MD 파일을 Claude Code, Gemini CLI 등 AI 에이전트에게 전달하여 심사 전략 분석을 수행하는 워크플로우를 지원한다.
|
|
6
|
+
|
|
7
|
+
## 요구사항
|
|
8
|
+
|
|
9
|
+
- Python 3.13+
|
|
10
|
+
- [uv](https://docs.astral.sh/uv/)
|
|
11
|
+
- USPTO API 키 ([ODP 포털](https://developer.uspto.gov/)에서 발급)
|
|
12
|
+
|
|
13
|
+
## 설치
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# 로컬 개발
|
|
17
|
+
uv sync
|
|
18
|
+
|
|
19
|
+
# PyPI에서 설치
|
|
20
|
+
pip install odp-oa-cli
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## API 키 설정
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# 대화형 설정 (권장) — ~/.oa-cli.toml 에 저장
|
|
27
|
+
uspto-oa configure
|
|
28
|
+
|
|
29
|
+
# 현재 설정 확인
|
|
30
|
+
uspto-oa configure --show
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
또는 환경변수로 지정:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
export USPTO_API_KEY=your_api_key_here
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 사용법
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# 1. 문서 다운로드 (file/{app_num}/ 에 저장)
|
|
43
|
+
uspto-oa download 16330077
|
|
44
|
+
|
|
45
|
+
# 2. XML 파싱 → prosecution.md 생성
|
|
46
|
+
uspto-oa extract 16330077
|
|
47
|
+
# 결과: file/16330077/16330077_prosecution.md
|
|
48
|
+
|
|
49
|
+
# 특허번호 입력 (출원번호 자동 변환)
|
|
50
|
+
uspto-oa download US12228644B2
|
|
51
|
+
|
|
52
|
+
# 특정 문서 코드만 다운로드
|
|
53
|
+
uspto-oa download 16330077 --doc-codes CTNF,CTFR,NOA
|
|
54
|
+
|
|
55
|
+
# 강제 재다운로드 (기존 파일 덮어쓰기)
|
|
56
|
+
uspto-oa download 16330077 --force
|
|
57
|
+
|
|
58
|
+
# 상세 로그
|
|
59
|
+
uspto-oa -v download 16330077
|
|
60
|
+
|
|
61
|
+
# 일회성 API 키 지정
|
|
62
|
+
uspto-oa download 16330077 --api-key YOUR_KEY
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 커맨드 옵션
|
|
66
|
+
|
|
67
|
+
**`uspto-oa download <application>`**
|
|
68
|
+
|
|
69
|
+
| 옵션 | 설명 |
|
|
70
|
+
|------|------|
|
|
71
|
+
| `--doc-codes CODES` | 쉼표 구분 문서 코드 (예: `CTNF,CTFR,NOA`). 생략 시 전체 수집 대상 |
|
|
72
|
+
| `--output-dir DIR` | 저장 경로 (기본: `file/{app_num}/`) |
|
|
73
|
+
| `--force` | 기존 파일도 재다운로드 |
|
|
74
|
+
| `--api-key TEXT` | API 키 (설정 파일·환경변수보다 우선) |
|
|
75
|
+
|
|
76
|
+
**`uspto-oa extract <application>`**
|
|
77
|
+
|
|
78
|
+
| 옵션 | 설명 |
|
|
79
|
+
|------|------|
|
|
80
|
+
| `--output-dir DIR` | 파일 디렉토리 (기본: `file/{app_num}/`) |
|
|
81
|
+
|
|
82
|
+
## 워크플로우
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
uspto-oa download {app_num}
|
|
86
|
+
└─ file/{app_num}/ 에 XML / PDF 저장
|
|
87
|
+
|
|
88
|
+
uspto-oa extract {app_num}
|
|
89
|
+
└─ file/{app_num}/{app_num}_prosecution.md 생성
|
|
90
|
+
└─ AI 에이전트 (Claude Code / Gemini CLI)
|
|
91
|
+
└─ 심사 전략 분석, 요약, 질의응답
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## 수집 대상 문서 코드
|
|
95
|
+
|
|
96
|
+
| 코드 | 의미 |
|
|
97
|
+
|------|------|
|
|
98
|
+
| `CTNF` | Non-Final Office Action |
|
|
99
|
+
| `CTFR` | Final Office Action |
|
|
100
|
+
| `NOA` / `NACT` | Notice of Allowance |
|
|
101
|
+
| `REM` | Remarks (의견서) |
|
|
102
|
+
| `ABN` | Abandonment |
|
|
103
|
+
| `SRNT` / `SRFW` | Search Report |
|
|
104
|
+
| `EXIN` | Examiner Interview |
|
|
105
|
+
| `RCE` / `RCEX` | Request for Continued Examination |
|
|
106
|
+
| `CTAV` | Advisory Action |
|
|
107
|
+
| `892` / `1449` / `IDS` | Prior Art / IDS |
|
|
108
|
+
| `A*` | Amendment 계열 전체 |
|
|
109
|
+
|
|
110
|
+
## 생성 파일 구조
|
|
111
|
+
|
|
112
|
+
`file/{app_num}/{app_num}_prosecution.md`:
|
|
113
|
+
|
|
114
|
+
| 섹션 | 내용 |
|
|
115
|
+
|------|------|
|
|
116
|
+
| 타임라인 | 전체 문서 날짜순 표 (XML/PDF 형식 표시) |
|
|
117
|
+
| Office Action 상세 | CTNF/CTFR 거절 항목 전문 |
|
|
118
|
+
| Notice of Allowance 상세 | 허여 청구항 + Examiner's Statement |
|
|
119
|
+
| PDF 전용 문서 | Amendment 등 이미지 PDF 목록 (AI 에이전트 직접 전달용) |
|
|
120
|
+
|
|
121
|
+
## PyPI 배포
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
uv build
|
|
125
|
+
uv run twine upload dist/*
|
|
126
|
+
```
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "uspto-oa-cli"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "USPTO 특허 심사과정 분석 CLI — 문서 다운로드 · XML 파싱 · MD 생성"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.13"
|
|
12
|
+
keywords = ["uspto", "patent", "office-action", "prosecution", "cli"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Environment :: Console",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Intended Audience :: Legal Industry",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.13",
|
|
21
|
+
"Topic :: Internet :: WWW/HTTP :: Indexing/Search",
|
|
22
|
+
]
|
|
23
|
+
dependencies = [
|
|
24
|
+
"click>=8.0",
|
|
25
|
+
"pypdf>=6.11.0",
|
|
26
|
+
"requests>=2.34.2",
|
|
27
|
+
"rich>=15.0.0",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Homepage = "https://github.com/noaa/uspto-oa-cli"
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
uspto-oa = "oa_cli.cli:main"
|
|
35
|
+
|
|
36
|
+
[tool.hatch.build.targets.wheel]
|
|
37
|
+
packages = ["src/oa_cli"]
|
|
38
|
+
|
|
39
|
+
[dependency-groups]
|
|
40
|
+
dev = [
|
|
41
|
+
"python-dotenv>=1.0",
|
|
42
|
+
"hatchling>=1.29.0",
|
|
43
|
+
"twine>=6.0",
|
|
44
|
+
"pytest>=8.0",
|
|
45
|
+
"ruff>=0.4",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[tool.pytest.ini_options]
|
|
49
|
+
testpaths = ["tests"]
|
|
50
|
+
addopts = "-v"
|
|
51
|
+
|
|
52
|
+
[tool.ruff.lint]
|
|
53
|
+
select = ["E", "F", "I"]
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"""USPTO 특허 심사과정 분석 CLI."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
|
|
12
|
+
from oa_cli import __version__
|
|
13
|
+
from oa_cli import config as cfg
|
|
14
|
+
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@click.group(invoke_without_command=True)
|
|
19
|
+
@click.version_option(__version__, prog_name="uspto-oa")
|
|
20
|
+
@click.option("--verbose", "-v", is_flag=True, default=False, help="상세 로그 출력.")
|
|
21
|
+
@click.pass_context
|
|
22
|
+
def main(ctx: click.Context, verbose: bool) -> None:
|
|
23
|
+
"""USPTO 특허 심사과정 분석 CLI.
|
|
24
|
+
|
|
25
|
+
\b
|
|
26
|
+
빠른 시작:
|
|
27
|
+
uspto-oa configure # API 키 저장
|
|
28
|
+
uspto-oa download 16330077 # 문서 다운로드
|
|
29
|
+
uspto-oa extract 16330077 # XML 파싱 → MD 생성
|
|
30
|
+
"""
|
|
31
|
+
ctx.ensure_object(dict)
|
|
32
|
+
ctx.obj["verbose"] = verbose
|
|
33
|
+
level = logging.DEBUG if verbose else logging.WARNING
|
|
34
|
+
logging.basicConfig(stream=sys.stderr, level=level, format="%(levelname)s %(message)s")
|
|
35
|
+
if ctx.invoked_subcommand is None:
|
|
36
|
+
click.echo(f"uspto-oa v{__version__}")
|
|
37
|
+
click.echo(ctx.get_help())
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# ── configure ────────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
@main.command()
|
|
43
|
+
@click.option("--show", is_flag=True, default=False, help="현재 설정만 표시하고 종료.")
|
|
44
|
+
def configure(show: bool) -> None:
|
|
45
|
+
"""API 키와 기본 설정을 저장합니다.
|
|
46
|
+
|
|
47
|
+
\b
|
|
48
|
+
설정 파일: ~/.oa-cli.toml
|
|
49
|
+
Enter 입력 시 기존 값 유지.
|
|
50
|
+
"""
|
|
51
|
+
existing = cfg.load()
|
|
52
|
+
auth_cfg = existing.get("auth", {})
|
|
53
|
+
|
|
54
|
+
click.echo("OA CLI — 설정")
|
|
55
|
+
click.echo("─" * 40)
|
|
56
|
+
click.echo(f"설정 파일: {cfg.CONFIG_PATH}")
|
|
57
|
+
click.echo()
|
|
58
|
+
|
|
59
|
+
current_key = auth_cfg.get("api_key", "")
|
|
60
|
+
|
|
61
|
+
if show:
|
|
62
|
+
click.echo(f" auth.api_key = {cfg.mask_key(current_key)}")
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
new_key = click.prompt(
|
|
66
|
+
f"USPTO API 키 [{cfg.mask_key(current_key)}]",
|
|
67
|
+
default="",
|
|
68
|
+
show_default=False,
|
|
69
|
+
).strip()
|
|
70
|
+
|
|
71
|
+
final_key = new_key if new_key else current_key
|
|
72
|
+
if not final_key:
|
|
73
|
+
click.echo("\n경고: API 키가 없습니다. 나중에 다시 실행하세요.", err=True)
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
cfg.save({"auth": {"api_key": final_key}})
|
|
77
|
+
click.echo(f"\n설정 저장: {cfg.CONFIG_PATH}")
|
|
78
|
+
click.echo(f" auth.api_key = {cfg.mask_key(final_key)}")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# ── download ─────────────────────────────────────────────────────────────────
|
|
82
|
+
|
|
83
|
+
@main.command()
|
|
84
|
+
@click.argument("application")
|
|
85
|
+
@click.option("--doc-codes", default=None, metavar="CODES",
|
|
86
|
+
help="쉼표 구분 문서 코드 (예: CTNF,CTFR,NOA). 생략 시 전체 수집 대상.")
|
|
87
|
+
@click.option("--output-dir", default=None, metavar="DIR", help="저장 경로 (기본: file/{app_num}/).")
|
|
88
|
+
@click.option("--force", is_flag=True, default=False, help="기존 파일도 재다운로드.")
|
|
89
|
+
@click.option("--api-key", default=None, help="API 키 (설정 파일·환경변수보다 우선).")
|
|
90
|
+
@click.pass_context
|
|
91
|
+
def download(
|
|
92
|
+
ctx: click.Context,
|
|
93
|
+
application: str,
|
|
94
|
+
doc_codes: Optional[str],
|
|
95
|
+
output_dir: Optional[str],
|
|
96
|
+
force: bool,
|
|
97
|
+
api_key: Optional[str],
|
|
98
|
+
) -> None:
|
|
99
|
+
"""심사과정 문서를 다운로드합니다 (XML 우선, PDF 폴백).
|
|
100
|
+
|
|
101
|
+
\b
|
|
102
|
+
예시:
|
|
103
|
+
oa download 16330077
|
|
104
|
+
oa download US12228644B2
|
|
105
|
+
oa download 16330077 --doc-codes CTNF,CTFR,NOA
|
|
106
|
+
oa download 16330077 --force
|
|
107
|
+
"""
|
|
108
|
+
from oa_cli.prosecution.collect import download_docs, normalize_app_number, resolve_patent_number
|
|
109
|
+
|
|
110
|
+
key = cfg.require_api_key(api_key)
|
|
111
|
+
raw = application
|
|
112
|
+
|
|
113
|
+
if raw.upper().startswith("US") and any(c.isalpha() for c in raw[2:]):
|
|
114
|
+
console.print(f"[dim]특허번호 '{raw}' → 출원번호 조회 중...[/dim]")
|
|
115
|
+
app_num = resolve_patent_number(raw, key)
|
|
116
|
+
console.print(f"[dim]출원번호: {app_num}[/dim]")
|
|
117
|
+
else:
|
|
118
|
+
app_num = normalize_app_number(raw)
|
|
119
|
+
|
|
120
|
+
out_dir = output_dir or str(Path.cwd() / "file" / app_num)
|
|
121
|
+
codes = doc_codes.split(",") if doc_codes else None
|
|
122
|
+
|
|
123
|
+
console.print(f"[bold]출원번호[/] {app_num} → [bold]{out_dir}[/]")
|
|
124
|
+
console.print()
|
|
125
|
+
|
|
126
|
+
results = download_docs(
|
|
127
|
+
app_number=app_num,
|
|
128
|
+
api_key=key,
|
|
129
|
+
doc_codes=codes,
|
|
130
|
+
output_dir=out_dir,
|
|
131
|
+
force=force,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
if not results:
|
|
135
|
+
console.print("[yellow]다운로드된 문서가 없습니다.[/yellow]")
|
|
136
|
+
return
|
|
137
|
+
|
|
138
|
+
table = Table(title=f"다운로드 결과 — {app_num}", show_lines=False)
|
|
139
|
+
table.add_column("날짜", style="cyan", no_wrap=True)
|
|
140
|
+
table.add_column("코드", style="magenta")
|
|
141
|
+
table.add_column("형식", justify="center")
|
|
142
|
+
table.add_column("파일명")
|
|
143
|
+
table.add_column("상태", justify="center")
|
|
144
|
+
|
|
145
|
+
downloaded = skipped = 0
|
|
146
|
+
for r in results:
|
|
147
|
+
status = "[dim]스킵[/dim]" if r["skipped"] else "[green]완료[/green]"
|
|
148
|
+
skipped += r["skipped"]
|
|
149
|
+
downloaded += not r["skipped"]
|
|
150
|
+
fmt = r.get("fmt", "pdf").upper()
|
|
151
|
+
fmt_display = f"[green]{fmt}[/green]" if fmt == "XML" else fmt
|
|
152
|
+
table.add_row(r["date"], r["code"], fmt_display, Path(r["path"]).name, status)
|
|
153
|
+
|
|
154
|
+
console.print(table)
|
|
155
|
+
console.print(f"\n[bold green]{downloaded}개 다운로드[/] / [dim]{skipped}개 스킵[/]")
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# ── extract ──────────────────────────────────────────────────────────────────
|
|
159
|
+
|
|
160
|
+
@main.command()
|
|
161
|
+
@click.argument("application")
|
|
162
|
+
@click.option("--output-dir", default=None, metavar="DIR", help="파일 디렉토리 (기본: file/{app_num}/).")
|
|
163
|
+
def extract(application: str, output_dir: Optional[str]) -> None:
|
|
164
|
+
"""다운로드된 XML 파일을 파싱해 prosecution.md 를 생성합니다.
|
|
165
|
+
|
|
166
|
+
\b
|
|
167
|
+
예시:
|
|
168
|
+
oa extract 16330077
|
|
169
|
+
# 결과: file/16330077/16330077_prosecution.md
|
|
170
|
+
"""
|
|
171
|
+
from oa_cli.prosecution.extract import extract as do_extract
|
|
172
|
+
from oa_cli.prosecution.collect import normalize_app_number
|
|
173
|
+
|
|
174
|
+
app_num = normalize_app_number(application)
|
|
175
|
+
file_dir = output_dir or str(Path.cwd() / "file" / app_num)
|
|
176
|
+
out_path = str(Path(file_dir) / f"{app_num}_prosecution.md")
|
|
177
|
+
|
|
178
|
+
console.print(f"[bold]출원번호[/] {app_num} → [bold]{out_path}[/]")
|
|
179
|
+
do_extract(app_num, file_dir=file_dir, output_path=out_path)
|
|
180
|
+
console.print(f"[bold green]완료[/] {out_path}")
|