From 064f18e6b6003b7fcb3aa5f5072e8e00d18acbb0 Mon Sep 17 00:00:00 2001 From: Jeremy Kersten Date: Wed, 11 Feb 2015 21:31:20 +0100 Subject: [PATCH] [FIX] calendar: stop to think that cron will be always on time Fix error where if no email_from was in openerp.tool.config, notif by mail was not sent Add optionnal param 'missing' to allow to have missing alarm. Missing field is the date from the last time that cron had run. Now we stop to process the next 30 minutes, but we process all alarm since the next cron. So, an email alarm will be always in delay, but a mail will be sent ! In v8 we use ICP to save the date, but it should be changed in master, maybe we should save the last execution time in the ir_cron model directly ? --- addons/calendar/calendar.py | 91 ++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py index 8943fe4c03f..2e8513fc5c2 100644 --- a/addons/calendar/calendar.py +++ b/addons/calendar/calendar.py @@ -373,12 +373,12 @@ class calendar_alarm_manager(osv.AbstractModel): tuple_params += (partner_id, ) #Add filter on hours - tuple_params += (seconds, seconds,) + tuple_params += (seconds,) cr.execute("""SELECT * FROM ( %s WHERE cal.active = True ) AS ALL_EVENTS WHERE ALL_EVENTS.first_alarm < (now() at time zone 'utc' + interval '%%s' second ) - AND ALL_EVENTS.last_alarm > (now() at time zone 'utc' - interval '%%s' second ) + AND ALL_EVENTS.last_event_date > (now() at time zone 'utc') """ % base_request, tuple_params) for event_id, first_alarm, last_alarm, first_meeting, last_meeting, min_duration, max_duration, rule in cr.fetchall(): @@ -395,20 +395,30 @@ class calendar_alarm_manager(osv.AbstractModel): return res - def do_check_alarm_for_one_date(self, cr, uid, one_date, event, event_maxdelta, in_the_next_X_seconds, after=False, notif=True, mail=True, context=None): - res = [] - alarm_type = [] + def do_check_alarm_for_one_date(self, cr, uid, one_date, event, event_maxdelta, in_the_next_X_seconds, after=False, notif=True, mail=True, missing=False, context=None): + # one_date: date of the event to check (not the same that in the event browse if recurrent) + # event: Event browse record + # event_maxdelta: biggest duration from alarms for this event + # in_the_next_X_seconds: looking in the future (in seconds) + # after: if not False: will return alert if after this date (date as string - todo: change in master) + # missing: if not False: will return alert even if we are too late + # notif: Looking for type notification + # mail: looking for type email + res = [] + + # TODO: replace notif and email in master by alarm_type + remove event_maxdelta and if using it + alarm_type = [] if notif: alarm_type.append('notification') if mail: alarm_type.append('email') - if one_date - timedelta(minutes=event_maxdelta) < datetime.now() + timedelta(seconds=in_the_next_X_seconds): # if an alarm is possible for this date + if one_date - timedelta(minutes=(missing and 0 or event_maxdelta)) < datetime.now() + timedelta(seconds=in_the_next_X_seconds): # if an alarm is possible for this date for alarm in event.alarm_ids: if alarm.type in alarm_type and \ - one_date - timedelta(minutes=alarm.duration_minutes) < datetime.now() + timedelta(seconds=in_the_next_X_seconds) and \ - (not after or one_date - timedelta(minutes=alarm.duration_minutes) > datetime.strptime(after.split('.')[0], DEFAULT_SERVER_DATETIME_FORMAT)): + one_date - timedelta(minutes=(missing and 0 or alarm.duration_minutes)) < datetime.now() + timedelta(seconds=in_the_next_X_seconds) and \ + (not after or one_date - timedelta(minutes=alarm.duration_minutes) > openerp.fields.Datetime.from_string(after)): alert = { 'alarm_id': alarm.id, 'event_id': event.id, @@ -418,55 +428,53 @@ class calendar_alarm_manager(osv.AbstractModel): return res def get_next_mail(self, cr, uid, context=None): + now = openerp.fields.Datetime.to_string(datetime.now()) + + icp = self.pool['ir.config_parameter'] + last_notif_mail = icp.get_param(cr, SUPERUSER_ID, 'calendar.last_notif_mail', default=False) or now + try: - cron = self.pool['ir.model.data'].get_object( - cr, uid, 'calendar', 'ir_cron_scheduler_alarm', context=context) + cron = self.pool['ir.model.data'].get_object(cr, uid, 'calendar', 'ir_cron_scheduler_alarm', context=context) except ValueError: _logger.error("Cron for " + self._name + " can not be identified !") return False - if cron.interval_type == "weeks": - cron_interval = cron.interval_number * 7 * 24 * 60 * 60 - elif cron.interval_type == "days": - cron_interval = cron.interval_number * 24 * 60 * 60 - elif cron.interval_type == "hours": - cron_interval = cron.interval_number * 60 * 60 - elif cron.interval_type == "minutes": - cron_interval = cron.interval_number * 60 - elif cron.interval_type == "seconds": - cron_interval = cron.interval_number - else: - cron_interval = False + interval_to_second = { + "weeks": 7 * 24 * 60 * 60, + "days": 24 * 60 * 60, + "hours": 60 * 60, + "minutes": 60, + "seconds": 1 + } - if not cron_interval: + if cron.interval_type not in interval_to_second.keys(): _logger.error("Cron delay can not be computed !") return False + cron_interval = cron.interval_number * interval_to_second[cron.interval_type] + all_events = self.get_next_potential_limit_alarm(cr, uid, cron_interval, notif=False, context=context) - for event in all_events: # .values() - max_delta = all_events[event]['max_duration'] - curEvent = self.pool.get('calendar.event').browse(cr, uid, event, context=context) + for curEvent in self.pool.get('calendar.event').browse(cr, uid, all_events.keys(), context=context): + max_delta = all_events[curEvent.id]['max_duration'] + if curEvent.recurrency: - bFound = False - LastFound = False + at_least_one = False + last_found = False for one_date in self.pool.get('calendar.event').get_recurrent_date_by_event(cr, uid, curEvent, context=context): in_date_format = one_date.replace(tzinfo=None) - LastFound = self.do_check_alarm_for_one_date(cr, uid, in_date_format, curEvent, max_delta, cron_interval, notif=False, context=context) - if LastFound: - for alert in LastFound: - self.do_mail_reminder(cr, uid, alert, context=context) - - if not bFound: # if it's the first alarm for this recurrent event - bFound = True - if bFound and not LastFound: # if the precedent event had an alarm but not this one, we can stop the search for this event + last_found = self.do_check_alarm_for_one_date(cr, uid, in_date_format, curEvent, max_delta, 0, after=last_notif_mail, notif=False, missing=True, context=context) + for alert in last_found: + self.do_mail_reminder(cr, uid, alert, context=context) + at_least_one = True # if it's the first alarm for this recurrent event + if at_least_one and not last_found: # if the precedent event had an alarm but not this one, we can stop the search for this event break else: in_date_format = datetime.strptime(curEvent.start, DEFAULT_SERVER_DATETIME_FORMAT) - LastFound = self.do_check_alarm_for_one_date(cr, uid, in_date_format, curEvent, max_delta, cron_interval, notif=False, context=context) - if LastFound: - for alert in LastFound: - self.do_mail_reminder(cr, uid, alert, context=context) + last_found = self.do_check_alarm_for_one_date(cr, uid, in_date_format, curEvent, max_delta, 0, after=last_notif_mail, notif=False, missing=True, context=context) + for alert in last_found: + self.do_mail_reminder(cr, uid, alert, context=context) + icp.set_param(cr, SUPERUSER_ID, 'calendar.last_notif_mail', now) def get_next_notif(self, cr, uid, context=None): ajax_check_every_seconds = 300 @@ -496,7 +504,7 @@ class calendar_alarm_manager(osv.AbstractModel): break else: in_date_format = datetime.strptime(curEvent.start, DEFAULT_SERVER_DATETIME_FORMAT) - LastFound = self.do_check_alarm_for_one_date(cr, uid, in_date_format, curEvent, max_delta, ajax_check_every_seconds, partner.calendar_last_notif_ack, mail=False, context=context) + LastFound = self.do_check_alarm_for_one_date(cr, uid, in_date_format, curEvent, max_delta, ajax_check_every_seconds, after=partner.calendar_last_notif_ack, mail=False, context=context) if LastFound: for alert in LastFound: all_notif.append(self.do_notif_reminder(cr, uid, alert, context=context)) @@ -515,6 +523,7 @@ class calendar_alarm_manager(osv.AbstractModel): cr, uid, [att.id for att in event.attendee_ids], + email_from=event.user_id.partner_id.email, template_xmlid='calendar_template_meeting_reminder', force=True, context=context