usoscal
USOS timetable as a calendar feed that keeps the past.
The iCalendar export built into USOS only contains what is ahead of today, so classes vanish from the calendar as soon as they pass. usoscal signs a student in through USOS, gives them a private .ics address and, when they ask for it, keeps every class it has ever seen.
make env # .env and docker-compose.override.yml from the examples
make recreate # the services in COMPOSE_PROFILES
make fmt and make check format, lint and test; without a local Go they run in the golang image.
Configuration
Everything comes from .env:
| Variable | Meaning |
|---|---|
PUBLIC_URL |
Address the service is opened at, used for the OAuth callback and for calendar addresses |
USOS_CONSUMER_KEY, USOS_CONSUMER_SECRET |
API key of the USOS installation, issued at <usos>/developers/ |
TOKEN_ENC_KEY |
32 random bytes in base64 (openssl rand -base64 32), encrypts stored tokens |
USOS_BASE_URL |
USOS API installation, https://usosapps.put.poznan.pl by default |
SYNC_INTERVAL |
How often each timetable is refreshed, 24h by default |
STORAGE_ROOT |
Host directory of the SQLite file, ./storage by default |
Changing TOKEN_ENC_KEY makes every stored token unreadable: people sign in again and get a new calendar address.
What a user gets
One page. Signed out, it explains the service and has a single button. Signed in, it shows:
- the calendar address, with a button to issue a new one and a button to revoke it, both of which stop the current address at once;
- the archive checkbox: on, past classes stay and earlier terms are fetched once; off, the stored past is deleted;
- when the timetable was last refreshed;
- sign-out, which revokes the token in USOS, deletes the stored tokens, stops the calendar address and, if asked, deletes the timetable.
An event carries the course name with the class type as its title, the room with the building and its postal address as the location, and the group, the lecturers and the USOS link in the description.
How it works
One Go binary and one SQLite file. internal/usos is the OAuth 1.0a client, internal/syncer refreshes timetables, internal/ical writes the feed, internal/server is the page and the feed endpoint, internal/store is the database, internal/seal is the encryption.
The feed is served from the database, never by calling USOS on a calendar request. Once per SYNC_INTERVAL the syncer asks USOS for every week from today to the end of the running semester and replaces those weeks with what it got, so a class that was moved or cancelled changes in the feed too. Days that have passed are never asked for again: with the archive on they stay as they were last seen, with it off they are deleted. Turning the archive on walks backwards week by week until USOS has returned nothing for half a year.
What USOS allows and where it misbehaves is written down in docs/usos-api.md, what is stored and how it is protected in docs/security.md.