From f4e5b52bb189219fec82d9f51a002152ecd5c926 Mon Sep 17 00:00:00 2001 From: Viktor Kovtun <viktor.kovtun@id.ethz.ch> Date: Mon, 5 Aug 2019 15:58:51 +0200 Subject: [PATCH] SSDM-8405 Research Collection Integration. --- .../eln-lims/1/as/master-data/data-model.xls | Bin 71168 -> 73728 bytes .../publication-api/plugin.properties | 2 + .../publication-api/publication-api.py | 93 +++++ .../html/img/research-collection-icon.png | Bin 0 -> 9943 bytes .../1/as/webapps/eln-lims/html/index.html | 4 + .../html/js/controllers/MainController.js | 15 +- .../eln-lims/html/js/server/ServerFacade.js | 106 ++++- .../webapps/eln-lims/html/js/util/FormUtil.js | 27 +- .../html/js/views/Export/ExportTreeView.js | 2 +- .../ResearchCollectionExportController.js | 127 ++++++ .../ResearchCollectionExportModel.js | 23 ++ .../ResearchCollectionExportView.js | 75 ++++ .../js/views/SideMenu/SideMenuWidgetView.js | 10 +- .../{exports-api.py => exportsApi.py} | 343 ++++++++-------- .../exports-api/generalExports.py | 87 ++++ .../exports-api/plugin.properties | 2 +- .../rc-exports-api/exportsApi.py | 1 + .../rc-exports-api/plugin.properties | 8 + .../rc-exports-api/rcExports.py | 384 ++++++++++++++++++ 19 files changed, 1095 insertions(+), 214 deletions(-) create mode 100644 openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/services/publication-api/plugin.properties create mode 100644 openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/services/publication-api/publication-api.py create mode 100644 openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/img/research-collection-icon.png create mode 100644 openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportController.js create mode 100644 openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportModel.js create mode 100644 openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportView.js rename openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/{exports-api.py => exportsApi.py} (80%) create mode 100644 openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/generalExports.py create mode 120000 openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/exportsApi.py create mode 100644 openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/plugin.properties create mode 100644 openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/rcExports.py diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls index 9f7724cae233674fc63335ad063636d9b015b00e..8d7784125bad3226f67a0cf35266de503b5a48cb 100644 GIT binary patch delta 15583 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 delta 12607 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 K-hcmS!T$q!u78UF diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/services/publication-api/plugin.properties b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/services/publication-api/plugin.properties new file mode 100644 index 00000000000..76d5aa52bab --- /dev/null +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/services/publication-api/plugin.properties @@ -0,0 +1,2 @@ +class = ch.ethz.sis.openbis.generic.server.asapi.v3.helper.service.JythonBasedCustomASServiceExecutor +script-path = publication-api.py \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/services/publication-api/publication-api.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/services/publication-api/publication-api.py new file mode 100644 index 00000000000..cc5c7e99fe2 --- /dev/null +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/services/publication-api/publication-api.py @@ -0,0 +1,93 @@ +import traceback + +from ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset import DataSetKind +from ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.create import DataSetCreation +from ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.fetchoptions import DataSetFetchOptions +from ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.id import DataSetPermId +from ch.ethz.sis.openbis.generic.asapi.v3.dto.datastore.id import DataStorePermId +from ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype import EntityKind +from ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.id import EntityTypePermId +from ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.id import ExperimentIdentifier +from ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.create import SampleCreation +from ch.ethz.sis.openbis.generic.asapi.v3.dto.space.id import SpacePermId +from ch.systemsx.cisd.common.logging import LogCategory +from ch.systemsx.cisd.openbis.generic.client.web.client.exception import UserFailureException +from java.util import ArrayList +from org.apache.log4j import Logger + +operationLog = Logger.getLogger(str(LogCategory.OPERATION) + '.publication-api.py') + + +def process(context, parameters): + method = parameters.get('method') + + try: + if method == 'insertPublication': + result = insertPublication(context, parameters) + else: + raise UserFailureException('Unknown method: "%s"' % str(method if method is not None else 'None')) + except Exception as e: + operationLog.error('Exception at: ' + traceback.format_exc()) + operationLog.error('Exception: ' + str(e)) + result = { + 'status': 'FAILED', + 'error': str(e) + } + return result + + +def insertPublication(context, parameters): + transaction = context.applicationService + sessionToken = transaction.loginAsSystem() + + v3 = context.applicationService + + name = parameters.get('name') + if name is None: + raise UserFailureException('name parameter missing') + + sampleId = createPublicationSample(parameters, sessionToken, v3).get(0) + createDataSet(parameters, sessionToken, v3, sampleId) + + return { + 'status': 'OK', + } + + +def createDataSet(parameters, sessionToken, v3, sampleId): + openBISRelatedIdentifiers = parameters.get('openBISRelatedIdentifiers').split(',') + identifiers = ArrayList(len(openBISRelatedIdentifiers)) + for identifier in openBISRelatedIdentifiers: + identifiers.add(DataSetPermId(identifier)) + + dataSetIds = v3.getDataSets(sessionToken, identifiers, DataSetFetchOptions()).keys() + operationLog.debug('Found %d data sets.' % len(dataSetIds)) + + dataSetCreation = DataSetCreation() + dataSetCreation.setAutoGeneratedCode(True) + dataSetCreation.setTypeId(EntityTypePermId('PUBLICATION_DATA', EntityKind.DATA_SET)) + dataSetCreation.setSampleId(sampleId) + dataSetCreation.setDataSetKind(DataSetKind.CONTAINER) + dataSetCreation.setComponentIds(dataSetIds) + dataSetCreation.setDataStoreId(DataStorePermId('STANDARD')) + v3.createDataSets(sessionToken, [dataSetCreation]) + + +def createPublicationSample(parameters, sessionToken, v3): + publicationOrganization = parameters.get('publicationOrganization') + publicationType = parameters.get('publicationType') # The only valid value for now is "Public Repository" + publicationDescription = parameters.get('publicationDescription') # Can be empty + publicationURL = parameters.get('publicationURL') + publicationIdentifier = parameters.get('publicationIdentifier') + + sampleCreation = SampleCreation() + sampleCreation.setTypeId(EntityTypePermId('PUBLICATION')) + sampleCreation.setExperimentId(ExperimentIdentifier('/PUBLICATIONS/PUBLIC_REPOSITORIES/PUBLICATIONS_COLLECTION')) + sampleCreation.setSpaceId(SpacePermId('PUBLICATIONS')) + sampleCreation.setProperty('$NAME', 'TEST NAME') + sampleCreation.setProperty('$PUBLICATION.ORGANIZATION', publicationOrganization) + sampleCreation.setProperty('$PUBLICATION.TYPE', publicationType) + sampleCreation.setProperty('$PUBLICATION.IDENTIFIER', publicationIdentifier) + sampleCreation.setProperty('$PUBLICATION.URL', publicationURL) + sampleCreation.setProperty('$PUBLICATION.DESCRIPTION', publicationDescription) + return v3.createSamples(sessionToken, [sampleCreation]) diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/img/research-collection-icon.png b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/img/research-collection-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..67c8597c90c2d1b3274d4c9639ae83b25db711ce GIT binary patch literal 9943 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 literal 0 HcmV?d00001 diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/index.html b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/index.html index 65763885d61..65c0c67df06 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/index.html +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/index.html @@ -249,6 +249,10 @@ <script type="text/javascript" src="./js/views/Export/ExportTreeModel.js"></script> <script type="text/javascript" src="./js/views/Export/ExportTreeView.js"></script> + <script type="text/javascript" src="./js/views/ResearchCollectionExport/ResearchCollectionExportController.js"></script> + <script type="text/javascript" src="./js/views/ResearchCollectionExport/ResearchCollectionExportModel.js"></script> + <script type="text/javascript" src="./js/views/ResearchCollectionExport/ResearchCollectionExportView.js"></script> + <script type="text/javascript" src="./js/views/DrawingBoards/DrawingBoardsController.js"></script> <script type="text/javascript" src="./js/views/DrawingBoards/DrawingBoardsModel.js"></script> <script type="text/javascript" src="./js/views/DrawingBoards/DrawingBoardsView.js"></script> diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js index 09690f4f1d5..142198f6bab 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/MainController.js @@ -376,12 +376,19 @@ function MainController(profile) { break; case "showExportTreePage": document.title = "Export Builder"; - var newView = new ExportTreeController(this); - var views = this._getNewViewModel(true, true, false); - newView.init(views); - this.currentView = newView; + var newExportView = new ExportTreeController(this); + var exportViews = this._getNewViewModel(true, true, false); + newExportView.init(exportViews); + this.currentView = newExportView; //window.scrollTo(0,0); break; + case "showResearchCollectionExportPage": + document.title = "Research Collection Export Builder"; + var newResearchCollectionExportView = new ResearchCollectionExportController(this); + var researchCollectionExportViews = this._getNewViewModel(true, true, false); + newResearchCollectionExportView.init(researchCollectionExportViews); + this.currentView = newResearchCollectionExportView; + break; case "showLabNotebookPage": document.title = "Lab Notebook"; var newView = new LabNotebookController(this); diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js index 5f6bde3add7..b9f816840a1 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js @@ -302,7 +302,65 @@ function ServerFacade(openbisServer) { "entities" : entities, "metadataOnly" : metadataOnly, }, callbackFunction, "exports-api"); - } + }; + + // + // Research collection export + // + this.exportRc = function(entities, includeRoot, metadataOnly, submissionUrl, submissionType, userInformation, callbackFunction) { + this.asyncExportRc({ + "method": "exportAll", + "includeRoot": includeRoot, + "entities": entities, + "metadataOnly": metadataOnly, + "submissionUrl": submissionUrl, + "submissionType": submissionType, + "userInformation": userInformation, + "originUrl": window.location.origin, + "sessionToken": this.openbisServer.getSession(), + }, callbackFunction, "rc-exports-api"); + }; + + this.asyncExportRc = function(parameters, callbackFunction, serviceId) { + require(["as/dto/service/execute/ExecuteAggregationServiceOperation", + "as/dto/operation/AsynchronousOperationExecutionOptions", "as/dto/service/id/DssServicePermId", + "as/dto/datastore/id/DataStorePermId", "as/dto/service/execute/AggregationServiceExecutionOptions"], + function(ExecuteAggregationServiceOperation, AsynchronousOperationExecutionOptions, DssServicePermId, DataStorePermId, + AggregationServiceExecutionOptions) { + var dataStoreId = new DataStorePermId("STANDARD"); + var dssServicePermId = new DssServicePermId(serviceId, dataStoreId); + var options = new AggregationServiceExecutionOptions(); + + options.withParameter("sessionToken", parameters["sessionToken"]); + + options.withParameter("entities", parameters["entities"]); + options.withParameter("includeRoot", parameters["includeRoot"]); + options.withParameter("metadataOnly", parameters["metadataOnly"]); + options.withParameter("method", parameters["method"]); + options.withParameter("originUrl", parameters["originUrl"]); + options.withParameter("submissionType", parameters["submissionType"]); + options.withParameter("submissionUrl", parameters["submissionUrl"]); + options.withParameter("entities", parameters["entities"]); + options.withParameter("userId", parameters["userInformation"]["id"]); + options.withParameter("userEmail", parameters["userInformation"]["email"]); + options.withParameter("userFirstName", parameters["userInformation"]["firstName"]); + options.withParameter("userLastName", parameters["userInformation"]["lastName"]); + + var operation = new ExecuteAggregationServiceOperation(dssServicePermId, options); + mainController.openbisV3.executeOperations([operation], new AsynchronousOperationExecutionOptions()).done(function(results) { + callbackFunction(results.executionId.permId); + }); + }); + }; + + // + // Gets submission types + // + this.listSubmissionTypes = function(callbackFunction) { + this.customELNApi({ + "method": "getSubmissionTypes", + }, callbackFunction, "rc-exports-api"); + }; // // Metadata Related Functions @@ -659,7 +717,6 @@ function ServerFacade(openbisServer) { this._callPasswordResetService = function(parameters, callbackFunction) { var _this = this; this.getOpenbisV3(function(openbisV3) { - openbisV3.loginAsAnonymousUser().done(function(sessionToken) { _this.openbisServer._internal.sessionToken = sessionToken; @@ -682,6 +739,7 @@ function ServerFacade(openbisServer) { } this.customELNApi = function(parameters, callbackFunction, service) { + var _this = this; if(!service) { service = "eln-lims-api"; } @@ -693,24 +751,28 @@ function ServerFacade(openbisServer) { var dataStoreCode = profile.getDefaultDataStoreCode(); this.openbisServer.createReportFromAggregationService(dataStoreCode, service, parameters, function(data) { - var error = null; - var result = {}; - if(data.error) { //Error Case 1 - error = data.error.message; - } else if (data.result.columns[1].title === "Error") { //Error Case 2 - error = data.result.rows[0][1].value; - } else if (data.result.columns[0].title === "STATUS" && data.result.rows[0][0].value === "OK") { //Success Case - result.message = data.result.rows[0][1].value; - result.data = data.result.rows[0][2].value; - if(result.data) { - result.data = JSON.parse(result.data); - } - } else { - error = "Unknown Error."; - } - callbackFunction(error, result); + _this.customELNApiCallbackHandler(data, callbackFunction); }); - } + }; + + this.customELNApiCallbackHandler = function(data, callbackFunction) { + var error = null; + var result = {}; + if (data && data.error) { //Error Case 1 + error = data.error.message; + } else if (data && data.result.columns[1].title === "Error") { //Error Case 2 + error = data.result.rows[0][1].value; + } else if (data && data.result.columns[0].title === "STATUS" && data.result.rows[0][0].value === "OK") { //Success Case + result.message = data.result.rows[0][1].value; + result.data = data.result.rows[0][2].value; + if(result.data) { + result.data = JSON.parse(result.data); + } + } else { + error = "Unknown Error."; + } + callbackFunction(error, result); + }; this.customELNASAPI = function(parameters, callbackFunction) { this.customASService(parameters, callbackFunction, "as-eln-lims-api"); @@ -2563,10 +2625,10 @@ function ServerFacade(openbisServer) { this.getSessionInformation = function(callbackFunction) { mainController.openbisV3.getSessionInformation().done(function(sessionInfo) { -Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â callbackFunction(sessionInfo); + callbackFunction(sessionInfo); Â Â Â Â Â Â Â Â }).fail(function(result) { - Util.showFailedServerCallError(result); - callbackFunction(false); + Util.showFailedServerCallError(result); + callbackFunction(false); }); } diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/FormUtil.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/FormUtil.js index e501e670936..9658d70d42f 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/FormUtil.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/FormUtil.js @@ -322,25 +322,28 @@ var FormUtil = new function() { Select2Manager.add($dropdown); return $dropdown; } - - this.getPlainDropdown = function(mapVals, placeHolder) { - var $component = $("<select>", {class : 'form-control'}); - if(placeHolder) { - $component.append($("<option>").attr('value', '').attr('selected', '').attr('disabled', '').text(placeHolder)); - } - for(var mIdx = 0; mIdx < mapVals.length; mIdx++) { + this.setValuesToComponent = function ($component, mapVals) { + for (var mIdx = 0; mIdx < mapVals.length; mIdx++) { var $option = $("<option>").attr('value', mapVals[mIdx].value).text(mapVals[mIdx].label); - if(mapVals[mIdx].disabled) { + if (mapVals[mIdx].disabled) { $option.attr('disabled', ''); } - if(mapVals[mIdx].selected) { + if (mapVals[mIdx].selected) { $option.attr('selected', ''); } $component.append($option); } + }; + + this.getPlainDropdown = function(mapVals, placeHolder) { + var $component = $("<select>", {class : 'form-control'}); + if (placeHolder) { + $component.append($("<option>").attr('value', '').attr('selected', '').attr('disabled', '').text(placeHolder)); + } + this.setValuesToComponent($component, mapVals); return $component; - } + }; this.getDataSetsDropDown = function(code, dataSetTypes) { var $component = $("<select>", { class : 'form-control ' }); @@ -350,11 +353,11 @@ var FormUtil = new function() { $component.append($("<option>").attr('value', '').attr('selected', '').attr('disabled', '').text('Select a dataset type')); - for(var i = 0; i < dataSetTypes.length; i++) { + for (var i = 0; i < dataSetTypes.length; i++) { var datasetType = dataSetTypes[i]; var label = Util.getDisplayNameFromCode(datasetType.code); var description = Util.getEmptyIfNull(datasetType.description); - if(description !== "") { + if (description !== "") { label += " (" + description + ")"; } diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/Export/ExportTreeView.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/Export/ExportTreeView.js index f09bd1a1cf9..7bbbd3de8e2 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/Export/ExportTreeView.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/Export/ExportTreeView.js @@ -39,7 +39,7 @@ function ExportTreeView(exportTreeController, exportTreeModel) { 'onClick': '$("form[name=\'exportTreeForm\']").submit()' }); $header.append($exportButton); - var $infoBox = FormUtil.getInfoBox("You can select any parts of the accesible openBIS structure to export:", [ + var $infoBox = FormUtil.getInfoBox("You can select any parts of the accessible openBIS structure to export:", [ "If you select a tree node and do not expand it, everything below this node will be exported by default.", "To export selectively only parts of a tree, open the nodes and select what to export." ]); diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportController.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportController.js new file mode 100644 index 00000000000..514b6a15d2d --- /dev/null +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportController.js @@ -0,0 +1,127 @@ +/* + * Copyright 2011 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function ResearchCollectionExportController(parentController) { + var researchCollectionExportModel = new ResearchCollectionExportModel(); + var researchCollectionExportView = new ResearchCollectionExportView(this, researchCollectionExportModel); + + this.init = function(views) { + researchCollectionExportView.repaint(views); + }; + + this.initialiseSubmissionTypesDropdown = function(callback) { + Util.blockUI(); + mainController.serverFacade.listSubmissionTypes(function(error, result) { + Util.unblockUI(); + if (error) { + Util.showError(error); + } else { + researchCollectionExportModel.submissionTypes = result.data.map(function (resultItem) { + return { + value: resultItem.url, + label: resultItem.title + }; + }); + researchCollectionExportView.refreshSubmissionTypeDropdown(); + } + }); + }; + + this.exportSelected = function() { + var _this = this; + var selectedNodes = $(researchCollectionExportModel.tree).fancytree('getTree').getSelectedNodes(); + + var selectedOption = researchCollectionExportView.$submissionTypeDropdown.find(":selected"); + var submissionUrl = selectedOption.val(); + var submissionType = selectedOption.text(); + + var toExport = []; + for (var eIdx = 0; eIdx < selectedNodes.length; eIdx++) { + var node = selectedNodes[eIdx]; + toExport.push({type: node.data.entityType, permId: node.key, expand: !node.expanded}); + } + + if (toExport.length === 0) { + Util.showInfo('First select something to export.'); + } else if (!submissionUrl) { + Util.showInfo('First select submission type.'); + } else { + Util.blockUI(); + this.getUserInformation(function(userInformation) { + mainController.serverFacade.exportRc(toExport, true, false, submissionUrl, submissionType, userInformation, + function(operationExecutionPermId) { + _this.waitForOpExecutionResponse(operationExecutionPermId, function(error, result) { + Util.unblockUI(); + if (result && result.data && result.data.url) { + var win = window.open(result.data.url, '_blank'); + win.focus(); + mainController.refreshView(); + } else { + if (error) { + Util.showError(error); + } else { + Util.showError('Returned result format is not correct.'); + } + } + }); + }); + }); + } + }; + + this.waitForOpExecutionResponse = function(operationExecutionPermIdString, callbackFunction) { + var _this = this; + require(["as/dto/operation/id/OperationExecutionPermId", + "as/dto/operation/fetchoptions/OperationExecutionFetchOptions"], + function(OperationExecutionPermId, OperationExecutionFetchOptions) { + var operationExecutionPermId = new OperationExecutionPermId(operationExecutionPermIdString); + var fetchOptions = new OperationExecutionFetchOptions(); + var fetchOptionsDetails = fetchOptions.withDetails(); + fetchOptionsDetails.withResults(); + fetchOptionsDetails.withError(); + mainController.openbisV3.getOperationExecutions([operationExecutionPermId], fetchOptions).done(function(results) { + var result = results[operationExecutionPermIdString]; + var v2Result = null; + if (result && result.details && result.details.results) { + v2Result = result.details.results[0]; + } + + if (result && result.state === 'FINISHED') { + mainController.serverFacade.customELNApiCallbackHandler(v2Result, callbackFunction); + } else if (!result || result.state === 'FAILED') { + mainController.serverFacade.customELNApiCallbackHandler(v2Result, callbackFunction); + } else { + setTimeout(function() { + _this.waitForOpExecutionResponse(operationExecutionPermIdString, callbackFunction); + }, 3000); + } + }); + }); + }; + + this.getUserInformation = function(callback) { + var userId = mainController.serverFacade.getUserId(); + mainController.serverFacade.getSessionInformation(function(sessionInfo) { + var userInformation = { + firstName: sessionInfo.person.firstName, + lastName: sessionInfo.person.lastName, + email: sessionInfo.person.email, + id: userId, + }; + callback(userInformation); + }); + }; +} \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportModel.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportModel.js new file mode 100644 index 00000000000..7099e83d875 --- /dev/null +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportModel.js @@ -0,0 +1,23 @@ +/* + * Copyright 2011 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function ResearchCollectionExportModel() { + this.submissionTypes = []; + + this.addSubmissionType = function(submissionType) { + submissionTypes.push(submissionType); + }; +} \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportView.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportView.js new file mode 100644 index 00000000000..7945f200cb3 --- /dev/null +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ResearchCollectionExport/ResearchCollectionExportView.js @@ -0,0 +1,75 @@ +/* + * Copyright 2011 ETH Zuerich, CISD + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function ResearchCollectionExportView(researchCollectionExportController, researchCollectionExportModel) { + var exportTreeView = new ExportTreeView(researchCollectionExportController, researchCollectionExportModel); + + this.repaint = function(views) { + researchCollectionExportController.initialiseSubmissionTypesDropdown(); + + var $header = views.header; + var $container = views.content; + + var $form = $("<div>"); + var $formColumn = $("<form>", { + 'name': 'rcExportForm', + 'role': 'form', + 'action': 'javascript:void(0);', + 'onsubmit': 'mainController.currentView.exportSelected();' + }); + $form.append($formColumn); + + var $infoBox = FormUtil.getInfoBox('You can select any parts of the accessible openBIS structure to export:', [ + 'If you select a tree node and do not expand it, everything below this node will be exported by default.', + 'To export selectively only parts of a tree, open the nodes and select what to export.' + ]); + $infoBox.css('border', 'none'); + $container.append($infoBox); + + var $tree = $('<div>', { 'id' : 'exportsTree' }); + $formColumn.append($('<br>')); + $formColumn.append(FormUtil.getBox().append($tree)); + + $container.append($form); + + this.paintSubmissionTypeDropdown($container); + + researchCollectionExportModel.tree = TreeUtil.getCompleteTree($tree); + + var $formTitle = $('<h2>').append('Research Collection Export Builder'); + $header.append($formTitle); + + var $exportButton = $('<input>', { 'type': 'submit', 'class': 'btn btn-primary', 'value': 'Export Selected', + 'onClick': '$("form[name=\'rcExportForm\']").submit()'}); + $header.append($exportButton); + }; + + this.paintSubmissionTypeDropdown = function($container) { + this.$submissionTypeDropdown = this.getSubmissionTypeDropdown(); + var entityTypeDropdownFormGroup = FormUtil.getFieldForComponentWithLabel(this.$submissionTypeDropdown, 'Submission Type', null, true); + entityTypeDropdownFormGroup.css('width', '50%'); + $container.append(entityTypeDropdownFormGroup); + }; + + this.getSubmissionTypeDropdown = function() { + return FormUtil.getDropdown(researchCollectionExportModel.submissionTypes, 'Select a submission type'); + }; + + this.refreshSubmissionTypeDropdown = function() { + FormUtil.setValuesToComponent(this.$submissionTypeDropdown, researchCollectionExportModel.submissionTypes); + } + +} \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SideMenu/SideMenuWidgetView.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SideMenu/SideMenuWidgetView.js index da2bc285601..41e50655fc3 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SideMenu/SideMenuWidgetView.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SideMenu/SideMenuWidgetView.js @@ -267,7 +267,14 @@ function SideMenuWidgetView(sideMenuWidgetController, sideMenuWidgetModel) { if(profile.mainMenu.showExports) { Â Â Â Â var exportBuilderLink = _this.getLinkForNode("Export Builder", "EXPORT_BUILDER", "showExportTreePage", null); - Â Â Â Â treeModelUtils.push({ displayName: "Export Builder", title : exportBuilderLink, entityType: "EXPORT_BUILDER", key : "EXPORT_BUILDER", folder : false, lazy : false, view : "showExportTreePage", icon : "glyphicon glyphicon-export" }); + Â Â Â Â treeModelUtils.push({ displayName: "Export Builder", title : exportBuilderLink, entityType: "EXPORT_BUILDER", key : "EXPORT_BUILDER", + folder : false, lazy : false, view : "showExportTreePage", icon : "glyphicon glyphicon-export" }); + + var researchCollectionExportBuilderLink = _this.getLinkForNode("Research Collection Export Builder", "RESEARCH_COLLECTION_EXPORT_BUILDER", + "showResearchCollectionExportPage", null); + treeModelUtils.push({ displayName: "Research Collection Export Builder", title: researchCollectionExportBuilderLink, + entityType: "RESEARCH_COLLECTION_EXPORT_BUILDER", key: "RESEARCH_COLLECTION_EXPORT_BUILDER", folder: false, lazy: false, + view: "showResearchCollectionExportPage" }); } if(profile.mainMenu.showStorageManager) { @@ -811,6 +818,7 @@ function SideMenuWidgetView(sideMenuWidgetController, sideMenuWidgetModel) { Â Â Â Â Â Â Â Â Â Â Â Â stock.setExpanded(true); Â Â Â Â Â Â Â Â } Â Â Â Â Â Â Â Â + setCustomIcon($tree, "RESEARCH_COLLECTION_EXPORT_BUILDER", "./img/research-collection-icon.png"); Â Â Â Â Â Â Â Â setCustomIcon($tree, "JUPYTER_WORKSPACE", "./img/jupyter-icon.png"); Â Â Â Â Â Â Â Â setCustomIcon($tree, "NEW_JUPYTER_NOTEBOOK", "./img/jupyter-icon.png"); } diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/exports-api.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/exportsApi.py similarity index 80% rename from openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/exports-api.py rename to openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/exportsApi.py index 1494b24e95d..932c0fa3023 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/exports-api.py +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/exportsApi.py @@ -13,42 +13,28 @@ # See the License for the specific language governing permissions and # limitations under the License. # -from collections import deque -import time import jarray -import threading - -#Java Core -from java.io import ByteArrayInputStream -from org.apache.commons.io import IOUtils -from org.apache.commons.io import FileUtils - -from java.lang import String -from java.lang import StringBuilder - +# To obtain the openBIS URL +from ch.systemsx.cisd.openbis.dss.generic.server import DataStoreServer; from ch.systemsx.cisd.openbis.generic.client.web.client.exception import UserFailureException - -#Zip Format +from collections import deque +# Zip Format from java.io import File; from java.io import FileInputStream; -from java.io import FileNotFoundException; from java.io import FileOutputStream; +from java.lang import String +from java.lang import StringBuilder from java.util.zip import ZipEntry; from java.util.zip import ZipOutputStream; -from ch.systemsx.cisd.common.mail import EMailAddress; +from org.apache.commons.io import FileUtils +# Java Core +from org.apache.commons.io import IOUtils -#To obtain the openBIS URL -from ch.systemsx.cisd.openbis.dss.generic.server import DataStoreServer; -from ch.systemsx.cisd.openbis.dss.generic.server import SessionTokenManager -from ch.systemsx.cisd.openbis.generic.shared.api.v1.dto import SearchCriteria OPENBISURL = DataStoreServer.getConfigParameters().getServerURL() + "/openbis/openbis"; V3_DSS_BEAN = "data-store-server_INTERNAL"; #V3 API - Metadata -from ch.systemsx.cisd.common.spring import HttpInvokerUtils; -from ch.ethz.sis.openbis.generic.asapi.v3 import IApplicationServerApi; -from ch.ethz.sis.openbis.generic.asapi.v3.dto.property import DataType; from HTMLParser import HTMLParser from ch.ethz.sis.openbis.generic.asapi.v3.dto.space.search import SpaceSearchCriteria; @@ -75,7 +61,6 @@ from ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.fetchoptions import DataSe from ch.ethz.sis.openbis.generic.asapi.v3.dto.property import DataType #V3 API - Files -from ch.ethz.sis.openbis.generic.dssapi.v3 import IDataStoreServerApi; from ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.search import DataSetFileSearchCriteria; from ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.fetchoptions import DataSetFileFetchOptions; from ch.systemsx.cisd.openbis.dss.generic.shared import ServiceProvider; @@ -94,7 +79,7 @@ from ch.systemsx.cisd.openbis.dss.client.api.v1 import DssComponentFactory #Logging from ch.systemsx.cisd.common.logging import LogCategory; from org.apache.log4j import Logger; -operationLog = Logger.getLogger(str(LogCategory.OPERATION) + ".exports-api.py"); +operationLog = Logger.getLogger(str(LogCategory.OPERATION) + ".exportsApi.py"); #AVI API - DTO from ch.ethz.sis.openbis.generic.asapi.v3.dto.project import Project @@ -107,9 +92,6 @@ from ch.ethz.sis import DOCXBuilder #Images export for word from org.jsoup import Jsoup; -from org.jsoup.nodes import Document; -from org.jsoup.nodes import Element; -from org.jsoup.select import Elements; class MLStripper(HTMLParser): def __init__(self): @@ -125,32 +107,23 @@ def strip_tags(html): s.feed(html) return s.get_data() -def process(tr, params, tableBuilder): - method = params.get("method"); - isOk = False; - result = None; - - # Set user using the service - - tr.setUserId(userId); - if method == "exportAll": - isOk = expandAndexport(tr, params); +def displayResult(isOk, tableBuilder, result=None): if isOk: tableBuilder.addHeader("STATUS"); tableBuilder.addHeader("MESSAGE"); tableBuilder.addHeader("RESULT"); row = tableBuilder.addRow(); - row.setCell("STATUS","OK"); + row.setCell("STATUS", "OK"); row.setCell("MESSAGE", "Operation Successful"); row.setCell("RESULT", result); - else : + else: tableBuilder.addHeader("STATUS"); tableBuilder.addHeader("MESSAGE"); row = tableBuilder.addRow(); - row.setCell("STATUS","FAIL"); + row.setCell("STATUS", "FAIL"); row.setCell("MESSAGE", "Operation Failed"); - + def addToExportWithoutRepeating(entitiesToExport, entityFound): found = False; @@ -161,38 +134,56 @@ def addToExportWithoutRepeating(entitiesToExport, entityFound): if not found: entitiesToExport.append(entityFound); -def expandAndexport(tr, params): - #Services used during the export process - # TO-DO Login on the services as ETL server but on behalf of the user that makes the call + +def validateDataSize(entitiesToExport, tr): + limitDataSizeInMegabytes = getConfigurationProperty(tr, 'limit-data-size-megabytes') + if limitDataSizeInMegabytes is None: + limitDataSizeInMegabytes = 500; + else: + limitDataSizeInMegabytes = int(limitDataSizeInMegabytes); + limitDataSizeInBytes = 1000000 * limitDataSizeInMegabytes; + estimatedSizeInBytes = 0; + for entityToExport in entitiesToExport: + if entityToExport["type"] == "FILE" and entityToExport["isDirectory"] == False: + estimatedSizeInBytes += entityToExport["length"]; + elif entityToExport["type"] != "FILE": + estimatedSizeInBytes += 12000; # AVG File Metadata size + estimatedSizeInMegabytes = estimatedSizeInBytes / 1000000; + operationLog.info( + "Size Limit check - limitDataSizeInBytes: " + str(limitDataSizeInBytes) + " > " + " estimatedSizeInBytes: " + str(estimatedSizeInBytes)); + if estimatedSizeInBytes > limitDataSizeInBytes: + raise UserFailureException("The selected data is " + str(estimatedSizeInMegabytes) + " MB that is bigger than the configured limit of " + str( + limitDataSizeInMegabytes) + " MB"); + + +def findEntitiesToExport(params): sessionToken = params.get("sessionToken"); v3 = ServiceProvider.getV3ApplicationService(); v3d = ServiceProvider.getApplicationContext().getBean(V3_DSS_BEAN); - mailClient = tr.getGlobalState().getMailClient(); metadataOnly = params.get("metadataOnly"); - entitiesToExport = []; entitiesToExpand = deque([]); - + entitiesToExport = []; entities = params.get("entities"); - includeRoot = params.get("includeRoot"); - userEmail = v3.getSessionInformation(sessionToken).getPerson().getEmail(); + for entity in entities: entityAsPythonMap = { "type" : entity.get("type"), "permId" : entity.get("permId"), "expand" : entity.get("expand") }; - addToExportWithoutRepeating(entitiesToExport, entityAsPythonMap); + entitiesToExport.append(entityAsPythonMap); if entity.get("expand"): entitiesToExpand.append(entityAsPythonMap); - + + operationLog.info("Found %d entities to expand." % len(entitiesToExpand)) while entitiesToExpand: entityToExpand = entitiesToExpand.popleft(); - type = entityToExpand["type"]; + type = entityToExpand["type"]; permId = entityToExpand["permId"]; operationLog.info("Expanding type: " + str(type) + " permId: " + str(permId)); - + if type == "ROOT": criteria = SpaceSearchCriteria(); results = v3.searchSpaces(sessionToken, criteria, SpaceFetchOptions()); operationLog.info("Found: " + str(results.getTotalCount()) + " spaces"); for space in results.getObjects(): - entityFound = { "type" : "SPACE", "permId" : space.getCode() }; + entityFound = {"type": "SPACE", "permId": space.getCode(), "registrationDate": space.getRegistrationDate()}; addToExportWithoutRepeating(entitiesToExport, entityFound); entitiesToExpand.append(entityFound); if type == "SPACE": @@ -201,7 +192,7 @@ def expandAndexport(tr, params): results = v3.searchProjects(sessionToken, criteria, ProjectFetchOptions()); operationLog.info("Found: " + str(results.getTotalCount()) + " projects"); for project in results.getObjects(): - entityFound = { "type" : "PROJECT", "permId" : project.getPermId().getPermId() }; + entityFound = {"type": "PROJECT", "permId": project.getPermId().getPermId(), "registrationDate": project.getRegistrationDate()}; addToExportWithoutRepeating(entitiesToExport, entityFound); entitiesToExpand.append(entityFound); if type == "PROJECT": @@ -210,7 +201,8 @@ def expandAndexport(tr, params): results = v3.searchExperiments(sessionToken, criteria, ExperimentFetchOptions()); operationLog.info("Found: " + str(results.getTotalCount()) + " experiments"); for experiment in results.getObjects(): - entityFound = { "type" : "EXPERIMENT", "permId" : experiment.getPermId().getPermId() }; + entityFound = {"type": "EXPERIMENT", "permId": experiment.getPermId().getPermId(), + "registrationDate": experiment.getRegistrationDate()}; addToExportWithoutRepeating(entitiesToExport, entityFound); entitiesToExpand.append(entityFound); if type == "EXPERIMENT": @@ -218,29 +210,33 @@ def expandAndexport(tr, params): criteria.withExperiment().withPermId().thatEquals(permId); results = v3.searchSamples(sessionToken, criteria, SampleFetchOptions()); operationLog.info("Found: " + str(results.getTotalCount()) + " samples"); - + dCriteria = DataSetSearchCriteria(); dCriteria.withExperiment().withPermId().thatEquals(permId); dCriteria.withoutSample(); - dResults = v3.searchDataSets(sessionToken, dCriteria, DataSetFetchOptions()); + fetchOptions = DataSetFetchOptions() + fetchOptions.withDataStore() + dResults = v3.searchDataSets(sessionToken, dCriteria, fetchOptions); operationLog.info("Found: " + str(dResults.getTotalCount()) + " datasets"); for dataset in dResults.getObjects(): - entityFound = { "type" : "DATASET", "permId" : dataset.getPermId().getPermId() }; + entityFound = {"type": "DATASET", "permId": dataset.getPermId().getPermId(), "registrationDate": dataset.getRegistrationDate()}; addToExportWithoutRepeating(entitiesToExport, entityFound); entitiesToExpand.append(entityFound); - + operationLog.info("Found: " + str(results.getTotalCount()) + " samples"); for sample in results.getObjects(): - entityFound = { "type" : "SAMPLE", "permId" : sample.getPermId().getPermId() }; + entityFound = {"type": "SAMPLE", "permId": sample.getPermId().getPermId(), "registrationDate": sample.getRegistrationDate()}; addToExportWithoutRepeating(entitiesToExport, entityFound); entitiesToExpand.append(entityFound); if type == "SAMPLE": criteria = DataSetSearchCriteria(); criteria.withSample().withPermId().thatEquals(permId); - results = v3.searchDataSets(sessionToken, criteria, DataSetFetchOptions()); + fetchOptions = DataSetFetchOptions() + fetchOptions.withDataStore() + results = v3.searchDataSets(sessionToken, criteria, fetchOptions); operationLog.info("Found: " + str(results.getTotalCount()) + " datasets"); for dataset in results.getObjects(): - entityFound = { "type" : "DATASET", "permId" : dataset.getPermId().getPermId() }; + entityFound = {"type": "DATASET", "permId": dataset.getPermId().getPermId(), "registrationDate": dataset.getRegistrationDate()}; addToExportWithoutRepeating(entitiesToExport, entityFound); entitiesToExpand.append(entityFound); if type == "DATASET" and not metadataOnly: @@ -249,70 +245,51 @@ def expandAndexport(tr, params): results = v3d.searchFiles(sessionToken, criteria, DataSetFileFetchOptions()); operationLog.info("Found: " + str(results.getTotalCount()) + " files"); for file in results.getObjects(): - entityFound = { "type" : "FILE", "permId" : permId, "path" : file.getPath(), "isDirectory" : file.isDirectory(), "length" : file.getFileLength() }; + entityFound = {"type": "FILE", "permId": permId, "path": file.getPath(), "isDirectory": file.isDirectory(), + "length": file.getFileLength()}; addToExportWithoutRepeating(entitiesToExport, entityFound); - - - limitDataSizeInMegabytes = getConfigurationProperty(tr, 'limit-data-size-megabytes'); - if limitDataSizeInMegabytes is None: - limitDataSizeInMegabytes = 500; - else: - limitDataSizeInMegabytes = int(limitDataSizeInMegabytes); - - limitDataSizeInBytes = 1000000 * limitDataSizeInMegabytes; - estimatedSizeInBytes = 0; - for entityToExport in entitiesToExport: - if entityToExport["type"] == "FILE" and entityToExport["isDirectory"] == False: - estimatedSizeInBytes += entityToExport["length"]; - elif entityToExport["type"] != "FILE": - estimatedSizeInBytes += 12000; #AVG File Metadata size - estimatedSizeInMegabytes = estimatedSizeInBytes / 1000000; - - operationLog.info("Size Limit check - limitDataSizeInBytes: " + str(limitDataSizeInBytes) + " > " + " estimatedSizeInBytes: " + str(estimatedSizeInBytes)); - if estimatedSizeInBytes > limitDataSizeInBytes: - raise UserFailureException("The selected data is " + str(estimatedSizeInMegabytes) + " MB that is bigger than the configured limit of " + str(limitDataSizeInMegabytes) + " MB"); - - operationLog.info("Found " + str(len(entitiesToExport)) + " entities to export, export thread will start"); - thread = threading.Thread(target=export, args=(sessionToken, entitiesToExport, includeRoot, userEmail, mailClient)); - thread.daemon = True; - thread.start(); - - return True; + return entitiesToExport + + +# Removes temporal folder and zip +def cleanUp(tempDirPath, tempZipFilePath): + FileUtils.forceDelete(File(tempDirPath)); + FileUtils.forceDelete(File(tempZipFilePath)); + + +# Generates ZIP file and stores it in workspace +def generateZipFile(entities, includeRoot, sessionToken, tempDirPath, tempZipFilePath): + # Create Zip File + fos = FileOutputStream(tempZipFilePath); + zos = ZipOutputStream(fos); + + generateFilesInZip(zos, entities, includeRoot, sessionToken, tempDirPath) -def export(sessionToken, entities, includeRoot, userEmail, mailClient): - #Services used during the export process + zos.close(); + fos.close(); + + +def generateFilesInZip(zos, entities, includeRoot, sessionToken, tempDirPath): + # Services used during the export process v3 = ServiceProvider.getV3ApplicationService(); v3d = ServiceProvider.getApplicationContext().getBean(V3_DSS_BEAN); - dssComponent = DssComponentFactory.tryCreate(sessionToken, OPENBISURL); - objectCache = {}; objectMapper = GenericObjectMapper(); objectMapper.enable(SerializationFeature.INDENT_OUTPUT); - - #Create temporal folder - tempDirName = "export_" + str(time.time()); - tempDirPathFile = File.createTempFile(tempDirName, None); - tempDirPathFile.delete(); - tempDirPathFile.mkdir(); - tempDirPath = tempDirPathFile.getCanonicalPath(); - #To avoid empty directories on the zip file, it makes the first found entity the base directory + # To avoid empty directories on the zip file, it makes the first found entity the base directory baseDirToCut = None; - - #Create Zip File - tempZipFileName = tempDirName + ".zip"; - tempZipFilePath = tempDirPath + ".zip"; - fos = FileOutputStream(tempZipFilePath); - zos = ZipOutputStream(fos); - + fileMetadata = [] + emptyZip = True + for entity in entities: - type = entity["type"]; + type = entity["type"]; permId = entity["permId"]; operationLog.info("exporting type: " + str(type) + " permId: " + str(permId)); entityObj = None; entityFilePath = None; - + if type == "SPACE": - pass #Do nothing + pass # Do nothing if type == "PROJECT": criteria = ProjectSearchCriteria(); criteria.withPermId().thatEquals(permId); @@ -333,7 +310,8 @@ def export(sessionToken, entities, includeRoot, userEmail, mailClient): fetchOps.withProperties(); fetchOps.withTags(); entityObj = v3.searchExperiments(sessionToken, criteria, fetchOps).getObjects().get(0); - entityFilePath = getFilePath(entityObj.getProject().getSpace().getCode(), entityObj.getProject().getCode(), entityObj.getCode(), None, None); + entityFilePath = getFilePath(entityObj.getProject().getSpace().getCode(), entityObj.getProject().getCode(), entityObj.getCode(), None, + None); if type == "SAMPLE": criteria = SampleSearchCriteria(); criteria.withPermId().thatEquals(permId); @@ -347,7 +325,9 @@ def export(sessionToken, entities, includeRoot, userEmail, mailClient): fetchOps.withParents().withProperties(); fetchOps.withChildren().withProperties(); entityObj = v3.searchSamples(sessionToken, criteria, fetchOps).getObjects().get(0); - entityFilePath = getFilePath(entityObj.getExperiment().getProject().getSpace().getCode(), entityObj.getExperiment().getProject().getCode(), entityObj.getExperiment().getCode(), entityObj.getCode(), None); + entityFilePath = getFilePath(entityObj.getExperiment().getProject().getSpace().getCode(), + entityObj.getExperiment().getProject().getCode(), entityObj.getExperiment().getCode(), entityObj.getCode(), + None); if type == "DATASET": criteria = DataSetSearchCriteria(); criteria.withPermId().thatEquals(permId); @@ -362,71 +342,84 @@ def export(sessionToken, entities, includeRoot, userEmail, mailClient): fetchOps.withParents().withProperties(); fetchOps.withChildren().withProperties(); entityObj = v3.searchDataSets(sessionToken, criteria, fetchOps).getObjects().get(0); - + sampleCode = None - if(entityObj.getSample() is not None): + if (entityObj.getSample() is not None): sampleCode = entityObj.getSample().getCode(); - - entityFilePath = getFilePath(entityObj.getExperiment().getProject().getSpace().getCode(), entityObj.getExperiment().getProject().getCode(), entityObj.getExperiment().getCode(), sampleCode, entityObj.getCode()); + + entityFilePath = getFilePath(entityObj.getExperiment().getProject().getSpace().getCode(), + entityObj.getExperiment().getProject().getCode(), entityObj.getExperiment().getCode(), sampleCode, + entityObj.getCode()); if type == "FILE" and not entity["isDirectory"]: datasetEntityObj = objectCache[entity["permId"]]; sampleCode = None - if(datasetEntityObj.getSample() is not None): + if (datasetEntityObj.getSample() is not None): sampleCode = datasetEntityObj.getSample().getCode(); - - datasetEntityFilePath = getFilePath(datasetEntityObj.getExperiment().getProject().getSpace().getCode(), datasetEntityObj.getExperiment().getProject().getCode(), datasetEntityObj.getExperiment().getCode(), sampleCode, datasetEntityObj.getCode()); + + datasetEntityFilePath = getFilePath(datasetEntityObj.getExperiment().getProject().getSpace().getCode(), + datasetEntityObj.getExperiment().getProject().getCode(), datasetEntityObj.getExperiment().getCode(), + sampleCode, datasetEntityObj.getCode()); filePath = datasetEntityFilePath + "/" + entity["path"]; - + if not includeRoot: - filePath = filePath[len(baseDirToCut):] #To avoid empty directories on the zip file, it makes the first found entity the base directory - - rawFileInputStream = DataSetFileDownloadReader(v3d.downloadFiles(sessionToken, [DataSetFilePermId(DataSetPermId(permId), entity["path"])], DataSetFileDownloadOptions())).read().getInputStream(); + filePath = filePath[ + len(baseDirToCut):] # To avoid empty directories on the zip file, it makes the first found entity the base directory + + rawFileInputStream = DataSetFileDownloadReader(v3d.downloadFiles(sessionToken, [DataSetFilePermId(DataSetPermId(permId), entity["path"])], + DataSetFileDownloadOptions())).read().getInputStream(); rawFile = File(tempDirPath + filePath + ".json"); rawFile.getParentFile().mkdirs(); IOUtils.copyLarge(rawFileInputStream, FileOutputStream(rawFile)); addToZipFile(filePath, rawFile, zos); - - #To avoid empty directories on the zip file, it makes the first found entity the base directory + emptyZip = False + + # To avoid empty directories on the zip file, it makes the first found entity the base directory if not includeRoot: if baseDirToCut is None and entityFilePath is not None: baseDirToCut = entityFilePath[:entityFilePath.rfind('/')]; if entityFilePath is not None: entityFilePath = entityFilePath[len(baseDirToCut):] # - + if entityObj is not None: objectCache[permId] = entityObj; - - operationLog.info("--> Entity type: " + type + " permId: " + permId + " obj: " + str(entityObj is not None) + " path: " + str(entityFilePath) + " before files."); + + operationLog.info("--> Entity type: " + type + " permId: " + permId + " obj: " + str(entityObj is not None) + " path: " + str( + entityFilePath) + " before files."); if entityObj is not None and entityFilePath is not None: - #JSON + # JSON entityJson = String(objectMapper.writeValueAsString(entityObj)); - addFile(tempDirPath, entityFilePath, "json", entityJson.getBytes(), zos); - #TEXT + fileMetadatum = addFile(tempDirPath, entityFilePath, "json", entityJson.getBytes(), zos); + fileMetadata.append(fileMetadatum) + emptyZip = False + # TEXT entityTXT = String(getTXT(entityObj, v3, sessionToken, False)); - addFile(tempDirPath, entityFilePath, "txt", entityTXT.getBytes(), zos); - #DOCX + fileMetadatum = addFile(tempDirPath, entityFilePath, "txt", entityTXT.getBytes(), zos); + fileMetadata.append(fileMetadatum) + # DOCX entityDOCX = getDOCX(entityObj, v3, sessionToken, False); - addFile(tempDirPath, entityFilePath, "docx", entityDOCX, zos); - #HTML + fileMetadatum = addFile(tempDirPath, entityFilePath, "docx", entityDOCX, zos); + fileMetadata.append(fileMetadatum) + # HTML entityHTML = getDOCX(entityObj, v3, sessionToken, True); - addFile(tempDirPath, entityFilePath, "html", entityHTML, zos); + fileMetadatum = addFile(tempDirPath, entityFilePath, "html", entityHTML, zos); + fileMetadata.append(fileMetadatum) operationLog.info("--> Entity type: " + type + " permId: " + permId + " post html."); - - zos.close(); - fos.close(); - - #Store on workspace to be able to generate a download link + if emptyZip: + raise IOError('Nothing added to ZIP file.') + return fileMetadata + + +def generateDownloadUrl(sessionToken, tempZipFileName, tempZipFilePath): + dssComponent = DssComponentFactory.tryCreate(sessionToken, OPENBISURL); + + # Store on workspace to be able to generate a download link operationLog.info("Zip file can be found on the temporal directory: " + tempZipFilePath); dssComponent.putFileToSessionWorkspace(tempZipFileName, FileInputStream(File(tempZipFilePath))); tempZipFileWorkspaceURL = DataStoreServer.getConfigParameters().getDownloadURL() + "/datastore_server/session_workspace_file_download?sessionID=" + sessionToken + "&filePath=" + tempZipFileName; operationLog.info("Zip file can be downloaded from the workspace: " + tempZipFileWorkspaceURL); - #Send Email - sendMail(mailClient, userEmail, tempZipFileWorkspaceURL); - #Remove temporal folder and zip - FileUtils.forceDelete(File(tempDirPath)); - FileUtils.forceDelete(File(tempZipFilePath)); - return True + return tempZipFileWorkspaceURL + def getDOCX(entityObj, v3, sessionToken, isHTML): docxBuilder = DOCXBuilder(); @@ -600,12 +593,25 @@ def getTXT(entityObj, v3, sessionToken, isRichText): return txtBuilder.toString(); def addFile(tempDirPath, entityFilePath, extension, fileContent, zos): - entityFile = File(tempDirPath + entityFilePath + "." + extension); + entityFileNameWithExtension = entityFilePath + "." + extension + entityFile = File(tempDirPath + entityFileNameWithExtension); entityFile.getParentFile().mkdirs(); IOUtils.write(fileContent, FileOutputStream(entityFile)); - addToZipFile(entityFilePath + "." + extension, entityFile, zos); + addToZipFile(entityFileNameWithExtension, entityFile, zos); FileUtils.forceDelete(entityFile); + extensionToMimeType = { + 'json': 'application/json', + 'txt': 'text/plain', + 'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'html': 'text/html', + } + + return { + 'fileName': entityFileNameWithExtension[1:], + 'mimeType': extensionToMimeType.get(extension, 'application/octet-stream') + } + def getFilePath(spaceCode, projCode, expCode, sampCode, dataCode): fileName = ""; if spaceCode is not None: @@ -621,27 +627,18 @@ def getFilePath(spaceCode, projCode, expCode, sampCode, dataCode): return fileName; def addToZipFile(path, file, zos): - fis = FileInputStream(file); - zipEntry = ZipEntry(path[1:]); # Making paths relative to make them compatible with Windows zip implementation - zos.putNextEntry(zipEntry); - - bytes = jarray.zeros(1024, "b"); + fis = FileInputStream(file); + zipEntry = ZipEntry(path[1:]); # Making paths relative to make them compatible with Windows zip implementation + zos.putNextEntry(zipEntry); + + bytes = jarray.zeros(1024, "b"); + length = fis.read(bytes); + while length >= 0: + zos.write(bytes, 0, length); length = fis.read(bytes); - while length >= 0: - zos.write(bytes, 0, length); - length = fis.read(bytes); - - zos.closeEntry(); - fis.close(); - -def sendMail(mailClient, userEmail, downloadURL): - replyTo = None; - fromAddress = None; - recipient1 = EMailAddress(userEmail); - topic = "Export Ready"; - message = "Download a zip file with your exported data at: " + downloadURL; - mailClient.sendEmailMessage(topic, message, replyTo, fromAddress, recipient1); - operationLog.info("--- MAIL ---" + " Recipient: " + userEmail + " Topic: " + topic + " Message: " + message); + + zos.closeEntry(); + fis.close(); def getConfigurationProperty(transaction, propertyName): threadProperties = transaction.getGlobalState().getThreadParameters().getThreadProperties(); diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/generalExports.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/generalExports.py new file mode 100644 index 00000000000..4a5845d984b --- /dev/null +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/generalExports.py @@ -0,0 +1,87 @@ +# +# Copyright 2016 ETH Zuerich, Scientific IT Services +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import threading +import time +from ch.systemsx.cisd.common.logging import LogCategory +from ch.systemsx.cisd.common.mail import EMailAddress +from ch.systemsx.cisd.openbis.dss.generic.shared import ServiceProvider +from java.io import File +from org.apache.log4j import Logger + +from exportsApi import generateZipFile, cleanUp, displayResult, findEntitiesToExport, validateDataSize, generateDownloadUrl + +operationLog = Logger.getLogger(str(LogCategory.OPERATION) + ".generalExports.py") + +def process(tr, params, tableBuilder): + method = params.get("method") + isOk = False + + # Set user using the service + + tr.setUserId(userId) + if method == "exportAll": + isOk = expandAndExport(tr, params) + + displayResult(isOk, tableBuilder) + +def expandAndExport(tr, params): + #Services used during the export process + # TO-DO Login on the services as ETL server but on behalf of the user that makes the call + sessionToken = params.get("sessionToken") + v3 = ServiceProvider.getV3ApplicationService() + userEmail = v3.getSessionInformation(sessionToken).getPerson().getEmail() + + entitiesToExport = findEntitiesToExport(params) + validateDataSize(entitiesToExport, tr) + + mailClient = tr.getGlobalState().getMailClient() + includeRoot = params.get("includeRoot") + + operationLog.info("Found " + str(len(entitiesToExport)) + " entities to export, export thread will start") + thread = threading.Thread(target=export, args=(sessionToken, entitiesToExport, includeRoot, userEmail, mailClient)) + thread.daemon = True + thread.start() + + return True + +def export(sessionToken, entities, includeRoot, userEmail, mailClient): + #Create temporal folder + tempDirName = "export_" + str(time.time()) + tempDirPathFile = File.createTempFile(tempDirName, None) + tempDirPathFile.delete() + tempDirPathFile.mkdir() + tempDirPath = tempDirPathFile.getCanonicalPath() + tempZipFileName = tempDirName + ".zip" + tempZipFilePath = tempDirPath + ".zip" + + generateZipFile(entities, includeRoot, sessionToken, tempDirPath, tempZipFilePath) + tempZipFileWorkspaceURL = generateDownloadUrl(sessionToken, tempZipFileName, tempZipFilePath) + + #Send Email + sendMail(mailClient, userEmail, tempZipFileWorkspaceURL) + + cleanUp(tempDirPath, tempZipFilePath) + return True + +def sendMail(mailClient, userEmail, downloadURL): + replyTo = None + fromAddress = None + recipient1 = EMailAddress(userEmail) + topic = "Export Ready" + message = "Download a zip file with your exported data at: " + downloadURL + mailClient.sendEmailMessage(topic, message, replyTo, fromAddress, recipient1) + operationLog.info("--- MAIL ---" + " Recipient: " + userEmail + " Topic: " + topic + " Message: " + message) \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/plugin.properties b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/plugin.properties index 223af21c0cf..e44c1b3b476 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/plugin.properties +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/plugin.properties @@ -1,4 +1,4 @@ label = Exports API class = ch.systemsx.cisd.openbis.dss.generic.server.plugins.jython.JythonIngestionService -script-path = exports-api.py +script-path = generalExports.py limit-data-size-megabytes = 10000 \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/exportsApi.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/exportsApi.py new file mode 120000 index 00000000000..ccb8a49feb3 --- /dev/null +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/exportsApi.py @@ -0,0 +1 @@ +../exports-api/exportsApi.py \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/plugin.properties b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/plugin.properties new file mode 100644 index 00000000000..eabafa539e8 --- /dev/null +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/plugin.properties @@ -0,0 +1,8 @@ +label = Research Collection Exports API +class = ch.systemsx.cisd.openbis.dss.generic.server.plugins.jython.JythonIngestionService +script-path = rcExports.py +limit-data-size-megabytes = 4000 +service-document-url=https://test.research-collection.ethz.ch/swordv2/servicedocument +realm=SWORD2 +user=youruser +password=yourpassword diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/rcExports.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/rcExports.py new file mode 100644 index 00000000000..e354f38dcdd --- /dev/null +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/rcExports.py @@ -0,0 +1,384 @@ +# +# Copyright 2016 ETH Zuerich, Scientific IT Services +# +# Licensed under the Apache License, Version 2.0 (the 'License'); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an 'AS IS' BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +import json +import os +import traceback +import xml.etree.ElementTree as ET + +import datetime +import time +from ch.systemsx.cisd.common.logging import LogCategory +from java.io import File +from java.io import FileOutputStream +from java.net import URI +from java.nio.file import Paths +from java.text import SimpleDateFormat +from java.util import UUID +from java.util.zip import ZipOutputStream +from org.apache.commons.io import FileUtils +from org.apache.log4j import Logger +from org.eclipse.jetty.client import HttpClient +from org.eclipse.jetty.client.util import BasicAuthentication +from org.eclipse.jetty.http import HttpMethod +from org.eclipse.jetty.util.ssl import SslContextFactory + +from exportsApi import displayResult, findEntitiesToExport, validateDataSize, getConfigurationProperty, generateFilesInZip, addToZipFile, cleanUp + +operationLog = Logger.getLogger(str(LogCategory.OPERATION) + '.rcExports.py') + + +def process(tr, params, tableBuilder): + method = params.get('method') + + # Set user using the service + tr.setUserId(userId) + + if method == 'exportAll': + resultUrl = expandAndExport(tr, params) + displayResult(resultUrl is not None, tableBuilder, '{"url": "' + resultUrl + '"}' if resultUrl is not None else None) + elif method == 'getSubmissionTypes': + collectionUrls = getSubmissionTypes(tr) + displayResult(collectionUrls is not None, tableBuilder, collectionUrls) + + +def getSubmissionTypes(tr): + url = getConfigurationProperty(tr, 'service-document-url') + + httpClient = None + try: + httpClient = authenticateUserJava(url, tr) + httpClient.setFollowRedirects(True) + httpClient.start() + + collections = fetchServiceDocument(url, httpClient) + finally: + if httpClient is not None: + httpClient.stop() + return collections + + +def expandAndExport(tr, params): + entitiesToExport = findEntitiesToExport(params) + validateDataSize(entitiesToExport, tr) + + userInformation = { + 'firstName': params.get('userFirstName'), + 'lastName': params.get('userLastName'), + 'email': params.get('userEmail'), + } + + operationLog.info('Found ' + str(len(entitiesToExport)) + ' entities to export') + return export(entities=entitiesToExport, tr=tr, params=params, userInformation=userInformation) + + +def export(entities, tr, params, userInformation): + #Create temporal folder + timeNow = time.time() + + exportDirName = 'export_' + str(timeNow) + exportDir = File.createTempFile(exportDirName, None) + exportDirPath = exportDir.getCanonicalPath() + exportDir.delete() + exportDir.mkdir() + + contentZipFileName = 'content.zip' + contentDirName = 'content_' + str(timeNow) + contentDir = File.createTempFile(contentDirName, None, exportDir) + contentDirPath = contentDir.getCanonicalPath() + contentDir.delete() + contentDir.mkdir() + + contentZipFilePath = exportDirPath + '/' + contentZipFileName + + exportZipFilePath = exportDirPath + '.zip' + exportZipFileName = exportDirName + '.zip' + + generateInternalZipFile(entities, params, contentDirPath, contentZipFilePath) + FileUtils.forceDelete(File(contentDirPath)) + + generateExternalZipFile(params=params, exportDirPath=exportDirPath, contentZipFilePath=contentZipFilePath, contentZipFileName=contentZipFileName, + exportZipFileName=exportZipFilePath, userInformation=userInformation, entities=entities) + resultUrl = sendToDSpace(params=params, tr=tr, tempZipFileName=exportZipFileName, tempZipFilePath=exportZipFilePath) + cleanUp(exportDirPath, exportZipFilePath) + return resultUrl + + +def sendToDSpace(params, tr, tempZipFileName, tempZipFilePath): + depositUrl = str(params.get('submissionUrl')) + + headers = { + 'In-Progress': 'true', + 'Content-Disposition': 'filename=' + tempZipFileName, + 'Content-Type': 'application/zip', + 'Content-Length': os.stat(tempZipFilePath).st_size, + 'Content-Transfer-Encoding': 'binary', + 'Packaging': 'http://purl.org/net/sword/package/METSDSpaceSIP', + 'On-Behalf-Of': str(params.get('userId')), + } + + httpClient = None + try: + httpClient = authenticateUserJava(depositUrl, tr) + + httpClient.setFollowRedirects(True) + httpClient.start() + + request = httpClient.newRequest(depositUrl) + for key, value in headers.iteritems(): + request.header(key, str(value)) + response = request.method(HttpMethod.POST).file(Paths.get(tempZipFilePath), 'application/zip').send() + status = response.getStatus() + if status >= 300: + reason = response.getReason() + raise ValueError('Unsuccessful response from the server: %s %s' % (status, reason)) + + xmlText = response.getContentAsString().encode('utf-8') + xmlRoot = ET.fromstring(xmlText) + linkElement = xmlRoot.find('xmlns:link[@rel="alternate"]', namespaces=dict(xmlns='http://www.w3.org/2005/Atom')) + if linkElement is None: + raise ValueError('No redirection URL is found in the response.') + + href = linkElement.attrib['href'] + + return href + except Exception as e: + operationLog.error('Exception at: ' + traceback.format_exc()) + operationLog.error('Exception: ' + str(e)) + raise e + finally: + if httpClient is not None: + httpClient.stop() + + +def authenticateUserJava(url, tr): + sslContextFactory = SslContextFactory() + httpClient = HttpClient(sslContextFactory) + uri = URI(url) + user = getConfigurationProperty(tr, 'user') + password = getConfigurationProperty(tr, 'password') + realm = getConfigurationProperty(tr, 'realm') + auth = httpClient.getAuthenticationStore() + auth.addAuthentication(BasicAuthentication(uri, realm, user, password)) + return httpClient + + +def fetchServiceDocument(url, httpClient): + response = httpClient.newRequest(url).method(HttpMethod.GET).send() + + xmlText = response.getContentAsString().encode('utf-8') + xmlRoot = ET.fromstring(xmlText) + collections = xmlRoot.findall('./xmlns:workspace/xmlns:collection[@href]', namespaces=dict(xmlns='http://www.w3.org/2007/app')) + + def collectionToDictionaryMapper(collection): + return { + 'title': collection.find('./atom:title', namespaces=dict(atom='http://www.w3.org/2005/Atom')).text, + 'url': collection.attrib['href'], + } + + return json.dumps(map(collectionToDictionaryMapper, collections)) + + +def generateInternalZipFile(entities, params, tempDirPath, tempZipFilePath): + # Generates ZIP file with selected item for export + + sessionToken = params.get('sessionToken') + includeRoot = params.get('includeRoot') + + fos = None + zos = None + try: + fos = FileOutputStream(tempZipFilePath) + zos = ZipOutputStream(fos) + + fileMetadata = generateFilesInZip(zos, entities, includeRoot, sessionToken, tempDirPath) + finally: + if zos is not None: + zos.close() + if fos is not None: + fos.close() + + return fileMetadata + + +def generateExternalZipFile(params, exportDirPath, contentZipFilePath, contentZipFileName, exportZipFileName, userInformation, entities): + # Generates ZIP file which will go to the research collection server + + originUrl=params.get('originUrl') + submissionType = str(params.get('submissionType')) + + fileMetadata = [ + { + 'fileName': contentZipFileName, + 'mimeType': 'application/zip' + } + ] + + fos = None + zos = None + try: + fos = FileOutputStream(exportZipFileName) + zos = ZipOutputStream(fos) + + addToZipFile(' ' + contentZipFileName, File(contentZipFilePath), zos) + + generateXML(zipOutputStream=zos, fileMetadata=fileMetadata, exportDirPath=exportDirPath, submissionType=submissionType, + userInformation=userInformation, entities=entities, originUrl=originUrl) + except Exception as e: + operationLog.error('Exception at: ' + traceback.format_exc()) + operationLog.error('Exception: ' + str(e)) + raise e + finally: + if zos is not None: + zos.close() + if fos is not None: + fos.close() + + +def generateXML(zipOutputStream, fileMetadata, exportDirPath, submissionType, userInformation, entities, originUrl): + ns = { + 'mets': 'http://www.loc.gov/METS/', + 'xlink': 'http://www.w3.org/1999/xlink', + 'dim': 'http://www.dspace.org/xmlns/dspace/dim' + } + + entityPermIds = map(lambda entity: entity['permId'], entities) + permIdsStr = reduce(lambda str, permId: str + ',' + permId, entityPermIds) + + withRegistrationDates = filter(lambda entity: 'registrationDate' in entity, entities) + registrationDates = map(lambda entity: entity['registrationDate'], withRegistrationDates) + + if len(registrationDates) > 0: + minDateStr = javaDateToStr(min(registrationDates, key=lambda date: date.getTime())) + maxDateStr = javaDateToStr(max(registrationDates, key=lambda date: date.getTime())) + else: + minDateStr = None + maxDateStr = None + + metsNS = ns['mets'] + xlinkNS = ns['xlink'] + dimNS = ns['dim'] + ET.register_namespace('mets', metsNS) + ET.register_namespace('xlink', xlinkNS) + ET.register_namespace('dim', dimNS) + + root = ET.Element(ET.QName(metsNS, 'METS')) + root.set('LABEL', 'DSpace Item') + root.set('ID', UUID.randomUUID().toString()) + + dmdSec = ET.SubElement(root, ET.QName(metsNS, 'dmdSec')) + dmdSec.set('GROUPID', 'group_dmd_0') + dmdSec.set('ID', 'dmd_1') + + mdWrap = ET.SubElement(dmdSec, ET.QName(metsNS, 'mdWrap')) + mdWrap.set('MDTYPE', 'OTHER') + mdWrap.set('OTHERMDTYPE', 'DIM') + + xmlData = ET.SubElement(mdWrap, ET.QName(metsNS, 'xmlData')) + + dim = ET.SubElement(xmlData, ET.QName(dimNS, 'dim')) + dim.set('dspaceType', 'ITEM') + + titleField = ET.SubElement(dim, ET.QName(dimNS, 'field')) + titleField.set('mdschema', 'dc') + titleField.set('element', 'title') + titleField.text = str(time.time()) + + typeField = ET.SubElement(dim, ET.QName(dimNS, 'field')) + typeField.set('mdschema', 'dc') + typeField.set('element', 'type') + typeField.text = submissionType + + userIdField = ET.SubElement(dim, ET.QName(dimNS, 'field')) + userIdField.set('mdschema', 'ethz') + userIdField.set('element', 'identifier') + userIdField.set('qualifier', 'openbis') + userIdField.text = permIdsStr + + userInfoField = ET.SubElement(dim, ET.QName(dimNS, 'field')) + userInfoField.set('mdschema', 'dc') + userInfoField.set('element', 'contributor') + userInfoField.set('qualifier', 'author') + userInfoField.text = userInformation['lastName'] + ', ' + userInformation['firstName'] + + publicationDateField = ET.SubElement(dim, ET.QName(dimNS, 'field')) + publicationDateField.set('mdschema', 'dc') + publicationDateField.set('element', 'date') + publicationDateField.set('qualifier', 'issued') + publicationDateField.text = datetime.date.today().strftime('%Y-%m-%d') + + elnLimsURLPattern = '/openbis-test/webapp/eln-lims/?menuUniqueId=null&viewName=' + + for entity in entities: + type = entity['type'] + viewName = '' + if type == 'SPACE': + viewName = 'showSpacePage' + if type == 'PROJECT': + viewName = 'showProjectPageFromPermId' + if type == 'EXPERIMENT': + viewName = 'showExperimentPageFromPermId' + if type == 'SAMPLE': + viewName = 'showViewSamplePageFromPermId' + if type == 'DATASET': + viewName = 'showViewDataSetPageFromPermId' + if type != 'FILE': + urlField = ET.SubElement(dim, ET.QName(dimNS, 'field')) + urlField.set('mdschema', 'ethz') + urlField.set('element', 'identifier') + urlField.set('qualifier', 'url') + urlField.text = originUrl + elnLimsURLPattern + viewName + '&viewData=' + entity['permId'] + + if minDateStr is not None and maxDateStr is not None: + creationDateField = ET.SubElement(dim, ET.QName(dimNS, 'field')) + creationDateField.set('mdschema', 'dc') + creationDateField.set('element', 'date') + creationDateField.set('qualifier', 'created') + creationDateField.text = minDateStr + '/' + maxDateStr if minDateStr != maxDateStr else minDateStr + + fileSec = ET.SubElement(root, ET.QName(metsNS, 'fileSec')) + fileGrp = ET.SubElement(fileSec, ET.QName(metsNS, 'fileGrp')) + fileGrp.set('USE', 'CONTENT') + + i = 0 + for fileMetadatum in fileMetadata: + i += 1 + file = ET.SubElement(fileGrp, ET.QName(metsNS, 'file')) + file.set('ID', 'file_' + str(i)) + fLocat = ET.SubElement(file, ET.QName(metsNS, 'FLocat')) + fLocat.set('LOCTYPE', 'URL') + fLocat.set('MIMETYPE', fileMetadatum.get('mimeType')) + fLocat.set('RETENTIONPERIOD', '10 years') + fLocat.set(ET.QName(xlinkNS, 'href'), fileMetadatum.get('fileName')) + + structMap = ET.SubElement(root, ET.QName(metsNS, 'structMap')) + structMap.set('LABEL', 'DSpace') + structMap.set('TYPE', 'LOGICAL') + div1 = ET.SubElement(structMap, ET.QName(metsNS, 'div')) + div1.set('DMDID', 'dmd_1') + div1.set('TYPE', 'DSpace Item') + + xmlFileName = 'mets.xml' + xmlFilePath = exportDirPath + '/' + xmlFileName + ET.ElementTree(root).write(xmlFilePath) + + xmlFile = File(xmlFilePath) + addToZipFile(' ' + xmlFileName, xmlFile, zipOutputStream) + # Space is added to the file name because the method chops out the first character + + +def javaDateToStr(javaDate): + dateFormat = SimpleDateFormat('yyyy-MM-dd') + return dateFormat.format(javaDate) -- GitLab