Skip to content

Commit f9317a4

Browse files
committed
This new commit adds a new functionality to the TAP+. With this new
development now it is possible to rename the name of the user table and/or its columns
1 parent fa423aa commit f9317a4

File tree

6 files changed

+327
-35
lines changed

6 files changed

+327
-35
lines changed

astroquery/gaia/core.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,5 +900,25 @@ def launch_job_async(self, query, name=None, output_file=None,
900900
upload_table_name=upload_table_name,
901901
autorun=autorun)
902902

903+
def rename_table(self, table_name=None, new_table_name=None, new_column_names_dict={},
904+
verbose=False):
905+
"""
906+
This new method allows to update the column names of a user table.
907+
header example: rename_table(table_name=old_table_name, new_table_name=new_table_name -optional-
908+
, new_column_names_dict=[old_column1:new_column1, old_column2:new_colum2...])
909+
Parameters
910+
----------
911+
table_name: str, required
912+
old name of the user's table
913+
new_table_name: str, required
914+
new name of the user's table
915+
new_column_names_dict: dict str:str, required
916+
dict with pairs "old_column1_name:new_column1_name"
917+
verbose : bool, optional, default 'False'
918+
flag to display information about the process
919+
"""
920+
return TapPlus.rename_table(self, table_name=table_name, new_table_name=new_table_name,
921+
new_column_names_dict=new_column_names_dict, verbose=verbose)
922+
903923

904924
Gaia = GaiaClass()

astroquery/utils/tap/conn/tapconn.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,32 @@ 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+
417+
418+
419+
394420
def __execute_post(self, context, data,
395421
content_type=CONTENT_TYPE_POST_DEFAULT,
396422
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: 162 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
from astropy.table.table import Table
3333
import tempfile
3434

35-
3635
__all__ = ['Tap', 'TapPlus']
3736

3837
VERSION = "20200428.1"
@@ -571,7 +570,7 @@ def __launchJobMultipart(self, query, uploadResource, uploadTableName,
571570
"FORMAT": str(outputFormat),
572571
"tapclient": str(TAP_CLIENT_ID),
573572
"QUERY": str(query),
574-
"UPLOAD": ""+str(uploadValue)}
573+
"UPLOAD": "" + str(uploadValue)}
575574
if autorun is True:
576575
args['PHASE'] = 'RUN'
577576
if name is not None:
@@ -657,7 +656,7 @@ def __parseUrl(self, url, verbose=False):
657656
if urlInfoPos < 0:
658657
raise ValueError("Invalid URL format")
659658

660-
urlInfo = url[(urlInfoPos+3):]
659+
urlInfo = url[(urlInfoPos + 3):]
661660

662661
items = urlInfo.split("/")
663662

