[IMP] google_calendar: add single event synchronization

Purpose
=======
Add the possibility to only synchronize one specific event of a user calendar instead of syncing the whole calendar.

See sub commits for more details.

Task-3285601

closes odoo/odoo#133921

Related: odoo/enterprise#46688
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
Odoo's Mergebot
2023-09-22 11:55:51 +00:00
committed by Odoo Online
3 changed files with 70 additions and 18 deletions
@@ -14,10 +14,13 @@ class Meeting(models.Model):
_name = 'calendar.event'
_inherit = ['calendar.event', 'google.calendar.sync']
MEET_ROUTE = 'meet.google.com'
google_id = fields.Char(
'Google Calendar Event Id', compute='_compute_google_id', store=True, readonly=False)
guests_readonly = fields.Boolean(
'Guests Event Modification Permission', default=False)
videocall_source = fields.Selection(selection_add=[('google_meet', 'Google Meet')], ondelete={'google_meet': 'set discuss'})
@api.depends('recurrence_id.google_id')
def _compute_google_id(self):
@@ -32,6 +35,12 @@ class Meeting(models.Model):
elif not event.google_id:
event.google_id = False
@api.depends('videocall_location')
def _compute_videocall_source(self):
events_with_google_url = self.filtered(lambda event: self.MEET_ROUTE in (event.videocall_location or ''))
events_with_google_url.videocall_source = 'google_meet'
super(Meeting, self - events_with_google_url)._compute_videocall_source()
@api.model
def _get_google_synced_fields(self):
return {'name', 'description', 'allday', 'start', 'date_end', 'stop',
+53 -17
View File
@@ -53,24 +53,10 @@ class User(models.Model):
def _sync_google_calendar(self, calendar_service: GoogleCalendarService):
self.ensure_one()
if self._get_google_sync_status() != "sync_active":
results = self._sync_request(calendar_service)
if not results or not results.get('events'):
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_calendar_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_calendar_account_id.calendar_sync_token = next_sync_token
events, default_reminders, full_sync = results.values()
# Google -> Odoo
send_updates = not full_sync
events.clear_type_ambiguity(self.env)
@@ -89,6 +75,50 @@ class User(models.Model):
return bool(events | synced_events) or bool(recurrences | synced_recurrences)
def _sync_single_event(self, calendar_service: GoogleCalendarService, odoo_event, event_id):
self.ensure_one()
results = self._sync_request(calendar_service, event_id)
if not results or not results.get('events'):
return False
event, default_reminders, full_sync = results.values()
# Google -> Odoo
send_updates = not full_sync
event.clear_type_ambiguity(self.env)
synced_events = self.env['calendar.event']._sync_google2odoo(event, default_reminders=default_reminders)
# Odoo -> Google
odoo_event.with_context(send_updates=send_updates)._sync_odoo2google(calendar_service)
return bool(odoo_event | synced_events)
def _sync_request(self, calendar_service, event_id=None):
if self._get_google_sync_status() != "sync_active":
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:
if not event_id:
events, next_sync_token, default_reminders = calendar_service.get_events(self.google_calendar_account_id.calendar_sync_token, token=token)
else:
# We force the sync_token parameter to avoid doing a full sync.
# Other events are fetched when the calendar view is displayed.
events, next_sync_token, default_reminders = calendar_service.get_events(sync_token=token, token=token, event_id=event_id)
except InvalidSyncToken:
events, next_sync_token, default_reminders = calendar_service.get_events(token=token)
full_sync = True
if next_sync_token:
self.google_calendar_account_id.calendar_sync_token = next_sync_token
return {
'events': events,
'default_reminders': default_reminders,
'full_sync': full_sync,
}
@api.model
def _sync_all_google_calendar(self):
""" Cron job """
@@ -103,6 +133,12 @@ class User(models.Model):
_logger.exception("[%s] Calendar Synchro - Exception : %s!", user, exception_to_unicode(e))
self.env.cr.rollback()
def is_google_calendar_synced(self):
""" True if Google Calendar settings are filled (Client ID / Secret) and user calendar is synced
meaning we can make API calls, false otherwise."""
self.ensure_one()
return self.google_calendar_token and self._get_google_sync_status() == 'sync_active'
def stop_google_synchronization(self):
self.ensure_one()
self.google_synchronization_stopped = True
@@ -29,8 +29,10 @@ class GoogleCalendarService():
self.google_service = google_service
@requires_auth_token
def get_events(self, sync_token=None, token=None, timeout=TIMEOUT):
def get_events(self, sync_token=None, token=None, event_id=None, timeout=TIMEOUT):
url = "/calendar/v3/calendars/primary/events"
if event_id:
url += f"/{event_id}"
headers = {'Content-type': 'application/json'}
params = {'access_token': token}
if sync_token:
@@ -51,6 +53,11 @@ class GoogleCalendarService():
raise InvalidSyncToken("Invalid sync token. Full sync required")
raise e
if event_id:
next_sync_token = None
default_reminders = ()
return GoogleEvent([data]), next_sync_token, default_reminders
events = data.get('items', [])
next_page_token = data.get('nextPageToken')
while next_page_token: