Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 67 additions & 9 deletions astroquery/esasky/__init__.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,80 @@
# Licensed under a 3-clause BSD style license - see LICENSE.rst
import warnings

from astropy import config as _config
from astropy.config import paths
from astropy.utils.exceptions import AstropyDeprecationWarning

import os

ESASKY_COMMON_SERVER = "https://sky.esa.int/esasky-tap/"

ESASKY_TAP_COMMON = "tap"


class Conf(_config.ConfigNamespace):
"""
Configuration parameters for `astroquery.esasky`.
"""
urlBase = _config.ConfigItem(
'https://sky.esa.int/esasky-tap',
'ESASky base URL')

timeout = _config.ConfigItem(
1000,
'Time limit for connecting to template_module server.')
ESASKY_DOMAIN_SERVER = _config.ConfigItem(ESASKY_COMMON_SERVER, "ESASky TAP Common Server",
aliases=['astroquery.esasky.urlBase'])
ESASKY_TAP_SERVER = _config.ConfigItem(ESASKY_COMMON_SERVER + ESASKY_TAP_COMMON, "ESASky TAP Server")
ESASKY_DATA_SERVER = _config.ConfigItem(ESASKY_COMMON_SERVER + 'data?', "ESASky Data Server")
ESASKY_TABLES_SERVER = _config.ConfigItem(ESASKY_COMMON_SERVER + ESASKY_TAP_COMMON + "/tables",
"ESASky TAP Tables Server")
ESASKY_TARGET_ACTION = _config.ConfigItem("servlet/target-resolver?", "ESASky Target Resolver")
ESASKY_MESSAGES = _config.ConfigItem("notification?action=GetNotifications", "ESASky Messages")
ESASKY_LOGIN_SERVER = _config.ConfigItem(ESASKY_COMMON_SERVER + 'login', "ESASky Login Server")
ESASKY_LOGOUT_SERVER = _config.ConfigItem(ESASKY_COMMON_SERVER + 'logout', "ESASky Logout Server")
ESASKY_CONNECTION_TIMEOUT = _config.ConfigItem(1000, 'Time limit for connecting to a data product server.',
aliases=['astroquery.esasky.timeout'])
ESASKY_ROW_LIMIT = _config.ConfigItem(10000, 'Maximum number of rows returned (set to -1 for unlimited).',
aliases=['astroquery.esasky.row_limit'])

@property
def urlBase(self):
return self.ESASKY_DOMAIN_SERVER

@urlBase.setter
def urlBase(self, value):
warnings.warn(
"'urlBase' is deprecated and will be removed in a future version. "
"Use 'ESASKY_DOMAIN_SERVER' instead.",
AstropyDeprecationWarning,
stacklevel=2,
)
self.ESASKY_DOMAIN_SERVER = value

@property
def timeout(self):
return self.ESASKY_CONNECTION_TIMEOUT

@timeout.setter
def timeout(self, value):
warnings.warn(
"'timeout' is deprecated and will be removed in a future version. "
"Use 'ESASKY_CONNECTION_TIMEOUT' instead.",
AstropyDeprecationWarning,
stacklevel=2,
)
self.ESASKY_CONNECTION_TIMEOUT = value

@property
def row_limit(self):
return self.ESASKY_ROW_LIMIT

@row_limit.setter
def row_limit(self, value):
warnings.warn(
"'row_limit' is deprecated and will be removed in a future version. "
"Use 'ESASKY_ROW_LIMIT' instead.",
AstropyDeprecationWarning,
stacklevel=2,
)
self.ESASKY_ROW_LIMIT = value

row_limit = _config.ConfigItem(
10000,
'Maximum number of rows returned (set to -1 for unlimited).')
cache_location = os.path.join(paths.get_cache_dir(), 'astroquery/esasky', )


conf = Conf()
Expand Down
146 changes: 67 additions & 79 deletions astroquery/esasky/core.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Licensed under a 3-clause BSD style license - see LICENSE.rst

from astropy.utils.exceptions import AstropyDeprecationWarning
import astroquery.esa.utils.utils as esautils
import json
import os
import tarfile as esatar
Expand All @@ -14,14 +15,13 @@
from astropy.coordinates import Angle
from astropy.io import fits
from astropy.utils.console import ProgressBar
from astropy.utils import deprecated
from astroquery import log
from requests import HTTPError
from requests import ConnectionError

from ..query import BaseQuery
from ..utils.tap.core import TapPlus
from astroquery.esa.utils import EsaTap
from ..utils import commons
from ..utils import async_to_sync
from . import conf
from .. import version
from astropy.coordinates.name_resolve import sesame_database
Expand All @@ -33,12 +33,15 @@
esatar.TarFile.extraction_filter = staticmethod(esatar.fully_trusted_filter)