@@ -672,7 +671,7 @@ def __parseUrl(self, url, verbose=False):
672671
if portPos > 0:
673672
# port found
674673
host = hostPort[0:portPos]
675-
port = int(hostPort[portPos+1:])
674+
port = int(hostPort[portPos + 1:])
676675
else:
677676
# no port found
678677
host = hostPort
@@ -693,10 +692,10 @@ def __parseUrl(self, url, verbose=False):
693692
tapContext = f"/{items[2]}"
694693
else:
695694
data = []
696-
for i in range(1, itemsSize-1):
695+
for i in range(1, itemsSize - 1):
697696
data.append(f"/{items[i]}")
698697
serverContext = utils.util_create_string_from_buffer(data)
699-
tapContext = f"/{items[itemsSize-1]}"
698+
tapContext = f"/{items[itemsSize - 1]}"
700699
if verbose:
701700
print(f"protocol: '{protocol}'")
702701
print(f"host: '{host}'")
@@ -713,6 +712,7 @@ class TapPlus(Tap):
713712
"""TAP plus class
714713
Provides TAP and TAP+ capabilities
715714
"""
715+
716716
def __init__(self, url=None,
717717
host=None,
718718
server_context=None,
@@ -1532,7 +1532,7 @@ def __uploadTableMultipart(self, resource, table_name=None,
15321532
chunk = f.read()
15331533
files = [['FILE', os.path.basename(resource), chunk]]
15341534
contentType, body = connHandler.encode_multipart(args, files)
1535-
else: # upload from URL
1535+
else: # upload from URL
15361536
args = {
15371537
"TASKID": str(-1),
15381538
"TABLE_NAME": str(table_name),
@@ -1628,14 +1628,14 @@ def delete_user_table(self, table_name=None, force_removal=False,
16281628
raise ValueError("Table name cannot be null")
16291629
if force_removal is True:
16301630
args = {
1631-
"TABLE_NAME": str(table_name),
1632-
"DELETE": "TRUE",
1633-
"FORCE_REMOVAL": "TRUE"}
1631+
"TABLE_NAME": str(table_name),
1632+
"DELETE": "TRUE",
1633+
"FORCE_REMOVAL": "TRUE"}
16341634
else:
16351635
args = {
1636-
"TABLE_NAME": str(table_name),
1637-
"DELETE": "TRUE",
1638-
"FORCE_REMOVAL": "FALSE"}
1636+
"TABLE_NAME": str(table_name),
1637+
"DELETE": "TRUE",
1638+
"FORCE_REMOVAL": "FALSE"}
16391639
connHandler = self.__getconnhandler()
16401640
data = connHandler.url_encode(args)
16411641
response = connHandler.execute_upload(data, verbose=verbose)
@@ -1648,6 +1648,145 @@ def delete_user_table(self, table_name=None, force_removal=False,
16481648
msg = f"Table '{table_name}' deleted."
16491649
print(msg)
16501650

1651+
def rename_table(self, table_name=None, new_table_name=None, new_column_names_dict={},
1652+
verbose=False):
1653+
"""
1654+
This new method allows to update the column names of a user table.
1655+
header example: rename_table(table_name=old_table_name, new_table_name=new_table_name -optional-
1656+
, new_column_names_dict=[old_column1:new_column1, old_column2:new_colum2...])
1657+
Parameters
1658+
----------
1659+
table_name: str, required
1660+
old name of the user's table
1661+
new_table_name: str, required
1662+
new name of the user's table
1663+
new_column_names_dict: dict str:str, required
1664+
dict with pairs "old_column1_name:new_column1_name"
1665+
verbose : bool, optional, default 'False'
1666+
flag to display information about the process
1667+
"""
1668+
args = {}
1669+
1670+
if table_name is None:
1671+
raise ValueError("Table name cannot be null")
1672+
if (new_table_name is None or new_table_name == '') and \
1673+
(new_column_names_dict is None or not new_column_names_dict):
1674+
raise ValueError("Please introduce as minimum a new table tame or a new name for a column with format "
1675+
"old_column1_name:new_column1_name, ... ,old_columnN_name:new_columnN_name")
1676+
1677+
# Now we will check that the table exist
1678+
table = self.load_table(table=table_name, verbose=verbose)
1679+
1680+
# Check now if the table exist and contains values
1681+
if table is None:
1682+
raise ValueError("Table name not found")
1683+
columns = table.columns
1684+
if len(columns) == 0:
1685+
raise ValueError("Table has no columns")
1686+
1687+
if new_table_name is not None or new_table_name != '':
1688+
if new_column_names_dict is None or not new_column_names_dict:
1689+
# case 1: We only need to rename the table
1690+
args = self.get_args_4_rename_table_only_table_name(table_name, new_table_name)
1691+
else:
1692+
# case 2: We need to rename both, columns and column name
1693+
args = self.get_args_4_rename_table_all(table_name, new_table_name, new_column_names_dict)
1694+
# __end_if
1695+
# __end_if
1696+
1697+
if new_table_name is None or new_table_name == '':
1698+
if new_column_names_dict is not None or new_column_names_dict:
1699+
# case 3: We only need to rename the columns but same column name
1700+
args = self.get_args_4_rename_table_only_columns(table_name, new_column_names_dict)
1701+
1702+
connHandler = self.__getconnhandler()
1703+
data = connHandler.url_encode(args)
1704+
response = connHandler.execute_table_tool(data, verbose=verbose)
1705+
if verbose:
1706+
print(response.status, response.reason)
1707+
print(response.getheaders())
1708+
connHandler.check_launch_response_status(response,
1709+
verbose,
1710+
200)
1711+
msg = f"Table '{table_name}' updated."
1712+
print(msg)
1713+
1714+
# __end_of_rename_table
1715+
1716+
def get_args_4_rename_table_only_table_name(self, table_name, new_table_name):
1717+
1718+
args = {
1719+
"action": "rename",
1720+
"new_table_name": new_table_name,
1721+
"table_name": table_name
1722+
}
1723+
return args
1724+
1725+
# __end_of_rename_table_only_table_name
1726+
1727+
def get_args_4_rename_table_all(self, table_name, new_table_name, new_column_names_dict):
1728+
count = 0
1729+
new_column_names = ""
1730+
# check if the changes proposed for the columns are correct.
1731+
for key, value in new_column_names_dict.items():
1732+
if key == '':
1733+
raise ValueError(f" Old column name introduced "f"{key}"f" cannot be empty")
1734+
if key == value:
1735+
raise ValueError(f" Old column name introduced "f"{key}"f" and new column name introduced "
1736+
f" "f"{key}"f" cannot be the same")
1737+
if value == "":
1738+
raise ValueError(f" New column name introduced "f"{value}"f" cannot be empty")
1739+
1740+
# Converting dict into a string with the format expected by the TAP server
1741+
new_pair = key + ":" + value
1742+
new_column_names = new_column_names + new_pair
1743+
count = count + 1
1744+
if count < len(new_column_names_dict):
1745+
new_column_names = new_column_names + ','
1746+
# __end_for_loop
1747+
1748+
args = {
1749+
"action": "rename",
1750+
"new_column_names": new_column_names,
1751+
"new_table_name": new_table_name,
1752+
"table_name": table_name
1753+
}
1754+
return args
1755+
1756+
# __end_of_rename_table_all
1757+
1758+
def get_args_4_rename_table_only_columns(self, table_name, new_column_names_dict):
1759+
# check if the changes proposed for the columns are correct.
1760+
1761+
new_column_names = ""
1762+
1763+
count = 0
1764+
for key, value in new_column_names_dict.items():
1765+
if key == '':
1766+
raise ValueError(f" Old column name introduced "f"{key}"f" cannot be empty")
1767+
if key == value:
1768+
raise ValueError(f" Old column name introduced "f"{key}"f" and new column name introduced "
1769+
f" "f"{key}"f" cannot be the same")
1770+
if value == "":
1771+
raise ValueError(f" New column name introduced "f"{value}"f" cannot be empty")
1772+
1773+
# Converting dict into a string with the format expected by the TAP server
1774+
new_pair = key + ":" + value
1775+
new_column_names = new_column_names + new_pair
1776+
count = count + 1
1777+
if count < len(new_column_names_dict):
1778+
new_column_names = new_column_names + ','
1779+
# __end_for_loop
1780+
1781+
args = {
1782+
"action": "rename",
1783+
"new_column_names": new_column_names,
1784+
"table_name": table_name
1785+
}
1786+
return args
1787+
1788+
# __end_of_rename_table_only_columns
1789+
16511790
def update_user_table(self, table_name=None, list_of_changes=[],
16521791
verbose=False):
16531792
"""Updates a user table
@@ -1738,11 +1877,11 @@ def update_user_table(self, table_name=None, list_of_changes=[],
17381877
def get_table_update_arguments(table_name, columns, list_of_changes):
17391878
num_cols = len(columns)
17401879
args = {
1741-
"ACTION": "edit",
1742-
"NUMTABLES": str(1),
1743-
"TABLE0_NUMCOLS": str(num_cols),
1744-
"TABLE0": str(table_name),
1745-
}
1880+
"ACTION": "edit",
1881+
"NUMTABLES": str(1),
1882+
"TABLE0_NUMCOLS": str(num_cols),
1883+
"TABLE0": str(table_name),
1884+
}
17461885
index = 0
17471886
for column in columns:
17481887
found_in_changes = False
@@ -1890,11 +2029,11 @@ def set_ra_dec_columns(self, table_name=None,
18902029
raise ValueError("Dec column name cannot be null")
18912030

18922031
args = {
1893-
"ACTION": "radec",
1894-
"TABLE_NAME": str(table_name),
1895-
"RA": str(ra_column_name),
1896-
"DEC": str(dec_column_name),
1897-
}
2032+
"ACTION": "radec",
2033+
"TABLE_NAME": str(table_name),
2034+
"RA": str(ra_column_name),
2035+
"DEC": str(dec_column_name),
2036+
}
18982037
connHandler = self.__getconnhandler()
18992038
data = connHandler.url_encode(args)
19002039
response = connHandler.execute_table_edit(data, verbose=verbose)
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<vod:tableset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xmlns:vod="http://www.ivoa.net/xml/VODataService/v1.1"
3+
xmlns:esatapplus="http://esa.int/xml/EsaTapPlus"
4+
xsi:type="vod:TableSet"
5+
xsi:schemaLocation="http://www.ivoa.net/xml/VODataService/v1.1 http://www.ivoa.net/xml/VODataService/v1.1 http://esa.int/xml/EsaTapPlus https://gea.esac.esa.int/tap-server/xml/esaTapPlusAttributes.xsd">
6+
<schema esatapplus:public="false">
7+
<name>user_mhenar</name>
8+
<description/>
9+
<table type="base_table" esatapplus:size="3" esatapplus:flags="0">
10+
<name>user_mhenar.table1</name>
11+
<description>
12+
<![CDATA[ Uploaded from disk ]]>
13+
</description>
14+
<column std="false" esatapplus:flags="16" esatapplus:ref="">
15+
<name>table1_oid</name>
16+
<description>
17+
<![CDATA[ Object Identifier ]]>
18+
</description>
19+
<unit/>
20+
<ucd/>
21+
<utype/>
22+
<dataType xsi:type="vod:TAPType">INTEGER</dataType>
23+
<flag>indexed</flag>
24+
<flag>primary</flag>
25+
</column>
26+
<column std="false" esatapplus:flags="0" esatapplus:ref="">
27+
<name>source_id</name>
28+
<description/>
29+
<unit/>
30+
<ucd>None</ucd>
31+
<utype>None</utype>
32+
<dataType xsi:type="vod:TAPType">VARCHAR</dataType>
33+
</column>
34+
<column std="false" esatapplus:flags="0" esatapplus:ref="">
35+
<name>ra</name>
36+
<description/>
37+
<unit/>
38+
<ucd/>
39+
<utype/>
40+
<dataType xsi:type="vod:TAPType">DOUBLE</dataType>
41+
</column>
42+
<column std="false" esatapplus:flags="0" esatapplus:ref="">
43+
<name>dec</name>
44+
<description/>
45+
<unit/>
46+
<ucd/>
47+
<utype/>
48+
<dataType xsi:type="vod:TAPType">DOUBLE</dataType>
49+
</column>
50+
</table>
51+
</schema>
52+
</vod:tableset>

0 commit comments

Comments
 (0)