Skip to content

Commit f66e357

Browse files
andamianAdrian Damian
andauthored
Support for --head and cutouts
Co-authored-by: Adrian Damian <[email protected]>
1 parent 9a68465 commit f66e357

File tree

3 files changed

+62
-41
lines changed

3 files changed

+62
-41
lines changed

vos/vos/tests/test_config.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@
6565
#
6666
# ***********************************************************************
6767
#
68-
import unittest
6968
# Test the vosconfig functionality
7069
from unittest.mock import Mock, patch
7170
import warnings

vos/vos/tests/test_vos.py

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# ****************** CANADIAN ASTRONOMY DATA CENTRE *******************
33
# ************* CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES **************
44
#
5-
# (c) 2023. (c) 2023.
5+
# (c) 2024. (c) 2024.
66
# Government of Canada Gouvernement du Canada
77
# National Research Council Conseil national de recherches
88
# Ottawa, Canada, K1A 0R6 Ottawa, Canada, K1A 0R6
@@ -70,6 +70,8 @@
7070

7171
import os
7272
import unittest
73+
import urllib.parse
74+
7375
import pytest
7476
import requests
7577
from xml.etree import ElementTree
@@ -142,18 +144,38 @@ def test_get_node_url():
142144
assert ('order=desc' == unquote(equery))
143145

144146
# test header view
145-
transfer_url = 'https://some.location/some/headers'
146-
response.headers = {'Location': transfer_url}
147-
client._endpoints[resource_id].conn.ws_client._session.get = \
148-
Mock(return_value=response)
149-
assert transfer_url == \
147+
transfer_url = 'https://mystorage.org/minoc/files/abc:VOS/002'
148+
client.transfer = Mock(return_value=[transfer_url])
149+
expected_url = transfer_url + '?META=true'
150+
assert expected_url == \
150151
client.get_node_url('vos://cadc.nrc.ca!vospace/auser',
151152
view='header')[0]
152-
# get the argument lists for client.conn.session.get
153-
args, kwargs = client._endpoints[
154-
resource_id].conn.ws_client._session.get.call_args_list[0]
155-
# check head is amongst the other parameters
156-
assert kwargs['params']['view'] == 'header'
153+
154+
# test pixel cutouts
155+
transfer_url1 = 'https://mystorage.org/minoc/files/abc:VOS/001'
156+
transfer_url2 = 'https://myotherstorage.org/minoc/files/abc:VOS/001'
157+
client.transfer = Mock(return_value=[transfer_url1, transfer_url2])
158+
pcutout = '[1][100:125,100:175]'
159+
expected_url1 = transfer_url1 + '?SUB=' + pcutout
160+
expected_url2 = transfer_url2 + '?SUB=' + pcutout
161+
assert expected_url1 == \
162+
client.get_node_url('vos://cadc.nrc.ca!vospace/auser',
163+
cutout=pcutout, view='cutout')[0]
164+
assert expected_url2 == \
165+
client.get_node_url('vos://cadc.nrc.ca!vospace/auser',
166+
cutout=pcutout, view='cutout')[1]
167+
168+
# test sky coordinates
169+
client.transfer = Mock(return_value=[transfer_url1, transfer_url2])
170+
scutout = 'CIRCLE=' + urllib.parse.quote('(1.1 2.2 3.3')
171+
expected_url1 = transfer_url1 + '?' + scutout
172+
expected_url2 = transfer_url2 + '?' + scutout
173+
assert expected_url1 == \
174+
client.get_node_url('vos://cadc.nrc.ca!vospace/auser',
175+
cutout=scutout, view='cutout')[0]
176+
assert expected_url2 == \
177+
client.get_node_url('vos://cadc.nrc.ca!vospace/auser',
178+
cutout=scutout, view='cutout')[1]
157179

158180

159181
class TestClient(unittest.TestCase):

