Command-line utility that fetches Heriot-Watt University timetable data and
produces standards-compliant iCalendar (.ics) files that can be imported into Apple Calendar, Google Calendar, or any other CalDAV-capable
consumer.
- Generates compact weekly-recurring events with proper EXDATE handling.
- Emits
X-WR-*metadata and CRLF line endings for maximum client compatibility (tested with Nextcloud and Alpine-based static hosting). - Supports filtering by course, activity type, semester window, and optional blocked-period inclusion.
- Offers preview mode to quickly inspect the next few sessions in the terminal.
- Includes an offline workflow that reuses saved JSON payloads so credentials are only needed once.
- Python 3.11+.
- Dependencies listed in
requirements.txt:requestsfor HTTPS API calls and optional token downloads.pydanticfor strict data validation.tzdatato supply Olson timezone definitions.
- uv—install via
curl -LsSf https://astral.sh/uv/install.sh | sh - (Optional)
pytest,ruff, andblackfor development—install viauv pip install -e '.[dev]'if desired.
git clone https://github.com/thomasbonr/hw_timetable.git
cd hw_timetable
uv venv --python 3.11 && source .venv/bin/activate # recommended
uv pip install -r requirements.txtRunning the CLI requires an Authorization: Bearer <token> header. You can
provide the token via the --token flag, the HW_TIMETABLE_ACCESS_TOKEN
environment variable, a .env file, or by reusing the cached value stored in
~/.cache/hw_timetable/token.txt (written automatically each time a token is
supplied).
- Sign in to the official HW timetable dashboard as usual (https://timetableexplorer.hw.ac.uk/timetable-dashboard).
- Open your browser developer tools and switch to the "Network" tab.
- Ensure the persistant logs are enabled, and refresh the page (look for
requests going to
https://timetableexplorer-api.hw.ac.uk/...). - Click the API request, locate the Request Headers section, and copy the
full value of the
Authorizationheader—it should look likeBearer eyJ0eXAiOiJKV1QiLCJhbGciOi.... - Paste that value into a safe place,
.envfile, etc.) so the CLI can reuse it. You may keep the leadingBearerprefix or remove it; Tokens expire after a ~1 hours, so repeat the steps whenever the API rejects a call.
# Export the token for the current shell session (Bash/macOS/Linux/WSL)
export HW_TIMETABLE_ACCESS_TOKEN="Bearer eyJ0eXAiOiJKV1QiLCJhbGciOi..."
# Download
python3 -m hw_timetable.cliWindows users can set the environment variable with either PowerShell or Command Prompt before running the CLI:
# PowerShell
$env:HW_TIMETABLE_ACCESS_TOKEN = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOi..."
python3 -m hw_timetable.cliREM Command Prompt (cmd.exe)
set HW_TIMETABLE_ACCESS_TOKEN=Bearer eyJ0eXAiOiJKV1QiLCJhbGciOi...
python3 -m hw_timetable.cliDon't forget to activate the virtual environment
source .venv/bin/activateRun the client
python3 -m hw_timetable.cliBy default, the tool writes a single .ics file into out/ics/ using a file
name derived from your programme metadata.
| Option | Description |
|---|---|
--tz Europe/London |
Override the timezone used for DTSTART/DTEND output. |
--include-blocked / --exclude-blocked |
Toggle inclusion of blocked periods (default: excluded). |
--start YYYY-MM-DD / --end YYYY-MM-DD |
Restrict events to a date range (inclusive). |
--filter-course CODE |
Append one or more course codes to include (repeat flag). |
--filter-type LEC,LAB |
Include only activities whose types match the comma-separated list. |
--only-current-semester |
Automatically detect the current semester window and drop the rest. |
--dump-json |
Save raw API responses under out/json/ for auditing/offline use. |
--offline |
Read previously dumped JSON fixtures instead of calling the API. |
--preview |
Print the next 10 upcoming sessions to stdout after writing the ICS. |
--verbose |
Enable debug logging for HTTP retries and filtering decisions. |
--token |
Inline bearer token for protected API calls (overrides env/cache). |
All options can be combined. For example, to fetch Semester 2 labs only, dump the raw payloads, and preview the next lectures:
python3 -m hw_timetable.cli \
--filter-type Lab \
--only-current-semester \
--include-blocked \
--dump-json \
--preview- Run with valid credentials once while passing
--dump-jsonto cache every endpoint:python3 -m hw_timetable.cli --dump-json. - Subsequent executions can use
--offline(optionally together with--dump-jsonto refresh the cache) and therefore run without any network or authentication requirements:python3 -m hw_timetable.cli --offline --preview.
Format, lint, and test before opening a PR:
python3 -m pip install -e .[dev]
ruff check
black .
python3 -m pytestThe test suite exercises the ICS builder, timezone handling, and offline CLI paths, so it is a good way to verify compatibility after making changes.