Skip to content

Commit c95d3e9

Browse files
deposit: push code to zenodo
* Closes #841. Signed-off-by: Ioannis Tsanaktsidis <[email protected]>
1 parent 47708ed commit c95d3e9

File tree

8 files changed

+233
-40
lines changed

8 files changed

+233
-40
lines changed

cap/config.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -444,9 +444,9 @@ def _(x):
444444
# Update CERN OAuth handlers - due to REST - mostly only redirect urls
445445
# and error flashing
446446
CERN_REMOTE_APP.update(dict(
447-
authorized_handler=authorized_signup_handler,
448-
disconnect_handler=disconnect_handler,
449-
))
447+
authorized_handler=authorized_signup_handler,
448+
disconnect_handler=disconnect_handler,
449+
))
450450

451451
CERN_REMOTE_APP['signup_handler']['view'] = signup_handler
452452

@@ -603,3 +603,7 @@ def _(x):
603603

604604
REANA_CLIENT_TOKEN = os.environ.get(
605605
'APP_REANA_CLIENT_TOKEN', None)
606+
607+
# Zenodo
608+
# ======
609+
ZENODO_ACCESS_TOKEN = os.environ.get('APP_ZENODO_ACCESS_TOKEN', None)

cap/jsonschemas/options/deposits/records/lhcb-v0.0.1.json

+5-2
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@
5454
},
5555
"gitlab_links": {
5656
"items": {
57-
"ui:field": "CapFiles"
57+
"ui:field": "CapFiles",
58+
"ui:options": {
59+
"zenodo": true
60+
}
5861
}
5962
},
6063
"ui:object": "accordionObjectField",
@@ -247,4 +250,4 @@
247250
}
248251
},
249252
"is_deposit": false
250-
}
253+
}

