diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/ServiceProvider.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/ServiceProvider.java index 10c3304f8c6632ef60fba8d73cae168e06d42c68..ce8fd97fbac6ae13180a5026ea4403ee32cf33a6 100644 --- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/ServiceProvider.java +++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/ServiceProvider.java @@ -138,6 +138,11 @@ public class ServiceProvider return ((IApplicationServerApi) getApplicationContext().getBean(V3_APPLICATION_SERVICE_BEAN)); } + public static IDataStoreServerApi getV3DataStoreService() + { + return ((IDataStoreServerApi) getApplicationContext().getBean(DataStoreServerApi.INTERNAL_SERVICE_NAME)); + } + public static IGeneralInformationService getGeneralInformationService() { return ((IGeneralInformationService) getApplicationContext().getBean( diff --git a/integration-tests/TEST_PYTHON_LIBRARIES_LOCALLY b/integration-tests/TEST_PYTHON_LIBRARIES_LOCALLY new file mode 100644 index 0000000000000000000000000000000000000000..025024e43c90b61c001bfd13aa4bcc7a55f0fd69 --- /dev/null +++ b/integration-tests/TEST_PYTHON_LIBRARIES_LOCALLY @@ -0,0 +1,17 @@ +# add the source folder to the PYTHONPATH: + +export PYTHONPATH=/Users/vermeul/openbis/integration-tests/source:/Users/vermeul/openbis/integration-tests/sourceTest:$PYTHONPATH + +# make sure you test the develop-version of pybis or obis + +cd ~/openbis/pybis/src/python +pip install -e . + +# ... and prevent this test-suite to install pybis from some source +vim openbis/integration-tests/source/test_pybis.py + +# on line 34, make sure that + + #self.installPybis() + +# is commented out, otherwise your installation is being overwritten diff --git a/integration-tests/source/systemtest/testcase.py b/integration-tests/source/systemtest/testcase.py index 1bb35a7792e50c1cec256efb97a6aa25e22ae235..63d1fa64f3c76dd8ce76c490bd36325bdcd7d04f 100644 --- a/integration-tests/source/systemtest/testcase.py +++ b/integration-tests/source/systemtest/testcase.py @@ -280,17 +280,20 @@ class TestCase(object): return ScreeningTestClient(self, installPath) def installPybis(self): - zipFile = self.artifactRepository.getPathToArtifact(OPENBIS_STANDARD_TECHNOLOGIES_PROJECT, 'pybis-') - installPath = "%s/pybis" % self.playgroundFolder - util.unzip(zipFile, installPath) - util.executeCommand(['pip', 'install', installPath + '/src/python'], "Installation of pybis failed.") + #zipFile = self.artifactRepository.getPathToArtifact(OPENBIS_STANDARD_TECHNOLOGIES_PROJECT, 'pybis-') + #installPath = "%s/pybis" % self.playgroundFolder + #util.unzip(zipFile, installPath) + #util.executeCommand(['pip', 'install', installPath + '/src/python'], "Installation of pybis failed.") + # install the local pybis in editable-mode (-e) + util.executeCommand(['pip', 'install', '-e', '../pybis/src/python'], "Installation of pybis failed.") def installObis(self): - zipFile = self.artifactRepository.getPathToArtifact(OPENBIS_STANDARD_TECHNOLOGIES_PROJECT, 'obis-') - installPath = "%s/obis" % self.playgroundFolder - util.unzip(zipFile, installPath) - print('pip install ' + installPath + '/src/python') - util.executeCommand(['pip', 'install', installPath + '/src/python'], "Installation of obis failed.") + #zipFile = self.artifactRepository.getPathToArtifact(OPENBIS_STANDARD_TECHNOLOGIES_PROJECT, 'obis-') + #installPath = "%s/obis" % self.playgroundFolder + #util.unzip(zipFile, installPath) + #print('pip install ' + installPath + '/src/python') + #util.executeCommand(['pip', 'install', installPath + '/src/python'], "Installation of obis failed.") + util.executeCommand(['pip', 'install', '-e', '../obis/src/python'], "Installation of pybis failed.") def getTemplatesFolder(self): @@ -815,4 +818,4 @@ class OpenbisController(_Controller): for line in lines: f.write("%s\n" % line) - \ No newline at end of file + diff --git a/integration-tests/test_pybis.py b/integration-tests/test_pybis.py index 34a6e06daff835b51b97e13083965219f756fcf5..12e38db131349e0ae71299617c2e096ee3b114d2 100755 --- a/integration-tests/test_pybis.py +++ b/integration-tests/test_pybis.py @@ -31,7 +31,7 @@ class TestCase(systemtest.testcase.TestCase): self.FILE = 'TEST_FILE_' + str(randrange(100000)) self.installOpenbis() - #self.installPybis() + self.installPybis() self.openbisController = self.createOpenbisController() self.openbisController.createTestDatabase("openbis") self.openbisController.allUp() diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/SingleSignOnServlet.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/SingleSignOnServlet.java index f370f8c7d41928d8d4bfcb43303f671e0bec0faf..dd45e86850433efcc9ddaddbbc062192444d829e 100644 --- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/SingleSignOnServlet.java +++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/SingleSignOnServlet.java @@ -17,6 +17,8 @@ package ch.ethz.sis.openbis.generic.server; import java.io.IOException; +import java.util.Arrays; +import java.util.Enumeration; import java.util.HashMap; import java.util.Map; @@ -108,6 +110,18 @@ public class SingleSignOnServlet extends AbstractServlet { String sessionId = getHeader(request, SESSION_ID_KEY, DEFAULT_SESSION_ID_KEY); String sessionToken = sessionTokenBySessionId.get(sessionId); + String returnURL = request.getParameter("return"); + if (returnURL != null) + { + handleLogOut(request, response, sessionId, sessionToken, returnURL); + } else + { + handleLogIn(request, response, sessionId, sessionToken); + } + } + + private void handleLogIn(HttpServletRequest request, HttpServletResponse response, String sessionId, String sessionToken) throws IOException + { if (sessionToken != null) { Session session = sessionManager.tryGetSession(sessionToken); @@ -138,13 +152,40 @@ public class SingleSignOnServlet extends AbstractServlet redirectToApp(request, response, sessionToken); } - protected void redirectToApp(HttpServletRequest request, HttpServletResponse response, String sessionToken) throws IOException + private void handleLogOut(HttpServletRequest request, HttpServletResponse response, String sessionId, String sessionToken, String returnURL) + throws IOException + { + operationLog.info("log out session id: " + sessionId); + if (sessionToken != null) + { + Session session = sessionManager.tryGetSession(sessionToken); + if (session != null) + { + sessionManager.closeSession(sessionToken); + operationLog.info("Session " + sessionToken + " closed."); + } + } + operationLog.info("Redirect to " + returnURL); + removeOpenbisCookies(request, response); + response.sendRedirect(returnURL); + } + + private void redirectToApp(HttpServletRequest request, HttpServletResponse response, String sessionToken) throws IOException { String host = request.getHeader("X-Forwarded-Host"); Template template = this.template.createFreshCopy(); template.bind("host", host); String redirectUrl = configurer.getResolvedProps().getProperty(REDIRECT_URL_KEY, template.createText()); operationLog.info("redirect to " + redirectUrl); + removeOpenbisCookies(request, response); + Cookie cookie = new Cookie("openbis", sessionToken); + cookie.setPath("/"); + response.addCookie(cookie); + response.sendRedirect(redirectUrl); + } + + private void removeOpenbisCookies(HttpServletRequest request, HttpServletResponse response) + { Cookie[] cookies = request.getCookies(); for (Cookie cookie : cookies) { @@ -156,10 +197,6 @@ public class SingleSignOnServlet extends AbstractServlet response.addCookie(cookie); } } - Cookie cookie = new Cookie("openbis", sessionToken); - cookie.setPath("/"); - response.addCookie(cookie); - response.sendRedirect(redirectUrl); } private String getHeader(HttpServletRequest request, String keyProperty, String defaultKey) diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/search/DefaultFullTextIndexer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/search/DefaultFullTextIndexer.java index 0d1b6ac00d43e9d33828be7ec3f63b20680905df..3060eb00acdbc0899db194ff5b5d9627951c500f 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/search/DefaultFullTextIndexer.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/search/DefaultFullTextIndexer.java @@ -148,6 +148,9 @@ final class DefaultFullTextIndexer implements IFullTextIndexer List<Long> subList = ids.subList(index, nextIndex); final List<T> results = listEntitiesWithRestrictedId(fullTextSession, clazz, subList); + if (subList.size() != results.size()) { + operationLog.error(String.format("The system tried to index %d but only found %d on the database.", subList.size(), results.size())); + } indexEntities(fullTextSession, results); index = nextIndex; operationLog.info(String.format("%d/%d %ss have been reindexed...", index, diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/BasicConstant.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/BasicConstant.java index 3416d16c13051a66faa0542a7bdfd38f2f0a08c1..978e14e2ef7b11b458fb26acc9a3fc595ff29e59 100644 --- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/BasicConstant.java +++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/BasicConstant.java @@ -93,7 +93,7 @@ public class BasicConstant /** * Canonical date format pattern used to render dates in GUI in a more readable way. */ - public static final String RENDERED_CANONICAL_DATE_FORMAT_PATTERN = "yyyy-MM-dd HH:mm:ss ZZZ"; + public static final String RENDERED_CANONICAL_DATE_FORMAT_PATTERN = "yyyy-MM-dd HH:mm:ss Z"; /** * Date format which does not include time zone. diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/initialize-master-data.py b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/initialize-master-data.py index f682ae31ed44532ac9bccc4a6c47c2d71b803c2c..d76ed8acd11035a28bd2b1b8c72d737f68e93147 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/initialize-master-data.py +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/initialize-master-data.py @@ -16,7 +16,6 @@ # MasterDataRegistrationTransaction Class from ch.ethz.sis.openbis.generic.server.asapi.v3 import ApplicationServerApi from ch.systemsx.cisd.openbis.generic.server import CommonServiceProvider -from ch.systemsx.cisd.openbis.generic.server import ComponentNames from ch.ethz.sis.openbis.generic.asapi.v3.dto.service.id import CustomASServiceCode from ch.ethz.sis.openbis.generic.asapi.v3.dto.service import CustomASServiceExecutionOptions from ch.systemsx.cisd.openbis.generic.server.jython.api.v1.impl import MasterDataRegistrationHelper @@ -25,19 +24,10 @@ import sys helper = MasterDataRegistrationHelper(sys.path) api = CommonServiceProvider.getApplicationContext().getBean(ApplicationServerApi.INTERNAL_SERVICE_NAME) sessionToken = api.loginAsSystem() -props = CustomASServiceExecutionOptions().withParameter('xls', helper.listXlsByteArrays())\ - .withParameter('xls_name', 'ELN-LIMS-LIFE-SCIENCES').withParameter('update_mode', 'IGNORE_EXISTING')\ +props = CustomASServiceExecutionOptions().withParameter('xls', helper.listXlsByteArrays()) \ + .withParameter('xls_name', 'ELN-LIMS-LIFE-SCIENCES').withParameter('update_mode', 'IGNORE_EXISTING') \ .withParameter('scripts', helper.getAllScripts()) -result = api.executeCustomASService(sessionToken, CustomASServiceCode("xls-import-api"), props); -# Updating MULTILINE_VARCHAR to use "Word Processor" for all Properties -daoFactory = CommonServiceProvider.getApplicationContext().getBean(ComponentNames.DAO_FACTORY); -currentSession = daoFactory.getSessionFactory().getCurrentSession(); -SQL = "UPDATE property_types SET meta_data = cast(:meta_data AS jsonb) WHERE id IN (SELECT id FROM property_types WHERE daty_id = (SELECT id FROM data_types WHERE code = 'MULTILINE_VARCHAR'))"; -meta_data = "{ \"custom_widget\" : \"Word Processor\" }"; -sqlQuery = currentSession.createSQLQuery(SQL); -sqlQuery.setParameter("meta_data", meta_data); -sqlQuery.executeUpdate(); -# +result = api.executeCustomASService(sessionToken, CustomASServiceCode("xls-import-api"), props) print("======================== master-data xls ingestion result ========================") print(result) print("======================== master-data xls ingestion result ========================") diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/master-data/data-model.xls b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/master-data/data-model.xls index b6725cb9c04a7e5c2b7e09d1e0be48893d3fa778..e2755043112873eaa41408f7a01ab4e152444d93 100644 Binary files a/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/master-data/data-model.xls and b/openbis_standard_technologies/dist/core-plugins/eln-lims-life-sciences/1/as/master-data/data-model.xls differ 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 4d69110b10b17aa7e664af2f3bc8fc636427f53f..c061f53d70d87bbbb9cf15446655855d2d1bf0ce 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/publication-api.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/services/publication-api/publication-api.py index b898b2b7b3fb242a979ca33933c46a083d92c1cf..f4e5c0d716dbd1b4bf84461a4a7a6646b2db7184 100644 --- 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 @@ -89,7 +89,7 @@ def getDefaultDataStoreCode(v3, sessionToken): def createPublicationSample(parameters, sessionToken, v3): publicationOrganization = parameters.get('publicationOrganization') if publicationOrganization is None: - raise ValueError('publicationOrganization parameter is None.') + publicationOrganization = '' name = parameters.get('name') if name is None: 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 2ed7d10ad30493b50d6f2ef725993e9b5a61454e..aa154b6a24013efa7ce3540fbf74cd1cec3f32bc 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 @@ -258,6 +258,10 @@ <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/ZenodoExport/ZenodoExportController.js"></script> + <script type="text/javascript" src="./js/views/ZenodoExport/ZenodoExportModel.js"></script> + <script type="text/javascript" src="./js/views/ZenodoExport/ZenodoExportView.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/config/Profile.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/Profile.js index b713b303923d04fa508d679c6db1851f4d2ce138..8b1a0d7ebb7f7f357ea1016d155394606e58f0fd 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/Profile.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/Profile.js @@ -135,6 +135,7 @@ $.extend(DefaultProfile.prototype, { // "ADMIN-BS-MBPR28.D.ETHZ.CH-E96954A7" : "http://localhost:8080/download" } this.singleSignOnUrlTemplate = null; + this.singleSignOnUrlLogoutTemplate = null; this.singleSignOnLinkLabel = 'Single Sign On Login'; this.customWidgetSettings = {}; 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 142198f6bab65d4da0dce1cf78171022e97001b4..1294aec2ba78fe8eec0fbf4992a436edea3db223 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 @@ -389,6 +389,13 @@ function MainController(profile) { newResearchCollectionExportView.init(researchCollectionExportViews); this.currentView = newResearchCollectionExportView; break; + case "showZenodoExportPage": + document.title = "Zenodo Export Builder"; + var newZenodoExportView = new ZenodoExportController(this); + var zenodoExportViews = this._getNewViewModel(true, true, false); + newZenodoExportView.init(zenodoExportViews); + this.currentView = newZenodoExportView; + 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 10486e4a5bbe79d136de70edc36f315712adf67a..96e277604e4a4ba2387881404142543ed6e52463 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 @@ -396,6 +396,51 @@ function ServerFacade(openbisServer) { }); }; + this.exportZenodo = function(entities, includeRoot, metadataOnly, userInformation, callbackFunction) { + this.asyncExportZenodo({ + "method": "exportAll", + "includeRoot": includeRoot, + "entities": entities, + "metadataOnly": metadataOnly, + "userInformation": userInformation, + "originUrl": window.location.origin, + "sessionToken": this.openbisServer.getSession(), + }, callbackFunction, "zenodo-exports-api"); + }; + + this.asyncExportZenodo = 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 dataStoreCode = profile.getDefaultDataStoreCode(); + var dataStoreId = new DataStorePermId(dataStoreCode); + 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 // @@ -1476,33 +1521,70 @@ function ServerFacade(openbisServer) { }); } + this.getResultsWithBrokenEqualsFix = function(hackFixForBrokenEquals, results, operator) { + if (!operator) { + operator = "AND"; + } + + if(hackFixForBrokenEquals.length > 0 && results) { + var filteredResults = []; + var resultsValid = new Array(results.length); + for (var vIdx = 0; vIdx < resultsValid.length; vIdx++) { + switch(operator) { + case "AND": + resultsValid[vIdx] = true; + break; + case "OR": + resultsValid[vIdx] = false; + break; + } + } + + for(var rIdx = 0; rIdx < results.length; rIdx++) { + var result = results[rIdx]; + for(var fIdx = 0; fIdx < hackFixForBrokenEquals.length; fIdx++) { + if( result && + result.properties && + result.properties[hackFixForBrokenEquals[fIdx].propertyCode] === hackFixForBrokenEquals[fIdx].value) { + switch(operator) { + case "AND": + resultsValid[rIdx] = resultsValid[rIdx] && true; + break; + case "OR": + resultsValid[rIdx] = resultsValid[rIdx] || true; + break; + } + } else { + switch(operator) { + case "AND": + resultsValid[rIdx] = resultsValid[rIdx] && false; + break; + case "OR": + resultsValid[rIdx] = resultsValid[rIdx] || false; + break; + } + } + } + } + + for(var rIdx = 0; rIdx < results.length; rIdx++) { + if(resultsValid[rIdx]) { + filteredResults.push(results[rIdx]); + } + } + + results = filteredResults; + } + + return results; + } + this.searchForEntityAdvanced = function(advancedSearchCriteria, advancedFetchOptions, callback, criteriaClass, fetchOptionsClass, searchMethodName) { + var _this = this; var searchFunction = function(searchCriteria, fetchOptions, hackFixForBrokenEquals) { mainController.openbisV3[searchMethodName](searchCriteria, fetchOptions) .done(function(apiResults) { - // - // Fix For broken equals PART 2 - // - var results = apiResults.objects; - var filteredResults = []; - if(hackFixForBrokenEquals.length > 0 && results) { - for(var rIdx = 0; rIdx < results.length; rIdx++) { - var result = results[rIdx]; - for(var fIdx = 0; fIdx < hackFixForBrokenEquals.length; fIdx++) { - if( result && - result.properties && - result.properties[hackFixForBrokenEquals[fIdx].propertyCode] === hackFixForBrokenEquals[fIdx].value) { - filteredResults.push(result); - } - } - } - } else { - filteredResults = results; - } - apiResults.objects = filteredResults; - // - // Fix For broken equals PART 2 - END - // + apiResults.objects = _this.getResultsWithBrokenEqualsFix(hackFixForBrokenEquals, apiResults.objects, advancedSearchCriteria.logicalOperator); callback(apiResults); }) .fail(function(result) { @@ -1825,14 +1907,14 @@ function ServerFacade(openbisServer) { var localReference = this; // - // Fix For broken equals PART 1 + // Collection Rules for broken equals Fix // Currently the back-end matches whole words instead doing a standard EQUALS // This fixes some most used cases for the storage system, but other use cases that use subcriterias can fail // var hackFixForBrokenEquals = []; if(sampleCriteria.matchClauses) { for(var cIdx = 0; cIdx < sampleCriteria.matchClauses.length; cIdx++) { - if(sampleCriteria.matchClauses[cIdx]["@type"] === "PropertyMatchClause" && + if(sampleCriteria.matchClauses[cIdx]["@type"] === "PropertyMatchClause" && sampleCriteria.matchClauses[cIdx]["compareMode"] === "EQUALS") { hackFixForBrokenEquals.push({ propertyCode : sampleCriteria.matchClauses[cIdx].propertyCode, @@ -1844,32 +1926,11 @@ function ServerFacade(openbisServer) { // // Fix For broken equals PART 1 - END // - + var _this = this; this.openbisServer.searchForSamplesWithFetchOptions(sampleCriteria, options, function(data) { var results = localReference.getInitializedSamples(data.result); - // - // Fix For broken equals PART 2 - // - var filteredResults = []; - if(hackFixForBrokenEquals.length > 0 && results) { - for(var rIdx = 0; rIdx < results.length; rIdx++) { - var result = results[rIdx]; - for(var fIdx = 0; fIdx < hackFixForBrokenEquals.length; fIdx++) { - if( result && - result.properties && - result.properties[hackFixForBrokenEquals[fIdx].propertyCode] === hackFixForBrokenEquals[fIdx].value) { - filteredResults.push(result); - } - } - } - } else { - filteredResults = results; - } - // - // Fix For broken equals PART 2 - END - // - - callbackFunction(filteredResults); + results = _this.getResultsWithBrokenEqualsFix(hackFixForBrokenEquals, results, "AND"); + callbackFunction(results); }); } 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 9658d70d42f076133974dd2075ce5654b55951de..19a34eb8422cac3228815ef88103b4e019eafa83 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 @@ -1176,7 +1176,17 @@ var FormUtil = new function() { } this._getDropboxFolderName = function(nameElements, dataSetTypeCode, name) { - var folderName = nameElements.join("+"); + var folderName = ""; + + for(var nIdx = 0; nIdx < nameElements.length; nIdx++) { + if(nameElements[nIdx]) { + if(nIdx !== 0) { + folderName += "+"; + } + folderName += nameElements[nIdx]; + } + } + for (var optionalPart of [dataSetTypeCode, name]) { if (optionalPart) { folderName += "+" + optionalPart; 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 514b6a15d2dcc03336b6c499ac7ad13528fbe454..df2a9b1fce2acd81bf794a0f7702ad527bde11a2 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 @@ -22,7 +22,7 @@ function ResearchCollectionExportController(parentController) { researchCollectionExportView.repaint(views); }; - this.initialiseSubmissionTypesDropdown = function(callback) { + this.initialiseSubmissionTypesDropdown = function() { Util.blockUI(); mainController.serverFacade.listSubmissionTypes(function(error, result) { Util.unblockUI(); @@ -56,6 +56,8 @@ function ResearchCollectionExportController(parentController) { if (toExport.length === 0) { Util.showInfo('First select something to export.'); + } else if (!this.isValid(toExport)) { + Util.showInfo('Not only spaces and the root should be selected. It will result in an empty export file.'); } else if (!submissionUrl) { Util.showInfo('First select submission type.'); } else { @@ -82,6 +84,16 @@ function ResearchCollectionExportController(parentController) { } }; + this.isValid = function(toExport) { + for (var i = 0; i < toExport.length; i++) { + var value = toExport[i]; + if (value.type !== 'ROOT' && value.type !== 'SPACE' || value.expand) { + return true; + } + } + return false; + }; + this.waitForOpExecutionResponse = function(operationExecutionPermIdString, callbackFunction) { var _this = this; require(["as/dto/operation/id/OperationExecutionPermId", 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 7945f200cb3b4bdf5591f784dbeff1a6335ce1f0..fa97054bbcc0ef9b17d3cc9d3cdb4c2e1ff6beae 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,8 +15,6 @@ */ function ResearchCollectionExportView(researchCollectionExportController, researchCollectionExportModel) { - var exportTreeView = new ExportTreeView(researchCollectionExportController, researchCollectionExportModel); - this.repaint = function(views) { researchCollectionExportController.initialiseSubmissionTypesDropdown(); @@ -52,7 +50,7 @@ function ResearchCollectionExportView(researchCollectionExportController, resear var $formTitle = $('<h2>').append('Research Collection Export Builder'); $header.append($formTitle); - var $exportButton = $('<input>', { 'type': 'submit', 'class': 'btn btn-primary', 'value': 'Export Selected', + var $exportButton = $('<input>', {'type': 'submit', 'class': 'btn btn-primary', 'value': 'Export Selected', 'onClick': '$("form[name=\'rcExportForm\']").submit()'}); $header.append($exportButton); }; diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SampleForm/SampleFormView.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SampleForm/SampleFormView.js index 5f1a59eb228352e90f692b6a428337bb6204f7a8..a4b303cd98b51654a4aa5817221b0ef10342324e 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SampleForm/SampleFormView.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SampleForm/SampleFormView.js @@ -361,8 +361,6 @@ function SampleFormView(sampleFormController, sampleFormModel) { sample = results.objects[rIdx]; } } - _this._sampleFormModel.sample.properties = sample.properties; - delete _this._sampleFormModel.sample.properties[profile.propertyReplacingCode]; if(_this._sampleFormModel.views.header) { _this._sampleFormModel.views.header.empty(); @@ -1245,7 +1243,7 @@ function SampleFormView(sampleFormController, sampleFormModel) { this._allowedToCreateChild = function() { var sample = this._sampleFormModel.v3_sample; - return sample.frozenForChildren == false && sample.experiment.frozenForSamples == false; + return sample.frozenForChildren == false && (!sample.experiment || sample.experiment.frozenForSamples == false); } this._allowedToEdit = function() { @@ -1255,21 +1253,21 @@ function SampleFormView(sampleFormController, sampleFormModel) { this._allowedToMove = function() { var sample = this._sampleFormModel.v3_sample; - return sample.experiment.frozenForSamples == false; + return !sample.experiment || sample.experiment.frozenForSamples == false; } this._allowedToDelete = function() { var sample = this._sampleFormModel.v3_sample; - return sample.frozen == false && sample.experiment.frozenForSamples == false; + return sample.frozen == false && (!sample.experiment || sample.experiment.frozenForSamples == false); } this._allowedToCopy = function() { var sample = this._sampleFormModel.v3_sample; - return sample.experiment.frozenForSamples == false; + return !sample.experiment || sample.experiment.frozenForSamples == false; } this._allowedToRegisterDataSet = function() { var sample = this._sampleFormModel.v3_sample; - return sample.frozenForDataSets == false && sample.experiment.frozenForDataSets == false; + return sample.frozenForDataSets == false && (!sample.experiment || sample.experiment.frozenForDataSets == false); } } \ 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/SettingsForm/SettingsFormController.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SettingsForm/SettingsFormController.js index 78ddfd0bd3f5f1b5219a904641976de2414d1ba3..787b3cf4ee32ec7eb542cd5b7f0afa72acde69e0 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SettingsForm/SettingsFormController.js +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/SettingsForm/SettingsFormController.js @@ -39,6 +39,27 @@ function SettingsFormController(mainController, settingsSample, mode) { } this.save = function(settings, widgetSettings) { + if(widgetSettings) { // Validate Widget Settings + for(var idx = 0; idx < widgetSettings.length; idx++) { + var widget = widgetSettings[idx]; + var property = profile.getPropertyType(widget["Property Type"]); + switch(widget.Widget) { + case "Word Processor": + if(property.dataType !== "MULTILINE_VARCHAR") { + Util.showUserError("Word Processor only works with MULTILINE_VARCHAR data type.", function() {}, true); + return; + } + break; + case "Spreadsheet": + if(property.dataType !== "XML") { + Util.showUserError("Spreadsheet only works with XML data type.", function() {}, true); + return; + } + break; + } + } + } + var _this = this; var onSave = function() { _this._settingsManager.validateAndsave(_this._settingsFormModel.settingsSample, settings, (function() { 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 03d1f8e2c9581b2f078153158d33a037359c596b..c7f77e5c4cd57151ba75599ade67fbfcd3b60f52 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 @@ -106,9 +106,14 @@ function SideMenuWidgetView(sideMenuWidgetController, sideMenuWidgetModel) { searchElement.css({"margin-right" : "2px"}); var logoutButton = FormUtil.getButtonWithIcon("glyphicon-off", function() { -     $('body').addClass('bodyLogin'); - mainController.serverFacade.logout(); - }); + $('body').addClass('bodyLogin'); + var logoutTemplate = mainController.profile.singleSignOnUrlLogoutTemplate; + if (logoutTemplate) { + window.location = logoutTemplate.replace("${host}", window.location.hostname); + } else { + mainController.serverFacade.logout(); + } + }); var $searchForm = $("<form>", { "onsubmit": "return false;" })                     .append(logoutButton) @@ -203,7 +208,7 @@ function SideMenuWidgetView(sideMenuWidgetController, sideMenuWidgetModel) {                 sortField = "displayName";             }             // descending order for registrationDate -            if (sortField == "registrationDate") { +            if (sortField === "registrationDate") {                 return naturalSort(resultB[sortField], resultA[sortField]);             }             return naturalSort(resultA[sortField], resultB[sortField]); @@ -226,8 +231,8 @@ function SideMenuWidgetView(sideMenuWidgetController, sideMenuWidgetModel) { } if(profile.mainMenu.showStock) { -     var inventoryLink = _this.getLinkForNode("Stock", "STOCK", "showStockPage", null); -     treeModel.push({ displayName: "Stock", title : inventoryLink, entityType: "STOCK", key : "STOCK", folder : true, lazy : true, view : "showStockPage", icon: "fa fa-shopping-cart" }); +     var stockLink = _this.getLinkForNode("Stock", "STOCK", "showStockPage", null); +     treeModel.push({ displayName: "Stock", title : stockLink, entityType: "STOCK", key : "STOCK", folder : true, lazy : true, view : "showStockPage", icon: "fa fa-shopping-cart" }); } var treeModelUtils = []; @@ -241,8 +246,8 @@ function SideMenuWidgetView(sideMenuWidgetController, sideMenuWidgetModel) { } if(profile.mainMenu.showUserProfile && profile.isFileAuthenticationService && profile.isFileAuthenticationUser) { -     var settingsLink = _this.getLinkForNode("User Profile", "USER_PROFILE", "showUserProfilePage", null); -     treeModelUtils.push({ title : settingsLink, entityType: "USER_PROFILE", key : "USER_PROFILE", folder : false, lazy : false, view : "showUserProfilePage", icon : "glyphicon glyphicon-user" }); +     var userProfileLink = _this.getLinkForNode("User Profile", "USER_PROFILE", "showUserProfilePage", null); +     treeModelUtils.push({ title : userProfileLink, entityType: "USER_PROFILE", key : "USER_PROFILE", folder : false, lazy : false, view : "showUserProfilePage", icon : "glyphicon glyphicon-user" }); } if(profile.mainMenu.showDrawingBoard) { @@ -264,19 +269,38 @@ function SideMenuWidgetView(sideMenuWidgetController, sideMenuWidgetModel) {     var advancedSearchLink = _this.getLinkForNode("Advanced Search", "ADVANCED_SEARCH", "showAdvancedSearchPage", null);     treeModelUtils.push({ displayName: "Advanced Search", title : advancedSearchLink, entityType: "ADVANCED_SEARCH", key : "ADVANCED_SEARCH", folder : false, lazy : false, view : "showAdvancedSearchPage", icon : "glyphicon glyphicon-search" }); } - - 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" }); - } - if (profile.mainMenu.showResearchCollectionExportBuilder) { - 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.showExports || profile.mainMenu.showResearchCollectionExportBuilder || profile.mainMenu.showZenodoExportBuilder) { + var treeModelExports = []; + + if (profile.mainMenu.showExports) { + var exportBuilderLink = _this.getLinkForNode("Export to ZIP", "EXPORT_TO_ZIP", "showExportTreePage", null); + treeModelExports.push({ + displayName: "Export to ZIP", title: exportBuilderLink, entityType: "EXPORT_TO_ZIP", key: "EXPORT_TO_ZIP", + folder: false, lazy: false, view: "showExportTreePage", icon: "glyphicon glyphicon-export" + }); + } + + if (profile.mainMenu.showResearchCollectionExportBuilder) { + var researchCollectionExportBuilderLink = _this.getLinkForNode("Export to Research Collection", + "EXPORT_TO_RESEARCH_COLLECTION", "showResearchCollectionExportPage", null); + treeModelExports.push({ + displayName: "Export to Research Collection", title: researchCollectionExportBuilderLink, + entityType: "EXPORT_TO_RESEARCH_COLLECTION", key: "EXPORT_TO_RESEARCH_COLLECTION", folder: false, lazy: false, + view: "showResearchCollectionExportPage", icon: "./img/research-collection-icon.png" + }); + } + + if (profile.mainMenu.showZenodoExportBuilder) { + var zenodoExportBuilderLink = _this.getLinkForNode("Export to Zenodo", "EXPORT_TO_ZENODO", "showZenodoExportPage", null); + treeModelExports.push({ + displayName: "Export to Zenodo", title: zenodoExportBuilderLink, entityType: "EXPORT_TO_ZENODO", + key: "EXPORT_TO_ZENODO", folder: false, lazy: false, view: "showZenodoExportPage", icon: "glyphicon glyphicon-export" + }); + } + + treeModelUtils.push({ displayName: "Exports", title: "Exports", entityType: "EXPORTS", key: "EXPORTS", folder: true, lazy: false, + expanded: false, children: treeModelExports, view: "showBlancPage", icon: "glyphicon glyphicon-export" }); } if(profile.mainMenu.showStorageManager) { @@ -299,26 +323,26 @@ function SideMenuWidgetView(sideMenuWidgetController, sideMenuWidgetModel) {     treeModelUtils.push({ displayName: "Settings", title : settingsLink, entityType: "SETTINGS", key : "SETTINGS", folder : false, lazy : false, view : "showSettingsPage", icon : "glyphicon glyphicon-cog" }); } - treeModel.push({ displayName: "Utilities", title : "Utilities", entityType: "UTILITIES", key : "UTILITIES", folder : true, lazy : false, expanded : true, children : treeModelUtils, icon : "glyphicon glyphicon-wrench" }); + treeModel.push({ displayName: "Utilities", title : "Utilities", entityType: "UTILITIES", key : "UTILITIES", folder : true, lazy : false, expanded : true, children : treeModelUtils, view: "showBlancPage", icon : "glyphicon glyphicon-wrench" }); treeModel.push({ displayName: "About", title : "About", entityType: "ABOUT", key : "ABOUT", folder : false, lazy : false, view : "showAbout", icon : "glyphicon glyphicon-info-sign" });         var glyph_opts = { -     map: { -     doc: "glyphicon glyphicon-file", -     docOpen: "glyphicon glyphicon-file", -     checkbox: "glyphicon glyphicon-unchecked", -     checkboxSelected: "glyphicon glyphicon-check", -     checkboxUnknown: "glyphicon glyphicon-share", -     dragHelper: "glyphicon glyphicon-play", -     dropMarker: "glyphicon glyphicon-arrow-right", -     error: "glyphicon glyphicon-warning-sign", -     expanderClosed: "glyphicon glyphicon-plus-sign", -     expanderLazy: "glyphicon glyphicon-plus-sign", // glyphicon-expand -     expanderOpen: "glyphicon glyphicon-minus-sign", // glyphicon-collapse-down -     folder: "glyphicon glyphicon-folder-close", -     folderOpen: "glyphicon glyphicon-folder-open", -     loading: "glyphicon glyphicon-refresh" -     } +     map: { +     doc: "glyphicon glyphicon-file", +     docOpen: "glyphicon glyphicon-file", +     checkbox: "glyphicon glyphicon-unchecked", +     checkboxSelected: "glyphicon glyphicon-check", +     checkboxUnknown: "glyphicon glyphicon-share", +     dragHelper: "glyphicon glyphicon-play", +     dropMarker: "glyphicon glyphicon-arrow-right", +     error: "glyphicon glyphicon-warning-sign", +     expanderClosed: "glyphicon glyphicon-plus-sign", +     expanderLazy: "glyphicon glyphicon-plus-sign", // glyphicon-expand +     expanderOpen: "glyphicon glyphicon-minus-sign", // glyphicon-collapse-down +     folder: "glyphicon glyphicon-folder-close", +     folderOpen: "glyphicon glyphicon-folder-open", +     loading: "glyphicon glyphicon-refresh" +     } };         var onLazyLoad = function(event, data) { @@ -381,7 +405,7 @@ function SideMenuWidgetView(sideMenuWidgetController, sideMenuWidgetModel) {     });                 });             }); -     } +     };         switch(type) {         case "LAB_NOTEBOOK": @@ -450,7 +474,7 @@ function SideMenuWidgetView(sideMenuWidgetController, sideMenuWidgetModel) {                                     registrationDate: space.registrationDate,                                 };     if(!space.getCode().endsWith("STOCK_CATALOG") && !space.getCode().endsWith("STOCK_ORDERS")) { -             results.push(spaceNode); +             results.push(spaceNode);     }     }     } @@ -661,24 +685,24 @@ function SideMenuWidgetView(sideMenuWidgetController, sideMenuWidgetModel) {             });                 } -     } +     };         var getCancelResultsFunction = function(dfd) { -             return function() { -                 dfd.resolve([]); -             } -     } +            return function() { +                dfd.resolve([]); +            } +     };         if(samplesToShow.length > 50) { -             var toExecute = function() { -            Util.blockUIConfirm("Do you want to show " + samplesWithoutELNParents.length + " " + ELNDictionary.Samples + " on the tree?", -                getOkResultsFunction(dfd, samplesToShow), -                getCancelResultsFunction(dfd)); -     } +            var toExecute = function() { +            Util.blockUIConfirm("Do you want to show " + samplesWithoutELNParents.length + " " + ELNDictionary.Samples + " on the tree?", +             getOkResultsFunction(dfd, samplesToShow), +             getCancelResultsFunction(dfd)); +     };         -             setTimeout(toExecute, 1000); +         setTimeout(toExecute, 1000);     } else { -             getOkResultsFunction(dfd, samplesToShow)(); +         getOkResultsFunction(dfd, samplesToShow)();     }             });             break; @@ -820,10 +844,9 @@ 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"); - } + }; function setCustomIcon($tree, nodeKey, iconImage) {         var node = $tree.fancytree("getTree").getNodeByKey(nodeKey); diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportController.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportController.js new file mode 100644 index 0000000000000000000000000000000000000000..34f489234de0f0119b948bb8139ea5a7e76855ff --- /dev/null +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportController.js @@ -0,0 +1,112 @@ +/* + * 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 ZenodoExportController(parentController) { + var exportModel = new ZenodoExportModel(); + var exportView = new ZenodoExportView(this, exportModel); + + this.init = function(views) { + exportView.repaint(views); + }; + + this.exportSelected = function() { + var _this = this; + var selectedNodes = $(exportModel.tree).fancytree('getTree').getSelectedNodes(); + + 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 (!this.isValid(toExport)) { + Util.showInfo('Not only spaces and the root should be selected. It will result in an empty export file.'); + } else { + Util.blockUI(); + this.getUserInformation(function(userInformation) { + mainController.serverFacade.exportZenodo(toExport, true, false, 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.isValid = function(toExport) { + for (var i = 0; i < toExport.length; i++) { + var value = toExport[i]; + if (value.type !== 'ROOT' && value.type !== 'SPACE' || value.expand) { + return true; + } + } + return false; + }; + + 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 = { + 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/ZenodoExport/ZenodoExportModel.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportModel.js new file mode 100644 index 0000000000000000000000000000000000000000..f555844b57e82e8c640965767f2077b1b66f213e --- /dev/null +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportModel.js @@ -0,0 +1,18 @@ +/* + * 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 ZenodoExportModel() { +} \ 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/ZenodoExport/ZenodoExportView.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportView.js new file mode 100644 index 0000000000000000000000000000000000000000..775a8893be6bbaed639dd4409ab6e73e14e56b43 --- /dev/null +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/views/ZenodoExport/ZenodoExportView.js @@ -0,0 +1,60 @@ +/* + * 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 ZenodoExportView(exportController, exportModel) { + this.repaint = function(views) { + 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 $infoBox1 = 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.' + ]); + $infoBox1.css('border', 'none'); + $container.append($infoBox1); + + var $infoBox2 = FormUtil.getInfoBox('Publication time constraint', [ + 'After the resource has been exported it should be published in Zenodo UI within 2 hours.', + 'Otherwise, the publication metadata will not be registered in openBIS.' + ]); + $infoBox2.css('border', 'none'); + $container.append($infoBox2); + + var $tree = $('<div>', { 'id' : 'exportsTree' }); + $formColumn.append($('<br>')); + $formColumn.append(FormUtil.getBox().append($tree)); + + $container.append($form); + + exportModel.tree = TreeUtil.getCompleteTree($tree); + + var $formTitle = $('<h2>').append('Zenodo 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); + }; +} \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/drop-boxes/eln-lims-dropbox/eln-lims-dropbox.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/drop-boxes/eln-lims-dropbox/eln-lims-dropbox.py index 50c8297589fcd2df04625c1510e88306723ac95c..71b0971ba7ed85a576b6b53fc307f89dbb4474f5 100644 --- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/drop-boxes/eln-lims-dropbox/eln-lims-dropbox.py +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/drop-boxes/eln-lims-dropbox/eln-lims-dropbox.py @@ -32,22 +32,15 @@ def process(transaction): # Parse entity Kind Format if entityKind == "O": - - if len(datasetInfo) >= 4: + OPENBISURL = DataStoreServer.getConfigParameters().getServerURL() + "/openbis/openbis" + v3 = HttpInvokerUtils.createServiceStub(IApplicationServerApi, OPENBISURL + IApplicationServerApi.SERVICE_URL, 30 * 1000); + projectSamplesEnabled = v3.getServerInformation(transaction.getOpenBisServiceSessionToken())['project-samples-enabled'] == 'true' + + if len(datasetInfo) >= 4 and projectSamplesEnabled: sampleSpace = datasetInfo[1]; projectCode = datasetInfo[2]; sampleCode = datasetInfo[3]; - - OPENBISURL = DataStoreServer.getConfigParameters().getServerURL() + "/openbis/openbis" - v3 = HttpInvokerUtils.createServiceStub(IApplicationServerApi, OPENBISURL + IApplicationServerApi.SERVICE_URL, 30 * 1000); - projectSamplesEnabled = v3.getServerInformation(transaction.getOpenBisServiceSessionToken())['project-samples-enabled'] == 'true' - - sample = None - if projectSamplesEnabled: - sample = transaction.getSample("/" +sampleSpace + "/" + projectCode + "/" + sampleCode); - else: - sample = transaction.getSample("/" +sampleSpace + "/" + sampleCode); - + sample = transaction.getSample("/" +sampleSpace + "/" + projectCode + "/" + sampleCode); if sample is None: raise UserFailureException(INVALID_FORMAT_ERROR_MESSAGE + ":" + SAMPLE_MISSING_ERROR_MESSAGE); if len(datasetInfo) >= 5: @@ -56,9 +49,20 @@ def process(transaction): name = datasetInfo[5]; if len(datasetInfo) > 6: raise UserFailureException(INVALID_FORMAT_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_SAMPLE_ERROR_MESSAGE); + elif len(datasetInfo) >= 3 and not projectSamplesEnabled: + sampleSpace = datasetInfo[1]; + sampleCode = datasetInfo[2]; + sample = transaction.getSample("/" +sampleSpace + "/" + sampleCode); + if sample is None: + raise UserFailureException(INVALID_FORMAT_ERROR_MESSAGE + ":" + SAMPLE_MISSING_ERROR_MESSAGE); + if len(datasetInfo) >= 4: + datasetType = datasetInfo[3]; + if len(datasetInfo) >= 5: + name = datasetInfo[4]; + if len(datasetInfo) > 5: + raise UserFailureException(INVALID_FORMAT_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_SAMPLE_ERROR_MESSAGE); else: raise UserFailureException(INVALID_FORMAT_ERROR_MESSAGE + ":" + FAILED_TO_PARSE_SAMPLE_ERROR_MESSAGE); - if entityKind == "E": if len(datasetInfo) >= 4: experimentSpace = datasetInfo[1]; 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 932c0fa302302c30bd0dc8174b5fd680c62a0707..7af095aa70a2bb27e997ae05a1d669464da3fd5c 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 @@ -645,4 +645,33 @@ def getConfigurationProperty(transaction, propertyName): try: return threadProperties.getProperty(propertyName); except: - return None \ No newline at end of file + return None + + +def generateZipFile(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 checkResponseStatus(response): + status = response.getStatus() + if status >= 300: + reason = response.getReason() + raise ValueError('Unsuccessful response from the server: %s %s' % (status, reason)) \ No newline at end of file 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 99f41ac9578a20a317c6af933a733e5e6b407d00..66e91990f86074101ad93186f4f6c7aac001b285 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 @@ -35,7 +35,8 @@ 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 +from exportsApi import displayResult, findEntitiesToExport, validateDataSize, getConfigurationProperty, generateFilesInZip, addToZipFile, cleanUp, \ + generateZipFile, checkResponseStatus operationLog = Logger.getLogger(str(LogCategory.OPERATION) + '.rcExports.py') @@ -106,7 +107,7 @@ def export(entities, tr, params, userInformation): exportZipFilePath = exportDirPath + '.zip' exportZipFileName = exportDirName + '.zip' - generateInternalZipFile(entities, params, contentDirPath, contentZipFilePath) + generateZipFile(entities, params, contentDirPath, contentZipFilePath) FileUtils.forceDelete(File(contentDirPath)) generateExternalZipFile(params=params, exportDirPath=exportDirPath, contentZipFilePath=contentZipFilePath, contentZipFileName=contentZipFileName, @@ -189,35 +190,6 @@ def fetchServiceDocument(url, httpClient): return json.dumps(map(collectionToDictionaryMapper, collections)) -def checkResponseStatus(response): - status = response.getStatus() - if status >= 300: - reason = response.getReason() - raise ValueError('Unsuccessful response from the server: %s %s' % (status, reason)) - - -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 diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/exportsApi.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/exportsApi.py new file mode 120000 index 0000000000000000000000000000000000000000..ccb8a49feb33c9712b493e377365bd0b7dad5f8d --- /dev/null +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-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/zenodo-exports-api/job-scheduler-source/JobScheduler.java b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/job-scheduler-source/JobScheduler.java new file mode 100644 index 0000000000000000000000000000000000000000..23558319831415c5d4b1a912d720553ce45efc7d --- /dev/null +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/job-scheduler-source/JobScheduler.java @@ -0,0 +1,51 @@ +/* + * 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. + */ + +package ch.ethz.sis; + +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.atomic.AtomicInteger; + +public class JobScheduler +{ + + public static void scheduleRepeatedRequest(final long period, final int repCount, final BooleanCallable callable) + { + final Timer timer = new Timer(false); + + final AtomicInteger remainingRepCount = new AtomicInteger(repCount); + timer.scheduleAtFixedRate(new TimerTask() + { + @Override + public void run() + { + final boolean returnedValue = callable.call(); + if (returnedValue || remainingRepCount.decrementAndGet() <= 0) + { + timer.cancel(); + } + } + + }, period, period); + } + + public interface BooleanCallable + { + boolean call(); + } + +} \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/lib/job-scheduler.jar b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/lib/job-scheduler.jar new file mode 100644 index 0000000000000000000000000000000000000000..3ab019d90a01dc5cb77d1c80b8f37dab09761137 Binary files /dev/null and b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/lib/job-scheduler.jar differ diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/plugin.properties b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/plugin.properties new file mode 100644 index 0000000000000000000000000000000000000000..46637e2ba22178234ec544460bef0591959e8a9a --- /dev/null +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/plugin.properties @@ -0,0 +1,7 @@ +label = Zenodo Exports API +class = ch.systemsx.cisd.openbis.dss.generic.server.plugins.jython.JythonIngestionService +script-path = zenodoExports.py +service-document-url=https://test.research-collection.ethz.ch/swordv2/servicedocument +limit-data-size-megabytes = 50000 +zenodoUrl=https://localhost +accessToken= \ No newline at end of file diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/zenodoExports.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/zenodoExports.py new file mode 100644 index 0000000000000000000000000000000000000000..4934cac3586e661ffe31a3142a24b52af2876ba7 --- /dev/null +++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/zenodo-exports-api/zenodoExports.py @@ -0,0 +1,260 @@ +# +# 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. +# + +from __future__ import print_function + +import json +import traceback + +import time +from ch.systemsx.cisd.common.logging import LogCategory +from ch.systemsx.cisd.openbis.dss.generic.shared import ServiceProvider +from ch.ethz.sis.openbis.generic.asapi.v3.dto.service.id import CustomASServiceCode +from ch.ethz.sis.openbis.generic.asapi.v3.dto.service import CustomASServiceExecutionOptions +from java.io import File +from java.nio.file import Paths +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 MultiPartContentProvider +from org.eclipse.jetty.client.util import PathContentProvider +from org.eclipse.jetty.client.util import StringContentProvider +from org.eclipse.jetty.http import HttpMethod +from org.eclipse.jetty.util.ssl import SslContextFactory +from org.json import JSONObject + +from ch.ethz.sis import JobScheduler +from exportsApi import findEntitiesToExport, validateDataSize, getConfigurationProperty, generateZipFile, checkResponseStatus, displayResult, cleanUp + +operationLog = Logger.getLogger(str(LogCategory.OPERATION) + '.zenodoExports.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) + + +def expandAndExport(tr, params): + entitiesToExport = findEntitiesToExport(params) + validateDataSize(entitiesToExport, tr) + + operationLog.info('Found ' + str(len(entitiesToExport)) + ' entities to export') + exportUrl = export(entities=entitiesToExport, tr=tr, params=params) + + return exportUrl + + +def export(entities, tr, params): + #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 + + generateZipFile(entities, params, contentDirPath, contentZipFilePath) + FileUtils.forceDelete(File(contentDirPath)) + + resultUrl = sendToZenodo(tr=tr, params=params, tempZipFilePath=contentZipFilePath, entities=entities) + FileUtils.forceDelete(File(exportDirPath)) + return resultUrl + + +def sendToZenodo(tr, params, tempZipFilePath, entities): + depositRootUrl = str(getConfigurationProperty(tr, 'zenodoUrl')) + '/api/deposit/depositions' + + accessToken = str(getConfigurationProperty(tr, 'accessToken')) + operationLog.info('accessToken: %s' % accessToken) + + httpClient = None + try: + httpClient = createHttpClient() + + httpClient.setFollowRedirects(False) + httpClient.start() + + depositionData = createDepositionResource(httpClient.newRequest(depositRootUrl), accessToken) + + depositionLinks = depositionData.get('links') + depositUrl = depositionLinks.get('files') + selfUrl = depositionLinks.get('self') + + submitFile(httpClient.newRequest(depositUrl), accessToken, tempZipFilePath) + addMetadata(httpClient.newRequest(selfUrl), accessToken) + + entityPermIds = map(lambda entity: entity['permId'], entities) + zenodoCallable = ZenodoCallable(params, accessToken, selfUrl, + reduce(lambda str, permId: str + ',' + permId, entityPermIds)) + zenodoCallable.scheduleMetadataCheck() + + result = depositionLinks.get('html') + return result + 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 submitFile(request, accessToken, tempZipFilePath): + multiPart = MultiPartContentProvider() + multiPart.addFilePart('file', 'content.zip', PathContentProvider(Paths.get(tempZipFilePath)), None) + multiPart.close() + addAuthenticationHeader(accessToken, request) + response = request.method(HttpMethod.POST).content(multiPart).send() + checkResponseStatus(response) + contentStr = response.getContentAsString() + + return JSONObject(contentStr) + + +def addMetadata(request, accessToken): + data = { + 'metadata': { + 'title': str(time.time()), + 'license': 'cc-zero', + 'upload_type': 'dataset', + 'description': 'Add some description.', + 'creators': [{'name': userId}] + } + } + + addAuthenticationHeader(accessToken, request) + jsonString = json.dumps(data) + response = request.method(HttpMethod.PUT).content(StringContentProvider(jsonString), 'application/json').send() + + checkResponseStatus(response) + + +def retrieve(request, accessToken): + addAuthenticationHeader(accessToken, request) + response = request.method(HttpMethod.GET).send() + contentStr = response.getContentAsString() + + # If the resource has been deleted instead of published return None. + if response.getStatus() == 410: + return None + + checkResponseStatus(response) + + return JSONObject(contentStr) + + +def createDepositionResource(request, accessToken): + addAuthenticationHeader(accessToken, request) + response = request.method(HttpMethod.POST).content(StringContentProvider('{}'), 'application/json').send() + checkResponseStatus(response) + + contentStr = response.getContentAsString() + return JSONObject(contentStr) + + +def addAuthenticationHeader(accessToken, request): + request.header('Authorization', 'Bearer ' + accessToken) + + +def createHttpClient(): + sslContextFactory = SslContextFactory() + sslContextFactory.setTrustAll(True) + return HttpClient(sslContextFactory) + + +class ZenodoCallable(object): + params = None + accessToken = None + selfUrl = None + permIdsStr = None + + def __init__(self, params, accessToken, selfUrl, permIdsStr): + self.params = params + self.accessToken = accessToken + self.selfUrl = selfUrl + self.permIdsStr = permIdsStr + + def scheduleMetadataCheck(self): + JobScheduler.scheduleRepeatedRequest(120000, 60, self.call) + + def call(self): + httpClient = None + + # Whether this method returned a completion result and it should not be called repeatedly. + actionCompleted = False + + try: + httpClient = createHttpClient() + + httpClient.setFollowRedirects(False) + httpClient.start() + + try: + publicationJson = retrieve(httpClient.newRequest(self.selfUrl), self.accessToken) + if publicationJson is None: + operationLog.info('Publication at the URL has been deleted.' % self.selfUrl) + actionCompleted = True + elif publicationJson.get('submitted'): + operationLog.info('Publication #%d submitted. Registering metadata.' % publicationJson.get('id')) + self.registerPublicationInOpenbis(publicationJson.get('metadata')) + actionCompleted = True + else: + operationLog.info('Publication #%d not submitted yet.' % publicationJson.get('id')) + except Exception as e: + operationLog.error('Exception at: ' + traceback.format_exc()) + operationLog.error('Exception: ' + str(e)) + actionCompleted = False + 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() + + return actionCompleted + + + def registerPublicationInOpenbis(self, publicationMetadataJson): + sessionToken = self.params.get('sessionToken') + v3 = ServiceProvider.getV3ApplicationService() + id = CustomASServiceCode('publication-api') + options = CustomASServiceExecutionOptions() \ + .withParameter('method', 'insertPublication') \ + .withParameter('publicationURL', self.selfUrl) \ + .withParameter('openBISRelatedIdentifiers', self.permIdsStr) \ + .withParameter('name', publicationMetadataJson.get('title')) \ + .withParameter('publicationDescription', publicationMetadataJson.get('description')) \ + .withParameter('publicationType', publicationMetadataJson.get('upload_type')) \ + .withParameter('publicationIdentifier', publicationMetadataJson.get('doi')) + result = v3.executeCustomASService(sessionToken, id, options) + return result diff --git a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/entrypoint.py b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/entrypoint.py index adf93431d3e19e72826f8cff495710a536a4e5d5..9806b27718bb3855c9f0eff450b44c2edb637b3b 100644 --- a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/entrypoint.py +++ b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/entrypoint.py @@ -26,9 +26,9 @@ def validate_data(xls_byte_arrays, update_mode, xls_name): def get_property(key, defaultValue): - propertyConfigurer = CommonServiceProvider.getApplicationContext().getBean("propertyConfigurer"); - properties = propertyConfigurer.getResolvedProps(); - return properties.getProperty(key, defaultValue); + propertyConfigurer = CommonServiceProvider.getApplicationContext().getBean("propertyConfigurer") + properties = propertyConfigurer.getResolvedProps() + return properties.getProperty(key, defaultValue) def read_versioning_information(xls_version_filepath): @@ -93,7 +93,8 @@ def process(context, parameters): if code in versioning_information: version = versioning_information[code] else: - version = creations_metadata.get_metadata_for(creation_type, creation).version if update_mode != "UPDATE_IF_EXISTS" else 0 + version = creations_metadata.get_metadata_for(creation_type, + creation).version if update_mode != "UPDATE_IF_EXISTS" else 0 versioning_information[code] = int(version) else: versioning_information = {} @@ -101,12 +102,14 @@ def process(context, parameters): if creation_type in versionable_types: for creation in creation_collection: code = get_metadata_name_for(creation_type, creation) - versioning_information[code] = creations_metadata.get_metadata_for(creation_type, creation).version if update_mode != "UPDATE_IF_EXISTS" else 0 + versioning_information[code] = creations_metadata.get_metadata_for(creation_type, + creation).version if update_mode != "UPDATE_IF_EXISTS" else 0 existing_elements = search_engine.find_all_existing_elements(creations) entity_kinds = search_engine.find_existing_entity_kind_definitions_for(creations) existing_vocabularies = search_engine.find_all_existing_vocabularies() - existing_unified_kinds = unify_properties_representation_of(creations, entity_kinds, existing_vocabularies, existing_elements) + existing_unified_kinds = unify_properties_representation_of(creations, entity_kinds, existing_vocabularies, + existing_elements) creations = PropertiesLabelHandler.rewrite_property_labels_to_codes(creations, existing_unified_kinds) server_duplicates_handler = OpenbisDuplicatesHandler(creations, creations_metadata, existing_elements, versioning_information, update_mode) @@ -117,4 +120,3 @@ def process(context, parameters): all_versioning_information[xls_version_name] = versioning_information save_versioning_information(all_versioning_information, xls_version_filepath) return res - diff --git a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/creation_to_update/update_parsers.py b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/creation_to_update/update_parsers.py index a9be5e9003d550f3a6ebf87aaeaa15f251cd60b6..93daca6f0a0b5e766fb24526a148da944271c4e9 100644 --- a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/creation_to_update/update_parsers.py +++ b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/creation_to_update/update_parsers.py @@ -8,14 +8,14 @@ from ch.ethz.sis.openbis.generic.asapi.v3.dto.space.update import SpaceUpdate from ch.ethz.sis.openbis.generic.asapi.v3.dto.project.update import ProjectUpdate from ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.update import PluginUpdate from java.lang import UnsupportedOperationException -from ..definition_to_creation import PropertyTypeDefinitionToCreationType, VocabularyDefinitionToCreationType, VocabularyTermDefinitionToCreationType, \ - PropertyAssignmentDefinitionToCreationType, SampleTypeDefinitionToCreationType, ExperimentTypeDefinitionToCreationType, \ - DatasetTypeDefinitionToCreationType, SpaceDefinitionToCreationType, ProjectDefinitionToCreationType, ExperimentDefinitionToCreationType, \ - SampleDefinitionToCreationType, ScriptDefinitionToCreationType -from .update_types import PropertyTypeCreationToUpdateType, VocabularyCreationToUpdateType, PropertyAssignmentCreationToUpdateType, \ - SampleTypeCreationToUpdateType, ExperimentTypeCreationToUpdateType, DatasetTypeCreationToUpdateType, \ - SpaceCreationToUpdateType, ProjectCreationToUpdateType, ExperimentCreationToUpdateType, \ - SampleCreationToUpdateType, ScriptCreationToUpdateType, VocabularyTermCreationToUpdateType +from ..definition_to_creation import PropertyTypeDefinitionToCreationType, VocabularyDefinitionToCreationType, \ + VocabularyTermDefinitionToCreationType, SampleTypeDefinitionToCreationType, ExperimentTypeDefinitionToCreationType, \ + DatasetTypeDefinitionToCreationType, SpaceDefinitionToCreationType, ProjectDefinitionToCreationType, \ + ExperimentDefinitionToCreationType, SampleDefinitionToCreationType, ScriptDefinitionToCreationType +from .update_types import PropertyTypeCreationToUpdateType, VocabularyCreationToUpdateType, \ + SampleTypeCreationToUpdateType, ExperimentTypeCreationToUpdateType, DatasetTypeCreationToUpdateType, \ + SpaceCreationToUpdateType, ProjectCreationToUpdateType, ExperimentCreationToUpdateType, SampleCreationToUpdateType, \ + ScriptCreationToUpdateType, VocabularyTermCreationToUpdateType class CreationToUpdateParserFactory(object): @@ -81,6 +81,13 @@ class PropertyTypeCreationToUpdateParser(object): property_type_update.typeId = existing_property_type.permId property_type_update.setLabel(creation.label) property_type_update.setDescription(creation.description) + metadata_update = property_type_update.getMetaData() + if existing_property_type.metaData: + for metaDataEntry in existing_property_type.metaData: + if creation.metaData and metaDataEntry not in creation.metaData: + metadata_update.remove(metaDataEntry) + if creation.metaData: + metadata_update.add(creation.metaData) return property_type_update def get_type(self): @@ -91,8 +98,10 @@ class EntityTypeCreationToUpdateParser(object): def parseAssignments(self, creation, existing_entity_type): assignments_update = ListUpdateValue() - creationPropertyAssignmentCodes = [str(property_assignment.propertyTypeId) for property_assignment in creation.propertyAssignments] - existingPropertyAssignmentCodes = [str(property_assignment.propertyType.code) for property_assignment in existing_entity_type.propertyAssignments] + creationPropertyAssignmentCodes = [str(property_assignment.propertyTypeId) for property_assignment in + creation.propertyAssignments] + existingPropertyAssignmentCodes = [str(property_assignment.propertyType.code) for property_assignment in + existing_entity_type.propertyAssignments] for property_assignment in existing_entity_type.propertyAssignments: if str(property_assignment.propertyType.code) in creationPropertyAssignmentCodes: continue @@ -205,9 +214,11 @@ class SampleCreationToUpdateParser(object): for child in existing_sample.children: existing_children_identifiers.extend([str(child.permId), str(child.identifier)]) - parentsToRemove = [parent.permId for parent in existing_sample.parents if parent.permId not in creation.parentIds and parent.identifier not in creation.parentIds] + parentsToRemove = [parent.permId for parent in existing_sample.parents if + parent.permId not in creation.parentIds and parent.identifier not in creation.parentIds] parentsToAdd = [parent for parent in creation.parentIds if str(parent) not in existing_parent_identifiers] - childrenToRemove = [child.permId for child in existing_sample.children if child.permId not in creation.childIds and child.identifier not in creation.parentIds] + childrenToRemove = [child.permId for child in existing_sample.children if + child.permId not in creation.childIds and child.identifier not in creation.parentIds] childrenToAdd = [child for child in creation.childIds if str(child) not in existing_children_identifiers] sample_update.childIds.remove([parent.permId for parent in existing_sample.children]) sample_update.parentIds.remove([child.permId for child in existing_sample.parents]) @@ -230,4 +241,3 @@ class ScriptCreationToUpdateParser(object): def get_type(self): return ScriptCreationToUpdateType - diff --git a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/definition_to_creation/creation_parsers.py b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/definition_to_creation/creation_parsers.py index 9a9f1918639be04793de2877c4264970cfae4852..d1681a5a469899b943903533b91f04474c3a82de 100644 --- a/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/definition_to_creation/creation_parsers.py +++ b/openbis_standard_technologies/dist/core-plugins/xls-import/1/as/services/xls-import-api/parsers/definition_to_creation/creation_parsers.py @@ -17,15 +17,20 @@ from ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.id import VocabularyPer from java.lang import UnsupportedOperationException from ch.systemsx.cisd.common.exceptions import UserFailureException from utils.openbis_utils import is_internal_namespace, get_script_name_for -from .creation_types import PropertyTypeDefinitionToCreationType, VocabularyDefinitionToCreationType, VocabularyTermDefinitionToCreationType, \ - PropertyAssignmentDefinitionToCreationType, SampleTypeDefinitionToCreationType, ExperimentTypeDefinitionToCreationType, \ - DatasetTypeDefinitionToCreationType, SpaceDefinitionToCreationType, ProjectDefinitionToCreationType, ExperimentDefinitionToCreationType, \ - SampleDefinitionToCreationType, ScriptDefinitionToCreationType +from .creation_types import PropertyTypeDefinitionToCreationType, VocabularyDefinitionToCreationType, \ + VocabularyTermDefinitionToCreationType, \ + PropertyAssignmentDefinitionToCreationType, SampleTypeDefinitionToCreationType, \ + ExperimentTypeDefinitionToCreationType, \ + DatasetTypeDefinitionToCreationType, SpaceDefinitionToCreationType, ProjectDefinitionToCreationType, \ + ExperimentDefinitionToCreationType, \ + SampleDefinitionToCreationType, ScriptDefinitionToCreationType +import json def get_boolean_from_string(text): if text.lower() not in [u'true', u'false']: - raise UserFailureException("Boolean field should either be 'true' or 'false' (case insensitive) but was " + text) + raise UserFailureException( + "Boolean field should either be 'true' or 'false' (case insensitive) but was " + text) return True if text and text.lower() == u'true' else False @@ -73,7 +78,10 @@ class PropertyTypeDefinitionToCreationParser(object): property_type_creation.description = prop.get(u'description') property_type_creation.dataType = DataType.valueOf(prop.get(u'data type')) property_type_creation.internalNameSpace = is_internal_namespace(prop.get(u'code')) - property_type_creation.vocabularyId = VocabularyPermId(prop.get(u'vocabulary code')) if prop.get(u'vocabulary code') is not None else None + property_type_creation.vocabularyId = VocabularyPermId(prop.get(u'vocabulary code')) if prop.get( + u'vocabulary code') is not None else None + metadata = json.loads(prop.get(u'metadata')) if prop.get(u'metadata') is not None else None + property_type_creation.metaData = metadata property_creations.append(property_type_creation) return property_creations @@ -150,7 +158,8 @@ class SampleTypeDefinitionToCreationParser(object): generatedCodePrefix = definition.attributes.get(u'generated code prefix') if generatedCodePrefix is not None: sample_creation.generatedCodePrefix = generatedCodePrefix - if u'validation script' in definition.attributes and definition.attributes.get(u'validation script') is not None: + if u'validation script' in definition.attributes and definition.attributes.get( + u'validation script') is not None: validation_script_path = definition.attributes.get(u'validation script') sample_creation.validationPluginId = PluginPermId(get_script_name_for(code, validation_script_path)) @@ -174,9 +183,11 @@ class ExperimentTypeDefinitionToCreationParser(object): experiment_type_creation = ExperimentTypeCreation() experiment_type_creation.code = code experiment_type_creation.description = definition.attributes.get(u'description') - if u'validation script' in definition.attributes and definition.attributes.get(u'validation script') is not None: + if u'validation script' in definition.attributes and definition.attributes.get( + u'validation script') is not None: validation_script_path = definition.attributes.get(u'validation script') - experiment_type_creation.validationPluginId = PluginPermId(get_script_name_for(code, validation_script_path)) + experiment_type_creation.validationPluginId = PluginPermId( + get_script_name_for(code, validation_script_path)) property_assignment_creations = [] property_assignment_parser = PropertyAssignmentDefinitionToCreationParser() @@ -198,7 +209,8 @@ class DatasetTypeDefinitionToCreationParser(object): code = definition.attributes.get(u'code') dataset_type_creation.code = code dataset_type_creation.description = definition.attributes.get(u'description') - if u'validation script' in definition.attributes and definition.attributes.get(u'validation script') is not None: + if u'validation script' in definition.attributes and definition.attributes.get( + u'validation script') is not None: validation_script_path = definition.attributes.get(u'validation script') dataset_type_creation.validationPluginId = PluginPermId(get_script_name_for(code, validation_script_path)) @@ -277,7 +289,7 @@ class SampleDefinitionToCreationParser(object): def parse(self, definition): samples = [] sample_attributes = [u'$', u'code', u'space', u'project', u'experiment', u'auto generate code', u'parents', - u'children'] + u'children'] for sample_properties in definition.properties: sample_creation = SampleCreation() sample_creation.typeId = EntityTypePermId(definition.attributes.get(u'sample type')) @@ -288,9 +300,11 @@ class SampleDefinitionToCreationParser(object): if u'$' in sample_properties and sample_properties.get(u'$') is not None: # may overwrite creationId from code, which is intended sample_creation.creationId = CreationId(sample_properties.get(u'$')) - if u'auto generate code' in sample_properties and sample_properties.get(u'auto generate code') is not None and \ + if u'auto generate code' in sample_properties and sample_properties.get( + u'auto generate code') is not None and \ sample_properties.get(u'auto generate code') != '': - sample_creation.autoGeneratedCode = get_boolean_from_string(sample_properties.get(u'auto generate code')) + sample_creation.autoGeneratedCode = get_boolean_from_string( + sample_properties.get(u'auto generate code')) if u'space' in sample_properties and sample_properties.get(u'space') is not None: sample_creation.spaceId = CreationId(sample_properties.get(u'space')) if u'project' in sample_properties and sample_properties.get(u'project') is not None: diff --git a/pybis/src/python/CHANGELOG.md b/pybis/src/python/CHANGELOG.md index 4ba563b5f32628fd97bd1378de9904040d4d3d28..1c68708da418d638fa3158a6d438d9e08b63e341 100644 --- a/pybis/src/python/CHANGELOG.md +++ b/pybis/src/python/CHANGELOG.md @@ -1,3 +1,8 @@ +## Changes with pybis-1.9.1 + +* bugfix: controlled vocabulary + + ## Changes with pybis-1.9.0 * new: search, create, update and delete Property Types @@ -6,6 +11,7 @@ * freeze entities to prevent changes * added more tests + ## Changes with pybis-1.8.5 * changed to v3 API when fetching datastores diff --git a/pybis/src/python/pybis/__init__.py b/pybis/src/python/pybis/__init__.py index 2ca20089ea36d860b51223bba7c30326c7957f96..4aa2b995675dc04f0874952732c359ada99bdd0f 100644 --- a/pybis/src/python/pybis/__init__.py +++ b/pybis/src/python/pybis/__init__.py @@ -1,7 +1,7 @@ name = 'pybis' __author__ = 'Swen Vermeul' __email__ = 'swen@ethz.ch' -__version__ = '1.9.0' +__version__ = '1.9.1' from . import pybis from .pybis import Openbis diff --git a/pybis/src/python/pybis/property.py b/pybis/src/python/pybis/property.py index 9dd9bf6c31f89a0868f743a03e04f97868aa485e..9d2a6431abf9304559c0fb9ddae52bf6b2e89f2f 100644 --- a/pybis/src/python/pybis/property.py +++ b/pybis/src/python/pybis/property.py @@ -60,7 +60,7 @@ class PropertyHolder(): if name in self._property_names: property_type = self._property_names[name] if property_type['dataType'] == 'CONTROLLEDVOCABULARY': - return self._get_terms(property_type['vocabulary']['code']) + return self._get_terms(property_type['code']) else: syntax = { property_type["label"] : property_type["dataType"]} if property_type["dataType"] == "TIMESTAMP": @@ -82,7 +82,7 @@ class PropertyHolder(): property_type = self._property_names[name] data_type = property_type['dataType'] if data_type == 'CONTROLLEDVOCABULARY': - voc = self._get_terms(property_type['vocabulary']['code']) + voc = self._get_terms(property_type['code']) value = str(value).upper() if value not in voc.df['code'].values: raise ValueError("Value for attribute {} must be one of these terms: {}".format( diff --git a/pybis/src/python/setup.py b/pybis/src/python/setup.py index ba3e2a12cd1d6c1af822210f2223dee6324fe419..723fcda825c708e6a385739579d327aa8d44c80e 100644 --- a/pybis/src/python/setup.py +++ b/pybis/src/python/setup.py @@ -11,7 +11,7 @@ with open("README.md", "r", encoding="utf-8") as fh: setup( name='PyBIS', - version= '1.9.0', + version= '1.9.1', author='Swen Vermeul • ID SIS • ETH Zürich', author_email='swen@ethz.ch', description='openBIS connection and interaction, optimized for using with Jupyter',