@async_to_sync
class ESASkyClass(BaseQuery):
class ESASkyClass(EsaTap):

URLbase = conf.urlBase
TIMEOUT = conf.timeout
DEFAULT_ROW_LIMIT = conf.row_limit
URLbase = conf.ESASKY_DOMAIN_SERVER
ESA_ARCHIVE_NAME = "ESASky"
TAP_URL = conf.ESASKY_TAP_SERVER
LOGIN_URL = conf.ESASKY_LOGIN_SERVER
LOGOUT_URL = conf.ESASKY_LOGOUT_SERVER
CONNECTION_TIMEOUT = conf.ESASKY_CONNECTION_TIMEOUT
DEFAULT_ROW_LIMIT = conf.ESASKY_ROW_LIMIT

__FITS_STRING = ".fits"
__FTZ_STRING = ".FTZ"
Expand Down Expand Up @@ -79,21 +82,29 @@ class ESASkyClass(BaseQuery):
SSO_TYPES = ['ALL', 'ASTEROID', 'COMET', 'SATELLITE', 'PLANET',
'DWARF_PLANET', 'SPACECRAFT', 'SPACEJUNK', 'EXOPLANET', 'STAR']

def __init__(self, tap_handler=None):
super().__init__()
def __init__(self, *, tap_handler=None, show_messages=False, auth_session=None, tap_url=None):
super().__init__(auth_session=auth_session, tap_url=tap_url)
if tap_handler is not None:
warnings.warn(
"The 'tap_handler' parameter is deprecated and will be removed in a future version. "
"Use the ESASky instance directly for TAP queries (Using esa.utils.EsaTap and PyVO).",
AstropyDeprecationWarning,
stacklevel=2,
)

if tap_handler is None:
self._tap = TapPlus(url=self.URLbase + "/tap")
else:
self._tap = tap_handler
if show_messages:
self.get_status_messages()

def query(self, query, *, output_file=None, output_format="votable", verbose=False):
"""Launches a synchronous job to query the ESASky TAP
def query(self, query, *, async_job=False, output_file=None, output_format="votable", verbose=False):
"""Launches a synchronous or asynchronous job to query the ESASky TAP

