diff --git a/__manifest__.py b/__manifest__.py
index 545080a5ddb6c781e200521c69e5b55c0c6369b2..5d837254cedf7afd5e357b53c5e0667838b3b919 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 0000000000000000000000000000000000000000..b62470e689bf8d3d77f38182170fb5235ae2beb5
--- /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 3a21af54e9ac9e1289e4d48249fefeae78a0c6ce..6312d657a52997017079e99f0e247f55167a2e47 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 0bd3044e456f2da86c0ccfe8949d4feeedaabb9b..7d12c2c7a9eff51bf683b5f1e558b706b6c9bfbf 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 0c12d1795f5adc1a80ccde8362c7f61b9db85097..d577483aa0b6ad02872bdc1b25d814b588c90a98 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>