Skip to content

Commit

Permalink
deposit: push code to zenodo
Browse files Browse the repository at this point in the history
 * Closes #841.

Signed-off-by: Ioannis Tsanaktsidis <[email protected]>
  • Loading branch information
ioannistsanaktsidis committed Sep 27, 2018
1 parent 47708ed commit c95d3e9
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 40 deletions.
10 changes: 7 additions & 3 deletions cap/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -444,9 +444,9 @@ def _(x):
# Update CERN OAuth handlers - due to REST - mostly only redirect urls
# and error flashing
CERN_REMOTE_APP.update(dict(
authorized_handler=authorized_signup_handler,
disconnect_handler=disconnect_handler,
))
authorized_handler=authorized_signup_handler,
disconnect_handler=disconnect_handler,
))

CERN_REMOTE_APP['signup_handler']['view'] = signup_handler

Expand Down Expand Up @@ -603,3 +603,7 @@ def _(x):

REANA_CLIENT_TOKEN = os.environ.get(
'APP_REANA_CLIENT_TOKEN', None)

# Zenodo
# ======
ZENODO_ACCESS_TOKEN = os.environ.get('APP_ZENODO_ACCESS_TOKEN', None)
7 changes: 5 additions & 2 deletions cap/jsonschemas/options/deposits/records/lhcb-v0.0.1.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@
},
"gitlab_links": {
"items": {
"ui:field": "CapFiles"
"ui:field": "CapFiles",
"ui:options": {
"zenodo": true
}
}
},
"ui:object": "accordionObjectField",
Expand Down Expand Up @@ -247,4 +250,4 @@
}
},
"is_deposit": false
}
}
5 changes: 5 additions & 0 deletions cap/modules/zenodo/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-

"""CAP Zenodo."""

from __future__ import absolute_import, print_function
62 changes: 62 additions & 0 deletions cap/modules/zenodo/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
#
# This file is part of CERN Analysis Preservation Framework.
# Copyright (C) 2018 CERN.
#
# CERN Analysis Preservation Framework is free software; you can redistribute
# it and/or modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# CERN Analysis Preservation Framework is distributed in the hope that it will
# be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with CERN Analysis Preservation Framework; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307, USA.
#
# In applying this license, CERN does not
# waive the privileges and immunities granted to it by virtue of its status
# as an Intergovernmental Organization or submit itself to any jurisdiction.


"""CAP Zenodo views."""

import requests

from flask import Blueprint, current_app, jsonify
from invenio_files_rest.models import FileInstance, ObjectVersion


zenodo_bp = Blueprint('cap_zenodo',
__name__,
url_prefix='/zenodo'
)


@zenodo_bp.route('/<bucket_id>/<filename>')
def upload_to_zenodo(bucket_id, filename):
"""Upload code to zenodo."""
params = {"access_token": current_app.config.get(
'ZENODO_ACCESS_TOKEN', '')}
filename = filename + '.tar.gz'

r = requests.post('https://sandbox.zenodo.org/api/deposit/depositions',
params=params, json={},
)

file_obj = ObjectVersion.get(bucket_id, filename)
file = FileInstance.get(file_obj.file_id)

bucket_url = r.json()['links']['bucket']
with open(file.uri, 'rb') as fp:
response = requests.put(
bucket_url + '/{}'.format(filename),
data=fp,
params=params,
)

return jsonify({"status": response.status_code})
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
'Flask-Cache>=0.13.1',
'Flask-Debugtoolbar>=0.10.1',
# CAP specific libraries
'PyGithub>=1.35',
'PyGithub>=1.43.2',
'python-gitlab>=1.0.2',

# Pinned libraries
Expand Down Expand Up @@ -134,6 +134,7 @@
'cap_lhcb = cap.modules.experiments.views.lhcb:lhcb_bp',
'cap_cms = cap.modules.experiments.views.cms:cms_bp',
'cap_reana = cap.modules.reana.views:reana_bp',
'cap_zenodo = cap.modules.zenodo.views:zenodo_bp',
'invenio_oauthclient = invenio_oauthclient.views.client:blueprint',
],
'invenio_celery.tasks': [
Expand Down
42 changes: 42 additions & 0 deletions ui/src/actions/drafts.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ export const UPLOAD_FILE_REQUEST = "UPLOAD_FILE_REQUEST";
export const UPLOAD_FILE_SUCCESS = "UPLOAD_FILE_SUCCESS";
export const UPLOAD_FILE_ERROR = "UPLOAD_FILE_ERROR";

export const UPLOAD_TO_ZENODO_REQUEST = "UPLOAD_TO_ZENODO_REQUEST";
export const UPLOAD_TO_ZENODO_SUCCESS = "UPLOAD_TO_ZENODO_SUCCESS";
export const UPLOAD_TO_ZENODO_ERROR = "UPLOAD_TO_ZENODO_ERROR";

export const EDIT_PUBLISHED_REQUEST = "EDIT_PUBLISHED_REQUEST";
export const EDIT_PUBLISHED_SUCCESS = "EDIT_PUBLISHED_SUCCESS";
export const EDIT_PUBLISHED_ERROR = "EDIT_PUBLISHED_ERROR";
Expand Down Expand Up @@ -298,12 +302,34 @@ export function permissionsItemError(error) {
};
}


