From bae19c0aa75763da611ead5c22e2937047b6ff38 Mon Sep 17 00:00:00 2001
From: felmer <franz-josef.elmer@id.ethz.ch>
Date: Thu, 16 May 2019 08:02:38 +0200
Subject: [PATCH] SSDM-8127: interactive entity selector implemented. bug in
 get_entity_for_identifier() of sample.py fixed

---
 jupyter-openbis-extension/sample.py           |   2 +-
 .../static/downloadDialog.js                  |  20 +-
 .../static/entitySearcher.js                  | 278 ++++++++++++++----
 jupyter-openbis-extension/static/spinner.gif  | Bin 0 -> 34357 bytes
 jupyter-openbis-extension/static/state.js     |   6 +-
 .../static/uploadDialog.js                    |  29 +-
 6 files changed, 241 insertions(+), 94 deletions(-)
 create mode 100644 jupyter-openbis-extension/static/spinner.gif

diff --git a/jupyter-openbis-extension/sample.py b/jupyter-openbis-extension/sample.py
index 752dfe7..3b0bf54 100644
--- a/jupyter-openbis-extension/sample.py
+++ b/jupyter-openbis-extension/sample.py
@@ -5,8 +5,8 @@ from .connection import openbis_connections
 from urllib.parse import parse_qs
 
 
-
 def get_entity_for_identifier(conn, identifier):
+    entity = None
     try:
         entity = conn.openbis.get_sample(identifier)
     except Exception as exc:
