Sélectionner une révision Git
scop_contribution_report.py
Bifurcation depuis
Le Filament / Confédération Générale des SCOP / cgscop_cotisation
Le projet source a une visibilité limitée.
-
Benjamin - Le Filament a rédigéBenjamin - Le Filament a rédigé
res_users.py 10,48 Kio
# Copyright 2021 Le Filament (https://le-filament.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
import logging
from netbluemind.calendar.api.ICalendarUids import ICalendarUids
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__)
class ResUser(models.Model):
_inherit = "res.users"
"""
This inheriting class adds some fields and methods to res.users in order to store
information for connecting user to Bluemind :
* bluemind_login / bluemind_password
* is_bm_connection_ok : boolean set when connection was successful
* bluemind_user_id : to store the identifier of the user in Bluemind
* bluemind_calendar_id : to store the main calendar id linked to user
* last_sync_version : last version of calendar synced
(Bluemind increments a versionning sequence at each change on calendar)
"""
bluemind_login = fields.Char("Bluemind e-mail")
bluemind_password = fields.Char("Bluemind password or API key")
is_bm_connection_ok = fields.Boolean("Bluemind Connection OK", default=False)
bluemind_user_id = fields.Char(
"Bluemind user ID", compute="_compute_bluemind_user_id", store=True
)
bluemind_calendar_id = fields.Char(
"Bluemind calendar ID", compute="_compute_bluemind_calendar_uid", store=True
)
last_sync_version = fields.Integer("Last sync version", readonly=True, default=0)
@api.depends("company_id.bluemind_url", "bluemind_login", "bluemind_password")
def bluemind_auth(self, force=False):
"""
This method connect to Bluemind API and returns the client object
This authentication is only performed if all the above conditions are met:
* bluemind_url is set on company
* bluemind_login and bluemind_password are set on user
* connection was already successful with provided creds or force
parameter is given (allowing to make first auth) - this last condition
is here to avoid locking the account in case the creds are incorrect,
since this method is used in most other methods to retrieve client object
"""
self.ensure_one()
if (
self.company_id.bluemind_url
and self.bluemind_login
and self.bluemind_password
and (self.is_bm_connection_ok or force)
):
try:
client = BMClient(self.company_id.bluemind_url + "/api")
client.login(self.bluemind_login, self.bluemind_password)
self.is_bm_connection_ok = True
return client
# TODO: better manage exception
except (ServerFault, Exception) as e:
self.is_bm_connection_ok = False
if force:
raise UserError(
_(
"Something went wrong during connection : [%s]",
exception_to_unicode(e),
)
)
return False
def force_auth(self):
"""
This methods force authentication (or re-auth) mechanism with creds stored on user
It is called from button on user view
"""
# TODO : add notification that it works or not
return self.bluemind_auth(force=True)
@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
"""
for user in self:
if (
user.company_id.bluemind_url
and user.bluemind_login
and user.bluemind_password
and user.is_bm_connection_ok
):
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):
"""
This method retrieves default calendar uid from Bluemind for the user
"""
for user in self:
client_bm = user.bluemind_auth()
if client_bm and user.bluemind_user_id:
user.bluemind_calendar_id = ICalendarUids(
client_bm.apiKey, client_bm.baseUrl
).getDefaultUserCalendar(user.bluemind_user_id)
def sync_bluemind_calendar(self):
"""
This methods retrieves last calendar changes from Bluemind
These charges are grouped in 3 objects :
* created : all events created since last sync
* updated : all events updated since last sync
* deleted : all events deleted since last sync
Then it creates/updates/deletes Odoo events accordingly
The parti pris here is the following : every event in Bluemind calendar
is recreated in Odoo with organizer = user
"""
self.ensure_one()
# Retrieve emails from all internal & active Odoo users
odoo_users_emails = (
self.sudo()
.env["res.users"]
.search([("share", "=", False), ("active", "=", True)])
.mapped("partner_id.email")
)
# TODO: add checks and error handling
# Retrieve all events modified since last sync (followed by self.bluemind_id_version)
bm_calendar = self.bluemind_auth().calendar(self.bluemind_calendar_id)
# TODO : avoid retrieving all events from beginning, limit to 3 months in past ?
# The following function actually retrieves only Blemind event ids, not the full events
bm_changeset = bm_calendar.changeset(self.last_sync_version)
bm_created_uids = bm_changeset.created
bm_updated_uids = bm_changeset.updated
bm_deleted_uids = bm_changeset.deleted
bm_last_version = bm_changeset.version
# Retrieve all events from Odoo with a bluemind_id
# (= already synced at least once with Bluemind)
Calendar = self.env["calendar.event"]
odoo_events_bm_linked = Calendar.search([("bluemind_id", "!=", False)])
odoo_events_bm_uids = odoo_events_bm_linked.mapped("bluemind_id")
# Calendar entries created on Bluemind side, not already in Odoo
# The events already in Odoo are ignored (probably already synced previously
if bm_created_uids:
events_to_create = []
bm_events_to_create_uids = [
e for e in bm_created_uids if e not in odoo_events_bm_uids
]
# Retrieve remaining full events from Bluemind
bm_events_to_create = bm_calendar.multipleGet(bm_events_to_create_uids)
# Create corresponding events in Odoo
for bm_event in bm_events_to_create:
# Only if Organizer is the same as Odoo user or if is not linked to
# any active Odoo internal user
if (
bm_event.value.main.organizer
and (bm_event.value.main.organizer.mailto == self.partner_id.email
or bm_event.value.main.organizer.mailto not in odoo_users_emails)
):
events_to_create.append(Calendar._bm_to_odoo_values(bm_event))
if events_to_create:
Calendar.create(events_to_create)
# Calendar entries that have been updated on Bluemind
# Retrieve full events from Bluemind
if bm_updated_uids:
bm_events_to_update = bm_calendar.multipleGet(bm_updated_uids)
# Update corresponding events
for bm_event in bm_events_to_update:
odoo_event = odoo_events_bm_linked.filtered_domain(
[("bluemind_id", "=", bm_event.uid)]
)
# If related event exists in Odoo and belongs to the user, update it
if odoo_event:
if odoo_event.user_id == self:
odoo_event.update_odoo_event_from_bm(bm_event)
# If event does not exist in Odoo log an info
else:
_logger.info(
"Event %s updated in Bluemind does not exist in Odoo", bm_event.uid
)
# Calendar entries that have been deleted on Bluemind to be deleted on Odoo
# (only if belongs to user)
if bm_deleted_uids:
odoo_events_to_delete = odoo_events_bm_linked.filtered_domain(
[("bluemind_id", "in", bm_deleted_uids), ("user_id", "=", self.id)]
)
if odoo_events_to_delete:
odoo_events_to_delete.unlink(from_bluemind=True)
# 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_events_no_bm.create_odoo_events_in_bm()
@api.model
def _sync_all_bm_calendar(self):
"""
Cron job to sync calendars for all users with is_bm_connection_ok
"""
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).sync_bluemind_calendar()
_logger.info(
"Calendar Synchro - Completed synchronization for %s", user
)
except Exception as e:
_logger.exception(
"[%s] Calendar Synchro - Exception : %s !",
user,
exception_to_unicode(e),
)