@@ -114,7 +114,7 @@ class RallyQueryFormatter(object):
114114 CONJUNCTIONS = ['and' , 'AND' , 'or' , 'OR' ]
115115 CONJUNCTION_PATT = re .compile (r'\s+(AND|OR)\s+' , re .I | re .M )
116116 ATTR_IDENTIFIER = r'[\w\.]+[a-zA-Z0-9]' # gotta be word-like possibly separated by '.' chars
117- RELATIONSHIP = r'=|!=|>|<|>=|<=|contains|!contains|in|!in|between|!between '
117+ RELATIONSHIP = r'=|!=|>|<|>=|<=|contains|!contains'
118118 ATTR_VALUE = r'"[^"]+"|[^ ]+' # double quoted value or has no leading, embedded or trailing spaces
119119 QUERY_CRITERIA_PATTERN = re .compile (r'^(%s) (%s) (%s)$' % (ATTR_IDENTIFIER , RELATIONSHIP , ATTR_VALUE ), re .M )
120120
@@ -198,25 +198,25 @@ def _encode(condition):
198198## print("RallyQueryFormatter parts: %s" % repr(parts))
199199##
200200 # if no CONJUNCTION is in parts, use the condition as is (simple case)
201+ # OR if the criteria looks like subset query or a range query
201202 conjunctions = [p for p in parts if p in RallyQueryFormatter .CONJUNCTIONS ]
202- if not conjunctions :
203- if mo := re .search (r'^(\w+)\s+(!?in)\s+(.+)$' , criteria , flags = re .I ):
203+ if not conjunctions or re .search (r'!?between .+\s+and\s+' , criteria , flags = re .I ):
204+ # Is this a subset expression, foo in baz,korn or foo !in burgers,fries,toast
205+ mo = re .search (r'^(\w+)\s+(!?in)\s+(.+)$' , criteria , flags = re .I )
206+ if mo :
204207 attr_name , cond , values = mo .group (1 ), mo .group (2 ), mo .group (3 )
205208 # Rally WSAPI supports attr_name in value1,value2,... directly but not so with !in
206209 if cond .lower () == '!in' : # we must construct an OR'ed express with != for each listed value
207210 # Rally WSAPI supports attr_name in value1,value2,... directly but not so with !in
208- criteria = RallyQueryFormatter .constructORedExpression (attr_name , cond , values )
209- elif mo := re . search ( r'^(\w+) (!?between)\s+(.+)\s+and\s+(.+)$' , criteria , flags = re . I ) :
210- attr_name , cond , lesser , greater = mo . group ( 1 ), mo . group ( 2 ), mo . group ( 3 ), mo . group ( 4 )
211- rlns = [ '>=' , '<=' ] if cond . lower () == 'between' else [ '<' , '>' ]
212- rln_op = 'AND' if cond . lower () == 'between' else 'OR'
213- lcond = '%s %s %s' % ( attr_name , rlns [ 0 ], lesser )
214- gcond = '%s %s %s' % (attr_name , rlns [ 1 ] , greater )
215- criteria = "(%s) %s (%s)" % ( lcond , rln_op , gcond )
211+ criteria = RallyQueryFormatter .constructSubsetExpression (attr_name , cond , values )
212+ else :
213+ # Is this a range expression, someDate between today and nextYear
214+ mo = re . search ( r'^(\w+) (!?between)\s+(.+)\s+and\s+(.+)$' , criteria , flags = re . I )
215+ if mo :
216+ attr_name , cond , lesser , greater = mo . group ( 1 ), mo . group ( 2 ), mo . group ( 3 ), mo . group ( 4 )
217+ criteria = RallyQueryFormatter . constructRangefulExpression (attr_name , cond , lesser , greater )
218+
216219 expression = quote (criteria .strip ()).replace ('%28' , '(' ).replace ('%29' , ')' )
217- ##
218- ## print("RallyQueryFormatter.no_conjunctions: |%s|" % expression)
219- ##
220220 return expression
221221
222222 parts = RallyQueryFormatter .validatePartsSyntax (parts )
@@ -241,42 +241,16 @@ def _encode(condition):
241241 @staticmethod
242242 def validatePartsSyntax (parts ):
243243 attr_ident = r'[\w\.]+[a-zA-Z0-9]'
244- relationship = r'=|!=|>|<|>=|<=|contains|!contains|in|!in|between|!between '
244+ relationship = r'=|!=|>|<|>=|<=|contains|!contains'
245245 attr_value = r'"[^"]+"|[^" ]+'
246246 criteria_pattern = re .compile (r'^(%s) (%s) (%s)$' % (attr_ident , relationship , attr_value ))
247247 quoted_value_pattern = re .compile (r'^(%s) (%s) ("[^"]+")$' % (attr_ident , relationship ))
248248 unquoted_value_pattern = re .compile (r'^(%s) (%s) ([^"].+[^"])$' % (attr_ident , relationship ))
249- subset_pattern = re .compile (r'(%s) (in|!in) (%s)' % (attr_ident , attr_value ), flags = re .I )
250- range_pattern = re .compile (r'(%s) (between|!between) (%s) and (%s)' % (attr_ident , attr_value , attr_value ), flags = re .I )
251249
252250 valid_parts = []
253251 front = ""
254252 while parts :
255253 part = "%s%s" % (front , parts .pop (0 ))
256- mo = subset_pattern .match (part )
257- if mo :
258- rln = '!=' if mo .group (1 ).startswith ('!' ) else '='
259- attr_name , values = mo .group (0 ), mo .group (2 )
260- # if rln == '=':
261- # binary_expr = constructORedExpression(attr_name, rln, values)
262- # else:
263- # binary_expr = constructANDedExpression(attr_name, rln, values)
264- valid_parts .append (binary_expr )
265- continue
266- mo = range_pattern .match (part )
267- if mo :
268- attr_name , cond = mo .group (0 ), mo .group (1 )
269- lesser_val , greater_val = mo .group (2 ), mo .group (3 )
270- rlns = ['<' , '>' ] if cond .startswith ('!' ) else ['>=' , '<=' ]
271- # in range
272- if cond .lower () == 'between' :
273- binary_expr = '((%s %s %s) AND (%s %s %s))' % \
274- (attr_name , rlns [0 ], lesser_val , attr_name , rlns [1 ], greater_val )
275- else :
276- binary_expr = '((%s %s %s) OR (%s %s %s))' % \
277- (attr_name , rlns [0 ], lesser_val , attr_name , rlns [1 ], greater_val )
278- valid_parts .append (binary_expr )
279- continue
280254 mo = criteria_pattern .match (part )
281255 if mo :
282256 valid_parts .append (part )
@@ -297,44 +271,49 @@ def validatePartsSyntax(parts):
297271
298272 return valid_parts
299273
274+ #
300275 # subset and range related ops for building queries
301-
276+ #
302277 @staticmethod
303- def constructORedExpression (field , relation , values ):
278+ def constructSubsetExpression (field , relation , values ):
304279 """
305280 intended for use when a subset operator (in or !in) is in play
306281 State in Defined, Accepted, Relased
307282 needs an ORed expression ((f = D OR f = A) OR ((f = R)))
308283 State !in Working, Fixed, Testing
309284 needs an ANDed expression ((f != W AND f != F) AND ((f != T)))
310285 """
311- operator = "="
312- if relation == '!in' :
286+ operator , conjunction = "=" , 'OR'
287+ if relation . lower () == '!in' :
313288 operator = "!="
289+ conjunction = 'AND'
290+ if isinstance (values , str ):
291+ if values .count (',' ) == 0 : # no commas equal only 1 value considered, put it in a list
292+ values = [values ]
293+ else :
294+ values = [item .lstrip ().rstrip () for item in values .split (',' )]
314295 if len (values ) == 1 :
315- return f'({ field } { operator } "{ values [0 ]} )"'
316- binary_expression = f'(({ field } { operator } "{ values [0 ]} ") OR ({ field } { operator } "{ values [1 ]} "))'
296+ return f'{ field } { operator } "{ values [0 ]} "'
297+ val1 , val2 = values [:2 ]
298+ binary_expression = f'({ field } { operator } "{ val1 } ") { conjunction } ({ field } { operator } "{ val2 } ")'
317299 for value in values [2 :]:
318- binary_expression = f'({ binary_expression } OR ({ field } { operator } "{ value } ") )'
300+ binary_expression = f'({ binary_expression } ) { conjunction } ({ field } { operator } "{ value } ")'
319301 return binary_expression
320302
321303 @staticmethod
322- def constructANDedExpression ( field , relation , values ):
304+ def constructRangefulExpression ( attr_name , cond , lesser , greater ):
323305 """
324306 intended for use when a range operator (between or !between) is in play
325307 DevPhase between 2021-05-23 and 2021-07-09
326308 needs a single ANDed expression ((dp >= d1) AND (dp <= d1)))
327309 DevPhase !between 2021-12-19 and 2022-01-03
328310 needs a single ORed expression ((dp < d1) OR (dp > d1)))
329311 """
330- operator = "="
331- if relation == '!in' :
332- operator = "!="
333- if len (values ) == 1 :
334- return f'({ field } { operator } "{ values [0 ]} )"'
335- binary_expression = f'(({ field } { operator } "{ values [0 ]} ") AND ({ field } { operator } "{ values [1 ]} "))'
336- for value in values [2 :]:
337- binary_expression = f'({ binary_expression } AND ({ field } { operator } "{ value } "))'
338- return binary_expression
312+ rlns = ['>=' , '<=' ] if cond .lower () == 'between' else ['<' , '>' ]
313+ conjunction = 'AND' if cond .lower () == 'between' else 'OR'
314+ lcond = '%s %s %s' % (attr_name , rlns [0 ], lesser )
315+ gcond = '%s %s %s' % (attr_name , rlns [1 ], greater )
316+ expression = "(%s) %s (%s)" % (lcond , conjunction , gcond )
317+ return expression
339318
340319##################################################################################################
0 commit comments