From 24d0370892e439216792dea2fefd0b1068edc98e Mon Sep 17 00:00:00 2001 From: Gaetan Delannay Date: Thu, 7 Jan 2010 20:25:18 +0100 Subject: [PATCH] Improved advanced search screen. --- gen/__init__.py | 7 +- gen/plone25/generator.py | 2 + gen/plone25/mixins/ToolMixin.py | 111 ++++++++++++++++++++++++++++---- gen/plone25/skin/search.pt | 6 +- gen/plone25/skin/widgets.pt | 79 ++++++++++++++++++++--- gen/po.py | 2 + 6 files changed, 182 insertions(+), 25 deletions(-) diff --git a/gen/__init__.py b/gen/__init__.py index f17fed0..5a40e6e 100755 --- a/gen/__init__.py +++ b/gen/__init__.py @@ -199,7 +199,12 @@ class String(Type): if complement: return (97 - (number % 97)) == checkNumber else: - return (number % 97) == checkNumber + # The check number can't be 0. In this case, we force it to be 97. + # This is the way Belgian bank account numbers work. I hope this + # behaviour is general enough to be implemented here. + mod97 = (number % 97) + if mod97 == 0: return checkNumber == 97 + else: return checkNumber == mod97 @staticmethod def MODULO_97(obj, value): return String._MODULO_97(obj, value) @staticmethod diff --git a/gen/plone25/generator.py b/gen/plone25/generator.py index f070577..09f8204 100644 --- a/gen/plone25/generator.py +++ b/gen/plone25/generator.py @@ -122,6 +122,8 @@ class Generator(AbstractGenerator): msg('search_results', '', msg.SEARCH_RESULTS), msg('search_results_descr', '', ' '), msg('search_new', '', msg.SEARCH_NEW), + msg('search_from', '', msg.SEARCH_FROM), + msg('search_to', '', msg.SEARCH_TO), msg('ref_invalid_index', '', msg.REF_INVALID_INDEX), msg('bad_int', '', msg.BAD_INT), msg('bad_float', '', msg.BAD_FLOAT), diff --git a/gen/plone25/mixins/ToolMixin.py b/gen/plone25/mixins/ToolMixin.py index 7e8de99..8995f04 100644 --- a/gen/plone25/mixins/ToolMixin.py +++ b/gen/plone25/mixins/ToolMixin.py @@ -1,7 +1,7 @@ # ------------------------------------------------------------------------------ import re, os, os.path, Cookie from appy.gen import Type, Search -from appy.gen.utils import FieldDescr, SomeObjects +from appy.gen.utils import FieldDescr, SomeObjects, sequenceTypes from appy.gen.plone25.mixins import AbstractMixin from appy.gen.plone25.mixins.FlavourMixin import FlavourMixin from appy.gen.plone25.wrappers import AbstractWrapper @@ -160,15 +160,30 @@ class ToolMixin(AbstractMixin): if search: # Add additional search criteria for fieldName, fieldValue in search.fields.iteritems(): + # Make the correspondance between the name of the field and the + # name of the corresponding index. attrName = fieldName if attrName == 'title': attrName = 'Title' elif attrName == 'description': attrName = 'Description' elif attrName == 'state': attrName = 'review_state' else: attrName = 'get%s%s'% (fieldName[0].upper(),fieldName[1:]) + # Express the field value in the way needed by the index if isinstance(fieldValue, basestring) and \ fieldValue.endswith('*'): v = fieldValue[:-1] - params[attrName] = {'query':[v,v+'Z'], 'range':'minmax'} + params[attrName] = {'query':(v,v+'Z'), 'range':'minmax'} + elif type(fieldValue) in sequenceTypes: + # We have a range of values instead of a single value + minv, maxv = fieldValue + rangev = 'minmax' + queryv = fieldValue + if minv == None: + rangev = 'max' + queryv = maxv + elif maxv == None: + rangev = 'min' + queryv = minv + params[attrName] = {'query':queryv, 'range':rangev} else: params[attrName] = fieldValue # Add a sort order if specified @@ -380,6 +395,48 @@ class ToolMixin(AbstractMixin): else: return False + def _searchValueIsEmpty(self, key): + '''Returns True if request value in key p_key can be considered as + empty.''' + rq = self.REQUEST.form + if key.endswith('*int'): + # We return True if "from" AND "to" values are empty. + toKey = '%s_to' % key[2:-4] + return not rq[key].strip() and not rq[toKey].strip() + elif key.endswith('*date'): + # We return True if "from" AND "to" values are empty. A value is + # considered as not empty if at least the year is specified. + toKey = '%s_to_year' % key[2:-5] + return not rq[key] and not rq[toKey] + else: + return not rq[key] + + def _getDateTime(self, year, month, day, setMin): + '''Gets a valid DateTime instance from date information coming from the + request as strings in p_year, p_month and p_day. Returns None if + p_year is empty. If p_setMin is True, when some + information is missing (month or day), we will replace it with the + minimum value (=1). Else, we will replace it with the maximum value + (=12, =31).''' + if not year: return None + if not month: + if setMin: month = 1 + else: month = 12 + if not day: + if setMin: day = 1 + else: day = 31 + DateTime = self.getProductConfig().DateTime + # We loop until we find a valid date. For example, we could loop from + # 2009/02/31 to 2009/02/28. + dateIsWrong = True + while dateIsWrong: + try: + res = DateTime('%s/%s/%s' % (year, month, day)) + dateIsWrong = False + except: + day = int(day)-1 + return res + def onSearchObjects(self): '''This method is called when the user triggers a search from search.pt.''' @@ -387,16 +444,40 @@ class ToolMixin(AbstractMixin): # Store the search criteria in the session criteria = {} for attrName in rq.form.keys(): - if attrName.startswith('w_'): + if attrName.startswith('w_') and \ + not self._searchValueIsEmpty(attrName): + # We have a(n interval of) value(s) that is not empty for a + # given field. attrValue = rq.form[attrName] - if attrValue: - if attrName.find('*') != -1: - attrName, attrType = attrName.split('*') - if attrType == 'bool': - exec 'attrValue = %s' % attrValue - if isinstance(attrValue, list): - attrValue = ' OR '.join(attrValue) - criteria[attrName[2:]] = attrValue + if attrName.find('*') != -1: + attrName, attrType = attrName.split('*') + if attrType == 'bool': + exec 'attrValue = %s' % attrValue + elif attrType == 'int': + # Get the "from" value + if not attrValue.strip(): attrValue = None + else: attrValue = int(attrValue) + # Get the "to" value + toValue = rq.form['%s_to' % attrName[2:]].strip() + if not toValue: toValue = None + else: toValue = int(toValue) + attrValue = (attrValue, toValue) + elif attrType == 'date': + prefix = attrName[2:] + # Get the "from" value + year = attrValue + month = rq.form['%s_from_month' % prefix] + day = rq.form['%s_from_day' % prefix] + fromDate = self._getDateTime(year, month, day, True) + # Get the "to" value" + year = rq.form['%s_to_year' % prefix] + month = rq.form['%s_to_month' % prefix] + day = rq.form['%s_to_day' % prefix] + toDate = self._getDateTime(year, month, day, False) + attrValue = (fromDate, toDate) + if isinstance(attrValue, list): + attrValue = ' OR '.join(attrValue) + criteria[attrName[2:]] = attrValue rq.SESSION['searchCriteria'] = criteria # Goto the screen that displays search results backUrl = '%s/query?type_name=%s&flavourNumber=%d&search=_advanced' % \ @@ -594,4 +675,12 @@ class ToolMixin(AbstractMixin): '''Truncates string p_value to p_numberOfChars.''' if len(value) > numberOfChars: return value[:numberOfChars] + '...' return value + + monthsIds = { + 1: 'month_jan', 2: 'month_feb', 3: 'month_mar', 4: 'month_apr', + 5: 'month_may', 6: 'month_jun', 7: 'month_jul', 8: 'month_aug', + 9: 'month_sep', 10: 'month_oct', 11: 'month_nov', 12: 'month_dec'} + def getMonthName(self, monthNumber): + '''Gets the translated month name of month numbered p_monthNumber.''' + return self.translate(self.monthsIds[int(monthNumber)], domain='plone') # ------------------------------------------------------------------------------ diff --git a/gen/plone25/skin/search.pt b/gen/plone25/skin/search.pt index 5c9797b..bdfa458 100644 --- a/gen/plone25/skin/search.pt +++ b/gen/plone25/skin/search.pt @@ -31,15 +31,15 @@ - - +
+
-

