Skip to content

Commit e5c1511

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

File tree

8 files changed

+234
-40
lines changed

8 files changed

+234
-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

+73-22
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,28 @@ 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+
console.log(":::::", props);
21+
let isZenodo = props.uiSchema["ui:options"]
22+
? props.uiSchema["ui:options"]["zenodo"]
23+
: null;
1524
this.state = {
1625
layerActive: false,
17-
selected: {}
26+
selected: {},
27+
isZenodo: isZenodo
1828
};
1929
}
2030

@@ -42,6 +52,9 @@ class CapFile extends React.Component {
4252
}
4353

4454
render() {
55+
let bucket = this.props.links ? this.props.links.get("bucket") : null;
56+
let bucket_id = bucket ? bucket.split("/").pop() : null;
57+
4558
return (
4659
<Box
4760
pad="small"
@@ -53,22 +66,46 @@ class CapFile extends React.Component {
5366
wrap={false}
5467
>
5568
{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>
69+
<Box>
70+
<Box direction="row">
71+
<Box pad="small">{this.props.formData}</Box>
72+
<Anchor
73+
icon={<Edit />}
74+
onClick={this._toggleFileManager.bind(this)}
75+
/>
76+
</Box>
77+
{this.state.isZenodo ? (
78+
<Box direction="row">
79+
<Button
80+
icon={<CloudUploadIcon />}
81+
label="Upload to zenodo"
82+
onClick={() => {
83+
this.props.uploadToZenodo(
84+
this.props.idSchema.$id,
85+
bucket_id,
86+
this.props.formData
87+
);
88+
}}
89+
/>
90+
{this.props.zenodoId == 200 ? (
91+
<Box pad="small">
92+
<Status value="ok" />
93+
</Box>
94+
) : null}
95+
</Box>
96+
) : null}
97+
</Box>
6398
) : (
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>
99+
<Box>
100+
<Box direction="row">
101+
<Anchor
102+
label="Open File Manager"
103+
onClick={this._toggleFileManager.bind(this)}
104+
/>
105+
<Box> -- OR -- </Box>
106+
<Box>Drag & Drop files here</Box>
107+
</Box>
108+
</Box>
72109
)}
73110
</Box>
74111
);
@@ -83,17 +120,31 @@ CapFile.propTypes = {
83120
onChange: PropTypes.func,
84121
properties: PropTypes.object,
85122
toggleFilemanagerLayer: PropTypes.func,
86-
formData: PropTypes.object
123+
formData: PropTypes.object,
124+
uploadToZenodo: PropTypes.func,
125+
links: PropTypes.object,
126+
zenodo: PropTypes.object,
127+
uiSchema: PropTypes.object,
128+
idSchema: PropTypes.object
87129
};
88130

131+
function mapStateToProps(state, props) {
132+
return {
133+
links: state.drafts.getIn(["current_item", "links"]),
134+
zenodoId: state.drafts.getIn(["zenodo", props.idSchema.$id, "status"])
135+
};
136+
}
137+
89138
function mapDispatchToProps(dispatch) {
90139
return {
91140
toggleFilemanagerLayer: (selectable = false, action) =>
92-
dispatch(toggleFilemanagerLayer(selectable, action))
141+
dispatch(toggleFilemanagerLayer(selectable, action)),
142+
uploadToZenodo: (element_id, bucket_id, filename) =>
143+
dispatch(uploadToZenodo(element_id, bucket_id, filename))
93144
};
94145
}
95146

96147
export default connect(
97-
null,
148+
mapStateToProps,
98149
mapDispatchToProps
99150
)(CapFile);

0 commit comments

Comments
 (0)