Skip to content

Commit fe442c0

Browse files
committed
services: add handling of async results of upload
* adds the task results to the record * addresses #1970 Signed-off-by: Ilias Koutsakis <[email protected]>
1 parent a070abe commit fe442c0

File tree

4 files changed

+84
-28
lines changed

4 files changed

+84
-28
lines changed

cap/modules/deposit/api.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
UpdateDepositPermission)
7979

8080
from .review import Reviewable
81-
from .tasks import upload_to_zenodo
81+
from .tasks import create_zenodo_upload_tasks
8282
from .utils import create_zenodo_deposit
8383

8484
_datastore = LocalProxy(lambda: current_app.extensions['security'].datastore)
@@ -287,9 +287,9 @@ def upload(self, pid, *args, **kwargs):
287287
self.commit()
288288

289289
# upload files to zenodo deposit
290-
upload_to_zenodo.delay(files, recid, token,
291-
deposit['id'],
292-
deposit['links']['bucket'])
290+
create_zenodo_upload_tasks(files, recid, token,
291+
deposit['id'],
292+
deposit['links']['bucket'])
293293
else:
294294
raise FileUploadError(
295295
'You cannot create an empty Zenodo deposit. '

cap/modules/deposit/tasks.py

+64-21
Original file line numberDiff line numberDiff line change
@@ -21,38 +21,81 @@
2121
# In applying this license, CERN does not
2222
# waive the privileges and immunities granted to it by virtue of its status
2323
# as an Intergovernmental Organization or submit itself to any jurisdiction.
24-
"""Tasks."""
24+
"""Zenodo Upload Tasks."""
2525

2626
from __future__ import absolute_import, print_function
2727

2828
import requests
2929
from flask import current_app
30-
from celery import shared_task
30+
from celery import chord, group, current_task, shared_task
31+
3132
from invenio_files_rest.models import FileInstance
33+
from invenio_db import db
34+
35+
from .utils import get_zenodo_deposit_from_record
36+
37+
38+
def create_zenodo_upload_tasks(files, recid, token,
39+
zenodo_depid, zenodo_bucket_url):
40+
"""Create the upload tasks and get the results."""
41+
current_app.logger.info(
42+
f'Uploading files to Zenodo {zenodo_depid}: {files}.')
43+
44+
# the only way to have a task that waits for
45+
# other tasks to finish is the `chord` structure
46+
upload_callback = save_results_to_record.s(depid=zenodo_depid, recid=recid)
47+
upload_tasks = group(
48+
upload.s(filename, recid, token, zenodo_bucket_url)
49+
for filename in files
50+
)
51+
52+
chord(upload_tasks, upload_callback).delay()
53+
54+
55+
@shared_task(autoretry_for=(Exception, ),
56+
retry_kwargs={
57+
'max_retries': 5,
58+
'countdown': 10
59+
})
60+
def upload(filename, recid, token, zenodo_bucket_url):
61+
"""Upload file to Zenodo."""
62+
from cap.modules.deposit.api import CAPDeposit
63+
record = CAPDeposit.get_record(recid)
64+
65+
file_obj = record.files[filename]
66+
file_ins = FileInstance.get(file_obj.file_id)
67+
task_id = current_task.request.id
68+
69+
with open(file_ins.uri, 'rb') as fp:
70+
resp = requests.put(
71+
url=f'{zenodo_bucket_url}/{filename}',
72+
data=fp,
73+
params=dict(access_token=token),
74+
)
75+
76+
current_app.logger.error(
77+
f'{task_id}: Zenodo upload of file `{filename}`: {resp.status_code}.') # noqa
78+
return task_id, {'file': filename, 'result': resp.status_code}
3279

3380

3481
@shared_task(autoretry_for=(Exception, ),
3582
retry_kwargs={
3683
'max_retries': 5,
3784
'countdown': 10
3885
})
39-
def upload_to_zenodo(files, recid, token, zenodo_depid, zenodo_bucket_url):
40-
"""Upload to Zenodo the files the user selected."""
86+
def save_results_to_record(tasks, depid, recid):
87+
"""Save the results of uploading to the record."""
4188
from cap.modules.deposit.api import CAPDeposit
42-
rec = CAPDeposit.get_record(recid)
43-
44-
for filename in files:
45-
file_obj = rec.files[filename]
46-
file_ins = FileInstance.get(file_obj.file_id)
47-
48-
with open(file_ins.uri, 'rb') as fp:
49-
resp = requests.put(
50-
url=f'{zenodo_bucket_url}/{filename}',
51-
data=fp,
52-
params=dict(access_token=token),
53-
)
54-
55-
if not resp.ok:
56-
current_app.logger.error(
57-
f'Uploading file {filename} to deposit {zenodo_depid} '
58-
f'failed with {resp.status_code}.')
89+
record = CAPDeposit.get_record(recid)
90+
91+
# update the tasks of the specified zenodo deposit (filename: status)
92+
# this way we can attach multiple deposits
93+
zenodo = get_zenodo_deposit_from_record(record, depid)
94+
for task in tasks:
95+
zenodo['tasks'][task[0]] = task[1]
96+
97+
record.commit()
98+
db.session.commit()
99+
100+
current_app.logger.info(
101+
f'COMPLETED: Zenodo {depid} uploads:\n{tasks}')

cap/modules/deposit/utils.py

+14
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,20 @@ def add_api_to_links(links):
8282
return response
8383

8484

85+
def get_zenodo_deposit_from_record(record, pid):
86+
"""Get the related Zenodo information from a record."""
87+
try:
88+
index = [idx for idx, deposit in enumerate(record['_zenodo'])
89+
if deposit['id'] == pid][0]
90+
91+
# set an empty dict as tasks if there is none
92+
record['_zenodo'][index].setdefault('tasks', {})
93+
return record['_zenodo'][index]
94+
except IndexError:
95+
raise FileUploadError(
96+
'The Zenodo pid you provided is not associated with this record.')
97+
98+
8599
def create_zenodo_deposit(token, data=None):
86100
"""Create a Zenodo deposit using the logged in user's credentials."""
87101
zenodo_url = current_app.config.get("ZENODO_SERVER_URL")

tests/integration/test_zenodo_upload.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def test_create_and_upload_to_zenodo(mock_token, app, users, deposit_with_file,
104104

105105
assert len(record['_zenodo']) == 1
106106
assert record['_zenodo'][0]['id'] == 111
107-
assert record['_zenodo'][0]['title'] == None
107+
assert record['_zenodo'][0]['title'] is None
108108
assert record['_zenodo'][0]['created'] == '2020-11-20T11:49:39.147767+00:00'
109109

110110

@@ -382,8 +382,7 @@ def test_zenodo_upload_file_not_uploaded_error(mock_token, app, users, deposit_w
382382
assert resp.status_code == 201
383383

384384
captured = capsys.readouterr()
385-
assert 'Uploading file test-file.txt to deposit 111 failed with 500' \
386-
in captured.err
385+
assert 'Zenodo upload of file `test-file.txt`: 500.' in captured.err
387386

388387

389388
@patch('cap.modules.deposit.api._fetch_token', return_value='test-token')

0 commit comments

Comments
 (0)