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 Binary files a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls and b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/master-data/data-model.xls differ 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 0000000000000000000000000000000000000000..76d5aa52bab6f89a78a2e2bd11542e728466b26d --- /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 0000000000000000000000000000000000000000..cc5c7e99fe2eb0518d370c82b61ccc04bee4cc2c --- /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/js/server/ServerFacade.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js index 18631b75318635184942854a3e1f2056b39e984f..b9f816840a1cef0a7b3966db54cb871d96a7c496 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 @@ -307,12 +307,58 @@ function ServerFacade(openbisServer) { // // Research collection export // - this.exportRc = function(entities, includeRoot, metadataOnly, callbackFunction) { + 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" : "exportAll", - "includeRoot" : includeRoot, - "entities" : entities, - "metadataOnly" : metadataOnly, + "method": "getSubmissionTypes", }, callbackFunction, "rc-exports-api"); }; @@ -671,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; @@ -694,6 +739,7 @@ function ServerFacade(openbisServer) { } this.customELNApi = function(parameters, callbackFunction, service) { + var _this = this; if(!service) { service = "eln-lims-api"; } @@ -705,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"); @@ -2575,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 e501e670936df304c7e03f164f910f7fbb453845..9658d70d42f076133974dd2075ce5654b55951de 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 f09bd1a1cf9355bcbac788de1f49c8f923ad59f8..7bbbd3de8e2eb73f3e5c18fc7f186fdeb7dd50fb 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 index 5424e879bc64f561f63f1b8fed3ff7a6ba0d4f61..514b6a15d2dcc03336b6c499ac7ad13528fbe454 100644 --- 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 @@ -15,7 +15,6 @@ */ function ResearchCollectionExportController(parentController) { - var parentController = parentController; var researchCollectionExportModel = new ResearchCollectionExportModel(); var researchCollectionExportView = new ResearchCollectionExportView(this, researchCollectionExportModel); @@ -23,27 +22,106 @@ function ResearchCollectionExportController(parentController) { 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 }); + toExport.push({type: node.data.entityType, permId: node.key, expand: !node.expanded}); } if (toExport.length === 0) { - Util.showInfo("First select something to export."); + 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.'); + } + } + }); + }); + }); } + }; - Util.blockUI(); - mainController.serverFacade.exportRc(toExport, true, false, function(error, result) { - if (error) { - Util.showError(error); - } else { - Util.showSuccess("Export is being processed. If you logout the process will stop.", function() { Util.unblockUI(); }); - mainController.refreshView(); - } + 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 index 3f99119599869a116cc69f7df2461f6beb366c98..7099e83d87552bbebcdff0b2788f00a34d18d6e1 100644 --- 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 @@ -15,5 +15,9 @@ */ 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 index a4ce7b4d9ac9a3de31dbb603a650cd00df06f1f6..7945f200cb3b4bdf5591f784dbeff1a6335ce1f0 100644 --- 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 @@ -15,38 +15,38 @@ */ function ResearchCollectionExportView(researchCollectionExportController, researchCollectionExportModel) { - var researchCollectionExportController = researchCollectionExportController; - var researchCollectionExportModel = 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();' + 'role': 'form', + 'action': 'javascript:void(0);', + 'onsubmit': 'mainController.currentView.exportSelected();' }); - $form.append($formColumn); - var $infoBox = FormUtil.getInfoBox("You can select any parts of the accesible 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." + 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"); + $infoBox.css('border', 'none'); $container.append($infoBox); - var $tree = $("<div>", { "id" : "exportsTree" }); - $formColumn.append($("<br>")); + 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'); @@ -55,5 +55,21 @@ function ResearchCollectionExportView(researchCollectionExportController, resear 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/dss/reporting-plugins/exports-api/exportsApi.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/exportsApi.py index 0180a55e830b6d3c02ed5b79582674cca31b961f..932c0fa302302c30bd0dc8174b5fd680c62a0707 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/exportsApi.py +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/exports-api/exportsApi.py @@ -108,8 +108,7 @@ def strip_tags(html): return s.get_data() -def displayResult(isOk, tableBuilder): - result = None; +def displayResult(isOk, tableBuilder, result=None): if isOk: tableBuilder.addHeader("STATUS"); tableBuilder.addHeader("MESSAGE"); @@ -167,11 +166,12 @@ def findEntitiesToExport(params): entities = params.get("entities"); for entity in entities: - entityAsPythonMap = {"type": entity.get("type"), "permId": entity.get("permId"), "expand": entity.get("expand")}; - addToExportWithoutRepeating(entitiesToExport, entityAsPythonMap); + entityAsPythonMap = { "type" : entity.get("type"), "permId" : entity.get("permId"), "expand" : entity.get("expand") }; + 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"]; @@ -183,7 +183,7 @@ def findEntitiesToExport(params): 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": @@ -192,7 +192,7 @@ def findEntitiesToExport(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": @@ -201,7 +201,8 @@ def findEntitiesToExport(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": @@ -213,25 +214,29 @@ def findEntitiesToExport(params): 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: @@ -254,19 +259,28 @@ def cleanUp(tempDirPath, 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) + + 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); objectCache = {}; objectMapper = GenericObjectMapper(); objectMapper.enable(SerializationFeature.INDENT_OUTPUT); - # To avoid empty directories on the zip file, it makes the first found entity the base directory baseDirToCut = None; + fileMetadata = [] + emptyZip = True - # Create Zip File - fos = FileOutputStream(tempZipFilePath); - zos = ZipOutputStream(fos); for entity in entities: type = entity["type"]; permId = entity["permId"]; @@ -357,6 +371,7 @@ def generateZipFile(entities, includeRoot, sessionToken, tempDirPath, tempZipFil rawFile.getParentFile().mkdirs(); IOUtils.copyLarge(rawFileInputStream, FileOutputStream(rawFile)); addToZipFile(filePath, rawFile, zos); + emptyZip = False # To avoid empty directories on the zip file, it makes the first found entity the base directory if not includeRoot: @@ -374,20 +389,25 @@ def generateZipFile(entities, includeRoot, sessionToken, tempDirPath, tempZipFil if entityObj is not None and entityFilePath is not None: # JSON entityJson = String(objectMapper.writeValueAsString(entityObj)); - addFile(tempDirPath, entityFilePath, "json", entityJson.getBytes(), zos); + 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); + fileMetadatum = addFile(tempDirPath, entityFilePath, "txt", entityTXT.getBytes(), zos); + fileMetadata.append(fileMetadatum) # DOCX entityDOCX = getDOCX(entityObj, v3, sessionToken, False); - addFile(tempDirPath, entityFilePath, "docx", entityDOCX, zos); + 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(); + if emptyZip: + raise IOError('Nothing added to ZIP file.') + return fileMetadata def generateDownloadUrl(sessionToken, tempZipFileName, tempZipFilePath): @@ -573,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: @@ -594,18 +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(); + + 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/rc-exports-api/plugin.properties b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/rc-exports-api/plugin.properties index 16b4f674e2cf42167bc0e424c4c15e2059e1bf81..eabafa539e8a00741e999e6ce7b45bbb7eb62ed9 100644 --- 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 @@ -4,5 +4,5 @@ script-path = rcExports.py limit-data-size-megabytes = 4000 service-document-url=https://test.research-collection.ethz.ch/swordv2/servicedocument realm=SWORD2 -user= -password= \ No newline at end of file +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 index dbc1a7a71e9494e28529b5e369140d0c6b9ba215..e354f38dcdd770f6a8b98b765556fc936ad5dfab 100644 --- 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 @@ -1,103 +1,384 @@ # # Copyright 2016 ETH Zuerich, Scientific IT Services # -# Licensed under the Apache License, Version 2.0 (the "License"); +# 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, +# 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 -import urllib2 +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 ch.systemsx.cisd.openbis.dss.generic.shared import ServiceProvider 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 generateZipFile, cleanUp, displayResult, findEntitiesToExport, validateDataSize, getConfigurationProperty +from exportsApi import displayResult, findEntitiesToExport, validateDataSize, getConfigurationProperty, generateFilesInZip, addToZipFile, cleanUp -operationLog = Logger.getLogger(str(LogCategory.OPERATION) + ".rcExports.py") +operationLog = Logger.getLogger(str(LogCategory.OPERATION) + '.rcExports.py') def process(tr, params, tableBuilder): - method = params.get("method") - isOk = False + method = params.get('method') # Set user using the service - tr.setUserId(userId) - if method == "exportAll": - isOk = expandAndExport(tr, params) - displayResult(isOk, tableBuilder) + 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 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() +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) - includeRoot = params.get("includeRoot") - - operationLog.info("Found " + str(len(entitiesToExport)) + " entities to export, export thread will start") - thread = threading.Thread(target=export, args=(tr, sessionToken, entitiesToExport, includeRoot)) - thread.daemon = True - thread.start() + userInformation = { + 'firstName': params.get('userFirstName'), + 'lastName': params.get('userLastName'), + 'email': params.get('userEmail'), + } - return True + operationLog.info('Found ' + str(len(entitiesToExport)) + ' entities to export') + return export(entities=entitiesToExport, tr=tr, params=params, userInformation=userInformation) -def export(tr, sessionToken, entities, includeRoot): +def export(entities, tr, params, userInformation): #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" + 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 - generateZipFile(entities, includeRoot, sessionToken, tempDirPath, tempZipFilePath) - #Send Email - sendToDSpace(tr, tempZipFileName, tempZipFilePath) +def sendToDSpace(params, tr, tempZipFileName, tempZipFilePath): + depositUrl = str(params.get('submissionUrl')) - cleanUp(tempDirPath, tempZipFilePath) - return True + 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) -def sendToDSpace(tr, tempZipFileName, tempZipFilePath): - collectionUrls = fetchServiceDocument(tr) - print('URLs: %s' % collectionUrls) - print('URLs count: %s' % len(collectionUrls)) + httpClient.setFollowRedirects(True) + httpClient.start() -def fetchServiceDocument(tr): + 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') - url = getConfigurationProperty(tr, 'service-document-url') - pm = urllib2.HTTPPasswordMgr() - pm.add_password(realm, url, user, password) - authHandler = urllib2.HTTPBasicAuthHandler(pm) - opener = urllib2.build_opener(authHandler) - urllib2.install_opener(opener) - response = urllib2.urlopen(url) - xmlText = response.read() + 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')) - collectionUrls = map(lambda collection: collection.attrib['href'], collections) - return collectionUrls + 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)