Metadata-Version: 2.4
Name: comci
Version: 0.7.0
Summary: Comci.net 시간표 API 비공식 클라이언트
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.28.0

# Comci.net 시간표 API 클라이언트

컴시간(comci.net) 비공식 API를 사용하여 학교 검색 및 시간표 조회를 할 수 있는 Python 모듈입니다.

## 설치

```bash
pip install -r requirements.txt
```

## 사용법

### 1. 학교 검색 (지역, 학교명, 학교코드)

```python
from comci import search_schools

# "신송" 검색
schools = search_schools("신송")
for s in schools:
    print(s["region"], s["school_name"], s["school_code"])
# 인천 신송중학교 49654
# 인천 신송고등학교 51825
```

### 2. 특정 학년/반 시간표 조회

```python
from comci import get_timetable

# 1학년 1반 시간표 (정리.md 반환 구조)
timetable = get_timetable(49654, grade=1, class_num=1)
# {"월": [...], "화": [...], "수": [...], "목": [...], "금": [...]}
```

### 2-1. 주차 선택 (일자 드롭다운과 동일)

학생 시간표에서 URL이 `...MF8x`(이번 주) → `...MF8y`(다음 주)처럼 바뀌는 것은 Base64 안의 마지막 숫자 `r` 때문입니다. `date_index`로 지정합니다.

```python
# r=1 (기본): 이번 주 — http://comci.net:4082/36179?NzM2MjlfNDk2NTRfMF8x 와 동일
get_timetable(49654, grade=1, class_num=1, date_index=1)

# r=2: 다음 주 — ...NzM2MjlfNDk2NTRfMF8y 와 동일
get_timetable(49654, grade=1, class_num=1, date_index=2)
```

### 3. 전체 학년/반 시간표 조회

```python
# 학년, 반 미지정 시 전체
timetable = get_timetable(49654)
# {"1학년 1반": {...}, "1학년 2반": {...}, ...}
```

### 4. 검색 후 바로 시간표 조회

```python
from comci import search_and_get_timetable

timetable = search_and_get_timetable("신송", school_index=0, grade=1, class_num=1)
# 다음 주: date_index=2
timetable_next = search_and_get_timetable("신송", school_index=0, grade=1, class_num=1, date_index=2)
```

### 5. 달력 날짜로 주차(r) 자동 선택 · 메타데이터

`일자자료`·`오늘r`을 쓰지 않고 `r=1`만 고정으로 부르면, **달력상 다음 주**인데 API는 이번 주만 주는 경우가 생깁니다. `on=`에 `datetime.date`를 넘기면 해당 날짜가 들어 있는 주의 `r`을 고른 뒤 그 주 JSON을 다시 요청합니다.

```python
from datetime import date, timedelta
from comci import get_timetable, get_day_timetable

today = date.today()

# 오늘이 속한 주 + 각 칸에 ISO 날짜·r 부착 (기본)
get_timetable(49654, grade=1, class_num=1, on=today)

# 응답에 r, 일자자료 파싱본, 오늘r 포함
bundle = get_timetable(49654, grade=1, class_num=1, on=today, with_context=True)
ctx = bundle["context"]   # requested_r, today_r, weeks, 일자자료, day_dates, …
tt = bundle["timetable"]

# 그날(월~금) 교시 열만
get_day_timetable(49654, today, grade=1, class_num=1)
```

저수준으로는 `fetch_timetable_for_calendar_date`, `extract_timetable_context`, `parse_ilja_weeks`, `comci_weekday`, `timetable_for_calendar_day` 등을 패키지에서 직접 가져다 쓸 수 있습니다.

### 6. 파싱 오류를 숨기지 않기

전 학급 조회 시 일부 반만 실패하면 기본은 건너뛰고, 리스트에만 기록합니다.

```python
errs: list[str] = []
get_timetable(49654, parse_errors_out=errs)
# strict_parse=True 이면 첫 예외에서 중단
```

### 7. `/th` 학급·교사 시간표 (`comci.th`)

교사용 페이지와 동일 엔드포인트(`…/36179_T?…`)에서 JSON을 가져온 뒤, **학생용과 같은 규칙**으로 파싱합니다. 학급 키는 `"1학년 1반"` 형식입니다.

```python
from comci import get_timetable_by_class, get_teacher_timetable
# 또는
from comci import th

# 전 학급 (키워드로 주차 지정 — 두 번째 위치 인자는 학년이 아님)
get_timetable_by_class(49654, date_index=1)

# 학생용 client와 같은 옵션: 학년·반, 달력 날짜, 컨텍스트
th.get_timetable(49654, grade=1, class_num=1, on=date.today(), with_context=True)

# 교사별 (칸에 class_label 추가). with_context=True면 {"context", "timetable"}
get_teacher_timetable(49654, date_index=1)
get_teacher_timetable(49654, on=date.today(), grade=1, class_num=1, with_context=True)

# 검색 → /th 시간표
th.search_and_get_timetable("신송", school_index=0, grade=1, class_num=1)

# 하루 교시만 (/th)
th.get_day_timetable(49654, date.today(), grade=1, class_num=1)
```

저수준: `th.fetch_timetable_raw`, `th.fetch_timetable_for_calendar_date`, `th.resolve_date_index_for_school`, `th.parse_timetable`, `th.parse_teacher_timetable`.

### 8. HTTP API (`api_server.py`)

Flask로 패키지 함수를 JSON으로 호출합니다. 학생용(`get_timetable` 등)과 별도로 **`th_` 접두**로 `/th` 전용 fn을 둡니다.

- `get_timetable_by_class`, `get_teacher_timetable`: 이제 `grade`, `class_num`, `on`, `with_context`, `enrich_calendar`, `strict_parse`(학급만) 등을 넘길 수 있습니다.
- `th_fetch_timetable_raw`, `th_fetch_timetable_for_calendar_date`, `th_resolve_date_index_for_school`, `th_extract_timetable_context`, `th_parse_timetable`, `th_get_day_timetable`, `th_search_and_get_timetable`

실행: `python api_server.py`

## 프로젝트 구조

```
comci/
├── __init__.py       # 패키지 진입점 (/th 는 get_timetable_by_class 등으로 노출)
├── config.py         # API URL, 상수 (Final)
├── _http.py          # requests·인코딩·연속 JSON 파싱 공용
├── types.py          # TypedDict 등
├── school_search.py  # 학교 검색
├── timetable.py      # 학생용(/st) 시간표 API 및 파싱
├── client.py         # 학생용 통합 클라이언트
└── th/
    ├── __init__.py
    ├── config.py     # /th 전용 호스트·헤더 등
    ├── timetable.py  # /th 원시 조회 + 파싱(학생 파서 재사용)
    └── client.py     # /th 통합 클라이언트
```

## 예시 실행

```bash
python example_usage.py
```

## 시간표 데이터 구조

각 요일별 리스트는 교시(1~8) 순서이며, 각 항목은:

```python
{
    "r": 1,                      # 이번 요청 주차 (URL의 r)
    "day": 1,                    # 월=1 … 금=5
    "calendar_date": "2026-03-16",  # 해당 요일의 실제 날짜 (enrich 기본 True)
    "period": 1,
    "time": "1(09:10)",          # 일과시간
    "subject": "국어",
    "teacher": "김*",
    "room": "101",
    "changed": False             # 자료147이 적용됐고 481과 코드가 다를 때 True
}
```

빈 칸(공강 등)은 기존과 같이 `None`입니다. 교사별 조회 시 각 칸에 `"class_label": "1학년 1반"` 이 추가됩니다.