cap/modules/zenodo/__init__.py

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""CAP Zenodo."""
4+
5+
from __future__ import absolute_import, print_function

cap/modules/zenodo/views.py

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# -*- coding: utf-8 -*-
2+
#
3+
# This file is part of CERN Analysis Preservation Framework.
4+
# Copyright (C) 2018 CERN.
5+
#
6+
# CERN Analysis Preservation Framework is free software; you can redistribute
7+
# it and/or modify it under the terms of the GNU General Public License as
8+
# published by the Free Software Foundation; either version 2 of the
9+
# License, or (at your option) any later version.
10+
#
11+
# CERN Analysis Preservation Framework is distributed in the hope that it will
12+
# be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
# General Public License for more details.
15+
#
16+
# You should have received a copy of the GNU General Public License
17+
# along with CERN Analysis Preservation Framework; if not, write to the
18+
# Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
19+
# MA 02111-1307, USA.
20+
#
21+
# In applying this license, CERN does not
22+
# waive the privileges and immunities granted to it by virtue of its status
23+
# as an Intergovernmental Organization or submit itself to any jurisdiction.
24+
25+
26+
"""CAP Zenodo views."""
27+
28+
import requests
29+
30+
from flask import Blueprint, current_app, jsonify
31+
from invenio_files_rest.models import FileInstance, ObjectVersion
32+
33+
34+
zenodo_bp = Blueprint('cap_zenodo',
35+
__name__,
36+
url_prefix='/zenodo'
37+
)
38+
39+
40+
@zenodo_bp.route('/<bucket_id>/<filename>')
41+
def upload_to_zenodo(bucket_id, filename):
42+
"""Upload code to zenodo."""
43+
params = {"access_token": current_app.config.get(
44+
'ZENODO_ACCESS_TOKEN', '')}
45+
filename = filename + '.tar.gz'
46+
47+
r = requests.post('https://sandbox.zenodo.org/api/deposit/depositions',
48+
params=params, json={},
49+
)
50+
51+
file_obj = ObjectVersion.get(bucket_id, filename)
52+
file = FileInstance.get(file_obj.file_id)
53+
54+
bucket_url = r.json()['links']['bucket']
55+
with open(file.uri, 'rb') as fp:
56+
response = requests.put(
57+
bucket_url + '/{}'.format(filename),
58+
data=fp,
59+
params=params,
60+
)
61+
62+
return jsonify({"status": response.status_code})

setup.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
'Flask-Cache>=0.13.1',
6565
'Flask-Debugtoolbar>=0.10.1',
6666
# CAP specific libraries
67-
'PyGithub>=1.35',
67+
'PyGithub>=1.43.2',
6868
'python-gitlab>=1.0.2',
6969

7070
# Pinned libraries
@@ -134,6 +134,7 @@
134134
'cap_lhcb = cap.modules.experiments.views.lhcb:lhcb_bp',
135135
'cap_cms = cap.modules.experiments.views.cms:cms_bp',
136136
'cap_reana = cap.modules.reana.views:reana_bp',
137+
'cap_zenodo = cap.modules.zenodo.views:zenodo_bp',
137138
'invenio_oauthclient = invenio_oauthclient.views.client:blueprint',
138139
],
139140
'invenio_celery.tasks': [

ui/src/actions/drafts.js

+42
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ export const UPLOAD_FILE_REQUEST = "UPLOAD_FILE_REQUEST";
5151
export const UPLOAD_FILE_SUCCESS = "UPLOAD_FILE_SUCCESS";
5252
export const UPLOAD_FILE_ERROR = "UPLOAD_FILE_ERROR";
5353

54+
export const UPLOAD_TO_ZENODO_REQUEST = "UPLOAD_TO_ZENODO_REQUEST";
55+
export const UPLOAD_TO_ZENODO_SUCCESS = "UPLOAD_TO_ZENODO_SUCCESS";
56+
export const UPLOAD_TO_ZENODO_ERROR = "UPLOAD_TO_ZENODO_ERROR";
57+
5458
export const EDIT_PUBLISHED_REQUEST = "EDIT_PUBLISHED_REQUEST";
5559
export const EDIT_PUBLISHED_SUCCESS = "EDIT_PUBLISHED_SUCCESS";
5660
export const EDIT_PUBLISHED_ERROR = "EDIT_PUBLISHED_ERROR";
@@ -298,12 +302,34 @@ export function permissionsItemError(error) {
298302
};
299303
}
300304

305+
301306
export function clearErrorSuccess() {
302307
return {
303308
type: CLEAR_ERROR_SUCCESS
304309
};
305310
}
306311

312+
export function uploadToZenodoRequest() {
313+
return {
314+
type: UPLOAD_TO_ZENODO_REQUEST
315+
};
316+
}
317+
318+
export function uploadToZenodoSuccess(element_id, status) {
319+
return {
320+
type: UPLOAD_TO_ZENODO_SUCCESS,
321+
element_id,
322+
status
323+
};
324+
}
325+
326+
export function uploadToZenodoError(error) {
327+
return {
328+
type: UPLOAD_TO_ZENODO_ERROR,
329+
error
330+
};
331+
}
332+
307333
// [TOFIX] Plug validation action if needed.
308334
// export function validate(data, schema) {
309335
// return dispatch => {
@@ -603,6 +629,22 @@ export function handlePermissions(draft_id, type, email, action, operation) {
603629
};
604630
}
605631

632+
export function uploadToZenodo(element_id, bucket_id, filename) {
633+
return dispatch => {
634+
dispatch(uploadToZenodoRequest());
635+
let file = filename.split("/").pop();
636+
let uri = `/api/zenodo/${bucket_id}/${file}`;
637+
axios
638+
.get(uri)
639+
.then(response => {
640+
dispatch(uploadToZenodoSuccess(element_id, response.status));
641+
})
642+
.catch(error => {
643+
dispatch(uploadToZenodoError(error));
644+
});
645+
};
646+
}
647+
606648
function _get_permissions_data(type, email, action, operation) {
607649
return [
608650
{

ui/src/components/drafts/form/themes/grommet/fields/CapFiles.js

+72-22
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,27 @@ import PropTypes from "prop-types";
33

44
import { connect } from "react-redux";
55

6-
import { Box, Anchor } from "grommet";
6+
import { Box, Anchor, Button } from "grommet";
77

88
import Edit from "grommet/components/icons/base/FormEdit";
9-
import { toggleFilemanagerLayer } from "../../../../../../actions/drafts";
9+
import CloudUploadIcon from "grommet/components/icons/base/CloudUpload";
10+
import {
11+
toggleFilemanagerLayer,
12+
uploadToZenodo
13+
} from "../../../../../../actions/drafts";
14+
15+
import Status from "grommet/components/icons/Status";
1016

1117
class CapFile extends React.Component {
1218
constructor(props) {
1319
super(props);
14-
20+
let isZenodo = props.uiSchema["ui:options"]
21+
? props.uiSchema["ui:options"]["zenodo"]
22+
: null;
1523
this.state = {
1624
layerActive: false,
17-
selected: {}
25+
selected: {},
26+
isZenodo: isZenodo
1827
};
1928
}
2029

@@ -42,6 +51,9 @@ class CapFile extends React.Component {
4251
}
4352

4453
render() {
54+
let bucket = this.props.links ? this.props.links.get("bucket") : null;
55+
let bucket_id = bucket ? bucket.split("/").pop() : null;
56+
4557
return (
4658
<Box
4759
pad="small"
@@ -53,22 +65,46 @@ class CapFile extends React.Component {
5365
wrap={false}
5466
>
5567
{this.props.formData ? (
56-
<React.Fragment>
57-
<span>{this.props.formData}</span>
58-
<Anchor
59-
icon={<Edit />}
60-
onClick={this._toggleFileManager.bind(this)}
61-
/>
62-
</React.Fragment>
68+
<Box>
69+
<Box direction="row">
70+
<Box pad="small">{this.props.formData}</Box>
71+
<Anchor
72+
icon={<Edit />}
73+
onClick={this._toggleFileManager.bind(this)}
74+
/>
75+
</Box>
76+
{this.state.isZenodo ? (
77+
<Box direction="row">
78+
<Button
79+
icon={<CloudUploadIcon />}
80+
label="Upload to zenodo"
81+
onClick={() => {
82+
this.props.uploadToZenodo(
83+
this.props.idSchema.$id,
84+
bucket_id,
85+
this.props.formData
86+
);
87+
}}
88+
/>
89+
{this.props.zenodoId == 200 ? (
90+
<Box pad="small">
91+
<Status value="ok" />
92+
</Box>
93+
) : null}
94+
</Box>
95+
) : null}
96+
</Box>
6397
) : (
64-
<React.Fragment>
65-
<Anchor
66-
label="Open File Manager"
67-
onClick={this._toggleFileManager.bind(this)}
68-
/>
69-
<Box> -- OR -- </Box>
70-
<Box>Drag & Drop files here</Box>
71-
</React.Fragment>
98+
<Box>
99+
<Box direction="row">
100+
<Anchor
101+
label="Open File Manager"
102+
onClick={this._toggleFileManager.bind(this)}
103+
/>
104+
<Box> -- OR -- </Box>
105+
<Box>Drag & Drop files here</Box>
106+
</Box>
107+
</Box>
72108
)}
73109
</Box>
74110
);
@@ -83,17 +119,31 @@ CapFile.propTypes = {
83119
onChange: PropTypes.func,
84120
properties: PropTypes.object,
85121
toggleFilemanagerLayer: PropTypes.func,
86-
formData: PropTypes.object
122+
formData: PropTypes.object,
123+
uploadToZenodo: PropTypes.func,
124+
links: PropTypes.object,
125+
zenodo: PropTypes.object,
126+
uiSchema: PropTypes.object,
127+
idSchema: PropTypes.object
87128
};
88129

130+
function mapStateToProps(state, props) {
131+
return {
132+
links: state.drafts.getIn(["current_item", "links"]),
133+
zenodoId: state.drafts.getIn(["zenodo", props.idSchema.$id, "status"])
134+
};
135+
}
136+
89137
function mapDispatchToProps(dispatch) {
90138
return {
91139
toggleFilemanagerLayer: (selectable = false, action) =>
92-
dispatch(toggleFilemanagerLayer(selectable, action))
140+
dispatch(toggleFilemanagerLayer(selectable, action)),
141+
uploadToZenodo: (element_id, bucket_id, filename) =>
142+
dispatch(uploadToZenodo(element_id, bucket_id, filename))
93143
};
94144
}
95145

96146
export default connect(
97-
null,
147+
mapStateToProps,
98148
mapDispatchToProps
99149
)(CapFile);

0 commit comments

Comments
 (0)