Add weather to calendar mode; fix CalDAV events never showing
Build and push server image / build-and-push (push) Successful in 48s
Build and push server image / build-and-push (push) Successful in 48s
Weather: multiple cities per frame, geocoded via Open-Meteo (no API key), shown above the event list on agenda/today & tomorrow/week views -- never month, no room for it there. Hand-drawn sun/cloud/rain/snow/ thunderstorm icons (no new font/icon asset, same primitives-only approach the rest of calendar_render.py already uses). City geocoding handles "City, State" qualifiers Open-Meteo's own search doesn't (disambiguates same-named cities, e.g. the three "Portland"s). CalDAV fix: events were never showing despite calendars discovering fine, because fetch_calendar_events relied on the calendar-query REPORT's server-side time-range filter, which real servers implement inconsistently (confirmed against a real server, not just guessed -- reproduced locally with Radicale). Switched to fetching every event unfiltered and doing all date-window filtering/expansion client-side, same approach already used for plain ICS feeds.
This commit is contained in:
+23
-12
@@ -24,12 +24,15 @@ calendar and an ICS subscription behave identically once fetched.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date, datetime, time as dtime
|
||||
import logging
|
||||
from datetime import date, datetime
|
||||
|
||||
import caldav
|
||||
import icalendar
|
||||
import recurring_ical_events
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
HTTP_TIMEOUT_S = 15
|
||||
|
||||
|
||||
@@ -68,19 +71,26 @@ def fetch_calendar_events(calendar_url: str, username: str, password: str,
|
||||
window_start: date, window_end: date) -> list[dict]:
|
||||
"""One CalDAV calendar's events in [window_start, window_end] -- same
|
||||
event dict shape as calendar_feed.fetch_source_events (no
|
||||
"owner_display_name"; the caller adds that). Fetches raw (unexpanded)
|
||||
calendar objects and runs them through the same icalendar +
|
||||
recurring_ical_events pipeline calendar_feed.py uses for ICS feeds,
|
||||
rather than relying on server-side expand (RFC 4791 leaves plenty of
|
||||
corner cases server implementations disagree on)."""
|
||||
"owner_display_name"; the caller adds that).
|
||||
|
||||
Deliberately does NOT use the calendar-query REPORT's server-side
|
||||
time-range filter (caldav.Calendar.date_search) -- RFC 4791 leaves
|
||||
that corner case underspecified and real servers disagree on it
|
||||
(the caldav package's own docs warn "servers often behave
|
||||
differently when presented with a search request"; confirmed here
|
||||
too, once against a real server, as a calendar whose events just
|
||||
silently never came back despite discovery/auth both working
|
||||
fine). Instead this fetches every event in the calendar unfiltered
|
||||
(get_events() is a plain "list VEVENTs" REPORT with no time-range
|
||||
element -- the much more universally-supported case) and does 100%
|
||||
of the date-window filtering/recurrence-expansion client-side via
|
||||
icalendar + recurring_ical_events, exactly like calendar_feed.py
|
||||
already does for plain ICS feeds. Heavier per-fetch (the whole
|
||||
calendar, not just the window) but far more reliable."""
|
||||
try:
|
||||
client = caldav.DAVClient(url=calendar_url, username=username, password=password, timeout=HTTP_TIMEOUT_S)
|
||||
calendar = caldav.Calendar(client=client, url=calendar_url)
|
||||
objects = calendar.date_search(
|
||||
start=datetime.combine(window_start, dtime.min),
|
||||
end=datetime.combine(window_end, dtime.min),
|
||||
expand=False,
|
||||
)
|
||||
objects = calendar.get_events()
|
||||
except Exception as e:
|
||||
raise CalDavError(str(e)) from e
|
||||
|
||||
@@ -89,7 +99,8 @@ def fetch_calendar_events(calendar_url: str, username: str, password: str,
|
||||
try:
|
||||
ical = icalendar.Calendar.from_ical(obj.data)
|
||||
occurrences = recurring_ical_events.of(ical).between(window_start, window_end)
|
||||
except Exception: # one malformed resource shouldn't blank the whole calendar
|
||||
except Exception as e: # one malformed resource shouldn't blank the whole calendar
|
||||
logger.warning("Could not parse a CalDAV event from %s: %s", calendar_url, e)
|
||||
continue
|
||||
for occ in occurrences:
|
||||
dtstart = occ.get("DTSTART")
|
||||
|
||||
Reference in New Issue
Block a user