1
1
# Licensed under a 3-clause BSD style license - see LICENSE.rst
2
2
3
3
import warnings
4
- from six import BytesIO
4
+ from six import StringIO , BytesIO
5
5
from astropy .table import Table
6
6
from astropy .io import fits
7
7
from astropy import coordinates
@@ -29,11 +29,15 @@ class HeasarcClass(BaseQuery):
29
29
TIMEOUT = conf .timeout
30
30
coord_systems = ['fk5' , 'fk4' , 'equatorial' , 'galactic' ]
31
31
32
- def query_async (self , request_payload , cache = True , url = conf . server ):
32
+ def query_async (self , request_payload , cache = True , url = None ):
33
33
"""
34
34
Submit a query based on a given request_payload. This allows detailed
35
35
control of the query to be submitted.
36
36
"""
37
+
38
+ if url is None :
39
+ url = conf .server
40
+
37
41
response = self ._request ('GET' , url , params = request_payload ,
38
42
timeout = self .TIMEOUT , cache = cache )
39
43
return response
@@ -43,7 +47,7 @@ def query_mission_list(self, cache=True, get_query_payload=False):
43
47
Returns a list of all available mission tables with descriptions
44
48
"""
45
49
request_payload = self ._args_to_payload (
46
- Entry = 'none' ,
50
+ entry = 'none' ,
47
51
mission = 'xxx' ,
48
52
displaymode = 'BatchDisplay'
49
53
)
@@ -57,8 +61,7 @@ def query_mission_list(self, cache=True, get_query_payload=False):
57
61
url = conf .server ,
58
62
cache = cache
59
63
)
60
- data = BytesIO (response .content )
61
- data_str = data .read ().decode ('utf-8' )
64
+ data_str = response .text
62
65
data_str = data_str .replace ('Table xxx does not seem to exist!\n \n \n \n Available tables:\n ' , '' )
63
66
table = Table .read (data_str , format = 'ascii.fixed_width_two_line' ,
64
67
delimiter = '+' , header_start = 1 , position_line = 2 ,
@@ -82,19 +85,12 @@ def query_mission_cols(self, mission, cache=True, get_query_payload=False,
82
85
* <custom> : User defined csv list of columns to be returned
83
86
All other parameters have no effect
84
87
"""
85
- # Query fails if nothing is found, so set search radius very large and
86
- # only take a single value (all we care about is the column names)
87
- kwargs ['resultmax' ] = 1
88
-
89
- # By default, return all column names
90
- fields = kwargs .get ('fields' , None )
91
- if fields is None :
92
- kwargs ['fields' ] = 'All'
93
88
94
89
response = self .query_region_async (position = '0.0 0.0' , mission = mission ,
95
90
radius = '361 degree' , cache = cache ,
96
91
get_query_payload = get_query_payload ,
97
- ** kwargs )
92
+ resultsmax = 1 ,
93
+ fields = 'All' )
98
94
99
95
# Return payload if requested
100
96
if get_query_payload :
@@ -177,20 +173,38 @@ def query_region_async(self, position, mission, radius,
177
173
# Submit the request
178
174
return self .query_async (request_payload , cache = cache )
179
175
180
- def _fallback (self , content ):
176
+ def _old_w3query_fallback (self , content ):
177
+ # old w3query (such as that used in ISDC) return very strange fits, with all ints
178
+
179
+ f = fits .open (BytesIO (content ))
180
+
181
+ for c in f [1 ].columns :
182
+ if c .disp is not None :
183
+ c .format = c .disp
184
+ else :
185
+ c .format = str (c .format ).replace ("I" , "A" )
186
+
187
+ I = BytesIO ()
188
+ f .writeto (I )
189
+ I .seek (0 )
190
+
191
+ return Table .read (I )
192
+
193
+ def _fallback (self , text ):
181
194
"""
182
195
Blank columns which have to be converted to float or in fail so
183
196
lets fix that by replacing with -1's
184
197
"""
185
198
186
- data = BytesIO ( content )
199
+ data = StringIO ( text )
187
200
header = fits .getheader (data , 1 ) # Get header for column info
188
201
colstart = [y for x , y in header .items () if "TBCOL" in x ]
189
202
collens = [int (float (y [1 :]))
190
203
for x , y in header .items () if "TFORM" in x ]
204
+
191
205
new_table = []
192
206
193
- old_table = content .split ("END" )[- 1 ].strip ()
207
+ old_table = text .split ("END" )[- 1 ].strip ()
194
208
for line in old_table .split ("\n " ):
195
209
newline = []
196
210
for n , tup in enumerate (zip (colstart , collens ), start = 1 ):
@@ -199,11 +213,11 @@ def _fallback(self, content):
199
213
newline .append (part )
200
214
if len (part .strip ()) == 0 :
201
215
if header ["TFORM%i" % n ][0 ] in ["F" , "I" ]:
202
- # extra space is required to sperate column
216
+ # extra space is required to separate column
203
217
newline [- 1 ] = "-1" .rjust (clen ) + " "
204
218
new_table .append ("" .join (newline ))
205
219
206
- data = BytesIO ( content .replace (old_table , "\n " .join (new_table )))
220
+ data = StringIO ( text .replace (old_table , "\n " .join (new_table )))
207
221
return Table .read (data , hdu = 1 )
208
222
209
223
def _parse_result (self , response , verbose = False ):
@@ -228,7 +242,10 @@ def _parse_result(self, response, verbose=False):
228
242
table = Table .read (data , hdu = 1 )
229
243
return table
230
244
except ValueError :
231
- return self ._fallback (response .content )
245
+ try :
246
+ return self ._fallback (response .text )
247
+ except Exception as e :
248
+ return self ._old_w3query_fallback (response .content )
232
249
233
250
def _args_to_payload (self , ** kwargs ):
234
251
"""
@@ -277,19 +294,27 @@ def _args_to_payload(self, **kwargs):
277
294
action : str, optional
278
295
Type of action to be taken (defaults to 'Query')
279
296
"""
297
+ # User-facing parameters are lower case, while parameters as passed to the HEASARC service are capitalized according to the HEASARC requirements.
298
+ # The necessary transformations are done in this function.
299
+
280
300
# Define the basic query for this object
301
+ mission = kwargs .pop ('mission' )
302
+
281
303
request_payload = dict (
282
304
tablehead = ('name=BATCHRETRIEVALCATALOG_2.0 {}'
283
- .format (kwargs .get ('mission' ))),
284
- Entry = kwargs .get ('entry' , 'none' ),
285
- Action = kwargs .get ('action' , 'Query' ),
286
- displaymode = kwargs .get ('displaymode' , 'FitsDisplay' )
305
+ .format (mission )),
306
+ Entry = kwargs .pop ('entry' , 'none' ),
307
+ Action = kwargs .pop ('action' , 'Query' ),
308
+ displaymode = kwargs .pop ('displaymode' , 'FitsDisplay' ),
309
+ resultsmax = kwargs .pop ('resultsmax' , '10' )
287
310
)
288
311
289
312
# Fill in optional information for refined queries
290
313
291
314
# Handle queries involving coordinates
292
- coordsys = kwargs .get ('coordsys' , 'fk5' )
315
+ coordsys = kwargs .pop ('coordsys' , 'fk5' )
316
+ equinox = kwargs .pop ('equinox' , None )
317
+
293
318
if coordsys .lower () == 'fk5' :
294
319
request_payload ['Coordinates' ] = 'Equatorial: R.A. Dec'
295
320
@@ -300,7 +325,6 @@ def _args_to_payload(self, **kwargs):
300
325
elif coordsys .lower () == 'equatorial' :
301
326
request_payload ['Coordinates' ] = 'Equatorial: R.A. Dec'
302
327
303
- equinox = kwargs .get ('equinox' , None )
304
328
if equinox is not None :
305
329
request_payload ['Equinox' ] = str (equinox )
306
330
@@ -312,7 +336,7 @@ def _args_to_payload(self, **kwargs):
312
336
.format (self .coord_systems ))
313
337
314
338
# Specify which table columns are to be returned
315
- fields = kwargs .get ('fields' , None )
339
+ fields = kwargs .pop ('fields' , None )
316
340
if fields is not None :
317
341
if fields .lower () == 'standard' :
318
342
request_payload ['Fields' ] = 'Standard'
@@ -322,20 +346,37 @@ def _args_to_payload(self, **kwargs):
322
346
request_payload ['varon' ] = fields .lower ().split (',' )
323
347
324
348
# Set search radius (arcmin)
325
- radius = kwargs .get ('radius' , None )
349
+ radius = kwargs .pop ('radius' , None )
326
350
if radius is not None :
327
351
request_payload ['Radius' ] = "{}" .format (u .Quantity (radius ).to (u .arcmin ))
328
352
329
353
# Maximum number of results to be returned
330
- resultmax = kwargs .get ('resultmax' , None )
354
+ resultmax = kwargs .pop ('resultmax' , None )
331
355
if resultmax is not None :
332
356
request_payload ['ResultMax' ] = int (resultmax )
333
357
334
358
# Set variable for sorting results
335
- sortvar = kwargs .get ('sortvar' , None )
359
+ sortvar = kwargs .pop ('sortvar' , None )
336
360
if sortvar is not None :
337
361
request_payload ['sortvar' ] = sortvar .lower ()
338
362
363
+ # Time range variable
364
+ _time = kwargs .pop ('time' , None )
365
+ if _time is not None :
366
+ request_payload ['Time' ] = _time
367
+
368
+ if len (kwargs ) > 0 :
369
+ mission_fields = [k .lower () for k in self .query_mission_cols (mission = mission )]
370
+
371
+ for k , v in kwargs .items ():
372
+ if k .lower () in mission_fields :
373
+ request_payload ['bparam_' + k .lower ()] = v
374
+ else :
375
+ raise ValueError ("unknown parameter '{}' provided, must be one of {!s}" .format (
376
+ k ,
377
+ mission_fields ,
378
+ ))
379
+
339
380
return request_payload
340
381
341
382
0 commit comments