Skip to content

Commit c4ebd0b

Browse files
committed
docs(calendar): explain advanced search flow
Assisted-by: Codex:gpt-5.6-terra Signed-off-by: Dick Tump <dick@tump.me>
1 parent 7355638 commit c4ebd0b

2 files changed

Lines changed: 14 additions & 0 deletions

File tree

ex_app/lib/all_tools/calendar_advanced_search.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ async def search_calendar_events(
7676
limit,
7777
)
7878
except ValueError as exception:
79+
# Model-generated tool arguments are reported as a structured result, not a tool exception.
7980
return _input_validation_result(exception)
8081
return await _search_calendar_events(
8182
nc,
@@ -96,6 +97,7 @@ async def _search_calendar_events(
9697
term_groups: list[list[str]],
9798
result_limit: int,
9899
) -> dict:
100+
"""Discover, select, query and aggregate calendars while preserving partial failures."""
99101
failures = []
100102
try:
101103
calendars, failed_discovery_responses = await _list_event_calendars(nc)
@@ -142,6 +144,7 @@ async def _search_calendar_events(
142144
event.pop("_uid", None)
143145
event.pop("_calendar_href", None)
144146
result_truncated = len(sorted_events) > result_limit
147+
# Discovery, selection, query or processing limits make absence unreliable.
145148
truncated = resource_truncated or result_truncated
146149
complete = not failures and not truncated
147150
result = {
@@ -184,6 +187,7 @@ async def _search_selected_calendars(
184187
events = []
185188
failures = []
186189
resource_truncated = False
190+
# Bound the full request and processing lifetime to cap concurrent DAV work and parsed response data.
187191
semaphore = asyncio.Semaphore(MAX_CONCURRENT_CALENDAR_QUERIES)
188192
query_body = calendar_query_body(bounds)
189193
calendar_results = await asyncio.gather(
@@ -217,6 +221,7 @@ async def _search_calendar(
217221
async with semaphore:
218222
try:
219223
xml_text = await _calendar_report(nc, calendar, query_body)
224+
# XML parsing and recurrence expansion are synchronous and may be expensive.
220225
return await asyncio.to_thread(
221226
_process_calendar_response,
222227
xml_text,
@@ -236,6 +241,7 @@ def _process_calendar_response(
236241
bounds: SearchBounds,
237242
term_groups: list[list[str]],
238243
) -> tuple[list[dict], list[dict], bool]:
244+
"""Process one calendar response without letting a bad resource discard its other events."""
239245
resources, failed_resources, resource_truncated = parse_calendar_data(xml_text)
240246
failures = []
241247
if failed_resources:
@@ -265,6 +271,7 @@ def _process_calendar_response(
265271
event["_calendar_href"] = calendar.href
266272
events.extend(resource_events)
267273
except Exception:
274+
# One malformed or unsupported resource must not make the calendar's successful matches disappear.
268275
parse_failures += 1
269276
if parse_failures:
270277
failures.append(
@@ -287,6 +294,7 @@ async def is_available(nc: AsyncNextcloudApp):
287294

288295

289296
async def _list_event_calendars(nc: AsyncNextcloudApp) -> tuple[list[CalendarCollection], int]:
297+
"""Follow CalDAV principal discovery to the current user's event calendars."""
290298
principal_response = await nc._session.adapter_dav.request(
291299
"PROPFIND",
292300
"/",
@@ -336,6 +344,7 @@ def _dav_headers(depth: str) -> dict[str, str]:
336344

337345

338346
def _same_origin_dav_path(nc: AsyncNextcloudApp, href: str) -> str:
347+
# WebCal subscriptions are read from Nextcloud's cached DAV collection, never from their external URL.
339348
target = urlsplit(href)
340349
endpoint = urlsplit(nc._session.cfg.endpoint)
341350
if target.scheme and (target.scheme, target.netloc) != (endpoint.scheme, endpoint.netloc):

ex_app/lib/all_tools/lib/calendar_search.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ def validate_search(
5959
text_term_groups: list[list[str]] | None,
6060
limit: int,
6161
) -> tuple[SearchBounds, list[str] | None, list[list[str]], int]:
62+
"""Validate and bound model-supplied arguments before any CalDAV request is made."""
6263
start = _parse_bound(range_start, "range_start")
6364
end = _parse_bound(range_end, "range_end")
6465
if start >= end:
@@ -111,6 +112,7 @@ def _validate_text_term_group(group: list[str]) -> list[str]:
111112

112113

113114
def parse_calendar_collections(xml_text: str) -> tuple[list[CalendarCollection], int]:
115+
"""Keep event-capable calendar collections, ignoring those explicitly limited to non-VEVENT components."""
114116
_check_xml_size(xml_text)
115117
root = _parse_xml(xml_text)
116118
calendars = []
@@ -154,6 +156,7 @@ def parse_calendar_home(xml_text: str) -> str:
154156

155157

156158
def parse_calendar_data(xml_text: str) -> tuple[list[str], int, bool]:
159+
"""Extract bounded iCalendar resources and retain whether the server response was only partly processed."""
157160
_check_xml_size(xml_text)
158161
root = _parse_xml(xml_text)
159162
resources = []
@@ -190,6 +193,7 @@ def expand_and_filter_events(
190193
bounds: SearchBounds,
191194
text_term_groups: list[list[str]],
192195
) -> list[dict[str, Any]]:
196+
"""Expand one resource's recurrences, then apply local text filtering to its occurrences."""
193197
calendar = Calendar.from_ical(icalendar_text)
194198
_validate_expansion_limits(calendar, bounds)
195199
recurrence_by_uid = _recurrence_metadata(calendar)
@@ -203,6 +207,7 @@ def expand_and_filter_events(
203207

204208

205209
def _validate_expansion_limits(calendar: Calendar, bounds: SearchBounds) -> None:
210+
"""Limit recurrence work before expansion, rather than only limiting returned search matches."""
206211
estimated_occurrences = 0
207212
for component in calendar.walk("VEVENT"):
208213
rrule = component.get("RRULE")

0 commit comments

Comments
 (0)