+
diff --git a/gen/plone25/skin/widgets.pt b/gen/plone25/skin/widgets.pt index 042cf16..7d5c1bb 100644 --- a/gen/plone25/skin/widgets.pt +++ b/gen/plone25/skin/widgets.pt @@ -1,5 +1,13 @@ -

Hello

+
   + + + + + + + +
@@ -19,10 +27,11 @@ tal:content="python: tool.truncate(tool.translate('%s_list_%s' % (appyType['label'], v)), 70)"> - +
- +
   @@ -35,29 +44,79 @@ - +
-

Hello

+ + + From + + + + + + To + + + + + +
  + + + / + / + +
+ + + / + / + +
-

Hello

+

-

Hello

+

-

Hello

+

-

Hello

+

-

Hello

+

diff --git a/gen/po.py b/gen/po.py index 93d229c..db740ea 100755 --- a/gen/po.py +++ b/gen/po.py @@ -62,6 +62,8 @@ class PoMessage: SEARCH_OBJECTS = 'Search objects of this type.' SEARCH_RESULTS = 'Search results' SEARCH_NEW = 'New search' + SEARCH_FROM = 'From' + SEARCH_TO = 'to' WORKFLOW_COMMENT = 'Optional comment' WORKFLOW_STATE = 'state' DATA_CHANGE = 'Data change'