Skip to content

Commit 6834ef9

Browse files
authored
Merge pull request #2077 from esdc-esac-esa-int/gaia-astroquery-1.0
Gaia astroquery 1.0
2 parents 15fab51 + c353e00 commit 6834ef9

File tree

16 files changed

+685
-271
lines changed

16 files changed

+685
-271
lines changed

CHANGES.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ gaia
3030
``astroquery.gaia.Gaia`` no longer ignore their ``columns`` argument when
3131
``radius`` is specified. [#2249]
3232

33+
- Enhanced methods 'launch_job' and 'launch_job_async' to avoid issues with
34+
the name provided by the user for the output file when the results are
35+
returned by the TAP in compressed format. [#2077]
36+
3337
mast
3438
^^^^
3539

@@ -48,6 +52,7 @@ sdss
4852
- Fix ``query_crossid`` to be able to query larger list of coordinates. [#2305]
4953

5054

55+
5156
Infrastructure, Utility and Other Changes and Additions
5257
-------------------------------------------------------
5358

@@ -57,10 +62,17 @@ Infrastructure, Utility and Other Changes and Additions
5762
- Callback hooks are deleted before caching. Potentially all cached queries
5863
prior to this PR will be rendered invalid. [#2295]
5964

65+
utils.tap
66+
^^^^^^^^^
67+
6068
- The modules that make use of the ``astroquery.utils.tap.model.job.Job`` class
6169
(e.g. Gaia) no longer print messages about where the results of async queries
6270
were written if the ``verbose`` setting is ``False``. [#2299]
6371

72+
- New method, ``rename_table``, which allows the user to rename table and
73+
column names. [#2077]
74+
75+
6476

6577
0.4.5 (2021-12-24)
6678
==================

astroquery/gaia/core.py

Lines changed: 99 additions & 35 deletions
Large diffs are not rendered by default.
728 Bytes
Binary file not shown.

astroquery/gaia/tests/test_gaiatap.py

Lines changed: 187 additions & 152 deletions
Large diffs are not rendered by default.

astroquery/utils/tap/conn/tapconn.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import mimetypes
2525
import time
2626

27-
from urllib.parse import urlencode
27+
from urllib.parse import urlencode
2828

2929
from astroquery.utils.tap.xmlparser import utils
3030
from astroquery.utils.tap import taputils
@@ -87,7 +87,7 @@ def __init__(self, ishttps,
8787
self.__connPort = port
8888
self.__connPortSsl = sslport
8989
if server_context is not None:
90-
if(server_context.startswith("/")):
90+
if server_context.startswith("/"):
9191
self.__serverContext = server_context
9292
else:
9393
self.__serverContext = f"/{server_context}"
@@ -106,8 +106,8 @@ def __init__(self, ishttps,
106106
self.__connectionHandler = connhandler
107107

108108
def __create_context(self, context):
109-
if (context is not None and context != ""):
110-
if(str(context).startswith("/")):
109+
if context is not None and context != "":
110+
if str(context).startswith("/"):
111111
return f"{self.__serverContext}{context}"
112112
else:
113113
return f"{self.__serverContext}/{context}"
@@ -391,6 +391,29 @@ def execute_table_edit(self, data,
391391
context = self.__get_table_edit_context()
392392
return self.__execute_post(context, data, content_type, verbose)
393393

394+
def execute_table_tool(self, data,
395+
content_type=CONTENT_TYPE_POST_DEFAULT,
396+
verbose=False):
397+
"""Executes a POST upload request
398+
The connection is done through HTTP or HTTPS depending on the login
399+
status (logged in -> HTTPS)
400+
401+
Parameters
402+
----------
403+
data : str, mandatory
404+
POST data
405+
content_type: str, optional, default: application/x-www-form-urlencoded
406+
HTTP(s) content-type header value
407+
verbose : bool, optional, default 'False'
408+
flag to display information about the process
409+
410+
Returns
411+
-------
412+
An HTTP(s) response object
413+
"""
414+
context = self.__get_table_edit_context()
415+
return self.__execute_post(context, data, content_type, verbose)
416+
394417
def __execute_post(self, context, data,
395418
content_type=CONTENT_TYPE_POST_DEFAULT,
396419
verbose=False):

astroquery/utils/tap/conn/tests/DummyConnHandler.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,9 @@ def execute_table_edit(self, data,
170170
verbose=False):
171171
return self.__execute_post(subcontext="tableEdit", data=data,
172172
content_type=content_type, verbose=verbose)
173+
174+
def execute_table_tool(self, data,
175+
content_type="application/x-www-form-urlencoded",
176+
verbose=False):
177+
return self.__execute_post(subcontext="TableTool", data=data,
178+
content_type=content_type, verbose=verbose)

astroquery/utils/tap/core.py

Lines changed: 111 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
European Space Agency (ESA)
1212
1313
Created on 30 jun. 2016
14-
15-
14+
Modified on 1 jun. 2021 by mhsarmiento
1615
"""
1716
from astroquery.utils.tap import taputils
1817
from astroquery.utils.tap.conn.tapconn import TapConn
@@ -32,7 +31,6 @@
3231
from astropy.table.table import Table
3332
import tempfile
3433

35-
3634
__all__ = ['Tap', 'TapPlus']
3735

3836
VERSION = "20200428.1"
@@ -246,6 +244,8 @@ def launch_job(self, query, name=None, output_file=None,
246244
----------
247245
query : str, mandatory
248246
query to be executed
247+
name : str, optional, default None
248+
custom name defined by the user for the job that is going to be created
249249
output_file : str, optional, default None
250250
file name where the results are saved if dumpToFile is True.
251251
If this parameter is not provided, the jobid is used instead
@@ -358,6 +358,8 @@ def launch_job_async(self, query, name=None, output_file=None,
358358
----------
359359
query : str, mandatory
360360
query to be executed
361+
name : str, optional, default None
362+
custom name defined by the user for the job that is going to be created
361363
output_file : str, optional, default None
362364
file name where the results are saved if dumpToFile is True.
363365
If this parameter is not provided, the jobid is used instead
@@ -571,7 +573,7 @@ def __launchJobMultipart(self, query, uploadResource, uploadTableName,
571573
"FORMAT": str(outputFormat),
572574
"tapclient": str(TAP_CLIENT_ID),
573575
"QUERY": str(query),
574-
"UPLOAD": ""+str(uploadValue)}
576+
"UPLOAD": "" + str(uploadValue)}
575577
if autorun is True:
576578
args['PHASE'] = 'RUN'
577579
if name is not None:
@@ -657,7 +659,7 @@ def __parseUrl(self, url, verbose=False):
657659
if urlInfoPos < 0:
658660
raise ValueError("Invalid URL format")
659661

660-
urlInfo = url[(urlInfoPos+3):]
662+
urlInfo = url[(urlInfoPos + 3):]
661663

662664
items = urlInfo.split("/")
663665

@@ -672,7 +674,7 @@ def __parseUrl(self, url, verbose=False):
672674
if portPos > 0:
673675
# port found
674676
host = hostPort[0:portPos]
675-
port = int(hostPort[portPos+1:])
677+
port = int(hostPort[portPos + 1:])
676678
else:
677679
# no port found
678680
host = hostPort
@@ -693,10 +695,10 @@ def __parseUrl(self, url, verbose=False):
693695
tapContext = f"/{items[2]}"
694696
else:
695697
data = []
696-
for i in range(1, itemsSize-1):
698+
for i in range(1, itemsSize - 1):
697699
data.append(f"/{items[i]}")
698700
serverContext = utils.util_create_string_from_buffer(data)
699-
tapContext = f"/{items[itemsSize-1]}"
701+
tapContext = f"/{items[itemsSize - 1]}"
700702
if verbose:
701703
print(f"protocol: '{protocol}'")
702704
print(f"host: '{host}'")
@@ -713,6 +715,7 @@ class TapPlus(Tap):
713715
"""TAP plus class
714716
Provides TAP and TAP+ capabilities
715717
"""
718+
716719
def __init__(self, url=None,
717720
host=None,
718721
server_context=None,
@@ -1245,7 +1248,7 @@ def get_datalinks(self, ids, verbose=False):
12451248
print("Retrieving datalink.")
12461249
if ids is None:
12471250
raise ValueError("Missing mandatory argument 'ids'")
1248-
if isinstance(ids, str):
1251+
if isinstance(ids, str):
12491252
ids_arg = f"ID={ids}"
12501253
else:
12511254
if isinstance(ids, int):
@@ -1368,7 +1371,7 @@ def login(self, user=None, password=None, credentials_file=None,
13681371
user = ins.readline().strip()
13691372
password = ins.readline().strip()
13701373
if user is None:
1371-
user = input("User: ")
1374+
user = input("User: ")
13721375
if user is None:
13731376
print("Invalid user name")
13741377
return
@@ -1532,7 +1535,7 @@ def __uploadTableMultipart(self, resource, table_name=None,
15321535
chunk = f.read()
15331536
files = [['FILE', os.path.basename(resource), chunk]]
15341537
contentType, body = connHandler.encode_multipart(args, files)
1535-
else: # upload from URL
1538+
else: # upload from URL
15361539
args = {
15371540
"TASKID": str(-1),
15381541
"TABLE_NAME": str(table_name),
@@ -1628,14 +1631,14 @@ def delete_user_table(self, table_name=None, force_removal=False,
16281631
raise ValueError("Table name cannot be null")
16291632
if force_removal is True:
16301633
args = {
1631-
"TABLE_NAME": str(table_name),
1632-
"DELETE": "TRUE",
1633-
"FORCE_REMOVAL": "TRUE"}
1634+
"TABLE_NAME": str(table_name),
1635+
"DELETE": "TRUE",
1636+
"FORCE_REMOVAL": "TRUE"}
16341637
else:
16351638
args = {
1636-
"TABLE_NAME": str(table_name),
1637-
"DELETE": "TRUE",
1638-
"FORCE_REMOVAL": "FALSE"}
1639+
"TABLE_NAME": str(table_name),
1640+
"DELETE": "TRUE",
1641+
"FORCE_REMOVAL": "FALSE"}
16391642
connHandler = self.__getconnhandler()
16401643
data = connHandler.url_encode(args)
16411644
response = connHandler.execute_upload(data, verbose=verbose)
@@ -1648,15 +1651,90 @@ def delete_user_table(self, table_name=None, force_removal=False,
16481651
msg = f"Table '{table_name}' deleted."
16491652
print(msg)
16501653

1654+
def rename_table(self, table_name=None, new_table_name=None, new_column_names_dict=None,
1655+
verbose=False):
1656+
""" This method allows you to update the column names of a user table.
1657+
1658+
Parameters
1659+
----------
1660+
table_name: str, required
1661+
old name of the user's table
1662+
new_table_name: str, required
1663+
new name of the user's table
1664+
new_column_names_dict: dict str:str
1665+
dict with pairs "old_column1_name:new_column1_name"
1666+
verbose : bool, optional, default 'False'
1667+
flag to display information about the process
1668+
1669+
"""
1670+
if new_column_names_dict is None:
1671+
new_column_names_dict = {}
1672+
args = {}
1673+
1674+
if table_name is None:
1675+
raise ValueError(
1676+
"Argument 'table_name' is mandatory. "
1677+
"Please introduce the name of the table that is going to be updated")
1678+
if (new_table_name is None) and (new_column_names_dict is None):
1679+
raise ValueError("Please introduce as minimum a new name for the table or a new name for a column with "
1680+
"format old_column1_name:new_column1_name, ... ,old_columnN_name:new_columnN_name")
1681+
if new_table_name is not None:
1682+
if new_column_names_dict is None:
1683+
# case 1: We only need to rename the table
1684+
args = self.get_args_4_rename_table(table_name, new_table_name)
1685+
else:
1686+
# case 2: We need to rename both, table and column names
1687+
args = self.get_args_4_rename_table(table_name, new_table_name, new_column_names_dict)
1688+
1689+
if new_table_name is None:
1690+
if new_column_names_dict:
1691+
# case 3: We only need to rename the columns but same table name
1692+
args = self.get_args_4_rename_table(table_name, table_name, new_column_names_dict)
1693+
1694+
connHandler = self.__getconnhandler()
1695+
data = connHandler.url_encode(args)
1696+
response = connHandler.execute_table_tool(data, verbose=verbose)
1697+
1698+
if verbose:
1699+
print(response.status, response.reason)
1700+
print(response.getheaders())
1701+
connHandler.check_launch_response_status(response,
1702+
verbose,
1703+
200)
1704+
if verbose:
1705+
msg = f"Table '{table_name}' updated."
1706+
print(msg)
1707+
1708+
def get_args_4_rename_table(self, table_name, new_table_name, new_column_names_dict):
1709+
1710+
args = {}
1711+
1712+
if not new_column_names_dict:
1713+
args = {
1714+
"action": "rename",
1715+
"new_table_name": new_table_name,
1716+
"table_name": table_name
1717+
}
1718+
else:
1719+
new_column_names = ','.join(f'{key}:{value}' for key, value in new_column_names_dict.items())
1720+
1721+
args = {
1722+
"action": "rename",
1723+
"new_column_names": new_column_names,
1724+
"new_table_name": new_table_name,
1725+
"table_name": table_name
1726+
}
1727+
return args
1728+
16511729
def update_user_table(self, table_name=None, list_of_changes=[],
16521730
verbose=False):
16531731
"""Updates a user table
16541732
16551733
Parameters
16561734
----------
1657-
table_name : str, required
1735+
table_name : str
16581736
table to be updated
1659-
list_of_changes : list, required
1737+
list_of_changes : list
16601738
list of lists, each one of them containing sets of
16611739
[column_name, field_name, value].
16621740
column_name is the name of the column to be updated
@@ -1738,11 +1816,11 @@ def update_user_table(self, table_name=None, list_of_changes=[],
17381816
def get_table_update_arguments(table_name, columns, list_of_changes):
17391817
num_cols = len(columns)
17401818
args = {
1741-
"ACTION": "edit",
1742-
"NUMTABLES": str(1),
1743-
"TABLE0_NUMCOLS": str(num_cols),
1744-
"TABLE0": str(table_name),
1745-
}
1819+
"ACTION": "edit",
1820+
"NUMTABLES": str(1),
1821+
"TABLE0_NUMCOLS": str(num_cols),
1822+
"TABLE0": str(table_name),
1823+
}
17461824
index = 0
17471825
for column in columns:
17481826
found_in_changes = False
@@ -1872,11 +1950,11 @@ def set_ra_dec_columns(self, table_name=None,
18721950
18731951
Parameters
18741952
----------
1875-
table_name : str, required
1953+
table_name : str
18761954
table to be set
1877-
ra_column_name : str, required
1955+
ra_column_name : str
18781956
ra column to be set
1879-
dec_column_name : str, required
1957+
dec_column_name : str
18801958
dec column to be set
18811959
verbose : bool, optional, default 'False'
18821960
flag to display information about the process
@@ -1890,11 +1968,11 @@ def set_ra_dec_columns(self, table_name=None,
18901968
raise ValueError("Dec column name cannot be null")
18911969

18921970
args = {
1893-
"ACTION": "radec",
1894-
"TABLE_NAME": str(table_name),
1895-
"RA": str(ra_column_name),
1896-
"DEC": str(dec_column_name),
1897-
}
1971+
"ACTION": "radec",
1972+
"TABLE_NAME": str(table_name),
1973+
"RA": str(ra_column_name),
1974+
"DEC": str(dec_column_name),
1975+
}
18981976
connHandler = self.__getconnhandler()
18991977
data = connHandler.url_encode(args)
19001978
response = connHandler.execute_table_edit(data, verbose=verbose)
@@ -1933,7 +2011,7 @@ def login(self, user=None, password=None, credentials_file=None,
19332011
user = ins.readline().strip()
19342012
password = ins.readline().strip()
19352013
if user is None:
1936-
user = input("User: ")
2014+
user = input("User: ")
19372015
if user is None:
19382016
log.info("Invalid user name")
19392017
return

0 commit comments

Comments
 (0)