diff --git a/product/ERP5Type/Constraint/AttributeEquality.py b/product/ERP5Type/Constraint/AttributeEquality.py index 8288049b26ab1b3b27faa91d321db09b944e2d13..91a16112501ee6f9408241b4b405c5aa05086847 100755 --- a/product/ERP5Type/Constraint/AttributeEquality.py +++ b/product/ERP5Type/Constraint/AttributeEquality.py @@ -1,8 +1,9 @@ ############################################################################## # -# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved. +# Copyright (c) 2002, 2005 Nexedi SARL and Contributors. All Rights Reserved. # Sebastien Robin <seb@nexedi.com> # Jean-Paul Smets <jp@nexedi.com> +# Romain Courteaud <romain@nexedi.com> # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsability of assessing all potential @@ -27,49 +28,36 @@ # ############################################################################## -from Constraint import Constraint +from PropertyExistence import PropertyExistence -from zLOG import LOG +class AttributeEquality(PropertyExistence): + """ + This constraint class allows to check / fix + attribute values. + Configuration example: + { 'id' : 'title', + 'description' : 'Title must be "ObjectTitle"', + 'type' : 'AttributeEquality', + 'title' : 'ObjectTitle', + }, + """ -class AttributeEquality(Constraint): + def checkConsistency(self, object, fixit=0): """ - This constraint class allows to check / fix - SetMappedValue object definitions: - - - modified attributes - - - modified base categories - - - domain base categories - - It is used for example in Transformations to check that elements - of an XMLMatrix include appropriate attributes which participate - in the price calculation. - - We consider that a list can be equal to a tuple. + This is the check method, we return a list of string, + each string corresponds to an error. + We will make sure that each non None constraint_definition is + satisfied (equality) """ - - def checkConsistency(self, object, fixit = 0): - """ - This is the check method, we return a list of string, - each string corresponds to an error. - - We will make sure that each non None constraint_definition is satisfied - (equality) - """ - - errors = [] - - # For each attribute name, we check equality - for attribute_name in self.constraint_definition.keys(): - attribute_value = self.constraint_definition[attribute_name] - if attribute_name is "mapped_value_base_category_list": - LOG("checkConsistency", 0, str((object.id, attribute_name,attribute_value))) - error_message = None - if not object.hasProperty(attribute_name): - error_message = "Attribute %s is not defined" % attribute_name - else: - identical = 1 + errors = PropertyExistence.checkConsistency(self, object, fixit=fixit) + for attribute_name, attribute_value in self.constraint_definition.items(): + error_message = None + # If property does not exist, error will be raise by + # PropertyExistence Constraint. + if object.hasProperty(attribute_name): + identical = 1 + if type(attribute_value) in (type(()), type([])): + # List type if len(object.getProperty(attribute_name)) != len(attribute_value): identical = 0 else: @@ -77,14 +65,18 @@ class AttributeEquality(Constraint): if item not in attribute_value: identical = 0 break - if not identical: - error_message = "Attribute %s is %s but sould be %s" % (attribute_name, - object.getProperty(attribute_name), attribute_value) - if error_message is not None: - if fixit: - object._setProperty(attribute_name, attribute_value) - error_message += " (Fixed)" - errors += [(object.getRelativeUrl(), 'AttributeEquality inconsistency', 100, error_message)] - - return errors - + else: + # Other type + identical = (attribute_value == object.getProperty(attribute_name)) + if not identical: + # Generate error_message + error_message = "Attribute %s is '%s' but should be '%s'" % \ + (attribute_name, object.getProperty(attribute_name), + attribute_value) + # Generate error + if error_message is not None: + if fixit: + object._setProperty(attribute_name, attribute_value) + error_message += " (Fixed)" + errors.append(self._generateError(object, error_message)) + return errors diff --git a/product/ERP5Type/Constraint/CategoryExistence.py b/product/ERP5Type/Constraint/CategoryExistence.py index ced48abbfb834f701d76ffb9f1f76425fff9cd80..d2df2152dc8398d44729748fd2c9ed25e7738980 100755 --- a/product/ERP5Type/Constraint/CategoryExistence.py +++ b/product/ERP5Type/Constraint/CategoryExistence.py @@ -1,7 +1,8 @@ ############################################################################## # -# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved. +# Copyright (c) 2002, 2005 Nexedi SARL and Contributors. All Rights Reserved. # Sebastien Robin <seb@nexedi.com> +# Romain Courteaud <romain@nexedi.com> # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsability of assessing all potential @@ -29,40 +30,35 @@ from Constraint import Constraint class CategoryExistence(Constraint): - """ - This method check and fix if an object respects the existence of a property. + """ + This method check and fix if an object respects the existence of + a category. + Configuration example: + { 'id' : 'category_existence', + 'description' : 'Category causality must be defined', + 'type' : 'CategoryExistence', + 'causality' : None, + }, + """ - For example we can check if every invoice line has a price defined on it. + def checkConsistency(self, object, fixit=0): """ - - def __init__(self, **constraint_definition): - """ - We need the definition list of the constraint - """ - self.constraint_definition = constraint_definition - - def checkConsistency(self, object, fixit = 0): - """ - this is the check method, we return a list of string, - each string corresponds to an error. - """ - - errors = [] - - # Retrieve values inside de PropertySheet (_constraints) - base_category = self.constraint_definition['base_category'] - - # Check arity and compare it with the min and max - error_message = None + This is the check method, we return a list of string, + each string corresponds to an error. + """ + errors = [] + # For each attribute name, we check if defined + for base_category in self.constraint_definition.keys(): + # Check existence of base category + error_message = "Category existence error for base category '%s': " % \ + base_category if not object.hasCategory(base_category): - error_message = "Category existence error for base category '%s': " % property_id \ - + " this document has no such category" - elif object.getProperty(property_id) is None: - error_message = "Category existence error for base category '%s': " % property_id \ - + " this property was not defined" - if error_message: - errors = [(object.getRelativeUrl(), 'CategoryMembershipArity inconsistency',104, error_message)] + error_message += " this document has no such category" + elif object.getProperty(base_category) is None: + error_message += " this property was not defined" else: - errors = [] - - return errors + error_message = None + # Raise error + if error_message: + errors.append(self._generateError(object, error_message)) + return errors diff --git a/product/ERP5Type/Constraint/CategoryMembershipArity.py b/product/ERP5Type/Constraint/CategoryMembershipArity.py index 978d436502dd02ef852b859bf48baf09e744bcb2..f4b5f2077aef7676cb01fe69ae8cec403dac9772 100755 --- a/product/ERP5Type/Constraint/CategoryMembershipArity.py +++ b/product/ERP5Type/Constraint/CategoryMembershipArity.py @@ -1,7 +1,8 @@ ############################################################################## # -# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved. +# Copyright (c) 2002, 2005 Nexedi SARL and Contributors. All Rights Reserved. # Sebastien Robin <seb@nexedi.com> +# Romain Courteaud <romain@nexedi.com> # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsability of assessing all potential @@ -29,54 +30,47 @@ from Constraint import Constraint class CategoryMembershipArity(Constraint): - """ + """ This method check and fix if an object respects the arity. + For example we can check if every Order has at + least one source. + Configuration exemple: + { 'id' : 'source', + 'description' : '', + 'type' : 'CategoryMembershipArity', + 'min_arity' : '1', + 'max_arity' : '1', + 'portal_type' : ('Organisation', ), + 'base_category' : ('source',) + }, + """ - For example we can check if every Organisation has at - least one address. - + def checkConsistency(self, object, fixit=0): """ - - def __init__(self, **constraint_definition): - """ - We need the definition list of the constraint - """ - self.constraint_definition = constraint_definition - - def checkConsistency(self, object, fixit = 0): - """ - this is the check method, we return a list of string, - each string corresponds to an error. - - We are looking the definition of the constraing where - are defined the minimum and the maximum arity, and the - list of objects we wants to check the arity. - """ - - errors = [] - - # Retrieve values inside de PropertySheet (_constraints) - base_category = self.constraint_definition['base_category'] - min_arity = int(self.constraint_definition['min_arity']) - max_arity = int(self.constraint_definition['max_arity']) - portal_type = self.constraint_definition['portal_type'] - - # Check arity and compare it with the min and max - arity = len(object.getCategoryMembershipList(base_category,portal_type=portal_type)) - if arity < min_arity or arity > max_arity: - if portal_type is (): - error_message = "Arrity error for relation '%s'" % base_category \ - + ", arity is equal to " \ - + str(arity) \ - + " but should be between " \ - + str(min_arity) + " and " + str(max_arity) - else: - error_message = "Arrity error for relation '%s' and portal_type : " % base_category \ - + str(portal_type) \ - + ", arity is equal to " \ - + str(arity) \ - + " but should be between " \ - + str(min_arity) + " and " + str(max_arity) - errors += [(object.getRelativeUrl(), 'CategoryMembershipArity inconsistency',104, error_message)] - - return errors + This is the check method, we return a list of string, + each string corresponds to an error. + We are looking the definition of the constraing where + are defined the minimum and the maximum arity, and the + list of objects we wants to check the arity. + """ + errors = [] + # Retrieve values inside de PropertySheet (_constraints) + base_category = self.constraint_definition['base_category'] + min_arity = int(self.constraint_definition['min_arity']) + max_arity = int(self.constraint_definition['max_arity']) + portal_type = self.constraint_definition['portal_type'] + # Check arity and compare it with the min and max + arity = len(object.getCategoryMembershipList(base_category, + portal_type=portal_type)) + if (arity < min_arity) or (arity > max_arity): + # Generate error message + error_message = "Arrity error for relation '%s'" % \ + base_category + if portal_type is not (): + error_message += " and portal_type: '%s'" % str(portal_type) + error_message += \ + ", arity is equal to %i but should be between %i and %i" % \ + (arity, min_arity, max_arity) + # Add error + errors.append(self._generateError(object, error_message)) + return errors diff --git a/product/ERP5Type/Constraint/CategoryRelatedMembershipArity.py b/product/ERP5Type/Constraint/CategoryRelatedMembershipArity.py index dee12e9f3665cf9073b39031c8cc5ac2d5a7dfda..14865afd975281c910c13b71106368a6ee216f17 100755 --- a/product/ERP5Type/Constraint/CategoryRelatedMembershipArity.py +++ b/product/ERP5Type/Constraint/CategoryRelatedMembershipArity.py @@ -1,7 +1,8 @@ ############################################################################## # -# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved. +# Copyright (c) 2002, 2005 Nexedi SARL and Contributors. All Rights Reserved. # Sebastien Robin <seb@nexedi.com> +# Courteaud Romain <romain@nexedi.com> # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsability of assessing all potential @@ -32,52 +33,45 @@ class CategoryRelatedMembershipArity(Constraint): """ This method check and fix if an object respects the arity from a category reverse membership point of view. - For example we can check if every Order has at most one Order Applied Rule. - + Configuration example: + { 'id' : 'applied_rule', + 'description' : 'There must at most one Applied Rule using this order', + 'type' : 'CategoryRelatedMembershipArity', + 'min_arity' : '1', + 'max_arity' : '1', + 'portal_type' : ('Applied Rule', ), + 'base_category' : ('causality',) + }, """ - def __init__(self, **constraint_definition): - """ - We need the definition list of the constraint - """ - self.constraint_definition = constraint_definition - - def checkConsistency(self, object, fixit = 0): + def checkConsistency(self, object, fixit=0): """ - this is the check method, we return a list of string, + This is the check method, we return a list of string, each string corresponds to an error. - We are looking the definition of the constraing where are defined the minimum and the maximum arity, and the list of objects we wants to check the arity. """ - errors = [] - # Retrieve values inside de PropertySheet (_constraints) base_category = self.constraint_definition['base_category'] min_arity = int(self.constraint_definition['min_arity']) max_arity = int(self.constraint_definition['max_arity']) portal_type = self.constraint_definition['portal_type'] - # Check arity and compare it with the min and max - arity = len(object._getRelatedValueList(base_category,portal_type=portal_type)) - if arity < min_arity or arity > max_arity: - if portal_type is (): - error_message = "Arrity error for reverse relation '%s'" % base_category \ - + ", arity is equal to " \ - + str(arity) \ - + " but should be between " \ - + str(min_arity) + " and " + str(max_arity) - else: - error_message = "Arrity error for reverse relation '%s' and portal_type : " % base_category \ - + str(portal_type) \ - + ", arity is equal to " \ - + str(arity) \ - + " but should be between " \ - + str(min_arity) + " and " + str(max_arity) - errors += [(object.getRelativeUrl(), 'CategoryRelatedMembershipArity inconsistency',104, error_message)] - + arity = len(object._getRelatedValueList(base_category, + portal_type=portal_type)) + if (arity < min_arity) or (arity > max_arity): + # Generate error message + error_message = "Arrity error for reverse relation '%s'" % \ + base_category + if portal_type is not (): + error_message += " and portal_type: '%s'" % str(portal_type) + error_message += \ + ", arity is equal to %i but should be between %i and %i" % \ + (arity, min_arity, max_arity) + # Add error + errors.append(self._generateError(object, error_message)) return errors diff --git a/product/ERP5Type/Constraint/Constraint.py b/product/ERP5Type/Constraint/Constraint.py index af5e1855de964a0c0721dbf145091828956d97fe..bd02b625981c0341e115327f6a69c1650f632fbf 100755 --- a/product/ERP5Type/Constraint/Constraint.py +++ b/product/ERP5Type/Constraint/Constraint.py @@ -1,8 +1,9 @@ ############################################################################## # -# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved. +# Copyright (c) 2002, 2005 Nexedi SARL and Contributors. All Rights Reserved. # Sebastien Robin <seb@nexedi.com> # Jean-Paul Smets <jp@nexedi.com> +# Courteaud Romain <romain@nexedi.com> # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsability of assessing all potential @@ -32,7 +33,8 @@ class Constraint: Default Constraint implementation """ - def __init__(self, id=None, description=None, type=None, **constraint_definition): + def __init__(self, id=None, description=None, type=None, + **constraint_definition): """ Remove unwanted attributes from constraint definition and keep them as instance attributes @@ -42,7 +44,8 @@ class Constraint: self.type = type self.constraint_definition = constraint_definition - def edit(self, id=None, description=None, type=None, **constraint_definition): + def edit(self, id=None, description=None, type=None, + **constraint_definition): """ Remove unwanted attributes from constraint definition and keep them as instance attributes @@ -52,7 +55,18 @@ class Constraint: if type is not None: self.type = type self.constraint_definition.update(constraint_definition) - def checkConsistency(self, object, fixit = 0): + def _generateError(self, object, error_message): + """ + Generic method used to generate error in checkConsistency. + """ + error = None + if error_message: + error = (object.getRelativeUrl(), + '%s inconsistency' % self.__class__.__name__, + 104, error_message) + return error + + def checkConsistency(self, object, fixit=0): """ Default method is to return no error. """ @@ -64,5 +78,4 @@ class Constraint: Default method is to call checkConsistency with fixit set to 1 """ - return self.checkConsistency(object, fixit = 1) - + return self.checkConsistency(object, fixit=1) diff --git a/product/ERP5Type/Constraint/PropertyExistence.py b/product/ERP5Type/Constraint/PropertyExistence.py index f1bfcd5ae8e5f7e0223f3e0a05d7e72390602031..779db5b84c871cf9954abfe95440b5c85d79ae7b 100755 --- a/product/ERP5Type/Constraint/PropertyExistence.py +++ b/product/ERP5Type/Constraint/PropertyExistence.py @@ -1,7 +1,8 @@ ############################################################################## # -# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved. +# Copyright (c) 2002, 2005 Nexedi SARL and Contributors. All Rights Reserved. # Sebastien Robin <seb@nexedi.com> +# Romain Courteaud <romain@nexedi.com> # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsability of assessing all potential @@ -29,40 +30,38 @@ from Constraint import Constraint class PropertyExistence(Constraint): - """ - This method check and fix if an object respects the existence of a property. + """ + This method check and fix if an object respects the existence of a + property. + For example we can check if every invoice line has a price defined + on it. + Configuration example: + { 'id' : 'property_existence', + 'description' : 'Property price must be defined', + 'type' : 'PropertyExistence', + 'price' : None, + }, + """ - For example we can check if every invoice line has a price defined on it. + def checkConsistency(self, object, fixit=0): """ - - def __init__(self, **constraint_definition): - """ - We need the definition list of the constraint - """ - self.constraint_definition = constraint_definition - - def checkConsistency(self, object, fixit = 0): - """ - this is the check method, we return a list of string, - each string corresponds to an error. - """ - - errors = [] - - # Retrieve values inside de PropertySheet (_constraints) - property_id = self.constraint_definition['property_id'] - - # Check arity and compare it with the min and max - error_message = None + This is the check method, we return a list of string, + each string corresponds to an error. + """ + errors = [] + # For each attribute name, we check if defined + for property_id in self.constraint_definition.keys(): + # Check existence of property + error_message = \ + "Property existence error for property '%s': " % property_id if not object.hasProperty(property_id): - error_message = "Property existence error for property '%s': " % property_id \ - + " this document has no such property" + error_message += " this document has no such property" elif object.getProperty(property_id) is None: - error_message = "Property existence error for property '%s': " % property_id \ - + " this property was not defined" - if error_message: - errors = [(object.getRelativeUrl(), 'CategoryMembershipArity inconsistency',104, error_message)] + error_message += " this property was not defined" else: - errors = [] - - return errors + error_message = None + # Return error + error = self._generateError(object, error_message) + if error is not None: + errors.append(error) + return errors diff --git a/product/ERP5Type/Constraint/PropertyTypeValidity.py b/product/ERP5Type/Constraint/PropertyTypeValidity.py index 84757af01dd13a8bfd6d1d7806adc09c07965cf2..2d93e25975b0072e0dc4729981c0f3e68e0fe4fa 100755 --- a/product/ERP5Type/Constraint/PropertyTypeValidity.py +++ b/product/ERP5Type/Constraint/PropertyTypeValidity.py @@ -1,8 +1,9 @@ ############################################################################## # -# Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved. +# Copyright (c) 2002, 2005 Nexedi SARL and Contributors. All Rights Reserved. # Sebastien Robin <seb@nexedi.com> # Jean-Paul Smets <jp@nexedi.com> +# Romain Courteaud <romain@nexedi.com> # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsability of assessing all potential @@ -28,85 +29,89 @@ ############################################################################## from Constraint import Constraint -from string import split class PropertyTypeValidity(Constraint): + """ + This constraint class allows to check / fix type of each + attributes define in the PropertySheets. + This Constraint is always created in ERP5Type/Utils.py + """ + + # Initialize type dict + _type_dict = { + 'string': (type('a'), ), + 'text': (type('a'), ), + 'int': (type(1), ), + 'boolean': (type(1), ), + 'float': (type(1.0), ), + 'long': (type(1L), ), + 'lines': (type([]), type(())), + 'tokens': (type([]), type(())), + 'selection': (type([]), type(())), + 'multiple selection': (type([]), type(())), + } + + def _checkPropertiesAttributes(self): """ - This constraint class allows to check / fix - SetMappedValue object definitions: - - - modified attributes - - - modified base categories - - - domain base categories - - It is used for example in Transformations to check that elements - of an XMLMatrix include appropriate attributes which participate - in the price calculation. - - We consider that a list can be equal to a tuple. + Make sure instance has no _properties """ - - def checkConsistency(self, object, fixit = 0): - """ - This is the check method, we return a list of string, - each string corresponds to an error. - - We will make sure that each non None constraint_definition is satisfied - (equality) - """ - - errors = [] - - # Make sure instance has no _properties - if '_properties' in object.__dict__: - # Remove _properties - error_message = "Instance has local _properties property" + # XXX FIXME what is _properties ? + errors = [] + if '_properties' in object.__dict__: + # Remove _properties + error_message = "Instance has local _properties property" + if fixit: + # XXX FIXME we have to set exception name ! +# try: + local_properties = object._properties + del object._properties + object._local_properties = [] + class_property_ids = object.propertyIds() + for p in local_properties: + if p['id'] not in class_property_ids: + object._local_properties.append(p) + error_message += " (Fixed)" +# except: +# error_message += " (ERROR)" + errors.append(self._generateError(object, error_message)) + return errors + + def checkConsistency(self, object, fixit=0): + """ + This is the check method, we return a list of string, + each string corresponds to an error. + """ + errors = [] + # XXX FIXME Is this still useful ? + errors.extend(self._checkPropertiesAttributes()) + # For each attribute name, we check type + for property in object.propertyMap(): + property_id = property['id'] + property_type = property['type'] + wrong_type = 0 + value = object.getProperty(property_id) + if value is not None: + # Check known type + try: + wrong_type = (type(value) not in self._type_dict[property_type]) + except KeyError: + wrong_type = 0 + error_message = "Attribute %s is defined with unknown type %s" % \ + (property_id, property_type) + errors += [(object.getRelativeUrl(), + 'PropertyTypeValidity inconsistency', + 100, error_message)] + if wrong_type: + # Type is wrong, so, raise constraint error + error_message = \ + "Attribute %s should be of type %s but is of type %s" % \ + (property_id, property_type, str(type(value))) if fixit: - try: - local_properties = object._properties - del object._properties - object._local_properties = [] - class_property_ids = object.propertyIds() - for p in local_properties: - if p['id'] not in class_property_ids: - object._local_properties.append(p) - error_message += " (Fixed)" - except: - error_message += " (ERROR)" - errors += [(object.getRelativeUrl(), 'PropertyTypeValidity inconsistency', - 100, error_message)] - - # For each attribute name, we check equality - for property in object.propertyMap(): - property_id = property['id'] - property_type = property['type'] - wrong_type = 0 - value = object.getProperty(property_id) - if value is not None: - if property_type == 'string' or property_type == 'text': - wrong_type = type(value) is not type('a') - elif property_type == 'int' or property_type == 'boolean': - wrong_type = type(value) is not type(1) - elif property_type == 'float': - wrong_type = type(value) is not type(1.0) - elif property_type == 'lines' or property_type == 'tokens'\ - or property_type == 'selection' or property_type == 'multiple selection': - wrong_type = type(value) is not type([]) and type(value) is not type(()) - - if wrong_type: - error_message = "Attribute %s should be of type %s but is of type %s" % (property_id, - property_type, str(type(value))) - if fixit: - try: - object.setProperty(property_id, value) - error_message += " (Fixed)" - except: - error_message += " (ERROR)" - - errors += [(object.getRelativeUrl(), 'PropertyTypeValidity inconsistency', - 100, error_message)] - - return errors - + # XXX FIXME do not use except without exception name ! +# try: + object.setProperty(property_id, value) + error_message += " (Fixed)" +# except: +# error_message += " (ERROR)" + errors.append(self._generateError(object, error_message)) + return errors