diff --git a/radicale_odoo_storage/__init__.py b/radicale_odoo_storage/__init__.py index 9b9fd25..d617adf 100644 --- a/radicale_odoo_storage/__init__.py +++ b/radicale_odoo_storage/__init__.py @@ -17,12 +17,23 @@ """ Odoo Radicale Storage Plugin """ -from json import dumps -import threading +# # 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 + + from contextlib import contextmanager +from time import strftime, strptime +import vobject from odoorpc import ODOO from odoorpc.error import RPCError -from radicale.storage import BaseCollection +from radicale import xmlutils +from radicale.storage import BaseCollection, Item, get_etag, get_uid_from_object +from radicale_odoo_auth import Auth class Collection(BaseCollection): @@ -32,7 +43,10 @@ class Collection(BaseCollection): def __init__(self, path): """ Init function """ - if not self.odoo: + self.__class__.odoo = Auth.odoo + if not self.__class__.odoo: + self.logger.error('No auth Odoo found...') + raise RuntimeError('No auth Odoo found') self.__class__.odoo_connect() # self.odoo_init() @@ -41,12 +55,14 @@ class Collection(BaseCollection): self.props = {} if 'odoo-contact' in attributes: self.tag = 'VADDRESSBOOK' + self.odoo_model = 'res.partner' self.content_suffix = '.vcf' self.props.update({'tag': 'VADDRESSBOOK', 'D:displayname': 'Odoo contacts', 'CR:addressbook-description': 'Contacts form your Odoo account'}) elif 'odoo-calendar' in attributes: self.tag = 'VCALENDAR' + self.odoo_model = 'calendar.event' self.content_suffix = '.ics' self.props.update({'tag': 'VCALENDAR', 'D:displayname': 'Odoo calendar', @@ -132,18 +148,116 @@ class Collection(BaseCollection): cls.logger.warning('Discover : %s (path), %s (depth), %s (cls.user), %s (attributes)' % (path, depth, cls.user, attributes)) yield cls(path) - contact_path = '%s/odoo-contact' % path - calendar_path = '%s/odoo-calendar' % path - yield cls(contact_path) - yield cls(calendar_path) + if len(attributes) == 1: # Got all if root is needed + contact_path = '%s/odoo-contact' % path + calendar_path = '%s/odoo-calendar' % path + yield cls(contact_path) + yield cls(calendar_path) def get_meta(self, key=None): """Get metadata value for collection """ - if key == 'tag': - return self.tag - return self.props.get(key) + if key: + return self.props.get(key) + else: + return self.props @classmethod def create_collection(cls, href, collection=None, props=None): """ Create collection implementation : only warns ATM """ cls.logger.error('Attemmpt to create a new collection for %s' % href) + + @classmethod + def get_contacts_from_odoo(cls, login): + """ Gets all contacts available from one Odoo login """ + cls.logger.info('Get contacts for Odoo user %s' % login) + partner_ids = cls.odoo.env['res.partner'].search([]) + cls.logger.debug(partner_ids) + return ['res.partner:%s' % pid for pid in partner_ids] + + def list(self): + """List collection items.""" + # TODO : get all ICS from Odoo... + self.logger.warning('List collection %s' % self.path) + self.logger.warning('Collection tag %s' % self.tag) + if self.tag: + if self.tag == 'VADDRESSBOOK': + for oid in self.__class__.get_contacts_from_odoo(self.owner): + yield oid + # for item in self.collection.list(): + # yield item.uid + self.content_suffix + + def _get_with_metadata(self, href): + """Fetch a single item from Odoo database""" + model, database_id = href.split(':') + fields = ['name', 'write_date', 'comment'] + data = self.odoo.execute(model, 'read', [int(database_id)], fields)[0] + self.logger.warning(data) + if model == 'res.partner': + # last_modified = strftime("%a, %d %b %Y %H:%M:%S GMT", + # strptime(data.get('write_date'), '%Y-%m-%d %H:%M:%S')) + last_modified = strftime("%Y-%m-%dT%H:%M:%SZ", + strptime(data.get('write_date'), '%Y-%m-%d %H:%M:%S')) + self.logger.warning(last_modified) + vobject_item = vobject.vCard() + vobject_item.add('n') + vobject_item.n.value = vobject.vcard.Name(family=data.get('name')) + vobject_item.add('fn') + vobject_item.fn.value = data.get('name') + vobject_item.add('uid').value = database_id + vobject_item.add('rev').value = last_modified + self.logger.warning(vobject_item.name) + self.logger.warning([c for c in vobject_item.components()]) + tag, start, end = xmlutils.find_tag_and_time_range(vobject_item) + self.logger.warning('Tag, start, end : %s %s %s' % (tag, start, end)) + text = vobject_item.serialize() + etag = get_etag(text) + uid = get_uid_from_object(vobject_item) + self.logger.warning('Text, ETAG, UID : %s %s %s' % (text, etag, uid)) + return Item( + self, href=href, last_modified=last_modified, etag=etag, + text=text, item=vobject_item, uid=href, + name=vobject_item.name, component_name=tag), (tag, start, end) + elif model == 'calendar.event': + raise NotImplementedError + else: + raise NotImplementedError + + def get(self, href, verify_href=True): + item, metadata = self._get_with_metadata(href) + self.logger.warning(item) + self.logger.warning(item.serialize()) + self.logger.warning(item.name) + self.logger.warning(item.last_modified) + self.logger.warning([c for c in item.components()]) + self.logger.warning(metadata) + return item + + def delete(self, href=None): + """ Can not delete collection but item, yes """ + self.logger.warning(href) + if href is None: + # Delete the collection + self.logger.error('Attempt to delete collection %s' % self.path) + raise ValueError('Can not delete collection') + else: + # Delete an item + raise NotImplementedError + + # def serialize(self): + # """ Get the whole collection unicode """ + # # TODO: CALENDAR + # self.logger.warning('From serialize : %s' % self.tag) + # if self.tag == "VADDRESSBOOK": + # self.logger.warning('HERE') + # self.logger.warning([item.serialize() for item in self.get_all()]) + # return ''.join((item.serialize() for item in self.get_all())) + # return '' + + @property + def last_modified(self): + """ Return last modified """ + last = self.odoo.env[self.odoo_model].search([], limit=1, order='write_date desc') + last_fields = self.odoo.execute(self.odoo_model, 'read', last, ['write_date'])[0] + self.logger.info(last_fields) + return strftime("%a, %d %b %Y %H:%M:%S GMT", + strptime(last_fields.get('write_date'), '%Y-%m-%d %H:%M:%S'))