Skip to content

Commit 1565e99

Browse files
authored
Merge branch 'master' into 80-do-not-post-full-extractor_info-with-metadata-v2
2 parents cb0b9e1 + 814b4e3 commit 1565e99

File tree

10 files changed

+278
-42
lines changed

10 files changed

+278
-42
lines changed

pyclowder/api/v1/datasets.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,40 @@ def upload_metadata(connector, client, datasetid, metadata):
260260
verify=connector.ssl_verify if connector else True)
261261
result.raise_for_status()
262262

263+
def upload_thumbnail(connector, host, key, datasetid, thumbnail):
264+
"""Upload thumbnail to Clowder.
265+
266+
Keyword arguments:
267+
connector -- connector information, used to get missing parameters and send status updates
268+
host -- the clowder host, including http and port, should end with a /
269+
key -- the secret key to login to clowder
270+
datasetid -- the dataset that the thumbnail should be associated with
271+
thumbnail -- the file containing the thumbnail
272+
"""
273+
logger = logging.getLogger(__name__)
274+
logger.info("Upload thumbnails to datasets is not available in V1")
275+
276+
277+
def upload_preview(connector, host, key, datasetid, previewfile, previewmetadata=None, preview_mimetype=None,
278+
visualization_name=None, visualization_description=None, visualization_config_data=None,
279+
visualization_component_id=None):
280+
"""Upload preview to Clowder.
281+
282+
Keyword arguments:
283+
connector -- connector information, used to get missing parameters and send status updates
284+
host -- the clowder host, including http and port, should end with a /
285+
key -- the secret key to login to clowder
286+
datasetid -- the dataset that is currently being processed
287+
previewfile -- the file containing the preview
288+
previewmetadata -- any metadata to be associated with preview, can contain a section_id
289+
to indicate the section this preview should be associated with.
290+
preview_mimetype -- (optional) MIME type of the preview file. By default, this is obtained from the
291+
file itself and this parameter can be ignored. E.g. 'application/vnd.clowder+custom+xml'
292+
"""
293+
294+
logger = logging.getLogger(__name__)
295+
logger.info("Upload preview to datasets is not available in V1")
296+
263297

264298
# TODO not done yet, need more testing
265299
class DatasetsApi(object):

pyclowder/api/v2/datasets.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import tempfile
1010

1111
import requests
12+
from requests_toolbelt.multipart.encoder import MultipartEncoder
1213

1314
from pyclowder.client import ClowderClient
1415
from pyclowder.collections import get_datasets, get_child_collections, delete as delete_collection
@@ -233,3 +234,120 @@ def upload_metadata(connector, client, datasetid, metadata):
233234
verify=connector.ssl_verify if connector else True)
234235
result.raise_for_status()
235236

237+
def upload_preview(connector, client, datasetid, previewfile, previewmetadata=None, preview_mimetype=None,
238+
visualization_name=None, visualization_description=None, visualization_config_data=None,
239+
visualization_component_id=None):
240+
"""Upload visualization to Clowder.
241+
242+
Keyword arguments:
243+
connector -- connector information, used to get missing parameters and send status updates
244+
client -- ClowderClient containing authentication credentials
245+
datsetid -- the dataset that is currently being processed
246+
previewfile -- the file containing the preview
247+
previewmetadata -- any metadata to be associated with preview, can contain a section_id
248+
to indicate the section this preview should be associated with.
249+
preview_mimetype -- (optional) MIME type of the preview file. By default, this is obtained from the
250+
file itself and this parameter can be ignored. E.g. 'application/vnd.clowder+custom+xml'
251+
"""
252+
253+
connector.message_process({"type": "dataset", "id": datasetid}, "Uploading dataset preview.")
254+
logger = logging.getLogger(__name__)
255+
256+
preview_id = None
257+
visualization_config_id = None
258+
259+
if os.path.exists(previewfile):
260+
261+
# upload visualization URL
262+
visualization_config_url = '%s/api/v2/visualizations/config' % client.host
263+
264+
if visualization_config_data is None:
265+
visualization_config_data = dict()
266+
267+
payload = json.dumps({
268+
"resource": {
269+
"collection": "datasets",
270+
"resource_id": datasetid
271+
},
272+
"client": client.host,
273+
"parameters": visualization_config_data,
274+
"visualization_mimetype": preview_mimetype,
275+
"visualization_component_id": visualization_component_id
276+
})
277+
278+
headers = {
279+
"X-API-KEY": client.key,
280+
"Content-Type": "application/json"
281+
}
282+
283+
response = connector.post(visualization_config_url, headers=headers, data=payload,
284+
verify=connector.ssl_verify if connector else True)
285+
286+
if response.status_code == 200:
287+
visualization_config_id = response.json()['id']
288+
logger.debug("Uploaded visualization config ID = [%s]", visualization_config_id)
289+
else:
290+
logger.error("An error occurred when uploading visualization config to dataset: " + datasetid)
291+
292+
if visualization_config_id is not None:
293+
294+
# upload visualization URL
295+
visualization_url = '%s/api/v2/visualizations?name=%s&description=%s&config=%s' % (
296+
client.host, visualization_name, visualization_description, visualization_config_id)
297+
298+
filename = os.path.basename(previewfile)
299+
if preview_mimetype is not None:
300+
multipart_encoder_object = MultipartEncoder(
301+
fields={'file': (filename, open(previewfile, 'rb'), preview_mimetype)})
302+
else:
303+
multipart_encoder_object = MultipartEncoder(fields={'file': (filename, open(previewfile, 'rb'))})
304+
headers = {'X-API-KEY': client.key,
305+
'Content-Type': multipart_encoder_object.content_type}
306+
response = connector.post(visualization_url, data=multipart_encoder_object, headers=headers,
307+
verify=connector.ssl_verify if connector else True)
308+
309+
if response.status_code == 200:
310+
preview_id = response.json()['id']
311+
logger.debug("Uploaded visualization data ID = [%s]", preview_id)
312+
else:
313+
logger.error("An error occurred when uploading the visualization data to dataset: " + datasetid)
314+
else:
315+
logger.error("Visualization data file not found")
316+
317+
return preview_id
318+
319+
def upload_thumbnail(connector, client, datasetid, thumbnail):
320+
"""Upload thumbnail to Clowder.
321+
322+
Keyword arguments:
323+
connector -- connector information, used to get missing parameters and send status updates
324+
host -- the clowder host, including http and port, should end with a /
325+
key -- the secret key to login to clowder
326+
datasetid -- the dataset that the thumbnail should be associated with
327+
thumbnail -- the file containing the thumbnail
328+
"""
329+
330+
logger = logging.getLogger(__name__)
331+
332+
connector.message_process({"type": "dataset", "id": datasetid}, "Uploading thumbnail to dataset.")
333+
334+
url = '%s/api/v2/thumbnails' % (client.host)
335+
336+
if os.path.exists(thumbnail):
337+
file_data = {"file": open(thumbnail, 'rb')}
338+
headers = {"X-API-KEY": client.key}
339+
result = connector.post(url, files=file_data, headers=headers,
340+
verify=connector.ssl_verify if connector else True)
341+
342+
thumbnailid = result.json()['id']
343+
logger.debug("uploaded thumbnail id = [%s]", thumbnailid)
344+
345+
connector.message_process({"type": "dataset", "id": datasetid}, "Uploading thumbnail to dataset.")
346+
headers = {'Content-Type': 'application/json',
347+
'X-API-KEY': client.key}
348+
url = '%s/api/v2/datasets/%s/thumbnail/%s' % (client.host, datasetid, thumbnailid)
349+
result = connector.patch(url, headers=headers,
350+
verify=connector.ssl_verify if connector else True)
351+
return result.json()["thumbnail_id"]
352+
else:
353+
logger.error("unable to upload thumbnail %s to dataset %s", thumbnail, datasetid)

