|
6 | 6 | This module contains methods for searching MAST missions.
|
7 | 7 | """
|
8 | 8 |
|
9 |
| -import requests |
| 9 | +import difflib |
10 | 10 | import warnings
|
11 | 11 |
|
12 | 12 | from astropy.table import Table
|
@@ -40,6 +40,7 @@ def __init__(self, *, mission='hst', service='search'):
|
40 | 40 | self.service = service
|
41 | 41 | self.mission = mission
|
42 | 42 | self.limit = 5000
|
| 43 | + self.columns = dict() # Info about columns for each mission |
43 | 44 |
|
44 | 45 | service_dict = {self.service: {'path': self.service, 'args': {}}}
|
45 | 46 | self._service_api_connection.set_service_params(service_dict, f"{self.service}/{self.mission}")
|
@@ -69,6 +70,37 @@ def _parse_result(self, response, *, verbose=False): # Used by the async_to_syn
|
69 | 70 |
|
70 | 71 | return results
|
71 | 72 |
|
| 73 | + def _validate_criteria(self, **criteria): |
| 74 | + """ |
| 75 | + Check that criteria keyword arguments are valid column names for the mission. |
| 76 | + Raises InvalidQueryError if a criteria argument is invalid. |
| 77 | +
|
| 78 | + Parameters |
| 79 | + ---------- |
| 80 | + **kwargs |
| 81 | + Keyword arguments representing criteria filters to apply. |
| 82 | +
|
| 83 | + Raises |
| 84 | + ------- |
| 85 | + InvalidQueryError |
| 86 | + If a keyword does not match any valid column names, an error is raised that suggests the closest |
| 87 | + matching column name, if available. |
| 88 | + """ |
| 89 | + # Ensure that self.columns in populated |
| 90 | + self.get_column_list() |
| 91 | + |
| 92 | + # Check each criteria argument for validity |
| 93 | + valid_cols = self.columns[self.mission]['name'] |
| 94 | + for kwd in criteria.keys(): |
| 95 | + if kwd not in valid_cols and kwd not in self._search_option_fields: |
| 96 | + closest_match = difflib.get_close_matches(kwd, valid_cols, n=1) |
| 97 | + error_msg = ( |
| 98 | + f"Filter '{kwd}' does not exist. Did you mean '{closest_match[0]}'?" |
| 99 | + if closest_match |
| 100 | + else f"Filter '{kwd}' does not exist." |
| 101 | + ) |
| 102 | + raise InvalidQueryError(error_msg) |
| 103 | + |
72 | 104 | @class_or_instance
|
73 | 105 | def query_region_async(self, coordinates, *, radius=3*u.arcmin, limit=5000, offset=0, **kwargs):
|
74 | 106 | """
|
@@ -104,6 +136,9 @@ def query_region_async(self, coordinates, *, radius=3*u.arcmin, limit=5000, offs
|
104 | 136 |
|
105 | 137 | self.limit = limit
|
106 | 138 |
|
| 139 | + # Check that criteria arguments are valid |
| 140 | + self._validate_criteria(**kwargs) |
| 141 | + |
107 | 142 | # Put coordinates and radius into consistent format
|
108 | 143 | coordinates = commons.parse_coordinates(coordinates)
|
109 | 144 |
|
@@ -170,6 +205,9 @@ def query_criteria_async(self, *, coordinates=None, objectname=None, radius=3*u.
|
170 | 205 |
|
171 | 206 | self.limit = limit
|
172 | 207 |
|
| 208 | + # Check that criteria arguments are valid |
| 209 | + self._validate_criteria(**criteria) |
| 210 | + |
173 | 211 | if objectname or coordinates:
|
174 | 212 | coordinates = utils.parse_input_location(coordinates, objectname)
|
175 | 213 |
|
@@ -237,25 +275,29 @@ def get_column_list(self):
|
237 | 275 |
|
238 | 276 | Returns
|
239 | 277 | -------
|
240 |
| - response : `~astropy.table.Table` that contains columns names, types and their descriptions |
| 278 | + response : `~astropy.table.Table` that contains columns names, types, and descriptions |
241 | 279 | """
|
242 | 280 |
|
243 |
| - url = f"{conf.server}/search/util/api/v0.1/column_list?mission={self.mission}" |
244 |
| - |
245 |
| - try: |
246 |
| - results = requests.get(url) |
247 |
| - results = results.json() |
248 |
| - rows = [] |
249 |
| - for result in results: |
250 |
| - result.pop('field_name') |
251 |
| - result.pop('queryable') |
252 |
| - result.pop('indexed') |
253 |
| - result.pop('default_output') |
254 |
| - rows.append((result['column_name'], result['qual_type'], result['description'])) |
255 |
| - data_table = Table(rows=rows, names=('name', 'data_type', 'description')) |
256 |
| - return data_table |
257 |
| - except Exception: |
258 |
| - raise Exception(f"Error occurred while trying to get column list for mission {self.mission}") |
| 281 | + if not self.columns.get(self.mission): |
| 282 | + try: |
| 283 | + # Send server request to get column list for current mission |
| 284 | + params = {'mission': self.mission} |
| 285 | + resp = utils._simple_request(f'{conf.server}/search/util/api/v0.1/column_list', params) |
| 286 | + |
| 287 | + # Parse JSON and extract necessary info |
| 288 | + results = resp.json() |
| 289 | + rows = [ |
| 290 | + (result['column_name'], result['qual_type'], result['description']) |
| 291 | + for result in results |
| 292 | + ] |
| 293 | + |
| 294 | + # Create Table with parsed data |
| 295 | + col_table = Table(rows=rows, names=('name', 'data_type', 'description')) |
| 296 | + self.columns[self.mission] = col_table |
| 297 | + except Exception: |
| 298 | + raise Exception(f'Error occurred while trying to get column list for mission {self.mission}') |
| 299 | + |
| 300 | + return self.columns[self.mission] |
259 | 301 |
|
260 | 302 |
|
261 | 303 | MastMissions = MastMissionsClass()
|
0 commit comments