caldav_sync: moving a single event in a recurrence is synchronizing well

This commit is contained in:
Marc Durepos 2024-11-06 16:06:43 -05:00
parent 14a8b6acf0
commit 0e07d42825
2 changed files with 52 additions and 44 deletions

View file

@ -7,8 +7,7 @@ from odoo.addons.calendar.models.calendar_recurrence import MAX_RECURRENT_EVENT
import caldav
import logging
from datetime import datetime, date
from icalendar import vCalAddress, vText, vDatetime, vRecur, Event
from bs4 import BeautifulSoup
from icalendar import vCalAddress, vText, vDatetime, vRecur, Event, vInt
import re
from pytz import timezone, utc
from typing import List, Dict, TypeVar, Optional
@ -71,6 +70,8 @@ class CalendarEvent(models.Model):
# when applying a recurrence.
caldav_recurrence_id = fields.Datetime(
string="CalDAV Recurrence ID",
compute="_compute_caldav_recurrence_id",
store=True,
)
caldav_user_ids = fields.Many2many(
comodel_name="res.users",
@ -83,6 +84,23 @@ class CalendarEvent(models.Model):
#### Field Computation Methods ####
###################################
@api.depends("recurrence_id.dtstart")
def _compute_caldav_recurrence_id(self):
for event in self:
if event.recurrence_id:
time = event.recurrence_id.dtstart.time()
date = event.start.date()
event.caldav_recurrence_id = datetime(
date.year,
date.month,
date.day,
time.hour,
time.minute,
time.second,
)
else:
event.caldav_recurrence_id = False
@api.depends("name", "description", "partner_ids", "location", "videocall_location")
def _compute_differs_from_base_event(self):
base_events = self.filtered("is_base_event")
@ -145,8 +163,8 @@ class CalendarEvent(models.Model):
:return: The timezone-aware datetime that uniquely identifies this
event in the recurrence chain.
"""
user_tz = timezone(self.user_id.tz)
recurrence_id = utc.localize(self.caldav_recurrence_id).astimezone(user_tz)
event_tz = timezone(self.event_tz)
recurrence_id = utc.localize(self.caldav_recurrence_id).astimezone(event_tz)
return recurrence_id
#################################################################
@ -171,11 +189,11 @@ class CalendarEvent(models.Model):
client = user._get_caldav_client()
calendar = client.calendar(url=user.caldav_calendar_url)
# try:
_logger.debug(f"Creating new CalDAV event for {event.name}")
_logger.info(f"Creating new CalDAV event for {event.name}")
caldav_events = event._create_in_icalendar(calendar)
for caldav_event in caldav_events:
caldav_uid = caldav_event.vobject_instance.vevent.uid.value
_logger.debug(f"New CalDAV UID: {caldav_uid}")
_logger.info(f"New CalDAV UID: {caldav_uid}")
event.with_context(caldav_no_sync=True).write(
{"caldav_uid": caldav_uid}
)
@ -195,9 +213,7 @@ class CalendarEvent(models.Model):
to_sync = self._to_sync()
if to_sync and not self.env.context.get("caldav_no_sync"):
for rec in to_sync:
_logger.debug(
f"Updating event {rec.name} in CalDAV. " f"{rec.fields_get()}"
)
_logger.info(f"Updating event {rec.name} at {rec.start} in " f"CalDAV.")
rec._sync_write_to_caldav()
return res
@ -211,19 +227,32 @@ class CalendarEvent(models.Model):
try:
event = self._find_in_caldav_calendar(calendar)
except caldav.error.NotFoundError:
_logger.debug(
_logger.info(
f"CalDAV event {self.caldav_uid} not found on server during update."
f" Creating it instead."
)
if event:
event.icalendar_component.update(ical_event_data)
event.save()
elif self.recurrence_id and not self.follow_recurrence:
base_event = calendar.event_by_uid(self.caldav_uid)
elif self.recurrence_id and not self.is_base_event:
base_event = self._get_caldav_base_event_by_uid(
calendar, self.caldav_uid
)
base_event.icalendar_instance.add_component(Event(**ical_event_data))
if self.caldav_recurrence_id != self.start:
tz = timezone(self.event_tz)
base_event.save()
else:
calendar.save_event(**ical_event_data)
calendar.add_event(**ical_event_data)
def _get_caldav_base_event_by_uid(
self, calendar: caldav.Calendar, uid: str
) -> Optional[CalendarEvent]:
for event in calendar.events():
comp = event.icalendar_component
event_uid = self._extract_component_text(comp, "uid")
event_recurrence_id = self._extract_component_text(comp, "recurrence-id")
if event_uid == uid and not event_recurrence_id:
return event
def unlink(self):
if not self.env.context.get("caldav_no_sync"):
@ -231,7 +260,7 @@ class CalendarEvent(models.Model):
try:
# TODO: fix the attempt to sync all events ... _to_sync is
# not working here
_logger.debug(f"Removing event {rec.name} from CalDAV")
_logger.info(f"Removing event {rec.name} from CalDAV")
rec._sync_unlink_to_caldav()
except Exception as e:
_logger.error(f"Failed to delete event from CalDAV server: {e}")
@ -243,7 +272,7 @@ class CalendarEvent(models.Model):
client = user._get_caldav_client()
calendar = client.calendar(url=user.caldav_calendar_url)
try:
_logger.debug(f"Removing CalDAV event {self.caldav_uid}")
_logger.info(f"Removing CalDAV event {self.caldav_uid}")
ical_event = self._find_in_caldav_calendar(calendar)
if ical_event:
ical_event.delete()
@ -304,17 +333,15 @@ class CalendarEvent(models.Model):
def _add_event_dates(self, event_data: Dict) -> None:
"""Add pertinent dates to event data, based on self."""
user_tz = timezone("UTC")
if self.user_id.tz:
user_tz = timezone(self.user_id.tz)
event_tz = timezone(self.event_tz)
event_data["last-modified"] = vDatetime(
utc.localize(self.write_date).astimezone(user_tz)
utc.localize(self.write_date).astimezone(event_tz)
)
event_data["created"] = vDatetime(
utc.localize(self.create_date).astimezone(user_tz)
utc.localize(self.create_date).astimezone(event_tz)
)
event_data["dtstart"] = vDatetime(utc.localize(self.start).astimezone(user_tz))
event_data["dtend"] = vDatetime(utc.localize(self.stop).astimezone(user_tz))
event_data["dtstart"] = vDatetime(utc.localize(self.start).astimezone(event_tz))
event_data["dtend"] = vDatetime(utc.localize(self.stop).astimezone(event_tz))
return event_data
def _add_event_recurrence_id(self, event_data: Dict) -> None:
@ -378,7 +405,7 @@ class CalendarEvent(models.Model):
CalDAV server. The caldav_uid that unites events of a series needs to be
separated, making a new uid for each base event (and its recurrences)."""
# Start by deleting the old calendar events since we will need to remake them
_logger.debug(
_logger.info(
f"Events {self} with CalDAV UIDs "
f"{self.mapped('caldav_uid')} are being "
f"detached."
@ -407,7 +434,7 @@ class CalendarEvent(models.Model):
recurrence_events.write({"caldav_uid": new_uid})
def _break_recurrence(self, future=True):
_logger.debug(
_logger.info(
f"Recurrence breaking for events {self} with CalDAV "
f"UIDs {self.mapped('caldav_uid')}."
)

View file

@ -5,25 +5,6 @@ from pytz import timezone, utc
class RecurrenceRule(models.Model):
_inherit = "calendar.recurrence"
def _recompute_event_caldav_recurrence_ids(self):
def _get_recurrence_id(event):
return event.start.astimezone(utc).replace(tzinfo=None)
for event in self.calendar_event_ids:
event.caldav_recurrence_id = _get_recurrence_id(event)
def _apply_recurrence(
self,
specific_values_creation=None,
no_send_edit=False,
generic_values_creation=None,
):
detached_events = super()._apply_recurrence(
specific_values_creation, no_send_edit, generic_values_creation
)
self._recompute_event_caldav_recurrence_ids()
return detached_events
def _detach_events(self, events):
events = super()._detach_events(events)
events.with_context(dont_notify=True)._post_recurrence_detach()