Import personal events from .ics files and subscriptions (#51) #121
No reviewers
Labels
No labels
area/ai
area/backend
area/frontend
area/infra
area/scheduler
area/wled
good-first-issue
priority/high
priority/low
priority/medium
type/bug
type/chore
type/ci-cd
type/docs
type/feature
type/qa
v1.0.0
v1.1.0
v1.2.0
v2.0.0
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
rbrooks/Iris-WLED!121
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/51-ical-import"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #51.
The other direction from #16: upload a
.icsor subscribe to a URL, and birthdays and anniversaries become events that drive the lights. Subscriptions refresh as part of the weekly candidate job, so an event added to a personal calendar this week gets a candidate scheme on the same run rather than waiting for the next one.De-dup needs no new column
Event ids are already
{event_key}_{year}, so derivingevent_keydeterministically from the VEVENTUIDmakes a re-import collide on the primary key by construction. That's exactly the "de-dup on re-import/refresh by UID" the issue asks for, with no UID column and no second uniqueness rule to keep in step with the first.UIDs are hashed rather than sanitised — real ones from Google and Apple (
040000008200E00074C5B7101A82E008/...@corp) contain characters that aren't id-safe and carry no meaning worth preserving. All that matters is that the same UID always maps to the same key.One load-bearing detail worth your eye
priority_resolver._SOURCE_RANKmaps a source to a tiebreak rank and defaults anything unrecognised to0— belowsystem. Adding the new"ical"source without registering it there would have made every imported event lose a priority tie against a builtin, silently, with nothing to show why.That's the same shape as the
leds.segsbug from #62: a lookup with a quiet default that turns a wrong answer into a plausible-looking one. Registered at the same rank asuser, and I confirmed two tests fail if the entry is removed rather than assuming they would.Upload and subscription behave differently, on purpose
icalevent. A hash collision, or a hand-made event that happens to share an id, is left alone.Recurrence
Yearly RRULEs expand per year, honouring
UNTIL,COUNTandINTERVAL. Two decisions:DTENDis read as exclusive — the same off-by-one the exporter has to avoid in the other direction, here meaning every imported event would otherwise be a day too long.Robustness
A failing feed is recorded against its subscription row rather than raised, so one dead calendar can't stop the weekly job for the rest, and the error shows in the UI instead of only in the container log. The manual sync endpoint still surfaces it as a 502. Fetches are bounded (20s, 5MB), and
webcal://is rewritten tohttps://since that's what Apple and Google hand out.Dependency change
icalendarmoves from dev-only to runtime. #16 needed it only to validate the exported feed in tests; parsing an upload needs it in production. Flagged in the #16 close comment so it wouldn't be a surprise here.Verification
ruff,mypyclean0009applies on a fresh DB andalembic checkreports no driftnode:22:tsc -b,eslint,vitest(76),npm run buildall cleanuvx pre-commit run --all-filespasses🤖 Generated with Claude Code