nusmods-api 0.1.0__tar.gz → 0.2.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: nusmods-api
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: A Python wrapper for the NUSMods API
5
5
  Author: kommade
6
6
  Author-email: kommade <khoojarrell@gmail.com>
@@ -8,225 +8,225 @@ Requires-Dist: requests>=2.34.2
8
8
  Requires-Python: >=3.15.0
9
9
  Description-Content-Type: text/markdown
10
10
 
11
- # nusmods-api
12
-
13
- A typed Python wrapper for the [NUSMods API](https://api.nusmods.com/).
14
-
15
- The client provides access to module lists, detailed module information, venue
16
- data, and shared timetable URLs from [nusmods.com](https://nusmods.com/).
17
- Responses are converted into immutable Python dataclasses.
18
-
19
- ## Requirements
20
-
21
- - Python 3.15 or newer
22
- - Internet access when making API requests
23
-
24
- ## Installation
25
-
26
- ### With uv
27
-
28
- ```powershell
29
- uv add nusmods-api
30
- ```
31
-
32
- For local development:
33
-
34
- ```powershell
35
- uv sync
36
- ```
37
-
38
- ### With pip
39
-
40
- ```powershell
41
- pip install nusmods-api
42
- ```
43
-
44
- ## Quick start
45
-
46
- ```python
47
- from nusmods_api import NUSModsClient
48
-
49
- client = NUSModsClient(academic_year="2026-2027")
50
-
51
- modules = client.get_module_list()
52
- for module in modules[:5]:
53
- print(module.moduleCode, module.title)
54
-
55
- module = client.get_module("CS2030S")
56
- print(module.title)
57
- print(module.description)
58
- ```
59
-
60
- If no academic year is supplied, the client uses the current calendar year and
61
- the following year. For example, a client created in 2026 uses `2026-2027`.
62
-
63
- ## Client configuration
64
-
65
- ```python
66
- from nusmods_api import NUSModsClient
67
-
68
- client = NUSModsClient(
69
- academic_year="2026-2027",
70
- timeout=20.0,
71
- cache_directory=".nusmods_cache",
72
- cache_expiration=86_400,
73
- )
74
- ```
75
-
76
- | Argument | Default | Description |
77
- | --- | --- | --- |
78
- | `academic_year` | Current year and next year | Academic year in `YYYY-YYYY` format |
79
- | `timeout` | `10.0` | HTTP timeout in seconds |
80
- | `cache_directory` | `.nusmods_cache` | Directory for cached JSON responses |
81
- | `cache_expiration` | `86400` | Cache lifetime in seconds; use `None` for no expiration |
82
-
83
- Set `cache_directory=None` to disable caching:
84
-
85
- ```python
86
- client = NUSModsClient(cache_directory=None)
87
- ```
88
-
89
- ## API usage
90
-
91
- ### Module list
92
-
93
- ```python
94
- modules = client.get_module_list()
95
-
96
- for module in modules:
97
- print(module.moduleCode, module.title, module.semesters)
98
- ```
99
-
100
- ### Detailed module information
101
-
102
- ```python
103
- modules = client.get_module_information()
104
- module = client.get_module(" cs2030s ")
105
-
106
- print(module.moduleCode) # CS2030S
107
- print(module.acadYear)
108
- print(module.semesterData)
109
- ```
110
-
111
- Module codes are stripped and normalized to uppercase by `get_module`.
112
-
113
- ### Venues
114
-
115
- ```python
116
- venues = client.get_venues(semester=1)
117
- print(venues)
118
-
119
- venue_information = client.get_venue_information(semester=1)
120
- for venue, entries in venue_information.items():
121
- print(venue, entries)
122
- ```
123
-
124
- `semester` must be `1` or `2`.
125
-
126
- ### Shared timetables
127
-
128
- Pass a NUSMods share URL to `get_shared_timetable`:
129
-
130
- ```python
131
- share_url = (
132
- "https://nusmods.com/timetable/sem-1/share?"
133
- "CS1231S=TUT:25B,LEC:1&"
134
- "CS2030S=REC:04,LAB:10B,LEC:1&"
135
- "GES1034=LEC:1,TUT:E2&"
136
- "IS1108=TUT:14,LEC:2&"
137
- "MA1522=LEC:1"
138
- )
139
-
140
- timetable = client.get_shared_timetable(share_url)
141
-
142
- for lesson in timetable.weekly:
143
- print(
144
- lesson.moduleCode,
145
- lesson.lessonType,
146
- lesson.classNo,
147
- lesson.day,
148
- lesson.startTime,
149
- lesson.endTime,
150
- lesson.venue,
151
- )
152
-
153
- for exam in timetable.exams:
154
- print(exam.moduleCode, exam.examDate, exam.examDuration)
155
- ```
156
-
157
- The client parses the module selections from the URL and retrieves the
158
- corresponding module data from the NUSMods API. Lesson type codes such as
159
- `LEC`, `TUT`, `LAB`, and `REC` are mapped to their full names.
160
-
161
- `Timetable` also provides convenience methods:
162
-
163
- ```python
164
- monday = timetable.get_day_schedule("Monday")
165
- exam = timetable.get_exam_for_module("CS2030S")
166
- ```
167
-
168
- ## Data models
169
-
170
- The package exposes frozen dataclasses for common API response types:
171
-
172
- - `ModuleCondensed` — module code, title, and offered semesters
173
- - `Module` — detailed module information
174
- - `Lesson` — class schedule information
175
- - `LessonWithModule` — a lesson including its module code
176
- - `VenueLesson` and `VenueInformation` — venue timetable data
177
- - `WeekRange` — date-based lesson ranges
178
- - `Workload` — workload components
179
- - `PrerequisiteTree` — nested prerequisite expressions
180
- - `ExamTimetable` — exam information
181
- - `Timetable` — selected weekly lessons and exams
182
-
183
- Some model fields retain the NUSMods API naming convention, such as
184
- `moduleCode`, `classNo`, `startTime`, `lessonType`, and `examDate`.
185
-
186
- ## Error handling
187
-
188
- The client raises package-specific exceptions:
189
-
190
- ```python
191
- from nusmods_api import (
192
- NUSModsHTTPError,
193
- NUSModsInvalidResponseError,
194
- NUSModsNotFoundError,
195
- NUSModsRequestError,
196
- )
197
-
198
- try:
199
- module = client.get_module("CS2030S")
200
- except NUSModsNotFoundError:
201
- print("The module or academic year was not found")
202
- except NUSModsHTTPError as error:
203
- print(error.status_code, error.url)
204
- except NUSModsInvalidResponseError:
205
- print("The API returned invalid JSON")
206
- except NUSModsRequestError:
207
- print("The API could not be reached")
208
- ```
209
-
210
- Invalid client arguments, malformed share URLs, invalid semesters, and
211
- unmatched lesson selections raise `ValueError`.
212
-
213
- ## Testing
214
-
215
- Run the default unit test suite:
216
-
217
- ```powershell
218
- uv run pytest
219
- ```
220
-
221
- The live integration test makes real requests to the NUSMods API:
222
-
223
- ```powershell
224
- uv run pytest tests/test_live_client.py -m live -q
225
- ```
226
-
227
- The live test requires internet access and may need its expected timetable
228
- dates updated when NUSMods publishes a new academic-year dataset.
229
-
230
- ## License
231
-
11
+ # nusmods-api
12
+
13
+ A typed Python wrapper for the [NUSMods API](https://api.nusmods.com/).
14
+
15
+ The client provides access to module lists, detailed module information, venue
16
+ data, and shared timetable URLs from [nusmods.com](https://nusmods.com/).
17
+ Responses are converted into immutable Python dataclasses.
18
+
19
+ ## Requirements
20
+
21
+ - Python 3.15 or newer
22
+ - Internet access when making API requests
23
+
24
+ ## Installation
25
+
26
+ ### With uv
27
+
28
+ ```powershell
29
+ uv add nusmods-api
30
+ ```
31
+
32
+ For local development:
33
+
34
+ ```powershell
35
+ uv sync
36
+ ```
37
+
38
+ ### With pip
39
+
40
+ ```powershell
41
+ pip install nusmods-api
42
+ ```
43
+
44
+ ## Quick start
45
+
46
+ ```python
47
+ from nusmods_api import NUSModsClient
48
+
49
+ client = NUSModsClient(academic_year="2026-2027")
50
+
51
+ modules = client.get_module_list()
52
+ for module in modules[:5]:
53
+ print(module.moduleCode, module.title)
54
+
55
+ module = client.get_module("CS2030S")
56
+ print(module.title)
57
+ print(module.description)
58
+ ```
59
+
60
+ If no academic year is supplied, the client uses the current calendar year and
61
+ the following year. For example, a client created in 2026 uses `2026-2027`.
62
+
63
+ ## Client configuration
64
+
65
+ ```python
66
+ from nusmods_api import NUSModsClient
67
+
68
+ client = NUSModsClient(
69
+ academic_year="2026-2027",
70
+ timeout=20.0,
71
+ cache_directory=".nusmods_cache",
72
+ cache_expiration=86_400,
73
+ )
74
+ ```
75
+
76
+ | Argument | Default | Description |
77
+ | --- | --- | --- |
78
+ | `academic_year` | Current year and next year | Academic year in `YYYY-YYYY` format |
79
+ | `timeout` | `10.0` | HTTP timeout in seconds |
80
+ | `cache_directory` | `.nusmods_cache` | Directory for cached JSON responses |
81
+ | `cache_expiration` | `86400` | Cache lifetime in seconds; use `None` for no expiration |
82
+
83
+ Set `cache_directory=None` to disable caching:
84
+
85
+ ```python
86
+ client = NUSModsClient(cache_directory=None)
87
+ ```
88
+
89
+ ## API usage
90
+
91
+ ### Module list
92
+
93
+ ```python
94
+ modules = client.get_module_list()
95
+
96
+ for module in modules:
97
+ print(module.moduleCode, module.title, module.semesters)
98
+ ```
99
+
100
+ ### Detailed module information
101
+
102
+ ```python
103
+ modules = client.get_module_information()
104
+ module = client.get_module(" cs2030s ")
105
+
106
+ print(module.moduleCode) # CS2030S
107
+ print(module.acadYear)
108
+ print(module.semesterData)
109
+ ```
110
+
111
+ Module codes are stripped and normalized to uppercase by `get_module`.
112
+
113
+ ### Venues
114
+
115
+ ```python
116
+ venues = client.get_venues(semester=1)
117
+ print(venues)
118
+
119
+ venue_information = client.get_venue_information(semester=1)
120
+ for venue, entries in venue_information.items():
121
+ print(venue, entries)
122
+ ```
123
+
124
+ `semester` must be `1` or `2`.
125
+
126
+ ### Shared timetables
127
+
128
+ Pass a NUSMods share URL to `get_shared_timetable`:
129
+
130
+ ```python
131
+ share_url = (
132
+ "https://nusmods.com/timetable/sem-1/share?"
133
+ "CS1231S=TUT:25B,LEC:1&"
134
+ "CS2030S=REC:04,LAB:10B,LEC:1&"
135
+ "GES1034=LEC:1,TUT:E2&"
136
+ "IS1108=TUT:14,LEC:2&"
137
+ "MA1522=LEC:1"
138
+ )
139
+
140
+ timetable = client.get_shared_timetable(share_url)
141
+
142
+ for lesson in timetable.weekly:
143
+ print(
144
+ lesson.moduleCode,
145
+ lesson.lessonType,
146
+ lesson.classNo,
147
+ lesson.day,
148
+ lesson.startTime,
149
+ lesson.endTime,
150
+ lesson.venue,
151
+ )
152
+
153
+ for exam in timetable.exams:
154
+ print(exam.moduleCode, exam.examDate, exam.examDuration)
155
+ ```
156
+
157
+ The client parses the module selections from the URL and retrieves the
158
+ corresponding module data from the NUSMods API. Lesson type codes such as
159
+ `LEC`, `TUT`, `LAB`, and `REC` are mapped to their full names.
160
+
161
+ `Timetable` also provides convenience methods:
162
+
163
+ ```python
164
+ monday = timetable.get_day_schedule("Monday")
165
+ exam = timetable.get_exam_for_module("CS2030S")
166
+ ```
167
+
168
+ ## Data models
169
+
170
+ The package exposes frozen dataclasses for common API response types:
171
+
172
+ - `ModuleCondensed` — module code, title, and offered semesters
173
+ - `Module` — detailed module information
174
+ - `Lesson` — class schedule information
175
+ - `LessonWithModule` — a lesson including its module code
176
+ - `VenueLesson` and `VenueInformation` — venue timetable data
177
+ - `WeekRange` — date-based lesson ranges
178
+ - `Workload` — workload components
179
+ - `PrerequisiteTree` — nested prerequisite expressions
180
+ - `ExamTimetable` — exam information
181
+ - `Timetable` — selected weekly lessons and exams
182
+
183
+ Some model fields retain the NUSMods API naming convention, such as
184
+ `moduleCode`, `classNo`, `startTime`, `lessonType`, and `examDate`.
185
+
186
+ ## Error handling
187
+
188
+ The client raises package-specific exceptions:
189
+
190
+ ```python
191
+ from nusmods_api import (
192
+ NUSModsHTTPError,
193
+ NUSModsInvalidResponseError,
194
+ NUSModsNotFoundError,
195
+ NUSModsRequestError,
196
+ )
197
+
198
+ try:
199
+ module = client.get_module("CS2030S")
200
+ except NUSModsNotFoundError:
201
+ print("The module or academic year was not found")
202
+ except NUSModsHTTPError as error:
203
+ print(error.status_code, error.url)
204
+ except NUSModsInvalidResponseError:
205
+ print("The API returned invalid JSON")
206
+ except NUSModsRequestError:
207
+ print("The API could not be reached")
208
+ ```
209
+
210
+ Invalid client arguments, malformed share URLs, invalid semesters, and
211
+ unmatched lesson selections raise `ValueError`.
212
+
213
+ ## Testing
214
+
215
+ Run the default unit test suite:
216
+
217
+ ```powershell
218
+ uv run pytest
219
+ ```
220
+
221
+ The live integration test makes real requests to the NUSMods API:
222
+
223
+ ```powershell
224
+ uv run pytest tests/test_live_client.py -m live -q
225
+ ```
226
+
227
+ The live test requires internet access and may need its expected timetable
228
+ dates updated when NUSMods publishes a new academic-year dataset.
229
+
230
+ ## License
231
+
232
232
  No license has been specified yet.