No description
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-11 13:42:40 +02:00
tests feat: Change the way the app works - works like a one-time CLI 2026-10-11 13:42:40 +02:00
usos_calendar feat: Change the way the app works - works like a one-time CLI 2026-10-11 13:42:40 +02:00
.env.example feat: Change the way the app works - works like a one-time CLI 2026-10-11 13:42:40 +02:00
.gitignore init 2026-09-25 15:43:12 +02:00
.python-version init 2026-09-25 15:43:12 +02:00
pyproject.toml feat: Change the way the app works - works like a one-time CLI 2026-10-11 13:42:40 +02:00
README.md feat: Change the way the app works - works like a one-time CLI 2026-10-11 13:42:40 +02:00
uv.lock feat: Change the way the app works - works like a one-time CLI 2026-10-11 13:42:40 +02:00

USOS Calendar PRz — one-time CLI importer

The application fetches your USOS PRz timetable for a specified date range and adds events to an existing Nextcloud calendar through CalDAV. It exits after the import. It does not run a server, publish a calendar, or synchronize subsequent changes.

1. Installation and configuration

Requirements: Python 3.12+ and uv.

Run all commands from the project directory. If you already have an .env file, keep it and update the required values.

uv sync --locked
cp -n .env.example .env
chmod 600 .env

Register an application on the USOS PRz developer site and add its USOS_CONSUMER_KEY and USOS_CONSUMER_SECRET to .env.

Configure the destination calendar:

CALDAV_URL=https://cloud.example.com/remote.php/dav/calendars/username/calendar-name/
CALDAV_USERNAME=username
CALDAV_PASSWORD=
CALDEV_IGNORE_SSL=false
TIMEZONE=Europe/Warsaw

CALDAV_URL is the private address of a specific CalDAV calendar, not the web interface, a public ICS subscription, or the main /remote.php/dav/ endpoint. Copy the CalDAV link from the selected calendar's settings or menu in Nextcloud. The calendar must already exist and allow write access.

Create a Nextcloud app password in the security settings. If CALDAV_PASSWORD is empty, the importer prompts for it without echoing your input. You can also store it in .env. Passwords are not accepted as command-line arguments. Do not share .env; Git ignores this file. Environment variables take precedence over .env values.

For a trusted local reverse proxy with a self-signed certificate, add this setting to .env:

CALDEV_IGNORE_SSL=true

This disables certificate and hostname verification for destination CalDAV requests. HTTPS encryption remains enabled, but the server's identity is no longer checked. The default is false, which keeps certificate verification enabled. USOS connections continue to verify certificates. Repeat your import command after changing the setting.

2. USOS authorization

uv run python -m usos_calendar auth

Open the displayed URL, sign in to USOS, approve access, and enter the PIN you receive. The access token and secret are saved to .env with 0600 permissions. Skip this step if you already have valid USOS_ACCESS_TOKEN and USOS_ACCESS_TOKEN_SECRET values. Authorization requests the studies|offline_access scopes. If access is revoked, authorize again.

The existing command uv run python -m usos_calendar.auth also works.

3. Preview and import

First, check connectivity and event counts without writing anything:

uv run python -m usos_calendar import --start 2026-10-01 --end 2027-02-28 --dry-run

Import the events:

uv run python -m usos_calendar import --start 2026-10-01 --end 2027-02-28

Both dates are inclusive. The importer selects events starting within this range in the configured USOS time zone. The summary reports how many events were added and how many existing events were skipped.

Override the destination URL, username, or time zone for a single run:

uv run python -m usos_calendar import --start 2026-10-01 --end 2027-02-28 \
  --calendar-url https://cloud.example.com/remote.php/dav/calendars/username/studies/ \
  --username username --timezone Europe/Warsaw

Show command-line help:

uv run python -m usos_calendar --help
uv run python -m usos_calendar import --help

Import options

Option Description
--start YYYY-MM-DD, --end YYYY-MM-DD Required date range; both days are included.
--calendar-url URL Existing calendar URL. Overrides CALDAV_URL from .env.
--username LOGIN Nextcloud username. Overrides CALDAV_USERNAME from .env.
--timezone ZONE USOS time zone. Overrides TIMEZONE; defaults to Europe/Warsaw.
--dry-run Fetches data and reports how many events would be added, without writing them.

The password comes from CALDAV_PASSWORD or an interactive terminal prompt. USOS credentials (USOS_CONSUMER_KEY, USOS_CONSUMER_SECRET, USOS_ACCESS_TOKEN, USOS_ACCESS_TOKEN_SECRET) come from environment variables or .env.

Import behavior

  • The entire USOS date range is fetched and validated before writing to Nextcloud. A source retrieval failure does not cause a partial import. Each USOS request covers at most seven days.
  • Existing event UIDs are checked throughout the destination calendar. A previously imported event is skipped even after you edit its title, location, or time in Nextcloud. Writes use If-None-Match: * to prevent overwriting existing resources.
  • Existing events are not updated or deleted. An event deleted in Nextcloud may be added again on a later import. A changed start time in USOS changes the UID, so importing again may add a new event alongside the previous one.
  • Events are written to Nextcloud individually. If a write fails, earlier writes remain; the CLI reports confirmed counts and exits with code 1. Repeat the same command to finish the import without duplicating existing events. Ctrl+C exits with code 130, invalid arguments with code 2, and success with code 0.
  • --dry-run reads USOS and the destination calendar without writing anything. Its summary shows the number of new and existing events.

USOS times are interpreted in TIMEZONE (default: Europe/Warsaw) and sent in UTC, preserving daylight saving time offsets. Nextcloud displays them in the user's configured time zone. Calendar content uses Polish: course names, class types, and locations prefer Polish source labels, with English as a fallback. Titles use the original Polish class-type abbreviations and room number; descriptions use Polish field labels. CLI messages, errors, documentation, and code comments remain in English.

USOS may return every possible meeting date for activities with a frequency of other or once. The importer preserves the returned dates within the selected range without guessing which meetings will actually take place. An empty timetable produces a summary with zero new events.

Migrating from the previous version

Add CALDAV_URL and your Nextcloud username and app password to .env. The old CALDAV_USERNAME and CALDAV_PASSWORD were credentials for the application's own server, not Nextcloud. PAST_DAYS, FUTURE_DAYS, CACHE_TTL_SECONDS, CALDAV_DATA_PATH, and CALENDAR_TOKEN are no longer used.

The HTTP server, local CalDAV service, and event override database have been replaced by the importer run through uv. Existing .local/calendar.sqlite3 files remain on disk, but the importer does not read them; manual changes from the old server are not migrated. The old /caldav/ and /calendar.ics endpoints are no longer supported.

Tests and code layout

uv run pytest -q

Tests cover event generation, date ranges, UTC/DST, the CalDAV protocol, duplicate handling, previews, failures, and the CLI. They do not require USOS or Nextcloud accounts. A real import requires your credentials and access to the destination server.

cli.py handles commands, auth.py handles OAuth authorization, usos/client.py fetches the timetable, calendar/feed.py builds events, and calendar/importer.py writes them to CalDAV.

Documentation: USOS timetable, OAuth, Nextcloud Calendar, CalDAV RFC 4791.