Changeset 49304
- Timestamp:
- 10/04/06 15:40:37 (3 years ago)
- Files:
-
- CPS3/products/CPSDirectory/trunk/CHANGES (modified) (2 diffs)
- CPS3/products/CPSDirectory/trunk/tests/test_utils.py (modified) (1 diff)
- CPS3/products/CPSDirectory/trunk/utils.py (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
CPS3/products/CPSDirectory/trunk/CHANGES
r49141 r49304 4 4 New features 5 5 ~~~~~~~~~~~~ 6 - utils.QueryMatcher knows about None, booleans, ints, and negated 6 7 - 7 8 Bug fixes … … 10 11 New internal features 11 12 ~~~~~~~~~~~~~~~~~~~~~ 12 - templates succession fy creation now customizable 13 - templates succession fy creation now customizable CPS3/products/CPSDirectory/trunk/tests/test_utils.py
r31560 r49304 22 22 from Testing.ZopeTestCase import doctest 23 23 24 from Products.CPSDirectory.utils import QueryMatcher 25 24 26 class DirectoryUtilsTestCase(unittest.TestCase): 25 pass 27 28 def testNegate_str(self): 29 query = {'sn': {'query': 'tutu', 'negate': True}} 30 matcher = QueryMatcher(query) 31 self.assert_(matcher.match({'sn': 'blob', 'givenName': '1'})) 32 self.failIf(matcher.match({'sn': 'tutu', 'givenName': '2'})) 33 34 def testNegate_int(self): 35 query = {'sn': {'query': 1, 'negate': True}} 36 matcher = QueryMatcher(query) 37 self.assert_(matcher.match({'sn': 34, 'givenName': '1'})) 38 self.failIf(matcher.match({'sn': 1, 'givenName': '2'})) 39 40 def testNegate_list(self): 41 query = {'sn': {'query': ('a', 'b'), 'negate': True}} 42 matcher = QueryMatcher(query) 43 self.assert_(matcher.match({'sn': 'c', 'givenName': '1'})) 44 self.failIf(matcher.match({'sn': 'a', 'givenName': '2'})) 26 45 27 46 CPS3/products/CPSDirectory/trunk/utils.py
r48968 r49304 22 22 23 23 from types import ListType, TupleType, StringType 24 import re 25 import operator 26 from types import NoneType 27 24 28 from Products.CPSUtil.text import toAscii 25 29 26 class QueryMatcher: 30 def operator_in(a, b): 31 # operator.contains with reversed operands 32 return a in b 33 34 def operator_notin(a, b): 35 return a not in b 36 37 38 class QueryMatcher(object): 27 39 """ Hold/prepare a query and allow to match entries against it. 28 40 … … 60 72 61 73 def __init__(self, query, accepted_keys=None, substring_keys=None): 74 self._substring_keys = substring_keys 62 75 _query = {} 63 match_types = {}76 ops = {} 64 77 for key, value in query.items(): 65 78 if accepted_keys is not None and key not in accepted_keys: 66 79 continue 67 if not value:68 # Ignore empty searches80 value, op = self._findType(key, value) 81 if op is None: 69 82 continue 70 if isinstance(value, StringType): 71 if substring_keys is not None and key in substring_keys: 72 match_types[key] = 'substring' 73 value = toAscii(value).lower() 74 else: 75 match_types[key] = 'exact' 76 elif isinstance(value, ListType) or isinstance(value, TupleType): 77 match_types[key] = 'list' 78 else: 79 raise ValueError("Bad value %s for '%s'" % (`value`, key)) 83 ops[key] = op 80 84 _query[key] = value 81 85 self.query = _query 82 self.match_types = match_types 86 self.ops = ops 87 88 def _findType(self, key, value, negate=False): 89 """Find op and value. 90 """ 91 if value in ('', None): # XXX 92 if negate: 93 raise NotImplementedError 94 # Ignore empty searches 95 return value, None 96 elif isinstance(value, basestring): 97 if (self._substring_keys is not None 98 and key in self._substring_keys): 99 if negate: 100 raise NotImplementedError 101 op = 'substring' 102 value = value.lower() 103 else: 104 if negate: 105 op = operator.ne 106 else: 107 op = operator.eq 108 elif isinstance(value, (int, long, bool)): 109 if negate: 110 op = operator.ne 111 else: 112 op = operator.eq 113 elif isinstance(value, (list, tuple)): 114 if negate: 115 op = operator_notin 116 else: 117 op = operator_in 118 elif isinstance(value, dict) and 'query' in value: 119 if negate and value.get('negate'): 120 raise ValueError("Cannot double negate") 121 query = value['query'] 122 return self._findType(key, query, negate=True) 123 else: 124 raise ValueError("Bad value %s for '%s'" % (`value`, key)) 125 return value, op 83 126 84 127 def getKeysSet(self): … … 88 131 """ Does the entry match the query ? Boolean valued. 89 132 """ 90 search_types = self.match_types91 ok = 1133 ops = self.ops 134 ok = True 92 135 for key, value in self.query.items(): 93 searched = entry.get(key) 94 if searched is None: 95 ok = 0 136 if key not in entry: 137 ok = False 96 138 break 97 if isinstance(searched, StringType): 139 searched = entry[key] 140 if isinstance(searched, (basestring, int, long, NoneType)): 141 # bool subclasses int 98 142 searched = (searched,) 99 143 matched = 0 144 value_re = None 145 op = ops[key] 146 if isinstance(value, basestring): 147 if op == 'substring': 148 value = toAscii(value).lower() 149 if '*' in value or '?' in value: 150 regexp = re.escape(value) 151 regexp = regexp.replace('\\?', '.?') 152 regexp = regexp.replace('\\*', '.*') 153 value_re = re.compile(regexp) 154 100 155 for item in searched: 101 156 # Wild cards like * are currently accepted 102 if search_types[key] == 'list': 103 matched = item in value 104 elif search_types[key] == 'substring': 157 if op == 'substring': 105 158 matched = value in toAscii(item).lower() or value == '*' 106 else: # search_types[key] == 'exact':107 matched = item == value159 else: # op is an operator 160 matched = op(item, value) 108 161 if matched: 109 162 break 110 163 if not matched: 111 ok = 0164 ok = False 112 165 break 113 return ok == 1166 return ok