Parameters
----------
query : str, mandatory
query (adql) to be executed
async_job : bool, optional, default 'False'
executes the query (job) in asynchronous/synchronous mode (default
synchronous)
output_file : str, optional, default None
file name where the results are saved if dumpToFile is True.
If this parameter is not provided, the jobid is used instead
Expand All @@ -107,44 +118,10 @@ def query(self, query, *, output_file=None, output_format="votable", verbose=Fal
-------
A table object
"""
if not verbose:
with warnings.catch_warnings():
commons.suppress_vo_warnings()
warnings.filterwarnings("ignore", category=u.UnitsWarning)
job = self._tap.launch_job(query=query, output_file=output_file, output_format=output_format,
verbose=False, dump_to_file=output_file is not None)
else:
job = self._tap.launch_job(query=query, output_file=output_file, output_format=output_format,
verbose=True, dump_to_file=output_file is not None)
return job.get_results()
return self.query_tap(query=query, async_job=async_job, output_file=output_file, output_format=output_format,
verbose=verbose)

def get_tables(self, *, only_names=True, verbose=False, cache=True):
"""
Get the available table in ESASky TAP service

Parameters
----------
only_names : bool, optional, default 'True'
True to load table names only
verbose : bool, optional, default 'False'
flag to display information about the process

Returns
-------
A list of tables
"""

if cache and self._cached_tables is not None:
tables = self._cached_tables
else:
tables = self._tap.load_tables(only_names=only_names, include_shared_tables=False, verbose=verbose)
self._cached_tables = tables
if only_names:
return [t.name for t in tables]
else:
return tables

def get_columns(self, table_name, *, only_names=True, verbose=False):
def get_columns(self, table_name, *, only_names=True):
"""
Get the available columns for a table in ESASky TAP service

Expand All @@ -154,30 +131,20 @@ def get_columns(self, table_name, *, only_names=True, verbose=False):
table name of which, columns will be returned
only_names : bool, optional, default 'True'
True to load table names only
verbose : bool, optional, default 'False'
flag to display information about the process

Returns
-------
A list of columns
"""

tables = self.get_tables(only_names=False, verbose=verbose)
columns = None
for table in tables:
if str(table.name) == str(table_name):
columns = table.columns
break

if columns is None:
raise ValueError("table name specified is not found in "
"ESASky TAP service")
columns = self.get_table(table=table_name).columns

if only_names:
return [c.name for c in columns]
else:
return columns

@deprecated(since="0.4.12", message="The ESASky module no longer uses the TapPlus module. Equivalent functionality"
"is available directly on the ESASky module (Using esa.utils.EsaTap and PyVO).")
def get_tap(self):
"""
Get a TAP+ instance for the ESASky servers, which supports
Expand All @@ -191,7 +158,7 @@ def get_tap(self):
tap : `~astroquery.utils.tap.core.TapPlus`
"""

return self._tap
return self

def list_maps(self):
"""
Expand Down Expand Up @@ -944,9 +911,9 @@ def query_ids_catalogs(self, source_ids, *, catalogs=__ALL_STRING, row_limit=DEF

Examples
--------
query_ids_catalogs(source_ids=['2CXO J090341.1-322609', '2CXO J090353.8-322642'], catalogs="CHANDRA-SC2")
query_ids_catalogs(source_ids='2CXO J090341.1-322609')
query_ids_catalogs(source_ids=['2CXO J090341.1-322609', '45057'], catalogs=["CHANDRA-SC2", "Hipparcos-2"])
query_ids_catalogs(source_ids=['2CXO J031306.2-852820', '2CXO J031339.7-852543'], catalogs="CHANDRA-SC21")
query_ids_catalogs(source_ids='2CXO J031306.2-852820')
query_ids_catalogs(source_ids=['2CXO J031306.2-852820', '45057'], catalogs=["CHANDRA-SC21", "Hipparcos-2"])
"""
sanitized_catalogs = self._sanitize_input_catalogs(catalogs)
sanitized_row_limit = self._sanitize_input_row_limit(row_limit)
Expand Down Expand Up @@ -1349,6 +1316,27 @@ def get_spectra_from_table(self, query_table_list, missions=__ALL_STRING,
log.info("No spectra found.")
return spectra

def get_status_messages(self):
"""Retrieve the messages to inform users about the status of the ESASky TAP"""

try:
esautils.execute_servlet_request(
url=conf.ESASKY_TAP_SERVER + "/" + conf.ESASKY_MESSAGES,
tap=self.tap,
query_params={},
parser_method=self.parse_messages_response
)
except OSError:
print("Status messages could not be retrieved")

def parse_messages_response(self, response):
string_messages = []
for line in response.iter_lines():
string_message = line.decode("utf-8")
string_messages.append(string_message[string_message.index('=') + 1:])
print(string_messages[len(string_messages)-1])
return string_messages

def _sanitize_input_radius(self, radius):
if isinstance(radius, (str, u.Quantity)):
return radius
Expand Down Expand Up @@ -1709,9 +1697,9 @@ def _query(self, name, descriptors, verbose=False, **kwargs):
if not query:
# Could not create query. The most common reason for this is a type mismatch between user specified ID and
# data type of database column.
# For example query_ids_catalogs(source_ids=["2CXO J090341.1-322609"], mission=["CHANDRA", "HSC"])
# For example query_ids_catalogs(source_ids=["2CXO J031306.2-852820"], mission=["CHANDRA", "HSC"])
# would be able to create a query for Chandra, but not for Hubble because the hubble source id column type
# is a number and "2CXO J090341.1-322609" cannot be converted to a number.
# is a number and "2CXO J031306.2-852820" cannot be converted to a number.
return query

return self.query(query, output_format="votable", verbose=verbose)
Expand Down Expand Up @@ -1773,13 +1761,13 @@ def _build_id_query(self, ids, row_limit, descriptor):
if id_column == "designation":
id_column = "obsid"

data_type = None
for column in self.get_columns(table_name=descriptor['table_name'], only_names=False):
datatype = None
for column in self.get_table(table=descriptor['table_name']).columns:
if column.name == id_column:
data_type = column.data_type
datatype = column.datatype

valid_ids = ids
if data_type in self._NUMBER_DATA_TYPES:
if datatype in self._NUMBER_DATA_TYPES:
valid_ids = [int(obs_id) for obs_id in ids if obs_id.isdigit()]
if not valid_ids:
raise ValueError(f"Could not construct query for mission {descriptor['mission']}. Database column "
Expand Down Expand Up @@ -1850,7 +1838,7 @@ def _send_get_request(self, url_extension, request_payload, cache):
return self._request('GET',
url,
params=request_payload,
timeout=self.TIMEOUT,
timeout=self.CONNECTION_TIMEOUT,
cache=cache,
headers=self._get_header())

Expand All @@ -1861,4 +1849,4 @@ def _get_header(self):
return {'User-Agent': user_agent}


ESASky = ESASkyClass()
ESASky = ESASkyClass(show_messages=False)
Loading
Loading