Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

deposit: push code to zenodo #899

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion cap/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ def _(x):
GITLAB_OAUTH_ACCESS_TOKEN = os.environ.get(
'APP_GITLAB_OAUTH_ACCESS_TOKEN', None)

# Reana server url
# Reana access token
# ================
REANA_ACCESS_TOKEN = {
'ATLAS': os.environ.get(
Expand All @@ -650,3 +650,8 @@ def _(x):
'LHCb': os.environ.get(
'APP_REANA_LHCb_ACCESS_TOKEN', None)
}

# Zenodo
# ======
ZENODO_SERVER_URL = os.environ.get('APP_ZENODO_SERVER_URL', None)
ZENODO_ACCESS_TOKEN = os.environ.get('APP_ZENODO_ACCESS_TOKEN', None)
5 changes: 4 additions & 1 deletion 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
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
63 changes: 63 additions & 0 deletions cap/modules/zenodo/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# -*- 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."""
zenodo_server_url = current_app.config.get('ZENODO_SERVER_URL')
params = {"access_token": current_app.config.get(
'ZENODO_ACCESS_TOKEN')}
filename = filename + '.tar.gz'

r = requests.post(zenodo_server_url,
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 @@ -143,6 +143,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 @@ -57,6 +57,10 @@ export const DELETE_FILE_REQUEST = "DELETE_FILE_REQUEST";
export const DELETE_FILE_SUCCESS = "DELETE_FILE_SUCCESS";
export const DELETE_FILE_ERROR = "DELETE_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 @@ -320,12 +324,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 @@ -717,6 +743,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
75 changes: 62 additions & 13 deletions ui/src/components/drafts/form/themes/grommet/fields/CapFiles.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,27 @@ import { connect } from "react-redux";

import Box from "grommet/components/Box";
import Anchor from "grommet/components/Anchor";
import Button from "grommet/components/Button"

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 @@ -43,6 +53,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 @@ -54,13 +67,35 @@ 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
Expand All @@ -82,17 +117,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);
52 changes: 39 additions & 13 deletions ui/src/reducers/drafts.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ import {
GENERAL_TITLE_CHANGED,
GENERAL_TITLE_REQUEST,
GENERAL_TITLE_SUCCESS,
GENERAL_TITLE_ERROR
GENERAL_TITLE_ERROR,
UPLOAD_TO_ZENODO_REQUEST,
UPLOAD_TO_ZENODO_SUCCESS,
UPLOAD_TO_ZENODO_ERROR
} from "../actions/drafts";

const initialState = Map({
Expand Down Expand Up @@ -93,7 +96,8 @@ const initialState = Map({
error: null,
links: null,
permissions: []
})
}),
zenodo: Map({})
});

export default function draftsReducer(state = initialState, action) {
Expand Down Expand Up @@ -218,17 +222,26 @@ export default function draftsReducer(state = initialState, action) {
msg: "Error while updating.."
});
case INIT_FORM:
return state.set(
"current_item",
Map({
id: null,
data: null,
loading: false,
error: null,
links: null,
files: Map({})
})
);
return state
.set(
"current_item",
Map({
id: null,
data: null,
loading: false,
error: null,
links: null,
files: Map({})
})
)
.set(
"zenodo",
Map({
loading: false,
error: null,
status: null
})
);
case UPLOAD_FILE_REQUEST:
return state.setIn(["current_item", "files", action.filename], {
key: action.filename,
Expand Down Expand Up @@ -323,6 +336,19 @@ export default function draftsReducer(state = initialState, action) {
.setIn(["current_item", "error"], action.error.response.data);
case CLEAR_ERROR_SUCCESS:
return state.setIn(["current_item", "error"], null);
case UPLOAD_TO_ZENODO_REQUEST:
return state
.setIn(["zenodo", "loading"], true)
.setIn(["zenodo", "error"], false);
case UPLOAD_TO_ZENODO_SUCCESS:
return state.setIn(
["zenodo", action.element_id, "status"],
action.status
);
case UPLOAD_TO_ZENODO_ERROR:
return state
.setIn(["zenodo", "loading"], false)
.setIn(["zenodo", "error"], action.error);
case FORM_DATA_CHANGE:
return state.setIn(["current_item", "formData"], action.data);
case GENERAL_TITLE_CHANGED:
Expand Down