@@ -1121,6 +1121,63 @@ def service_request_async(self, service, params, *, pagesize=None, page=None, **
11211121
11221122        return  self ._portal_api_connection .service_request_async (service , params , pagesize , page , ** kwargs )
11231123
1124+     def  _normalize_filter_value (self , key : str , val ) ->  list :
1125+         """ 
1126+         Normalize a filter value into a list suitable for MAST filters. 
1127+ 
1128+         Parameters 
1129+         ---------- 
1130+         key : str 
1131+             Parameter name (used for error messages). 
1132+         val : any 
1133+             Raw filter value. 
1134+ 
1135+         Returns 
1136+         ------- 
1137+         list 
1138+             Normalized filter values. 
1139+         """ 
1140+         # Range filters must be dicts with 'min' and 'max' 
1141+         if  isinstance (val , dict ):
1142+             if  not  {"min" , "max" }.issubset (val .keys ()):
1143+                 raise  InvalidQueryError (
1144+                     f'Range filter for "{ key }  " must be a dictionary with "min" and "max" keys.' 
1145+                 )
1146+             return  [val ]
1147+ 
1148+         # Convert numpy arrays to lists 
1149+         if  isinstance (val , np .ndarray ):
1150+             val  =  val .tolist ()
1151+ 
1152+         # Convert numpy arrays, sets, or tuples to lists 
1153+         if  isinstance (val , (set , tuple )):
1154+             val  =  list (val )
1155+ 
1156+         # Wrap scalars into a list 
1157+         return  val  if  isinstance (val , list ) else  [val ]
1158+ 
1159+     def  _build_filters (self , service_params ):
1160+         """ 
1161+         Construct filters for filtered services. 
1162+ 
1163+         Parameters 
1164+         ---------- 
1165+         service_params : dict 
1166+             Parameters not classified as request/position keys. 
1167+ 
1168+         Returns 
1169+         ------- 
1170+         list of dict 
1171+             Filters suitable for a MAST filtered query. 
1172+         """ 
1173+         filters  =  []
1174+         for  key , val  in  service_params .items ():
1175+             filters .append ({
1176+                 "paramName" : key ,
1177+                 "values" : self ._normalize_filter_value (key , val )
1178+             })
1179+         return  filters 
1180+ 
11241181    def  mast_query (self , service , columns = None , ** kwargs ):
11251182        """ 
11261183        Given a Mashup service and parameters as keyword arguments, builds and excecutes a Mashup query. 
@@ -1129,53 +1186,57 @@ def mast_query(self, service, columns=None, **kwargs):
11291186        ---------- 
11301187        service : str 
11311188            The Mashup service to query. 
1132-         columns : str, optional 
1189+         columns : str or list , optional 
11331190            Specifies the columns to be returned as a comma-separated list, e.g. "ID, ra, dec". 
11341191        **kwargs : 
11351192            Service-specific parameters and MashupRequest properties. See the 
11361193            `service documentation <https://mast.stsci.edu/api/v0/_services.html>`__ and the 
11371194            `MashupRequest Class Reference <https://mast.stsci.edu/api/v0/class_mashup_1_1_mashup_request.html>`__ 
11381195            for valid keyword arguments. 
11391196
1197+             For filtered services (i.e. those with "filtered" in the service name), 
1198+             parameters that are not related to position or MashupRequest properties 
1199+             are treated as filters. If the column has discrete values, the parameter value should be a 
1200+             single value or list of values, and values will be matched exactly. If the column is continuous, 
1201+             you can filter by a single value, a list of values, or a range of values. If filtering by a range of values, 
1202+             the parameter value should be a dict in the form ``{'min': minVal, 'max': maxVal}``. 
1203+ 
11401204        Returns 
11411205        ------- 
11421206        response : `~astropy.table.Table` 
11431207        """ 
11441208        # Specific keywords related to positional and MashupRequest parameters. 
1145-         position_keys  =  [ 'ra' , 'dec' , 'radius' , 'position' ] 
1146-         request_keys  =  [ 'format' , 'data' , 'filename' , 'timeout' , 'clearcache' ,
1147-                         'removecache' , 'removenullcolumns' , 'page' , 'pagesize' ] 
1209+         position_keys  =  { 'ra' , 'dec' , 'radius' , 'position' } 
1210+         request_keys  =  { 'format' , 'data' , 'filename' , 'timeout' , 'clearcache' ,
1211+                         'removecache' , 'removenullcolumns' , 'page' , 'pagesize' } 
11481212
1149-         # Explicit formatting for Mast's filtered services 
1150-         if  'filtered'  in  service .lower ():
1213+         # Split params into categories 
1214+         position_params  =  {k : v  for  k , v  in  kwargs .items () if  k .lower () in  position_keys }
1215+         request_params  =  {k : v  for  k , v  in  kwargs .items () if  k .lower () in  request_keys }
1216+         service_params  =  {k : v  for  k , v  in  kwargs .items () if  k .lower () not  in   position_keys  |  request_keys }
11511217
1152-             # Separating the filter params from the positional and service_request method params. 
1153-             filters  =  [{'paramName' : k , 'values' : kwargs [k ]} for  k  in  kwargs 
1154-                        if  k .lower () not  in   position_keys + request_keys ]
1155-             position_params  =  {k : v  for  k , v  in  kwargs .items () if  k .lower () in  position_keys }
1156-             request_params  =  {k : v  for  k , v  in  kwargs .items () if  k .lower () in  request_keys }
1218+         # Handle filtered vs. non-filtered services 
1219+         if  'filtered'  in  service .lower ():
1220+             filters  =  self ._build_filters (service_params )
11571221
1158-             # Mast's filtered services require at least one filter 
1159-             if  filters  ==  []:
1160-                 raise  InvalidQueryError ("Please provide at least one filter." )
1222+             if  not  filters :
1223+                 raise  InvalidQueryError ('Please provide at least one filter.' )
11611224
1162-             # Building 'params' for Mast.service_request 
1163-             if  columns  is  None :
1164-                 columns  =  '*' 
1225+             if  columns  is  not   None  and  isinstance (columns , list ):
1226+                 columns  =  ',' .join (columns )
11651227
1166-             params  =  {'columns' : columns ,
1167-                       'filters' : filters ,
1168-                       ** position_params 
1169-                       }
1228+             params  =  {
1229+                 'columns' : columns  or  '*' ,
1230+                 'filters' : filters ,
1231+                 ** position_params ,
1232+             }
11701233        else :
1171- 
1172-             # Separating service specific params from service_request method params 
1173-             params  =  {k : v  for  k , v  in  kwargs .items () if  k .lower () not  in   request_keys }
1174-             request_params  =  {k : v  for  k , v  in  kwargs .items () if  k .lower () in  request_keys }
1175- 
1176-             # Warning for wrong input 
11771234            if  columns  is  not   None :
1178-                 warnings .warn ("'columns' parameter will not mask non-filtered services" , InputWarning )
1235+                 warnings .warn (
1236+                     "'columns' parameter is ignored for non-filtered services." ,
1237+                     InputWarning 
1238+                 )
1239+             params  =  {** service_params , ** position_params }
11791240
11801241        return  self .service_request (service , params , ** request_params )
11811242
0 commit comments