From 11ba5f7160cd13f3bc1824bca5ec637730f926e7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9mi=20-=20Le=20Filament?= <remi@le-filament.com>
Date: Tue, 26 Apr 2022 17:20:02 +0200
Subject: [PATCH] [ADD] initial sync from Odoo

---
 models/calendar_event.py | 89 ++++++++++++++++++++++------------------
 models/res_users.py      | 31 ++++++++++----
 2 files changed, 71 insertions(+), 49 deletions(-)

diff --git a/models/calendar_event.py b/models/calendar_event.py
index 46220a1..d566b76 100644
--- a/models/calendar_event.py
+++ b/models/calendar_event.py
@@ -83,16 +83,15 @@ class CalendarEvent(models.Model):
             stop = bm_event.value.main.dtend.iso8601
             data.update({"allday": True, "start_date": start, "stop_date": stop})
         else:
-            timeZone_start = timezone(bm_event.value.main.dtstart.timezone)
-            timeZone_stop = timezone(bm_event.value.main.dtend.timezone)
+            utc = timezone("UTC")
             start = (
                 parse(bm_event.value.main.dtstart.iso8601)
-                .astimezone(timeZone_start)
+                .astimezone(utc)
                 .replace(tzinfo=None)
             )
             stop = (
                 parse(bm_event.value.main.dtend.iso8601)
-                .astimezone(timeZone_stop)
+                .astimezone(utc)
                 .replace(tzinfo=None)
             )
             data.update({"allday": False, "start": start, "stop": stop})
@@ -143,15 +142,16 @@ class CalendarEvent(models.Model):
         # Event object initialization
         bm_event = VEventSeries()
         bm_event.main = VEvent()
-        bm_event.main.classification = ICalendarElementClassification(
-            PRIVACY_CONVERTER_O2B.get(event.privacy)
-        )
         bm_event.main.dtstart = BmDateTime()
         bm_event.main.dtend = BmDateTime()
 
+        # Event generic information
         bm_event.main.summary = event.name
         bm_event.main.description = event.description or ""
         bm_event.main.location = event.location or ""
+        bm_event.main.classification = ICalendarElementClassification(
+            PRIVACY_CONVERTER_O2B.get(event.privacy)
+        )
 
         # These fields are required (although not marked as such in doc / code)
         # Otherwise you get a NullPointerException
@@ -224,12 +224,49 @@ class CalendarEvent(models.Model):
         )
         return uid.decode()
 
-    def update_odoo_event_from_bm(self, odoo_event, bm_event):
+    def update_odoo_event_from_bm(self, bm_event):
         """
-        Update an Odoo event (odoo_event) with fields from a Bluemind event (bm_event)
+        Update an Odoo event with fields from a Bluemind event (bm_event)
         """
         new_odoo_fields = self._bm_to_odoo_values(bm_event)
-        odoo_event.write(new_odoo_fields)
+        self.write(new_odoo_fields)
+
+    def create_odoo_events_in_bm(self):
+        """
+        Create events from Odoo (odoo_events) in Bluemind
+        """
+        # Create a list of Bluemind events to be created
+        bm_events = VEventChanges()
+        bm_events.add = []
+        for odoo_event in self:
+            # Avoid recreating events if already linked to Bluemind event
+            if not odoo_event.bluemind_id:
+                # Create a Bluemind event object and fills it with values
+                # from Odoo event
+                event_add = VEventChangesItemAdd()
+                event_add.uid = self.generate_uid_bluemind()
+                event_add.value = self._odoo_to_bm_values(odoo_event)
+                # Avoids sending notification from Bluemind
+                # (since assumed already sent from Odoo)
+                event_add.sendNotification = False
+                # Add Bluemind event to list
+                bm_events.add.append(event_add)
+                # Set bluemind_id on Odoo event (without pushing to Bluemind)
+                odoo_event.write({"bluemind_id": event_add.uid}, from_bluemind=True)
+        # If list of Bluemind events to be created is not empty,
+        # create these events in Bluemind
+        if bm_events.add:
+            try:
+                bm_calendar = self.env.user.bluemind_auth().calendar(
+                    self.env.user.bluemind_calendar_id
+                )
+                bm_calendar.updates(bm_events)
+            except (ServerFault, Exception) as e:
+                # TODO: better manage exceptions
+                _logger.warning(
+                    "Did not manage to push events to Bluemind, error [%s]",
+                    exception_to_unicode(e),
+                )
 
     # Take caution about attendees (only one calendar event in Odoo for all attendees,
     # when in Bluemind you get a different one per attendee)
