diff --git a/product/ERP5/Document/BusinessTemplate.py b/product/ERP5/Document/BusinessTemplate.py index da2e6ec906fc02f04ef5e87add37d3333b46dbf6..454d395b071924a67b18fd4730f6b0e3030042fa 100644 --- a/product/ERP5/Document/BusinessTemplate.py +++ b/product/ERP5/Document/BusinessTemplate.py @@ -101,6 +101,7 @@ catalog_method_list = ('_is_catalog_list_method_archive', catalog_method_filter_list = ('_filter_expression_archive', '_filter_expression_instance_archive', + '_filter_expression_cache_key_archive', '_filter_type_archive',) INSTALLED_BT_FOR_DIFF = 'installed_bt_for_diff' @@ -2150,9 +2151,8 @@ class CatalogMethodTemplateItem(ObjectTemplateItem): self._method_properties = PersistentMapping() self._is_filtered_archive = PersistentMapping() - self._filter_expression_archive = PersistentMapping() - self._filter_expression_instance_archive = PersistentMapping() - self._filter_type_archive = PersistentMapping() + for method in catalog_method_filter_list: + setattr(self, method, PersistentMapping()) def _extractMethodProperties(self, catalog, method_id): """Extracts properties for a given method in the catalog. @@ -2184,20 +2184,14 @@ class CatalogMethodTemplateItem(ObjectTemplateItem): method_id = obj.id self._method_properties[method_id] = self._extractMethodProperties( catalog, method_id) - self._is_filtered_archive[method_id] = 0 - if catalog.filter_dict.has_key(method_id): - if catalog.filter_dict[method_id]['filtered']: - self._is_filtered_archive[method_id] = \ - catalog.filter_dict[method_id]['filtered'] - self._filter_expression_archive[method_id] = \ - catalog.filter_dict[method_id]['expression'] - self._filter_expression_instance_archive[method_id] = \ - catalog.filter_dict[method_id]['expression_instance'] - self._filter_type_archive[method_id] = \ - catalog.filter_dict[method_id]['type'] + filter = catalog.filter_dict.get(method_id, {}) + self._is_filtered_archive[method_id] = filter.get('filtered', 0) + for method in catalog_method_filter_list: + property = method[8:-8] + if property in filter: + getattr(self, method)[method_id] = filter[property] def generateXml(self, path): - catalog = _getCatalogValue(self) obj = self._objects[path] method_id = obj.id xml_data = '<catalog_method>' @@ -2207,23 +2201,18 @@ class CatalogMethodTemplateItem(ObjectTemplateItem): xml_data += '\n <value>%s</value>' %(value,) xml_data += '\n </item>' - if catalog.filter_dict.has_key(method_id): - if catalog.filter_dict[method_id]['filtered']: + if self._is_filtered_archive.get(method_id): xml_data += '\n <item key="_is_filtered_archive" type="int">' xml_data += '\n <value>1</value>' xml_data += '\n </item>' for method in catalog_method_filter_list: - try: - value = getattr(self, method, '')[method_id] - except KeyError: - # the method has no key - continue if method != '_filter_expression_instance_archive': - if type(value) in (type(''), type(u'')): + value = getattr(self, method, {}).get(method_id) + if isinstance(value, basestring): xml_data += '\n <item key="%s" type="str">' %(method,) xml_data += '\n <value>%s</value>' %(str(value)) xml_data += '\n </item>' - elif type(value) in (type(()), type([])): + elif value: xml_data += '\n <item key="%s" type="tuple">'%(method) for item in value: xml_data += '\n <value>%s</value>' %(str(item)) @@ -2323,12 +2312,14 @@ class CatalogMethodTemplateItem(ObjectTemplateItem): expr_instance = None else: expr_instance = self._filter_expression_instance_archive[method_id] - filter_type = self._filter_type_archive[method_id] catalog.filter_dict[method_id] = PersistentMapping() catalog.filter_dict[method_id]['filtered'] = 1 catalog.filter_dict[method_id]['expression'] = expression catalog.filter_dict[method_id]['expression_instance'] = expr_instance - catalog.filter_dict[method_id]['type'] = filter_type + catalog.filter_dict[method_id]['expression_cache_key'] = \ + self._filter_expression_cache_key_archive.get(method_id, ()) + catalog.filter_dict[method_id]['type'] = \ + self._filter_type_archive.get(method_id, ()) elif method_id in catalog.filter_dict.keys(): catalog.filter_dict[method_id]['filtered'] = 0 @@ -2420,21 +2411,16 @@ class CatalogMethodTemplateItem(ObjectTemplateItem): value = str(method.getElementsByTagName('value')[0].childNodes[0].data) else: value = '' - key = str(key) elif key_type == "int": value = int(method.getElementsByTagName('value')[0].childNodes[0].data) - key = str(key) elif key_type == "tuple": - value = [] - value_list = method.getElementsByTagName('value') - for item in value_list: - value.append(item.childNodes[0].data) + value = tuple(item.childNodes[0].data + for item in method.getElementsByTagName('value')) else: LOG('BusinessTemplate import CatalogMethod, type unknown', 0, key_type) continue if key in catalog_method_list or key in catalog_method_filter_list: - dict = getattr(self, key, {}) - dict[id] = value + getattr(self, key)[id] = value else: # new style key self._method_properties.setdefault(id, PersistentMapping())[key] = 1 diff --git a/product/ERP5/tests/testBusinessTemplate.py b/product/ERP5/tests/testBusinessTemplate.py index 1161b540c09c5262cdfb5e321335d68bb7b39878..ae7b6d0cac6934079f7f406943f8838b8342cd2e 100644 --- a/product/ERP5/tests/testBusinessTemplate.py +++ b/product/ERP5/tests/testBusinessTemplate.py @@ -1381,12 +1381,13 @@ class TestBusinessTemplate(ERP5TypeTestCase, LogInterceptor): sql_uncatalog_object.sort() catalog.sql_uncatalog_object = tuple(sql_uncatalog_object) # set filter for this method - expression = 'python: isMovement' + expression = 'python: context.isPredicate()' expr_instance = Expression(expression) catalog.filter_dict[method_id] = PersistentMapping() catalog.filter_dict[method_id]['filtered'] = 1 catalog.filter_dict[method_id]['expression'] = expression catalog.filter_dict[method_id]['expression_instance'] = expr_instance + catalog.filter_dict[method_id]['expression_cache_key'] = 'portal_type', catalog.filter_dict[method_id]['type'] = [] @@ -1410,12 +1411,13 @@ class TestBusinessTemplate(ERP5TypeTestCase, LogInterceptor): sql_uncatalog_object.sort() catalog.sql_uncatalog_object = tuple(sql_uncatalog_object) # set filter for this method - expression = 'python: isDelivery' + expression = 'python: context.isDelivery()' expr_instance = Expression(expression) catalog.filter_dict[method_id] = PersistentMapping() catalog.filter_dict[method_id]['filtered'] = 1 catalog.filter_dict[method_id]['expression'] = expression catalog.filter_dict[method_id]['expression_instance'] = expr_instance + catalog.filter_dict[method_id]['expression_cache_key'] = 'portal_type', catalog.filter_dict[method_id]['type'] = [] def stepCreateNewCatalogMethod(self, sequence=None, sequence_list=None, **kw): @@ -1494,15 +1496,15 @@ class TestBusinessTemplate(ERP5TypeTestCase, LogInterceptor): self.failUnless(catalog is not None) method_id = sequence.get('zsql_method_id', None) zsql_method = catalog._getOb(method_id, None) - self.failUnless(zsql_method is not None) + self.assertNotEqual(zsql_method, None) # check catalog properties self.failUnless(method_id in catalog.sql_uncatalog_object) # check filter - self.failUnless(method_id in catalog.filter_dict.keys()) filter_dict = catalog.filter_dict[method_id] self.assertEqual(filter_dict['filtered'], 1) - self.assertEqual(filter_dict['expression'], 'python: isMovement') - self.assertEqual(filter_dict['type'], []) + self.assertEqual(filter_dict['expression'], 'python: context.isPredicate()') + self.assertEqual(filter_dict['expression_cache_key'], ('portal_type',)) + self.assertEqual(filter_dict['type'], ()) def stepCheckUpdatedCatalogMethodExists(self, sequence=None, sequence_list=None, **kw): """ @@ -1513,15 +1515,15 @@ class TestBusinessTemplate(ERP5TypeTestCase, LogInterceptor): self.failUnless(catalog is not None) method_id = sequence.get('zsql_method_id', None) zsql_method = catalog._getOb(method_id, None) - self.failUnless(zsql_method is not None) + self.assertNotEqual(zsql_method, None) # check catalog properties self.failUnless(method_id in catalog.sql_uncatalog_object) # check filter - self.failUnless(method_id in catalog.filter_dict.keys()) filter_dict = catalog.filter_dict[method_id] self.assertEqual(filter_dict['filtered'], 1) - self.assertEqual(filter_dict['expression'], 'python: isDelivery') - self.assertEqual(filter_dict['type'], []) + self.assertEqual(filter_dict['expression'], 'python: context.isDelivery()') + self.assertEqual(filter_dict['expression_cache_key'], ('portal_type',)) + self.assertEqual(filter_dict['type'], ()) def stepCheckCatalogMethodRemoved(self, sequence=None, sequence_list=None, **kw): """ diff --git a/product/ZSQLCatalog/SQLCatalog.py b/product/ZSQLCatalog/SQLCatalog.py index d7f4918cfe2425f5df8b233b3ec16ca67647f091..1d5dad5a5b2ab6e9e863f179d2a475124dc47743 100644 --- a/product/ZSQLCatalog/SQLCatalog.py +++ b/product/ZSQLCatalog/SQLCatalog.py @@ -36,6 +36,8 @@ import sys import urllib import string import pprint +import re +import warnings from cStringIO import StringIO from xml.dom.minidom import parse from xml.sax.saxutils import escape, quoteattr @@ -62,6 +64,7 @@ try: from Products.CMFCore.Expression import Expression from Products.PageTemplates.Expressions import getEngine from Products.CMFCore.utils import getToolByName + new_context_search = re.compile(r'\bcontext\b').search withCMF = 1 except ImportError: withCMF = 0 @@ -1368,8 +1371,7 @@ class Catalog(Folder, if method_id_list is None: method_id_list = self.sql_catalog_object_list - econtext_cache = {} - expression_result_cache = {} + econtext = getEngine().getContext() argument_cache = {} try: @@ -1377,56 +1379,71 @@ class Catalog(Folder, enableReadOnlyTransactionCache(self) filter_dict = self.filter_dict - isMethodFiltered = self.isMethodFiltered + catalogged_object_list_cache = {} for method_name in method_id_list: - if isMethodFiltered(method_name): - catalogged_object_list = [] - append = catalogged_object_list.append + # We will check if there is an filter on this + # method, if so we may not call this zsqlMethod + # for this object + expression = None + try: filter = filter_dict[method_name] - type_set = frozenset(filter['type']) or None - expression = filter['expression_instance'] - expression_cache_key_list = filter.get('expression_cache_key', '').split() - for object in object_list: - # We will check if there is an filter on this - # method, if so we may not call this zsqlMethod - # for this object - if type_set is not None and object.getPortalType() not in type_set: - continue - elif expression is not None: + if filter['filtered']: + if filter.get('type'): + expression = Expression('python: context.getPortalType() in ' + + repr(tuple(filter['type']))) + LOG('SQLCatalog', WARNING, + "Convert deprecated type filter for %r into %r expression" + % (method_name, expression.text)) + filter['type'] = () + filter['expression'] = expression.text + filter['expression_instance'] = expression + else: + expression = filter['expression_instance'] + except KeyError: + pass + if expression is None: + catalogged_object_list = object_list + else: + text = expression.text + catalogged_object_list = catalogged_object_list_cache.get(text) + if catalogged_object_list is None: + catalogged_object_list_cache[text] = catalogged_object_list = [] + append = catalogged_object_list.append + old_context = new_context_search(text) is None + if old_context: + warnings.warn("Filter expression for %r (%r): using variables" + " other than 'context' is deprecated and slower." + % (method_name, text), DeprecationWarning) + expression_cache_key_list = filter.get('expression_cache_key', ()) + expression_result_cache = {} + for object in object_list: if expression_cache_key_list: - # We try to save results of expressions by portal_type - # or by anyother key which can prevent us from evaluating - # expressions. This cache is built each time we reindex + # Expressions are slow to evaluate because they are executed + # in restricted environment. So we try to save results of + # expressions by portal_type or any other key. + # This cache is built each time we reindex # objects but we could also use over multiple transactions # if this can improve performance significantly + # ZZZ - we could find a way to compute this once only + cache_key = tuple(object.getProperty(key) for key + in expression_cache_key_list) try: - cache_key = (object.getProperty(key, None) for key in expression_cache_key_list) - # ZZZ - we could find a way to compute this once only - cache_key = (method_name, tuple(cache_key)) - result = expression_result_cache[cache_key] - compute_result = 0 + if expression_result_cache[cache_key]: + append(object) + continue except KeyError: - cache_result = 1 - compute_result = 1 + pass + if old_context: + result = expression(self.getExpressionContext(object)) else: - cache_result = 0 - compute_result = 1 - if compute_result: - try: - econtext = econtext_cache[object.uid] - except KeyError: - econtext = self.getExpressionContext(object) - econtext_cache[object.uid] = econtext + econtext.setLocal('context', object) result = expression(econtext) - if cache_result: + if expression_cache_key_list: expression_result_cache[cache_key] = result - if not result: - continue - append(object) - else: - catalogged_object_list = object_list + if result: + append(object) - if len(catalogged_object_list) == 0: + if not catalogged_object_list: continue #LOG('catalogObjectList', 0, 'method_name = %s' % (method_name,)) @@ -2325,27 +2342,17 @@ class Catalog(Folder, # We will first look if the filter is activated if not self.filter_dict.has_key(id): self.filter_dict[id] = PersistentMapping() - self.filter_dict[id]['filtered'] = 0 - self.filter_dict[id]['type'] = [] - self.filter_dict[id]['expression'] = "" - self.filter_dict[id]['expression_cache_key'] = "portal_type" if REQUEST.has_key('%s_box' % id): self.filter_dict[id]['filtered'] = 1 else: self.filter_dict[id]['filtered'] = 0 - if REQUEST.has_key('%s_expression' % id): - expression = REQUEST['%s_expression' % id] - if expression == "": - self.filter_dict[id]['expression'] = "" - self.filter_dict[id]['expression_instance'] = None - else: - expr_instance = Expression(expression) - self.filter_dict[id]['expression'] = expression - self.filter_dict[id]['expression_instance'] = expr_instance + expression = REQUEST.get('%s_expression' % id, '').strip() + self.filter_dict[id]['expression'] = expression + if expression: + self.filter_dict[id]['expression_instance'] = Expression(expression) else: - self.filter_dict[id]['expression'] = "" self.filter_dict[id]['expression_instance'] = None if REQUEST.has_key('%s_type' % id): @@ -2356,14 +2363,8 @@ class Catalog(Folder, else: self.filter_dict[id]['type'] = [] - if REQUEST.has_key('%s_expression_cache_key' % id): - expression_cache_key = REQUEST['%s_expression_cache_key' % id] - if expression_cache_key == "": - self.filter_dict[id]['expression_cache_key'] = expression_cache_key - else: - self.filter_dict[id]['expression_cache_key'] = "" - else: - self.filter_dict[id]['expression_cache_key'] = "" + self.filter_dict[id]['expression_cache_key'] = \ + tuple(sorted(REQUEST.get('%s_expression_cache_key' % id, '').split())) if RESPONSE and URL1: RESPONSE.redirect(URL1 + '/manage_catalogFilter?manage_tabs_message=Filter%20Changed') @@ -2406,7 +2407,7 @@ class Catalog(Folder, self.filter_dict = PersistentMapping() return "" try: - return self.filter_dict[method_name]['expression_cache_key'] + return ' '.join(self.filter_dict[method_name]['expression_cache_key']) except KeyError: return "" return "" @@ -2426,6 +2427,7 @@ class Catalog(Folder, def isPortalTypeSelected(self, method_name, portal_type): """ Returns true if the portal type is selected for this method. + XXX deprecated """ if withCMF: if getattr(aq_base(self), 'filter_dict', None) is None: @@ -2440,6 +2442,7 @@ class Catalog(Folder, def getFilteredPortalTypeList(self, method_name): """ Returns the list of portal types which define the filter. + XXX deprecated """ if withCMF: if getattr(aq_base(self), 'filter_dict', None) is None: @@ -2469,12 +2472,12 @@ class Catalog(Folder, def getExpressionContext(self, ob): ''' An expression context provides names for TALES expressions. + XXX deprecated ''' if withCMF: data = { 'here': ob, 'container': aq_parent(aq_inner(ob)), - 'nothing': None, #'root': ob.getPhysicalRoot(), #'request': getattr( ob, 'REQUEST', None ), #'modules': SecureModuleImporter,