From 4208fdddf0f691ad885658f929eb757d319592e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20-=20Le=20Filament?= <remi@le-filament.com> Date: Thu, 28 Oct 2021 19:17:56 +0200 Subject: [PATCH] [ADD] write on odoo event pushes to Bluemind --- __manifest__.py | 3 +- data/cron_data.xml | 16 ++++++ models/calendar_event.py | 118 +++++++++++++++++++++++--------------- models/res_users.py | 27 ++++++++- views/res_users_views.xml | 30 +++++++--- 5 files changed, 139 insertions(+), 55 deletions(-) create mode 100644 data/cron_data.xml diff --git a/__manifest__.py b/__manifest__.py index 545080a..5d83725 100644 --- a/__manifest__.py +++ b/__manifest__.py @@ -9,8 +9,9 @@ "calendar", ], "data": [ - #"security/ir.model.access.csv", + # "security/ir.model.access.csv", # datas + "data/cron_data.xml", # views "views/res_company_views.xml", "views/res_users_views.xml", diff --git a/data/cron_data.xml b/data/cron_data.xml new file mode 100644 index 0000000..b62470e --- /dev/null +++ b/data/cron_data.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<odoo> + <data> + <record forcecreate="True" id="ir_cron_sync_all_bm_calendars" model="ir.cron"> + <field name="name">Bluemind: synchronization</field> + <field name="model_id" ref="model_res_users" /> + <field name="state">code</field> + <field name="code">model._sync_all_bm_calendar()</field> + <field name="user_id" ref="base.user_root" /> + <field name="interval_number">4</field> + <field name="interval_type">hours</field> + <field name="numbercall">-1</field> + <field eval="False" name="doall" /> + </record> + </data> +</odoo> diff --git a/models/calendar_event.py b/models/calendar_event.py index 3a21af5..6312d65 100644 --- a/models/calendar_event.py +++ b/models/calendar_event.py @@ -10,6 +10,7 @@ from dateutil.relativedelta import relativedelta from netbluemind.calendar.api.VEvent import VEvent from netbluemind.calendar.api.VEventChanges import VEventChanges from netbluemind.calendar.api.VEventChangesItemAdd import VEventChangesItemAdd +from netbluemind.calendar.api.VEventChangesItemModify import VEventChangesItemModify from netbluemind.calendar.api.VEventSeries import VEventSeries from netbluemind.core.api.date.BmDateTime import BmDateTime from netbluemind.core.api.date.BmDateTimePrecision import BmDateTimePrecision @@ -27,6 +28,7 @@ from netbluemind.python.client import ServerFault from pytz import timezone from odoo import api, fields, models +from odoo.loglevels import exception_to_unicode _logger = logging.getLogger(__name__) @@ -42,6 +44,8 @@ PRIVACY_CONVERTER_O2B = { } +# TODO: manage attendee_ids, alarm_ids ? +# TODO: check if recurrency prperly working class CalendarEvent(models.Model): _inherit = "calendar.event" @@ -120,71 +124,66 @@ class CalendarEvent(models.Model): return data def _odoo_to_bm_values(self, event): - bm_event = VEventChangesItemAdd() - bm_event.uid = self.generate_uid_bluemind() - bm_event.value = VEventSeries() - bm_event.value.main = VEvent() - bm_event.value.main.classification = ICalendarElementClassification( + bm_event = VEventSeries() + bm_event.main = VEvent() + bm_event.main.classification = ICalendarElementClassification( PRIVACY_CONVERTER_O2B.get(event.privacy) ) - bm_event.value.main.dtstart = BmDateTime() - bm_event.value.main.dtend = BmDateTime() - bm_event.sendNotification = False + bm_event.main.dtstart = BmDateTime() + bm_event.main.dtend = BmDateTime() - bm_event.value.main.summary = event.name - bm_event.value.main.description = event.description or "" - bm_event.value.main.location = event.location or "" + bm_event.main.summary = event.name + bm_event.main.description = event.description or "" + bm_event.main.location = event.location or "" # These fields are required (although not marked as such in doc / code) # Otherwise you get a NullPointerException - bm_event.value.main.attendees = [] - bm_event.value.main.categories = [] - bm_event.value.main.attachments = [] + bm_event.main.attendees = [] + bm_event.main.categories = [] + bm_event.main.attachments = [] if event.allday: - bm_event.value.main.dtstart.iso8601 = event.start_date.isoformat() - bm_event.value.main.dtstart.precision = BmDateTimePrecision("Date") + bm_event.main.dtstart.iso8601 = event.start_date.isoformat() + bm_event.main.dtstart.precision = BmDateTimePrecision("Date") stop_date = event.stop_date + relativedelta(days=1) - bm_event.value.main.dtend.iso8601 = stop_date.isoformat() - bm_event.value.main.dtend.precision = BmDateTimePrecision("Date") + bm_event.main.dtend.iso8601 = stop_date.isoformat() + bm_event.main.dtend.precision = BmDateTimePrecision("Date") else: tz = timezone(self.env.context.get("tz")) - bm_event.value.main.dtstart.iso8601 = event.start.astimezone(tz).isoformat( + bm_event.main.dtstart.iso8601 = event.start.astimezone(tz).isoformat( timespec="milliseconds" ) - bm_event.value.main.dtstart.timezone = tz.zone - bm_event.value.main.dtstart.precision = BmDateTimePrecision("DateTime") - bm_event.value.main.dtend.iso8601 = event.stop.astimezone(tz).isoformat( + bm_event.main.dtstart.timezone = tz.zone + bm_event.main.dtstart.precision = BmDateTimePrecision("DateTime") + bm_event.main.dtend.iso8601 = event.stop.astimezone(tz).isoformat( timespec="milliseconds" ) - bm_event.value.main.dtend.timezone = tz.zone - bm_event.value.main.dtend.precision = BmDateTimePrecision("DateTime") + bm_event.main.dtend.timezone = tz.zone + bm_event.main.dtend.precision = BmDateTimePrecision("DateTime") # Recurrency Handling if event.recurrency: - bm_event.value.main.rrule = ICalendarElementRRule() - bm_event.value.main.rrule.frequency = ICalendarElementRRuleFrequency( + bm_event.main.rrule = ICalendarElementRRule() + bm_event.main.rrule.frequency = ICalendarElementRRuleFrequency( event.frequency.toupper() ) - bm_event.value.main.rrule.interval = event.interval + bm_event.main.rrule.interval = event.interval if event.end_type == "count": - bm_event.value.main.rrule.count = event.count + bm_event.main.rrule.count = event.count elif event.end_type == "end_date": - bm_event.value.main.rrule.until = BmDateTime() - bm_event.value.main.rrule.until.iso8601 = event.until.strftime( - "%Y-%m-%d" - ) - bm_event.value.main.rrule.until.precision = BmDateTimePrecision("Date") - bm_event.value.main.rrule.until.timezone = event.event_tz + bm_event.main.rrule.until = BmDateTime() + bm_event.main.rrule.until.iso8601 = event.until.strftime("%Y-%m-%d") + bm_event.main.rrule.until.precision = BmDateTimePrecision("Date") + bm_event.main.rrule.until.timezone = event.event_tz if ( event.frequency == "monthly" and event.month_by == "day" ) or event.frequency == "weekly": - bm_event.value.main.rrule.byDay = [ICalendarElementRRuleWeekDay()] - bm_event.value.main.rrule.byDay[0].day = str(event.weekday) - bm_event.value.main.rrule.byDay[0].offset = int(event.byday) + bm_event.main.rrule.byDay = [ICalendarElementRRuleWeekDay()] + bm_event.main.rrule.byDay[0].day = str(event.weekday) + bm_event.main.rrule.byDay[0].offset = int(event.byday) elif event.frequency == "monthly" and event.month_by == "date": - bm_event.value.main.rrule.byMonthDay = [event.day] + bm_event.main.rrule.byMonthDay = [event.day] return bm_event @@ -208,13 +207,38 @@ class CalendarEvent(models.Model): # TODO: Create function to update Odoo event from Bluemind updated event def update_odoo_event_from_bm(self, odoo_event, bm_event): - return NotImplementedError + return NotImplementedError() # TODO: add function to update Bluemind object when Odoo object is modified # Take caution about attendees (only one calendar event in Odoo for all attendees, # when in Bluemind you get a different one per attendee) - def write(self, values): - return super().write(values) + def write(self, values, from_bluemind=False): + # For now this function pushes again the full event to bluemind, + # TODO: check if this could be improved to push only a few fields ? + result = super().write(values) + if result and not from_bluemind: + bm_events = VEventChanges() + bm_events.modify = [] + for event in self.filtered("bluemind_id"): + if event.bluemind_id: + event_change = VEventChangesItemModify() + event_change.uid = event.bluemind_id + event_change.value = self._odoo_to_bm_values(event) + event_change.sendNotification = False + bm_events.modify.append(event_change) + if bm_events.modify: + 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), + ) + return result @api.model_create_multi def create(self, vals_list): @@ -223,9 +247,12 @@ class CalendarEvent(models.Model): bm_events.add = [] for odoo_event in odoo_events: if not odoo_event.bluemind_id: - bm_event = self._odoo_to_bm_values(odoo_event) - bm_events.add.append(bm_event) - odoo_event.bluemind_id = bm_event.uid + event_add = VEventChangesItemAdd() + event_add.uid = self.generate_uid_bluemind() + event_add.value = self._odoo_to_bm_values(odoo_event) + event_add.sendNotification = False + bm_events.add.append(event_add) + odoo_event.write({"bluemind_id": event_add.uid}, from_bluemind=True) if bm_events.add: try: bm_calendar = self.env.user.bluemind_auth().calendar( @@ -235,7 +262,8 @@ class CalendarEvent(models.Model): except (ServerFault, Exception) as e: # TODO: better manage exceptions _logger.warning( - "Did not manage to push events to Bluemind, error [%s]", e + "Did not manage to push events to Bluemind, error [%s]", + exception_to_unicode(e), ) return odoo_events diff --git a/models/res_users.py b/models/res_users.py index 0bd3044..7d12c2c 100644 --- a/models/res_users.py +++ b/models/res_users.py @@ -7,6 +7,7 @@ from netbluemind.python.client import BMClient, ServerFault from odoo import _, api, fields, models from odoo.exceptions import UserError +from odoo.loglevels import exception_to_unicode _logger = logging.getLogger(__name__) @@ -45,10 +46,17 @@ class ResUser(models.Model): self.is_bm_connection_ok = False if force: raise UserError( - _("Something went wrong during connection : [%s]", e) + _( + "Something went wrong during connection : [%s]", + exception_to_unicode(e), + ) ) return False + def force_auth(self): + # TODO : add notification that it works or not ! + return self.bluemind_auth(force=True) + @api.depends("company_id.bluemind_domain", "bluemind_login", "bluemind_password") def _compute_bluemind_user_id(self): for user in self: @@ -70,7 +78,7 @@ class ResUser(models.Model): if user.bluemind_user_id: user.bluemind_calendar_id = "calendar:Default:" + user.bluemind_user_id - def bluemind_sync(self): + def sync_bluemind_calendar(self): self.ensure_one() # TODO: add checks and error handling # Retrieve all events modified since last sync (followed by self.bluemind_id_version) @@ -115,3 +123,18 @@ class ResUser(models.Model): odoo_events_to_delete.unlink(from_bluemind=True) self.last_sync_version = bm_last_version + + @api.model + def _sync_all_bm_calendar(self): + """ Cron job """ + users = self.env["res.users"].search([("is_bm_connection_ok", "!=", False)]) + for user in users: + _logger.info("Calendar Synchro - Starting synchronization for %s", user) + try: + user.with_user(user).sudo().sync_bluemind_calendar() + except Exception as e: + _logger.exception( + "[%s] Calendar Synchro - Exception : %s !", + user, + exception_to_unicode(e), + ) diff --git a/views/res_users_views.xml b/views/res_users_views.xml index 0c12d17..d577483 100644 --- a/views/res_users_views.xml +++ b/views/res_users_views.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="UTF-8"?> +<?xml version="1.0" encoding="UTF-8" ?> <!-- Copyright 2021 Le Filament License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). --> <odoo> @@ -6,16 +6,32 @@ <record id="view_users_form" model="ir.ui.view"> <field name="name">res.users.form</field> <field name="model">res.users</field> - <field name="inherit_id" ref="base.view_users_form"/> + <field name="inherit_id" ref="base.view_users_form_simple_modif" /> <field name="arch" type="xml"> - <notebook colspan="4" position="inside"> + <notebook position="inside"> <page string="Bluemind Calendar" name="bm_calendar"> <group class="text-break"> - <field name="bluemind_login"/> - <field name="bluemind_password" password="True"/> - <field name="bluemind_user_id" readonly="True"/> - <field name="bluemind_calendar_id" readonly="True"/> + <field name="bluemind_login" /> + <field name="bluemind_password" password="True" /> + <field name="bluemind_user_id" readonly="True" /> + <field name="bluemind_calendar_id" readonly="True" /> + <field name="is_bm_connection_ok" readonly="True" /> </group> + <button + string="Force authentication" + class="btn btn-secondary" + type="object" + name="force_auth" + context="{'default_user_id': id}" + /> + <button + string="Sync calendar" + class="btn btn-primary" + type="object" + name="sync_bluemind_calendar" + attrs="{'invisible': [('is_bm_connection_ok', '=', False)]}" + context="{'default_user_id': id}" + /> </page> </notebook> </field> -- GitLab