diff --git a/CHANGES.rst b/CHANGES.rst index eabaaef26d..d6dca1e0a9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -21,10 +21,10 @@ esa.euclid ``datalabs_path``, ``file_name`` and ``hdu_index``. [#3438] - The default value of the parameter ``output_format`` in the the methods ``launch_job``, ``launch_job_async`` and ``cone_search`` is changed to "votable_gzip". [#3497] - - - Methods ``cone_search`` and ``cross_match_basic`` now define the parameters ``table_name`` and ``ra_column_name`` and ``dec_column_name`` independently [#3496] +- The method ``get_spectrum`` accepts the new parameter ``linking_parameter`` to retrieve the spectra by source_id and + sourcepatch_id [#3543] vizier ^^^^^^ diff --git a/astroquery/esa/euclid/__init__.py b/astroquery/esa/euclid/__init__.py index b193ff4e9b..30de2cf3f8 100644 --- a/astroquery/esa/euclid/__init__.py +++ b/astroquery/esa/euclid/__init__.py @@ -137,6 +137,8 @@ class Conf(_config.ConfigNamespace): VALID_DATALINK_RETRIEVAL_TYPES = ['SPECTRA_BGS', 'SPECTRA_RGS'] + VALID_LINKING_PARAMETERS = {'SOURCE_ID', 'SOURCEPATCH_ID'} + conf = Conf() diff --git a/astroquery/esa/euclid/core.py b/astroquery/esa/euclid/core.py index c27e765e7e..af35de3e37 100644 --- a/astroquery/esa/euclid/core.py +++ b/astroquery/esa/euclid/core.py @@ -18,6 +18,7 @@ from astropy import units as u from astropy.coordinates import Angle from astropy.units import Quantity +from astropy.utils import deprecated_renamed_argument from requests.exceptions import HTTPError from astroquery import log @@ -34,6 +35,7 @@ class EuclidClass(TapPlus): __ERROR_MSG_REQUESTED_PRODUCT_TYPE = "Missing required argument: 'product_type'" __ERROR_MSG_REQUESTED_GENERIC = "Missing required argument" __ERROR_MSG_REQUESTED_RADIUS = "Radius cannot be greater than 30 arcminutes" + EUCLID_MESSAGES = "notification?action=GetNotifications" """ @@ -42,6 +44,7 @@ class EuclidClass(TapPlus): ROW_LIMIT = conf.ROW_LIMIT __VALID_DATALINK_RETRIEVAL_TYPES = conf.VALID_DATALINK_RETRIEVAL_TYPES + __VALID_LINKING_PARAMETERS = conf.VALID_LINKING_PARAMETERS def __init__(self, *, environment='PDR', tap_plus_conn_handler=None, datalink_handler=None, cutout_handler=None, verbose=False, show_server_messages=True): @@ -1450,7 +1453,9 @@ def get_cutout(self, *, file_path=None, instrument=None, id=None, coordinate, ra return files - def get_spectrum(self, *, source_id, schema='sedm', retrieval_type="ALL", output_file=None, verbose=False): + @deprecated_renamed_argument('source_id', 'ids', since='0.4.12') + def get_spectrum(self, *, ids, schema='sedm', retrieval_type="ALL", linking_parameter='SOURCE_ID', + output_file=None, verbose=False): """ Downloads a spectrum with datalink. @@ -1462,13 +1467,17 @@ def get_spectrum(self, *, source_id, schema='sedm', retrieval_type="ALL", output Parameters ---------- - source_id : str, mandatory, default None - source id for the spectrum + ids : str or int, mandatory + identifier for the spectrum schema : str, mandatory, default 'sedm' the data release retrieval_type : str, optional, default 'ALL' to retrieve all data from the list of sources retrieval type identifier. Possible values are: 'SPECTRA_BGS' for the blue spectrum and 'SPECTRA_RGS' for the red one. + linking_parameter : str, optional, default SOURCE_ID, valid values: SOURCE_ID or SOURCEPATCH_ID + By default, all the identifiers are considered as source_id + SOURCE_ID: the identifiers are considered as source_id + SOURCEPATCH_ID: the identifiers are considered as sourcepatch_id output_file : str, optional output file name. If no value is provided, a temporary one is created with the name "/temp_<%Y%m%d_%H%M%S>/.fits" @@ -1483,7 +1492,7 @@ def get_spectrum(self, *, source_id, schema='sedm', retrieval_type="ALL", output """ - if source_id is None or schema is None: + if ids is None or schema is None: raise ValueError(self.__ERROR_MSG_REQUESTED_GENERIC) rt = str(retrieval_type).upper() @@ -1493,14 +1502,22 @@ def get_spectrum(self, *, source_id, schema='sedm', retrieval_type="ALL", output params_dict = {} - id_value = """{schema} {source_id}""".format(**{'schema': schema, 'source_id': source_id}) + id_value = """{schema} {source_id}""".format(**{'schema': schema, 'source_id': ids}) params_dict['ID'] = id_value params_dict['SCHEMA'] = schema params_dict['RETRIEVAL_TYPE'] = str(retrieval_type) params_dict['USE_ZIP_ALWAYS'] = 'true' params_dict['TAPCLIENT'] = 'ASTROQUERY' - fits_file = source_id + '.fits.zip' + if linking_parameter not in self.__VALID_LINKING_PARAMETERS: + raise ValueError( + f"Invalid linking_parameter value '{linking_parameter}' (Valid values: " + f"{', '.join(self.__VALID_LINKING_PARAMETERS)})") + else: + if linking_parameter != 'SOURCE_ID': + params_dict['LINKING_PARAMETER'] = linking_parameter + + fits_file = ids + '.fits.zip' if output_file is not None: if not output_file.endswith('.zip'): @@ -1522,10 +1539,10 @@ def get_spectrum(self, *, source_id, schema='sedm', retrieval_type="ALL", output try: self.__eucliddata.load_data(params_dict=params_dict, output_file=output_file_full_path, verbose=verbose) except HTTPError as err: - log.error(f'Cannot retrieve spectrum for source_id {source_id}, schema {schema}. HTTP error: {err}') + log.error(f'Cannot retrieve spectrum for source_id {ids}, schema {schema}. HTTP error: {err}') return None except Exception as exx: - log.error(f'Cannot retrieve spectrum for source_id {source_id}, schema {schema}: {str(exx)}') + log.error(f'Cannot retrieve spectrum for source_id {ids}, schema {schema}: {str(exx)}') return None self.__extract_file(output_file_full_path=output_file_full_path, output_dir=output_dir, files=files) @@ -1548,8 +1565,10 @@ def get_datalinks(self, ids, *, linking_parameter='SOURCE_ID', extra_options=Non ---------- ids : str, int, list of str or list of int, mandatory list of identifiers - linking_parameter : str, optional, default SOURCE_ID, valid values: SOURCE_ID + linking_parameter : str, optional, default SOURCE_ID, valid values: SOURCE_ID or SOURCEPATCH_ID By default, all the identifiers are considered as source_id + SOURCE_ID: the identifiers are considered as source_id + SOURCEPATCH_ID: the identifiers are considered as sourcepatch_id extra_options : str, optional, default None, valid values: METADATA To let customize the server behaviour, if present. If provided with value METADATA, the extra fields datalabs_path, file_name & hdu_index will be retrieved. @@ -1562,10 +1581,17 @@ def get_datalinks(self, ids, *, linking_parameter='SOURCE_ID', extra_options=Non """ - return self.__eucliddata.get_datalinks(ids=ids, - linking_parameter=linking_parameter, - extra_options=extra_options, - verbose=verbose) + if linking_parameter not in self.__VALID_LINKING_PARAMETERS: + raise ValueError( + f"Invalid linking_parameter value '{linking_parameter}' (Valid values: " + f"{', '.join(self.__VALID_LINKING_PARAMETERS)})") + + final_linking_parameter = None + if linking_parameter != 'SOURCE_ID': + final_linking_parameter = linking_parameter + + return self.__eucliddata.get_datalinks(ids=ids, linking_parameter=final_linking_parameter, + extra_options=extra_options, verbose=verbose) def get_scientific_product_list(self, *, observation_id=None, tile_index=None, category=None, group=None, product_type=None, dataset_release='REGREPROC1_R2', dsr_part1=None, dsr_part2=None, diff --git a/astroquery/esa/euclid/tests/DummyTapHandler.py b/astroquery/esa/euclid/tests/DummyTapHandler.py index 820a0e83dd..4507fd8bf4 100644 --- a/astroquery/esa/euclid/tests/DummyTapHandler.py +++ b/astroquery/esa/euclid/tests/DummyTapHandler.py @@ -291,11 +291,9 @@ def get_cutout(self, coordinate, file_path=None, self.__parameters['output_file'] = output_file return output_file - def get_spectrum(self, source_id=None, - schema='sedm_pvpr01', - output_file=None): + def get_spectrum(self, id=None, schema='sedm_pvpr01', output_file=None): self.__invokedMethod = 'get_spectrum' - self.__parameters['source_id'] = source_id + self.__parameters['id'] = id self.__parameters['schema'] = schema self.__parameters['output_file'] = output_file return output_file diff --git a/astroquery/esa/euclid/tests/test_euclidtap.py b/astroquery/esa/euclid/tests/test_euclidtap.py index 02397229ce..c35fdfffce 100644 --- a/astroquery/esa/euclid/tests/test_euclidtap.py +++ b/astroquery/esa/euclid/tests/test_euclidtap.py @@ -1135,7 +1135,7 @@ def test_get_spectrum(tmp_path_factory): tap = EuclidClass(tap_plus_conn_handler=conn_handler, datalink_handler=tap_plus, show_server_messages=False) - result = tap.get_spectrum(source_id='2417660845403252054', schema='sedm_sc8', output_file=None) + result = tap.get_spectrum(ids='2417660845403252054', schema='sedm_sc8', output_file=None) assert result is not None @@ -1149,10 +1149,30 @@ def test_get_spectrum(tmp_path_factory): fits_file = os.path.join(tmp_path_factory.mktemp("euclid_tmp"), 'my_fits_file.fits') - result = tap.get_spectrum(source_id='2417660845403252054', schema='sedm_sc8', output_file=fits_file) + result = tap.get_spectrum(ids='2417660845403252054', schema='sedm_sc8', output_file=fits_file) assert result is not None + remove_temp_dir() + + fits_file = os.path.join(tmp_path_factory.mktemp("euclid_tmp"), 'my_fits_file.fits') + + result = tap.get_spectrum(ids='2417660845403252054', schema='sedm_sc8', linking_parameter="SOURCE_ID", + output_file=fits_file) + + assert result is not None + + remove_temp_dir() + + fits_file = os.path.join(tmp_path_factory.mktemp("euclid_tmp"), 'my_fits_file.fits') + + result = tap.get_spectrum(ids='1499442653027920313123456789', schema='sedm_sc8', linking_parameter="SOURCEPATCH_ID", + output_file=fits_file) + + assert result is not None + + remove_temp_dir() + @patch.object(TapPlus, 'load_data') def test_get_spectrum_exceptions_2(mock_load_data, caplog): @@ -1169,7 +1189,7 @@ def test_get_spectrum_exceptions_2(mock_load_data, caplog): mock_load_data.side_effect = HTTPError("launch_job_async HTTPError") - tap.get_spectrum(source_id='2417660845403252054', schema='sedm_sc8', output_file=None) + tap.get_spectrum(ids='2417660845403252054', schema='sedm_sc8', output_file=None) mssg = ("Cannot retrieve spectrum for source_id 2417660845403252054, schema sedm_sc8. HTTP error: launch_job_async " "HTTPError") @@ -1177,7 +1197,7 @@ def test_get_spectrum_exceptions_2(mock_load_data, caplog): mock_load_data.side_effect = Exception("launch_job_async Exception") - tap.get_spectrum(source_id='2417660845403252054', schema='sedm_sc8', output_file=None) + tap.get_spectrum(ids='2417660845403252054', schema='sedm_sc8', output_file=None) mssg = "Cannot retrieve spectrum for source_id 2417660845403252054, schema sedm_sc8: launch_job_async Exception" assert caplog.records[1].msg == mssg @@ -1198,15 +1218,20 @@ def test_get_spectrum_exceptions(): # if source_id is None or schema is None: with pytest.raises(ValueError, match="Missing required argument"): - tap.get_spectrum(source_id=None, schema='sedm_sc8', output_file=None) + tap.get_spectrum(ids=None, schema='sedm_sc8', output_file=None) with pytest.raises(ValueError, match="Missing required argument"): - tap.get_spectrum(source_id='2417660845403252054', schema=None, output_file=None) + tap.get_spectrum(ids='2417660845403252054', schema=None, output_file=None) with pytest.raises(ValueError, match=( "Invalid argument value for 'retrieval_type'. Found hola, expected: 'ALL' or any of \\['SPECTRA_BGS', " "'SPECTRA_RGS'\\]")): - tap.get_spectrum(retrieval_type='hola', source_id='2417660845403252054', schema='schema', output_file=None) + tap.get_spectrum(retrieval_type='hola', ids='2417660845403252054', schema='schema', output_file=None) + + linking_parameter = 'NOT_VALID' + with pytest.raises(ValueError, match=f"^Invalid linking_parameter value '{linking_parameter}' .*"): + tap.get_spectrum(ids='2417660845403252054', schema='sedm_sc8', linking_parameter=linking_parameter, + output_file='fits_file') def test_get_scientific_data_product_list(): diff --git a/docs/esa/euclid/euclid.rst b/docs/esa/euclid/euclid.rst index 161073bf8f..d5216d5b30 100644 --- a/docs/esa/euclid/euclid.rst +++ b/docs/esa/euclid/euclid.rst @@ -379,7 +379,7 @@ Alternatively, the get_datalinks_ method can be used to find out if a given sour Download the spectra: >>> inp_source = str(res['source_id'][0]) # Note: the input of get_spectrum must be a string. ->>> dl_out = Euclid.get_spectrum(source_id=inp_source, retrieval_type = "SPECTRA_RGS", verbose = True) +>>> dl_out = Euclid.get_spectrum(ids=inp_source, retrieval_type = "SPECTRA_RGS", verbose = True) >>> print(f'Spectra downloaded and saved in: {dl_out}') Read the spectra and convert it to Astropy table: