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>
# 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(Constraint):
class AttributeEquality(PropertyExistence):
"""
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.
attribute values.
Configuration example:
{ 'id' : 'title',
'description' : 'Title must be "ObjectTitle"',
'type' : 'AttributeEquality',
'title' : 'ObjectTitle',
},
"""
def checkConsistency(self, object, fixit = 0):
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)
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)))
errors = PropertyExistence.checkConsistency(self, object, fixit=fixit)
for attribute_name, attribute_value in self.constraint_definition.items():
error_message = None
if not object.hasProperty(attribute_name):
error_message = "Attribute %s is not defined" % attribute_name
else:
# 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
else:
# Other type
identical = (attribute_value == object.getProperty(attribute_name))
if not identical:
error_message = "Attribute %s is %s but sould be %s" % (attribute_name,
object.getProperty(attribute_name), attribute_value)
# 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 += [(object.getRelativeUrl(), 'AttributeEquality inconsistency', 100, error_message)]
errors.append(self._generateError(object, error_message))
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>
# 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
......@@ -30,39 +31,34 @@ from Constraint import Constraint
class CategoryExistence(Constraint):
"""
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.
"""
def __init__(self, **constraint_definition):
"""
We need the definition list of the constraint
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,
},
"""
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.
"""
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
# 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 = []
error_message = None
# Raise error
if error_message:
errors.append(self._generateError(object, error_message))
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>
# 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
......@@ -31,52 +32,45 @@ 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 Organisation has at
least one address.
"""
def __init__(self, **constraint_definition):
"""
We need the definition list of the constraint
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',)
},
"""
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.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)]
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
##############################################################################
#
# 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.
"""
def __init__(self, **constraint_definition):
"""
We need the definition list of the constraint
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',)
},
"""
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
##############################################################################
#
# 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)
##############################################################################
#
# 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
......@@ -30,39 +31,37 @@ from Constraint import Constraint
class PropertyExistence(Constraint):
"""
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.
"""
def __init__(self, **constraint_definition):
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,
},
"""
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.
"""
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
# 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 = []
error_message = None
# Return error
error = self._generateError(object, error_message)
if error is not None:
errors.append(error)
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>
# 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,43 +29,40 @@
##############################################################################
from Constraint import Constraint
from string import split
class PropertyTypeValidity(Constraint):
"""
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 constraint class allows to check / fix type of each
attributes define in the PropertySheets.
This Constraint is always created in ERP5Type/Utils.py
"""
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,
each string corresponds to an error.
We will make sure that each non None constraint_definition is satisfied
(equality)
Make sure instance has no _properties
"""
# XXX FIXME what is _properties ?
errors = []
# Make sure instance has no _properties
if '_properties' in object.__dict__:
# Remove _properties
error_message = "Instance has local _properties property"
if fixit:
try:
# XXX FIXME we have to set exception name !
# try:
local_properties = object._properties
del object._properties
object._local_properties = []
......@@ -73,40 +71,47 @@ class PropertyTypeValidity(Constraint):
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)]
# except:
# error_message += " (ERROR)"
errors.append(self._generateError(object, 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():
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(())
# 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:
error_message = "Attribute %s should be of type %s but is of type %s" % (property_id,
property_type, str(type(value)))
# 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:
# XXX FIXME do not use except without exception name !
# try:
object.setProperty(property_id, value)
error_message += " (Fixed)"
except:
error_message += " (ERROR)"
errors += [(object.getRelativeUrl(), 'PropertyTypeValidity inconsistency',
100, error_message)]
# except:
# error_message += " (ERROR)"
errors.append(self._generateError(object, error_message))
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