pyclowder/api/v2/files.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ def download_info(connector, client, fileid):
9494

9595
return result
9696

97+
9798
def download_summary(connector, client, fileid):
9899
"""Download file summary from Clowder.
99100
@@ -272,7 +273,6 @@ def upload_tags(connector, client, fileid, tags):
272273
verify=connector.ssl_verify if connector else True)
273274

274275

275-
# TODO not implemented in v2
276276
def upload_thumbnail(connector, client, fileid, thumbnail):
277277
"""Upload thumbnail to Clowder.
278278
@@ -284,10 +284,28 @@ def upload_thumbnail(connector, client, fileid, thumbnail):
284284
thumbnail -- the file containing the thumbnail
285285
"""
286286

287-
# TODO: Update the code below after V2 endpoint for uploading a thumbnail is ready.
288287
logger = logging.getLogger(__name__)
289-
logger.info("Thumbnail upload is under construction and currently skipped in Clowder V2 extractors!")
290-
pass
288+
289+
connector.message_process({"type": "file", "id": fileid}, "Uploading thumbnail to file.")
290+
291+
url = '%s/api/v2/thumbnails' % (client.host)
292+
293+
if os.path.exists(thumbnail):
294+
file_data = {"file": open(thumbnail, 'rb')}
295+
headers = {"X-API-KEY": client.key}
296+
result = connector.post(url, files=file_data, headers=headers,
297+
verify=connector.ssl_verify if connector else True)
298+
299+
thumbnailid = result.json()['id']
300+
logger.debug("uploaded thumbnail id = [%s]", thumbnailid)
301+
headers = {'Content-Type': 'application/json',
302+
'X-API-KEY': client.key}
303+
url = '%s/api/v2/files/%s/thumbnail/%s' % (client.host, fileid, thumbnailid)
304+
result = connector.patch(url, headers=headers,
305+
verify=connector.ssl_verify if connector else True)
306+
return result.json()["thumbnail_id"]
307+
else:
308+
logger.error("unable to upload thumbnail %s to file %s", thumbnail, fileid)
291309

292310

293311
def upload_to_dataset(connector, client, datasetid, filepath, check_duplicate=False):

pyclowder/connectors.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -565,6 +565,23 @@ def post(self, url, data=None, json_data=None, raise_status=True, **kwargs):
565565

566566
return response
567567

568+
def patch(self, url, data=None, json_data=None, raise_status=True, **kwargs):
569+
"""
570+
This methods wraps the Python requests PATCH method
571+
:param url: URl to use in PATCH request
572+
:param data: (optional) data (Dictionary, bytes, or file-like object) to send in the body of PATCH request
573+
:param json_data: (optional) json data to send with PATCH request
574+
:param raise_status: (optional) If set to True, call raise_for_status. Default is True.
575+
:param kwargs: List of other optional arguments to pass to PATCH call
576+
:return: Response of the PATCH request
577+
"""
578+
579+
response = requests.patch(url, data=data, json=json_data, **kwargs)
580+
if raise_status:
581+
response.raise_for_status()
582+
583+
return response
584+
568585
def put(self, url, data=None, raise_status=True, **kwargs):
569586
"""
570587
This methods wraps the Python requests PUT method

0 commit comments

Comments
 (0)