export function clearErrorSuccess() {
return {
type: CLEAR_ERROR_SUCCESS
};
}

export function uploadToZenodoRequest() {
return {
type: UPLOAD_TO_ZENODO_REQUEST
};
}

export function uploadToZenodoSuccess(element_id, status) {
return {
type: UPLOAD_TO_ZENODO_SUCCESS,
element_id,
status
};
}

export function uploadToZenodoError(error) {
return {
type: UPLOAD_TO_ZENODO_ERROR,
error
};
}

// [TOFIX] Plug validation action if needed.
// export function validate(data, schema) {
// return dispatch => {
Expand Down Expand Up @@ -603,6 +629,22 @@ export function handlePermissions(draft_id, type, email, action, operation) {
};
}

export function uploadToZenodo(element_id, bucket_id, filename) {
return dispatch => {
dispatch(uploadToZenodoRequest());
let file = filename.split("/").pop();
let uri = `/api/zenodo/${bucket_id}/${file}`;
axios
.get(uri)
.then(response => {
dispatch(uploadToZenodoSuccess(element_id, response.status));
})
.catch(error => {
dispatch(uploadToZenodoError(error));
});
};
}

function _get_permissions_data(type, email, action, operation) {
return [
{
Expand Down
94 changes: 72 additions & 22 deletions ui/src/components/drafts/form/themes/grommet/fields/CapFiles.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,27 @@ import PropTypes from "prop-types";

import { connect } from "react-redux";

import { Box, Anchor } from "grommet";
import { Box, Anchor, Button } from "grommet";

import Edit from "grommet/components/icons/base/FormEdit";
import { toggleFilemanagerLayer } from "../../../../../../actions/drafts";
import CloudUploadIcon from "grommet/components/icons/base/CloudUpload";
import {
toggleFilemanagerLayer,
uploadToZenodo
} from "../../../../../../actions/drafts";

import Status from "grommet/components/icons/Status";

class CapFile extends React.Component {
constructor(props) {
super(props);

let isZenodo = props.uiSchema["ui:options"]
? props.uiSchema["ui:options"]["zenodo"]
: null;
this.state = {
layerActive: false,
selected: {}
selected: {},
isZenodo: isZenodo
};
}

Expand Down Expand Up @@ -42,6 +51,9 @@ class CapFile extends React.Component {
}

render() {
let bucket = this.props.links ? this.props.links.get("bucket") : null;
let bucket_id = bucket ? bucket.split("/").pop() : null;

return (
<Box
pad="small"
Expand All @@ -53,22 +65,46 @@ class CapFile extends React.Component {
wrap={false}
>
{this.props.formData ? (
<React.Fragment>
<span>{this.props.formData}</span>
<Anchor
icon={<Edit />}
onClick={this._toggleFileManager.bind(this)}
/>
</React.Fragment>
<Box>
<Box direction="row">
<Box pad="small">{this.props.formData}</Box>
<Anchor
icon={<Edit />}
onClick={this._toggleFileManager.bind(this)}
/>
</Box>
{this.state.isZenodo ? (
<Box direction="row">
<Button
icon={<CloudUploadIcon />}
label="Upload to zenodo"
onClick={() => {
this.props.uploadToZenodo(
this.props.idSchema.$id,
bucket_id,
this.props.formData
);
}}
/>
{this.props.zenodoId == 200 ? (
<Box pad="small">
<Status value="ok" />
</Box>
) : null}
</Box>
) : null}
</Box>
) : (
<React.Fragment>
<Anchor
label="Open File Manager"
onClick={this._toggleFileManager.bind(this)}
/>
<Box> -- OR -- </Box>
<Box>Drag & Drop files here</Box>
</React.Fragment>
<Box>
<Box direction="row">
<Anchor
label="Open File Manager"
onClick={this._toggleFileManager.bind(this)}
/>
<Box> -- OR -- </Box>
<Box>Drag & Drop files here</Box>
</Box>
</Box>
)}
</Box>
);
Expand All @@ -83,17 +119,31 @@ CapFile.propTypes = {
onChange: PropTypes.func,
properties: PropTypes.object,
toggleFilemanagerLayer: PropTypes.func,
formData: PropTypes.object
formData: PropTypes.object,
uploadToZenodo: PropTypes.func,
links: PropTypes.object,
zenodo: PropTypes.object,
uiSchema: PropTypes.object,
idSchema: PropTypes.object
};

function mapStateToProps(state, props) {
return {
links: state.drafts.getIn(["current_item", "links"]),
zenodoId: state.drafts.getIn(["zenodo", props.idSchema.$id, "status"])
};
}

function mapDispatchToProps(dispatch) {
return {
toggleFilemanagerLayer: (selectable = false, action) =>
dispatch(toggleFilemanagerLayer(selectable, action))
dispatch(toggleFilemanagerLayer(selectable, action)),
uploadToZenodo: (element_id, bucket_id, filename) =>
dispatch(uploadToZenodo(element_id, bucket_id, filename))
};
}

export default connect(
null,
mapStateToProps,
mapDispatchToProps
)(CapFile);
Loading

0 comments on commit c95d3e9

Please sign in to comment.