diff --git a/jupyter-openbis-extension/static/downloadDialog.js b/jupyter-openbis-extension/static/downloadDialog.js
index 304d4a7..b948043 100644
--- a/jupyter-openbis-extension/static/downloadDialog.js
+++ b/jupyter-openbis-extension/static/downloadDialog.js
@@ -1,10 +1,11 @@
 define([
         "base/js/dialog",
+        "jquery",
         "./common",
         "./state",
         "./entitySearcher"
     ],
-    function (dialog, common, state, entitySearcher) {
+    function (dialog, $, common, state, entitySearcher) {
 
         var spinner = document.createElement("IMG")
         spinner.className="openbis-feedback"
@@ -120,9 +121,9 @@ define([
                 return false
             }
 
-            currentEntityIdentifier = entityIdentifier.value
+            currentEntityIdentifier = entityIdentifier.firstChild.value
             if (!currentEntityIdentifier) {
-                alert('Please specify a Sample or Experiment identifier/permId')
+                alert('Please specify an Entity identifier/permId')
                 return false
             }
             var url = env.notebook.base_url 
@@ -168,19 +169,11 @@ define([
 
                 var showDataSets = document.createElement("DIV")
                 var title = document.createElement("STRONG")
-                title.textContent = "Sample or Experiment identifier/permId: "
+                title.textContent = "Entity identifier/permId: "
                 showDataSets.appendChild(title)
                 showDataSets.style.marginTop = '10px'
 
-                // TODO Build and replace this component
-                // var entityIdentifier = entitySearcher.getEntitySearcher(state)
-
-                var entityIdentifier = document.createElement("INPUT")
-                entityIdentifier.type = "text"
-                entityIdentifier.name = "entityIdentifier"
-                entityIdentifier.size = 40
-                entityIdentifier.placeholder = "Sample or Experiment identifier/permId"
-                entityIdentifier.value = state.entityIdentifier
+                var entityIdentifier = entitySearcher.getEntitySearcherForDownload(state)
 
                 var datasets_table = document.createElement("DIV")
                 var pagingContainer = document.createElement("DIV")
@@ -228,7 +221,6 @@ define([
                 download_dialog_box.appendChild(path)
 
                 function saveState() {
-                    state.entityIdentifier = entityIdentifier.value
                     state.directPermId = datasetPermId.value
                     state.workingDirectory = downloadPath.value
                 }
diff --git a/jupyter-openbis-extension/static/entitySearcher.js b/jupyter-openbis-extension/static/entitySearcher.js
index 1b74fb9..889db40 100644
--- a/jupyter-openbis-extension/static/entitySearcher.js
+++ b/jupyter-openbis-extension/static/entitySearcher.js
@@ -1,78 +1,240 @@
-define([],
-    function () {
+define(["jquery", "./jquery-select2/js/select2.min"],
+    function($, select2) {
         return {
-            loadResorce(pathToResource, onLoad, jsOrCss) {
-                var resource = null;
+            loadResource(pathToResource, jsOrCss, onLoad) {
+                var resource = null
 
-                if(jsOrCss === 'js') {
-                    resource = document.createElement('script');
-                    resource.type = 'text/javascript';
-                    resource.src = pathToResource;
+                if (jsOrCss === 'js') {
+                    resource = document.createElement('script')
+                    resource.type = 'text/javascript'
+                    resource.src = pathToResource
                 }
-                
-                if(jsOrCss === 'css') {
-                    resource = document.createElement('link');
-                    resource.type = 'text/css';
-                    resource.rel = 'stylesheet';
-                    resource.href = pathToResource;
+
+                if (jsOrCss === 'css') {
+                    resource = document.createElement('link')
+                    resource.type = 'text/css'
+                    resource.rel = 'stylesheet'
+                    resource.href = pathToResource
                 }
 
-                resource.onload = onLoad;
-                resource.onreadystatechange= function () {
-                        if (this.readyState == 'complete') { 
-                            onLoad();
-                        }
+                resource.onload = onLoad
+                resource.onreadystatechange = function() {
+                    if (this.readyState == 'complete') {
+                        onLoad()
+                    }
                 }
-                
-                var head = document.getElementsByTagName('head')[0];
-                head.appendChild(resource);
+
+                var head = document.getElementsByTagName('head')[0]
+                head.appendChild(resource)
             },
             getRequireJSV3Config(baseURL) {
-            	return {
-					baseUrl : baseURL + "/openbis/resources/api/v3",
-					paths : {
-						"stjs" : "lib/stjs/js/stjs",
-						"underscore" : "lib/underscore/js/underscore",
-						"moment" : "lib/moment/js/moment"
-					},
-					shim : {
-						"stjs" : {
-							exports : "stjs",
-							deps : [ "underscore" ]
-						},
-						"underscore" : {
-							exports : "_"
-						}
-					}
-				}
-			},
-            getEntitySearcher(state) {
-            	var _this = this;
+                return {
+                    baseUrl: baseURL + "/openbis/resources/api/v3",
+                    paths: {
+                        "stjs": "lib/stjs/js/stjs",
+                        "underscore": "lib/underscore/js/underscore",
+                        "moment": "lib/moment/js/moment"
+                    },
+                    shim: {
+                        "stjs": {
+                            exports: "stjs",
+                            deps: ["underscore"]
+                        },
+                        "underscore": {
+                            exports: "_"
+                        }
+                    }
+                }
+            },
+            getEntitySearcherForDownload(state) {
+                return this.getEntitySearcher(state, false)
+            },
+            getEntitySearcherForUpload(state) {
+                return this.getEntitySearcher(state, true)
+            },
+            getEntitySearcher(state, upload) {
+                var _this = this
                 var connection_name = state.connection.name
                 if (!connection_name) {
                     alert('Please choose a connection')
                     return false
                 }
 
-                if(!state.openbisService) {
-                	var config = this.getRequireJSV3Config(state.connection.dto.url)
-                	require.config(config)
-	                require(['openbis'], function(openbis) {
-						var apiUrl = state.connection.dto.url + "/openbis/openbis/rmi-application-server-v3.json"
-					    var v3 = new openbis(apiUrl)
-					    v3.login(state.connection.dto.username, state.connection.dto.password)
-					    .done(function(sessionToken) {
-					    	state.openbisService = v3
-					    	alert('openbis v3 service login succeed for ' + apiUrl +' : trusted-cross-origin-domains is set.')
-					 	}).fail(function(result) {
-					 		alert('openbis v3 service login failed for ' + apiUrl +' : trusted-cross-origin-domains is probably not set.')
-            			});
-        			});
+                var element = document.createElement("SPAN")
+                element.innerHTML = "<span style='color:orange;margin:5px'>loading...</span>"
+                if (!state.openbisService) {
+                    require.config(this.getRequireJSV3Config(state.connection.dto.url))
+                    require(["openbis", "as/dto/experiment/search/ExperimentSearchCriteria", 
+                             "as/dto/experiment/fetchoptions/ExperimentFetchOptions",
+                             "as/dto/sample/search/SampleSearchCriteria",
+                             "as/dto/sample/fetchoptions/SampleFetchOptions"], function(openbis) {
+                        var apiUrl = state.connection.dto.url + "/openbis/openbis/rmi-application-server-v3.json"
+                        var v3 = new openbis(apiUrl)
+                        v3.login(state.connection.dto.username, state.connection.dto.password)
+                            .done(function(sessionToken) {
+                                state.openbisService = v3
+                                _this.loadResource("/nbextensions/openbis/jquery-select2/css/select2.min.css", 'css', function() {
+                                    _this.createDropdown(element, state, upload)
+                                })
+                            }).fail(function(result) {
+                                alert('openbis v3 service login failed for ' + apiUrl 
+                                        + " : property 'trusted-cross-origin-domains' is probably not set in service.properties.")
+                            })
+                    })
+                } else {
+                    _this.createDropdown(element, state, upload)
                 }
 
-                var element = document.createElement("SPAN")
                 return element
+            },
+            createDropdown(container, state, upload) {
+                var _this = this
+                var $select = $("<select>", {class : 'form-control'})
+                $select.attr("multiple", "multiple")
+                $select.attr("required", "required")
+
+                container.innerHTML = null
+                $select.each(function() {
+                    container.appendChild(this)
+                })
+                $select.select2({
+                    width: upload ? '100%' : '80%', 
+                    maximumSelectionLength: 1,
+                    minimumInputLength: 2,
+                    placeholder : "Entity identifier/permId",
+                    ajax: {
+                        delay: 1000,
+                        processResults: function (data) {
+                            var results = []
+
+                            for(var dIdx = 0; dIdx < data.length; dIdx++) {
+                                var group = {
+                                        text: data[dIdx].type, 
+                                        children : []
+                                }
+
+                                var entities = data[dIdx].objects
+                                for (var eIdx = 0; eIdx < entities.length; eIdx++) {
+                                    group.children.push({
+                                        id : entities[eIdx].permId.permId,
+                                        text : _this.getDisplayName(entities[eIdx]),
+                                        data : {
+                                            id : entities[eIdx].permId.permId,
+                                            text : _this.getDisplayName(entities[eIdx]),
+                                            data : entities[eIdx]
+                                        }
+                                    })
+                                }
+
+                                if (entities.length > 0) {
+                                    results.push(group)
+                                }
+                            }
+
+                            return {
+                                "results": results,
+                                "pagination": {
+                                    "more": false
+                                }
+                            }
+                        },
+                        transport: function (params, success, failure) {
+                            var searchResults = []
+                            _this.searchExperiments(state, params, function(result) {
+                                searchResults.push(result)
+                                _this.searchSamples(state, params, function(result) {
+                                    searchResults.push(result)
+                                    success(searchResults)
+                                })
+                            })
+                            return {
+                                abort : function () { /* Not implemented */ }
+                            }
+                        }
+                    }
+                })
+                $select.on("select2:select", function() {
+                    if (upload) {
+                        state.uploadEntity = _this.getSelected($select)[0]
+                    } else {
+                        state.entity = _this.getSelected($select)[0]
+                    }
+                })
+                if (upload && state.uploadEntity) {
+                    _this.addSelected($select, state.uploadEntity)
+                } else if (upload == false && state.entity) {
+                    _this.addSelected($select, state.entity)
+                }
+            },
+            searchExperiments(state, params, action) {
+                var ExperimentSearchCriteria = require("as/dto/experiment/search/ExperimentSearchCriteria")
+                var searchCriteria = new ExperimentSearchCriteria().withOrOperator()
+                searchCriteria.withCode().thatContains(params.data.q)
+                searchCriteria.withProperty("$NAME").thatContains(params.data.q)
+                var ExperimentFetchOptions = require("as/dto/experiment/fetchoptions/ExperimentFetchOptions")
+                var fetchOptions = new ExperimentFetchOptions()
+                fetchOptions.withProperties()
+                state.openbisService.searchExperiments(searchCriteria, fetchOptions).done(action)
+            },
+            searchSamples(state, params, action) {
+                var SampleSearchCriteria = require("as/dto/sample/search/SampleSearchCriteria")
+                var searchCriteria = new SampleSearchCriteria().withOrOperator()
+                searchCriteria.withCode().thatContains(params.data.q)
+                searchCriteria.withProperty("$NAME").thatContains(params.data.q)
+                var SampleFetchOptions = require("as/dto/sample/fetchoptions/SampleFetchOptions")
+                var fetchOptions = new SampleFetchOptions()
+                fetchOptions.withProperties()
+                state.openbisService.searchSamples(searchCriteria, fetchOptions).done(action)
+            },
+            getDisplayName(entity) {
+                var text = ""
+                var propertyReplacingCode = "$NAME"
+                if (entity.identifier && entity.identifier.identifier) {
+                    text = entity.identifier.identifier
+                }
+                if (entity.properties && entity.properties[propertyReplacingCode]) {
+                    text += " (" + entity.properties[propertyReplacingCode] + ")"
+                }
+                return text
+            },
+            getSelected($select) {
+                var selected = $select.select2('data')
+                var entities = []
+                for (var eIdx = 0; eIdx < selected.length; eIdx++) {
+                    if (selected[eIdx].data) {
+                        entities.push(selected[eIdx].data.data)
+                    }
+                    if (selected[eIdx].element.data) {
+                        entities.push(selected[eIdx].element.data.data)
+                    }
+                }
+                return entities
+            },
+            addSelected($select, v3entity) {
+                var text = this.getDisplayName(v3entity)
+                var id = null
+                if (v3entity.permId && v3entity.permId.permId) { //Only v3 objects supported
+                    id = v3entity.permId.permId
+                } else {
+                    throw {
+                        name: "NonV3ObjectException",
+                        message: "Object without v3 permId",
+                        toString: function() {
+                            return this.name + ": " + this.message
+                        }
+                    }
+                }
+                
+                var data = {
+                        id : id,
+                        text : text,
+                        data : v3entity
+                }
+                
+                var newOption = new Option(text, id, true, true)
+                newOption.data = data
+                $select.append(newOption).trigger('change')
             }
         }
     }
-)
\ No newline at end of file
+)
diff --git a/jupyter-openbis-extension/static/spinner.gif b/jupyter-openbis-extension/static/spinner.gif
new file mode 100644
index 0000000000000000000000000000000000000000..58d18d4c3a5baac5a486e28b639f050baceffb3e
GIT binary patch
literal 34357
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
Sn0W_g-hr8S;Qx(x;C}%vcf(Qu

literal 0
HcmV?d00001

diff --git a/jupyter-openbis-extension/static/state.js b/jupyter-openbis-extension/static/state.js
index 2a0f35c..71ea8e2 100644
--- a/jupyter-openbis-extension/static/state.js
+++ b/jupyter-openbis-extension/static/state.js
@@ -12,7 +12,7 @@ define([],
             // upload dialog
             uploadDataSetType: null,
             uploadDataSetTypes: {},
-            uploadEntityIdentifier: '',
+            uploadEntity: null,
             datasetCheckboxes: [],
             fileCheckboxes: [],
             selectedFiles: [],
@@ -20,11 +20,11 @@ define([],
 
             // download dialog
             selectedDatasets: new Set([]),
-            entityIdentifier: '',
+            entity: null,
             workingDirectory: '',
 
             // openBIS v3 connection
-            openBISV3 : null
+            openbisService : null
         }
     }
 )
\ No newline at end of file
diff --git a/jupyter-openbis-extension/static/uploadDialog.js b/jupyter-openbis-extension/static/uploadDialog.js
index 0690d40..9f848ee 100644
--- a/jupyter-openbis-extension/static/uploadDialog.js
+++ b/jupyter-openbis-extension/static/uploadDialog.js
@@ -4,9 +4,9 @@ define([
         "jquery",
         "./state",
         "./common",
+        "./entitySearcher"
     ],
-    function (dialog, utils, $, state, common) {
-
+    function (dialog, utils, $, state, common, entitySearcher) {
         var errorElements = { }
         function createErrorElement(name) {
             var element = document.createElement("STRONG")
@@ -23,10 +23,10 @@ define([
         var spinner = document.createElement("IMG")
         spinner.className="openbis-feedback"
         spinner.src=""
-        function showSpinner() {
-            spinner.src="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.5.8/ajax-loader.gif"
+        function showSpinner(env) {
+            spinner.src = env.notebook.base_url + "nbextensions/openbis/spinner.gif"
         }
-        function hideSpinner() {
+        function hideSpinner(env) {
             spinner.src=""
         }
 
@@ -68,7 +68,7 @@ define([
                         response.json()
                             .then(function (data) {
                                 var change_input_fields = function () {
-                                    hideSpinner()
+                                    hideSpinner(env)
                                     cleanErrors()
 
                                     var oldType = state.uploadDataSetType
@@ -203,22 +203,16 @@ define([
                 getDatasetTypes(env, state.connection.name, dataset_types, input_fields)
 
                 var sample_title = document.createElement("STRONG")
-                sample_title.textContent = "Sample or Experiment identifier/permId"
+                sample_title.textContent = "Entity"
 
                 var sample_error = createErrorElement('entityIdentifier')
 
-                var entityIdentifier = document.createElement("INPUT")
-                entityIdentifier.type = "text"
-                entityIdentifier.name = 'entityIdentifier'
-                entityIdentifier.placeholder = "Sample or Experiment identifier/permId"
-                entityIdentifier.value = state.uploadEntityIdentifier
-                entityIdentifier.size = "90"
-                entityIdentifier.style.width="100%"
+                var entityIdentifier = entitySearcher.getEntitySearcherForUpload(state)
 
                 var ds_title = document.createElement("STRONG")
                 var dataSetListContainer = document.createElement("DIV")
                 if (env.notebook.metadata.datasets) {
-                    ds_title.textContent = "DataSets"
+                    ds_title.textContent = "Parent DataSets"
                     dataSetListContainer.style.maxHeight="150px"
                     dataSetListContainer.style.overflow="auto"
                     get_dataset_list(env, dataSetListContainer)
@@ -256,7 +250,6 @@ define([
                             state.uploadDataSetTypes[state.uploadDataSetType][element.name] = element.value
                         }
                     }
-                    state.uploadEntityIdentifier = entityIdentifier.value
                     state.unselectedDatasets = state.datasetCheckboxes.filter(cb => !cb.checked).map(cb => cb.value)
                     state.selectedFiles = state.fileCheckboxes.filter(cb => cb.checked).map(cb => cb.value)
                 }
@@ -286,7 +279,7 @@ define([
                         "type": dataset_types.value,
                         "files": files,
                         "parents": state.datasetCheckboxes.filter(cb => cb.checked).map(cb => cb.value),
-                        "entityIdentifier": entityIdentifier.value,
+                        "entityIdentifier": entityIdentifier.firstChild.value,
                         "props": props
                     }
 
@@ -333,7 +326,7 @@ define([
                         }
                     }
 
-                    showSpinner()
+                    showSpinner(env)
                     cleanErrors()
                     utils.ajax(settings)
                     return false
-- 
GitLab