From 3350d83d94df78670b38960d466e868f76fa074d Mon Sep 17 00:00:00 2001 From: Fabien BOURGEOIS Date: Thu, 10 May 2018 17:31:04 +0200 Subject: [PATCH] [ADD][WIP]Radicale Odoo Storage : add calendar unique events display --- radicale_odoo_storage/__init__.py | 85 ++++++++++++++++++++++++------- 1 file changed, 66 insertions(+), 19 deletions(-) diff --git a/radicale_odoo_storage/__init__.py b/radicale_odoo_storage/__init__.py index 638a83e..864cfaa 100644 --- a/radicale_odoo_storage/__init__.py +++ b/radicale_odoo_storage/__init__.py @@ -18,16 +18,18 @@ """ Odoo Radicale Storage Plugin """ # # PLAN -# 1. Implement readonly from Odoo only -# 2. Implement contacts first (and so vcf) -# 3. Implement unique events (.ics) -# 4. Implement recurrent events -# 5. Begin write (two way) for contacts -# 5. Begin write (two way) for calendar +# 1. OK : Implement readonly from Odoo only +# 2. OK : Implement contacts first (and so vcf) +# 3. OK : Implement unique events (.ics) and timezone +# 4. Implement notifications for events +# 5. Implement recurrent events +# 6. Begin write (two way) for contacts +# 7. Begin write (two way) for calendar from contextlib import contextmanager from time import strftime, strptime +import pytz import vobject from odoorpc import ODOO from odoorpc.error import RPCError @@ -47,8 +49,10 @@ class Collection(BaseCollection): if not self.__class__.odoo: self.logger.error('No auth Odoo found...') raise RuntimeError('No auth Odoo found') - self.__class__.odoo_connect() + # self.__class__.odoo_connect() # self.odoo_init() + odoo_timezone = self.configuration.get('storage', 'odoo_timezone') + self.__class__.server_timezone = pytz.timezone(odoo_timezone) attributes = path.strip('/').split('/') self.tag = None @@ -118,6 +122,11 @@ class Collection(BaseCollection): self.create_collection(calendar_path, props=props) self.logger.info('Collection creation for Odoo Sync : %s' % calendar_path) + @classmethod + def odoo_date_to_utc(cls, date_obj): + """ Transform naive Odoo date object to UTC TZ """ + local_date = cls.server_timezone.localize(date_obj, is_dst=None) + return local_date.astimezone(pytz.utc) @classmethod @contextmanager @@ -176,6 +185,13 @@ class Collection(BaseCollection): partner_ids = cls.odoo.env['res.partner'].search([]) return ['res.partner:%s' % pid for pid in partner_ids] + @classmethod + def get_events_from_odoo(cls, login): + """ Gets all events available from one Odoo login """ + cls.logger.info('Get events for Odoo user %s' % login) + event_ids = cls.odoo.env['calendar.event'].search([]) + return ['calendar.event:%s' % eid for eid in event_ids] + def sync(self, old_token=None): """ Debug purpose """ token, ilist = super(Collection, self).sync(old_token) @@ -185,13 +201,17 @@ class Collection(BaseCollection): def list(self): """List collection items.""" - # TODO : get all ICS from Odoo... self.logger.debug('List collection %s' % self.path) self.logger.debug('Collection tag %s' % self.tag) if self.tag: if self.tag == 'VADDRESSBOOK': - for oid in self.__class__.get_contacts_from_odoo(self.owner): + for oid in self.get_contacts_from_odoo(self.owner): yield oid + self.content_suffix + elif self.tag == 'VCALENDAR': + for oid in self.get_events_from_odoo(self.owner): + yield oid + self.content_suffix + else: + raise NotImplementedError @classmethod def _generate_vcard_from_odoo(cls, partner): @@ -230,6 +250,40 @@ class Collection(BaseCollection): vobject_item.add('rev').value = last_modified return vobject_item + @classmethod + def _generate_ics_from_odoo(cls, href, event): + """ Generate and return UCS object from Odoo calendar.event record """ + utc_dtstart = cls.odoo_date_to_utc(event.start_datetime) + utc_dtstop = cls.odoo_date_to_utc(event.stop_datetime) + last_modified = str(cls.odoo_date_to_utc(event.write_date)) + cal = vobject.iCalendar() + cal.add('vevent') + cal.vevent.add('summary').value = event.name + cal.vevent.add('location').value = event.location + cal.vevent.add('description').value = event.description + cal.vevent.add('dtstart').value = utc_dtstart + cal.vevent.add('dtend').value = utc_dtstop + # cal.vevent.add('duration').value = event.duration + if event.categ_ids: + categs = [categ.name for categ in event.categ_ids] + cal.vevent.add('categories').value = categs + cal.vevent.add('uid').value = href + cal.vevent.add('rev').value = last_modified + cal.add('rev').value = last_modified + return cal + + def _get_item_from_vobject(self, href, vobject_item): + """ Return Item from Vobject and HREF """ + vobject_item.add('uid').value = href + tag, start, end = xmlutils.find_tag_and_time_range(vobject_item) + text = vobject_item.serialize() + etag = get_etag(text) + # uid = get_uid_from_object(vobject_item) + return Item( + self, href=href, last_modified=vobject_item.rev.value, etag=etag, + text=text, item=vobject_item, uid=href, + name=vobject_item.name, component_name=tag), (tag, start, end) + def _get_with_metadata(self, href): """Fetch a single item from Odoo database""" model, database_id = href.split(':') @@ -240,17 +294,10 @@ class Collection(BaseCollection): record = self.odoo.env[model].browse([database_id]) if model == 'res.partner': vobject_item = self._generate_vcard_from_odoo(record) - vobject_item.add('uid').value = href - tag, start, end = xmlutils.find_tag_and_time_range(vobject_item) - text = vobject_item.serialize() - etag = get_etag(text) - # uid = get_uid_from_object(vobject_item) - return Item( - self, href=href, last_modified=vobject_item.rev.value, etag=etag, - text=text, item=vobject_item, uid=href, - name=vobject_item.name, component_name=tag), (tag, start, end) + return self._get_item_from_vobject(href, vobject_item) elif model == 'calendar.event': - raise NotImplementedError + vobject_item = self._generate_ics_from_odoo(href, record) + return self._get_item_from_vobject(href, vobject_item) else: raise NotImplementedError