def _google_delete(self, google_service: GoogleCalendarService, google_id, timeout=TIMEOUT): with google_calendar_token(self.env.user.sudo()) as token: if token: google_service.delete(google_id, token=token, timeout=timeout) # When the record has been deleted on our side, we need to delete it on google but we don't want # to raise an error because the record don't exists anymore. self.exists().need_sync = False
def _sync_google_calendar(self, calendar_service: GoogleCalendarService): self.ensure_one() if self.google_synchronization_stopped: return False full_sync = not bool(self.google_calendar_sync_token) with google_calendar_token(self) as token: try: events, next_sync_token, default_reminders = calendar_service.get_events( self.google_calendar_sync_token, token=token) except InvalidSyncToken: events, next_sync_token, default_reminders = calendar_service.get_events( token=token) full_sync = True self.google_calendar_sync_token = next_sync_token # Google -> Odoo recurrences = events.filter(lambda e: e.is_recurrence()) synced_recurrences = self.env['calendar.recurrence']._sync_google2odoo( recurrences) synced_events = self.env['calendar.event']._sync_google2odoo( events - recurrences, default_reminders=default_reminders) # Odoo -> Google recurrences = self.env['calendar.recurrence']._get_records_to_sync( full_sync=full_sync) recurrences -= synced_recurrences recurrences._sync_odoo2google(calendar_service) synced_events |= recurrences.calendar_event_ids - recurrences._get_outliers( ) events = self.env['calendar.event']._get_records_to_sync( full_sync=full_sync) (events - synced_events)._sync_odoo2google(calendar_service) return bool(events | synced_events) or bool(recurrences | synced_recurrences)
def reset_account(self): google = GoogleCalendarService(self.env['google.service']) events = self.env['calendar.event'].search([ ('user_id', '=', self.user_id.id), ('google_id', '!=', False)]) if self.delete_policy in ('delete_google', 'delete_both'): with google_calendar_token(self.user_id) as token: for event in events: google.delete(event.google_id, token=token) if self.delete_policy in ('delete_odoo', 'delete_both'): events.google_id = False events.unlink() if self.sync_policy == 'all': events.write({ 'google_id': False, 'need_sync': True, }) self.user_id.google_cal_account_id._set_auth_tokens(False, False, 0) self.user_id.write({ 'google_calendar_sync_token': False, 'google_calendar_cal_id': False, })
def _google_delete(self, google_service: GoogleCalendarService, google_id, timeout=TIMEOUT): with google_calendar_token(self.env.user.sudo()) as token: if token: google_service.delete(google_id, token=token, timeout=timeout) self.need_sync = False
def _google_patch(self, google_service: GoogleCalendarService, google_id, values, timeout=TIMEOUT): with google_calendar_token(self.env.user.sudo()) as token: if token: try: google_service.patch(google_id, values, token=token, timeout=timeout) except HTTPError as e: if e.response.status_code in (400, 403): self._google_error_handling(e) self.need_sync = False
def _sync_event(self): # For weird reasons, we can't sync status when we are not the responsible # We can't adapt google_value to only keep ['id', 'summary', 'attendees', 'start', 'end', 'reminders'] # and send that. We get a Forbidden for non-organizer error even if we only send start, end that are mandatory ! all_events = self.mapped('event_id').filtered(lambda e: e.google_id) other_events = all_events.filtered(lambda e: e.user_id and e.user_id.id != self.env.user.id) for user in other_events.mapped('user_id'): service = GoogleCalendarService(self.env['google.service'].with_user(user)) other_events.filtered(lambda ev: ev.user_id.id == user.id).with_user(user)._sync_odoo2google(service) google_service = GoogleCalendarService(self.env['google.service']) (all_events - other_events)._sync_odoo2google(google_service)
def _sync_google_calendar(self, calendar_service: GoogleCalendarService): self.ensure_one() if self.google_synchronization_stopped: return False # don't attempt to sync when another sync is already in progress, as we wouldn't be # able to commit the transaction anyway (row is locked) self.env.cr.execute( """SELECT id FROM res_users WHERE id = %s FOR NO KEY UPDATE SKIP LOCKED""", [self.id]) if not self.env.cr.rowcount: _logger.info("skipping calendar sync, locked user %s", self.login) return False full_sync = not bool(self.google_calendar_sync_token) with google_calendar_token(self) as token: try: events, next_sync_token, default_reminders = calendar_service.get_events( self.google_cal_account_id.calendar_sync_token, token=token) except InvalidSyncToken: events, next_sync_token, default_reminders = calendar_service.get_events( token=token) full_sync = True self.google_cal_account_id.calendar_sync_token = next_sync_token # Google -> Odoo events.clear_type_ambiguity(self.env) recurrences = events.filter(lambda e: e.is_recurrence()) synced_recurrences = self.env['calendar.recurrence']._sync_google2odoo( recurrences) synced_events = self.env['calendar.event']._sync_google2odoo( events - recurrences, default_reminders=default_reminders) # Odoo -> Google recurrences = self.env['calendar.recurrence']._get_records_to_sync( full_sync=full_sync) recurrences -= synced_recurrences recurrences._sync_odoo2google(calendar_service) synced_events |= recurrences.calendar_event_ids - recurrences._get_outliers( ) synced_events |= synced_recurrences.calendar_event_ids - synced_recurrences._get_outliers( ) events = self.env['calendar.event']._get_records_to_sync( full_sync=full_sync) (events - synced_events)._sync_odoo2google(calendar_service) return bool(events | synced_events) or bool(recurrences | synced_recurrences)
def write(self, vals): res = super().write(vals) if vals.get('state'): # When the state is changed, the corresponding event must be sync with google google_service = GoogleCalendarService(self.env['google.service']) self.event_id.filtered('google_id')._sync_odoo2google(google_service) return res
def synchroniser_google_user(self, event, user): if user: with google_calendar_token(user.sudo()) as token: if token: _logger.warning("## token = %s" % (token)) headers = { 'Content-type': 'application/json', 'Authorization': 'Bearer %s' % token } params = {'access_token': token} values = event._google_values() _logger.warning("## values = %s" % (values)) google_service = GoogleCalendarService( self.with_user(user).env['google.service']) event_id = values.get('id') _logger.warning( "## synchroniser_google_action event_id = %s" % (event_id)) try: _logger.warning("## _google_patch event = %s" % (event)) event.with_user(user)._google_patch(google_service, event_id, values, timeout=3) except: _logger.exception( "## _google_patch ERROR event = %s" % (event))
def _apply_recurrence(self, specific_values_creation=None, no_send_edit=False): events = self.filtered('need_sync').calendar_event_ids detached_events = super()._apply_recurrence(specific_values_creation, no_send_edit) google_service = GoogleCalendarService(self.env['google.service']) # If a synced event becomes a recurrence, the event needs to be deleted from # Google since it's now the recurrence which is synced. # Those events are kept in the database and their google_id is updated # according to the recurrence google_id, therefore we need to keep an inactive copy # of those events with the original google id. The next sync will then correctly # delete those events from Google. vals = [] for event in events.filtered('google_id'): if event.active and event.google_id != event.recurrence_id._get_event_google_id( event): vals += [{ 'name': event.name, 'google_id': event.google_id, 'start': event.start, 'stop': event.stop, 'active': False, 'need_sync': True, }] event._google_delete(google_service, event.google_id) event.google_id = False self.env['calendar.event'].create(vals) self.calendar_event_ids.need_sync = False return detached_events
def sync_data(self, model, **kw): """ This route/function is called when we want to synchronize Odoo calendar with Google Calendar. Function return a dictionary with the status : need_config_from_admin, need_auth, need_refresh, sync_stopped, success if not calendar_event The dictionary may contains an url, to allow Odoo Client to redirect user on this URL for authorization for example """ if model == 'calendar.event': GoogleCal = GoogleCalendarService(request.env['google.service']) # Checking that admin have already configured Google API for google synchronization ! client_id = request.env['ir.config_parameter'].sudo().get_param( 'google_calendar_client_id') if not client_id or client_id == '': action_id = '' if GoogleCal._can_authorize_google(request.env.user): action_id = request.env.ref( 'base_setup.action_general_configuration').id return { "status": "need_config_from_admin", "url": '', "action": action_id } # Checking that user have already accepted Odoo to access his calendar ! if not GoogleCal.is_authorized(request.env.user): url = GoogleCal._google_authentication_url( from_url=kw.get('fromurl')) return {"status": "need_auth", "url": url} # If App authorized, and user access accepted, We launch the synchronization need_refresh = request.env.user.sudo()._sync_google_calendar( GoogleCal) # If synchronization has been stopped if not need_refresh and request.env.user.google_synchronization_stopped: return {"status": "sync_stopped", "url": ''} return { "status": "need_refresh" if need_refresh else "no_new_event_from_google", "url": '' } return {"status": "success"}
def _google_insert(self, google_service: GoogleCalendarService, values, timeout=TIMEOUT): if not values: return with google_calendar_token(self.env.user.sudo()) as token: if token: google_id = google_service.insert(values, token=token, timeout=timeout) self.write({ 'google_id': google_id, 'need_sync': False, })
def _sync_all_google_calendar(self): """ Cron job """ users = self.env['res.users'].search([('google_calendar_rtoken', '!=', False), ('google_synchronization_stopped', '=', False)]) google = GoogleCalendarService(self.env['google.service']) for user in users: _logger.info("Calendar Synchro - Starting synchronization for %s", user) try: user.with_user(user).sudo()._sync_google_calendar(google) except Exception as e: _logger.exception("[%s] Calendar Synchro - Exception : %s !", user, exception_to_unicode(e))
def create(self, vals_list): if any(vals.get('google_id') for vals in vals_list): self._from_google_ids.clear_cache(self) records = super().create(vals_list) google_service = GoogleCalendarService(self.env['google.service']) records_to_sync = records.filtered(lambda r: r.need_sync and r.active) for record in records_to_sync: record._google_insert(google_service, record._google_values(), timeout=3) return records
def write(self, vals): google_service = GoogleCalendarService(self.env['google.service']) if 'google_id' in vals: self._from_google_ids.clear_cache(self) synced_fields = self._get_google_synced_fields() if 'need_sync' not in vals and vals.keys() & synced_fields: vals['need_sync'] = True result = super().write(vals) for record in self.filtered('need_sync'): if record.google_id: record._google_patch(google_service, record.google_id, record._google_values(), timeout=3) return result
def _google_insert(self, google_service: GoogleCalendarService, values, timeout=TIMEOUT): if not values: return with google_calendar_token(self.env.user.sudo()) as token: if token: try: google_id = google_service.insert(values, token=token, timeout=timeout) # Everything went smoothly self.with_context(dont_notify=True).write({ 'google_id': google_id, 'need_sync': False, }) except HTTPError as e: if e.response.status_code in (400, 403): self._google_error_handling(e) self.need_sync = False
def setUp(self): super().setUp() self.google_service = GoogleCalendarService(self.env['google.service']) # Make sure this test will work for the next 30 years self.env['ir.config_parameter'].set_param( 'google_calendar.sync.range_days', 10000)
def setUp(self): super().setUp() self.google_service = GoogleCalendarService(self.env['google.service'])
def _sync_event(self): google_service = GoogleCalendarService(self.env['google.service']) self.event_id.filtered(lambda e: e.google_id)._sync_odoo2google( google_service)