def _sync_microsoft2odoo(self, microsoft_events: MicrosoftEvent, default_reminders=()): """Synchronize Microsoft recurrences in Odoo. Creates new recurrences, updates existing ones. :return: synchronized odoo """ existing = microsoft_events.exists(self.env) new = microsoft_events - existing - microsoft_events.cancelled() new_recurrent = new.filter(lambda e: e.is_recurrent()) default_values = {} odoo_values = [ dict(self._microsoft_to_odoo_values(e, default_reminders, default_values), need_sync_m=False) for e in (new - new_recurrent) ] new_odoo = self.with_context(dont_notify=True).create(odoo_values) synced_recurrent_records = self.with_context(dont_notify=True)._sync_recurrence_microsoft2odoo(new_recurrent) if not self._context.get("dont_notify"): new_odoo._notify_attendees() synced_recurrent_records._notify_attendees() cancelled = existing.cancelled() cancelled_odoo = self.browse(cancelled.odoo_ids(self.env)) cancelled_odoo._cancel_microsoft() recurrent_cancelled = self.env['calendar.recurrence'].search([ ('microsoft_id', 'in', (microsoft_events.cancelled() - cancelled).microsoft_ids())]) recurrent_cancelled._cancel_microsoft() synced_records = new_odoo + cancelled_odoo + synced_recurrent_records.calendar_event_ids for mevent in (existing - cancelled).filter(lambda e: e.lastModifiedDateTime and not e.seriesMasterId): # Last updated wins. # This could be dangerous if microsoft server time and odoo server time are different if mevent.is_recurrence(): odoo_record = self.env['calendar.recurrence'].browse(mevent.odoo_id(self.env)) else: odoo_record = self.browse(mevent.odoo_id(self.env)) odoo_record_updated = pytz.utc.localize(odoo_record.write_date) updated = parse(mevent.lastModifiedDateTime or str(odoo_record_updated)) if updated >= odoo_record_updated: vals = dict(odoo_record._microsoft_to_odoo_values(mevent, default_reminders), need_sync_m=False) odoo_record.write(vals) if odoo_record._name == 'calendar.recurrence': odoo_record._update_microsoft_recurrence(mevent, microsoft_events) synced_recurrent_records |= odoo_record else: synced_records |= odoo_record return synced_records, synced_recurrent_records
def _sync_recurrence_microsoft2odoo(self, microsoft_events: MicrosoftEvent): recurrent_masters = microsoft_events.filter(lambda e: e.is_recurrence()) recurrents = microsoft_events.filter(lambda e: e.is_recurrent_not_master()) default_values = {'need_sync_m': False} new_recurrence = self.env['calendar.recurrence'] for recurrent_master in recurrent_masters: new_calendar_recurrence = dict(self.env['calendar.recurrence']._microsoft_to_odoo_values(recurrent_master, (), default_values), need_sync_m=False) to_create = recurrents.filter(lambda e: e.seriesMasterId == new_calendar_recurrence['microsoft_id']) recurrents -= to_create base_values = dict(self.env['calendar.event']._microsoft_to_odoo_values(recurrent_master, (), default_values), need_sync_m=False) to_create_values = [] if new_calendar_recurrence.get('end_type', False) in ['count', 'forever']: to_create = list(to_create)[:MAX_RECURRENT_EVENT] for recurrent_event in to_create: if recurrent_event.type == 'occurrence': value = self.env['calendar.event']._microsoft_to_odoo_recurrence_values(recurrent_event, (), base_values) else: value = self.env['calendar.event']._microsoft_to_odoo_values(recurrent_event, (), default_values) to_create_values += [dict(value, need_sync_m=False)] new_calendar_recurrence['calendar_event_ids'] = [(0, 0, to_create_value) for to_create_value in to_create_values] new_recurrence_odoo = self.env['calendar.recurrence'].create(new_calendar_recurrence) new_recurrence_odoo.base_event_id = new_recurrence_odoo.calendar_event_ids[0] if new_recurrence_odoo.calendar_event_ids else False new_recurrence |= new_recurrence_odoo microsoft_ids = [x.seriesMasterId for x in recurrents] recurrences = self.env['calendar.recurrence'].search([('microsoft_id', 'in', microsoft_ids)]) for recurrent_master_id in set([x.seriesMasterId for x in recurrents]): recurrence_id = recurrences.filtered(lambda ev: ev.microsoft_id == recurrent_master_id) to_update = recurrents.filter(lambda e: e.seriesMasterId == recurrent_master_id) for recurrent_event in to_update: if recurrent_event.type == 'occurrence': value = self.env['calendar.event']._microsoft_to_odoo_recurrence_values(recurrent_event, (), {'need_sync_m': False}) else: value = self.env['calendar.event']._microsoft_to_odoo_values(recurrent_event, (), default_values) existing_event = recurrence_id.calendar_event_ids.filtered(lambda e: e._range() == (value['start'], value['stop'])) if not existing_event: continue value.pop('start') value.pop('stop') existing_event._write_from_microsoft(recurrent_event, value) new_recurrence |= recurrence_id return new_recurrence
def get_events(self, sync_token=None, token=None, timeout=TIMEOUT): url = "/v1.0/me/calendarView/delta" headers = { 'Content-type': 'application/json', 'Authorization': 'Bearer %s' % token, 'Prefer': 'odata.maxpagesize=50,outlook.body-content-type="text"' } params = {} if sync_token: params['$deltatoken'] = sync_token else: params['startDateTime'] = fields.Datetime.subtract( fields.Datetime.now(), years=3).strftime("%Y-%m-%dT00:00:00Z") params['endDateTime'] = fields.Datetime.add( fields.Datetime.now(), years=3).strftime("%Y-%m-%dT00:00:00Z") try: status, data, time = self.microsoft_service._do_request( url, params, headers, method='GET', timeout=timeout) except requests.HTTPError as e: if e.response.status_code == 410 and 'fullSyncRequired' in str( e.response.content): raise InvalidSyncToken( "Invalid sync token. Full sync required") raise e events = data.get('value', []) next_page_token = data.get('@odata.nextLink') while next_page_token: status, data, time = self.microsoft_service._do_request( next_page_token, {}, headers, preuri='', method='GET', timeout=timeout) next_page_token = data.get('@odata.nextLink') events += data.get('value', []) next_sync_token_url = data.get('@odata.deltaLink') next_sync_token = urls.url_parse( next_sync_token_url).decode_query().get('$deltatoken', False) default_reminders = data.get('defaultReminders') return MicrosoftEvent(events), next_sync_token, default_reminders