Commit ab9a0474 authored by Ayush Tiwari's avatar Ayush Tiwari

bt5_config: Add reduce function for Business Template

parent 576c3235
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
import hashlib import hashlib
import fnmatch import fnmatch
from datetime import datetime
from Products.ERP5Type.XMLObject import XMLObject from Products.ERP5Type.XMLObject import XMLObject
from Products.ERP5Type.Globals import Persistent from Products.ERP5Type.Globals import Persistent
from Acquisition import Implicit, aq_base, aq_inner, aq_parent from Acquisition import Implicit, aq_base, aq_inner, aq_parent
...@@ -68,6 +69,7 @@ class BusinessTemplate(XMLObject): ...@@ -68,6 +69,7 @@ class BusinessTemplate(XMLObject):
) )
template_path_list = () template_path_list = ()
status = ''
# Declarative security # Declarative security
security = ClassSecurityInfo() security = ClassSecurityInfo()
...@@ -89,7 +91,7 @@ class BusinessTemplate(XMLObject): ...@@ -89,7 +91,7 @@ class BusinessTemplate(XMLObject):
Flatenned: BI(s) should be at the zeroth layer. Flatenned: BI(s) should be at the zeroth layer.
Build: BI(s) do have values from the OS DB. Build: BI(s) do have values from the OS DB.
""" """
pass return self.status
def applytoERP5(self, DB): def applytoERP5(self, DB):
"""Apply the flattened/reduced business template to the DB""" """Apply the flattened/reduced business template to the DB"""
...@@ -133,7 +135,7 @@ class BusinessTemplate(XMLObject): ...@@ -133,7 +135,7 @@ class BusinessTemplate(XMLObject):
"""Upgrade the business template""" """Upgrade the business template"""
pass pass
def flatten(self): def flattenBusienssTemplate(self):
""" """
Flattening a reduced business template with two path p1 and p2 where p1 <> p2: Flattening a reduced business template with two path p1 and p2 where p1 <> p2:
...@@ -141,13 +143,55 @@ class BusinessTemplate(XMLObject): ...@@ -141,13 +143,55 @@ class BusinessTemplate(XMLObject):
A reduced business template BT is said to be flattened if and only if: A reduced business template BT is said to be flattened if and only if:
flatten(BT) = BT flatten(BT) = BT
""" """
if self.getStatus() != 'reduced':
raise ValueError, 'Please reduce the BT before flatenning'
else:
# TODO: Still not clear on what flattens means here
pass pass
def reduceBT(self): def reduceBusienssTemplate(self):
""" """
Reduce the current Business Template Reduction is a function that takes a Business Template as input and returns
a smaller business template by taking out values with lower priority layers.
Two path on different layer are reduced as a single path with the highest layer:
If l1 > l2,
reduce([(p, s, l1, (a, b, c)), (p, s, l2, (d, e))]) = [(p, s, l1, merge(a, b, c))]
Where the merge is a monotonic commutative function that depends on the type of a, b and c:
if a, b and c are sets, merge = union
if a, b and c are lists, merge = ordered concatenation
if a, b and c are objects, merge = the object created the last
else merge = MAX
A business template BT is said to be reduced if and only if:
reduce(BT) = BT
""" """
pass path_list = [path_item.getBusinessPath() for path in self._path_item_list]
seen_path_list = set()
unique_path_list = [x for x in path_list if x not in seen_path_list and not seen_path_list.add(x)]
# Create an extra dict for values on path which are repeated in the path list
seen_path_dict = {path: [] for path in seen_path_list}
unique_path_dict = {}
for path_item in self._path_item_list:
if path_item._path in seen_path_list:
seen_path_dict[path_item._path].append(path_item)
else:
unique_path_dict[path_item._path].append(path_item)
# Reduce the values and get the merged result out of it
for path, path_item_list in seen_path_dict.items():
merged_business_item = reduce(lambda x, y: x+y, path_item_list)
seen_path_dict[path] = merged_business_item
# Add both unique and seen path item and update _path_item_list attribute
reduced_path_item_list = seen_path_dict.values() + unique_value_dict.values()
self._path_item_list = reduced_path_item_list
self.status = 'reduced'
class BusinessItem(Implicit, Persistent): class BusinessItem(Implicit, Persistent):
...@@ -155,6 +199,11 @@ class BusinessItem(Implicit, Persistent): ...@@ -155,6 +199,11 @@ class BusinessItem(Implicit, Persistent):
attributes for a path configuration being: attributes for a path configuration being:
- path (similar to an xpath expression) - path (similar to an xpath expression)
Examples of path :
portal_type/Person
portal_type/Person#title
portal_type/Person#property_sheet?ancestor=DublinCore
portal_type/Person#property_sheet?position=2
- sign (+1/-1) - sign (+1/-1)
- layer (0, 1, 2, 3, etc.) - layer (0, 1, 2, 3, etc.)
- value (a set of pickable value in python) - value (a set of pickable value in python)
...@@ -169,6 +218,7 @@ class BusinessItem(Implicit, Persistent): ...@@ -169,6 +218,7 @@ class BusinessItem(Implicit, Persistent):
self._sign = int(sign) self._sign = int(sign)
self._layer = int(layer) self._layer = int(layer)
self._value = value self._value = value
if value:
# Generate hash of from the value # Generate hash of from the value
self._sha = self._generateHash() self._sha = self._generateHash()
...@@ -181,8 +231,8 @@ class BusinessItem(Implicit, Persistent): ...@@ -181,8 +231,8 @@ class BusinessItem(Implicit, Persistent):
# Raise in case there is no value for the BusinessItem object # Raise in case there is no value for the BusinessItem object
raise ValueError, "Value not defined for the %s BusinessItem" %self._path raise ValueError, "Value not defined for the %s BusinessItem" %self._path
else: else:
# Expects to raise error on case the value for the object is not # Expects to raise error on case the value for the object
# picklable # is not picklable
sha1 = hashlib.sha1(self._value).hexdigest() sha1 = hashlib.sha1(self._value).hexdigest()
def build(self, context, **kw): def build(self, context, **kw):
...@@ -201,8 +251,22 @@ class BusinessItem(Implicit, Persistent): ...@@ -201,8 +251,22 @@ class BusinessItem(Implicit, Persistent):
_recursiveRemoveUid(obj) _recursiveRemoveUid(obj)
self._value = obj self._value = obj
def applyValueToPath(self):
"""
Apply the value to the path given.
1. If the path doesn't exist, and its a new object, create the object.
2. If the path doesn't exist, and its a new property, apply the property on
the object.
3. If the path doesn't exist, and its a new property, raise error.
"""
pass
def _resolvePath(self, folder, relative_url_list, id_list): def _resolvePath(self, folder, relative_url_list, id_list):
""" """
XXX: Needs to be updated for Business Item as we now consider that we
can also use path to add properties.
This method calls itself recursively. This method calls itself recursively.
The folder is the current object which contains sub-objects. The folder is the current object which contains sub-objects.
...@@ -226,6 +290,78 @@ class BusinessItem(Implicit, Persistent): ...@@ -226,6 +290,78 @@ class BusinessItem(Implicit, Persistent):
relative_url_list + [object_id], id_list[1:])) relative_url_list + [object_id], id_list[1:]))
return path_list return path_list
def setPropertyToPath(self, path, property_name, value):
"""
Set property for the object at given path
"""
portal = self.getPortalObject()
obj = portal.unrestrictedTraverse(path)
obj.setProperty(property_name, value)
def generateXML(self):
"""
Generate XML for different objects/type/properties differently.
1. Objects: Use XMLImportExport from ERP5Type
2. For properties, first get the property type, then create XML object
for the different property differenty(Use ObjectPropertyItem from BT5)
3. For attributes, we can export part of the object, rather than exporting
whole of the object
"""
pass
def __add__(self, other):
"""
Add the values from the path when the path is same for 2 objects
"""
if self._path != other._path:
raise ValueError, "BusinessItem are incommensurable"
else:
# Create a new BusinessItem by merging the values of the objects and
# taking out the one with lower priority layer
higer_priority_layer = max(self._layer, other._layer)
# Merge the values
merged_value = self._mergeValue(self._value, other._value)
return BusinessItem(self._path, 1, higer_priority_layer, merged_value)
def _mergeValue(self, val1, val2):
"""
Merge two values to create a new value.
Need to take care of type of values
"""
built_in_number_type = (int, long, float, complex)
built_in_container_type = (tuple, list, dict, set)
if not val1:
return val2
if not val2:
return val1
# Now, consider the type of both values
if isinstance(val1, built_in_number_type) and isinstance(val2, built_in_number_type):
merged_value = max(val1, val2)
elif isinstance(val1, set) and isinstance(val2, set):
merged_value = va1.union(val2)
elif isinstance(val1, list) and isinstance(val2, list):
merged_value = val1 + val2
elif isinstance(val1, tuple) and isinstance(val2, tuple):
merged_value = val1 + val2
else:
# In all other case, check if the values are objects and then take the
# objects created last.
# XXX: Should we go with creation date or modification_date ??
# TODO: Add check that the values are ERP5 objects
creation_date_1 = getattr(val1, 'creation_date', 0)
creation_date_2 = getattr(val2, 'creation_date', 0)
# TODO: In case both are created at same time, prefer one with higher
# priority layer
if creation_date_1 > creation_date_2:
merged_value = val1
else:
merged_value = val2
return merged_value
def getBusinessPath(self): def getBusinessPath(self):
return self._path return self._path
...@@ -238,19 +374,11 @@ class BusinessItem(Implicit, Persistent): ...@@ -238,19 +374,11 @@ class BusinessItem(Implicit, Persistent):
def getBusinessPathValue(self): def getBusinessPathValue(self):
return self._value return self._value
def setBusinessPathValue(self, value):
self._value = value
def getBusinessPathSha(self): def getBusinessPathSha(self):
return self._sha return self._sha
def getParentBusinessTemplate(self): def getParentBusinessTemplate(self):
return self.aq_parent return self.aq_parent
def generateXML(self):
"""
Generate XML for different objects/type/properties differently.
1. Objects: Use XMLImportExport from ERP5Type
2. For properties, first get the property type, then create XML object
for the different property differenty(Use ObjectPropertyItem from BT5)
3. For attributes, we can export part of the object, rather than exporting
whole of the object
"""
pass
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment