Skip to content

Commit 8ae63fb

Browse files
authored
Merge pull request #3118 from andamian/CADC-13741
Added support for temporary table upload in `alma`
2 parents 147546c + f41f708 commit 8ae63fb

File tree

5 files changed

+106
-26
lines changed

5 files changed

+106
-26
lines changed

CHANGES.rst

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ alma
2222

2323
- Added support for frequency_resolution in KHz [#3035]
2424

25+
- Added support for temporary upload tables in query_tap [#3118]
26+
2527
- Changed the way galactic ranges are used in queries [#3105]
2628

2729
ehst

astroquery/alma/core.py

+17-3
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@
138138
'Project': {
139139
'Project code': ['project_code', 'proposal_id', _gen_str_sql],
140140
'Project title': ['project_title', 'obs_title', _gen_str_sql],
141-
'PI name': ['pi_name', 'obs_creator_name', _gen_str_sql],
141+
'PI name': ['pi_name', 'pi_name', _gen_str_sql],
142142
'Proposal authors': ['proposal_authors', 'proposal_authors', _gen_str_sql],
143143
'Project abstract': ['project_abstract', 'proposal_abstract', _gen_str_sql],
144144
'Publication count': ['publication_count', 'NA', _gen_str_sql],
@@ -678,19 +678,33 @@ def query_sia(self, *, pos=None, band=None, time=None, pol=None,
678678

679679
query_sia.__doc__ = query_sia.__doc__.replace('_SIA2_PARAMETERS', SIA2_PARAMETERS_DESC)
680680

681-
def query_tap(self, query, maxrec=None):
681+
def query_tap(self, query, *, maxrec=None, uploads=None):
682682
"""
683683
Send query to the ALMA TAP. Results in pyvo.dal.TapResult format.
684684
result.table in Astropy table format
685685
686686
Parameters
687687
----------
688+
query : str
689+
ADQL query to execute
688690
maxrec : int
689691
maximum number of records to return
692+
uploads : dict
693+
a mapping from temporary table names to objects containing a votable. These
694+
temporary tables can be referred to in queries. The keys in the dictionary are
695+
the names of temporary tables which need to be prefixed with the TAP_UPLOAD
696+
schema in the actual query. The values are either astropy.table.Table instances
697+
or file names or file like handles such as io.StringIO to table definition in
698+
IVOA VOTable format.
699+
700+
Examples
701+
--------
702+
>>> uploads = {'tmptable': '/tmp/tmptable_def.xml'}
703+
>>> rslt = query_tap(self, query, maxrec=None, uploads=uploads)
690704
691705
"""
692706
log.debug('TAP query: {}'.format(query))
693-
return self.tap.search(query, language='ADQL', maxrec=maxrec)
707+
return self.tap.search(query, language='ADQL', maxrec=maxrec, uploads=uploads)
694708

695709
def help_tap(self):
696710
print('Table to query is "voa.ObsCore".')

astroquery/alma/tests/test_alma.py

+12-7
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ def test_query():
269269
"select * from ivoa.obscore WHERE "
270270
"(INTERSECTS(CIRCLE('ICRS',1.0,2.0,1.0), s_region) = 1) "
271271
"AND science_observation='T' AND data_rights='Public'",
272-
language='ADQL', maxrec=None)
272+
language='ADQL', maxrec=None, uploads=None)
273273

274274
# one row result
275275
tap_mock = Mock()
@@ -291,7 +291,7 @@ def test_query():
291291
"(INTERSECTS(CIRCLE('ICRS',1.0,2.0,0.16666666666666666), s_region) = 1) "
292292
"AND band_list LIKE '%3%' AND science_observation='T' AND "
293293
"data_rights='Proprietary'",
294-
language='ADQL', maxrec=None)
294+
language='ADQL', maxrec=None, uploads=None)
295295

296296
# repeat for legacy columns
297297
mock_result = Mock()
@@ -313,7 +313,7 @@ def test_query():
313313
"(INTERSECTS(CIRCLE('ICRS',1.0,2.0,0.16666666666666666), s_region) = 1) "
314314
"AND band_list LIKE '%3%' AND science_observation='T' AND "
315315
"data_rights='Proprietary'",
316-
language='ADQL', maxrec=None)
316+
language='ADQL', maxrec=None, uploads=None)
317317
row_legacy = result_legacy[0]
318318
row = result[0]
319319
for item in _OBSCORE_TO_ALMARESULT.items():
@@ -347,7 +347,7 @@ def test_query():
347347
"(band_list LIKE '%1%' OR band_list LIKE '%3%') AND "
348348
"t_min=55197.0 AND pol_states='/XX/YY/' AND s_fov=0.012313 AND "
349349
"t_exptime=25 AND science_observation='F'",
350-
language='ADQL', maxrec=None
350+
language='ADQL', maxrec=None, uploads=None
351351
)
352352

