Commit 1b9b2267 authored by Romain Courteaud's avatar Romain Courteaud

Clean up current implementation.

Modify API of PropertyExistence and CategoryExistence, in order to test
multiple properties/categories.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@3776 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent aaf4e4e0
############################################################################## ##############################################################################
# #
# 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> # Sebastien Robin <seb@nexedi.com>
# Jean-Paul Smets <jp@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 # WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential # programmers who take the whole responsability of assessing all potential
...@@ -27,49 +28,36 @@ ...@@ -27,49 +28,36 @@
# #
############################################################################## ##############################################################################
from Constraint import Constraint from PropertyExistence import PropertyExistence
from zLOG import LOG class AttributeEquality(PropertyExistence):
class AttributeEquality(Constraint):
""" """
This constraint class allows to check / fix This constraint class allows to check / fix
SetMappedValue object definitions: attribute values.
Configuration example:
- modified attributes { 'id' : 'title',
'description' : 'Title must be "ObjectTitle"',
- modified base categories 'type' : 'AttributeEquality',
'title' : 'ObjectTitle',
- 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.
""" """
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. each string corresponds to an error.
We will make sure that each non None constraint_definition is
We will make sure that each non None constraint_definition is satisfied satisfied (equality)
(equality)
""" """
errors = PropertyExistence.checkConsistency(self, object, fixit=fixit)
errors = [] for attribute_name, attribute_value in self.constraint_definition.items():
# 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 error_message = None
if not object.hasProperty(attribute_name): # If property does not exist, error will be raise by
error_message = "Attribute %s is not defined" % attribute_name # PropertyExistence Constraint.
else: if object.hasProperty(attribute_name):
identical = 1 identical = 1
if type(attribute_value) in (type(()), type([])):
# List type
if len(object.getProperty(attribute_name)) != len(attribute_value): if len(object.getProperty(attribute_name)) != len(attribute_value):
identical = 0 identical = 0
else: else:
...@@ -77,14 +65,18 @@ class AttributeEquality(Constraint): ...@@ -77,14 +65,18 @@ class AttributeEquality(Constraint):
if item not in attribute_value: if item not in attribute_value:
identical = 0 identical = 0
break break
else:
# Other type
identical = (attribute_value == object.getProperty(attribute_name))
if not identical: if not identical:
error_message = "Attribute %s is %s but sould be %s" % (attribute_name, # Generate error_message
object.getProperty(attribute_name), attribute_value) 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 error_message is not None:
if fixit: if fixit:
object._setProperty(attribute_name, attribute_value) object._setProperty(attribute_name, attribute_value)
error_message += " (Fixed)" error_message += " (Fixed)"
errors += [(object.getRelativeUrl(), 'AttributeEquality inconsistency', 100, error_message)] errors.append(self._generateError(object, error_message))
return errors return errors
############################################################################## ##############################################################################
# #
# 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> # Sebastien Robin <seb@nexedi.com>
# Romain Courteaud <romain@nexedi.com>
# #
# WARNING: This program as such is intended to be used by professional # WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential # programmers who take the whole responsability of assessing all potential
...@@ -30,39 +31,34 @@ from Constraint import Constraint ...@@ -30,39 +31,34 @@ from Constraint import Constraint
class CategoryExistence(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.
For example we can check if every invoice line has a price defined on it. Configuration example:
""" { 'id' : 'category_existence',
'description' : 'Category causality must be defined',
def __init__(self, **constraint_definition): 'type' : 'CategoryExistence',
""" 'causality' : None,
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. each string corresponds to an error.
""" """
errors = [] errors = []
# For each attribute name, we check if defined
# Retrieve values inside de PropertySheet (_constraints) for base_category in self.constraint_definition.keys():
base_category = self.constraint_definition['base_category'] # Check existence of base category
error_message = "Category existence error for base category '%s': " % \
# Check arity and compare it with the min and max base_category
error_message = None
if not object.hasCategory(base_category): if not object.hasCategory(base_category):
error_message = "Category existence error for base category '%s': " % property_id \ error_message += " this document has no such category"
+ " this document has no such category" elif object.getProperty(base_category) is None:
elif object.getProperty(property_id) is None: error_message += " this property was not defined"
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)]
else: else:
errors = [] error_message = None
# Raise error
if error_message:
errors.append(self._generateError(object, error_message))
return errors return errors
############################################################################## ##############################################################################
# #
# 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> # Sebastien Robin <seb@nexedi.com>
# Romain Courteaud <romain@nexedi.com>
# #
# WARNING: This program as such is intended to be used by professional # WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential # programmers who take the whole responsability of assessing all potential
...@@ -31,52 +32,45 @@ from Constraint import Constraint ...@@ -31,52 +32,45 @@ from Constraint import Constraint
class CategoryMembershipArity(Constraint): class CategoryMembershipArity(Constraint):
""" """
This method check and fix if an object respects the arity. This method check and fix if an object respects the arity.
For example we can check if every Order has at
For example we can check if every Organisation has at least one source.
least one address. Configuration exemple:
{ 'id' : 'source',
""" 'description' : '',
'type' : 'CategoryMembershipArity',
def __init__(self, **constraint_definition): 'min_arity' : '1',
""" 'max_arity' : '1',
We need the definition list of the constraint 'portal_type' : ('Organisation', ),
'base_category' : ('source',)
},
""" """
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. each string corresponds to an error.
We are looking the definition of the constraing where We are looking the definition of the constraing where
are defined the minimum and the maximum arity, and the are defined the minimum and the maximum arity, and the
list of objects we wants to check the arity. list of objects we wants to check the arity.
""" """
errors = [] errors = []
# Retrieve values inside de PropertySheet (_constraints) # Retrieve values inside de PropertySheet (_constraints)
base_category = self.constraint_definition['base_category'] base_category = self.constraint_definition['base_category']
min_arity = int(self.constraint_definition['min_arity']) min_arity = int(self.constraint_definition['min_arity'])
max_arity = int(self.constraint_definition['max_arity']) max_arity = int(self.constraint_definition['max_arity'])
portal_type = self.constraint_definition['portal_type'] portal_type = self.constraint_definition['portal_type']
# Check arity and compare it with the min and max # Check arity and compare it with the min and max
arity = len(object.getCategoryMembershipList(base_category,portal_type=portal_type)) arity = len(object.getCategoryMembershipList(base_category,
if arity < min_arity or arity > max_arity: portal_type=portal_type))
if portal_type is (): if (arity < min_arity) or (arity > max_arity):
error_message = "Arrity error for relation '%s'" % base_category \ # Generate error message
+ ", arity is equal to " \ error_message = "Arrity error for relation '%s'" % \
+ str(arity) \ base_category
+ " but should be between " \ if portal_type is not ():
+ str(min_arity) + " and " + str(max_arity) error_message += " and portal_type: '%s'" % str(portal_type)
else: error_message += \
error_message = "Arrity error for relation '%s' and portal_type : " % base_category \ ", arity is equal to %i but should be between %i and %i" % \
+ str(portal_type) \ (arity, min_arity, max_arity)
+ ", arity is equal to " \ # Add error
+ str(arity) \ errors.append(self._generateError(object, error_message))
+ " but should be between " \
+ str(min_arity) + " and " + str(max_arity)
errors += [(object.getRelativeUrl(), 'CategoryMembershipArity inconsistency',104, error_message)]
return errors return errors
############################################################################## ##############################################################################
# #
# 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> # Sebastien Robin <seb@nexedi.com>
# Courteaud Romain <romain@nexedi.com>
# #
# WARNING: This program as such is intended to be used by professional # WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential # programmers who take the whole responsability of assessing all potential
...@@ -32,52 +33,45 @@ class CategoryRelatedMembershipArity(Constraint): ...@@ -32,52 +33,45 @@ class CategoryRelatedMembershipArity(Constraint):
""" """
This method check and fix if an object respects the arity This method check and fix if an object respects the arity
from a category reverse membership point of view. from a category reverse membership point of view.
For example we can check if every Order has at For example we can check if every Order has at
most one Order Applied Rule. most one Order Applied Rule.
Configuration example:
""" { 'id' : 'applied_rule',
'description' : 'There must at most one Applied Rule using this order',
def __init__(self, **constraint_definition): 'type' : 'CategoryRelatedMembershipArity',
""" 'min_arity' : '1',
We need the definition list of the constraint 'max_arity' : '1',
'portal_type' : ('Applied Rule', ),
'base_category' : ('causality',)
},
""" """
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. each string corresponds to an error.
We are looking the definition of the constraing where We are looking the definition of the constraing where
are defined the minimum and the maximum arity, and the are defined the minimum and the maximum arity, and the
list of objects we wants to check the arity. list of objects we wants to check the arity.
""" """
errors = [] errors = []
# Retrieve values inside de PropertySheet (_constraints) # Retrieve values inside de PropertySheet (_constraints)
base_category = self.constraint_definition['base_category'] base_category = self.constraint_definition['base_category']
min_arity = int(self.constraint_definition['min_arity']) min_arity = int(self.constraint_definition['min_arity'])
max_arity = int(self.constraint_definition['max_arity']) max_arity = int(self.constraint_definition['max_arity'])
portal_type = self.constraint_definition['portal_type'] portal_type = self.constraint_definition['portal_type']
# Check arity and compare it with the min and max # Check arity and compare it with the min and max
arity = len(object._getRelatedValueList(base_category,portal_type=portal_type)) arity = len(object._getRelatedValueList(base_category,
if arity < min_arity or arity > max_arity: portal_type=portal_type))
if portal_type is (): if (arity < min_arity) or (arity > max_arity):
error_message = "Arrity error for reverse relation '%s'" % base_category \ # Generate error message
+ ", arity is equal to " \ error_message = "Arrity error for reverse relation '%s'" % \
+ str(arity) \ base_category
+ " but should be between " \ if portal_type is not ():
+ str(min_arity) + " and " + str(max_arity) error_message += " and portal_type: '%s'" % str(portal_type)
else: error_message += \
error_message = "Arrity error for reverse relation '%s' and portal_type : " % base_category \ ", arity is equal to %i but should be between %i and %i" % \
+ str(portal_type) \ (arity, min_arity, max_arity)
+ ", arity is equal to " \ # Add error
+ str(arity) \ errors.append(self._generateError(object, error_message))
+ " but should be between " \
+ str(min_arity) + " and " + str(max_arity)
errors += [(object.getRelativeUrl(), 'CategoryRelatedMembershipArity inconsistency',104, error_message)]
return errors return errors
############################################################################## ##############################################################################
# #
# 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> # Sebastien Robin <seb@nexedi.com>
# Jean-Paul Smets <jp@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 # WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential # programmers who take the whole responsability of assessing all potential
...@@ -32,7 +33,8 @@ class Constraint: ...@@ -32,7 +33,8 @@ class Constraint:
Default Constraint implementation 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 Remove unwanted attributes from constraint definition and keep
them as instance attributes them as instance attributes
...@@ -42,7 +44,8 @@ class Constraint: ...@@ -42,7 +44,8 @@ class Constraint:
self.type = type self.type = type
self.constraint_definition = constraint_definition 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 Remove unwanted attributes from constraint definition and keep
them as instance attributes them as instance attributes
...@@ -52,7 +55,18 @@ class Constraint: ...@@ -52,7 +55,18 @@ class Constraint:
if type is not None: self.type = type if type is not None: self.type = type
self.constraint_definition.update(constraint_definition) 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. Default method is to return no error.
""" """
...@@ -64,5 +78,4 @@ class Constraint: ...@@ -64,5 +78,4 @@ class Constraint:
Default method is to call checkConsistency with Default method is to call checkConsistency with
fixit set to 1 fixit set to 1
""" """
return self.checkConsistency(object, fixit = 1) return self.checkConsistency(object, fixit=1)
############################################################################## ##############################################################################
# #
# 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> # Sebastien Robin <seb@nexedi.com>
# Romain Courteaud <romain@nexedi.com>
# #
# WARNING: This program as such is intended to be used by professional # WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential # programmers who take the whole responsability of assessing all potential
...@@ -30,39 +31,37 @@ from Constraint import Constraint ...@@ -30,39 +31,37 @@ from Constraint import Constraint
class PropertyExistence(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. For example we can check if every invoice line has a price defined
""" on it.
Configuration example:
def __init__(self, **constraint_definition): { 'id' : 'property_existence',
'description' : 'Property price must be defined',
'type' : 'PropertyExistence',
'price' : None,
},
""" """
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. each string corresponds to an error.
""" """
errors = [] errors = []
# For each attribute name, we check if defined
# Retrieve values inside de PropertySheet (_constraints) for property_id in self.constraint_definition.keys():
property_id = self.constraint_definition['property_id'] # Check existence of property
error_message = \
# Check arity and compare it with the min and max "Property existence error for property '%s': " % property_id
error_message = None
if not object.hasProperty(property_id): if not object.hasProperty(property_id):
error_message = "Property existence error for property '%s': " % property_id \ error_message += " this document has no such property"
+ " this document has no such property"
elif object.getProperty(property_id) is None: elif object.getProperty(property_id) is None:
error_message = "Property existence error for property '%s': " % property_id \ error_message += " this property was not defined"
+ " this property was not defined"
if error_message:
errors = [(object.getRelativeUrl(), 'CategoryMembershipArity inconsistency',104, error_message)]
else: else:
errors = [] error_message = None
# Return error
error = self._generateError(object, error_message)
if error is not None:
errors.append(error)
return errors return errors
############################################################################## ##############################################################################
# #
# 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> # Sebastien Robin <seb@nexedi.com>
# Jean-Paul Smets <jp@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 # WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential # programmers who take the whole responsability of assessing all potential
...@@ -28,43 +29,40 @@ ...@@ -28,43 +29,40 @@
############################################################################## ##############################################################################
from Constraint import Constraint from Constraint import Constraint
from string import split
class PropertyTypeValidity(Constraint): class PropertyTypeValidity(Constraint):
""" """
This constraint class allows to check / fix This constraint class allows to check / fix type of each
SetMappedValue object definitions: attributes define in the PropertySheets.
This Constraint is always created in ERP5Type/Utils.py
- 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.
""" """
def checkConsistency(self, object, fixit = 0): # 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 is the check method, we return a list of string, Make sure instance has no _properties
each string corresponds to an error.
We will make sure that each non None constraint_definition is satisfied
(equality)
""" """
# XXX FIXME what is _properties ?
errors = [] errors = []
# Make sure instance has no _properties
if '_properties' in object.__dict__: if '_properties' in object.__dict__:
# Remove _properties # Remove _properties
error_message = "Instance has local _properties property" error_message = "Instance has local _properties property"
if fixit: if fixit:
try: # XXX FIXME we have to set exception name !
# try:
local_properties = object._properties local_properties = object._properties
del object._properties del object._properties
object._local_properties = [] object._local_properties = []
...@@ -73,40 +71,47 @@ class PropertyTypeValidity(Constraint): ...@@ -73,40 +71,47 @@ class PropertyTypeValidity(Constraint):
if p['id'] not in class_property_ids: if p['id'] not in class_property_ids:
object._local_properties.append(p) object._local_properties.append(p)
error_message += " (Fixed)" error_message += " (Fixed)"
except: # except:
error_message += " (ERROR)" # error_message += " (ERROR)"
errors += [(object.getRelativeUrl(), 'PropertyTypeValidity inconsistency', errors.append(self._generateError(object, error_message))
100, error_message)] return errors
# For each attribute name, we check equality 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(): for property in object.propertyMap():
property_id = property['id'] property_id = property['id']
property_type = property['type'] property_type = property['type']
wrong_type = 0 wrong_type = 0
value = object.getProperty(property_id) value = object.getProperty(property_id)
if value is not None: if value is not None:
if property_type == 'string' or property_type == 'text': # Check known type
wrong_type = type(value) is not type('a') try:
elif property_type == 'int' or property_type == 'boolean': wrong_type = (type(value) not in self._type_dict[property_type])
wrong_type = type(value) is not type(1) except KeyError:
elif property_type == 'float': wrong_type = 0
wrong_type = type(value) is not type(1.0) error_message = "Attribute %s is defined with unknown type %s" % \
elif property_type == 'lines' or property_type == 'tokens'\ (property_id, property_type)
or property_type == 'selection' or property_type == 'multiple selection': errors += [(object.getRelativeUrl(),
wrong_type = type(value) is not type([]) and type(value) is not type(()) 'PropertyTypeValidity inconsistency',
100, error_message)]
if wrong_type: if wrong_type:
error_message = "Attribute %s should be of type %s but is of type %s" % (property_id, # Type is wrong, so, raise constraint error
property_type, str(type(value))) error_message = \
"Attribute %s should be of type %s but is of type %s" % \
(property_id, property_type, str(type(value)))
if fixit: if fixit:
try: # XXX FIXME do not use except without exception name !
# try:
object.setProperty(property_id, value) object.setProperty(property_id, value)
error_message += " (Fixed)" error_message += " (Fixed)"
except: # except:
error_message += " (ERROR)" # error_message += " (ERROR)"
errors.append(self._generateError(object, error_message))
errors += [(object.getRelativeUrl(), 'PropertyTypeValidity inconsistency',
100, error_message)]
return errors return errors
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