odysseus/src
AzaelMew 6d3e9c299a Fix YEARLY recurring CalDAV events only showing on DTSTART year (#179)
* 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>
2026-06-01 13:42:44 +09:00
..
search Odysseus v1.0 2026-05-31 23:58:26 +09:00
agent_loop.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
agent_runs.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
agent_tools.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
ai_interaction.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
api_key_manager.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
app_helpers.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
app_initializer.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
assistant_log.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
auth_helpers.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
bg_jobs.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
bg_monitor.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
builtin_actions.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
builtin_mcp.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
caldav_sync.py Fix duplicate CalDAV sync UIDs 2026-06-01 02:17:43 +00:00
chat_handler.py Recognize local vision models so their images aren't dropped (#185) 2026-06-01 13:09:21 +09:00
chat_helpers.py Recognize local vision models so their images aren't dropped (#185) 2026-06-01 13:09:21 +09:00
chat_processor.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
chroma_client.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
cleanup_service.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
config.py Align SearXNG fallback URL 2026-06-01 10:50:07 +09:00
constants.py Align SearXNG fallback URL 2026-06-01 10:50:07 +09:00
context_compactor.py Preserve large pasted messages in context 2026-06-01 12:38:35 +09:00
database.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
deep_research.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
document_actions.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
document_processor.py Fix vision attachment timeout and stale cache 2026-06-01 02:04:46 +00:00
email_thread_parser.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
embeddings.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
endpoint_resolver.py Improve OpenRouter and Groq provider requests 2026-06-01 10:32:14 +09:00
event_bus.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
exceptions.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
goal_based_extractor.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
integrations.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
llm_core.py Improve OpenRouter and Groq provider requests 2026-06-01 10:32:14 +09:00
mcp_manager.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
memory.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
memory_vector.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
model_context.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
model_discovery.py Improve Ollama setup and model endpoint handling 2026-06-01 10:00:15 +09:00
pdf_form_doc.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
pdf_forms.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
personal_docs.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
preset_manager.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
prompt_security.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
rag_manager.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
rag_singleton.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
rag_vector.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
rate_limiter.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
request_models.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
research_handler.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
research_utils.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
secret_storage.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
session_actions.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
settings.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
task_endpoint.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
task_scheduler.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
teacher_escalation.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
text_helpers.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
tool_execution.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
tool_implementations.py Fix YEARLY recurring CalDAV events only showing on DTSTART year (#179) 2026-06-01 13:42:44 +09:00
tool_index.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
tool_parsing.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
tool_schemas.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
tool_security.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
topic_analyzer.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
upload_handler.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
visual_report.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
webhook_manager.py Odysseus v1.0 2026-05-31 23:58:26 +09:00
youtube_handler.py Odysseus v1.0 2026-05-31 23:58:26 +09:00