Skip to content

Commit

Permalink
Fixed negation of groups '()' as reported in #1058
Browse files Browse the repository at this point in the history
Fixed: Negations are now correctly consumed when used in: open groups, logical operators, functions and iterables.
  • Loading branch information
Alejandro Casanovas committed Apr 12, 2024
1 parent 91f5c0d commit 094399b
Showing 1 changed file with 41 additions and 22 deletions.
63 changes: 41 additions & 22 deletions O365/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -951,9 +951,12 @@ def _add_filter(self, *filter_data):
self._filters.append(self._chain)
sentence, attrs = filter_data
for i, group in enumerate(self._open_group_flag):
if group is True:
# Open a group
sentence = '(' + sentence
if group is True or group is None:
# Open a group: None Flags a group that is negated
if group is True:
sentence = '(' + sentence
else:
sentence = 'not (' + sentence
self._open_group_flag[i] = False # set to done
self._filters.append([self._attribute, sentence, attrs])
else:
Expand Down Expand Up @@ -1006,14 +1009,18 @@ def logical_operator(self, operation, word):
:rtype: Query
"""
word = self._parse_filter_word(word)
# consume negation
negation = self._negation
if negation:
self._negation = False
self._add_filter(
*self._prepare_sentence(self._attribute, operation, word,
self._negation))
*self._prepare_sentence(self._attribute, operation, word, negation)
)
return self

@fluent
def equals(self, word):
""" Add a equals check
""" Add an equals check
:param word: word to compare with
:rtype: Query
Expand All @@ -1022,7 +1029,7 @@ def equals(self, word):

@fluent
def unequal(self, word):
""" Add a unequals check
""" Add an unequals check
:param word: word to compare with
:rtype: Query
Expand Down Expand Up @@ -1080,10 +1087,12 @@ def function(self, function_name, word):
:rtype: Query
"""
word = self._parse_filter_word(word)

# consume negation
negation = self._negation
if negation:
self._negation = False
self._add_filter(
*self._prepare_function(function_name, self._attribute, word,
self._negation))
*self._prepare_function(function_name, self._attribute, word, negation))
return self

@fluent
Expand Down Expand Up @@ -1115,7 +1124,7 @@ def endswith(self, word):

@fluent
def iterable(self, iterable_name, *, collection, word, attribute=None, func=None,
operation=None, negate=False):
operation=None, negation=False):
""" Performs a filter with the OData 'iterable_name' keyword
on the collection
Expand All @@ -1134,7 +1143,7 @@ def iterable(self, iterable_name, *, collection, word, attribute=None, func=None
the collection
:param str operation: the logical operation to apply to the attribute
inside the collection
:param bool negate: negate the funcion or operation inside the iterable
:param bool negation: negate the funcion or operation inside the iterable
:rtype: Query
"""

Expand All @@ -1157,21 +1166,26 @@ def iterable(self, iterable_name, *, collection, word, attribute=None, func=None
attribute = 'a/{}'.format(attribute)

if func is not None:
sentence = self._prepare_function(func, attribute, word, negate)
sentence = self._prepare_function(func, attribute, word, negation)
else:
sentence = self._prepare_sentence(attribute, operation, word, negate)
sentence = self._prepare_sentence(attribute, operation, word, negation)

filter_str, attrs = sentence

filter_data = '{}/{}(a:{})'.format(collection, iterable_name, filter_str), attrs
# consume negation
negation = 'not' if self._negation else ''
if self._negation:
self._negation = False

filter_data = '{} {}/{}(a:{})'.format(negation, collection, iterable_name, filter_str).strip(), attrs
self._add_filter(*filter_data)

self._attribute = current_att

return self

@fluent
def any(self, *, collection, word, attribute=None, func=None, operation=None, negate=False):
def any(self, *, collection, word, attribute=None, func=None, operation=None, negation=False):
""" Performs a filter with the OData 'any' keyword on the collection
For example:
Expand All @@ -1189,16 +1203,16 @@ def any(self, *, collection, word, attribute=None, func=None, operation=None, ne
inside the collection
:param str operation: the logical operation to apply to the
attribute inside the collection
:param bool negate: negate the funcion or operation inside the iterable
:param bool negation: negates the funcion or operation inside the iterable
:rtype: Query
"""

return self.iterable('any', collection=collection, word=word,
attribute=attribute, func=func, operation=operation,
negate=negate)
negation=negation)

@fluent
def all(self, *, collection, word, attribute=None, func=None, operation=None, negate=False):
def all(self, *, collection, word, attribute=None, func=None, operation=None, negation=False):
""" Performs a filter with the OData 'all' keyword on the collection
For example:
Expand All @@ -1216,13 +1230,13 @@ def all(self, *, collection, word, attribute=None, func=None, operation=None, ne
inside the collection
:param str operation: the logical operation to apply to the
attribute inside the collection
:param bool negate: negate the funcion or operation inside the iterable
:param bool negation: negate the funcion or operation inside the iterable
:rtype: Query
"""

return self.iterable('all', collection=collection, word=word,
attribute=attribute, func=func, operation=operation,
negate=negate)
negation=negation)

@fluent
def order_by(self, attribute=None, *, ascending=True):
Expand All @@ -1243,7 +1257,12 @@ def order_by(self, attribute=None, *, ascending=True):

def open_group(self):
""" Applies a precedence grouping in the next filters """
self._open_group_flag.append(True)
# consume negation
if self._negation:
self._negation = False
self._open_group_flag.append(None) # flag a negated group open with None
else:
self._open_group_flag.append(True)
return self

def close_group(self):
Expand Down

0 comments on commit 094399b

Please sign in to comment.