1414from astropy .table import Table , MaskedColumn
1515from astropy .utils .decorators import deprecated_renamed_argument
1616
17+ from .. import log
1718from ..query import BaseQuery
1819from ..utils import async_to_sync
1920from ..utils .class_or_instance import class_or_instance
@@ -84,7 +85,12 @@ def _json_to_table(json_obj, data_key='data'):
8485 col_data = np .array ([x [idx ] for x in json_obj [data_key ]], dtype = object )
8586 except KeyError :
8687 # it's not a data array, fall back to using column name as it is array of dictionaries
87- col_data = np .array ([x [col_name ] for x in json_obj [data_key ]], dtype = object )
88+ try :
89+ col_data = np .array ([x [col_name ] for x in json_obj [data_key ]], dtype = object )
90+ except KeyError :
91+ # Skip column names not found in data
92+ log .debug ('Column %s was not found in data. Skipping...' , col_name )
93+ continue
8894 if ignore_value is not None :
8995 col_data [np .where (np .equal (col_data , None ))] = ignore_value
9096
@@ -112,6 +118,8 @@ class ServiceAPI(BaseQuery):
112118
113119 SERVICE_URL = conf .server
114120 REQUEST_URL = conf .server + "/api/v0.1/"
121+ MISSIONS_DOWNLOAD_URL = conf .server + "/search/"
122+ MAST_DOWNLOAD_URL = conf .server + "/api/v0.1/Download/file"
115123 SERVICES = {}
116124
117125 def __init__ (self , session = None ):
@@ -270,27 +278,28 @@ def service_request_async(self, service, params, pagesize=None, page=None, use_j
270278
271279 request_url = self .REQUEST_URL + service_url .format (** compiled_service_args )
272280
281+ # Default headers
273282 headers = {
274283 'User-Agent' : self ._session .headers ['User-Agent' ],
275284 'Content-Type' : 'application/x-www-form-urlencoded' ,
276285 'Accept' : 'application/json'
277286 }
287+
278288 # Params as a list of tuples to allow for multiple parameters added
279289 catalogs_request = []
280- if not page :
281- page = params .pop ('page' , None )
282- if not pagesize :
283- pagesize = params .pop ('pagesize' , None )
290+ page = page or params .pop ('page' , None )
291+ pagesize = pagesize or params .pop ('pagesize' , None )
284292
293+ # Add pagination if specified
285294 if page is not None :
286295 catalogs_request .append (('page' , page ))
287296 if pagesize is not None :
288297 catalogs_request .append (('pagesize' , pagesize ))
289298
299+ # Populate parameters based on `use_json`
290300 if not use_json :
291- # Decompose filters, sort
292- for prop , value in kwargs .items ():
293- params [prop ] = value
301+ # When not using JSON, merge kwargs into params and build query
302+ params .update (kwargs )
294303 catalogs_request .extend (self ._build_catalogs_params (params ))
295304 else :
296305 headers ['Content-Type' ] = 'application/json'
@@ -307,9 +316,10 @@ def service_request_async(self, service, params, pagesize=None, page=None, use_j
307316 catalogs_request = params_dict
308317
309318 # Removing single-element lists. Single values will live on their own (except for `sort_by`)
310- for key in catalogs_request .keys ():
311- if (key != 'sort_by' ) & (len (catalogs_request [key ]) == 1 ):
312- catalogs_request [key ] = catalogs_request [key ][0 ]
319+ catalogs_request = {
320+ k : v if k == 'sort_by' or len (v ) > 1 else v [0 ]
321+ for k , v in params_dict .items ()
322+ }
313323
314324 # Otherwise, catalogs_request can remain as the original params dict
315325 else :
@@ -318,6 +328,40 @@ def service_request_async(self, service, params, pagesize=None, page=None, use_j
318328 response = self ._request ('POST' , request_url , data = catalogs_request , headers = headers , use_json = use_json )
319329 return response
320330
331+ @class_or_instance
332+ def missions_request_async (self , service , params ):
333+ """
334+ Builds and executes an asynchronous query to the MAST Search API.
335+ Parameters
336+ ----------
337+ service : str
338+ The MAST Search API service to query. Should be present in self.SERVICES.
339+ params : dict
340+ JSON object containing service parameters.
341+ Returns
342+ -------
343+ response : list of `~requests.Response`
344+ """
345+ service_config = self .SERVICES .get (service .lower ())
346+ request_url = self .REQUEST_URL + service_config .get ('path' )
347+
348+ # Default headers
349+ headers = {
350+ 'User-Agent' : self ._session .headers ['User-Agent' ],
351+ 'Content-Type' : 'application/json' ,
352+ 'Accept' : 'application/json'
353+ }
354+
355+ # make request
356+ data , params = (params , None )
357+ response = self ._request (method = 'POST' ,
358+ url = request_url ,
359+ params = params ,
360+ data = data ,
361+ headers = headers ,
362+ use_json = True )
363+ return response
364+
321365 def _build_catalogs_params (self , params ):
322366 """
323367 Gathers parameters for Catalogs.MAST usage and translates to valid API syntax tuples
@@ -387,12 +431,6 @@ def check_catalogs_criteria_params(self, criteria):
387431 response : boolean
388432 Whether the passed dict has at least one criteria parameter
389433 """
390- criteria_check = False
391- non_criteria_params = ["columns" , "sort_by" , "page_size" , "pagesize" , "page" ]
392- criteria_keys = criteria .keys ()
393- for key in criteria_keys :
394- if key not in non_criteria_params :
395- criteria_check = True
396- break
397434
398- return criteria_check
435+ non_criteria_params = ["columns" , "sort_by" , "page_size" , "pagesize" , "page" ]
436+ return any (key not in non_criteria_params for key in criteria )
0 commit comments