[FIX] calendar: correct search in recurrent event
Add a 3 use cases with tests that were failing before.
datetime filters were not working correctly on stop filters
The `_get_recurrent_date_by_event` was only computing recurrency on the start
field of an event which can make a difference. A recurring event
start=2017-01-01 09:00:00; stop=2017-01-01 10:00:0
was not matched in a search ('stop', '>', '2017-01-01 09:30:00')
The filter != was not handled in the recurring use cases.
e.g. making the search 'start is set' in the web client is equal to
('start', '!=', False)
Searching with only a start filter matching recurring events but not the
first ("real") occurence, was returning no result if no stop filter was present.
start=2017-01-01 09:00:00; count=5
had no result in a search ('start', '>', '2017-01-02')
This commit is contained in:
@@ -523,9 +523,23 @@ class Meeting(models.Model):
|
||||
return partners
|
||||
|
||||
@api.multi
|
||||
def _get_recurrent_date_by_event(self):
|
||||
""" Get recurrent dates based on Rule string and all event where recurrent_id is child """
|
||||
def _get_recurrent_dates_by_event(self):
|
||||
""" Get recurrent start and stop dates based on Rule string"""
|
||||
start_dates = self._get_recurrent_date_by_event(date_field='start')
|
||||
stop_dates = self._get_recurrent_date_by_event(date_field='stop')
|
||||
return zip(start_dates, stop_dates)
|
||||
|
||||
@api.multi
|
||||
def _get_recurrent_date_by_event(self, date_field='start'):
|
||||
""" Get recurrent dates based on Rule string and all event where recurrent_id is child
|
||||
|
||||
date_field: the field containing the reference date information for recurrency computation
|
||||
"""
|
||||
self.ensure_one()
|
||||
if date_field in self._fields.keys() and self._fields[date_field].type in ('date', 'datetime'):
|
||||
reference_date = self[date_field]
|
||||
else:
|
||||
reference_date = self.start
|
||||
|
||||
def todate(date):
|
||||
val = parser.parse(''.join((re.compile('\d')).findall(date)))
|
||||
@@ -535,14 +549,14 @@ class Meeting(models.Model):
|
||||
return val.astimezone(timezone)
|
||||
|
||||
timezone = pytz.timezone(self._context.get('tz') or 'UTC')
|
||||
startdate = pytz.UTC.localize(fields.Datetime.from_string(self.start)) # Add "+hh:mm" timezone
|
||||
if not startdate:
|
||||
startdate = datetime.now()
|
||||
event_date = pytz.UTC.localize(fields.Datetime.from_string(reference_date)) # Add "+hh:mm" timezone
|
||||
if not event_date:
|
||||
event_date = datetime.now()
|
||||
|
||||
# Convert the start date to saved timezone (or context tz) as it'll
|
||||
# Convert the event date to saved timezone (or context tz) as it'll
|
||||
# define the correct hour/day asked by the user to repeat for recurrence.
|
||||
startdate = startdate.astimezone(timezone) # transform "+hh:mm" timezone
|
||||
rset1 = rrule.rrulestr(str(self.rrule), dtstart=startdate, forceset=True)
|
||||
event_date = event_date.astimezone(timezone) # transform "+hh:mm" timezone
|
||||
rset1 = rrule.rrulestr(str(self.rrule), dtstart=event_date, forceset=True)
|
||||
recurring_meetings = self.search([('recurrent_id', '=', self.id), '|', ('active', '=', False), ('active', '=', True)])
|
||||
|
||||
for meeting in recurring_meetings:
|
||||
@@ -986,27 +1000,38 @@ class Meeting(models.Model):
|
||||
result.append(meeting.id)
|
||||
result_data.append(meeting.get_search_fields(order_fields))
|
||||
continue
|
||||
rdates = meeting._get_recurrent_date_by_event()
|
||||
rdates = meeting._get_recurrent_dates_by_event()
|
||||
|
||||
for r_date in rdates:
|
||||
for r_start_date, r_stop_date in rdates:
|
||||
# fix domain evaluation
|
||||
# step 1: check date and replace expression by True or False, replace other expressions by True
|
||||
# step 2: evaluation of & and |
|
||||
# check if there are one False
|
||||
pile = []
|
||||
ok = True
|
||||
r_date = r_start_date # default for empty domain
|
||||
for arg in domain:
|
||||
if str(arg[0]) in ('start', 'stop', 'final_date'):
|
||||
if str(arg[0]) == 'start':
|
||||
r_date = r_start_date
|
||||
else:
|
||||
r_date = r_stop_date
|
||||
if arg[2] and len(arg[2]) > len(r_date.strftime(DEFAULT_SERVER_DATE_FORMAT)):
|
||||
dformat = DEFAULT_SERVER_DATETIME_FORMAT
|
||||
else:
|
||||
dformat = DEFAULT_SERVER_DATE_FORMAT
|
||||
if (arg[1] == '='):
|
||||
ok = r_date.strftime('%Y-%m-%d') == arg[2]
|
||||
ok = r_date.strftime(dformat) == arg[2]
|
||||
if (arg[1] == '>'):
|
||||
ok = r_date.strftime('%Y-%m-%d') > arg[2]
|
||||
ok = r_date.strftime(dformat) > arg[2]
|
||||
if (arg[1] == '<'):
|
||||
ok = r_date.strftime('%Y-%m-%d') < arg[2]
|
||||
ok = r_date.strftime(dformat) < arg[2]
|
||||
if (arg[1] == '>='):
|
||||
ok = r_date.strftime('%Y-%m-%d') >= arg[2]
|
||||
ok = r_date.strftime(dformat) >= arg[2]
|
||||
if (arg[1] == '<='):
|
||||
ok = r_date.strftime('%Y-%m-%d') <= arg[2]
|
||||
ok = r_date.strftime(dformat) <= arg[2]
|
||||
if (arg[1] == '!='):
|
||||
ok = r_date.strftime(dformat) != arg[2]
|
||||
pile.append(ok)
|
||||
elif str(arg) == str('&') or str(arg) == str('|'):
|
||||
pile.append(arg)
|
||||
@@ -1531,6 +1556,18 @@ class Meeting(models.Model):
|
||||
if not self._context.get('virtual_id', True):
|
||||
return super(Meeting, self).search(new_args, offset=offset, limit=limit, order=order, count=count)
|
||||
|
||||
if any(arg[0] == 'start' for arg in args) and \
|
||||
not any(arg[0] in ('stop', 'final_date') for arg in args):
|
||||
# domain with a start filter but with no stop clause should be extended
|
||||
# e.g. start=2017-01-01, count=5 => virtual occurences must be included in ('start', '>', '2017-01-02')
|
||||
start_args = new_args
|
||||
new_args = []
|
||||
for arg in start_args:
|
||||
new_arg = arg
|
||||
if arg[0] in ('start_date', 'start_datetime', 'start',):
|
||||
new_args += ['|', '&', ('recurrency', '=', 1), ('final_date', arg[1], arg[2])]
|
||||
new_args.append(new_arg)
|
||||
|
||||
# offset, limit, order and count must be treated separately as we may need to deal with virtual ids
|
||||
events = super(Meeting, self).search(new_args, offset=0, limit=0, order=None, count=False)
|
||||
events = self.browse(events.get_recurrent_ids(args, order=order))
|
||||
|
||||
@@ -96,3 +96,52 @@ class TestRecurrentEvent(common.TransactionCase):
|
||||
# I update the description of two meetings, and check that both have been updated
|
||||
self.calendar_event_sprint_review.write({'description': "Some description"})
|
||||
self.assertEqual(self.calendar_event_sprint_review.description, "Some description", "Event %d has not been updated" % self.calendar_event_sprint_review.id)
|
||||
|
||||
def test_recurrent_meeting4(self):
|
||||
# I create a weekly meeting till a particular end date.
|
||||
self.CalendarEvent.create({
|
||||
'start': '2017-01-22 11:47:00',
|
||||
'stop': '2017-01-22 12:47:00',
|
||||
'day': 0.0,
|
||||
'duration': 1.0,
|
||||
'final_date': '2017-06-30',
|
||||
'end_type': 'end_date',
|
||||
'fr': True,
|
||||
'mo': True,
|
||||
'th': True,
|
||||
'tu': True,
|
||||
'we': True,
|
||||
'name': 'Review code with programmer',
|
||||
'recurrency': True,
|
||||
'rrule_type': 'weekly'
|
||||
})
|
||||
|
||||
# I search for a recurrent weekly meetings that take place at a given date.
|
||||
meetings_count = self.CalendarEvent.with_context({'virtual_id': True}).search_count([
|
||||
('start', '<=', '2017-01-24'), ('stop', '>=', '2017-01-24'), ('name', '=', 'Review code with programmer')
|
||||
])
|
||||
self.assertEqual(meetings_count, 1, 'Recurrent weekly meetings are not found using date filter !')
|
||||
|
||||
# I search for a recurrent weekly meetings that take place at a given date and time.
|
||||
meetings_count = self.CalendarEvent.with_context({'virtual_id': True}).search_count([
|
||||
('start', '<=', '2017-01-24 11:55:00'), ('stop', '>=', '2017-01-24 11:55:00'), ('name', '=', 'Review code with programmer')
|
||||
])
|
||||
self.assertEqual(meetings_count, 1, 'Recurrent weekly meetings are not found using time filter !')
|
||||
|
||||
# I search using the filter 'start date is set'
|
||||
meetings_count = self.CalendarEvent.with_context({'virtual_id': True}).search_count([
|
||||
('start', '!=', False), ('stop', '>=', '2017-06-30 11:55:00'), ('name', '=', 'Review code with programmer')
|
||||
])
|
||||
self.assertEqual(meetings_count, 1, "Last recurrent weekly meetings are not found using 'is set' filter !")
|
||||
|
||||
# I search for a recurrent weekly meetings that take place at a given date and time.
|
||||
meetings_count = self.CalendarEvent.with_context({'virtual_id': True}).search_count([
|
||||
('start', '<=', '2017-01-24 11:55:00'), ('stop', '>=', '2017-01-24 15:55:00')
|
||||
])
|
||||
self.assertEqual(meetings_count, 0, 'Too late recurrent meetings are found using time filter !')
|
||||
|
||||
# I search using a start filter but no stop
|
||||
meetings_count = self.CalendarEvent.with_context({'virtual_id': True}).search_count([
|
||||
('start', '>=', '2017-06-30 08:00:00'), ('name', '=', 'Review code with programmer')
|
||||
])
|
||||
self.assertEqual(meetings_count, 1, "Last recurrent weekly meetings are not found without stop filter !")
|
||||
|
||||
Reference in New Issue
Block a user