[Erp5-report] r40636 arnaud.fontaine - in /erp5/trunk/products/ERP5Type: Core/ PropertyShee...

nobody at svn.erp5.org nobody at svn.erp5.org
Thu Nov 25 05:53:46 CET 2010


Author: arnaud.fontaine
Date: Thu Nov 25 05:53:46 2010
New Revision: 40636

URL: http://svn.erp5.org?rev=40636&view=rev
Log:
Add Attribute Equality Constraint for ZODB Property Sheets

Added:
    erp5/trunk/products/ERP5Type/Core/AttributeEqualityConstraint.py
      - copied, changed from r40345, erp5/trunk/products/ERP5Type/Constraint/AttributeEquality.py
    erp5/trunk/products/ERP5Type/PropertySheet/AttributeEqualityConstraint.py
Modified:
    erp5/trunk/products/ERP5Type/PropertySheet/__init__.py
    erp5/trunk/products/ERP5Type/mixin/constraint.py
    erp5/trunk/products/ERP5Type/tests/testDynamicClassGeneration.py

Copied: erp5/trunk/products/ERP5Type/Core/AttributeEqualityConstraint.py (from r40345, erp5/trunk/products/ERP5Type/Constraint/AttributeEquality.py)
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Core/AttributeEqualityConstraint.py?p2=erp5/trunk/products/ERP5Type/Core/AttributeEqualityConstraint.py&p1=erp5/trunk/products/ERP5Type/Constraint/AttributeEquality.py&r1=40345&r2=40636&rev=40636&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Constraint/AttributeEquality.py [utf8] (original)
+++ erp5/trunk/products/ERP5Type/Core/AttributeEqualityConstraint.py [utf8] Thu Nov 25 05:53:46 2010
@@ -1,9 +1,10 @@
 ##############################################################################
 #
-# Copyright (c) 2002, 2005 Nexedi SARL and Contributors. All Rights Reserved.
-#                    Sebastien Robin <seb at nexedi.com>
-#                    Jean-Paul Smets <jp at nexedi.com>
-#                    Romain Courteaud <romain at nexedi.com>
+# Copyright (c) 2002-2010 Nexedi SARL and Contributors. All Rights Reserved.
+#                         Sebastien Robin <seb at nexedi.com>
+#                         Jean-Paul Smets <jp at nexedi.com>
+#                         Romain Courteaud <romain at nexedi.com>
+#                         Arnaud Fontaine <arnaud.fontaine at 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,21 +29,40 @@
 #
 ##############################################################################
 
-from PropertyExistence import PropertyExistence
+from Products.ERP5Type.mixin.constraint import ConstraintMixin
+from AccessControl import ClassSecurityInfo
+from Products.ERP5Type import Permissions, PropertySheet
+from Products.CMFCore.Expression import Expression
+from Products.ERP5Type.Utils import createExpressionContext
 
-class AttributeEquality(PropertyExistence):
+class AttributeEqualityConstraint(ConstraintMixin):
   """
-    This constraint class allows to check / fix
-    attribute values.
-    Configuration example:
-    { 'id'            : 'title',
-      'description'   : 'Title must be "ObjectTitle"',
-      'type'          : 'AttributeEquality',
-      'title'         : 'ObjectTitle',
-      'condition'     : 'python: object.getPortalType() == 'Foo',
-    },
+  This constraint checks the values of a given attribute name on this
+  object. This is only relevant for ZODB Property Sheets (filesystem
+  Property Sheets rely on Products.ERP5Type.Constraint.PropertyExistence
+  instead). Note that the attribute expected value is now a TALES
+  Expression to be able to use any Python type and not only strings.
+
+  For example, if we would like to check whether the attribute 'title'
+  has 'ObjectTitle' as its value, we would create a 'Attribute
+  Equality Constraint' within that Property Sheet and set 'title' as
+  the 'Attribute Name' and 'python: "ObjectTitle"' as the 'Attribute
+  Value', then set the 'Predicate' if necessary (known as 'condition'
+  for filesystem Property Sheets).
   """
+  meta_type = 'ERP5 Attribute Equality Constraint'
+  portal_type = 'Attribute Equality Constraint'
+
+  # Declarative security
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.AccessContentsInformation)
 
