# -*- coding: utf-8 -*- ############################################################################## # # Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved. # Hervé Poulain <herve@nexedi.com> # Mohamadou Mbengue <mohamadou@nexedi.com> # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsability of assessing all potential # consequences resulting from its eventual inadequacies and bugs # End users who are looking for a ready-to-use solution with commercial # garantees and support are strongly adviced to contract a Free Software # Service Company # # This program is Free Software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################## from Products.ERP5Type.Utils import cartesianProduct from Products.ERP5TioSafe.Conduit.TioSafeResourceConduit import TioSafeResourceConduit from Products.ERP5SyncML.Document.Conflict import Conflict from lxml import etree from zLOG import LOG, INFO class ZencartResourceConduit(TioSafeResourceConduit): """ This is the conduit use to synchonize TioSafe Resources """ def __init__(self): self.xml_object_tag = 'resource' def _createContent(self, xml=None, object=None, object_id=None, sub_object=None, reset_local_roles=0, reset_workflow=0, simulate=0, **kw): #LOG("TioSafeNodeConduit._createConten", 300, "xml = %s" %(etree.tostring(xml, pretty_print=1),)) # if exist namespace retrieve only the tag index = 0 if xml.nsmap not in [None, {}]: index = -1 # init the new_id of the product and the checker of the creation new_id = None product_created = False # this dict contains the element to set to the product keyword = {} # this dict will contains a list of tuple (base_category, variation) variation_dict = {} resource_type = self.getObjectType(xml=xml).strip() # browse the xml for node in xml: # works on tags, not on comments if type(node.tag) is not str: continue tag = node.tag.split('}')[index] #LOG("browsing tag %s, value %s" %(tag, node.text), 300, "keyword = %s" %(keyword,)) if tag == 'category': # retrieve through the mapping the base category and the variation LOG("_createContent", 0, node.text) mapping = object.getMappingFromCategory(node.text.encode('utf-8')) LOG("_createContent", 0, mapping) base_category, variation = mapping.split('/', 1) variation_dict.setdefault(base_category, []).append(variation) else: keyword[tag] = node.text.encode('utf-8') # Create the product at the end of the xml browsing create_method_id = "create%s" %(resource_type,) create_method = getattr(object, create_method_id, None) if create_method is not None: create_result = create_method(**keyword) else: raise ValueError, 'Impossible to find a create method named %s and object %s' %(create_method_id, object.getPath(),) new_id = object.IntegrationSite_lastID(type='Product')[0].getId() # create the full variations in the integration site if variation_dict: # the cartesianProduct requires to build a list of list of variations builder_variation_list = [] for key, value in variation_dict.items(): variation_list = [] for variation in value: variation_list.append((key, variation)) builder_variation_list.append(variation_list) ## build and browse the list of variations variation_list = cartesianProduct(builder_variation_list) for var_list in variation_list: for variation in var_list: object.product_module.createProductCategory( product_id=new_id, base_category=variation[0], variation=variation[1], ) return object[new_id] def _deleteContent(self, object=None, object_id=None): """ This method allows to remove a product in the integration site """ LOG("_deleteContent ", 0, "object_id=%s" % object_id) delete_method_id = "deleteProduct" # XXX-AUREL : must find a way to fix this delete_method = getattr(object, delete_method_id, None) if delete_method is not None: return delete_method(product_id=object_id) else: raise ValueError, 'Impossible to find a delete method named %s and object %s' %(delete_method_id, object.getPath(),) def _updateXupdateUpdate(self, document=None, xml=None, previous_xml=None, **kw): """ This method is called in updateNode and allows to work on the update of elements. """ conflict_list = [] xpath_expression = xml.get('select') tag = xpath_expression.split('/')[-1] integration_site = document.context.getParentValue() new_value = xml.text # retrieve the previous xml etree through xpath previous_xml = previous_xml.xpath(xpath_expression) try: previous_value = previous_xml[0].text except IndexError: raise ValueError, 'Too little or too many value, only one is required for %s' % ( previous_xml ) if isinstance(previous_value, unicode): previous_value = previous_value.encode('utf-8') if isinstance(new_value, unicode): new_value = new_value.encode('utf-8') # check if it's a work on product or on categories if tag.split('[')[0] == 'category': # call the method which allows to work on a specific part, the update of # categories LOG("_updateXupdateUpdate ", 0, "previous_value=%s" % previous_value) LOG("_updateXupdateUpdate ", 0, "new_value=%s" % new_value) conflict_list += self._updateCategory(document, xml, previous_value, new_value) else: # getter used to retrieve the current values and to check conflicts property_list = ['title', 'reference', 'description', 'sale_price'] getter_value_dict = dict(zip( property_list, [ getattr(document, prop, None) for prop in property_list ] )) # create and fill a conflict when the integration site value, the erp5 # value and the previous value are differents current_value = getter_value_dict[tag] if type(current_value) == float: current_value = '%.6f' % current_value if isinstance(current_value, unicode): current_value = current_value.encode('utf-8') if current_value not in [new_value, previous_value]: conflict = Conflict( object_path=document.getPhysicalPath(), keyword=tag, ) conflict.setXupdate(etree.tostring(xml, encoding='utf-8')) conflict.setLocalValue(current_value) conflict.setRemoteValue(new_value) conflict_list.append(conflict) else: keyword = {'product_id': document.getId(), tag: new_value , } #LOG("update product", 0, "update keyword=%s" % keyword) document.context.product_module.updateProduct(**keyword) new_document = document.context.product_module[document.getId()] document.updateProperties(new_document) return conflict_list def _updateXupdateDel(self, document=None, xml=None, previous_xml=None, **kw): """ This method is called in updateNode and allows to remove elements. """ conflict_list = [] tag = xml.get('select').split('/')[-1] integration_site = document.context.getParentValue() LOG("Xupdate delete", 0, "tag=%s, previous_xml=%s, xml=%s, kw=%s" % (tag, previous_xml, xml, kw)) if tag.split('[')[0] == 'category': # retrieve the previous xml etree through xpath previous_xml = previous_xml.xpath(tag) try: previous_value = integration_site.getMappingFromCategory( previous_xml[0].text.encode('utf-8'), ) except IndexError: raise IndexError, 'Too little or too many value, only one is required for %s' % ( previous_xml ) # retrieve the current value to check if exists a conflict current_value = etree.XML(document.asXML()).xpath(tag)[0].text LOG("updateXupdateDel", 0, current_value) current_value = integration_site.getMappingFromCategory(current_value.encode('utf-8')) LOG("updateXupdateDel", 0, current_value) # work on variations variation_brain_list = document.context.getProductCategoryList() for brain in variation_brain_list: if brain.category == current_value and previous_value == current_value: base_category, variation = current_value.split('/', 1) document.context.product_module.deleteProductCategory( product_id=document.getId(), base_category=base_category, variation=variation, ) else: # previous value different from current value conflict = Conflict( object_path=document.getPhysicalPath(), keyword=tag, ) conflict.setXupdate(etree.tostring(xml, encoding='utf-8')) conflict.setLocalValue(previous_value) conflict.setRemoteValue(current_value) conflict_list.append(conflict) else: keyword = {'product_id': document.getId(), tag: 'NULL' , } document.context.product_module.updateProduct(**keyword) new_document = document.context.product_module[document.getId()] document.updateProperties(new_document) return conflict_list def _updateXupdateInsertOrAdd(self, document=None, xml=None, previous_xml=None, **kw): """ This method is called in updateNode and allows to add elements. """ conflict_list = [] integration_site = document.context.getParentValue() variation_list = [] # browse subnode of the insert and check what will be create for subnode in xml.getchildren(): new_tag = subnode.attrib['name'] new_value = subnode.text if new_tag == 'category': LOG("updateXupdateInsertOrAdd", 0, new_value) #mapping = integration_site.getMappingFromCategory(new_value) mapping = integration_site.getMappingFromCategory(new_value.encode('utf-8')) LOG("updateXupdateInsertOrAdd", 0, mapping) base_category, variation = mapping.split('/', 1) document.context.product_module.createProductCategory( product_id=document.getId(), base_category=base_category, variation=variation, ) else: keyword = {'product_id': document.getId(), new_tag: new_value.encode('utf-8'), } document.context.product_module.updateProduct(**keyword) new_document = document.context.product_module[document.getId()] document.updateProperties(new_document) return conflict_list def _updateCategory(self, document=None, xml=None, previous_value=None, new_value=None): """ This method allows to update a Category in the Integration Site. """ conflict_list = [] # work on variations integration_site = document.context.getParentValue() if previous_value != "" and new_value != "" and previous_value != new_value: LOG("_updateCategory", 0, "new_value=%s previous_value=%s" % (new_value, previous_value)) previous_value = integration_site.getMappingFromCategory(previous_value) new_value = integration_site.getMappingFromCategory(new_value) base_category, variation = previous_value.split('/', 1) document.context.product_module.deleteProductCategory( product_id=document.getId(), base_category=base_category, variation=variation, ) #LOG("_updateCategory delete", 0, "base_category=%s variation=%s" % (base_category, variation)) base_category, variation = new_value.split('/', 1) document.context.product_module.createProductCategory( product_id=document.getId(), base_category=base_category, variation=variation, ) #LOG("_updateCategory create", 0, "base_category=%s variation=%s" % (base_category, variation)) return conflict_list