caldav_sync: all seems good. further testing to come before merging into 17.0

This commit is contained in:
Marc Durepos 2024-11-08 12:41:02 -05:00
parent 90d05d75a2
commit 992778e353
2 changed files with 50 additions and 16 deletions

View file

@ -539,7 +539,8 @@ class CalendarEvent(models.Model):
:param user: The res.user record for whom to synchronize events. :param user: The res.user record for whom to synchronize events.
""" """
_logger.info(f"Polling CalDAV server for user {user.name}") _logger.info(f"Polling CalDAV server for user {user.name}")
events = user._get_caldav_events() calendar = user._get_caldav_client().calendar(url=user.caldav_calendar_url)
events = calendar.events()
synced_events = self.env["calendar.event"] synced_events = self.env["calendar.event"]
for caldav_event in events: for caldav_event in events:
ical_event = caldav_event.icalendar_instance ical_event = caldav_event.icalendar_instance
@ -556,7 +557,27 @@ class CalendarEvent(models.Model):
) )
orphaned_events = orphaned_events._to_sync() orphaned_events = orphaned_events._to_sync()
if orphaned_events: if orphaned_events:
orphaned_events.with_context(caldav_no_sync=True).with_user(user).unlink() base_orphans = orphaned_events.filtered(
lambda ev: ev.recurrence_id and ev.is_base_event
)
for base_orphan in base_orphans:
try:
if calendar.event_by_uid(base_orphan.caldav_uid):
# There are some events remaining in this recurrence series,
# so we have synchronized them individually.
pass
except caldav.error.NotFoundError:
# There are no more events with this UID, so we need to clear
# out the whole recurrence chain from the Odoo side.
ctx = {"caldav_no_sync": True}
recurrence = base_orphan.recurrence_id
recurrence.calendar_event_ids.with_context(**ctx).with_user(
user
).unlink()
recurrence.with_context(**ctx).with_user(user).unlink()
(orphaned_events - base_orphans).with_context(
caldav_no_sync=True
).with_user(user).unlink()
@api.model @api.model
def _sync_event_from_ical( def _sync_event_from_ical(

View file

@ -1,12 +1,13 @@
from collections.abc import Iterable from collections.abc import Iterable
from odoo.tests import TransactionCase from odoo.tests import TransactionCase
from odoo import Command from odoo import Command
from unittest.mock import patch, MagicMock, PropertyMock from unittest.mock import patch, MagicMock, DEFAULT
import icalendar import icalendar
from pathlib import Path from pathlib import Path
from .common import CaldavTestCommon from .common import CaldavTestCommon
from contextlib import contextmanager from contextlib import contextmanager
from datetime import datetime, UTC, timedelta from datetime import datetime, UTC, timedelta
import caldav
WEEKDAY_MAP = { WEEKDAY_MAP = {
0: "SUN", 0: "SUN",
@ -28,7 +29,6 @@ def _patch_caldav_with_events_from_ics(ics_paths, user, last_modified=None):
with ( with (
patch("caldav.DAVClient") as MockDAVClient, patch("caldav.DAVClient") as MockDAVClient,
patch("caldav.Calendar") as MockCalendar, patch("caldav.Calendar") as MockCalendar,
patch("caldav.Event") as MockEvent,
): ):
mock_client = MockDAVClient.return_value mock_client = MockDAVClient.return_value
mock_calendar = MockCalendar.return_value mock_calendar = MockCalendar.return_value
@ -43,6 +43,12 @@ def _patch_caldav_with_events_from_ics(ics_paths, user, last_modified=None):
raise Exception("Calendar does not exist.") raise Exception("Calendar does not exist.")
mock_calendar.side_effect = calendar_side_effect mock_calendar.side_effect = calendar_side_effect
def event_by_uid_side_effect(self, uid):
for event in self.events:
if str(event.icalendar_component.get("uid")) == uid:
return event
ical_events = [] ical_events = []
if ics_paths: if ics_paths:
if not isinstance(ics_paths, Iterable): if not isinstance(ics_paths, Iterable):
@ -51,18 +57,25 @@ def _patch_caldav_with_events_from_ics(ics_paths, user, last_modified=None):
with ics_path.open("rb") as file: with ics_path.open("rb") as file:
ical_content = file.read() ical_content = file.read()
ical_events.append(icalendar.Calendar.from_ical(ical_content)) ical_events.append(icalendar.Calendar.from_ical(ical_content))
mock_caldav_events = [] if last_modified:
for ical_event in ical_events: for event in ical_events:
mock_event = MockEvent() event["last-modified"] = last_modified
mock_event.icalendar_instance = ical_event event["dtstamp"] = last_modified
if last_modified:
for component in ical_event.walk(): base_events = [event for event in ical_events if not event.get("recurrence-id")]
if component.name == "VEVENT": for base_event in base_events:
component["last-modified"] = last_modified.strftime( child_events = [
"%Y%m%dT%H%M%SZ" event
) for event in ical_events
mock_caldav_events.append(mock_event) if event.get("recurrence-id")
mock_calendar.events.return_value = mock_caldav_events and event.get("uid") == base_event.get("uid")
]
for child_event in child_events:
base_event.add_component(child_event)
mock_calendar.add_event(base_event)
caldav_events = [caldav.Event(data=event) for event in base_events]
mock_calendar.events.return_value = caldav_events
mock_calendar.event_by_uid.side_effect = event_by_uid_side_effect
user._compute_is_caldav_enabled() user._compute_is_caldav_enabled()
yield yield