7
7
"""
8
8
9
9
import difflib
10
+ from json import JSONDecodeError
10
11
import warnings
11
12
import os
12
13
import time
13
14
14
- from requests import HTTPError
15
+ from requests import HTTPError , RequestException
15
16
16
17
import astropy .units as u
17
18
import astropy .coordinates as coord
22
23
from ..utils .class_or_instance import class_or_instance
23
24
from ..exceptions import InvalidQueryError , MaxResultsWarning , InputWarning
24
25
25
- from . import utils
26
+ from . import utils , conf
26
27
from .core import MastQueryWithLogin
27
28
28
29
@@ -43,11 +44,13 @@ def __init__(self):
43
44
44
45
services = {"panstarrs" : {"path" : "panstarrs/{data_release}/{table}.json" ,
45
46
"args" : {"data_release" : "dr2" , "table" : "mean" }}}
47
+ self ._catalogs_mast_search_options = ['columns' , 'sort_by' , 'table' , 'data_release' ]
46
48
47
49
self ._service_api_connection .set_service_params (services , "catalogs" , True )
48
50
49
51
self .catalog_limit = None
50
52
self ._current_connection = None
53
+ self ._service_columns = dict () # Info about columns for Catalogs.MAST services
51
54
52
55
def _parse_result (self , response , * , verbose = False ):
53
56
@@ -59,6 +62,99 @@ def _parse_result(self, response, *, verbose=False):
59
62
60
63
return results_table
61
64
65
+ def _get_service_col_config (self , catalog , release = 'dr2' , table = 'mean' ):
66
+ """
67
+ For a given Catalogs.MAST catalog, return a list of all searchable columns and their descriptions.
68
+ As of now, this function is exclusive to the Pan-STARRS catalog.
69
+
70
+ Parameters
71
+ ----------
72
+ catalog : str
73
+ The catalog to be queried.
74
+ release : str, optional
75
+ Catalog data release to query from.
76
+ table : str, optional
77
+ Catalog table to query from.
78
+
79
+ Returns
80
+ -------
81
+ response : `~astropy.table.Table` that contains columns names, types, and descriptions
82
+ """
83
+ # Only supported for PanSTARRS currently
84
+ if catalog != 'panstarrs' :
85
+ return
86
+
87
+ service_key = (catalog , release , table )
88
+ if service_key not in self ._service_columns :
89
+ try :
90
+ # Send server request to get column list for given parameters
91
+ request_url = f'{ conf .catalogs_server } /api/v0.1/{ catalog } /{ release } /{ table } /metadata.json'
92
+ resp = utils ._simple_request (request_url )
93
+
94
+ # Parse JSON and extract necessary info
95
+ results = resp .json ()
96
+ rows = [
97
+ (result ['column_name' ], result ['db_type' ], result ['description' ])
98
+ for result in results
99
+ ]
100
+
101
+ # Create Table with parsed data
102
+ col_table = Table (rows = rows , names = ('name' , 'data_type' , 'description' ))
103
+ self ._service_columns [service_key ] = col_table
104
+
105
+ except JSONDecodeError as ex :
106
+ raise JSONDecodeError (f'Failed to decode JSON response while attempting to get column list'
107
+ f' for { catalog } catalog { table } , { release } : { ex } ' )
108
+ except RequestException as ex :
109
+ raise ConnectionError (f'Failed to connect to the server while attempting to get column list'
110
+ f' for { catalog } catalog { table } , { release } : { ex } ' )
111
+ except KeyError as ex :
112
+ raise KeyError (f'Expected key not found in response data while attempting to get column list'
113
+ f' for { catalog } catalog { table } , { release } : { ex } ' )
114
+ except Exception as ex :
115
+ raise RuntimeError (f'An unexpected error occurred while attempting to get column list'
116
+ f' for { catalog } catalog { table } , { release } : { ex } ' )
117
+
118
+ return self ._service_columns [service_key ]
119
+
120
+ def _validate_service_criteria (self , catalog , ** criteria ):
121
+ """
122
+ Check that criteria keyword arguments are valid column names for the service.
123
+ Raises InvalidQueryError if a criteria argument is invalid.
124
+
125
+ Parameters
126
+ ----------
127
+ catalog : str
128
+ The catalog to be queried.
129
+ **criteria
130
+ Keyword arguments representing criteria filters to apply.
131
+
132
+ Raises
133
+ -------
134
+ InvalidQueryError
135
+ If a keyword does not match any valid column names, an error is raised that suggests the closest
136
+ matching column name, if available.
137
+ """
138
+ # Ensure that self._service_columns is populated
139
+ release = criteria .get ('data_release' , 'dr2' )
140
+ table = criteria .get ('table' , 'mean' )
141
+ col_config = self ._get_service_col_config (catalog , release , table )
142
+
143
+ if col_config :
144
+ # Check each criteria argument for validity
145
+ valid_cols = list (col_config ['name' ]) + self ._catalogs_mast_search_options
146
+ for kwd in criteria .keys ():
147
+ col = next ((name for name in valid_cols if name .lower () == kwd .lower ()), None )
148
+ if not col :
149
+ closest_match = difflib .get_close_matches (kwd , valid_cols , n = 1 )
150
+ error_msg = (
151
+ f"Filter '{ kwd } ' does not exist for { catalog } catalog { table } , { release } . "
152
+ f"Did you mean '{ closest_match [0 ]} '?"
153
+ if closest_match
154
+ else f"Filter '{ kwd } ' does not exist for { catalog } catalog { table } , { release } ."
155
+ )
156
+ raise InvalidQueryError (error_msg )
157
+
62
158
@class_or_instance
63
159
def query_region_async (self , coordinates , * , radius = 0.2 * u .deg , catalog = "Hsc" ,
64
160
version = None , pagesize = None , page = None , ** criteria ):
@@ -92,7 +188,15 @@ def query_region_async(self, coordinates, *, radius=0.2*u.deg, catalog="Hsc",
92
188
**criteria
93
189
Other catalog-specific keyword args.
94
190
These can be found in the (service documentation)[https://mast.stsci.edu/api/v0/_services.html]
95
- for specific catalogs. For example one can specify the magtype for an HSC search.
191
+ for specific catalogs. For example, one can specify the magtype for an HSC search.
192
+ For catalogs available through Catalogs.MAST (PanSTARRS), the Column Name is the keyword, and the argument
193
+ should be either an acceptable value for that parameter, or a list consisting values, or tuples of
194
+ decorator, value pairs (decorator, value). In addition, columns may be used to select the return columns,
195
+ consisting of a list of column names. Results may also be sorted through the query with the parameter
196
+ sort_by composed of either a single Column Name to sort ASC, or a list of Column Nmaes to sort ASC or
197
+ tuples of Column Name and Direction (ASC, DESC) to indicate sort order (Column Name, DESC).
198
+ Detailed information of Catalogs.MAST criteria usage can
199
+ be found `here <https://catalogs.mast.stsci.edu/docs/index.html>`__.
96
200
97
201
Returns
98
202
-------
@@ -110,21 +214,24 @@ def query_region_async(self, coordinates, *, radius=0.2*u.deg, catalog="Hsc",
110
214
'dec' : coordinates .dec .deg ,
111
215
'radius' : radius .deg }
112
216
113
- # valid criteria keywords
114
- valid_criteria = []
115
-
116
217
# Determine API connection and service name
117
218
if catalog .lower () in self ._service_api_connection .SERVICES :
118
219
self ._current_connection = self ._service_api_connection
119
220
service = catalog
120
221
222
+ # validate user criteria
223
+ self ._validate_service_criteria (catalog .lower (), ** criteria )
224
+
121
225
# adding additional user specified parameters
122
226
for prop , value in criteria .items ():
123
227
params [prop ] = value
124
228
125
229
else :
126
230
self ._current_connection = self ._portal_api_connection
127
231
232
+ # valid criteria keywords
233
+ valid_criteria = []
234
+
128
235
# Sorting out the non-standard portal service names
129
236
if catalog .lower () == "hsc" :
130
237
if version == 2 :
@@ -217,7 +324,15 @@ def query_object_async(self, objectname, *, radius=0.2*u.deg, catalog="Hsc",
217
324
**criteria
218
325
Catalog-specific keyword args.
219
326
These can be found in the `service documentation <https://mast.stsci.edu/api/v0/_services.html>`__.
220
- for specific catalogs. For example one can specify the magtype for an HSC search.
327
+ for specific catalogs. For example, one can specify the magtype for an HSC search.
328
+ For catalogs available through Catalogs.MAST (PanSTARRS), the Column Name is the keyword, and the argument
329
+ should be either an acceptable value for that parameter, or a list consisting values, or tuples of
330
+ decorator, value pairs (decorator, value). In addition, columns may be used to select the return columns,
331
+ consisting of a list of column names. Results may also be sorted through the query with the parameter
332
+ sort_by composed of either a single Column Name to sort ASC, or a list of Column Nmaes to sort ASC or
333
+ tuples of Column Name and Direction (ASC, DESC) to indicate sort order (Column Name, DESC).
334
+ Detailed information of Catalogs.MAST criteria usage can
335
+ be found `here <https://catalogs.mast.stsci.edu/docs/index.html>`__.
221
336
222
337
Returns
223
338
-------
@@ -298,6 +413,9 @@ def query_criteria_async(self, catalog, *, pagesize=None, page=None, **criteria)
298
413
self ._current_connection = self ._service_api_connection
299
414
service = catalog
300
415
416
+ # validate user criteria
417
+ self ._validate_service_criteria (catalog .lower (), ** criteria )
418
+
301
419
if not self ._current_connection .check_catalogs_criteria_params (criteria ):
302
420
raise InvalidQueryError ("At least one non-positional criterion must be supplied." )
303
421
0 commit comments