+  property_sheets = (PropertySheet.SimpleItem,
+                     PropertySheet.Predicate,
+                     PropertySheet.Reference,
+                     PropertySheet.AttributeEqualityConstraint)
+
+  # Define by default error messages
   _message_id_list = ['message_invalid_attribute_value',
                       'message_invalid_attribute_value_fixed']
   message_invalid_attribute_value = "Attribute ${attribute_name} "\
@@ -50,44 +70,60 @@ class AttributeEquality(PropertyExistenc
   message_invalid_attribute_value_fixed = "Attribute ${attribute_name} "\
        "value is ${current_value} but should be ${expected_value} (Fixed)"
 
-  def checkConsistency(self, obj, fixit=0):
-    """Check the object's consistency.
-      We will make sure that each non None constraint_definition is 
-      satisfied (equality)
+  security.declareProtected(Permissions.AccessContentsInformation,
+                            'checkConsistency')
+  def checkConsistency(self, obj, fixit=False):
+    """
+    Check the object's consistency.
     """
-    if not self._checkConstraintCondition(obj):
+    if not self.test(obj):
       return []
-    errors = PropertyExistence.checkConsistency(self, obj, fixit=fixit)
-    for attribute_name, expected_value in self.constraint_definition.items():
-      message_id = None
-      mapping = dict()
-      # If property does not exist, error will be raised by 
-      # PropertyExistence Constraint.
-      if obj.hasProperty(attribute_name):
-        identical = 1
-        if isinstance(expected_value, (list, tuple)):
-          # List type
-          if len(obj.getProperty(attribute_name)) != len(expected_value):
-            identical = 0
-          else:
-            for item in obj.getProperty(attribute_name):
-              if item not in expected_value:
-                identical = 0
-                break
+
+    attribute_name = self.getConstraintAttributeName()
+
+    # If property does not exist, error will be raised by
+    # PropertyExistence Constraint, but the value has to be set at
+    # least once as there is no need to perform any check if it is the
+    # default value
+    if obj.hasProperty(attribute_name):
+      identical = True
+
+      # The expected value of the attribute is a TALES Expression
+      attribute_expected_value_expression = Expression(
+        self.getConstraintAttributeValue())
+
+      attribute_expected_value = attribute_expected_value_expression(
+        createExpressionContext(obj))
+
+      attribute_value = obj.getProperty(attribute_name)
+
+      if isinstance(attribute_expected_value, (list, tuple)):
+        # List type
+        if len(attribute_value) != len(attribute_expected_value):
+          identical = False
         else:
-          # Other type
-          identical = (expected_value == obj.getProperty(attribute_name))
-        if not identical:
-          message_id = 'message_invalid_attribute_value'
-          mapping(attribute_name=attribute_name,
-                  attribute_value=obj.getProperty(attribute_name),
-                  expected_value=expected_value)
-      # Generate error
-      if message_id is not None:
+          for item in attribute_value:
+            if item not in attribute_expected_value:
+              identical = False
+              break
+      else:
+        # Other primitive type
+        identical = (attribute_expected_value == attribute_value)
+
+      if not identical:
+        # Generate error and fix it if required
         if fixit:
-          obj._setProperty(attribute_name, expected_value)
+          obj._setProperty(attribute_name, attribute_expected_value)
           message_id = 'message_invalid_attribute_value_fixed'
-        errors.append(self._generateError(obj,
-                        self._getMessage(message_id), mapping))
-    return errors
+        else:
+          message_id = 'message_invalid_attribute_value'
+
+        error = self._generateError(
+          obj, self._getMessage(message_id),
+          dict(attribute_name=attribute_name,
+               attribute_value=attribute_value,
+               expected_value=attribute_expected_value))
+
+        return [error]
 
+    return []

Added: erp5/trunk/products/ERP5Type/PropertySheet/AttributeEqualityConstraint.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/PropertySheet/AttributeEqualityConstraint.py?rev=40636&view=auto
==============================================================================
--- erp5/trunk/products/ERP5Type/PropertySheet/AttributeEqualityConstraint.py (added)
+++ erp5/trunk/products/ERP5Type/PropertySheet/AttributeEqualityConstraint.py [utf8] Thu Nov 25 05:53:46 2010
@@ -0,0 +1,46 @@
+##############################################################################
+#
+# Copyright (c) 2010 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Arnaud Fontaine <arnaud.fontaine at 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.
+#
+##############################################################################
+
+class AttributeEqualityConstraint:
+    """
+    Define an Attribute Equality Constraint for ZODB Property Sheets
+    """
+    _properties = (
+        {   'id': 'constraint_attribute_name',
+            'type': 'string',
+            'description' : 'Attribute name whose values are checked' },
+        {   'id': 'constraint_attribute_value',
+            'type': 'string',
+            'description' : 'Valid values of the Attribute' },
+        {   'id': 'message_invalid_attribute_value',
+            'type': 'string',
+            'description' : 'Error message when the attribute value is invalid' },
+        {   'id': 'message_invalid_attribute_value_fixed',
+            'type': 'string',
+            'description' : 'Error message when the attribute value is invalid but has been fixed' },
+        )

Modified: erp5/trunk/products/ERP5Type/PropertySheet/__init__.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/PropertySheet/__init__.py?rev=40636&r1=40635&r2=40636&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/PropertySheet/__init__.py [utf8] (original)
+++ erp5/trunk/products/ERP5Type/PropertySheet/__init__.py [utf8] Thu Nov 25 05:53:46 2010
@@ -19,3 +19,4 @@ from AcquiredProperty import AcquiredPro
 from DynamicCategoryProperty import DynamicCategoryProperty
 from CategoryExistenceConstraint import CategoryExistenceConstraint
 from PropertyExistenceConstraint import PropertyExistenceConstraint
+from AttributeEqualityConstraint import AttributeEqualityConstraint

Modified: erp5/trunk/products/ERP5Type/mixin/constraint.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/mixin/constraint.py?rev=40636&r1=40635&r2=40636&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/mixin/constraint.py [utf8] (original)
+++ erp5/trunk/products/ERP5Type/mixin/constraint.py [utf8] Thu Nov 25 05:53:46 2010
@@ -42,6 +42,10 @@ class ConstraintMixin(Predicate):
   Mixin Constraint implementation (only relevant for ZODB Property
   sheets, use Products.ERP5Type.Constraint instead for filesystem
   Property Sheets) relying on Predicate
+
+  @todo: Add code to import constraints requiring a new TALES
+         Expression field in predicate to be able to import
+         'condition' properly
   """
   # Declarative security
   security = ClassSecurityInfo()

Modified: erp5/trunk/products/ERP5Type/tests/testDynamicClassGeneration.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/tests/testDynamicClassGeneration.py?rev=40636&r1=40635&r2=40636&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/tests/testDynamicClassGeneration.py [utf8] (original)
+++ erp5/trunk/products/ERP5Type/tests/testDynamicClassGeneration.py [utf8] Thu Nov 25 05:53:46 2010
@@ -284,7 +284,10 @@ class TestZodbPropertySheet(ERP5TypeTest
       id=base_category_id, portal_type='Base Category')
 
     # Create a dummy sub-category
-    new_base_category.newContent(reference='sub_category',
+    new_base_category.newContent(reference='sub_category1',
+                                 portal_type='Category')
+
+    new_base_category.newContent(reference='sub_category2',
                                  portal_type='Category')
 
     if operation_type == 'change':
@@ -343,6 +346,25 @@ class TestZodbPropertySheet(ERP5TypeTest
       # XXX
       # constraint_portal_type=('TODO',))
 
+  def _newAttributeEqualityConstraint(self):
+    """
+    Create a new Attribute Equality Constraint within test Property
+    Sheet
+    """
+    # For testing primitive type as attribute value
+    self.test_property_sheet.newContent(
+      reference='test_attribute_equality_constraint',
+      portal_type='Attribute Equality Constraint',
+      constraint_attribute_name='title',
+      constraint_attribute_value='python: "my_valid_title"')
+
+    # For testing list type as attribute value
+    self.test_property_sheet.newContent(
+      reference='test_attribute_list_equality_constraint',
+      portal_type='Attribute Equality Constraint',
+      constraint_attribute_name='categories_list',
+      constraint_attribute_value='python: ("sub_category1", "sub_category2")')
+
   def afterSetUp(self):
     """
     Create a test Property Sheet (and its properties)
@@ -369,6 +391,10 @@ class TestZodbPropertySheet(ERP5TypeTest
       # Sheet
       self._newCategoryExistenceConstraint()
 
+      # Create an Attribute Equality Constraint in the test Property
+      # Sheet
+      self._newAttributeEqualityConstraint()
+
       # Create all the test Properties
       for operation_type in ('change', 'delete', 'assign'):
         self._newStandardProperty(operation_type)
@@ -503,19 +529,19 @@ class TestZodbPropertySheet(ERP5TypeTest
       # Category Property
       self.assertHasAttribute(new_person, 'setTestCategoryPropertyAssign')
 
-      new_person.setTestCategoryPropertyAssign('sub_category')
+      new_person.setTestCategoryPropertyAssign('sub_category1')
 
       self.assertEquals(new_person.getTestCategoryPropertyAssign(),
-                        'sub_category')
+                        'sub_category1')
 
       # Dynamic Category Property
       self.assertHasAttribute(new_person,
                               'setTestDynamicCategoryPropertyAssign')
 
-      new_person.setTestDynamicCategoryPropertyAssign('sub_category')
+      new_person.setTestDynamicCategoryPropertyAssign('sub_category1')
 
       self.assertEquals(new_person.getTestDynamicCategoryPropertyAssign(),
-                        'sub_category')
+                        'sub_category1')
 
     finally:
       # Perform a commit here because Workflow interactions keeps a
@@ -729,10 +755,6 @@ class TestZodbPropertySheet(ERP5TypeTest
                        constraint_reference,
                        setter_function,
                        value):
-    """
-    Check whether the given constraint has been properly defined and
-    checkConsistency() is correct
-    """
     constraint = self._getConstraintByReference(constraint_reference)
     self.failIfEqual(None, constraint)
     self.assertEquals(1, len(constraint.checkConsistency(self.test_module)))
@@ -745,9 +767,8 @@ class TestZodbPropertySheet(ERP5TypeTest
     Take the test module and check whether the Property Existence
     Constraint is available. Until the property has been set to a
     value, the constraint should fail
-
-    @see ERP5Type.Base.Base.hasProperty
     """
+    # See ERP5Type.Base.Base.hasProperty()
     self._checkConstraint('test_property_existence_constraint',
                           self.test_module.setTestStandardPropertyConstraint,
                           'foobar')
@@ -760,7 +781,38 @@ class TestZodbPropertySheet(ERP5TypeTest
     """
     self._checkConstraint('test_category_existence_constraint',
                           self.test_module.setTestCategoryPropertyConstraint,
-                          'sub_category')
+                          'sub_category1')
+
+  def testAttributeEqualityConstraint(self):
+    """
+    Take the test module and check whether the Attribute Equality
+    Constraint is available. Until the attribute to be checked has
+    been set to its expected value, the constraint should fail. The
+    purpose is to test only primitive types (e.g. not list)
+    """
+    # As checkConsistency calls hasProperty before checking the value,
+    # the property to be tested has to be set at least once (whatever
+    # the value)
+    self.test_module.setTitle('invalid_value')
+
+    self._checkConstraint('test_attribute_equality_constraint',
+                          self.test_module.setTitle,
+                          'my_valid_title')
+
+  def testAttributeListEqualityConstraint(self):
+    """
+    Take the test module and check whether the Attribute Equality
+    Constraint is available. Until the attribute to be checked has
+    been set to its expected value (a list of categories), the
+    constraint should fail. The purpose is to test only list types
+
+    @see testAttributeEqualityConstraint
+    """
+    self.test_module.setCategoryList(('sub_category1',))
+
+    self._checkConstraint('test_attribute_list_equality_constraint',
+                          self.test_module.setCategoryList,
+                          ('sub_category1', 'sub_category2'))
 
 TestZodbPropertySheet = skip("ZODB Property Sheets code is not enabled yet")(
   TestZodbPropertySheet)




More information about the Erp5-report mailing list