353353
tap_mock.reset()
@@ -361,7 +361,7 @@ def test_query():
361361
"AND spectral_resolution=2000000 "
362362
"AND (INTERSECTS(CIRCLE('ICRS',1.0,2.0,1.0), "
363363
"s_region) = 1) AND science_observation='T' AND data_rights='Public'",
364-
language='ADQL', maxrec=None)
364+
language='ADQL', maxrec=None, uploads=None)
365365

366366

367367
@pytest.mark.filterwarnings("ignore::astropy.utils.exceptions.AstropyUserWarning")
@@ -499,9 +499,14 @@ def test_tap():
499499
alma._tap = tap_mock
500500
result = alma.query_tap('select * from ivoa.ObsCore')
501501
assert len(result.table) == 0
502-
503502
tap_mock.search.assert_called_once_with('select * from ivoa.ObsCore',
504-
language='ADQL', maxrec=None)
503+
language='ADQL', maxrec=None, uploads=None)
504+
505+
tap_mock.search.reset_mock()
506+
result = alma.query_tap('select * from ivoa.ObsCore', maxrec=10, uploads={'tmptable': 'votable_file.xml'})
507+
assert len(result.table) == 0
508+
tap_mock.search.assert_called_once_with(
509+
'select * from ivoa.ObsCore', language='ADQL', maxrec=10, uploads={'tmptable': 'votable_file.xml'})
505510

506511

507512
@pytest.mark.parametrize('data_archive_url',

astroquery/alma/tests/test_alma_remote.py

+42-12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Licensed under a 3-clause BSD style license - see LICENSE.rst
22
from datetime import datetime, timezone
33
import os
4+
from io import StringIO
45
from pathlib import Path
56
from urllib.parse import urlparse
67
import re
@@ -23,7 +24,6 @@
2324
except ImportError:
2425
HAS_REGIONS = False
2526

26-
# ALMA tests involving staging take too long, leading to travis timeouts
2727
# TODO: make this a configuration item
2828
SKIP_SLOW = True
2929

@@ -91,14 +91,15 @@ def test_SgrAstar(self, tmp_path, alma):
9191

9292
assert '2013.1.00857.S' in result_s['Project code']
9393

94+
@pytest.mark.skipif("SKIP_SLOW")
9495
def test_freq(self, alma):
9596
payload = {'frequency': '85..86'}
9697
result = alma.query(payload)
9798
assert len(result) > 0
9899
for row in result:
99100
# returned em_min and em_max are in m
100101
assert row['frequency'] >= 85
101-
assert row['frequency'] <= 100
102+
assert row['frequency'] <= 86
102103
assert '3' in row['band_list']
103104

104105
def test_bands(self, alma):
@@ -216,10 +217,7 @@ def test_data_info(self, tmp_path, alma):
216217
trimmed_access_url_list = [e for e in data_info_tar['access_url'].data if len(e) > 0]
217218
trimmed_access_urls = (trimmed_access_url_list,)
218219
mock_calls = download_files_mock.mock_calls[0][1]
219-
print(f"\n\nComparing {mock_calls} to {trimmed_access_urls}\n\n")
220-
# comparison = download_files_mock.mock_calls[0][1] == data_info_tar['access_url']
221220
assert mock_calls == trimmed_access_urls
222-
# assert comparison.all()
223221

224222
def test_download_data(self, tmp_path, alma):
225223
# test only fits files from a program
@@ -239,9 +237,8 @@ def test_download_data(self, tmp_path, alma):
239237
alma._download_file.call_count == len(results)
240238
assert len(results) == len(urls)
241239

240+
@pytest.mark.skipif("SKIP_SLOW")
242241
def test_download_and_extract(self, tmp_path, alma):
243-
# TODO: slowish, runs for ~90s
244-
245242
alma.cache_location = tmp_path
246243
alma._cycle0_tarfile_content_table = {'ID': ''}
247244

@@ -345,16 +342,18 @@ def test_misc(self, alma):
345342

346343
result = alma.query_object('M83', public=True, science=True)
347344
assert len(result) > 0
348-
result = alma.query(payload={'pi_name': '*Bally*'}, public=False,
349-
maxrec=10)
345+
with pytest.warns(expected_warning=DALOverflowWarning,
346+
match="Partial result set. Potential causes MAXREC, async storage space, etc."):
347+
result = alma.query(payload={'pi_name': 'Bally*'}, public=True,
348+
maxrec=10)
350349
assert result
351350
# Add overwrite=True in case the test previously died unexpectedly
352351
# and left the temp file.
353352
result.write('/tmp/alma-onerow.txt', format='ascii', overwrite=True)
354353
for row in result:
355-
assert 'Bally' in row['obs_creator_name']
354+
assert 'Bally' in row['pi_name']
356355
result = alma.query(payload=dict(project_code='2016.1.00165.S'),
357-
public=False)
356+
public=True)
358357
assert result
359358
for row in result:
360359
assert '2016.1.00165.S' == row['proposal_id']
@@ -398,8 +397,9 @@ def test_misc(self, alma):
398397
assert result
399398
for row in result:
400399
assert '6' == row['band_list']
401-
assert 'ginsburg' in row['obs_creator_name'].lower()
400+
assert 'ginsburg' in row['pi_name'].lower()
402401

