nusmods-api 0.1.0__tar.gz → 0.3.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.
- {nusmods_api-0.1.0 → nusmods_api-0.3.0}/PKG-INFO +222 -222
- {nusmods_api-0.1.0 → nusmods_api-0.3.0}/README.md +221 -221
- {nusmods_api-0.1.0 → nusmods_api-0.3.0}/pyproject.toml +1 -1
- {nusmods_api-0.1.0 → nusmods_api-0.3.0}/pyproject.toml.orig +1 -1
- {nusmods_api-0.1.0 → nusmods_api-0.3.0}/src/nusmods_api/__init__.py +6 -0
- {nusmods_api-0.1.0 → nusmods_api-0.3.0}/src/nusmods_api/cache.py +64 -64
- {nusmods_api-0.1.0 → nusmods_api-0.3.0}/src/nusmods_api/client.py +393 -393
- {nusmods_api-0.1.0 → nusmods_api-0.3.0}/src/nusmods_api/exceptions.py +40 -40
- {nusmods_api-0.1.0 → nusmods_api-0.3.0}/src/nusmods_api/models.py +180 -180
- {nusmods_api-0.1.0 → nusmods_api-0.3.0}/src/nusmods_api/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: nusmods-api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.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.
|