@@ -314,8 +314,9 @@ def execute(self):
314314        if  body :
315315            self ._logger .debug ('  body: %s' , body )
316316
317+         params  =  "&" .join ("%s=%s"  %  (k , v ) for  k , v  in  self .get_query_params ().items ())
317318        response  =  self ._connection .request (
318-             self .get_method (), url , headers = headers , params = self . get_query_params () , data = body )
319+             self .get_method (), url , headers = headers , params = params , data = body )
319320
320321        self ._logger .debug ('Received response' )
321322        self ._logger .debug ('  url: %s' , response .url )
@@ -623,7 +624,7 @@ def expand(self, expand):
623624    def  filter (self , filter_val ):
624625        """Sets the filter expression.""" 
625626        # returns QueryRequest 
626-         self ._filter  =  quote ( filter_val ) 
627+         self ._filter  =  filter_val 
627628        return  self 
628629
629630    # def nav(self, key_value, nav_property): 
@@ -993,6 +994,212 @@ def __gt__(self, value):
993994        return  GetEntitySetFilter .format_filter (self ._proprty , 'gt' , value )
994995
995996
997+ class  FilterExpression :
998+     """A class representing named expression of OData $filter""" 
999+ 
1000+     def  __init__ (self , ** kwargs ):
1001+         self ._expressions  =  kwargs 
1002+         self ._other  =  None 
1003+         self ._operator  =  None 
1004+ 
1005+     @property  
1006+     def  expressions (self ):
1007+         """Get expressions where key is property name with the operator suffix 
1008+            and value is the left hand side operand. 
1009+         """ 
1010+ 
1011+         return  self ._expressions .items ()
1012+ 
1013+     @property  
1014+     def  other (self ):
1015+         """Get an instance of the other operand""" 
1016+ 
1017+         return  self ._other 
1018+ 
1019+     @property  
1020+     def  operator (self ):
1021+         """The other operand""" 
1022+ 
1023+         return  self ._operator 
1024+ 
1025+     def  __or__ (self , other ):
1026+         if  self ._other  is  not   None :
1027+             raise  RuntimeError ('The FilterExpression already initialized' )
1028+ 
1029+         self ._other  =  other 
1030+         self ._operator  =  "or" 
1031+         return  self 
1032+ 
1033+     def  __and__ (self , other ):
1034+         if  self ._other  is  not   None :
1035+             raise  RuntimeError ('The FilterExpression already initialized' )
1036+ 
1037+         self ._other  =  other 
1038+         self ._operator  =  "and" 
1039+         return  self 
1040+ 
1041+ 
1042+ class  GetEntitySetFilterChainable :
1043+     """ 
1044+     Example expressions 
1045+         FirstName='Tim' 
1046+         FirstName__contains='Tim' 
1047+         Age__gt=56 
1048+         Age__gte=6 
1049+         Age__lt=78 
1050+         Age__lte=90 
1051+         Age__range=(5,9) 
1052+         FirstName__in=['Tim', 'Bob', 'Sam'] 
1053+         FirstName__startswith='Tim' 
1054+         FirstName__endswith='mothy' 
1055+         Addresses__Suburb='Chatswood' 
1056+         Addresses__Suburb__contains='wood' 
1057+     """ 
1058+ 
1059+     OPERATORS  =  [
1060+         'startswith' ,
1061+         'endswith' ,
1062+         'lt' ,
1063+         'lte' ,
1064+         'gt' ,
1065+         'gte' ,
1066+         'contains' ,
1067+         'range' ,
1068+         'in' ,
1069+         'length' ,
1070+         'eq' 
1071+     ]
1072+ 
1073+     def  __init__ (self , entity_type , filter_expressions , exprs ):
1074+         self ._entity_type  =  entity_type 
1075+         self ._filter_expressions  =  filter_expressions 
1076+         self ._expressions  =  exprs 
1077+ 
1078+     @property  
1079+     def  expressions (self ):
1080+         """Get expressions as a list of tuples where the first item 
1081+            is a property name with the operator suffix and the second item 
1082+            is a left hand side value. 
1083+         """ 
1084+ 
1085+         return  self ._expressions .items ()
1086+ 
1087+     def  proprty_obj (self , name ):
1088+         """Returns a model property for a particular property""" 
1089+ 
1090+         return  self ._entity_type .proprty (name )
1091+ 
1092+     def  _decode_and_combine_filter_expression (self , filter_expression ):
1093+         filter_expressions  =  [self ._decode_expression (expr , val ) for  expr , val  in  filter_expression .expressions ]
1094+         return  self ._combine_expressions (filter_expressions )
1095+ 
1096+     def  _process_query_objects (self ):
1097+         """Processes FilterExpression objects to OData lookups""" 
1098+ 
1099+         filter_expressions  =  []
1100+ 
1101+         for  expr  in  self ._filter_expressions :
1102+             lhs_expressions  =  self ._decode_and_combine_filter_expression (expr )
1103+ 
1104+             if  expr .other  is  not   None :
1105+                 rhs_expressions  =  self ._decode_and_combine_filter_expression (expr .other )
1106+                 filter_expressions .append (f'({ lhs_expressions }  ) { expr .operator }   ({ rhs_expressions }  )' )
1107+             else :
1108+                 filter_expressions .append (lhs_expressions )
1109+ 
1110+         return  filter_expressions 
1111+ 
1112+     def  _process_expressions (self ):
1113+         filter_expressions  =  [self ._decode_expression (expr , val ) for  expr , val  in  self .expressions ]
1114+ 
1115+         filter_expressions .extend (self ._process_query_objects ())
1116+ 
1117+         return  filter_expressions 
1118+ 
1119+     def  _decode_expression (self , expr , val ):
1120+         field  =  None 
1121+         # field_heirarchy = [] 
1122+         operator  =  'eq' 
1123+         exprs  =  expr .split ('__' )
1124+ 
1125+         for  part  in  exprs :
1126+             if  self ._entity_type .has_proprty (part ):
1127+                 field  =  part 
1128+                 # field_heirarchy.append(part) 
1129+             elif  part  in  self .__class__ .OPERATORS :
1130+                 operator  =  part 
1131+             else :
1132+                 raise  ValueError (f'"{ part }  " is not a valid property or operator' )
1133+         # field = '/'.join(field_heirarchy) 
1134+ 
1135+         # target_field = self.proprty_obj(field_heirarchy[-1]) 
1136+         expression  =  self ._build_expression (field , operator , val )
1137+ 
1138+         return  expression 
1139+ 
1140+     # pylint: disable=no-self-use 
1141+     def  _combine_expressions (self , expressions ):
1142+         return  ' and ' .join (expressions )
1143+ 
1144+     # pylint: disable=too-many-return-statements, too-many-branches 
1145+     def  _build_expression (self , field_name , operator , value ):
1146+         target_field  =  self .proprty_obj (field_name )
1147+ 
1148+         if  operator  not  in   ['length' , 'in' , 'range' ]:
1149+             value  =  target_field .to_literal (value )
1150+ 
1151+         if  operator  ==  'lt' :
1152+             return  f'{ field_name }   lt { value }  ' 
1153+ 
1154+         if  operator  ==  'lte' :
1155+             return  f'{ field_name }   le { value }  ' 
1156+ 
1157+         if  operator  ==  'gte' :
1158+             return  f'{ field_name }   ge { value }  ' 
1159+ 
1160+         if  operator  ==  'gt' :
1161+             return  f'{ field_name }   gt { value }  ' 
1162+ 
1163+         if  operator  ==  'startswith' :
1164+             return  f'startswith({ field_name }  , { value }  ) eq true' 
1165+ 
1166+         if  operator  ==  'endswith' :
1167+             return  f'endswith({ field_name }  , { value }  ) eq true' 
1168+ 
1169+         if  operator  ==  'length' :
1170+             value  =  int (value )
1171+             return  f'length({ field_name }  ) eq { value }  ' 
1172+ 
1173+         if  operator  in  ['contains' ]:
1174+             return  f'substringof({ value }  , { field_name }  ) eq true' 
1175+ 
1176+         if  operator  ==  'range' :
1177+             if  not  isinstance (value , (tuple , list )):
1178+                 raise  TypeError ('Range must be tuple or list not {}' .format (type (value )))
1179+ 
1180+             if  len (value ) !=  2 :
1181+                 raise  ValueError ('Only two items can be passed in a range.' )
1182+ 
1183+             low_bound  =  target_field .to_literal (value [0 ])
1184+             high_bound  =  target_field .to_literal (value [1 ])
1185+ 
1186+             return  f'{ field_name }   gte { low_bound }   and { field_name }   lte { high_bound }  ' 
1187+ 
1188+         if  operator  ==  'in' :
1189+             literal_values  =  (f'{ field_name }   eq { target_field .to_literal (item )}  '  for  item  in  value )
1190+             return  ' or ' .join (literal_values )
1191+ 
1192+         if  operator  ==  'eq' :
1193+             return  f'{ field_name }   eq { value }  ' 
1194+ 
1195+         raise  ValueError (f'Invalid expression { operator }  ' )
1196+ 
1197+     def  __str__ (self ):
1198+         expressions  =  self ._process_expressions ()
1199+         result  =  self ._combine_expressions (expressions )
1200+         return  quote (result )
1201+ 
1202+ 
9961203class  GetEntitySetRequest (QueryRequest ):
9971204    """GET on EntitySet""" 
9981205
@@ -1005,6 +1212,19 @@ def __getattr__(self, name):
10051212        proprty  =  self ._entity_type .proprty (name )
10061213        return  GetEntitySetFilter (proprty )
10071214
1215+     def  _set_filter (self , filter_val ):
1216+         filter_text  =  self ._filter  +  ' and '  if  self ._filter  else  '' 
1217+         filter_text  +=  filter_val 
1218+         self ._filter  =  filter_text 
1219+ 
1220+     def  filter (self , * args , ** kwargs ):
1221+         if  args  and  len (args ) ==  1  and  isinstance (args [0 ], str ):
1222+             self ._filter  =  args [0 ]
1223+         else :
1224+             self ._set_filter (str (GetEntitySetFilterChainable (self ._entity_type , args , kwargs )))
1225+ 
1226+         return  self 
1227+ 
10081228
10091229class  EntitySetProxy :
10101230    """EntitySet Proxy""" 
0 commit comments