diff --git a/cap/config.py b/cap/config.py index e0b83151ed..727be66cf9 100644 --- a/cap/config.py +++ b/cap/config.py @@ -889,6 +889,10 @@ 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) + # APP_DEFAULT_SECURE_HEADERS = { # 'force_https': False, # 'force_https_permanent': False, diff --git a/cap/jsonschemas/options/deposits/records/lhcb-v0.0.1.json b/cap/jsonschemas/options/deposits/records/lhcb-v0.0.1.json index c2191a6941..545ec24e5e 100644 --- a/cap/jsonschemas/options/deposits/records/lhcb-v0.0.1.json +++ b/cap/jsonschemas/options/deposits/records/lhcb-v0.0.1.json @@ -1,228 +1,176 @@ -{ - "ui:order":[ - "basic_info", - "stripping_turbo_selection", - "ntuple_userdst_production", - "user_analysis", - "additional_resources" +{ + "ui:order": [ + "basic_info", + "stripping_turbo_selection", + "ntuple_userdst_production", + "user_analysis", + "additional_resources" + ], + "basic_info": { + "ui:object": "accordionObjectField", + "ui:order": [ + "analysis_title", + "measurement", + "analysis_proponents", + "reviewers", + "review_egroup", + "analysis_status", + "institutes", + "keywords" ], - "basic_info":{ - "ui:object":"accordionObjectField", - "ui:order":[ - "analysis_title", - "measurement", - "analysis_proponents", - "reviewers", - "review_egroup", - "analysis_status", - "institutes", - "keywords" - ], - "analysis_title":{ - "ui:options":{ - "suggestions":"/api/lhcb/analysis?query=", - "autofill_from":"/api/lhcb/analysis/details?title=", - "autofill_fields":[ - [ - [ - "measurement" - ], - [ - "basic_info", - "measurement" - ] - ], - [ - [ - "status" - ], - [ - "basic_info", - "status" - ] - ], - [ - [ - "egroup" - ], - [ - "basic_info", - "review_egroup" - ] - ], - [ - [ - "wg" - ], - [ - "basic_info", - "institutes" - ] - ] - ] - } - }, - "analysis_proponents": { - "items": { - "ui:options": { - "stringify": ["name"] - }, - "name": { - "ui:options": { - "autofill_from": "/api/orcid?name=", - "autofill_fields": [ - [ - [ - "orcid" - ], - [ - "basic_info", - "analysis_proponents", - "#", - "orcid" - ] - ] - ] - } - } - } + "analysis_title": { + "ui:options": { + "suggestions": "/api/lhcb/analysis?query=", + "autofill_from": "/api/lhcb/analysis/details?title=", + "autofill_fields": [ + [["measurement"], ["basic_info", "measurement"]], + [["status"], ["basic_info", "status"]], + [["egroup"], ["basic_info", "review_egroup"]], + [["wg"], ["basic_info", "institutes"]] + ] + } + }, + "analysis_proponents": { + "items": { + "ui:options": { + "stringify": ["name"] }, - "reviewers": { - "items": { - "ui:options": { - "stringify": ["name"] - }, - "name": { - "ui:options": { - "autofill_from": "/api/orcid?name=", - "autofill_fields": [ - [ - [ - "orcid" - ], - [ - "basic_info", - "reviewers", - "#", - "orcid" - ] - ] - ] - } - } - } + "name": { + "ui:options": { + "autofill_from": "/api/orcid?name=", + "autofill_fields": [ + [["orcid"], ["basic_info", "analysis_proponents", "#", "orcid"]] + ] + } } + } }, - "stripping_turbo_selection":{ - "items":{ - "ui:order":[ - "dataset_type", - "name", - "stripping_turbo_line", - "bookkeping_locations" - ], - "bookkeping_locations": { - "ui:array": "StringArrayField" - } + "reviewers": { + "items": { + "ui:options": { + "stringify": ["name"] }, - "ui:array":"AccordionArrayField" + "name": { + "ui:options": { + "autofill_from": "/api/orcid?name=", + "autofill_fields": [ + [["orcid"], ["basic_info", "reviewers", "#", "orcid"]] + ] + } + } + } + } + }, + "stripping_turbo_selection": { + "items": { + "ui:order": [ + "dataset_type", + "name", + "stripping_turbo_line", + "bookkeping_locations" + ], + "bookkeping_locations": { + "ui:array": "StringArrayField" + } }, - "ntuple_userdst_production":{ - "items":{ - "ui:order":[ - "name", - "input_dataset", - "platform", - "davinci_version", - "output_eos_location", - "wg_production_bk_path", - "davinci_scripts", - "ganga_scripts" - ], - "davinci_scripts":{ - "items":{ - "ui:field":"CapFiles" - } - }, - "ganga_scripts":{ - "items":{ - "ui:field":"CapFiles" - } - } - }, - "ui:array":"AccordionArrayField" + "ui:array": "AccordionArrayField" + }, + "ntuple_userdst_production": { + "items": { + "ui:order": [ + "name", + "input_dataset", + "platform", + "davinci_version", + "output_eos_location", + "wg_production_bk_path", + "davinci_scripts", + "ganga_scripts" + ], + "davinci_scripts": { + "items": { + "ui:field": "CapFiles" + } + }, + "ganga_scripts": { + "items": { + "ui:field": "CapFiles" + } + } }, - "user_analysis":{ - "ui:object":"accordionObjectField", - "ui:order": ["gitlab_links", "docker_registries", "additional_repos","basic_scripts"], - "additional_repos": { - "items": { - "ui:field":"CapFiles" - } - }, - "gitlab_links": { - "items": { - "ui:field":"CapFiles" - } - }, - "docker_registries": { - "items": { - "ui:field":"CapFiles" - } - }, - "basic_scripts": { - "scripts": { - "items": { - "ui:field":"CapFiles" - } - } + "ui:array": "AccordionArrayField" + }, + "user_analysis": { + "ui:object": "accordionObjectField", + "ui:order": [ + "gitlab_links", + "docker_registries", + "additional_repos", + "basic_scripts" + ], + "additional_repos": { + "items": { + "ui:field": "CapFiles" + } + }, + "gitlab_links": { + "items": { + "ui:field": "CapFiles", + "ui:options": { + "zenodo": true } + } }, - "additional_resources":{ - "ui:object":"accordionObjectField", - "ui:order":[ - "internal_discussions", - "presentations", - "publications", - "documentations" - ], - "internal_discussions": { - "items":{ - "ui:options": { - "stringify": ["title"] - }, - "ui:order": [ - "title", - "url", - "meeting" - ] - } - }, - "documentations": { - "items":{ - "ui:options": { - "stringify": ["title"] - } - } + "docker_registries": { + "items": { + "ui:field": "CapFiles" + } + }, + "basic_scripts": { + "scripts": { + "items": { + "ui:field": "CapFiles" + } + } + } + }, + "additional_resources": { + "ui:object": "accordionObjectField", + "ui:order": [ + "internal_discussions", + "presentations", + "publications", + "documentations" + ], + "internal_discussions": { + "items": { + "ui:options": { + "stringify": ["title"] }, - "presentations": { - "items":{ - "ui:options": { - "stringify": ["title"] - }, - "ui:order": [ - "title", - "url", - "meeting" - ] - } + "ui:order": ["title", "url", "meeting"] + } + }, + "documentations": { + "items": { + "ui:options": { + "stringify": ["title"] + } + } + }, + "presentations": { + "items": { + "ui:options": { + "stringify": ["title"] }, - "publications": { - "items":{ - "ui:options": { - "stringify": ["analysis_number"] - } - } + "ui:order": ["title", "url", "meeting"] + } + }, + "publications": { + "items": { + "ui:options": { + "stringify": ["analysis_number"] } + } } + } } diff --git a/cap/modules/zenodo/__init__.py b/cap/modules/zenodo/__init__.py new file mode 100644 index 0000000000..502a593b85 --- /dev/null +++ b/cap/modules/zenodo/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- + +"""CAP Zenodo.""" + +from __future__ import absolute_import, print_function diff --git a/cap/modules/zenodo/views.py b/cap/modules/zenodo/views.py new file mode 100644 index 0000000000..2cffe28598 --- /dev/null +++ b/cap/modules/zenodo/views.py @@ -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('//') +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}) diff --git a/setup.py b/setup.py index 742e42d879..8c0993cf64 100644 --- a/setup.py +++ b/setup.py @@ -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 @@ -133,6 +133,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': [ diff --git a/ui/src/actions/drafts.js b/ui/src/actions/drafts.js index 83b7e0d855..b47d049e2e 100644 --- a/ui/src/actions/drafts.js +++ b/ui/src/actions/drafts.js @@ -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"; @@ -295,6 +299,27 @@ export function permissionsItemError(error) { }; } +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 => { @@ -604,6 +629,22 @@ export function addPermissions(draft_id, email, action) { }; } +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(action, email, operation) { return [ { diff --git a/ui/src/components/drafts/form/themes/grommet/fields/CapFiles.js b/ui/src/components/drafts/form/themes/grommet/fields/CapFiles.js index 416f5b43fb..bb224bb6a6 100644 --- a/ui/src/components/drafts/form/themes/grommet/fields/CapFiles.js +++ b/ui/src/components/drafts/form/themes/grommet/fields/CapFiles.js @@ -3,18 +3,28 @@ 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); - + console.log(":::::", props); + let isZenodo = props.uiSchema["ui:options"] + ? props.uiSchema["ui:options"]["zenodo"] + : null; this.state = { layerActive: false, - selected: {} + selected: {}, + isZenodo: isZenodo }; } @@ -42,6 +52,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 ( {this.props.formData ? ( - - {this.props.formData} - } - onClick={this._toggleFileManager.bind(this)} - /> - + + + {this.props.formData} + } + onClick={this._toggleFileManager.bind(this)} + /> + + {this.state.isZenodo ? ( + +