402+
@pytest.mark.skip("Not sure what this is supposed to do")
403403
def test_user(self, alma):
404404
# miscellaneous set of tests from current users
405405
rslt = alma.query({'band_list': [6], 'project_code': '2012.1.*'},
@@ -561,6 +561,36 @@ def test_big_download_regression(alma):
561561
alma.download_files([files['access_url'][3]])
562562

563563

564+
@pytest.mark.remote_data
565+
def test_tap_upload():
566+
tmp_table = StringIO('''<?xml version="1.0" encoding="UTF-8"?>
567+
<VOTABLE xmlns="http://www.ivoa.net/xml/VOTable/v1.3"
568+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.3">
569+
<RESOURCE>
570+
<TABLE>
571+
<FIELD name="prop_id" datatype="char" arraysize="*">
572+
<DESCRIPTION>external URI for the physical artifact</DESCRIPTION>
573+
</FIELD>
574+
<DATA>
575+
<TABLEDATA>
576+
<TR>
577+
<TD>2013.1.01365.S</TD>
578+
</TR>
579+
</TABLEDATA>
580+
</DATA>
581+
</TABLE>
582+
</RESOURCE>
583+
</VOTABLE>''')
584+
585+
alma = Alma()
586+
res = alma.query_tap(
587+
'select top 3 proposal_id from ivoa.ObsCore oc join TAP_UPLOAD.proj_codes pc on oc.proposal_id=pc.prop_id',
588+
uploads={'proj_codes': tmp_table})
589+
assert len(res) == 3
590+
for row in res:
591+
assert row['proposal_id'] == '2013.1.01365.S'
592+
593+
564594
@pytest.mark.remote_data
565595
def test_download_html_file(alma, tmp_path):
566596
alma.cache_location = tmp_path

docs/alma/alma.rst

+33-4
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ or if you wanted all projects by a given PI:
178178
179179
>>> Alma.query(payload=dict(pi_name='Ginsburg, Adam'))
180180
181-
The ''query_sia'' method offers another way to query ALMA using the IVOA SIA
181+
The ``query_sia`` method offers another way to query ALMA using the IVOA SIA
182182
subset of keywords returning results in 'ObsCore' format. For example,
183183
to query for all images that have ``'XX'`` polarization (note that this query is too large
184184
to run, it is just shown as an example):
@@ -187,9 +187,9 @@ to run, it is just shown as an example):
187187
188188
>>> Alma.query_sia(pol='XX') # doctest: +SKIP
189189
190-
Finally, the ''query_tap'' method is the most general way of querying the ALMA
190+
Finally, the ``query_tap`` method is the most general way of querying the ALMA
191191
metadata. This method is used to send queries to the service using the
192-
'ObsCore' columns as constraints. The returned result is also in 'ObsCore'
192+
``ObsCore`` columns as constraints. The returned result is also in ``ObsCore``
193193
format.
194194
195195
.. doctest-remote-data::
@@ -210,8 +210,37 @@ One can also query by keyword, spatial resolution, etc:
210210
... "in ('Disks around high-mass stars', 'Asymptotic Giant Branch (AGB) stars') "
211211
... "AND science_observation='T'") # doctest: +IGNORE_OUTPUT
212212
213+
``query_tap`` also supports uploading temporary tables that can be used to join to in queries.
214+
These temporary tables can be defined as ''astropy.table.Table'' instances or references to file names or
215+
file like handles (`~io.StringIO` instances for example) of table definitions in IVOA VOTable format.
216+
Below is a very simple example of using `~astropy.table.Table` temporary table with the ``proj_codes`` name.
217+
Note that the table name must always be prefixed with the ``TAP_UPLOAD`` schema when referenced in queries.
213218
214-
Use the ``help_tap`` method to learn about the ALMA 'ObsCore' keywords and
219+
.. doctest-remote-data::
220+
221+
>>> from astropy.table import Table
222+
>>> tmp_table = Table([['2013.1.01365.S', '2013.A.00014.S']], names=['prop_id'], dtype=['S'])
223+
>>> Alma.query_tap('select distinct target_name from ivoa.ObsCore oc join TAP_UPLOAD.proj_codes pc on oc.proposal_id=pc.prop_id order by target_name',
224+
... uploads={'proj_codes': tmp_table})
225+
<DALResultsTable length=13>
226+
target_name
227+
str256
228+
------------
229+
Ceres
230+
J0042-4030
231+
J0334-4008
232+
J1733-130
233+
J1751+0939
234+
J1751+096
235+
J1851+0035
236+
J1924-2914
237+
Neptune
238+
SGP-UR-54092
239+
Titan
240+
Uranus
241+
W43-MM1
242+
243+
Use the ``help_tap`` method to learn about the ALMA ``ObsCore`` keywords and
215244
their types.
216245
217246
.. doctest-remote-data::

0 commit comments

Comments
 (0)