From b37416376b959ff1e7b3699308fa621d5dc8dbe7 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Wed, 27 Jun 2018 21:41:29 +0200 Subject: [PATCH] [FIX] google_calendar: rrule crash Consider the following: ``` python from datetime import datetime from dateutil import rrule import pytz dt = pytz.UTC.localize(datetime(2016, 9, 2, 15, 42, 43, 205)) rset = rrule.rrulestr('FREQ=WEEKLY;INTERVAL=2;BYDAY=SU,FR,SA', dtstart=dt, forceset=True, tzinfos={}) test = [d for d in rset] ``` This will generate the following error: ``` Traceback (most recent call last): File "", line 1, in File "/usr/lib/python2.7/dist-packages/dateutil/rrule.py", line 1164, in _iter advance_iterator(ritem) File "/usr/lib/python2.7/dist-packages/dateutil/rrule.py", line 1091, in __next__ self.dt = advance_iterator(self.gen) File "/usr/lib/python2.7/dist-packages/dateutil/rrule.py", line 649, in _iter date = datetime.date.fromordinal(ii.yearordinal+i) ValueError: year is out of range ``` If such an event is received from Google Calendar, the synchronization crashes. To avoid this, we enforce a `COUNT` condition if the latter or `UNTIL` is not explicitly specified. This gives: ``` python rset = rrule.rrulestr('FREQ=WEEKLY;INTERVAL=2;BYDAY=SU,FR,SA;COUNT=100', dtstart=dt, forceset=True, tzinfos={}) ``` Note that this was supposed to be fixed in https://github.com/odoo/odoo/blob/9dcc32c3041e7ad3549a09c5ed68ee3ef22c7a26/addons/calendar/models/calendar.py#L1287 However, this was working with the old API. The new API and its computed fields does something different: 1. create the calendar event with the provided `rrule` 2. trigger the inverse method `_inverse_rrule` => `count=100` is added to the computed parameters 3. however, doing so won't recompute `rrule` for obvious reasons (it was the initial value provided, so we don't recompute it) 4. the crash is met in `_get_recurrent_date_by_event` when using `self.rrule` The fundamental issue is that `_inverse_rrule` is not strictly the inverse of `_compute_rrule`. Fixes #25189 opw-1861658 --- addons/calendar/models/calendar.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/addons/calendar/models/calendar.py b/addons/calendar/models/calendar.py index 7e67db98496..411efd987ba 100644 --- a/addons/calendar/models/calendar.py +++ b/addons/calendar/models/calendar.py @@ -1470,6 +1470,10 @@ class Meeting(models.Model): @api.multi def write(self, values): + # FIXME: neverending recurring events + if 'rrule' in values: + values['rrule'] = self._fix_rrule(values) + # compute duration, only if start and stop are modified if not 'duration' in values and 'start' in values and 'stop' in values: values['duration'] = self._get_duration(values['start'], values['stop']) @@ -1538,6 +1542,10 @@ class Meeting(models.Model): @api.model def create(self, values): + # FIXME: neverending recurring events + if 'rrule' in values: + values['rrule'] = self._fix_rrule(values) + if not 'user_id' in values: # Else bug with quick_create when we are filter on an other user values['user_id'] = self.env.user.id @@ -1742,3 +1750,13 @@ class Meeting(models.Model): activity_values['user_id'] = values['user_id'] if activity_values.keys(): self.mapped('activity_ids').write(activity_values) + + @api.model + def _fix_rrule(self, values): + rule_str = values.get('rrule') + if rule_str: + rule = rrule.rrulestr(rule_str) + if not rule._until and not rule._count: + rule._count = 100 + rule_str = str(rule).split('RRULE:')[-1] + return rule_str