[Erp5-report] r19220 - /erp5/trunk/products/ZSQLCatalog/SearchKey/SearchKey.py

nobody at svn.erp5.org nobody at svn.erp5.org
Sun Feb 10 19:08:03 CET 2008


Author: jp
Date: Sun Feb 10 19:08:02 2008
New Revision: 19220

URL: http://svn.erp5.org?rev=19220&view=rev
Log:
moved from Key.py - sorry for not using mv

Added:
    erp5/trunk/products/ZSQLCatalog/SearchKey/SearchKey.py

Added: erp5/trunk/products/ZSQLCatalog/SearchKey/SearchKey.py
URL: http://svn.erp5.org/erp5/trunk/products/ZSQLCatalog/SearchKey/SearchKey.py?rev=19220&view=auto
==============================================================================
--- erp5/trunk/products/ZSQLCatalog/SearchKey/SearchKey.py (added)
+++ erp5/trunk/products/ZSQLCatalog/SearchKey/SearchKey.py Sun Feb 10 19:08:02 2008
@@ -1,0 +1,244 @@
+##############################################################################
+#
+# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
+#                     Ivan Tyagov <ivan 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.
+#
+##############################################################################
+
+from Products.ZSQLCatalog.Query.SimpleQuery import SimpleQuery as Query
+from Products.ZSQLCatalog.Query.ComplexQuery import ComplexQuery
+from Products.ZSQLCatalog.SQLCatalog import getSearchKeyInstance
+
+import ply.yacc as yacc
+import ply.lex as lex
+
+class SearchKey:
+  """ BaseKey is a base class that implements a parser of 
+      search grammar used in ERP5. It also implements all generic 
+      search key class methods."""
+
+  # main logical operators
+  operators = ('OR', 'AND',)
+  default_operator = '='
+
+  # in ERP5 search grammar white space is extremely important
+  # so we can not ignore it.
+  #t_ignore  = ' \t' 
+
+  # no need to rack down line numbers
+  #def t_newline(self, t):
+  #  r'\n+'
+  #  #t.lexer.lineno += len(t.value)
+
+  def t_error(self, t):
+    #print "Illegal character '%s'" % t.value[0]
+    t.lexer.skip(1)
+
+  def p_error(self, p):
+    pass
+
+  def build(self, **kwargs):
+    """ This method will initialize respective search key class with 
+        tokens' definitions. """
+    self.lexer = lex.lex(object = self, **kwargs)
+
+  def tokenize(self, data):
+    """ Return list of tokens according to respective 
+        search key tokens' definitions. """
+    result = []
+    self.lexer.input(data)
+    while 1:
+      tok = self.lexer.token()
+      if not tok: 
+        break
+      result.append(tok) 
+    return result
+
+  # Grouping of tokens
+  def getOperatorForTokenList(self, tokens):
+    """ Generic implementation that will return respective 
+        operator for a token list. The first found occurence wins."""
+    token = tokens[0]
+    token_type = token.type
+    if token_type in self.sub_operators:
+      return token.value, tokens[1:]
+    else:
+      return self.default_operator, tokens    
+
+  def groupByLogicalOperator(self, tokens, logical_operator ='OR'):
+    """ Split tokens list into one or many OR concatanated tokens list 
+    """
+    sub_tokens_or_groups = []
+    tmp_token_list = []
+    for token in tokens:
+      if token.type != logical_operator:
+        tmp_token_list.append(token)
+      else:
+        sub_tokens_or_groups.append(tmp_token_list)
+        tmp_token_list = []
+    # append remainig last tokens
+    sub_tokens_or_groups.append(tmp_token_list)
+    return sub_tokens_or_groups    
+
+  # SQL quoting (each search key should override them it if needed)
+  def quoteSQLKey(self, key, format):
+    """ Return a quoted string of the value. """
+    return key
+
+  def quoteSQLString(self, value, format):
+    """ Return a quoted string of the value. """
+    return "'%s'" %value
+
+  # SQL generation
+  def buildSQLExpression(self, key, value, 
+                               format=None, mode=None, range_value=None, stat__=0):
+    """ Generic implementation. Leave details to respective key. """
+    if range_value is not None:
+      # if range_value we handle directly (i.e no parsing of search string)
+      where_expressions, select_expressions = \
+         self.buildSQLExpressionFromRange(key, value, 
+                                          format, mode, range_value, stat__) 
+    else:
+      # search string parsing is needed
+      where_expressions, select_expressions = \
+        self.buildSQLExpressionFromSearchString(key, str(value), 
+                                                format, mode, range_value, stat__) 
+    return where_expressions, select_expressions
+
+  def buildSQLExpressionFromSearchString(self, key, value, format, mode, range_value, stat__):
+    complex_query = self.buildQuery(key, value, format, mode, range_value, stat__)
+    if complex_query is None:
+      # Query could not be generated from search string
+      sql_expression = {'where_expression': '1', 
+                        'select_expression_list': []}
+    else:
+      sql_expression = complex_query(keyword_search_keys = [],
+                                     datetime_search_keys = [],
+                                     full_text_search_keys = [])
+    return sql_expression['where_expression'], sql_expression['select_expression_list']
+
+  def buildQuery(self, key, value, format, mode, range_value, stat__):
+    """ Build Query """
+    query_list = []
+    # tokenize searchs string into tokens for Search Key
+    tokens = self.tokenize(value)
+
+    # split tokens list into one or more 'OR' tokens lists
+    tokens_or_groups = self.groupByLogicalOperator(tokens, 'OR')
+
+    # remove empty tokens lists
+    tokens_or_groups = filter(lambda x: len(x), tokens_or_groups)
+
+    # get a ComplexQuery for a sub token list
+    for tokens_or_group in tokens_or_groups:
+      query = self.buildQueryForTokenList(tokens_or_group, key, value, format)
+      if query is not None:
+        # query could be generated for token list
+        query_list.append(query)
+
+    if len(query_list):
+      # join query list in one really big ComplexQuery
+      return ComplexQuery(*query_list,
+                          **{'operator':'OR'})
+
+  def buildQueryForTokenList(self, tokens, key, value, format):
+    """ Build a ComplexQuery for a token list """
+    query_list = []
+    logical_groups = self.groupByLogicalOperator(tokens, 'AND')
+    for group_tokens in logical_groups:
+      token_values = [x.value for x in group_tokens]
+      sub_operator, sub_tokens = self.getOperatorForTokenList(group_tokens)
+      sub_tokens_values = [x.value for x in sub_tokens]
+      query_kw = {key: ' '.join(sub_tokens_values),
+                  'type': self.default_key_type,
+                  'format': format,
+                  'range': sub_operator}
+      query_list.append(Query(**query_kw))
+
+    # join query list in one really big ComplexQuery
+    complex_query = ComplexQuery(*query_list, 
+                                 **{'operator': 'AND'})
+    return complex_query
+
+  def buildSQLExpressionFromRange(self, key, value, format, mode, range_value, stat__):
+    """ This method will generate SQL expressions
+       from explicitly passed list of values and 
+       range_value in ('min', 'max', ..)"""
+    key = self.quoteSQLKey(key, format)  
+    where_expression = ''
+    select_expressions = []
+    if isinstance(value, (list, tuple)):
+      if len(value) > 1:
+        # value should contain at least two items
+        query_min = self.quoteSQLString(value[0], format)
+        query_max = self.quoteSQLString(value[1], format)
+      else:
+        # value contains only one item
+        query_min = query_max = self.quoteSQLString(value[0], format)          
+    else:
+      query_min = query_max = self.quoteSQLString(value, format)
+    if range_value == 'min':
+      where_expression = "%s >= %s" % (key, query_min)
+    elif range_value == 'max':
+      where_expression = "%s < %s" % (key, query_max)
+    elif range_value == 'minmax' :
+      where_expression = "%s >= %s AND %s < %s" % (key, query_min, key, query_max)
+    elif range_value == 'minngt' :
+      where_expression = "%s >= %s AND %s <= %s" % (key, query_min, key, query_max)
+    elif range_value == 'ngt':
+      where_expression =  "%s <= %s" % (key, query_max)
+    elif range_value == 'nlt':
+      where_expression = "%s > %s" % (key, query_max)
+    elif range_value == 'like':
+      where_expression = "%s LIKE %s" % (key, query_max)
+    elif range_value == 'not_like':
+      where_expression = "%s NOT LIKE %s" % (key, query_max)
+    elif range_value in ('=', '>', '<', '>=', '<=','!=',):
+      where_expression = "%s %s %s" % (key, range_value, query_max)    
+    return where_expression, select_expressions
+
+
+##  def groupByOperator(self, tokens, group_by_operators_list = operators):
+##    """ Generic implementation of splitting tokens into logical
+##        groups defided by respective list of logical operator
+##        defined for respective search key.  """
+##    items = []
+##    last_operator = None
+##    operators_mapping_list = []
+##    last_operator = {'operator': None,
+##                     'tokens': []}
+##    for token in tokens:
+##      token_type = token.type
+##      token_value = token.value
+##      if token_type in group_by_operators_list:
+##        # (re) init it
+##        last_operator = {'operator': token,
+##                         'tokens': []}
+##        operators_mapping_list.append(last_operator)
+##      else:
+##        # not an operator just a value token
+##        last_operator['tokens'].append(token)
+##        if last_operator not in operators_mapping_list:
+##          operators_mapping_list.append(last_operator)
+##    return operators_mapping_list      




More information about the Erp5-report mailing list