* Fix YEARLY recurring CalDAV events only showing on DTSTART year (#170) Recurring events with RRULE:FREQ=YEARLY only appeared in the calendar on the year matching DTSTART, not in subsequent years. The list_events query filtered by , which excludes recurring events whose original dtend (e.g. 2019-07-22) falls before the requested window (e.g. 2026). Fix: split the query into two branches — non-recurring events still require window overlap, but recurring events (with non-empty RRULE) are fetched by dtstart < end_dt alone. A new helper, _expand_rrule_occurrences(), uses dateutil.rrule to expand each recurring event into individual occurrence dicts within the requested date range, so YEARLY/WEEKLY/MONTHLY events render correctly across all years. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * recurrence: compound UIDs, frontend fixes, python-dateutil req, tests - Replace _expand_rrule_occurrences with _expand_rrule that emits stable compound UIDs ({base_uid}::{date_or_datetime}) so the frontend can distinguish occurrences from the same series. Non-recurring events pass through with is_recurrence=false and series_uid=uid. - Add _resolve_base_uid() to extract the base series UID from compound UIDs — used by PUT/DELETE /api/calendar/events/{uid} and the manage_calendar tool so edits/deletes always target the base row. - Update manage_calendar tool to import and use _resolve_base_uid. - Frontend _updateEvent / _deleteEvent: detect compound UIDs and invalidate localStorage cache after success so stale sibling occurrences aren't shown. - Add python-dateutil to requirements.txt as an explicit dependency. - Add 14 regression tests in tests/test_calendar_recurrence.py covering _resolve_base_uid edge cases, _expand_rrule with yearly/weekly/monthly/all-day/bad-rrule, unique UIDs, and metadata inheritance. - Merge upstream's cleaner SQLAlchemy or_/and_ query pattern. * recurrence: overlapping malformed-RRULE, exclusive end, multi-day crossings Fix three edge cases in _expand_rrule: 1. Malformed-RRULE fallback now checks window overlap. list_events fetches recurring rows with only dtstart < end_dt, so a broken old recurring event could appear in unrelated future windows. Now fallback returns [] unless the base event's dtstart/dtend actually intersect [start, end). 2. Exclusive end boundary. rule.between(start, end, inc=True) was inclusive on end, but the route contract and non-recurring SQL filter both use [start, end). Added occ_start >= end guard. 3. Multi-day crossings. A recurring occurrence that starts before the window but ends inside it was missed (only occ_start was checked). Now expands from start - duration and filters by occ_start < end AND occ_end > start, matching non-recurring overlap behavior. Tests: +4 tests for these cases (18 total) --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
42 lines
1.2 KiB
Plaintext
42 lines
1.2 KiB
Plaintext
fastapi
|
|
uvicorn
|
|
python-multipart
|
|
python-dotenv
|
|
httpx
|
|
pydantic
|
|
pydantic-settings
|
|
SQLAlchemy
|
|
pypdf
|
|
beautifulsoup4
|
|
charset-normalizer
|
|
numpy
|
|
# Vector store + local embeddings for RAG, semantic memory, and tool
|
|
# selection. Used on core agent paths, so installed by default — the app
|
|
# still degrades to keyword fallback if they're ever missing.
|
|
# chromadb-client is the lightweight HTTP client (talks to a standalone
|
|
# ChromaDB service); fastembed runs local ONNX embeddings.
|
|
chromadb-client
|
|
fastembed
|
|
youtube-transcript-api
|
|
# Markdown rendering for research reports (src/visual_report.py).
|
|
# Imported at module-top so it's a hard core dep, not optional.
|
|
markdown
|
|
# Calendar .ics import/export (routes/calendar_routes.py).
|
|
icalendar
|
|
# Recurrence rule expansion for calendar events (routes/calendar_routes.py).
|
|
# Imported directly as dateutil.rrule — make it explicit even though caldav
|
|
# pulls it in transitively.
|
|
python-dateutil
|
|
# CalDAV sync (src/caldav_sync.py). Handles PROPFIND discovery + REPORT
|
|
# fetch across Radicale, Nextcloud, Apple, Fastmail; we'd be reinventing
|
|
# the protocol without it.
|
|
caldav
|
|
cryptography
|
|
bcrypt
|
|
mcp
|
|
pyotp
|
|
qrcode[pil]
|
|
croniter
|
|
pytest
|
|
pytest-asyncio
|