Exemplo n.º 1
0
def construct_single_q(rule):
    operator = rule['operator']
    neg = False
    if operator.startswith("not_"):
        neg = True
        operator = operator[4:]

    cond = {
        "equal": "exact",
        "begins_with": "istartswith",
        "contains": "icontains",
        "ends_with": "iendswith",
        "less": "lt",
        "less_or_equal": "lte",
        "greater": "gt",
        "greater_or_equal": "gte",
        "between": "range"
    }[operator]

    if cond != "range":
        cond_dict = {"{}__{}".format(rule['id'], cond): rule['value']}
    else:
        cond_dict = {"{}__{}".format(rule['id'], cond): tuple(rule['value'])}

    q_obj = Q(**cond_dict)

    if neg:
        q_obj = ~q_obj

    return q_obj
Exemplo n.º 2
0
 def _parse_relationals(cls, expr_string, escape='__'):
     """
     Based on shunting-yard algorithm
     (see http://en.wikipedia.org/wiki/Shunting-yard_algorithm)
     with modifications for skipping over non logical/relational operators
     and associated parentheses.
     """
     if isinstance(expr_string, bool):
         return expr_string
     # Splits and throws away empty tokens (between parens and operators)
     # and encloses the whole expression in parens
     tokens = (['('] + [
         t for t in cls._tokenize_logic_re.split(expr_string.strip()) if t
     ] + [')'])
     if len([1 for t in tokens if t.strip().endswith('(')
             ]) != tokens.count(')'):
         raise NineMLMathParseError(
             "Unbalanced parentheses in expression: {}".format(expr_string))
     tokens = tokens
     operators = []  # stack (in SY algorithm terminology)
     operands = []  # output stream
     is_relational = []  # whether the current parens should be parsed
     # Num operands to concat when not parsing relation/bool. Because we are
     # also splitting on parenthesis, non-logic/relational expressions will
     # still be split on parenthesis and we need to keep track of how many
     # pieces they are in.
     num_args = [0]  # top-level should always be set to 1
     for tok in tokens:
         # If opening paren or function name + paren
         if cls._left_paren_func_re.match(tok):
             operators.append(tok)
             is_relational.append(False)
             num_args.append(0)
         # Closing paren.
         elif tok == ')':
             # Join together sub-expressions that have been split by parens
             # not used for relational/boolean expressions (e.g. functions)
             n = num_args.pop()
             if n > 1:
                 operands = operands[:-n] + [''.join(operands[-n:])]
             # If parens enclosed relat/logic (i.e. '&', '|', '<', '>', etc)
             if is_relational.pop():
                 # Get all the operators within the enclosing parens.
                 # Need to sort by order of precedence
                 try:
                     # Get index of last open paren
                     i = -1
                     while not cls._left_paren_func_re.match(operators[i]):
                         i -= 1
                 except IndexError:
                     raise NineMLMathParseError(
                         "Unbalanced parentheses in expression: {}".format(
                             expr_string))
                 # Get lists of operators and operands at this level
                 # (i.e. after the last left paren)
                 level_operators = operators[(i + 1):]
                 level_operands = operands[i:]
                 # Pop these operators and operands off the list
                 # (along with the paren/function-call)
                 open_paren = operators[i]
                 operators = operators[:i]
                 operands = operands[:i]
                 while level_operators:
                     # Sort in order of precedence
                     prec = [cls._precedence[o] for o in level_operators]
                     i = sorted(list(range(len(prec))),
                                key=prec.__getitem__)[0]
                     # Pop first operator
                     operator = level_operators.pop(i)
                     arg1, arg2 = (level_operands.pop(i),
                                   level_operands.pop(i))
                     if operator.startswith('&'):
                         func = "And{}({}, {})".format(escape, arg1, arg2)
                     elif operator.startswith('|'):
                         func = "Or{}({}, {})".format(escape, arg1, arg2)
                     elif operator.startswith('='):
                         func = "Eq{}({}, {})".format(escape, arg1, arg2)
                     elif operator == '<':
                         func = "Lt{}({}, {})".format(escape, arg1, arg2)
                     elif operator == '>':
                         func = "Gt{}({}, {})".format(escape, arg1, arg2)
                     elif operator == '<=':
                         func = "Le{}({}, {})".format(escape, arg1, arg2)
                     elif operator == '>=':
                         func = "Ge{}({}, {})".format(escape, arg1, arg2)
                     else:
                         assert False
                     level_operands.insert(i, func)
                 new_operand = level_operands[0]
                 if open_paren != '(':  # Function/logical negation
                     new_operand = open_paren + new_operand + ')'
                 operands.append(new_operand)
             # If parens enclosed something else
             else:
                 try:
                     # Apply the function/parens to the last operand
                     operands[-1] = operators.pop() + operands[-1] + ')'
                 except IndexError:
                     raise NineMLMathParseError(
                         "Unbalanced parentheses in expression: {}".format(
                             expr_string))
             num_args[-1] += 1
         # If the token is one of ('&', '|', '<', '>', '<=', '>=' or '==')
         elif cls._tokenize_logic_re.match(tok):
             operators.append(tok)
             is_relational[-1] = True  # parse the last set of parenthesis
             # Check if there are more than one LHS sub-expr. to concatenate
             n = num_args[-1]
             if n == 0:
                 raise NineMLMathParseError(
                     "Logical/relational operator directly after a "
                     "parenthesis or start of expression: {}".format(
                         expr_string))
             elif n > 1:
                 operands = operands[:-n] + [''.join(operands[-n:])]
             num_args[-1] = 0
         # If the token is an atom or a subexpr not containing any
         # logic/relational operators or parens.
         else:
             operands.append(tok)
             num_args[-1] += 1
     # After it is processed, operands should contain the parsed expression
     # as a single item
     return operands[0]