@@ -286,37 +323,7 @@ class CalendarEvent(models.Model):
         """
         # Calls base create() function first
         odoo_events = super().create(vals_list)
-        # Create a list of Bluemind events to be created
-        bm_events = VEventChanges()
-        bm_events.add = []
-        for odoo_event in odoo_events:
-            if not odoo_event.bluemind_id:
-                # Create a Bluemind event object and fills it with values
-                # from Odoo event
-                event_add = VEventChangesItemAdd()
-                event_add.uid = self.generate_uid_bluemind()
-                event_add.value = self._odoo_to_bm_values(odoo_event)
-                # Avoids sending notification from Bluemind
-                # (since assumed already sent from Odoo)
-                event_add.sendNotification = False
-                # Add Bluemind event to list
-                bm_events.add.append(event_add)
-                # Set bluemind_id on Odoo event (without pushing to Bluemind)
-                odoo_event.write({"bluemind_id": event_add.uid}, from_bluemind=True)
-        # If list of Bluemind events to be created is not empty,
-        # create these events in Bluemind
-        if bm_events.add:
-            try:
-                bm_calendar = self.env.user.bluemind_auth().calendar(
-                    self.env.user.bluemind_calendar_id
-                )
-                bm_calendar.updates(bm_events)
-            except (ServerFault, Exception) as e:
-                # TODO: better manage exceptions
-                _logger.warning(
-                    "Did not manage to push events to Bluemind, error [%s]",
-                    exception_to_unicode(e),
-                )
+        self.create_odoo_events_in_bm()
         return odoo_events
 
     def unlink(self, from_bluemind=False):
diff --git a/models/res_users.py b/models/res_users.py
index bed8e98..b6cc78e 100644
--- a/models/res_users.py
+++ b/models/res_users.py
@@ -80,7 +80,7 @@ class ResUser(models.Model):
         # TODO : add notification that it works or not
         return self.bluemind_auth(force=True)
 
-    @api.depends("company_id.bluemind_domain", "bluemind_login", "bluemind_password")
+    @api.depends("company_id.bluemind_domain", "bluemind_login", "bluemind_password", "is_bm_connection_ok")
     def _compute_bluemind_user_id(self):
         """
         This method retrieves the user id from Bluemind client object
@@ -90,13 +90,16 @@ class ResUser(models.Model):
                 user.company_id.bluemind_url
                 and user.bluemind_login
                 and user.bluemind_password
+                and user.is_bm_connection_ok
             ):
-                user.bluemind_user_id = (
-                    user.bluemind_auth()
-                    .directory(user.company_id.bluemind_domain)
-                    .getByEmail(user.bluemind_login)
-                    .entryUid
-                )
+                client = user.bluemind_auth()
+                if client:
+                    user.bluemind_user_id = (
+                        client
+                        .directory(user.company_id.bluemind_domain)
+                        .getByEmail(user.bluemind_login)
+                        .entryUid
+                    )
 
     @api.depends("bluemind_user_id")
     def _compute_bluemind_calendar_uid(self):
@@ -162,7 +165,7 @@ class ResUser(models.Model):
             )
             # If event exists in Odoo update it
             if odoo_event:
-                Calendar.update_odoo_event_from_bm(odoo_event, bm_event)
+                odoo_event.update_odoo_event_from_bm(bm_event)
             # Otherwise log an error
             else:
                 _logger.error(
@@ -178,6 +181,18 @@ class ResUser(models.Model):
         # Update user last_sync_version with the latest one retrieved from Bluemind
         self.last_sync_version = bm_last_version
 
+        # TODO : sync all Odoo events without bluemind_id
+        # Should we sync only events on which user_id is the current user
+        # Or also all events to which he was invited as participant ?
+        # First we would sync only the ones where he is user_id
+        # (because otherwise you would set bluemind_id on an event which
+        # does not belong to the current user, and may have already been synced
+        # - with different bluemind_id by the organizer (user_id))
+        odoo_events_no_bm = Calendar.search(
+            [("user_id", "=", self.id), ("bluemind_id", "=", False)]
+        )
+        odoo_event_no_bm.create_odoo_events_in_bm()
+
     @api.model
     def _sync_all_bm_calendar(self):
         """
-- 
GitLab