vos/vos/vos.py

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1770,10 +1770,11 @@ def copy(self, source, destination, send_md5=False, disposition=False,
17701770
if cutout_match.group('pix'):
17711771
cutout = cutout_match.group('pix')
17721772
elif cutout_match.group('wcs') is not None:
1773-
cutout = "CIRCLE ICRS {} {} {}".format(
1773+
from urllib.parse import quote
1774+
cutout = 'CIRCLE=' + quote('{} {} {}'.format(
17741775
cutout_match.group('ra'),
17751776
cutout_match.group('dec'),
1776-
cutout_match.group('rad'))
1777+
cutout_match.group('rad')))
17771778
else:
17781779
raise ValueError("Bad source name: {}".format(source))
17791780
source = cutout_match.group('filename')
@@ -2249,31 +2250,9 @@ def get_node_url(self, uri, method='GET', view=None, limit=None,
22492250
uri = self.fix_uri(uri)
22502251

22512252
if sort is not None and not isinstance(sort, SortNodeProperty):
2252-
raise TypeError('sort must be an instace of vos.NodeProperty Enum')
2253+
raise TypeError('sort must be an instance of vos.NodeProperty Enum')
22532254
if order not in [None, 'asc', 'desc']:
22542255
raise ValueError('order must be either "asc" or "desc"')
2255-
if view in ['data', 'cutout'] and method == 'GET':
2256-
node = self.get_node(uri, limit=0)
2257-
if node.islink():
2258-
target = node.node.findtext(Node.TARGET)
2259-
logger.debug('{} is a link to {}'.format(node.uri, target))
2260-
if target is None:
2261-
raise OSError(errno.ENOENT, "No target for link")
2262-
parts = urlparse(target)
2263-
if parts.scheme != "vos":
2264-
# This is not a link to another VOSpace node so lets just
2265-
# return the target as the url
2266-
url = target
2267-
if cutout is not None:
2268-
url = "{0}?cutout={1}".format(target, cutout)
2269-
logger.debug("Line 3.1.2")
2270-
logger.debug("Returning URL: {0}".format(url))
2271-
return [url]
2272-
logger.debug("Getting URLs for: {0}".format(target))
2273-
return self.get_node_url(target, method=method, view=view,
2274-
limit=limit, next_uri=next_uri,
2275-
cutout=cutout, sort=sort, order=order,
2276-
full_negotiation=full_negotiation)
22772256

22782257
logger.debug("Getting URL for: " + str(uri))
22792258

@@ -2290,7 +2269,7 @@ def get_node_url(self, uri, method='GET', view=None, limit=None,
22902269
else:
22912270
do_shortcut = self.transfer_shortcut
22922271

2293-
if not do_shortcut and method == 'GET' and view in ['data', 'cutout']:
2272+
if not do_shortcut and method == 'GET' and view in ['data', 'cutout', 'header']:
22942273
return self._get(uri, view=view, cutout=cutout)
22952274

22962275
if not do_shortcut and method == 'PUT':
@@ -2319,7 +2298,7 @@ def get_node_url(self, uri, method='GET', view=None, limit=None,
23192298
fields['uri'] = next_uri
23202299

23212300
tmp_url = '{}/{}'.format(endpoints.nodes, parts.path.strip('/'))
2322-
# include the parameters into the url. Use Request to get it rigth
2301+
# include the parameters into the url. Use Request to get it right
23232302
req = requests.Request(method, tmp_url, params=fields)
23242303
prepped = req.prepare()
23252304
url = prepped.url
@@ -2449,8 +2428,29 @@ def move(self, src_uri, destination_uri):
24492428

24502429
def _get(self, uri, view=None, cutout=None):
24512430
with nodeCache.volatile(uri):
2452-
return self.transfer(self.get_endpoints(uri).transfer,
2453-
uri, "pullFromVoSpace", None, cutout)
2431+
urls = self.transfer(self.get_endpoints(uri).transfer,
2432+
uri, "pullFromVoSpace", None, None)
2433+
# assume that the returned urls point to SODA services
2434+
if view == 'header':
2435+
head_urls = []
2436+
for url in urls:
2437+
head_urls.append('{}?META=true'.format(url))
2438+
urls = head_urls
2439+
elif cutout:
2440+
cutout_urls = []
2441+
for url in urls:
2442+
if cutout.strip().startswith('['):
2443+
# pixel cutout
2444+
cutout_urls.append('{}?SUB={}'.format(url, cutout))
2445+
elif cutout.strip().startswith('CIRCLE'):
2446+
# circle cutout
2447+
cutout_urls.append('{}?{}'.format(url, cutout))
2448+
else:
2449+
# TODO add support for other SODA cutouts SUB, POL etc
2450+
raise ValueError('Unknown cutout type: ' + cutout)
2451+
urls = cutout_urls
2452+
2453+
return urls
24542454

24552455
def _put(self, uri, content_length=None, md5_checksum=None):
24562456
with nodeCache.volatile(uri):

0 commit comments

Comments
 (0)