comci 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.
comci-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,90 @@
1
+ Metadata-Version: 2.4
2
+ Name: comci
3
+ Version: 0.1.0
4
+ Summary: Comci.net 시간표 API 비공식 클라이언트
5
+ Requires-Python: >=3.8
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: requests>=2.28.0
8
+
9
+ # Comci.net 시간표 API 클라이언트
10
+
11
+ 컴시간(comci.net) 비공식 API를 사용하여 학교 검색 및 시간표 조회를 할 수 있는 Python 모듈입니다.
12
+
13
+ ## 설치
14
+
15
+ ```bash
16
+ pip install -r requirements.txt
17
+ ```
18
+
19
+ ## 사용법
20
+
21
+ ### 1. 학교 검색 (지역, 학교명, 학교코드)
22
+
23
+ ```python
24
+ from comci import search_schools
25
+
26
+ # "신송" 검색
27
+ schools = search_schools("신송")
28
+ for s in schools:
29
+ print(s["region"], s["school_name"], s["school_code"])
30
+ # 인천 신송중학교 49654
31
+ # 인천 신송고등학교 51825
32
+ ```
33
+
34
+ ### 2. 특정 학년/반 시간표 조회
35
+
36
+ ```python
37
+ from comci import get_timetable
38
+
39
+ # 1학년 1반 시간표 (정리.md 반환 구조)
40
+ timetable = get_timetable(49654, grade=1, class_num=1)
41
+ # {"월": [...], "화": [...], "수": [...], "목": [...], "금": [...]}
42
+ ```
43
+
44
+ ### 3. 전체 학년/반 시간표 조회
45
+
46
+ ```python
47
+ # 학년, 반 미지정 시 전체
48
+ timetable = get_timetable(49654)
49
+ # {"1학년 1반": {...}, "1학년 2반": {...}, ...}
50
+ ```
51
+
52
+ ### 4. 검색 후 바로 시간표 조회
53
+
54
+ ```python
55
+ from comci import search_and_get_timetable
56
+
57
+ timetable = search_and_get_timetable("신송", school_index=0, grade=1, class_num=1)
58
+ ```
59
+
60
+ ## 프로젝트 구조
61
+
62
+ ```
63
+ comci/
64
+ ├── __init__.py # 패키지 진입점
65
+ ├── config.py # API URL, 상수
66
+ ├── school_search.py # 학교 검색
67
+ ├── timetable.py # 시간표 API 및 파싱
68
+ └── client.py # 통합 클라이언트
69
+ ```
70
+
71
+ ## 예시 실행
72
+
73
+ ```bash
74
+ python example_usage.py
75
+ ```
76
+
77
+ ## 시간표 데이터 구조
78
+
79
+ 각 요일별 리스트는 교시(1~8) 순서이며, 각 항목은:
80
+
81
+ ```python
82
+ {
83
+ "period": 1, # 교시
84
+ "time": "08:40", # 시간
85
+ "subject": "국어", # 과목
86
+ "teacher": "김선생", # 교사
87
+ "room": "101", # 강의실
88
+ "changed": False # 변경 수업 여부
89
+ }
90
+ ```
comci-0.1.0/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # Comci.net 시간표 API 클라이언트
2
+
3
+ 컴시간(comci.net) 비공식 API를 사용하여 학교 검색 및 시간표 조회를 할 수 있는 Python 모듈입니다.
4
+
5
+ ## 설치
6
+
7
+ ```bash
8
+ pip install -r requirements.txt
9
+ ```
10
+
11
+ ## 사용법
12
+
13
+ ### 1. 학교 검색 (지역, 학교명, 학교코드)
14
+
15
+ ```python
16
+ from comci import search_schools
17
+
18
+ # "신송" 검색
19
+ schools = search_schools("신송")
20
+ for s in schools:
21
+ print(s["region"], s["school_name"], s["school_code"])
22
+ # 인천 신송중학교 49654
23
+ # 인천 신송고등학교 51825
24
+ ```
25
+
26
+ ### 2. 특정 학년/반 시간표 조회
27
+
28
+ ```python
29
+ from comci import get_timetable
30
+
31
+ # 1학년 1반 시간표 (정리.md 반환 구조)
32
+ timetable = get_timetable(49654, grade=1, class_num=1)
33
+ # {"월": [...], "화": [...], "수": [...], "목": [...], "금": [...]}
34
+ ```
35
+
36
+ ### 3. 전체 학년/반 시간표 조회
37
+
38
+ ```python
39
+ # 학년, 반 미지정 시 전체
40
+ timetable = get_timetable(49654)
41
+ # {"1학년 1반": {...}, "1학년 2반": {...}, ...}
42
+ ```
43
+
44
+ ### 4. 검색 후 바로 시간표 조회
45
+
46
+ ```python
47
+ from comci import search_and_get_timetable
48
+
49
+ timetable = search_and_get_timetable("신송", school_index=0, grade=1, class_num=1)
50
+ ```
51
+
52
+ ## 프로젝트 구조
53
+
54
+ ```
55
+ comci/
56
+ ├── __init__.py # 패키지 진입점
57
+ ├── config.py # API URL, 상수
58
+ ├── school_search.py # 학교 검색
59
+ ├── timetable.py # 시간표 API 및 파싱
60
+ └── client.py # 통합 클라이언트
61
+ ```
62
+
63
+ ## 예시 실행
64
+
65
+ ```bash
66
+ python example_usage.py
67
+ ```
68
+
69
+ ## 시간표 데이터 구조
70
+
71
+ 각 요일별 리스트는 교시(1~8) 순서이며, 각 항목은:
72
+
73
+ ```python
74
+ {
75
+ "period": 1, # 교시
76
+ "time": "08:40", # 시간
77
+ "subject": "국어", # 과목
78
+ "teacher": "김선생", # 교사
79
+ "room": "101", # 강의실
80
+ "changed": False # 변경 수업 여부
81
+ }
82
+ ```
@@ -0,0 +1,16 @@
1
+ """
2
+ Comci.net 시간표 API 클라이언트
3
+ """
4
+
5
+ from .client import get_timetable, search_and_get_schools, search_and_get_timetable
6
+ from .school_search import search_schools
7
+ from .timetable import fetch_timetable_raw, parse_timetable
8
+
9
+ __all__ = [
10
+ "search_schools",
11
+ "search_and_get_schools",
12
+ "fetch_timetable_raw",
13
+ "parse_timetable",
14
+ "get_timetable",
15
+ "search_and_get_timetable",
16
+ ]
@@ -0,0 +1,69 @@
1
+ """
2
+ Comci.net API 통합 클라이언트
3
+ """
4
+
5
+ from typing import Any, Dict, List, Optional
6
+
7
+ from .school_search import search_schools
8
+ from .timetable import fetch_timetable_raw, parse_timetable
9
+
10
+
11
+ def search_and_get_schools(query: str) -> List[Dict[str, Any]]:
12
+ """
13
+ 검색어로 학교 검색
14
+
15
+ Returns:
16
+ [{"region": "인천", "school_name": "신송중학교", "school_code": 49654}, ...]
17
+ """
18
+ return search_schools(query)
19
+
20
+
21
+ def get_timetable(
22
+ school_code: int,
23
+ grade: Optional[int] = None,
24
+ class_num: Optional[int] = None,
25
+ date_index: int = 1,
26
+ ) -> Dict[str, Any]:
27
+ """
28
+ 학교 시간표 조회 (정리.md 기준)
29
+
30
+ Args:
31
+ school_code: 학교코드
32
+ grade: 학년 (1-based). None이면 전체
33
+ class_num: 반 (1-based). None이면 전체
34
+ date_index: 날짜 인덱스 (r)
35
+
36
+ Returns:
37
+ - grade, class_num 지정 시: {"월": [...], "화": [...], "수": [...], "목": [...], "금": [...]}
38
+ - 미지정 시: {"1학년 1반": {"월": [...], ...}, "1학년 2반": {...}, ...}
39
+ """
40
+ raw = fetch_timetable_raw(school_code, date_index)
41
+ parsed = parse_timetable(raw, grade=grade, class_num=class_num)
42
+ if grade is not None and class_num is not None and parsed:
43
+ return next(iter(parsed.values()))
44
+ return parsed
45
+
46
+
47
+ def search_and_get_timetable(
48
+ query: str,
49
+ school_index: int = 0,
50
+ grade: Optional[int] = None,
51
+ class_num: Optional[int] = None,
52
+ ) -> Dict[str, Any]:
53
+ """
54
+ 검색 → 학교 선택 → 시간표 조회를 한 번에 수행
55
+
56
+ Args:
57
+ query: 검색어
58
+ school_index: 검색 결과 중 선택할 학교 인덱스 (0=첫 번째)
59
+ grade: 학년 (None=전체)
60
+ class_num: 반 (None=전체)
61
+
62
+ Returns:
63
+ 시간표 데이터
64
+ """
65
+ schools = search_schools(query)
66
+ if not schools:
67
+ return {}
68
+ school = schools[school_index]
69
+ return get_timetable(school["school_code"], grade=grade, class_num=class_num)
@@ -0,0 +1,34 @@
1
+ """
2
+ Comci.net API 설정
3
+ """
4
+
5
+ # API 기본 URL
6
+ BASE_URL = "http://comci.net:4082/36179"
7
+
8
+ # 학교 검색 API 파라미터 prefix
9
+ SEARCH_PREFIX = "17384l"
10
+
11
+ # 시간표 API 고정 키
12
+ TIMETABLE_KEY = "73629"
13
+ TIMETABLE_CACHE = "0"
14
+
15
+ # 기본 날짜 인덱스 (r 파라미터)
16
+ DEFAULT_DATE_INDEX = 1
17
+
18
+ # 요청 헤더 (정리.md 기준)
19
+ DEFAULT_HEADERS = {
20
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
21
+ "Accept": "application/json,text/plain,*/*",
22
+ "Accept-Language": "ko-KR,ko;q=0.9",
23
+ "Referer": "http://comci.net:4082/st",
24
+ "x-requested-with": "XMLHttpRequest",
25
+ }
26
+
27
+ # 인코딩
28
+ SEARCH_ENCODING = "euc-kr"
29
+
30
+ # 요일 매핑
31
+ DAYS_OF_WEEK = ["월", "화", "수", "목", "금"]
32
+
33
+ # 빈 수업 판단 기준
34
+ EMPTY_CLASS_THRESHOLD = 100
@@ -0,0 +1,58 @@
1
+ """
2
+ 학교 검색 API 모듈
3
+ """
4
+
5
+ import json
6
+ import urllib.parse
7
+ from typing import Any, Dict, List
8
+
9
+ import requests
10
+
11
+ from .config import BASE_URL, SEARCH_PREFIX, SEARCH_ENCODING, DEFAULT_HEADERS
12
+
13
+
14
+ def encode_search_query(query: str) -> str:
15
+ """검색어를 EUC-KR 인코딩 후 퍼센트 인코딩"""
16
+ return urllib.parse.quote(query, encoding=SEARCH_ENCODING, safe="")
17
+
18
+
19
+ def search_schools(query: str) -> List[Dict[str, Any]]:
20
+ """
21
+ 학교 검색 API 호출
22
+
23
+ Args:
24
+ query: 검색어 (예: "신송", "신")
25
+
26
+ Returns:
27
+ 검색 결과 리스트. 각 항목: {region, school_name, school_code}
28
+ 알림 메시지(없으면 추가 검색하세요)는 제외됨
29
+ """
30
+ encoded = encode_search_query(query)
31
+ url = f"{BASE_URL}?{SEARCH_PREFIX}{encoded}"
32
+
33
+ response = requests.get(url, headers=DEFAULT_HEADERS, timeout=10)
34
+ response.raise_for_status()
35
+ # API 응답 인코딩 (JSON은 UTF-8)
36
+ if response.encoding in (None, "ISO-8859-1", "ascii"):
37
+ response.encoding = "utf-8"
38
+
39
+ # API가 여러 JSON 객체를 연속 반환할 수 있음 - 첫 번째만 파싱
40
+ data, _ = json.JSONDecoder().raw_decode(response.text)
41
+ raw_results = data.get("학교검색", [])
42
+ if not raw_results and data:
43
+ # 단일 키인 경우 해당 값 사용
44
+ vals = list(data.values())
45
+ if vals and isinstance(vals[0], list):
46
+ raw_results = vals[0]
47
+
48
+ results = []
49
+ for item in raw_results:
50
+ # [0, "알림", "없으면 추가 검색하세요", 0] 형태의 알림 제외
51
+ if len(item) >= 4 and item[1] != "알림" and item[3] != 0:
52
+ results.append({
53
+ "region": item[1],
54
+ "school_name": item[2],
55
+ "school_code": item[3],
56
+ })
57
+
58
+ return results
@@ -0,0 +1,251 @@
1
+ """
2
+ 시간표 API 및 파싱 모듈
3
+ """
4
+
5
+ import base64
6
+ import json
7
+ from typing import Any, Dict, List, Optional
8
+
9
+ import requests
10
+
11
+ from .config import (
12
+ BASE_URL,
13
+ TIMETABLE_KEY,
14
+ TIMETABLE_CACHE,
15
+ DEFAULT_DATE_INDEX,
16
+ DEFAULT_HEADERS,
17
+ DAYS_OF_WEEK,
18
+ EMPTY_CLASS_THRESHOLD,
19
+ )
20
+
21
+
22
+ def build_timetable_param(school_code: int, date_index: int = DEFAULT_DATE_INDEX) -> str:
23
+ """시간표 API용 Base64 파라미터 생성"""
24
+ raw = f"{TIMETABLE_KEY}_{school_code}_{TIMETABLE_CACHE}_{date_index}"
25
+ return base64.b64encode(raw.encode("utf-8")).decode("utf-8")
26
+
27
+
28
+ def fetch_timetable_raw(school_code: int, date_index: int = DEFAULT_DATE_INDEX) -> Dict[str, Any]:
29
+ """
30
+ 학교 시간표 원본 JSON 조회
31
+
32
+ Args:
33
+ school_code: 학교코드 (검색 API에서 획득)
34
+ date_index: 날짜 인덱스 (r 파라미터)
35
+
36
+ Returns:
37
+ API 응답 JSON 전체
38
+ """
39
+ param = build_timetable_param(school_code, date_index)
40
+ url = f"{BASE_URL}?{param}"
41
+
42
+ response = requests.get(url, headers=DEFAULT_HEADERS, timeout=10)
43
+ response.raise_for_status()
44
+ if response.encoding in (None, "ISO-8859-1", "ascii"):
45
+ response.encoding = "utf-8"
46
+
47
+ # API가 여러 JSON 객체를 연속 반환할 수 있음 - 자료481 포함된 객체 찾기
48
+ text = response.text.strip()
49
+ decoder = json.JSONDecoder()
50
+ idx = 0
51
+ while idx < len(text):
52
+ try:
53
+ data, end = decoder.raw_decode(text[idx:])
54
+ if isinstance(data, dict) and (
55
+ "자료481" in data or any("481" in str(k) for k in data.keys())
56
+ ):
57
+ return data
58
+ idx += end
59
+ except json.JSONDecodeError:
60
+ break
61
+ data, _ = json.JSONDecoder().raw_decode(text)
62
+ return data
63
+
64
+
65
+ def _get_code(data: Any, grade: int, class_num: int, day: int, period: int) -> int:
66
+ """
67
+ 자료481/자료147에서 코드 추출
68
+ 실제 구조: [numGrades, grade0, grade1, ...], grade_i=[numClasses, class0, ...],
69
+ class_j=[5, day0, day1, ...], day_k=[numPeriods, code1, code2, ...]
70
+ """
71
+ if not data or not isinstance(data, list):
72
+ return 0
73
+ # grade: 0-based, 인덱스 0은 numGrades이므로 grade+1
74
+ if grade + 1 >= len(data):
75
+ return 0
76
+ grade_data = data[grade + 1]
77
+ if not isinstance(grade_data, list) or class_num + 1 >= len(grade_data):
78
+ return 0
79
+ class_data = grade_data[class_num + 1]
80
+ if not isinstance(class_data, list) or day >= len(class_data):
81
+ return 0
82
+ day_data = class_data[day] # day 1-based (1=월) -> 인덱스 1
83
+ if not isinstance(day_data, list) or period >= len(day_data):
84
+ return 0
85
+ # day_data[0] = num periods, 실제 코드는 [1]부터 (period 1-based)
86
+ val = day_data[period] if period < len(day_data) else 0
87
+ return int(val) if val else 0
88
+
89
+
90
+ def _get_room(data: Any, grade: int, class_num: int, day: int, period: int) -> str:
91
+ """자료245에서 강의실 문자열 추출 (자료481과 동일 구조)"""
92
+ if not data or not isinstance(data, list):
93
+ return ""
94
+ if grade + 1 >= len(data):
95
+ return ""
96
+ grade_data = data[grade + 1]
97
+ if not isinstance(grade_data, list) or class_num + 1 >= len(grade_data):
98
+ return ""
99
+ class_data = grade_data[class_num + 1]
100
+ if not isinstance(class_data, list) or day >= len(class_data):
101
+ return ""
102
+ day_data = class_data[day] # day 1-based
103
+ if not isinstance(day_data, list) or period >= len(day_data):
104
+ return ""
105
+ val = day_data[period] # period 1-based
106
+ return str(val) if val else ""
107
+
108
+
109
+ def _parse_single_class(
110
+ data: Dict[str, Any],
111
+ grade: int,
112
+ class_num: int,
113
+ ) -> Dict[str, List[Dict[str, Any]]]:
114
+ """
115
+ 특정 학년/반의 시간표 파싱
116
+ grade, class_num은 0-based 인덱스
117
+ """
118
+ split_val = data.get("분리", 100)
119
+ period_times = data.get("일과시간", [])
120
+ room_data = _get_data_key(data, "245")
121
+ base_data = _get_data_key(data, "481")
122
+ changed_data = _get_data_key(data, "147")
123
+ subject_data = _get_data_key(data, "492")
124
+ teacher_data = _get_data_key(data, "446")
125
+
126
+ timetable_by_day: Dict[str, List[Dict[str, Any]]] = {
127
+ day: [] for day in DAYS_OF_WEEK
128
+ }
129
+
130
+ for period in range(1, 9): # 1~8교시
131
+ row = []
132
+ for day in range(1, 6): # 1~5 (월~금)
133
+ base = _get_code(base_data, grade, class_num, day, period)
134
+ changed = _get_code(changed_data, grade, class_num, day, period)
135
+ code = changed if changed > 0 else base
136
+
137
+ if not code or code <= EMPTY_CLASS_THRESHOLD:
138
+ row.append(None)
139
+ continue
140
+
141
+ # 정리.md: 분리=100 → teacher=floor(code/분리), subject=code%분리
142
+ # 분리=1000 → 공식 반대 (mTh/mSb 로직)
143
+ if split_val == 100:
144
+ teacher_idx = code // split_val
145
+ subject_idx = code % split_val
146
+ else:
147
+ teacher_idx = code % split_val
148
+ subject_idx = code // split_val
149
+
150
+ subject = subject_data[subject_idx] if subject_idx < len(subject_data) else "미정"
151
+ teacher = teacher_data[teacher_idx] if teacher_idx < len(teacher_data) else "미정"
152
+
153
+ room_raw = _get_room(room_data, grade, class_num, day, period)
154
+ parts = str(room_raw).split("_", 1)
155
+ room = parts[1] if len(parts) > 1 else (parts[0] if parts else "")
156
+
157
+ time_str = period_times[period - 1] if period <= len(period_times) else ""
158
+
159
+ row.append({
160
+ "period": period,
161
+ "time": time_str,
162
+ "day": day,
163
+ "subject": subject,
164
+ "teacher": teacher,
165
+ "room": room,
166
+ "changed": base != changed,
167
+ })
168
+
169
+ for idx, day_name in enumerate(DAYS_OF_WEEK):
170
+ timetable_by_day[day_name].append(row[idx])
171
+
172
+ return timetable_by_day
173
+
174
+
175
+ def _get_nested(data: List, *indices: int):
176
+ """중첩 리스트/딕셔너리 안전 접근"""
177
+ obj = data
178
+ for idx in indices:
179
+ if obj is None or (isinstance(obj, list) and idx >= len(obj)):
180
+ return 0
181
+ if isinstance(obj, list):
182
+ obj = obj[idx]
183
+ elif isinstance(obj, dict):
184
+ obj = obj.get(idx, 0)
185
+ return obj if obj is not None else 0
186
+
187
+
188
+ def _get_data_key(data: Dict, suffix: str):
189
+ """키 인코딩 이슈 대응 - suffix로 키 찾기"""
190
+ key = "자료" + suffix
191
+ if key in data:
192
+ return data[key]
193
+ for k in data.keys():
194
+ if suffix in str(k):
195
+ return data[k]
196
+ return []
197
+
198
+
199
+ def parse_timetable(
200
+ data: Dict[str, Any],
201
+ grade: Optional[int] = None,
202
+ class_num: Optional[int] = None,
203
+ ) -> Dict[str, Any]:
204
+ """
205
+ 시간표 JSON 파싱
206
+
207
+ Args:
208
+ data: fetch_timetable_raw() 반환값
209
+ grade: 학년 (1-based, 1=1학년). None이면 전체
210
+ class_num: 반 (1-based, 1=1반). None이면 전체
211
+
212
+ Returns:
213
+ - grade, class_num 둘 다 지정: 해당 학년/반만
214
+ {"1학년 1반": {"월": [...], "화": [...], ...}}
215
+ - 둘 다 None: 전체 학년/반
216
+ {"1학년 1반": {...}, "1학년 2반": {...}, ...}
217
+ """
218
+ base_data = _get_data_key(data, "481")
219
+ if not base_data or not isinstance(base_data, list):
220
+ return {}
221
+
222
+ num_grades = base_data[0] if isinstance(base_data[0], (int, float)) else len(base_data) - 1
223
+ result = {}
224
+
225
+ if grade is not None and class_num is not None:
226
+ g_idx = grade - 1
227
+ c_idx = class_num - 1
228
+ if g_idx < num_grades and g_idx + 1 < len(base_data):
229
+ grade_data = base_data[g_idx + 1]
230
+ num_classes = grade_data[0] if isinstance(grade_data, list) and grade_data else 0
231
+ if c_idx < num_classes:
232
+ key = f"{grade}학년 {class_num}반"
233
+ result[key] = _parse_single_class(data, g_idx, c_idx)
234
+ return result
235
+
236
+ # 전체 학년/반
237
+ for g_idx in range(num_grades):
238
+ if g_idx + 1 >= len(base_data):
239
+ continue
240
+ grade_data = base_data[g_idx + 1]
241
+ if not isinstance(grade_data, list):
242
+ continue
243
+ num_classes = grade_data[0] if isinstance(grade_data[0], (int, float)) else len(grade_data) - 1
244
+ for c_idx in range(num_classes):
245
+ key = f"{g_idx + 1}학년 {c_idx + 1}반"
246
+ try:
247
+ result[key] = _parse_single_class(data, g_idx, c_idx)
248
+ except (IndexError, KeyError, TypeError):
249
+ continue
250
+
251
+ return result
@@ -0,0 +1,90 @@
1
+ Metadata-Version: 2.4
2
+ Name: comci
3
+ Version: 0.1.0
4
+ Summary: Comci.net 시간표 API 비공식 클라이언트
5
+ Requires-Python: >=3.8
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: requests>=2.28.0
8
+
9
+ # Comci.net 시간표 API 클라이언트
10
+
11
+ 컴시간(comci.net) 비공식 API를 사용하여 학교 검색 및 시간표 조회를 할 수 있는 Python 모듈입니다.
12
+
13
+ ## 설치
14
+
15
+ ```bash
16
+ pip install -r requirements.txt
17
+ ```
18
+
19
+ ## 사용법
20
+
21
+ ### 1. 학교 검색 (지역, 학교명, 학교코드)
22
+
23
+ ```python
24
+ from comci import search_schools
25
+
26
+ # "신송" 검색
27
+ schools = search_schools("신송")
28
+ for s in schools:
29
+ print(s["region"], s["school_name"], s["school_code"])
30
+ # 인천 신송중학교 49654
31
+ # 인천 신송고등학교 51825
32
+ ```
33
+
34
+ ### 2. 특정 학년/반 시간표 조회
35
+
36
+ ```python
37
+ from comci import get_timetable
38
+
39
+ # 1학년 1반 시간표 (정리.md 반환 구조)
40
+ timetable = get_timetable(49654, grade=1, class_num=1)
41
+ # {"월": [...], "화": [...], "수": [...], "목": [...], "금": [...]}
42
+ ```
43
+
44
+ ### 3. 전체 학년/반 시간표 조회
45
+
46
+ ```python
47
+ # 학년, 반 미지정 시 전체
48
+ timetable = get_timetable(49654)
49
+ # {"1학년 1반": {...}, "1학년 2반": {...}, ...}
50
+ ```
51
+
52
+ ### 4. 검색 후 바로 시간표 조회
53
+
54
+ ```python
55
+ from comci import search_and_get_timetable
56
+
57
+ timetable = search_and_get_timetable("신송", school_index=0, grade=1, class_num=1)
58
+ ```
59
+
60
+ ## 프로젝트 구조
61
+
62
+ ```
63
+ comci/
64
+ ├── __init__.py # 패키지 진입점
65
+ ├── config.py # API URL, 상수
66
+ ├── school_search.py # 학교 검색
67
+ ├── timetable.py # 시간표 API 및 파싱
68
+ └── client.py # 통합 클라이언트
69
+ ```
70
+
71
+ ## 예시 실행
72
+
73
+ ```bash
74
+ python example_usage.py
75
+ ```
76
+
77
+ ## 시간표 데이터 구조
78
+
79
+ 각 요일별 리스트는 교시(1~8) 순서이며, 각 항목은:
80
+
81
+ ```python
82
+ {
83
+ "period": 1, # 교시
84
+ "time": "08:40", # 시간
85
+ "subject": "국어", # 과목
86
+ "teacher": "김선생", # 교사
87
+ "room": "101", # 강의실
88
+ "changed": False # 변경 수업 여부
89
+ }
90
+ ```
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ comci/__init__.py
4
+ comci/client.py
5
+ comci/config.py
6
+ comci/school_search.py
7
+ comci/timetable.py
8
+ comci.egg-info/PKG-INFO
9
+ comci.egg-info/SOURCES.txt
10
+ comci.egg-info/dependency_links.txt
11
+ comci.egg-info/requires.txt
12
+ comci.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ requests>=2.28.0
@@ -0,0 +1 @@
1
+ comci
@@ -0,0 +1,15 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "comci"
7
+ version = "0.1.0"
8
+ description = "Comci.net 시간표 API 비공식 클라이언트"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ dependencies = ["requests>=2.28.0"]
12
+
13
+ [tool.setuptools]
14
+ packages = ["comci"]
15
+
comci-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+