Exemplo n.º 3
0
 def _parse_relationals(cls, expr_string, escape='__'):
     """
     Based on shunting-yard algorithm
     (see http://en.wikipedia.org/wiki/Shunting-yard_algorithm)
     with modifications for skipping over non logical/relational operators
     and associated parentheses.
     """
     if isinstance(expr_string, bool):
         return expr_string
     # Splits and throws away empty tokens (between parens and operators)
     # and encloses the whole expression in parens
     tokens = (['('] +
               [t for t in cls._tokenize_logic_re.split(expr_string.strip())
                if t] + [')'])
     if len([1 for t in tokens
             if t.strip().endswith('(')]) != tokens.count(')'):
         raise NineMLMathParseError(
             "Unbalanced parentheses in expression: {}".format(expr_string))
     tokens = tokens
     operators = []  # stack (in SY algorithm terminology)
     operands = []  # output stream
     is_relational = []  # whether the current parens should be parsed
     # Num operands to concat when not parsing relation/bool. Because we are
     # also splitting on parenthesis, non-logic/relational expressions will
     # still be split on parenthesis and we need to keep track of how many
     # pieces they are in.
     num_args = [0]  # top-level should always be set to 1
     for tok in tokens:
         # If opening paren or function name + paren
         if cls._left_paren_func_re.match(tok):
             operators.append(tok)
             is_relational.append(False)
             num_args.append(0)
         # Closing paren.
         elif tok == ')':
             # Join together sub-expressions that have been split by parens
             # not used for relational/boolean expressions (e.g. functions)
             n = num_args.pop()
             if n > 1:
                 operands = operands[:-n] + [''.join(operands[-n:])]
             # If parens enclosed relat/logic (i.e. '&', '|', '<', '>', etc)
             if is_relational.pop():
                 # Get all the operators within the enclosing parens.
                 # Need to sort by order of precedence
                 try:
                     # Get index of last open paren
                     i = -1
                     while not cls._left_paren_func_re.match(operators[i]):
                         i -= 1
                 except IndexError:
                     raise NineMLMathParseError(
                         "Unbalanced parentheses in expression: {}"
                         .format(expr_string))
                 # Get lists of operators and operands at this level
                 # (i.e. after the last left paren)
                 level_operators = operators[(i + 1):]
                 level_operands = operands[i:]
                 # Pop these operators and operands off the list
                 # (along with the paren/function-call)
                 open_paren = operators[i]
                 operators = operators[:i]
                 operands = operands[:i]
                 while level_operators:
                     # Sort in order of precedence
                     prec = [cls._precedence[o] for o in level_operators]
                     i = sorted(list(range(len(prec))), key=prec.__getitem__)[0]
                     # Pop first operator
                     operator = level_operators.pop(i)
                     arg1, arg2 = (level_operands.pop(i),
                                   level_operands.pop(i))
                     if operator.startswith('&'):
                         func = "And{}({}, {})".format(escape, arg1, arg2)
                     elif operator.startswith('|'):
                         func = "Or{}({}, {})".format(escape, arg1, arg2)
                     elif operator.startswith('='):
                         func = "Eq{}({}, {})".format(escape, arg1, arg2)
                     elif operator == '<':
                         func = "Lt{}({}, {})".format(escape, arg1, arg2)
                     elif operator == '>':
                         func = "Gt{}({}, {})".format(escape, arg1, arg2)
                     elif operator == '<=':
                         func = "Le{}({}, {})".format(escape, arg1, arg2)
                     elif operator == '>=':
                         func = "Ge{}({}, {})".format(escape, arg1, arg2)
                     else:
                         assert False
                     level_operands.insert(i, func)
                 new_operand = level_operands[0]
                 if open_paren != '(':  # Function/logical negation
                     new_operand = open_paren + new_operand + ')'
                 operands.append(new_operand)
             # If parens enclosed something else
             else:
                 try:
                     # Apply the function/parens to the last operand
                     operands[-1] = operators.pop() + operands[-1] + ')'
                 except IndexError:
                     raise NineMLMathParseError(
                         "Unbalanced parentheses in expression: {}"
                         .format(expr_string))
             num_args[-1] += 1
         # If the token is one of ('&', '|', '<', '>', '<=', '>=' or '==')
         elif cls._tokenize_logic_re.match(tok):
             operators.append(tok)
             is_relational[-1] = True  # parse the last set of parenthesis
             # Check if there are more than one LHS sub-expr. to concatenate
             n = num_args[-1]
             if n == 0:
                 raise NineMLMathParseError(
                     "Logical/relational operator directly after a "
                     "parenthesis or start of expression: {}"
                     .format(expr_string))
             elif n > 1:
                 operands = operands[:-n] + [''.join(operands[-n:])]
             num_args[-1] = 0
         # If the token is an atom or a subexpr not containing any
         # logic/relational operators or parens.
         else:
             operands.append(tok)
             num_args[-1] += 1
     # After it is processed, operands should contain the parsed expression
     # as a single item
     return operands[0]