diff --git a/README.md b/README.md index 84902e209f934bedbade23be7f39cb387eb17b2b..4b3aad6a330d7d1ac841f1bf525d0d21b1ea7a91 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,18 @@  +# Repository organization + +The repository contains these kind of modules used to build the openBIS distributable: +- api-*: API Facades +- app-*: Applications +- build: Build scripts +- core-plugins-*: Core plugins distributed with openBIS +- lib-*: Internally maintained libraries used to build openBIS +- server-*: Server components +- test-*: Integration tests +- ui-*: User interfaces + # Software Requirements - JDK 17 diff --git a/build/bin/branch.sh b/build/bin/branch.sh index 384728c8ccf2161e6886c2ec7e6029bbc89b60bd..d732f7453085ae03814b6142abcfac2bfcc6bb0e 100755 --- a/build/bin/branch.sh +++ b/build/bin/branch.sh @@ -17,7 +17,7 @@ if [ -n "$branch_heads" ]; then fi # cd to repository root directory -cd "$(dirname "$0")/../../../.." +cd "$(dirname "$0")/../.." # create branch in git from master git checkout -b $1 diff --git a/build/bin/build.sh b/build/bin/build.sh index 2d9b2eb6bd486ab89509ee4dd252de52d77a5e34..6e75bc1e92809e71dc02a713ae9912f6ddd5476e 100755 --- a/build/bin/build.sh +++ b/build/bin/build.sh @@ -30,7 +30,7 @@ branch=$1 tag=$2 # cd to repository root directory -cd "$(dirname "$0")/../../../.." +cd "$(dirname "$0")/../.." # checkout tag git checkout $tag diff --git a/build/bin/tag.sh b/build/bin/tag.sh index e0c689a5badb8bd38395e2755fb435ce7c8d687a..234c70e6d9a1c4b3b0a5ac79c91e44f938bc2098 100755 --- a/build/bin/tag.sh +++ b/build/bin/tag.sh @@ -12,7 +12,7 @@ branch=$1 tag=$2 # cd to repository root directory -cd "$(dirname "$0")/../../../.." +cd "$(dirname "$0")/../.." # switch to branch - exit if it does not exist git checkout $branch diff --git a/build/javaproject.gradle b/build/javaproject.gradle index 4ef001c0855cb3a18c7c90ae02e288f961625088..964a9ae5f00a9b708b68f315155d0612b2b3f14a 100644 --- a/build/javaproject.gradle +++ b/build/javaproject.gradle @@ -29,8 +29,8 @@ configurations.all { // gradleVersion = '4.10.3' // distributionUrl = "https://services.gradle.org/distributions/gradle-4.10.3-bin.zip" //} -sourceCompatibility='1.8' -targetCompatibility='1.8' +sourceCompatibility='11' +targetCompatibility='11' sourceSets { main { diff --git a/core-plugin-openbis/build.gradle b/core-plugin-openbis/build.gradle index 38b76d30e3434b2691adca1983eaaa3b912a75e6..1a7dc5afbdee9b66b494535223f037e80b1a3524 100644 --- a/core-plugin-openbis/build.gradle +++ b/core-plugin-openbis/build.gradle @@ -59,6 +59,9 @@ configurations.create('server_original_data_store') configurations.create('javadoc_sources') configurations.create('javadoc_compilation') configurations.create('blast_tar') +configurations.create('libOpenbisCommonJavadoc') +configurations.create('serverOriginalDataStoreJavadoc') +configurations.create('serverApplicationServerJavadoc') def jettyVersion = '9.4.44' def jettyDistributionVersion = '9.4.44.v20210927' @@ -119,6 +122,9 @@ dependencies { exclude group: 'google', module: 'gwt-user' } + libOpenbisCommonJavadoc project(':lib-openbis-common') + serverOriginalDataStoreJavadoc project(':server-original-data-store') + serverApplicationServerJavadoc project(':server-application-server') } sourceSets { @@ -470,7 +476,46 @@ task obisZip(type: Zip) { from '../app-openbis-command-line/' } -task clientsAndApis(type: Zip, dependsOn: [dssClientZip, queryApiZip, apiV3Zip, screeningApiZip, pybisZip, obisZip, dropboxJavaDocZip, dynamicApiJavadocZip]) { +task libOpenbisCommonJavadoc(type: Javadoc) { + source project(':lib-openbis-common').files('source/java').getAsFileTree().matching { + include "**/ch/ethz/sis/openbis/common/io/hierarchical_content/api/**/*.java" + } + classpath = configurations.libOpenbisCommonJavadoc +} + +task libOpenbisCommonJavadocZip(type: Zip, dependsOn: libOpenbisCommonJavadoc) { + archiveName 'javadoc-lib-openbis-common.zip' + from libOpenbisCommonJavadoc.destinationDir +} + +task serverOriginalDataStoreJavadoc(type: Javadoc) { + source project(':server-original-data-store').files('source/java').getAsFileTree().matching { + include "**/ch/systemsx/cisd/openbis/dss/generic/server/plugins/jython/api/**/*.java" + include "**/ch/systemsx/cisd/openbis/dss/generic/shared/api/internal/**/*.java" + } + classpath = configurations.serverOriginalDataStoreJavadoc +} + +task serverOriginalDataStoreJavadocZip(type: Zip, dependsOn: serverOriginalDataStoreJavadoc) { + archiveName 'javadoc-server-original-data-store.zip' + from serverOriginalDataStoreJavadoc.destinationDir +} + +task serverApplicationServerJavadoc(type: Javadoc) { + source project(':server-application-server').files('source/java').getAsFileTree().matching { + include "**/ch/systemsx/cisd/openbis/generic/shared/managed_property/api/**/*.java" + } + classpath = configurations.serverApplicationServerJavadoc +} + +task serverApplicationServerJavadocZip(type: Zip, dependsOn: serverApplicationServerJavadoc) { + archiveName 'javadoc-server-application-server.zip' + from serverApplicationServerJavadoc.destinationDir +} + +task clientsAndApis(type: Zip, dependsOn: [dssClientZip, queryApiZip, apiV3Zip, screeningApiZip, pybisZip, obisZip, + dropboxJavaDocZip, dynamicApiJavadocZip, libOpenbisCommonJavadocZip, + serverOriginalDataStoreJavadocZip, serverApplicationServerJavadocZip]) { baseName 'openBIS-clients-and-APIs' from dssClientZip.archivePath from queryApiZip.archivePath @@ -480,6 +525,9 @@ task clientsAndApis(type: Zip, dependsOn: [dssClientZip, queryApiZip, apiV3Zip, from obisZip.archivePath from dropboxJavaDocZip.archivePath from dynamicApiJavadocZip.archivePath + from libOpenbisCommonJavadocZip.archivePath + from serverOriginalDataStoreJavadocZip.archivePath + from serverApplicationServerJavadocZip.archivePath from('dist/client/readme.txt') rename { filename -> if (filename.startsWith('pybis-')) { @@ -519,17 +567,17 @@ task generateJavadoc(type: Javadoc) { include "**/ch/ethz/sis/openbis/generic/asapi/**/*.java" include "**/ch/ethz/sis/openbis/generic/dssapi/**/*.java" include "**/ch/systemsx/cisd/**/api/**/*.java" - include "**/ch/systemsx/cisd/common/annotation/*.java" - include "**/ch/systemsx/cisd/base/**/*.java" - include "**/OpenBISScreeningML.java" include "**/ch/systemsx/cisd/**/etlserver/TopLevelDataSetRegistratorGlobalState.java" include "**/ch/systemsx/cisd/**/etlserver/registrator/*.java" + include "**/ch/systemsx/cisd/base/**/*.java" + include "**/ch/systemsx/cisd/common/annotation/*.java" include "**/ch/systemsx/cisd/common/mail/*.java" + include "**/OpenBISScreeningML.java" } } classpath = configurations.javadoc_compilation maxMemory = "1024m" - options.addStringOption("source", "1.8") + options.addStringOption("source", "11") } build.dependsOn zip diff --git a/docs/api-openbis-matlab/Python_install_windows.png b/docs/api-openbis-matlab/Python_install_windows.png new file mode 100644 index 0000000000000000000000000000000000000000..42656cbed26790563456aa2a0df72d12eb67164c Binary files /dev/null and b/docs/api-openbis-matlab/Python_install_windows.png differ diff --git a/docs/api-openbis-matlab/home.md b/docs/api-openbis-matlab/home.md new file mode 100644 index 0000000000000000000000000000000000000000..ba572543cb2f5b9c07329e2b5e56ed5c2258471b --- /dev/null +++ b/docs/api-openbis-matlab/home.md @@ -0,0 +1,31 @@ +# How to access openBIS from MATLAB + +## Preamble +[openBIS](https://wiki-bsse.ethz.ch/display/bis/Home) is a research data management system developed by [ETH SIS](https://sis.id.ethz.ch/). Data stored in openBIS can be accessed directly via the web UI or programmatically using APIs. For example, [pyBIS](https://sissource.ethz.ch/sispub/openbis/tree/master/pybis) is a project that provides a Python 3 module for interacting with openBIS. +[MATLAB](https://ch.mathworks.com/products/matlab.html) is a high-level numerical computing environment that is popular in many areas of science. This repository provides a toolbox to access data in openBIS directly from MATLAB. + +## Setup +The toolbox interacts with openBIS by calling pyBIS functions directly from MATLAB. Therefore, both Python and MATLAB have to be installed and configured properly. Please consult the [MATLAB - Python compatibility table](https://www.mathworks.com/content/dam/mathworks/mathworks-dot-com/support/sysreq/files/python-compatibility.pdf) to choose the correct versions. Also note that Python 2.7 is no longer supported! + +#### macOS +On macOS, the setup has been tested with a Miniconda Python distribution. +1. Download and install [Miniconda3](https://conda.io/miniconda.html) (use a Python version according to the [MATLAB - Python compatibility table](https://www.mathworks.com/content/dam/mathworks/mathworks-dot-com/support/sysreq/files/python-compatibility.pdf)) +2. Open the terminal and install pyBIS with pip: `pip install pybis` +3. Find the path to your Python executable: `which python` +4. Open MATLAB and set the Python executable. On Matlab R2019b or later, use the command: `pyenv('Version', 'Path/to/python')`. Replace with the path found in previous step. On earlier versions of Matlab, the `pyenv` command is called `pyversion`. + +#### Windows 10 +On Windows using the Anaconda or Miniconda approach did not work (for some reason, MATLAB could not find the Python modules). On the other hand, using the standard Python installation seems to work. +1. Download and install Python [here](https://www.python.org/downloads/windows/) (use a Python version according to the [MATLAB - Python compatibility table](https://www.mathworks.com/content/dam/mathworks/mathworks-dot-com/support/sysreq/files/python-compatibility.pdf)). Make sure to choose the **64-bit version**. +2. During the installation, make sure Python is added to the Path and registered as default Python interpreter. To do this, select the little tick box `Add Python 3.x to PATH` in the installation window: + + +3. Open Windows PowerShell and install pyBIS with pip: `pip install pybis` +4. Find the path to your Python executable by typing: `Get-Command python`. The path is listed in the Source column, i.e. `C:\Users\user\AppData\Local\Programs\Python\Python38\python.exe`. Copy the path by selecting it and pressing `Ctrl-C` +5. Open MATLAB and set the Python executable. On Matlab R2019b or later, use the command: `pyenv('Version', 'C:\Path\to\Programs\python.exe')`. Replace with the path found in step 4. On earlier versions of Matlab, the `pyenv` command is called `pyversion`. + +## Usage +Download [this repository](https://sissource.ethz.ch/sispub/openbis/-/tree/master/api-openbis-matlab) and add it to your Matlab Path. If you are running the toolbox for the first time, make sure to carry out the steps described under **Setup** above. An [example script](https://sissource.ethz.ch/hluetcke/matlab-openbis/blob/master/openbis_example.mlx) demonstrating some common usage patterns is provided in the repository. The script can be run interactively in the MATLAB Live Editor. Type `doc OpenBis` in the Matlab Command Window to access the built-in documentation. + +## Notes +I do not have time to test these instructions and the toolbox with all combinations of Python & Matlab versions on different operating systems. In general, a combination of recent Python and Matlab versions should work on macOS and Windows. If you run into any issues, please feel free to contact the [SIS Helpdesk](mailto:sis.helpdesk@ethz.ch). \ No newline at end of file diff --git a/lib-file-download/javaproject.gradle b/lib-file-download/javaproject.gradle index b56780d6291ded6391af3e131ba9599ef7ece91d..388b924f3d502dceffeadf5d3636b74c0a048747 100644 --- a/lib-file-download/javaproject.gradle +++ b/lib-file-download/javaproject.gradle @@ -23,8 +23,8 @@ task wrapper(type: Wrapper) { distributionUrl = "http://svnsis.ethz.ch/repos/cisd/ivy-repository/trunk/gradle/distribution/3.5/gradle-3.5-all.zip" } -sourceCompatibility='1.8' -targetCompatibility='1.8' +sourceCompatibility='11' +targetCompatibility='11' sourceSets { main { diff --git a/lib-microservice-server/build.gradle b/lib-microservice-server/build.gradle index 1cccca08c92828675c0cd65ca6e07d3b7c42c1a1..8f7456d40d04d83a82b35dba77093c2e9e77f462 100644 --- a/lib-microservice-server/build.gradle +++ b/lib-microservice-server/build.gradle @@ -1,11 +1,11 @@ apply plugin: 'java-library' apply plugin: 'application' -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(11)) - } -} +//java { +// toolchain { +// languageVersion.set(JavaLanguageVersion.of(11)) +// } +//} repositories { ivy { diff --git a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/xls/importer/helper/PropertyAssignmentImportHelper.java b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/xls/importer/helper/PropertyAssignmentImportHelper.java index d0a699346d74be8462669152d8576094e531f426..507464ab8d7c5fedc2e2f876e7bd053ec4e9a46d 100644 --- a/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/xls/importer/helper/PropertyAssignmentImportHelper.java +++ b/server-application-server/source/java/ch/ethz/sis/openbis/generic/server/xls/importer/helper/PropertyAssignmentImportHelper.java @@ -121,7 +121,7 @@ public class PropertyAssignmentImportHelper extends BasicImportHelper String newVersion = getValueByColumnName(header, values, PropertyAssignmentImportHelper.Attribute.Version); String code = getValueByColumnName(header, values, PropertyAssignmentImportHelper.Attribute.Code); - return VersionUtils.isNewVersion(newVersion, VersionUtils.getStoredVersion(beforeVersions, ImportTypes.PROPERTY_TYPE.getType(), code)); + return !existingCodes.contains(code) || VersionUtils.isNewVersion(newVersion, VersionUtils.getStoredVersion(beforeVersions, ImportTypes.PROPERTY_TYPE.getType(), code)); } @Override protected boolean isObjectExist(Map<String, Integer> header, List<String> values) diff --git a/ui-eln-lims/src/core-plugins/eln-lims/1/as/webapps/eln-lims/html/img/eln-lims-dropbox-example.png b/ui-eln-lims/src/core-plugins/eln-lims/1/as/webapps/eln-lims/html/img/eln-lims-dropbox-example.png index e504ded955d53e7228bffe5c689ad6d8b3f6ec17..b8ea3973208da99f58ed56ac2e681066ededf4c4 100644 Binary files a/ui-eln-lims/src/core-plugins/eln-lims/1/as/webapps/eln-lims/html/img/eln-lims-dropbox-example.png and b/ui-eln-lims/src/core-plugins/eln-lims/1/as/webapps/eln-lims/html/img/eln-lims-dropbox-example.png differ diff --git a/ui-eln-lims/src/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/FormUtil.js b/ui-eln-lims/src/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/FormUtil.js index ec3b6587ffaea898b6a418319e9fff431ff3fe73..421c299af1bb689c8221d45b01899753bc970d35 100644 --- a/ui-eln-lims/src/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/FormUtil.js +++ b/ui-eln-lims/src/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/util/FormUtil.js @@ -1543,8 +1543,7 @@ var FormUtil = new function() { .append("<center><b>Screenshot example showing the eln-lims dropbox network folder and how the results will be visualized in the ELN after upload</b></center>") .append("The eln-lims dropbox requires a root folder with a specific name. This name contains information on where the data should be uploaded.").append("<br>") .append("1. Generate the name of the root folder with this helper tool using the form below.").append("<br>") - .append("2. The root folder should contain another folder, with a name of your choice, with the data to upload. This can have as many layers as needed.").append("<br>") - .append("3. The upload will be triggered automatically and the data will become visible in the object/experiment to which it was uploaded.").append("<br>"); + .append("2. The upload will be triggered automatically and the data will become visible in the object/experiment to which it was uploaded.").append("<br>"); // dataset type dropdown diff --git a/ui-eln-lims/src/core-plugins/eln-lims/1/dss/file-system-plugins/eln-tree/resolver-plugins/flow.py b/ui-eln-lims/src/core-plugins/eln-lims/1/dss/file-system-plugins/eln-tree/resolver-plugins/flow.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0df6ce62396370745613ed49abcd446fca61a46c 100644 --- a/ui-eln-lims/src/core-plugins/eln-lims/1/dss/file-system-plugins/eln-tree/resolver-plugins/flow.py +++ b/ui-eln-lims/src/core-plugins/eln-lims/1/dss/file-system-plugins/eln-tree/resolver-plugins/flow.py @@ -0,0 +1,26 @@ +from ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.search import DataSetSearchCriteria +from ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.fetchoptions import DataSetFetchOptions + +import script + +def addSampleChildNodes(path, samplePermId, sampleType, response, acceptor, context): + dataSetSearchCriteria = DataSetSearchCriteria() + dataSetSearchCriteria.withOrOperator() + dataSetSearchCriteria.withSample().withPermId().thatEquals(samplePermId) + parentsSearchCriteria = dataSetSearchCriteria.withSample().withParents() + parentsSearchCriteria.withPermId().thatEquals(samplePermId) + fetchOptions = DataSetFetchOptions() + fetchOptions.withType() + fetchOptions.withProperties() + fetchOptions.withSample() + dataSets = context.getApi().searchDataSets(context.getSessionToken(), dataSetSearchCriteria, fetchOptions).getObjects() + for dataSet in dataSets: + if acceptor.acceptDataSet(dataSet): + dataSetCode = dataSet.getCode() + content = context.getContentProvider().asContent(dataSetCode) + contentNode = content.getRootNode() + script.addDataSetFileNodes(path, dataSetCode, contentNode, response, acceptor, context) + +for t in ["FACS_ARIA", "INFLUX", "LSR_FORTESSA", "CYTOFLEX_S", "MOFLO_XDP", "S3E", "SONY_SH800S", "SONY_MA900"]: + acceptor.hideSampleType("%s_SPECIMEN" % t) + acceptor.sampleChildrenHandlers["%s_EXPERIMENT" % t] = addSampleChildNodes diff --git a/ui-eln-lims/src/core-plugins/eln-lims/1/dss/file-system-plugins/eln-tree/resolver-plugins/microscopy.py b/ui-eln-lims/src/core-plugins/eln-lims/1/dss/file-system-plugins/eln-tree/resolver-plugins/microscopy.py index e2d4f56fde2a475d36106902372b3b5e852f59ab..68885e4499b13be33d80e6cbb035371aa2339aa3 100644 --- a/ui-eln-lims/src/core-plugins/eln-lims/1/dss/file-system-plugins/eln-tree/resolver-plugins/microscopy.py +++ b/ui-eln-lims/src/core-plugins/eln-lims/1/dss/file-system-plugins/eln-tree/resolver-plugins/microscopy.py @@ -10,6 +10,8 @@ acceptor.hideDataSetType("MICROSCOPY_IMG_THUMBNAIL") def addSampleChildNodes(path, samplePermId, sampleType, response, acceptor, context): dataSetSearchCriteria = DataSetSearchCriteria() + dataSetSearchCriteria.withOrOperator() + dataSetSearchCriteria.withSample().withPermId().thatEquals(samplePermId) parentsSearchCriteria = dataSetSearchCriteria.withSample().withParents() parentsSearchCriteria.withPermId().thatEquals(samplePermId) fetchOptions = DataSetFetchOptions() diff --git a/ui-eln-lims/src/core-plugins/eln-lims/1/dss/file-system-plugins/eln-tree/script.py b/ui-eln-lims/src/core-plugins/eln-lims/1/dss/file-system-plugins/eln-tree/script.py index 1bfc219846fb319352ef61f54b1b4312ab0bf0af..e530f7af482e37495e087a855bbf1a2ac0347328 100644 --- a/ui-eln-lims/src/core-plugins/eln-lims/1/dss/file-system-plugins/eln-tree/script.py +++ b/ui-eln-lims/src/core-plugins/eln-lims/1/dss/file-system-plugins/eln-tree/script.py @@ -260,13 +260,11 @@ def getNode(subPath, acceptor, context): nodeType = parentNode.getType() entityType = parentNode.getEntityType() permId = parentNode.getPermId() - print(">>>>>>>>>> GET NODE: %s, %s, %s" % (path, nodeType, parentNode.permIds)) if nodeType == "EXPERIMENT": addExperimentChildNodes(parentPathString, permId, entityType, None, acceptor, context) elif nodeType == "SAMPLE": addSampleChildNodes(parentPathString, permId, entityType, None, acceptor, context) elif nodeType == "DATASET": - print("====== GET DATASET NODE: %s, %s" % (path, parentNode.permIds)) dataSetCode, contentNode, _ = getContentNode(parentNode.getPermId(), context) addDataSetFileNodes(parentPathString, dataSetCode, contentNode, None, acceptor, context) else: @@ -342,7 +340,7 @@ def addDataSetFileNodes(path, dataSetCode, contentNode, response, acceptor, cont filePath = "%s/%s" % (path, nodeName) filePermId = "%s::%s" % (dataSetCode, childNode.getRelativePath()) cachedNode = context.getCache().getNode(filePath) - if cachedNode is not None: + if cachedNode is not None and filePermId not in cachedNode.permIds: cachedNode.addPermId(filePermId) else: context.getCache().putNode(NodeWithEntityType("DATASET", filePermId, None), filePath)