Skip to content
Extraits de code Groupes Projets
Sélectionner une révision Git
  • a09ba183b1591cb69979f2ed99ecb468ef20b4d0
  • 16.0 par défaut protégée
  • 18.0
  • 17.0
  • 14.0 protégée
  • 15.0 protégée
  • 12.0 protégée
  • 10.0 protégée
8 résultats

16.0.Dockerfile

Blame
  • calendar_event.py 14,30 Kio
    # Copyright 2021 Le Filament (https://le-filament.com)
    # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
    
    import binascii
    import logging
    import os
    
    from dateutil.parser import parse
    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
    from netbluemind.icalendar.api.ICalendarElementClassification import (
        ICalendarElementClassification,
    )
    from netbluemind.icalendar.api.ICalendarElementRRule import ICalendarElementRRule
    from netbluemind.icalendar.api.ICalendarElementRRuleFrequency import (
        ICalendarElementRRuleFrequency,
    )
    from netbluemind.icalendar.api.ICalendarElementRRuleWeekDay import (
        ICalendarElementRRuleWeekDay,
    )
    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__)
    
    PRIVACY_CONVERTER_B2O = {
        "Public": "public",
        "Private": "private",
        "Confidential": "confidential",
    }
    PRIVACY_CONVERTER_O2B = {
        "public": "Public",
        "private": "Private",
        "confidential": "Confidential",
    }
    
    
    # TODO: manage attendee_ids, organizer, alarm_ids, priority, status, categories ?
    # TODO: check if recurrency properly working without exceptions
    # TODO: manage exceptions in recurrencies
    class CalendarEvent(models.Model):
        _inherit = "calendar.event"
    
        """
        This inheriting class adds 1 field to calendar.event table :
          * bluemind_id = Unique identifier of event in Bluemind
        It also adds a number of methods to transform events between Bluemind and Odoo formats
        """
    
        bluemind_id = fields.Char("Bluemind Event ID")
    
        def _bm_to_odoo_values(self, bm_event):
            """
            This method converts a bluemind event (given as a parameter) into
            a dict that can later be used to create/update an event in Odoo
            """
            # Generic fields
            data = {
                "name": bm_event.value.main.summary,
                "bluemind_id": bm_event.uid,
                "privacy": PRIVACY_CONVERTER_B2O.get(
                    bm_event.value.main.classification.value,
                    self.default_get(["privacy"])["privacy"],
                ),
                "location": bm_event.value.main.location,
                "description": bm_event.value.main.description,
                "user_id": self.env.user.id,
                "create_uid": self.env.user.id,
            }
    
            # Dates handling, with timezones
            if bm_event.value.main.dtstart.precision.value == "Date":
                start = bm_event.value.main.dtstart.iso8601
                stop = bm_event.value.main.dtend.iso8601
                data.update({"allday": True, "start_date": start, "stop_date": stop})
            else:
                utc = timezone("UTC")
                start = (
                    parse(bm_event.value.main.dtstart.iso8601)
                    .astimezone(utc)
                    .replace(tzinfo=None)
                )
                stop = (
                    parse(bm_event.value.main.dtend.iso8601)
                    .astimezone(utc)
                    .replace(tzinfo=None)
                )
                data.update({"allday": False, "start": start, "stop": stop})
    
            # Recurrency handling
            rrule = bm_event.value.main.rrule
            if rrule:
                data.update(
                    {
                        "recurrency": True,
                        "rrule_type": rrule.frequency.name.lower(),
                        "interval": rrule.interval,
                    }
                )
                if rrule.count:
                    data.update({"end_type": "count", "count": rrule.count})
                elif rrule.until:
                    data.update({"end_type": "end_date", "until": rrule.until.iso8601[:10]})
                else:
                    data.update({"end_type": "forever"})
                if rrule.frequency.name == "MONTHLY":
                    if rrule.byDay:
                        data.update(
                            {
                                "month_by": "day",
                                "weekday": rrule.byDay[0].day,
                                rrule.byDay[0].day.lower(): True,
                                "byday": str(rrule.byDay[0].offset),
                            }
                        )
                    else:
                        data.update({"month_by": "date", "day": rrule.byMonthDay})
                elif rrule.frequency.name == "WEEKLY":
                    data.update(
                        {
                            "weekday": rrule.byDay[0].day,
                            rrule.byDay[0].day.lower(): True,
                        }
                    )
            # Returns data dictionary
            return data
    
        def _odoo_to_bm_values(self, event):
            """
            This method converts an Odoo event (given as a parameter) into
            a Bluemind event object that can then be used to create/update an event in Bluemind
            """
            # Event object initialization
            bm_event = VEventSeries()
            bm_event.main = VEvent()
            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
            bm_event.main.attendees = []
            bm_event.main.categories = []
            bm_event.main.attachments = []
    
            # Specific case for full day events
            if event.allday:
                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.main.dtend.iso8601 = stop_date.isoformat()
                bm_event.main.dtend.precision = BmDateTimePrecision("Date")
            # All other cases
            else:
                tz = timezone(self.env.context.get("tz"))
                bm_event.main.dtstart.iso8601 = event.start.astimezone(tz).isoformat(
                    timespec="milliseconds"
                )
                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.main.dtend.timezone = tz.zone
                bm_event.main.dtend.precision = BmDateTimePrecision("DateTime")
    
            # Recurrency Handling
            if event.recurrency:
                bm_event.main.rrule = ICalendarElementRRule()
                bm_event.main.rrule.frequency = ICalendarElementRRuleFrequency(
                    event.frequency.toupper()
                )
                bm_event.main.rrule.interval = event.interval
                if event.end_type == "count":
                    bm_event.main.rrule.count = event.count
                elif event.end_type == "end_date":
                    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.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.main.rrule.byMonthDay = [event.day]
    
            return bm_event
    
        def generate_uid_bluemind(self):
            """
            Creates a uid which corresponds to bluemind representation\
            (needed for all uplink creation)
            """
            uid = (
                binascii.b2a_hex(os.urandom(4))
                + b"-"
                + binascii.b2a_hex(os.urandom(2))
                + b"-"
                + binascii.b2a_hex(os.urandom(2))
                + b"-"
                + binascii.b2a_hex(os.urandom(2))
                + b"-"
                + binascii.b2a_hex(os.urandom(6))
            )
            return uid.decode()
    
        def update_odoo_event_from_bm(self, bm_event):
            """
            Update an Odoo event with fields from a Bluemind event (bm_event)
            """
            new_odoo_fields = self._bm_to_odoo_values(bm_event)
            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)
        def write(self, values, from_bluemind=False):
            """
            Overwrite of write() default method to update Bluemind event everytime
            an event is updated in Odoo
            A parameter is added "from_bluemind" to avoid updating a Bluemind event
            when change is already coming from Bluemind
            """
            # Calls base write() function first
            result = super().write(values)
    
            # AFAICT Bluemind needs all the fields to be provided,
            # you cannot send only the updated ones
            if result and not from_bluemind:
                # Create a list of Bluemind events to be updated
                bm_events = VEventChanges()
                bm_events.modify = []
                # Only update events with a bluemind_id
                for event in self.filtered("bluemind_id"):
                    if event.bluemind_id:
                        # Create a Bluemind event object and fills it with values
                        # from Odoo event
                        event_change = VEventChangesItemModify()
                        event_change.uid = event.bluemind_id
                        event_change.value = self._odoo_to_bm_values(event)
                        # Avoids sending notification from Bluemind
                        # (since assumed already sent from Odoo)
                        event_change.sendNotification = False
                        # Add Bluemind event to list
                        bm_events.modify.append(event_change)
                # If list of Bluemind events to be updated is not empty,
                # update these events in Bluemind
                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):
            """
            Overwrite of create() default method to create Bluemind event everytime
            an event is created in Odoo
            """
            # Calls base create() function first
            odoo_events = super().create(vals_list)
            self.create_odoo_events_in_bm()
            return odoo_events
    
        def unlink(self, from_bluemind=False):
            """
            Overwrite of unlink() default method to delete Bluemind event everytime
            an event is deleted in Odoo
            A parameter is added "from_bluemind" to avoid deleting a Bluemind event
            when deletion is already coming from Bluemind
            """
            if not from_bluemind:
                for event in self:
                    # If Odoo event is linked to a Bluemind event
                    if event.bluemind_id:
                        bm_calendar = self.env.user.bluemind_auth().calendar(
                            self.env.user.bluemind_calendar_id
                        )
                        try:
                            bm_calendar.delete(event.bluemind_id, False)
                        except (ServerFault):
                            # TODO: better manage exceptions
                            # If deletion fails, keep event in Odoo but archive it
                            event.active = False
            # Calls base unlink() function last only on active events
            # (to avoid deleting the Odoo event in case it was just archived)
            return super(CalendarEvent, self.filtered(lambda e: e.active is True)).unlink()