From d5b1d49b723ebd54a14fff3d1aa25515b0c0d7eb Mon Sep 17 00:00:00 2001 From: Ian Munro Date: Mon, 29 Feb 2016 14:03:04 +0000 Subject: [PATCH 01/68] Add get OriginalFileContent.m for possible consumption by getFileAnnotationContent --- .../src/annotations/getOriginalFileContent.m | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 components/tools/OmeroM/src/annotations/getOriginalFileContent.m diff --git a/components/tools/OmeroM/src/annotations/getOriginalFileContent.m b/components/tools/OmeroM/src/annotations/getOriginalFileContent.m new file mode 100644 index 00000000000..9cf44c9375c --- /dev/null +++ b/components/tools/OmeroM/src/annotations/getOriginalFileContent.m @@ -0,0 +1,53 @@ +function getOriginalFileContent(session, originalFile, path) +% GETORIGINALFILECONTENT Reads the file content of an OriginalFile obtained +% from a file annotation +% +% getOriginalFileContent(session, originalFile, path) reads the file +% content of the input originalfile and saves it to the file +% specified by the input path. +% +% +% +% See also: GETFILEANNOTATIONCONTENT + +% Copyright (C) 2016 University of Dundee & Open Microscopy Environment. +% All rights reserved. +% +% This program is free software; you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation; either version 2 of the License, or +% (at your option) any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License along +% with this program; if not, write to the Free Software Foundation, Inc., +% 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +% Input check + +if ~isa(originalFile, 'omero.model.OriginalFileI'), + assert(isLoadedFA(fileAnnotation),... + 'Not an original file:'); +end + +context = java.util.HashMap; +context.put('omero.group', '-1'); + +% Initialize raw file store +store = session.createRawFileStore(); + +% Set file annotation id +store.setFileId(originalFile.getId().getValue(), context); + +% Read data and cast into int8 +fid = fopen(path, 'w'); +byteArr = store.read(0,originalFile.getSize().getValue()); +fwrite(fid,byteArr,'*uint8'); +fclose(fid); + +% Close the file store +store.close(); \ No newline at end of file From 1efe4b60f50b68ea99ac73ef8133c77dfcfa4c78 Mon Sep 17 00:00:00 2001 From: Ian Munro Date: Mon, 29 Feb 2016 15:26:23 +0000 Subject: [PATCH 02/68] Address typo in Input check --- .../tools/OmeroM/src/annotations/getOriginalFileContent.m | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/components/tools/OmeroM/src/annotations/getOriginalFileContent.m b/components/tools/OmeroM/src/annotations/getOriginalFileContent.m index 9cf44c9375c..fe29d516193 100644 --- a/components/tools/OmeroM/src/annotations/getOriginalFileContent.m +++ b/components/tools/OmeroM/src/annotations/getOriginalFileContent.m @@ -28,11 +28,7 @@ function getOriginalFileContent(session, originalFile, path) % 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. % Input check - -if ~isa(originalFile, 'omero.model.OriginalFileI'), - assert(isLoadedFA(fileAnnotation),... - 'Not an original file:'); -end +assert(isa(originalFile, 'omero.model.OriginalFileI'),'Not an original file:'); context = java.util.HashMap; context.put('omero.group', '-1'); From 2da784e0f32674dcadb0f71b55b47d31281e1bab Mon Sep 17 00:00:00 2001 From: Ian Munro Date: Tue, 1 Mar 2016 11:48:58 +0000 Subject: [PATCH 03/68] Modify getFileAnnotationContent to consume new function. --- .../src/annotations/getFileAnnotationContent.m | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/components/tools/OmeroM/src/annotations/getFileAnnotationContent.m b/components/tools/OmeroM/src/annotations/getFileAnnotationContent.m index b91fc09ee65..9e552c33ba4 100644 --- a/components/tools/OmeroM/src/annotations/getFileAnnotationContent.m +++ b/components/tools/OmeroM/src/annotations/getFileAnnotationContent.m @@ -41,8 +41,6 @@ function getFileAnnotationContent(session, fileAnnotation, path) ip.addParamValue('group', [], @(x) isscalar(x) && isnumeric(x)); ip.parse(fileAnnotation, path); -context = java.util.HashMap; -context.put('omero.group', '-1'); if ~isa(fileAnnotation, 'omero.model.FileAnnotationI'), % Load the file annotation from the server @@ -52,17 +50,5 @@ function getFileAnnotationContent(session, fileAnnotation, path) 'Could not load the file annotation: %u', faID); end -% Initialize raw file store -store = session.createRawFileStore(); +getOriginalFileContent(session, fileAnnotation.getFile(), path) -% Set file annotation id -file = fileAnnotation.getFile(); -store.setFileId(file.getId().getValue(), context); - -% Read data and cast into int8 -fid = fopen(path, 'w'); -fwrite(fid, store.read(0, file.getSize().getValue()), 'int8'); -fclose(fid); - -% Close the file store -store.close() \ No newline at end of file From b064fb6b668788b62242d6bc1d139279f146c4a8 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Mon, 7 Mar 2016 11:13:05 +0000 Subject: [PATCH 04/68] Don't disable P/D/I right-click menu and toolbar in tags tree --- .../static/webclient/javascript/ome.tree.js | 6 ------ .../templates/webclient/data/containers.html | 12 ------------ 2 files changed, 18 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js index c3ae3682072..d1468c898d7 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js @@ -802,12 +802,6 @@ $(function() { 'items' : function(node){ var config = {}; - // Hack to disable context menu for tags tree - // Need to fix permissions using map() in api_tagged query first - if (WEBCLIENT.URLS.tree_top_level === WEBCLIENT.URLS.api_tags_and_tagged) { - return config; - } - config["create"] = { "label" : "Create new", "_disabled": true, diff --git a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html index 680383608a6..05945338cbe 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html +++ b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html @@ -753,17 +753,6 @@
- {% ifequal menu 'usertags' %} - -
    -
  • - -
  • - -
  • -
- - {% else %}
    @@ -791,7 +780,6 @@
- {% endifequal %}
From 91b4c25c59128737a3707262b532c793589e8642 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Mon, 7 Mar 2016 15:05:55 +0000 Subject: [PATCH 05/68] Right-click menu items to create Tag and Tagset --- .../static/webclient/javascript/ome.tree.js | 124 ++++++++---------- .../javascript/ome.webclient.actions.js | 4 +- .../templates/webclient/data/containers.html | 1 + 3 files changed, 56 insertions(+), 73 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js index d1468c898d7..b4271440177 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js @@ -805,7 +805,26 @@ $(function() { config["create"] = { "label" : "Create new", "_disabled": true, - "submenu": { + }; + + var tagTree = (WEBCLIENT.URLS.tree_top_level === WEBCLIENT.URLS.api_tags_and_tagged); + if (tagTree) { + config["create"]["submenu"] = { + "tagset": { + "label" : "Tag Set", + "_disabled" : true, + "icon" : WEBCLIENT.URLS.static_webclient + 'image/left_sidebar_icon_tags.png', + action : function (node) {OME.handleNewContainer("tagset"); }, + }, + "tag": { + "label" : "Tag", + "_disabled" : true, + "icon" : WEBCLIENT.URLS.static_webclient + 'image/left_sidebar_icon_tag.png', + action : function (node) {OME.handleNewContainer("tagset"); }, + } + }; + } else { + config["create"]["submenu"] = { "project": { "label" : "Project", "_disabled": true, @@ -824,8 +843,8 @@ $(function() { "icon" : WEBCLIENT.URLS.static_webclient + 'image/folder_screen16.png', action: function (node) {OME.handleNewContainer("screen"); }, } - } - }; + }; + } config["ccp"] = { "label" : "Edit", @@ -955,9 +974,21 @@ $(function() { // List of permissions related disabling // use canLink, canDelete etc classes on each node to enable/disable right-click menu - // TODO Potentially #8879 needs to be handled either by disabling all subnodes or by never - // creating them. As the menu is created anew each time there is no reason not to never create - // those nodes + var userId = WEBCLIENT.active_user_id, + canCreate = (userId === WEBCLIENT.USER.id || userId === -1); + + if(canCreate) { + // Enable tag or P/D/I submenus created above + config["create"]["_disabled"] = false; + if (tagTree) { + config["create"]["submenu"]["tagset"]["_disabled"] = false; + config["create"]["submenu"]["tag"]["_disabled"] = false; + } else { + config["create"]["submenu"]["project"]["_disabled"] = false; + config["create"]["submenu"]["dataset"]["_disabled"] = false; + config["create"]["submenu"]["screen"]["_disabled"] = false; + } + } // Disable delete if no canDelete permission if (OME.nodeHasPermission(node, 'canDelete')) { @@ -966,83 +997,34 @@ $(function() { // Enable 'Move to group' if 'canChgrp' if(OME.nodeHasPermission(node, 'canChgrp')) { - // Can chgrp everything except Plate 'run' - if (node.type !== "acquisition") { + // Can chgrp everything except Plate 'run', 'tag' and 'tagset' + if (["acquisition", "tag", "tagset"].indexOf(node.type) === -1) { config["chgrp"]["_disabled"] = false; } } - // The only cases where 'Create' menu depends on selected node are - // Project & Dataset (see below). For all others we can enable 'Create'... - if (node.type !== 'project' && node.type !== 'dataset') { - - var userId = WEBCLIENT.USER.id, - canCreate = (userId === WEBCLIENT.USER.id || userId === -1); - - if(canCreate) { - config["create"]["_disabled"] = false; - config["create"]["submenu"]["project"]["_disabled"] = false; - config["create"]["submenu"]["dataset"]["_disabled"] = false; - config["create"]["submenu"]["screen"]["_disabled"] = false; - } - } if (OME.nodeHasPermission(node, 'canLink')) { var to_paste = false, buffer = this.get_buffer(), - parent_id = node.parent; + parent_id = node.parent, + parent_type = this.get_node(parent_id).type, + node_type = node.type; if(this.can_paste() && buffer.node) { to_paste = buffer.node[0].type; } - // If canLink, handle other node types... - if (node.type === "project") { - // Project: can create Dataset - config["create"]["_disabled"] = false; - config["create"]["submenu"]["dataset"]["_disabled"] = false; - // if we have a dataset, allow paste - if (to_paste === "dataset") { - config["ccp"]["_disabled"] = false; - config["ccp"]["submenu"]["paste"]["_disabled"] = false; - } - } else if(node.type === "dataset") { - // Dataset, allow cut - config["ccp"]["_disabled"] = false; - config["ccp"]["submenu"]["cut"]["_disabled"] = false; - // If project parent, allow copy. - if (this.get_node(parent_id).type === 'project') { - config["ccp"]["submenu"]["copy"]["_disabled"] = false; - } - // we have an image, allow paste - if (to_paste === "image") { - config["ccp"]["_disabled"] = false; - config["ccp"]["submenu"]["paste"]["_disabled"] = false; - } - } else if (node.type === "image") { - // Image, allow cut + var canCut = (["dataset", "image", "plate"].indexOf(node_type) > -1); + var canCopy = ((node_type === "dataset" && parent_type === "project") || + (node_type === "image" && parent_type === "dataset") || + (node_type === "plate" && parent_type === "screen")); + var canPaste = ((node_type === "project" && to_paste === "dataset") || + (node_type === "dataset" && to_paste === "image") || + (node_type === "screen" && to_paste === "plate")); + if (canCut || canCopy || canPaste){ config["ccp"]["_disabled"] = false; - config["ccp"]["submenu"]["cut"]["_disabled"] = false; - // If dataset parent, allow copy. - if (this.get_node(parent_id).type === 'dataset') { - config["ccp"]["submenu"]["copy"]["_disabled"] = false; - } - // allow 'share' - config["share"]["_disabled"] = false; - } else if (node.type === "screen") { - // Screen: we have a Plate, allow paste - if (to_paste === "plate") { - config["ccp"]["_disabled"] = false; - config["ccp"]["submenu"]["paste"]["_disabled"] = false; - } - } else if (node.type === "plate") { - // Plate, allow cut - config["ccp"]["_disabled"] = false; - config["ccp"]["submenu"]["cut"]["_disabled"] = false; - // If Screen parent, allow copy. - if (this.get_node(parent_id).type === 'screen') { - config["ccp"]["submenu"]["copy"]["_disabled"] = false; - } - } else if (node.type === "acquisition") { - // nothing else needs to be enabled + config["ccp"]["submenu"]["cut"]["_disabled"] = !canCut; + config["ccp"]["submenu"]["copy"]["_disabled"] = !canCopy; + config["ccp"]["submenu"]["paste"]["_disabled"] = !canPaste; } } diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js index 01776087c45..739400b2dfb 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js @@ -492,8 +492,6 @@ OME.handleDelete = function(deleteUrl, filesetCheckUrl, userId) { } var notOwned = false; $.each(selected, function(index, node) { - // Add the nodes that are to be deleted - ajax_data.push(node.type + '=' + node.data.obj.id); // What types are being deleted and how many (for pluralization) var dtype = node.type; if (dtype in dtypes) { @@ -501,6 +499,8 @@ OME.handleDelete = function(deleteUrl, filesetCheckUrl, userId) { } else { dtypes[dtype] = 1; } + // Add the nodes that are to be deleted + ajax_data.push(dtype.replace('tagset', 'tag') + '=' + node.data.obj.id); // If the node type is not 'image' then ask about deleting contents if (!askDeleteContents && node.type != 'image') { askDeleteContents = true; diff --git a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html index 05945338cbe..5c4ddd337a2 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html +++ b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html @@ -60,6 +60,7 @@ WEBCLIENT.active_group_id = {{ active_group.id }}; WEBCLIENT.USER = {'id': {{ ome.user.id }} }; + WEBCLIENT.active_user_id = {{ ome.user_id }}; WEBCLIENT.URLS = {}; WEBCLIENT.URLS.webindex = "{% url 'webindex' %}"; From 76e3a439b910da9f1b92607b29ecf4e514799f89 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Mon, 7 Mar 2016 16:28:56 +0000 Subject: [PATCH 06/68] Handle creation of tags and tagsets from context menu --- .../webclient/controller/container.py | 12 +++++++++ .../webclient/static/webclient/css/dusty.css | 2 +- .../static/webclient/javascript/ome.tree.js | 4 +-- .../templates/webclient/data/containers.html | 6 ++++- .../OmeroWeb/omeroweb/webclient/views.py | 26 ++++++++++++++----- .../omeroweb/webclient/webclient_gateway.py | 19 ++++++++++++++ 6 files changed, 58 insertions(+), 11 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/controller/container.py b/components/tools/OmeroWeb/omeroweb/webclient/controller/container.py index 6fea1e1e1dc..24b9b934212 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/controller/container.py +++ b/components/tools/OmeroWeb/omeroweb/webclient/controller/container.py @@ -809,6 +809,18 @@ def createProject(self, name, description=None): def createScreen(self, name, description=None): return self.conn.createScreen(name, description) + def createTag(self, name, description=None): + tId = self.conn.createTag(name, description) + if self.tag and self.tag.getNs() == omero.constants.metadata.NSINSIGHTTAGSET: + link = omero.model.AnnotationAnnotationLinkI() + link.setParent(omero.model.TagAnnotationI(self.tag.getId(), False)) + link.setChild(omero.model.TagAnnotationI(tId, False)) + self.conn.saveObject(link) + return tId + + def createTagset(self, name, description=None): + return self.conn.createTagset(name, description) + def checkMimetype(self, file_type): if file_type is None or len(file_type) == 0: file_type = "application/octet-stream" diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/css/dusty.css b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/css/dusty.css index ec2fb7ee0e1..78871dc8302 100755 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/css/dusty.css +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/css/dusty.css @@ -440,7 +440,7 @@ button::-moz-focus-inner { */ - .tag { +.tag_annotation_wrapper .tag { display:inline-block; float:left; margin:1px 1px; diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js index b4271440177..37f07f68cd2 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js @@ -740,7 +740,7 @@ $(function() { }, 'experimenter': { 'icon' : WEBCLIENT.URLS.static_webclient + 'image/icon_user.png', - 'valid_children': ['project','dataset','screen','plate'] + 'valid_children': ['project','dataset','screen','plate', 'tag', 'tagset'] }, 'tagset': { 'icon': WEBCLIENT.URLS.static_webclient + 'image/left_sidebar_icon_tags.png', @@ -820,7 +820,7 @@ $(function() { "label" : "Tag", "_disabled" : true, "icon" : WEBCLIENT.URLS.static_webclient + 'image/left_sidebar_icon_tag.png', - action : function (node) {OME.handleNewContainer("tagset"); }, + action : function (node) {OME.handleNewContainer("tag"); }, } }; } else { diff --git a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html index 5c4ddd337a2..7a0c72d4fe9 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html +++ b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html @@ -550,7 +550,11 @@ if(selected.length == 1 && selected[0].type === 'project' && cont_type == 'dataset') { url = '{% url 'manage_action_containers' "addnewcontainer" %}project/'+selected[0].data.obj.id+'/'; parent = selected[0]; - // otherwise create an orphan of "folder_type" ('project', 'dataset', 'screen' etc. ) + // If a tagset is selected, create tag under it + } else if(selected.length == 1 && selected[0].type === 'tagset' && cont_type == 'tag') { + url = '{% url 'manage_action_containers' "addnewcontainer" %}tagset/'+selected[0].data.obj.id+'/'; + parent = selected[0]; + // otherwise create an orphan of "folder_type" ('project', 'dataset', 'screen', 'tag', 'tagset' etc. ) } else { url = '{% url 'manage_action_containers' "addnewcontainer" %}'; // Make sure top level objects get added to jsTree root (current selected may be project, dataset, image or screen) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/views.py b/components/tools/OmeroWeb/omeroweb/webclient/views.py index 8df7c07bd9d..229f8f6543e 100755 --- a/components/tools/OmeroWeb/omeroweb/webclient/views.py +++ b/components/tools/OmeroWeb/omeroweb/webclient/views.py @@ -2599,9 +2599,6 @@ def manage_action_containers(request, action, o_type=None, o_id=None, manager = None if o_type in ("dataset", "project", "image", "screen", "plate", "acquisition", "well", "comment", "file", "tag", "tagset"): - if o_type == 'tagset': - # TODO: this should be handled by the BaseContainer - o_type = 'tag' kw = {'index': index} if o_type is not None and o_id > 0: kw[str(o_type)] = long(o_id) @@ -2616,13 +2613,13 @@ def manage_action_containers(request, action, o_type=None, o_id=None, form = None if action == 'addnewcontainer': - # Used within the jsTree to add a new Project, Dataset etc under a + # Used within the jsTree to add a new Project, Dataset, Tag, Tagset etc under a # specified parent OR top-level if not request.method == 'POST': return HttpResponseRedirect(reverse("manage_action_containers", args=["edit", o_type, o_id])) - if o_type is not None and hasattr(manager, o_type) and o_id > 0: - # E.g. Parent o_type is 'project'... + if o_type == "project" and hasattr(manager, o_type) and o_id > 0: + # If Parent o_type is 'project'... form = ContainerForm(data=request.POST.copy()) if form.is_valid(): logger.debug( @@ -2638,8 +2635,22 @@ def manage_action_containers(request, action, o_type=None, o_id=None, d.update({e[0]: unicode(e[1])}) rdict = {'bad': 'true', 'errs': d} return HttpJsonResponse(rdict) + elif o_type == "tagset" and o_id > 0: + form = ContainerForm(data=request.POST.copy()) + if form.is_valid(): + name = form.cleaned_data['name'] + description = form.cleaned_data['description'] + oid = manager.createTag(name, description) + rdict = {'bad': 'false', 'id': oid} + return HttpJsonResponse(rdict) + else: + d = dict() + for e in form.errors.iteritems(): + d.update({e[0]: unicode(e[1])}) + rdict = {'bad': 'true', 'errs': d} + return HttpJsonResponse(rdict) elif request.POST.get('folder_type') in ("project", "screen", - "dataset"): + "dataset", "tag", "tagset"): # No parent specified. We can create orphaned 'project', 'dataset' # etc. form = ContainerForm(data=request.POST.copy()) @@ -2653,6 +2664,7 @@ def manage_action_containers(request, action, o_type=None, o_id=None, name, description, img_ids=request.POST.getlist('image', None)) else: + # lookup method, E.g. createTag, createProject etc. oid = getattr(manager, "create" + folder_type.capitalize())(name, description) rdict = {'bad': 'false', 'id': oid} diff --git a/components/tools/OmeroWeb/omeroweb/webclient/webclient_gateway.py b/components/tools/OmeroWeb/omeroweb/webclient/webclient_gateway.py index 950300d28ca..f6f0dfb51b3 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/webclient_gateway.py +++ b/components/tools/OmeroWeb/omeroweb/webclient/webclient_gateway.py @@ -638,6 +638,25 @@ def createScreen(self, name, description=None): sc.description = rstring(str(description)) return self.saveAndReturnId(sc) + def createTag(self, name, description=None): + """ Creates new Tag and returns ID """ + + tag = omero.model.TagAnnotationI() + tag.textValue = rstring(str(name)) + if description is not None and description != "": + tag.description = rstring(str(description)) + return self.saveAndReturnId(tag) + + def createTagset(self, name, description=None): + """ Creates new Tag Set and returns ID """ + + tag = omero.model.TagAnnotationI() + tag.textValue = rstring(str(name)) + tag.ns = rstring(omero.constants.metadata.NSINSIGHTTAGSET) + if description is not None and description != "": + tag.description = rstring(str(description)) + return self.saveAndReturnId(tag) + def createContainer(self, dtype, name, description=None): """ Creates new Project, Dataset or Screen and returns ID """ From 17569266c7c6b559f511468ddd2e479893f2fcf4 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Mon, 7 Mar 2016 16:39:46 +0000 Subject: [PATCH 07/68] Add Tag and Tagset buttons to toolbar --- .../templates/webclient/data/containers.html | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html index 7a0c72d4fe9..e818296a697 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html +++ b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html @@ -294,6 +294,8 @@ "addproject":false, 'adddataset':false, 'addscreen':false, + 'addtag': false, + 'addtagset': false, 'copy':false, 'cut':false, 'paste': false, @@ -310,6 +312,8 @@ toolbar_config["addproject"] = true; toolbar_config["adddataset"] = true; toolbar_config['addscreen'] = true; + toolbar_config["addtag"] = true; + toolbar_config["addtagset"] = true; } if(selected.length > 0) { @@ -663,6 +667,14 @@ OME.handleNewContainer("screen"); }); + $('#addtagButton').click(function() { + OME.handleNewContainer("tag"); + }); + + $('#addtagsetButton').click(function() { + OME.handleNewContainer("tagset"); + }); + $('#copyButton').click(function() { var objs = inst.get_selected(true) inst.copy(objs); @@ -760,10 +772,19 @@
    + + {% ifequal menu 'usertags' %} +
  • +
  • + {% else %}
  • + + {% endifequal %}
  • From ac34196b6c91725bc58996aac6ac6b7dca4ce74f Mon Sep 17 00:00:00 2001 From: Will Moore Date: Mon, 7 Mar 2016 22:48:18 +0000 Subject: [PATCH 08/68] Handle Tags linking/deleting in api_link --- .../static/webclient/javascript/ome.tree.js | 3 ++- .../OmeroWeb/omeroweb/webclient/views.py | 24 +++++++++++++++---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js index 37f07f68cd2..6faf75aa826 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js @@ -748,7 +748,8 @@ $(function() { }, 'tag': { 'icon': WEBCLIENT.URLS.static_webclient + 'image/left_sidebar_icon_tag.png', - 'valid_children': ['project, dataset, image, screen, plate, acquisition'] + 'valid_children': ['project, dataset, image, screen, plate, acquisition'], + 'draggable': true }, 'project': { 'icon': WEBCLIENT.URLS.static_webclient + 'image/folder16.png', diff --git a/components/tools/OmeroWeb/omeroweb/webclient/views.py b/components/tools/OmeroWeb/omeroweb/webclient/views.py index 229f8f6543e..ad0107e9dde 100755 --- a/components/tools/OmeroWeb/omeroweb/webclient/views.py +++ b/components/tools/OmeroWeb/omeroweb/webclient/views.py @@ -93,7 +93,7 @@ from omero.model import ProjectI, DatasetI, ImageI, \ ScreenI, PlateI, \ ProjectDatasetLinkI, DatasetImageLinkI, \ - ScreenPlateLinkI + ScreenPlateLinkI, AnnotationAnnotationLinkI, TagAnnotationI from omero import ApiUsageException, ServerError, CmdError from omero.rtypes import rlong, rlist @@ -811,11 +811,12 @@ def api_plate_acquisition_list(request, conn=None, **kwargs): def get_object_links(conn, parent_type, parent_id, child_type, child_ids): """ This is just used internally by api_link DELETE below """ + print 'get_object_links', parent_type, parent_id, child_type, child_ids if parent_type == 'orphaned': return None link_type = None if parent_type == 'experimenter': - if child_type == 'dataset' or child_type == 'plate': + if child_type in ['dataset', 'plate', 'tag']: # This will be a requested link if a dataset or plate is # moved from the de facto orphaned datasets/plates, it isn't # an error, but no link actually needs removing @@ -829,7 +830,9 @@ def get_object_links(conn, parent_type, parent_id, child_type, child_ids): elif parent_type == 'screen': if child_type == 'plate': link_type = 'ScreenPlateLink' - + elif parent_type == 'tagset': + if child_type == 'tag': + link_type = 'AnnotationAnnotationLink' if not link_type: raise Http404("json data needs 'parent_type' and 'child_type'") @@ -893,6 +896,12 @@ def create_link(parent_type, parent_id, child_type, child_id): l.setParent(screen) l.setChild(plate) return l + elif parent_type == 'tagset': + if child_type == 'tag': + l = AnnotationAnnotationLinkI() + l.setParent(TagAnnotationI(long(parent_id), False)) + l.setChild(TagAnnotationI(long(child_id), False)) + return l return None response = {'success': False} @@ -929,6 +938,10 @@ def create_link(parent_type, parent_id, child_type, child_id): # return remaining links in same format as json above # e.g. {"dataset":{"10":{"image":[1,2,3]}}} for rl in remainingLinks: + if not rl.loaded: + # TODO: why AnnotationAnnotationLinks not loaded? + print "Link NOT Loaded" + continue pid = rl.parent.id.val cid = rl.child.id.val # Deleting links still in progress above - ignore these @@ -950,7 +963,10 @@ def create_link(parent_type, parent_id, child_type, child_id): if request.method == 'POST' and len(linksToSave) > 0: # Need to set context to correct group (E.g parent group) - p = conn.getQueryService().get(parent_type.title(), parent_id, + ptype = parent_type.title() + if ptype in ["Tagset", "Tag"]: + ptype = "TagAnnotation" + p = conn.getQueryService().get(ptype, parent_id, conn.SERVICE_OPTS) conn.SERVICE_OPTS.setOmeroGroup(p.details.group.id.val) logger.info("api_link: Saving %s links" % len(linksToSave)) From 46624c2a5d372a6076e57d374f8dba2069d1095f Mon Sep 17 00:00:00 2001 From: Will Moore Date: Tue, 8 Mar 2016 16:31:37 +0000 Subject: [PATCH 09/68] Fix editname/tagset in manage_action_containers --- .../tools/OmeroWeb/omeroweb/webclient/controller/container.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/controller/container.py b/components/tools/OmeroWeb/omeroweb/webclient/controller/container.py index 24b9b934212..ef61eda4fd1 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/controller/container.py +++ b/components/tools/OmeroWeb/omeroweb/webclient/controller/container.py @@ -117,6 +117,8 @@ def __init__(self, conn, project=None, dataset=None, image=None, if tagset is not None: self.obj_type = "tagset" self.tag = self.conn.getObject("Annotation", tagset) + # We need to check if tagset via hasattr(manager, o_type) + self.tagset = self.tag self.assertNotNone(self.tag, tagset, "Tag") self.assertNotNone(self.tag._obj, tagset, "Tag") if comment is not None: From 7dcf0f43c16e53c8b3b7763077945c0500b83a43 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Tue, 8 Mar 2016 16:44:43 +0000 Subject: [PATCH 10/68] Fix Robot tests for right-click on P/D. Create P/D/S enabled --- components/tests/ui/testcases/web/create_scenario.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/components/tests/ui/testcases/web/create_scenario.txt b/components/tests/ui/testcases/web/create_scenario.txt index 0e93d91eb6e..49d595c771f 100644 --- a/components/tests/ui/testcases/web/create_scenario.txt +++ b/components/tests/ui/testcases/web/create_scenario.txt @@ -131,17 +131,17 @@ Test Container Creation Enabled Create Button Should Be Enabled project Create Button Should Be Enabled dataset Create Button Should Be Enabled screen - Node Popup Menu Item Should Be Disabled Project + Node Popup Menu Item Should Be Enabled Project Node Popup Menu Item Should Be Enabled Dataset - Node Popup Menu Item Should Be Disabled Screen + Node Popup Menu Item Should Be Enabled Screen Node Popup Menu Item Should Be Enabled Delete Select First Dataset With Children Create Button Should Be Enabled project Create Button Should Be Enabled dataset Create Button Should Be Enabled screen - Node Popup Menu Item Should Be Disabled Project - Node Popup Menu Item Should Be Disabled Dataset - Node Popup Menu Item Should Be Disabled Screen + Node Popup Menu Item Should Be Enabled Project + Node Popup Menu Item Should Be Enabled Dataset + Node Popup Menu Item Should Be Enabled Screen Node Popup Menu Item Should Be Enabled Delete ${imageId}= Select First Image Create Button Should Be Enabled project From 6b89ab7e71b145a53e57792a0f23512afb5f41f2 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Tue, 8 Mar 2016 16:47:03 +0000 Subject: [PATCH 11/68] Remove print statement --- components/tools/OmeroWeb/omeroweb/webclient/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/views.py b/components/tools/OmeroWeb/omeroweb/webclient/views.py index ad0107e9dde..5de844be38c 100755 --- a/components/tools/OmeroWeb/omeroweb/webclient/views.py +++ b/components/tools/OmeroWeb/omeroweb/webclient/views.py @@ -811,7 +811,6 @@ def api_plate_acquisition_list(request, conn=None, **kwargs): def get_object_links(conn, parent_type, parent_id, child_type, child_ids): """ This is just used internally by api_link DELETE below """ - print 'get_object_links', parent_type, parent_id, child_type, child_ids if parent_type == 'orphaned': return None link_type = None From f63d41996abc4295c5456fd48f0abc2eff7eb1ad Mon Sep 17 00:00:00 2001 From: Will Moore Date: Tue, 8 Mar 2016 22:45:07 +0000 Subject: [PATCH 12/68] Don't try to cut link between tag on image 'Cut' --- .../webclient/javascript/jquery.jstree.omecut_plugin.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js index c334c481d05..6b76158b5fe 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js @@ -97,9 +97,11 @@ inst.delete_node(node); - // Objects which were already orphaned require no action except to be added to the paste buffer + // Objects which were already orphaned (or parent is tag) require no action + // except to be added to the paste buffer if (parent.type !== 'experimenter' && - parent.type !== 'orphaned') { + parent.type !== 'orphaned' && + parent.type !== 'tag') { // Do the unlinking. Result will tell us whether object is orphaned // If orphaned, move object under 'orphaned' or 'experimenter' From 73714d51051cb03bab537eef5f8e7b74acd8b523 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Wed, 9 Mar 2016 09:30:05 +0000 Subject: [PATCH 13/68] flake8 fix --- components/tools/OmeroWeb/omeroweb/webclient/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/views.py b/components/tools/OmeroWeb/omeroweb/webclient/views.py index 5de844be38c..d0b91b54b21 100755 --- a/components/tools/OmeroWeb/omeroweb/webclient/views.py +++ b/components/tools/OmeroWeb/omeroweb/webclient/views.py @@ -2628,8 +2628,8 @@ def manage_action_containers(request, action, o_type=None, o_id=None, form = None if action == 'addnewcontainer': - # Used within the jsTree to add a new Project, Dataset, Tag, Tagset etc under a - # specified parent OR top-level + # Used within the jsTree to add a new Project, Dataset, Tag, + # Tagset etc under a specified parent OR top-level if not request.method == 'POST': return HttpResponseRedirect(reverse("manage_action_containers", args=["edit", o_type, o_id])) From 887a4cdc491f64f71e42e935aaee3a0fe3ddd3e2 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Wed, 9 Mar 2016 13:36:45 +0000 Subject: [PATCH 14/68] Enable cut & copy in Tag Tree correctly --- .../javascript/jquery.jstree.omecut_plugin.js | 8 ++--- .../static/webclient/javascript/ome.tree.js | 29 +++++++++++++++---- .../javascript/ome.webclient.actions.js | 2 +- .../templates/webclient/data/containers.html | 26 +++++++---------- .../OmeroWeb/omeroweb/webclient/views.py | 4 ++- 5 files changed, 42 insertions(+), 27 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js index 6b76158b5fe..4ce29ecd88a 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js @@ -93,16 +93,16 @@ // Handle this as a separate each for now $.each(tmp, function(index, node) { var parent = inst.get_node(inst.get_parent(node)); - // remove node... - inst.delete_node(node); - // Objects which were already orphaned (or parent is tag) require no action // except to be added to the paste buffer if (parent.type !== 'experimenter' && parent.type !== 'orphaned' && parent.type !== 'tag') { + // remove node... + inst.delete_node(node); + // Do the unlinking. Result will tell us whether object is orphaned // If orphaned, move object under 'orphaned' or 'experimenter' $.when(unlinkNode(inst, node, parent)).done(function(rsp) { @@ -126,7 +126,7 @@ var new_node_data = inst._get_node_data(node); // Newly orphaned objects get moved to the appropriate location - if (node.type === 'dataset' || node.type === 'plate') { + if (node.type === 'dataset' || node.type === 'plate' || node.type === 'tag') { newParent = ownerExperimenter; } else if (node.type === 'image') { // Get the orphaned directory for this experimenter diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js index 6faf75aa826..7fb443df5d9 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js @@ -787,9 +787,12 @@ $(function() { var inst = $.jstree.reference(nodes[0]); // Check if the node types are draggable and the particular nodes have the // 'canLink' permission. All must pass + // Don't allow dragging of any object from under a tag for (var index in nodes) { - if (!inst.get_rules(nodes[index]).draggable || - !OME.nodeHasPermission(nodes[index], 'canLink') + var node = nodes[index]; + if (!inst.get_rules(node).draggable || + !OME.nodeHasPermission(node, 'canLink') || + inst.get_node(node.parent).type === 'tag' ) { return false; } @@ -1014,13 +1017,29 @@ $(function() { if(this.can_paste() && buffer.node) { to_paste = buffer.node[0].type; } - var canCut = (["dataset", "image", "plate"].indexOf(node_type) > -1); + + // Currently we allow to Cut, even if we don't delete parent link! + // E.g. can Cut orphaned Image or orphaned Dataset. TODO: review this! + var canCut = (["dataset", "image", "plate", "tag"].indexOf(node_type) > -1); + // In Tag tree. Don't allow cut under tag + if (parent_type == "tag") { + canCut = false; + } + + // Currently we only allow Copy if parent is compatible?! TODO: review this! var canCopy = ((node_type === "dataset" && parent_type === "project") || (node_type === "image" && parent_type === "dataset") || - (node_type === "plate" && parent_type === "screen")); + (node_type === "plate" && parent_type === "screen") || + (node_type === "tag" && parent_type === "tagset")); + // In Tag tree, allow Copy of Dataset/Image/Plate under tag + if (["dataset", "image", "plate"].indexOf(node_type) > -1 && parent_type === "tag"){ + canCopy = true; + } + var canPaste = ((node_type === "project" && to_paste === "dataset") || (node_type === "dataset" && to_paste === "image") || - (node_type === "screen" && to_paste === "plate")); + (node_type === "screen" && to_paste === "plate") || + (node_type === "tagset" && to_paste === "tag")); if (canCut || canCopy || canPaste){ config["ccp"]["_disabled"] = false; config["ccp"]["submenu"]["cut"]["_disabled"] = !canCut; diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js index 739400b2dfb..8364bc58187 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js @@ -454,7 +454,7 @@ OME.truncateNames = (function(){ return truncateNames; }()); -// Handle deletion of selected objects in jsTree in container_tags.html and containers.html +// Handle deletion of selected objects in jsTree in containers.html OME.handleDelete = function(deleteUrl, filesetCheckUrl, userId) { var datatree = $.jstree.reference($('#dataTree')); var selected = datatree.get_selected(true); diff --git a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html index e818296a697..21e7fadd28e 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html +++ b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html @@ -93,21 +93,8 @@ // Variable to store selection data when using jstree refresh var refreshPathsReverse = []; - // Variable to enable resetting of the reload central panel timeout - // var centreReloadTimeout = false; - // function centreReload(event, data) { - - // if (centreReloadTimeout) { - // window.clearTimeout(centreReloadTimeout); - // } - - // centreReloadTimeout = window.setTimeout(function() { - // // Update the central panel - // update_thumbnails_panel(event, data); - // centreReloadTimeout = false; - // }, 200); - // } + // Called from ome.tree.js var updateParentRemoveNode = function(inst, node, parent) { /* Update any other instances of the parent of this node to remove it * Also Based on if the parent of this node has any children @@ -347,15 +334,21 @@ } - // Only allow copy/cut if the selected item(s) are elligible. This uses the slightly + // Only allow cut if the selected item(s) are elligible. This uses the slightly // confusingly named 'is_draggable' which is part of the drag'n'drop plugin // which in turn uses a jstree node type property 'draggable' // It also checks it the selected nodes can be linked if(inst.settings.dnd.is_draggable(selected)) { - toolbar_config['copy'] = true; toolbar_config['cut'] = true; } + // Allow Copy of Dataset/Image/Plate if you 'canLink' all selected nodes + var canCopy = selected.reduce(function(prev, n){ + var childType = (["dataset", "image", "screen", "tag"].indexOf(n.type) > -1); + return childType && OME.nodeHasPermission(n, 'canLink') && prev; + }, true); + toolbar_config['copy'] = canCopy; + // Only images can be added to a basket and only if they all are toolbar_config['createshare'] = true; $.each(selected, function(index, node) { @@ -470,6 +463,7 @@ // Remove duplicate nodes, normally as a result of copy_node // or move_node + // Global function, called from omecut_plugin function removeDuplicate(inst, node, parentId) { var parent = inst.get_node(parentId), found = false; diff --git a/components/tools/OmeroWeb/omeroweb/webclient/views.py b/components/tools/OmeroWeb/omeroweb/webclient/views.py index d0b91b54b21..19b891410ab 100755 --- a/components/tools/OmeroWeb/omeroweb/webclient/views.py +++ b/components/tools/OmeroWeb/omeroweb/webclient/views.py @@ -839,8 +839,10 @@ def get_object_links(conn, parent_type, parent_id, child_type, child_ids): params.addIds(child_ids) qs = conn.getQueryService() + # Need to fetch child and parent, otherwise + # AnnotationAnnotationLink is not loaded q = """ - from %s olink + from %s olink join fetch olink.child join fetch olink.parent where olink.child.id in (:ids) """ % link_type if parent_id: From 88666b8d5e8503b5a2b9d2236e0a44513ad89884 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Wed, 9 Mar 2016 21:42:38 +0000 Subject: [PATCH 15/68] Delete of Tagset leaves Tags as orphans in tree --- .../javascript/ome.webclient.actions.js | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js index 8364bc58187..b1a3c9780da 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js @@ -550,6 +550,20 @@ OME.handleDelete = function(deleteUrl, filesetCheckUrl, userId) { type: "POST", success: function(r){ + // If we've deleted Tagset, child Tags should appear as orphans in tree + // Before deleting, copy data from each child, to add back below... + var child_tags = []; + if (dtypes["tagset"]) { + selected.forEach(function(node){ + node.children.forEach(function(ch) { + ch = datatree.get_node(ch); + // _get_node_data is provided by the omecut_plugin + var d = datatree._get_node_data(ch); + child_tags.push(d); + }); + }); + } + datatree.delete_node(selected); // Update the central panel with new selection @@ -564,6 +578,12 @@ OME.handleDelete = function(deleteUrl, filesetCheckUrl, userId) { removeDuplicateNodes(datatree, node); }); + // Re-create child tags under experimenter parent + // TODO: We don't check if these are under other Tagsets + child_tags.forEach(function(d){ + datatree.create_node(firstParent, d); + }); + // Update the central panel in case delete has removed an icon $.each(selected, function(index, node) { var e = {'type': 'delete_node'}; From 65703f784406f870f4008e6e5ca9b3c245ebb183 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Wed, 9 Mar 2016 21:51:25 +0000 Subject: [PATCH 16/68] flake8 fix --- .../tools/OmeroWeb/omeroweb/webclient/controller/container.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/controller/container.py b/components/tools/OmeroWeb/omeroweb/webclient/controller/container.py index ef61eda4fd1..1c43cc214f1 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/controller/container.py +++ b/components/tools/OmeroWeb/omeroweb/webclient/controller/container.py @@ -813,7 +813,8 @@ def createScreen(self, name, description=None): def createTag(self, name, description=None): tId = self.conn.createTag(name, description) - if self.tag and self.tag.getNs() == omero.constants.metadata.NSINSIGHTTAGSET: + if (self.tag and + self.tag.getNs() == omero.constants.metadata.NSINSIGHTTAGSET): link = omero.model.AnnotationAnnotationLinkI() link.setParent(omero.model.TagAnnotationI(self.tag.getId(), False)) link.setChild(omero.model.TagAnnotationI(tId, False)) From f945e052d09686f05fb3658b8e267e10c8464846 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Thu, 10 Mar 2016 09:57:29 +0000 Subject: [PATCH 17/68] Don't show cut Dataset etc as orphans in Tag Tree --- .../static/webclient/javascript/jquery.jstree.omecut_plugin.js | 3 ++- .../webclient/templates/webclient/data/containers.html | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js index 4ce29ecd88a..4d5603ae9ae 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js @@ -118,7 +118,8 @@ } } } - if (orphaned) { + // If cut obj is now an orphan (and we're not in the TAG_TREE)... + if (orphaned && !WEBCLIENT.TAG_TREE) { // Get the experimenter that owns this object // This handles the multi-experimenters shown case var ownerExperimenter = inst.locate_node('experimenter-' + activeUserId())[0], diff --git a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html index 21e7fadd28e..278c7f3433a 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html +++ b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html @@ -79,6 +79,7 @@ WEBCLIENT.URLS.reset_rdef_json = "{% url 'reset_rdef_json' %}"; {% ifequal menu 'usertags' %} + WEBCLIENT.TAG_TREE = true; WEBCLIENT.URLS.tree_top_level = WEBCLIENT.URLS.api_tags_and_tagged; {% else %} WEBCLIENT.URLS.tree_top_level = WEBCLIENT.URLS.api_containers; From a92dd7cab417e42565830627ed458c395d0ffaed Mon Sep 17 00:00:00 2001 From: Will Moore Date: Thu, 10 Mar 2016 11:03:14 +0000 Subject: [PATCH 18/68] On tagset delete, don't orphan tags if still under other tagsets --- .../javascript/ome.webclient.actions.js | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js index b1a3c9780da..21a0241ccc8 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.webclient.actions.js @@ -572,17 +572,30 @@ OME.handleDelete = function(deleteUrl, filesetCheckUrl, userId) { if (firstParent.type !== "plate") { datatree.select_node(firstParent); } - $.each(disabledNodes, function(index, node) { - //TODO Make use of server calculated update like chgrp? - updateParentRemoveNode(datatree, node, firstParent); - removeDuplicateNodes(datatree, node); - }); - // Re-create child tags under experimenter parent - // TODO: We don't check if these are under other Tagsets - child_tags.forEach(function(d){ - datatree.create_node(firstParent, d); - }); + // Here we try to handle children of the deleted object. + // In case we deleted a "tagset", child tags should be kept as orphans under experimenter + // (unless they are found under other tag sets). + // For other objects we remove any duplicates of the object + // (E.g if "dataset" is deleted and appears in tree multiple times) + // In both cases we can only work with loaded data - Don't know if 'tag' or 'dataset' + // is under unloaded 'tagset' or 'project'. + // Would need to get this info from server as we do with 'Cut' + if (dtypes["tagset"]) { + // Re-create child tags under experimenter parent + child_tags.forEach(function(d){ + var nodeId = d.type + '-' + d.data.obj.id; + if (!datatree.locate_node(nodeId, firstParent)) { + datatree.create_node(firstParent, d); + } + }); + } else { + // Remove duplicates of the deleted object + $.each(disabledNodes, function(index, node) { + updateParentRemoveNode(datatree, node, firstParent); + removeDuplicateNodes(datatree, node); + }); + } // Update the central panel in case delete has removed an icon $.each(selected, function(index, node) { From 6f34f6fe69a8ad943179d028c0d7c36d0bf1a0fd Mon Sep 17 00:00:00 2001 From: Will Moore Date: Thu, 10 Mar 2016 11:16:03 +0000 Subject: [PATCH 19/68] Enable multi-selection of tags and tagsets in jsTree --- .../webclient/static/webclient/javascript/ome.tree.js | 3 --- components/tools/OmeroWeb/omeroweb/webclient/views.py | 8 +++++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js index 7fb443df5d9..6c0b74c56f3 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js @@ -362,9 +362,6 @@ $(function() { if (selected.length > 0 && selected[0].type !== node.type) { return false; } - if (selected.length > 0 && (node.type === 'tag' || node.type === 'tagset')) { - return false; - } // Also disallow the selection if it is a multi-select and the new target // is already selected diff --git a/components/tools/OmeroWeb/omeroweb/webclient/views.py b/components/tools/OmeroWeb/omeroweb/webclient/views.py index 19b891410ab..c6364bc70e9 100755 --- a/components/tools/OmeroWeb/omeroweb/webclient/views.py +++ b/components/tools/OmeroWeb/omeroweb/webclient/views.py @@ -2036,7 +2036,13 @@ def batch_annotate(request, conn=None, **kwargs): obj_string = "&".join(obj_ids) link_string = "|".join(obj_ids).replace("=", "-") if len(groupIds) == 0: - return handlerInternalError(request, "No objects found") + # No supported objects found. + # If multiple tags / tagsets selected, return placeholder + if (len(request.GET.getlist('tag')) > 0 or + len(request.GET.getlist('tagset')) > 0): + return HttpResponse("

    Can't batch annotate tags

    ") + else: + return handlerInternalError(request, "No objects found") groupId = list(groupIds)[0] conn.SERVICE_OPTS.setOmeroGroup(groupId) From 60e8f4d70dbe8efc25c8d174d41d1563dcdecada Mon Sep 17 00:00:00 2001 From: Will Moore Date: Thu, 10 Mar 2016 12:03:01 +0000 Subject: [PATCH 20/68] Don't allow right-click 'New' if not canLink() This replicates Insight behaviour --- .../webclient/static/webclient/javascript/ome.tree.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js index 6c0b74c56f3..3c1a805bf39 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js @@ -976,9 +976,13 @@ $(function() { // use canLink, canDelete etc classes on each node to enable/disable right-click menu var userId = WEBCLIENT.active_user_id, - canCreate = (userId === WEBCLIENT.USER.id || userId === -1); + canCreate = (userId === WEBCLIENT.USER.id || userId === -1), + canLink = OME.nodeHasPermission(node, 'canLink'); - if(canCreate) { + + // Although not everything created here will go under selected node, + // we still don't allow creation if linking not allowed + if(canCreate && canLink) { // Enable tag or P/D/I submenus created above config["create"]["_disabled"] = false; if (tagTree) { @@ -1004,7 +1008,7 @@ $(function() { } } - if (OME.nodeHasPermission(node, 'canLink')) { + if (canLink) { var to_paste = false, buffer = this.get_buffer(), parent_id = node.parent, From 9d8b0ed8c1fdf2ace54f1dcafe4fcfab97636cd2 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Thu, 10 Mar 2016 12:13:34 +0000 Subject: [PATCH 21/68] Don't allow toolbar 'New' if not canLink() --- .../webclient/templates/webclient/data/containers.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html index 278c7f3433a..d9ca588be7b 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html +++ b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html @@ -295,6 +295,13 @@ var userId = {{ ome.user_id }}, canCreate = (userId === {{ ome.user.id }} || userId === -1); + // Also need all selected items to allow linking, + // because new objects are sometimes linked under selected items + canCreate = selected.reduce(function(prev, n){ + var canLnk = (n.type === 'experimenter' || OME.nodeHasPermission(n, 'canLink')); + return canLnk && prev; + }, canCreate); + // These nodes can be Orphans, so creation is not selection-specific if (canCreate) { toolbar_config["addproject"] = true; @@ -332,7 +339,6 @@ return false; } }); - } // Only allow cut if the selected item(s) are elligible. This uses the slightly From 66a5dff4bbdccd1601aaec6941bd31d747c6b450 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Fri, 11 Mar 2016 16:00:52 +0000 Subject: [PATCH 22/68] Fix Copy / Paste enabled for robot tests --- .../webclient/static/webclient/javascript/ome.tree.js | 5 +++-- .../webclient/templates/webclient/data/containers.html | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js index 3c1a805bf39..ba9c21d43b6 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js @@ -977,12 +977,13 @@ $(function() { var userId = WEBCLIENT.active_user_id, canCreate = (userId === WEBCLIENT.USER.id || userId === -1), - canLink = OME.nodeHasPermission(node, 'canLink'); + canLink = OME.nodeHasPermission(node, 'canLink'), + parentAllowsCreate = (node.type === "orphaned" || node.type === "experimenter"); // Although not everything created here will go under selected node, // we still don't allow creation if linking not allowed - if(canCreate && canLink) { + if(canCreate && (canLink || parentAllowsCreate)) { // Enable tag or P/D/I submenus created above config["create"]["_disabled"] = false; if (tagTree) { diff --git a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html index d9ca588be7b..2087eea5dee 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html +++ b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html @@ -298,7 +298,7 @@ // Also need all selected items to allow linking, // because new objects are sometimes linked under selected items canCreate = selected.reduce(function(prev, n){ - var canLnk = (n.type === 'experimenter' || OME.nodeHasPermission(n, 'canLink')); + var canLnk = (n.type === 'experimenter' || n.type === 'orphaned' || OME.nodeHasPermission(n, 'canLink')); return canLnk && prev; }, canCreate); @@ -351,7 +351,7 @@ // Allow Copy of Dataset/Image/Plate if you 'canLink' all selected nodes var canCopy = selected.reduce(function(prev, n){ - var childType = (["dataset", "image", "screen", "tag"].indexOf(n.type) > -1); + var childType = (["dataset", "image", "plate", "tag"].indexOf(n.type) > -1); return childType && OME.nodeHasPermission(n, 'canLink') && prev; }, true); toolbar_config['copy'] = canCopy; From 8ef4c612cb52192b072f01bd195dc0814633c450 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Fri, 11 Mar 2016 16:07:08 +0000 Subject: [PATCH 23/68] Updates and fixes to Robot tests --- components/tests/ui/testcases/web/create_scenario.txt | 9 +++++++++ components/tests/ui/testcases/web/post_test.txt | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/components/tests/ui/testcases/web/create_scenario.txt b/components/tests/ui/testcases/web/create_scenario.txt index 49d595c771f..1da4b4db27e 100644 --- a/components/tests/ui/testcases/web/create_scenario.txt +++ b/components/tests/ui/testcases/web/create_scenario.txt @@ -151,7 +151,16 @@ Test Container Creation Enabled Node Popup Menu Item Should Be Enabled Dataset Node Popup Menu Item Should Be Enabled Screen Node Popup Menu Item Should Be Enabled Delete + Select Orphaned Images Section + Create Button Should Be Enabled project + Create Button Should Be Enabled dataset + Create Button Should Be Enabled screen + Node Popup Menu Item Should Be Enabled Project + Node Popup Menu Item Should Be Enabled Dataset + Node Popup Menu Item Should Be Enabled Screen + Node Popup Menu Item Should Be Disabled Delete + ${imageId}= Select First Image Thumbnail Should Be Selected ${imageId} ${imageNodeId}= Select Image By Id ${imageId} # Using hot-key fails on jsTree 3 (worked OK previously)... diff --git a/components/tests/ui/testcases/web/post_test.txt b/components/tests/ui/testcases/web/post_test.txt index 34bbd1a73e9..a2ad24adecd 100644 --- a/components/tests/ui/testcases/web/post_test.txt +++ b/components/tests/ui/testcases/web/post_test.txt @@ -36,10 +36,11 @@ Test Cut Paste Orphaned Image ${nodeId} Select Orphaned Images Section ${imageId} Select First Orphaned Image Click Element id=cutButton - Wait Until Keyword Succeeds ${TIMEOUT} ${INTERVAL} Page Should Not Contain Element xpath=//li[@id='${nodeId}']//li[@data-id='${imageId}'] + Page Should Contain Element xpath=//li[@id='${nodeId}']//li[@data-id='${imageId}'] ${dId} Select First Dataset Click Element id=pasteButton Dataset Should Contain Image ${imageId} ${dId} + Page Should Not Contain Element xpath=//li[@id='${nodeId}']//li[@data-id='${imageId}'] Test Cut Paste Dataset [Documentation] Create 2 Projects and a Dataset. Cut and Paste the Dataset. From 8fa3d05cbe367680f4ef8a4a09f4442c831c8581 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Sat, 12 Mar 2016 22:14:22 +0000 Subject: [PATCH 24/68] Toolbar 'Copy' button disabled for orphan Tag etc --- .../webclient/templates/webclient/data/containers.html | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html index 2087eea5dee..a9fd5210865 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html +++ b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html @@ -351,8 +351,13 @@ // Allow Copy of Dataset/Image/Plate if you 'canLink' all selected nodes var canCopy = selected.reduce(function(prev, n){ - var childType = (["dataset", "image", "plate", "tag"].indexOf(n.type) > -1); - return childType && OME.nodeHasPermission(n, 'canLink') && prev; + var node_type = n.type, + parent_type = inst.get_node(n.parent).type; + var plink = ((node_type === "dataset" && parent_type === "project") || + (node_type === "image" && parent_type === "dataset") || + (node_type === "plate" && parent_type === "screen") || + (node_type === "tag" && parent_type === "tagset")); + return plink && OME.nodeHasPermission(n, 'canLink') && prev; }, true); toolbar_config['copy'] = canCopy; From 6f9170ab6845631bec45b93dbbd7f204cad76c08 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Sat, 12 Mar 2016 23:14:34 +0000 Subject: [PATCH 25/68] Cut tags show up as orphaned in tag tree --- .../static/webclient/javascript/jquery.jstree.omecut_plugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js index 4d5603ae9ae..dce1c0b4ad5 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js @@ -119,7 +119,7 @@ } } // If cut obj is now an orphan (and we're not in the TAG_TREE)... - if (orphaned && !WEBCLIENT.TAG_TREE) { + if (orphaned) { // Get the experimenter that owns this object // This handles the multi-experimenters shown case var ownerExperimenter = inst.locate_node('experimenter-' + activeUserId())[0], From c489535b0a2e18fb473d80bc96b2b2563f55b24e Mon Sep 17 00:00:00 2001 From: Will Moore Date: Sat, 12 Mar 2016 23:39:34 +0000 Subject: [PATCH 26/68] Better fix for previous commit --- .../static/webclient/javascript/jquery.jstree.omecut_plugin.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js index dce1c0b4ad5..18a87d74201 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/jquery.jstree.omecut_plugin.js @@ -119,7 +119,8 @@ } } // If cut obj is now an orphan (and we're not in the TAG_TREE)... - if (orphaned) { + var wrongTree = (WEBCLIENT.TAG_TREE && (node.type === 'dataset' || node.type === 'image')); + if (orphaned && !wrongTree) { // Get the experimenter that owns this object // This handles the multi-experimenters shown case var ownerExperimenter = inst.locate_node('experimenter-' + activeUserId())[0], From 97b6f9f1b42674dc6ca03220a545e5baec28a51f Mon Sep 17 00:00:00 2001 From: Will Moore Date: Sun, 13 Mar 2016 22:57:46 +0000 Subject: [PATCH 27/68] Switch order of Tagset and Tag buttons in toolbar --- .../webclient/templates/webclient/data/containers.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html index a9fd5210865..f47d6f84884 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html +++ b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html @@ -780,10 +780,10 @@
      {% ifequal menu 'usertags' %} -
    • +
    • {% else %}
    • From 6ac402099be5d6223e0e6262351b78f761603a55 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Sun, 13 Mar 2016 23:14:02 +0000 Subject: [PATCH 28/68] Allow Copy in toolbar of Tagged ds/img/plate --- .../webclient/templates/webclient/data/containers.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html index f47d6f84884..e45609d1ed2 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html +++ b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html @@ -353,11 +353,15 @@ var canCopy = selected.reduce(function(prev, n){ var node_type = n.type, parent_type = inst.get_node(n.parent).type; + // In tag tree, can Copy a tagged object, ready to paste under a true parent node + var tagged_child = (["dataset", "image", "plate"].indexOf(node_type) > -1 && + parent_type === "tag"); + // Can also Copy objects under their true parent types (NOT orphaned tag, dataset etc) var plink = ((node_type === "dataset" && parent_type === "project") || (node_type === "image" && parent_type === "dataset") || (node_type === "plate" && parent_type === "screen") || (node_type === "tag" && parent_type === "tagset")); - return plink && OME.nodeHasPermission(n, 'canLink') && prev; + return (tagged_child || plink) && OME.nodeHasPermission(n, 'canLink') && prev; }, true); toolbar_config['copy'] = canCopy; From 0e826664309f550980006c83468cb8bb87e74c15 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Mon, 14 Mar 2016 10:15:46 +0000 Subject: [PATCH 29/68] New Dataset/Tag as sibling of selected Dataset/Tag --- .../templates/webclient/data/containers.html | 42 ++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html index e45609d1ed2..67692eab912 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html +++ b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html @@ -558,20 +558,42 @@ } }); - // If a project is selected, create dataset under it + // Default: Create an orphan of "folder_type" ('project', 'dataset', 'screen', 'tag', 'tagset' etc. ) + url = '{% url 'manage_action_containers' "addnewcontainer" %}'; + // Find the 'experimenter' node as parent + var root = inst.get_node('#'); + $.each(root.children, function(index, id) { + var node = inst.get_node(id); + if (node.type === 'experimenter' && node.data.obj.id === {{ ome.user_id }}) { + parent = node; + // Break out of each + return false; + } + }); + + // If a project is selected (or selected is a child of project) create dataset under it var url, position = 0; var parent = false; - if(selected.length == 1 && selected[0].type === 'project' && cont_type == 'dataset') { - url = '{% url 'manage_action_containers' "addnewcontainer" %}project/'+selected[0].data.obj.id+'/'; - parent = selected[0]; - // If a tagset is selected, create tag under it - } else if(selected.length == 1 && selected[0].type === 'tagset' && cont_type == 'tag') { - url = '{% url 'manage_action_containers' "addnewcontainer" %}tagset/'+selected[0].data.obj.id+'/'; - parent = selected[0]; - // otherwise create an orphan of "folder_type" ('project', 'dataset', 'screen', 'tag', 'tagset' etc. ) + if(selected.length == 1 && cont_type == 'dataset') { + if (selected[0].type === 'project') { + parent = selected[0]; + } else if (inst.get_node(selected[0].parent).type === 'project') { + parent = inst.get_node(selected[0].parent); + } + // If a tagset is selected (or selected is a child of tagset), create tag under it + } else if(selected.length == 1 && cont_type == 'tag') { + if (selected[0].type === 'tagset') { + parent = selected[0]; + } else if (inst.get_node(selected[0].parent).type === 'tagset') { + parent = inst.get_node(selected[0].parent); + } + } + if (parent) { + url = '{% url 'manage_action_containers' "addnewcontainer" %}' + parent.type + '/' + parent.data.obj.id + '/'; } else { + // otherwise create an orphan of "folder_type" ('project', 'dataset', 'screen', 'tag', 'tagset' etc. ) url = '{% url 'manage_action_containers' "addnewcontainer" %}'; - // Make sure top level objects get added to jsTree root (current selected may be project, dataset, image or screen) + // Find 'experimenter' to be parent var root = inst.get_node('#'); $.each(root.children, function(index, id) { var node = inst.get_node(id); From f04e22ee6e363d166630203fe43cb4afaacd935c Mon Sep 17 00:00:00 2001 From: Will Moore Date: Mon, 14 Mar 2016 12:41:34 +0000 Subject: [PATCH 30/68] Update 'Create Dataset' tests according to previous commit --- components/tests/ui/resources/web/tree.txt | 1 - .../ui/testcases/web/create_scenario.txt | 33 +++++++++++++------ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/components/tests/ui/resources/web/tree.txt b/components/tests/ui/resources/web/tree.txt index 7b4319398e7..7d00cfb6134 100644 --- a/components/tests/ui/resources/web/tree.txt +++ b/components/tests/ui/resources/web/tree.txt @@ -131,7 +131,6 @@ Delete Container Click Dialog Button Yes # Wait for activities to show job done, then refresh tree... Wait Until Keyword Succeeds ${TIMEOUT} ${INTERVAL} Page Should Contain Element xpath=//span[@id='jobstatus'] - Wait Until Keyword Succeeds ${TIMEOUT} ${INTERVAL} Reload Page Key Down [Arguments] ${keyCode} ${cssSelector}=body diff --git a/components/tests/ui/testcases/web/create_scenario.txt b/components/tests/ui/testcases/web/create_scenario.txt index 1da4b4db27e..339deb0fc02 100644 --- a/components/tests/ui/testcases/web/create_scenario.txt +++ b/components/tests/ui/testcases/web/create_scenario.txt @@ -62,17 +62,17 @@ Project Should Exist Wait Until Page Contains Element xpath=//li[@id='${treeRootId}']/ul/li[@id='${nodeId}'] Create Dataset Using Icon and Check If Orphaned - [Arguments] ${isOrphaned} - ${projectId}= Run Keyword If '${isOrphaned}' == '${notOrphaned}' Get Text css=tr.data_heading_id strong + [Arguments] ${isOrphaned} ${deleteDataset}=True ${projectId}=0 + # ${projectId}= Run Keyword If '${isOrphaned}' == '${notOrphaned}' Get Text css=tr.data_heading_id strong ${datasetId}= Create Dataset ${CtxDatasetName} Run Keyword If '${isOrphaned}' == '${orphaned}' Dataset Exists and Is Orphaned ${datasetId} Run Keyword If '${isOrphaned}' == '${notOrphaned}' Dataset Exists and Is Not Orphaned ${datasetId} ${projectId} - Delete Container + Run Keyword If ${deleteDataset} Delete Container [Return] ${datasetId} Create Dataset Using Right Click and Check If Orphaned - [Arguments] ${RootId} ${DatasetName} ${isOrphaned} - ${projectId}= Run Keyword If '${isOrphaned}' == '${notOrphaned}' Get Text css=tr.data_heading_id strong + [Arguments] ${RootId} ${DatasetName} ${isOrphaned} ${projectId}=0 + # ${projectId}= Run Keyword If '${isOrphaned}' == '${notOrphaned}' Get Text css=tr.data_heading_id strong Right Click Create P/D/S ${RootId} Dataset ${DatasetName} ${datasetId}= Check Right And Center Panels For Active Container Dataset ${DatasetName} Run Keyword If '${isOrphaned}' == '${orphaned}' Dataset Exists and Is Orphaned ${datasetId} @@ -224,11 +224,16 @@ Test Create Dataset Using Icon Select Experimenter Create Dataset Using Icon and Check If Orphaned ${orphaned} - Select First Project - Create Dataset Using Icon and Check If Orphaned ${notOrphaned} + # We don't delete new Dataset 'False'. Get datasetId and delete below + ${pId}= Select First Project + ${dId}= Create Dataset Using Icon and Check If Orphaned ${notOrphaned} False ${pId} - Select First Dataset - Create Dataset Using Icon and Check If Orphaned ${orphaned} + # Dataset just-created is still selected. Create sibling Dataset (with Delete) + Create Dataset Using Icon and Check If Orphaned ${notOrphaned} True ${pId} + + # Now select and Delete the new Dataset we created under Project + Select Dataset By Id ${dId} + Delete Container Select First Screen Create Dataset Using Icon and Check If Orphaned ${orphaned} @@ -249,7 +254,15 @@ Test Create Dataset Using Right Click ${pId}= Select First Project ${treeId}= Wait For Project Node ${pId} - Create Dataset Using Right Click and Check If Orphaned ${treeId} ${CtxDatasetName} ${notOrphaned} + ${dId}= Create Dataset Using Right Click and Check If Orphaned ${treeId} ${CtxDatasetName} ${notOrphaned} ${pId} + # Don't Delete new Dataset here - Test creating next Dataset as sibling... + + ${treeId}= Wait For Dataset Node ${dId} + Create Dataset Using Right Click and Check If Orphaned ${treeId} ${CtxDatasetName} ${notOrphaned} ${pId} + Delete Container + + # Now select and Delete the new Dataset we created under Project + Select Dataset By Id ${dId} Delete Container ${imageId}= Select And Expand Image From e3457dd7dd7335ddeb5f1317a36080b9ea1228ed Mon Sep 17 00:00:00 2001 From: Will Moore Date: Mon, 14 Mar 2016 14:57:36 +0000 Subject: [PATCH 31/68] Tweak tags icon to be more different than tag icon --- .../image/left_sidebar_icon_tag_locked.png | Bin 1585 -> 0 bytes .../image/left_sidebar_icon_tags.png | Bin 1478 -> 3299 bytes .../image/left_sidebar_icon_tags_locked.png | Bin 1631 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 components/tools/OmeroWeb/omeroweb/webclient/static/webclient/image/left_sidebar_icon_tag_locked.png delete mode 100644 components/tools/OmeroWeb/omeroweb/webclient/static/webclient/image/left_sidebar_icon_tags_locked.png diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/image/left_sidebar_icon_tag_locked.png b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/image/left_sidebar_icon_tag_locked.png deleted file mode 100644 index 351ebbec6a75220cd014f146b3b8e1dd1e4df84b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1585 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2co$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@%#$-iB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}j8rKN?b ziMgw#k%5_op`nF^vze2lxrwu}E08ubhnay*uaT=OP_dDPg^?RDCR`0I44f^TOblJ! z%q!9uRX$b za+mbq2Ay3zJX%dRI+6ke9tQnyQdseBk+pM%Uqq(sy-~B9v^23QL^!dDuIPRYUC7eZIp6 z8fI_%_>P|c`8T?~s%qE8iyOsPS@r$<$N1#)WEid>Ub!3-t_l^9k zJm22tDQAn$p4IH1v~jh^(l{lly*eAWo3|}Jx4|cAmVncaS0(Qptc)+O)Zp6E)%h)U z?SjCTXM8+=8r&`UyImg?^^M_VnKyef~1wq?PXaDC$rqIi#79nviN7FiDbpM z2XgcH?WUyP-k9B4>NWSsUbXefJ=*!vpRhs*&`FujOvV&$9c{*>5ICki} zP~N>=iW6)5KBv@IHN5*6EZ%wI;Im%o01c7-1wHx?A1#$hwq3ttmcqZ+%OA8eFf+Jc W(q1fW`S28|5chQTb6Mw<&;$V62U2|i diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/image/left_sidebar_icon_tags.png b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/image/left_sidebar_icon_tags.png index 82a801efd1f69993c73a77064f322f1034fe9c8a..caec5ec90dfdb5801fea6cd998f574b673974ae8 100644 GIT binary patch delta 3295 zcmV<53?TEy3*#A(B!3BTNLh0L01FcU01FcV0GgZ_000V4X+uL$P-t&-Z*ypGa3D!T zLm+T+Z)Rz1WdHzp+MQEpR8#2|J@?-9LQ9B%luK_?6$l_wLW_VDktQl32@pz%A)(n7 zQNa;KMFbnjpojyGj)066Q7jCK3fKqaA)=0hqlk*i`{8?|Yk$_f_vX$1wbwr9tn;0- z&j-K=43f59&ghTmgWD0l;*TI7}*0BAb^tj|`8MF3bZ02F3R#5n-i zEdVe{S7t~6u(trf&JYW-00;~KFj0twDF6g}0AR=?BX|IWnE(_<@>e|ZE3OddDgXd@ znX){&BsoQaTL>+22Uk}v9w^R97b_GtVFF>AKrX_0nSU8Ffiw@`^UMGMppg|3;Dhu1 zc+L*4&dxTDwhmt{>c0m6B4T3W{^ifBa6kY6;dFk{{wy!E8h|?nfNlPwCGG@hUJIag z_lst-4?wj5py}FI^KkfnJUm6Akh$5}<>chpO2k52Vaiv1{%68pz*qfj`F=e7_x0eu z;v|7GU4MZ`1o+^>%=Ap99M6&ogks$0k4OBs3;+Bb(;~!4V!2o<6ys46agIcqjPo+3 zB8fthDa9qy|77CdEc*jK-!%ZRYCZvbku9iQV*~a}ClFY4z~c7+0P?$U!PF=S1Au6Q z;m>#f??3%Vpd|o+W=WE9003S@Bra6Svp>fO0Dk~Ppn)o|K^yeJ7%adB9Ki+L!3+Fg zHiSYX#KJ-lLJDMn9CBbOtb#%)hRv`YDqt_vKpix|QD}yfa1JiQRk#j4a1Z)n2%fLC6RbVIkUx0b+_+BaR3cnT7Zv!AJxWizFb)h!jyGOOZ85F;a?DAXP{m@;!0_ zIe&*-M!JzZ$N(~e{D!NlT@zqLtGcXcuVrX|L#Xx)I%#9!{6gSJKPrN9dR61N3(c z4Tcqi$B1Vr8Jidf7-t!G7_XR2rWw%BIv?Wdily+ylO`+*KY$4Vz$Cr4+G&IO(4Q`uA9rwXSQO+7mGt}d!;r5mBU zM0dY#r|y`ZzFvTyOmC;&dA;ZQ9DOhSRQ+xGr}ak+SO&8UBnI0I&KNw!HF0k|9WTe* z@liuv!$3o&VU=N*;e?U7(SJOn)kcj*4~%KXT;n9;ZN_cJqb3F>Atp;r>P_yNQcbz0 zDW*G2J50yT%*~?B)|oY%Ju%lZ=bPu7*PGwBU|M)uEVih&xMfMQu79>|wtZn|Vi#w( z#jeBdlf9FDx_yoPJqHbk*$%56S{;6Kv~mM9!g3B(KJ}#RZ#@)!h;8Eq#KMS9gFl*neeosSBfoHYnBQIkwkyowPu(zdm zs`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMeBmZRodjHV?r+_5^X9J0W zL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0?0=B0A@}E)&XLY(4uw#D z=+@8&Vdi0r!+s1Wg@=V#hChyQh*%oYF_$%W(cD9G-$eREmPFp0XE9GXuPsV7Dn6<% zYCPIEx-_~!#x7=A%+*+(SV?S4962s3t~PFLzTf=q^M~S{;tS(@7nm=|U2u7!&cgJC zrxvL$5-d8FKz~e#PB@hCK@cja7K|nG6L%$!3VFgE!e=5c(KgYD*h5?@9!~N|DouKl z?2)`Rc_hU%r7Y#SgeR$xyi5&D-J3d|7MgY-Z8AMNy)lE5k&tmhsv%92wrA>R=4N)w ztYw9={>5&Kw=W)*2gz%*kgNq+Eef_mrsz~!DAy_nvVUh~S7yJ>iOM;atDY;(?aZ^v z+mJV$@1Ote62cPUlD4IWOIIx&SmwQ~YB{nzae3Pc;}r!fhE@iwJh+OsDs9zItL;~p zu715HdQEGAUct(O!LkCy1<%NCg+}G`0PgpNm-?d@-hMgNe6^V+j6x$b<6@S<$ z+<4_1hktL%znR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX4c}I@?e+FW+b@^R zDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i&_B8C(+grT%{XWUQ z+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?SIDu(gXbmBM!FLxzyDi(mhmCkJc;e zM-ImyzW$x>cP$Mz4ONYt#^NJzM0w=t_X*$k9t}F$c8q(h;Rn+nb{%IOFKR-X@|s4Q zQ=0o*Vq3aT%s$c9>fU<%N829{oHRUHc}nwC$!Xf@g42^{^3RN&m7RTlF8SPG+oHC6 z=YM0)-)awU@466l;nGF_i|0GMJI-A4xODQe+vO8ixL2C5I$v$-bm~0*lhaSfyPUh4 zuDM)mx$b(swR>jw=^LIm&fWCAdGQwi*43UlJ>9+YdT;l|_x0Zv-F|W>{m#p~*>@-I zt-MdXU-UrjLD@syht)q@{@mE_+<$7ocYmPs(cDM(28Dyq{*m>M4?_iynUBkc4TkHU zI6gT!;y-fz>HMcd&t%Ugo)`Y2{>!cx7B7DI)$7;J(U{Spm-3gBzioV_{p!H$8L!*M z!p0uH$#^p{Ui4P`?ZJ24cOCDe-w#jZd?0@)|7iKK^;6KN`;!@ylm7$*nDhK&Gk-1H z0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F}0006GNkln;qK6!8-0B|TJ2*CT#>sbGhgRZGyTp*Q9pxm?_My}7PH*9=Z zOXJ1sw^&|%1K-dPZr`{D#)Z^174N>J5eker?9f$W*)4qA%;EXtg@b307JqtQH!_%u zM==+V5aXifh^}1nJM7@3q}v4%$&Gw*el26%QxpYOr;C|b1oOAzp5vmau>fvH!=B?J zT3lL@;mHtuUN3T6g;g$CCF26qq06{C z8v_8Coh}C3XRA;`Zl`4FntwX#h_bsihH1A!15qX80>O&`geJ!S$fCSg zL!)Wp<2KKY0RT#tAEFWD7AF7z002ovPDHLkV1i00FD?K8 literal 1478 zcmeAS@N?(olHy`uVBq!ia0vp^f*{Pn1|+R>-G2co$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@%#$-iB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`kBtHuNWFoz#!AFNG#Ad)HBe}%?0@jth%@)C>7xhtg4GcDhpEegHnt0 zON)|$@sXws(+mtd{1$-}0$pR}Uz7=ql*AmD{N&Qy)VvZ;7h5Huj9yA+ij}j8rKN?b ziMgw#k%5_op`nF^vze2lxrwu}E08ubhnay*uc@Pjxq+d%vAKntv7w=>p{1Fbg_Dbu zg`1_Lp|hL03rw$PUU5lcUUDkT-b|pqP`yTY^;$U>r6!i-7lq{K=fF}xKt_H^esM;A zfr4|enSzF)v7P}q;th-qO!ad0Obkt->A+auz|_jf(8|C7h%^-xG<*}2GxI=tH9>I+ z2^yEwqRg_?6t|-MTm^7sSY={ytg|W5pDu>RMwUi~*aHcoH-(%4)(1L9AC!`iQWi`I zm|Q_jcs>Dg;5jWd5174*fcdCsvzZnH1LH|g7srr_TWhAA*JlnCIQ~3$^(s}-C7xSX zy$a`;SWx_x-NK4PeeZ-H>^lmD-T7h)9-f+XP3@Ab?lMPS1W-g#qHfQg`-z#Ijqp$s`%7Mz$rsv(u@ynizYWIIP42)`zNQ) zvrEQG)MGi@N8`d9YZ5NJR+Q=9Z#?V5?W>uE)^F`+zgyfd&~?n4CnaEx;48PEU7Bof zuJK7ozj)}WwA!im^TrCHL;fdiyiGJ6R-FAbr(nX?y}U7e(ivyZ75`#g)H$W2lWT&; ziQL5VE3cb#xXLXI(79?7sMQpwKBLfHZK=J=x~sycwKr`s`Mhchcl?Ck0*U#l3y*x0 zaEpAs{UH0g>|R@T)7ickvNjgDtIcK&JahQps^zDWta2Xr+!e_ZyOv#REpsKsq)1Ve z%VO;d<*t`YKXrU?zGBdIz5mND&U}1TWu_K@I}VQP{pEjsM7kRKByIEYMVjFRuS87fY|8ayg_c7&gu%-e0n+S#ugR3 zVH37k1&#z&V9WzFG(!MVNG!A7LQoNma6=|UC74u(iR8fO1#;dP z>vGJb)sOn(-c+ECWt|udyWMWcErw{O2u77kWq?B@5^@$o*GdPAdxQ>GVvs>gxGW6m zWJ%fq1Q_u`x`b7MT%;pMpqxhIOJav>R47g|*n>M^6hdH%3Y=@$+QpiPf5RBl+GSqp zBw!QaqDvSHmyhL%K`^J?s~rUlxoEHj49O)0w`*xj2}L+qgH{D{Umz=K#WWJ7QX!Sd zbxIK;Q=q6qp^;^(5TnzjckAkRj#w97_20ZqOPNI}IAGMsosj7T90 zlAzMjIuOH%6ceEsf^cIt0Hhdk3+V`8X9sdQB8`?{$Wp?pXJ`r-P6kGf5v)#(suc=7 zCy_`rwn!sb>;DlL=7NC(mGqy=3T|=b8d$#U6Yk|@rx6aWy%?^KQuBa75&p*LuBN{?#7Oa!s%cAZ1}Kh@)I_*N7BAX8JRe4dlV{&h-mWp!Uvv? z>o}jhxzie=l?WvZFQneDty$5Zedc%^e<$4+HnZ2C?7t(d^VY1-dm!3#=|JcNc%4Et zr7!1`_uEt3CU@+Nd(Zer^(@oLtx4)H9z5v1x*9s&J<#{IAbwM<%Y_S;#_AM9F^*%q z;NQIDqtb-xQd+#LzFJ#l#F`IGYQr~&l&_+i1#v?uF_o%k&EOJgXyVN`Q}6bz%Kl*A zYW`queDTA%D}MOt)@8wtM+d_%2@kd9fLC_IM;otIOe^)<*WA83*i%_oo}YJ-KmTC+ z-Ot+Na&Mlq?>{)B9%}U2i*~F(1yl)o?sdhZry$SC5HZ0lO_1*6qfz+Dlii_T`RoS}nYcs>1`E|WBW2bKEpV{BgB6PHTZ~NtX z;zOHLT@!wNzrGtfVg9(hJcKEJ_HZzL!;8C%^Wv Date: Tue, 15 Mar 2016 15:56:49 +0000 Subject: [PATCH 32/68] Remove Robot 'Test Copy Paste Orphaned Image'. Not supported now --- .../tests/ui/testcases/web/post_test.txt | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/components/tests/ui/testcases/web/post_test.txt b/components/tests/ui/testcases/web/post_test.txt index a2ad24adecd..4f2ecbe0f2e 100644 --- a/components/tests/ui/testcases/web/post_test.txt +++ b/components/tests/ui/testcases/web/post_test.txt @@ -174,25 +174,6 @@ Test Copy Paste Image Select Image By Id ${imageId} Delete Container -Test Copy Paste Orphaned Image - [Documentation] Test copy pasting an orphaned Image into a Dataset. Check if the link exists on Both locations. - - Wait Until Keyword Succeeds ${TIMEOUT} ${INTERVAL} Reload Page - Select Experimenter - ${nodeId} Select Orphaned Images Section - ${imageId} Select First Orphaned Image - Click Element id=copyButton - ${dnodeId} Wait For Dataset Node Text test copy-paste TO here - Click Node ${dnodeId} - Click Element id=pasteButton - - Click Node ${dnodeId} - Wait Until Page Contains Element xpath=//li[@id='${nodeId}']//li[@data-id='${imageId}'] - Wait Until Page Contains Element xpath=//li[@id='${dnodeId}']//li[@data-id='${imageId}'] - - Select Image By Id ${imageId} - Delete Container - Test Copy Paste Plate [Documentation] Test copy pasting a plate into another screen. Check if the link exists on both the screens. From 3f4c353357a71075a2a3a059b1a2ebda38f0c1ec Mon Sep 17 00:00:00 2001 From: Will Moore Date: Thu, 17 Mar 2016 21:59:29 +0000 Subject: [PATCH 33/68] Disable Cut and Copy of P/D/I/S/P in Tag Tree --- .../static/webclient/javascript/ome.tree.js | 14 +++++++------- .../templates/webclient/data/containers.html | 9 ++++----- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js index ba9c21d43b6..e36aab574d1 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js @@ -755,11 +755,11 @@ $(function() { 'dataset': { 'icon': WEBCLIENT.URLS.static_webclient + 'image/folder_image16.png', 'valid_children': ['image'], - 'draggable': true + 'draggable': !WEBCLIENT.TAG_TREE }, 'image': { 'icon': WEBCLIENT.URLS.static_webclient + 'image/image16.png', - 'draggable': true + 'draggable': !WEBCLIENT.TAG_TREE }, 'screen': { 'icon': WEBCLIENT.URLS.static_webclient + 'image/folder_screen16.png', @@ -768,7 +768,7 @@ $(function() { 'plate': { 'icon': WEBCLIENT.URLS.static_webclient + 'image/folder_plate16.png', 'valid_children': ['acquisition'], - 'draggable': true + 'draggable': !WEBCLIENT.TAG_TREE }, 'acquisition': { 'icon': WEBCLIENT.URLS.static_webclient + 'image/run16.png', @@ -1024,7 +1024,7 @@ $(function() { // E.g. can Cut orphaned Image or orphaned Dataset. TODO: review this! var canCut = (["dataset", "image", "plate", "tag"].indexOf(node_type) > -1); // In Tag tree. Don't allow cut under tag - if (parent_type == "tag") { + if (tagTree && node_type !== "tag") { canCut = false; } @@ -1033,9 +1033,9 @@ $(function() { (node_type === "image" && parent_type === "dataset") || (node_type === "plate" && parent_type === "screen") || (node_type === "tag" && parent_type === "tagset")); - // In Tag tree, allow Copy of Dataset/Image/Plate under tag - if (["dataset", "image", "plate"].indexOf(node_type) > -1 && parent_type === "tag"){ - canCopy = true; + // In Tag tree, can't Copy anything except tag + if (tagTree && node_type !== "tag"){ + canCopy = false; } var canPaste = ((node_type === "project" && to_paste === "dataset") || diff --git a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html index 67692eab912..22996eee978 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html +++ b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/containers.html @@ -353,15 +353,14 @@ var canCopy = selected.reduce(function(prev, n){ var node_type = n.type, parent_type = inst.get_node(n.parent).type; - // In tag tree, can Copy a tagged object, ready to paste under a true parent node - var tagged_child = (["dataset", "image", "plate"].indexOf(node_type) > -1 && - parent_type === "tag"); - // Can also Copy objects under their true parent types (NOT orphaned tag, dataset etc) + // In tag tree, can't copy anything except a tag + var invalidType = (WEBCLIENT.TAG_TREE && node_type !== "tag"); + // Can Copy objects under their true parent types (NOT orphaned tag, dataset etc) var plink = ((node_type === "dataset" && parent_type === "project") || (node_type === "image" && parent_type === "dataset") || (node_type === "plate" && parent_type === "screen") || (node_type === "tag" && parent_type === "tagset")); - return (tagged_child || plink) && OME.nodeHasPermission(n, 'canLink') && prev; + return (!invalidType) && plink && OME.nodeHasPermission(n, 'canLink') && prev; }, true); toolbar_config['copy'] = canCopy; From ac9238979a844449c2aed84462f7c712b064d3a4 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Fri, 18 Mar 2016 13:00:20 +0000 Subject: [PATCH 34/68] Remove un-needed handling of unloaded links in api_links() --- components/tools/OmeroWeb/omeroweb/webclient/views.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/views.py b/components/tools/OmeroWeb/omeroweb/webclient/views.py index c6364bc70e9..eb69f0428ba 100755 --- a/components/tools/OmeroWeb/omeroweb/webclient/views.py +++ b/components/tools/OmeroWeb/omeroweb/webclient/views.py @@ -939,10 +939,6 @@ def create_link(parent_type, parent_id, child_type, child_id): # return remaining links in same format as json above # e.g. {"dataset":{"10":{"image":[1,2,3]}}} for rl in remainingLinks: - if not rl.loaded: - # TODO: why AnnotationAnnotationLinks not loaded? - print "Link NOT Loaded" - continue pid = rl.parent.id.val cid = rl.child.id.val # Deleting links still in progress above - ignore these From a67e6be309d9d08a306e0d520f669e7711d6a2a3 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Fri, 18 Mar 2016 12:28:27 +0000 Subject: [PATCH 35/68] Move 4 manage_action_container tests to test_containers --- .../test/integration/test_containers.py | 194 ++++++++++++++++++ .../OmeroWeb/test/integration/test_csrf.py | 129 +----------- 2 files changed, 195 insertions(+), 128 deletions(-) create mode 100644 components/tools/OmeroWeb/test/integration/test_containers.py diff --git a/components/tools/OmeroWeb/test/integration/test_containers.py b/components/tools/OmeroWeb/test/integration/test_containers.py new file mode 100644 index 00000000000..d7ba3585211 --- /dev/null +++ b/components/tools/OmeroWeb/test/integration/test_containers.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2016 University of Dundee & Open Microscopy Environment. +# All rights reserved. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +""" +Tests creation, linking, editing & deletion of containers +""" + +import omero +import omero.clients +from omero.rtypes import rstring, rtime +from weblibrary import IWebTest +from weblibrary import _csrf_post_response, _post_response +from weblibrary import _csrf_get_response, _get_response +from weblibrary import _csrf_delete_response, _delete_response + +import json + +from django.test import Client +from django.core.urlresolvers import reverse + + +try: + from PIL import Image, ImageDraw # see ticket:2597 +except ImportError: # see ticket:2597 + import Image + import ImageDraw + +from random import randint as rint +import tempfile + + +class TestContainers(IWebTest): + """ + Tests creation, linking, editing & deletion of containers + """ + + def blank_image(self): + """ + Returns a new foundational Image with Channel objects attached for + view method testing. + """ + print dir(self) + + pixels = self.pix(client=self.client) + for the_c in range(pixels.getSizeC().val): + channel = omero.model.ChannelI() + channel.logicalChannel = omero.model.LogicalChannelI() + pixels.addChannel(channel) + image = pixels.getImage() + return self.sf.getUpdateService().saveAndReturnObject(image) + + def test_add_and_rename_container(self): + + # Add project + request_url = reverse("manage_action_containers", + args=["addnewcontainer"]) + data = { + 'folder_type': 'project', + 'name': 'foobar' + } + _post_response(self.django_client, request_url, data) + response = _csrf_post_response(self.django_client, request_url, data) + pid = json.loads(response.content).get("id") + + # Add dataset to the project + request_url = reverse("manage_action_containers", + args=["addnewcontainer", "project", pid]) + data = { + 'folder_type': 'dataset', + 'name': 'foobar' + } + _post_response(self.django_client, request_url, data) + _csrf_post_response(self.django_client, request_url, data) + + # Rename project + request_url = reverse("manage_action_containers", + args=["savename", "project", pid]) + data = { + 'name': 'anotherfoobar' + } + _post_response(self.django_client, request_url, data) + _csrf_post_response(self.django_client, request_url, data) + + # Change project description + request_url = reverse("manage_action_containers", + args=["savedescription", "project", pid]) + data = { + 'description': 'anotherfoobar' + } + _post_response(self.django_client, request_url, data) + _csrf_post_response(self.django_client, request_url, data) + + def test_paste_move_remove_deletamany_image(self): + + # Add dataset + request_url = reverse("manage_action_containers", + args=["addnewcontainer"]) + data = { + 'folder_type': 'dataset', + 'name': 'foobar' + } + _post_response(self.django_client, request_url, data) + response = _csrf_post_response(self.django_client, request_url, data) + did = json.loads(response.content).get("id") + + img = self.make_image() + print img + + # Link image to Dataset + request_url = reverse("api_links") + data = { + 'dataset': {did: {'image': [img.id.val]}} + } + + _post_response(self.django_client, request_url, data) + _csrf_post_response(self.django_client, + request_url, + json.dumps(data), + content_type="application/json") + + # Unlink image from Dataset + request_url = reverse("api_links") + data = { + 'dataset': {did: {'image': [img.id.val]}} + } + _delete_response(self.django_client, request_url, data) + response = _csrf_delete_response(self.django_client, + request_url, + json.dumps(data), + content_type="application/json") + # Response will contain remaining links from image (see test_links.py) + response = json.loads(response.content) + assert response == {"success": True} + + def test_create_share(self): + + img = self.make_image() + request_url = reverse("manage_action_containers", + args=["add", "share"]) + data = { + 'enable': 'on', + 'image': img.id.val, + 'members': self.user.id.val, + 'message': 'foobar' + } + + _post_response(self.django_client, request_url, data) + _csrf_post_response(self.django_client, request_url, data) + + def test_edit_share(self): + + # create images + images = [self.createTestImage(session=self.sf), + self.createTestImage(session=self.sf)] + + sid = self.sf.getShareService().createShare( + "foobar", rtime(None), images, [self.user], [], True) + + request_url = reverse("manage_action_containers", + args=["save", "share", sid]) + + data = { + 'enable': 'on', + 'image': [i.id.val for i in images], + 'members': self.user.id.val, + 'message': 'another foobar' + } + _post_response(self.django_client, request_url, data) + _csrf_post_response(self.django_client, request_url, data) + + # remove image from share + request_url = reverse("manage_action_containers", + args=["removefromshare", "share", sid]) + data = { + 'source': images[1].id.val, + } + _post_response(self.django_client, request_url, data) + _csrf_post_response(self.django_client, request_url, data) diff --git a/components/tools/OmeroWeb/test/integration/test_csrf.py b/components/tools/OmeroWeb/test/integration/test_csrf.py index 957d43a97b4..36c4091370c 100644 --- a/components/tools/OmeroWeb/test/integration/test_csrf.py +++ b/components/tools/OmeroWeb/test/integration/test_csrf.py @@ -24,7 +24,7 @@ import omero import omero.clients -from omero.rtypes import rstring, rtime +from omero.rtypes import rstring from weblibrary import IWebTest from weblibrary import _csrf_post_response, _post_response from weblibrary import _csrf_get_response, _get_response @@ -104,47 +104,6 @@ def test_forgot_password(self): _post_response(self.django_client, request_url, data) _csrf_post_response(self.django_client, request_url, data) - def test_add_and_rename_container(self): - - # Add project - request_url = reverse("manage_action_containers", - args=["addnewcontainer"]) - data = { - 'folder_type': 'project', - 'name': 'foobar' - } - _post_response(self.django_client, request_url, data) - response = _csrf_post_response(self.django_client, request_url, data) - pid = json.loads(response.content).get("id") - - # Add dataset to the project - request_url = reverse("manage_action_containers", - args=["addnewcontainer", "project", pid]) - data = { - 'folder_type': 'dataset', - 'name': 'foobar' - } - _post_response(self.django_client, request_url, data) - _csrf_post_response(self.django_client, request_url, data) - - # Rename project - request_url = reverse("manage_action_containers", - args=["savename", "project", pid]) - data = { - 'name': 'anotherfoobar' - } - _post_response(self.django_client, request_url, data) - _csrf_post_response(self.django_client, request_url, data) - - # Change project description - request_url = reverse("manage_action_containers", - args=["savedescription", "project", pid]) - data = { - 'description': 'anotherfoobar' - } - _post_response(self.django_client, request_url, data) - _csrf_post_response(self.django_client, request_url, data) - def test_move_data(self): group_id = self.new_group(experimenters=[self.user]).id.val @@ -254,92 +213,6 @@ def test_attach_file(self): # Remove file, see remove tag, # http://localhost/webclient/action/remove/[comment|tag|file]/ID/ - def test_paste_move_remove_deletamany_image(self): - - # Add dataset - request_url = reverse("manage_action_containers", - args=["addnewcontainer"]) - data = { - 'folder_type': 'dataset', - 'name': 'foobar' - } - _post_response(self.django_client, request_url, data) - response = _csrf_post_response(self.django_client, request_url, data) - did = json.loads(response.content).get("id") - - img = self.image_with_channels() - - # Link image to Dataset - request_url = reverse("api_links") - data = { - 'dataset': {did: {'image': [img.id.val]}} - } - - _post_response(self.django_client, request_url, data) - _csrf_post_response(self.django_client, - request_url, - json.dumps(data), - content_type="application/json") - - # Unlink image from Dataset - request_url = reverse("api_links") - data = { - 'dataset': {did: {'image': [img.id.val]}} - } - _delete_response(self.django_client, request_url, data) - response = _csrf_delete_response(self.django_client, - request_url, - json.dumps(data), - content_type="application/json") - # Response will contain remaining links from image (see test_links.py) - response = json.loads(response.content) - assert response == {"success": True} - - def test_create_share(self): - - img = self.image_with_channels() - request_url = reverse("manage_action_containers", - args=["add", "share"]) - data = { - 'enable': 'on', - 'image': img.id.val, - 'members': self.user.id.val, - 'message': 'foobar' - } - - _post_response(self.django_client, request_url, data) - _csrf_post_response(self.django_client, request_url, data) - - def test_edit_share(self): - - # create images - images = [self.createTestImage(session=self.sf), - self.createTestImage(session=self.sf)] - - sid = self.sf.getShareService().createShare( - "foobar", rtime(None), images, [self.user], [], True) - - request_url = reverse("manage_action_containers", - args=["save", "share", sid]) - - data = { - 'enable': 'on', - 'image': [i.id.val for i in images], - 'members': self.user.id.val, - 'message': 'another foobar' - } - _post_response(self.django_client, request_url, data) - _csrf_post_response(self.django_client, request_url, data) - - # remove image from share - request_url = reverse("manage_action_containers", - args=["removefromshare", "share", sid]) - data = { - 'source': images[1].id.val, - } - _post_response(self.django_client, request_url, data) - _csrf_post_response(self.django_client, request_url, data) - def test_edit_channel_names(self): """ From b260289bb6b0cb8e3c98ee2899f0cd198c781516 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Fri, 18 Mar 2016 12:31:08 +0000 Subject: [PATCH 36/68] Remove unused imports from test_containers.py --- .../OmeroWeb/test/integration/test_containers.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/components/tools/OmeroWeb/test/integration/test_containers.py b/components/tools/OmeroWeb/test/integration/test_containers.py index d7ba3585211..edfe8b74170 100644 --- a/components/tools/OmeroWeb/test/integration/test_containers.py +++ b/components/tools/OmeroWeb/test/integration/test_containers.py @@ -23,28 +23,16 @@ import omero import omero.clients -from omero.rtypes import rstring, rtime +from omero.rtypes import rtime from weblibrary import IWebTest from weblibrary import _csrf_post_response, _post_response -from weblibrary import _csrf_get_response, _get_response from weblibrary import _csrf_delete_response, _delete_response import json -from django.test import Client from django.core.urlresolvers import reverse -try: - from PIL import Image, ImageDraw # see ticket:2597 -except ImportError: # see ticket:2597 - import Image - import ImageDraw - -from random import randint as rint -import tempfile - - class TestContainers(IWebTest): """ Tests creation, linking, editing & deletion of containers From e8b8fd70129388fd5e07c091b394fa89e945620e Mon Sep 17 00:00:00 2001 From: Will Moore Date: Fri, 18 Mar 2016 12:38:29 +0000 Subject: [PATCH 37/68] Moved 2 manage_action_container tests to test_tags.py --- .../OmeroWeb/test/integration/test_csrf.py | 42 --------- .../OmeroWeb/test/integration/test_tags.py | 87 +++++++++++++++++++ 2 files changed, 87 insertions(+), 42 deletions(-) create mode 100644 components/tools/OmeroWeb/test/integration/test_tags.py diff --git a/components/tools/OmeroWeb/test/integration/test_csrf.py b/components/tools/OmeroWeb/test/integration/test_csrf.py index 36c4091370c..3ebef59c03d 100644 --- a/components/tools/OmeroWeb/test/integration/test_csrf.py +++ b/components/tools/OmeroWeb/test/integration/test_csrf.py @@ -130,48 +130,6 @@ def test_add_and_remove_comment(self): # Remove comment, see remove tag, # http://localhost/webclient/action/remove/[comment|tag|file]/ID/ - def test_add_edit_and_remove_tag(self): - - # Add tag - img = self.image_with_channels() - tag = self.new_tag() - request_url = reverse('annotate_tags') - data = { - 'image': img.id.val, - 'filter_mode': 'any', - 'filter_owner_mode': 'all', - 'index': 0, - 'newtags-0-description': '', - 'newtags-0-tag': 'foobar', - 'newtags-0-tagset': '', - 'newtags-INITIAL_FORMS': 0, - 'newtags-MAX_NUM_FORMS': 1000, - 'newtags-TOTAL_FORMS': 1, - 'tags': tag.id.val - } - _post_response(self.django_client, request_url, data) - _csrf_post_response(self.django_client, request_url, data) - - # Edit tag, see save container name and description - # http://localhost/webclient/action/savename/tag/ID/ - # http://localhost/webclient/action/savedescription/tag/ID/ - - # Remove tag - request_url = reverse("manage_action_containers", - args=["remove", "tag", tag.id.val]) - data = { - 'index': 0, - 'parent': "image-%i" % img.id.val - } - _post_response(self.django_client, request_url, data) - _csrf_post_response(self.django_client, request_url, data) - - # Delete tag - request_url = reverse("manage_action_containers", - args=["delete", "tag", tag.id.val]) - _post_response(self.django_client, request_url, {}) - _csrf_post_response(self.django_client, request_url, {}) - def test_attach_file(self): # Due to EOF both posts must be test separately diff --git a/components/tools/OmeroWeb/test/integration/test_tags.py b/components/tools/OmeroWeb/test/integration/test_tags.py new file mode 100644 index 00000000000..855092be7b7 --- /dev/null +++ b/components/tools/OmeroWeb/test/integration/test_tags.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# Copyright (C) 2016 University of Dundee & Open Microscopy Environment. +# All rights reserved. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +""" +Tests creation, linking, editing and deletion of Tags +""" + +import omero +import omero.clients +from omero.rtypes import rstring +from weblibrary import IWebTest +from weblibrary import _csrf_post_response, _post_response + +from django.core.urlresolvers import reverse + + +class TestCsrf(IWebTest): + """ + Tests creation, linking, editing and deletion of Tags + """ + + def new_tag(self): + """ + Returns a new Tag objects + """ + tag = omero.model.TagAnnotationI() + tag.textValue = rstring(self.uuid()) + tag.ns = rstring("pytest") + return self.sf.getUpdateService().saveAndReturnObject(tag) + + def test_add_edit_and_remove_tag(self): + + # Add tag + img = self.make_image() + tag = self.new_tag() + request_url = reverse('annotate_tags') + data = { + 'image': img.id.val, + 'filter_mode': 'any', + 'filter_owner_mode': 'all', + 'index': 0, + 'newtags-0-description': '', + 'newtags-0-tag': 'foobar', + 'newtags-0-tagset': '', + 'newtags-INITIAL_FORMS': 0, + 'newtags-MAX_NUM_FORMS': 1000, + 'newtags-TOTAL_FORMS': 1, + 'tags': tag.id.val + } + _post_response(self.django_client, request_url, data) + _csrf_post_response(self.django_client, request_url, data) + + # Edit tag, see save container name and description + # http://localhost/webclient/action/savename/tag/ID/ + # http://localhost/webclient/action/savedescription/tag/ID/ + + # Remove tag + request_url = reverse("manage_action_containers", + args=["remove", "tag", tag.id.val]) + data = { + 'index': 0, + 'parent': "image-%i" % img.id.val + } + _post_response(self.django_client, request_url, data) + _csrf_post_response(self.django_client, request_url, data) + + # Delete tag + request_url = reverse("manage_action_containers", + args=["delete", "tag", tag.id.val]) + _post_response(self.django_client, request_url, {}) + _csrf_post_response(self.django_client, request_url, {}) From 20228b4b472f282e3f5d645633ed2b39ddcb3622 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Fri, 18 Mar 2016 13:57:10 +0000 Subject: [PATCH 38/68] Add test_create_tag_and_tagset() to test_tags.py --- .../OmeroWeb/test/integration/test_tags.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/components/tools/OmeroWeb/test/integration/test_tags.py b/components/tools/OmeroWeb/test/integration/test_tags.py index 855092be7b7..deeb93cbfdb 100644 --- a/components/tools/OmeroWeb/test/integration/test_tags.py +++ b/components/tools/OmeroWeb/test/integration/test_tags.py @@ -26,7 +26,9 @@ from omero.rtypes import rstring from weblibrary import IWebTest from weblibrary import _csrf_post_response, _post_response +from weblibrary import _get_response +import json from django.core.urlresolvers import reverse @@ -44,6 +46,35 @@ def new_tag(self): tag.ns = rstring("pytest") return self.sf.getUpdateService().saveAndReturnObject(tag) + def test_create_tag_and_tagset(self): + # Create Tagset + request_url = reverse("manage_action_containers", + args=["addnewcontainer"]) + data = { + 'folder_type': 'tagset', + 'name': 'testTagset' + } + response = _csrf_post_response(self.django_client, request_url, data) + tagsetId = json.loads(response.content).get("id") + + # Add tag to the tagset + request_url = reverse("manage_action_containers", + args=["addnewcontainer", "tagset", tagsetId]) + data = { + 'folder_type': 'tag', + 'name': 'tagInTagset' + } + _post_response(self.django_client, request_url, data) + response2 = _csrf_post_response(self.django_client, request_url, data) + tagId = json.loads(response2.content).get("id") + + # Check that tag is listed under tagset... + request_url = reverse("api_tags_and_tagged") + data = {'id': tagsetId} + data = _get_response_json(self.django_client, request_url, data) + assert len(data['tags']) == 1 + assert data['tags'][0]['id'] == tagId + def test_add_edit_and_remove_tag(self): # Add tag @@ -85,3 +116,9 @@ def test_add_edit_and_remove_tag(self): args=["delete", "tag", tag.id.val]) _post_response(self.django_client, request_url, {}) _csrf_post_response(self.django_client, request_url, {}) + + +def _get_response_json(django_client, request_url, query_string): + rsp = _get_response(django_client, request_url, + query_string, status_code=200) + return json.loads(rsp.content) From afa2cd2564a56680f61416c3c7befb3ddfbd46e3 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Fri, 18 Mar 2016 14:36:24 +0000 Subject: [PATCH 39/68] Add test_edit_tag_and_tagset() to test_tags.py --- .../OmeroWeb/test/integration/test_tags.py | 58 +++++++++++++++++-- 1 file changed, 52 insertions(+), 6 deletions(-) diff --git a/components/tools/OmeroWeb/test/integration/test_tags.py b/components/tools/OmeroWeb/test/integration/test_tags.py index deeb93cbfdb..8b5e770e899 100644 --- a/components/tools/OmeroWeb/test/integration/test_tags.py +++ b/components/tools/OmeroWeb/test/integration/test_tags.py @@ -28,6 +28,7 @@ from weblibrary import _csrf_post_response, _post_response from weblibrary import _get_response +import pytest import json from django.core.urlresolvers import reverse @@ -47,16 +48,26 @@ def new_tag(self): return self.sf.getUpdateService().saveAndReturnObject(tag) def test_create_tag_and_tagset(self): - # Create Tagset + """ + Creates a Tagset then a Tag within the Tagset + """ + + tsValue = 'testTagset' request_url = reverse("manage_action_containers", args=["addnewcontainer"]) data = { 'folder_type': 'tagset', - 'name': 'testTagset' + 'name': tsValue } response = _csrf_post_response(self.django_client, request_url, data) tagsetId = json.loads(response.content).get("id") + # check creation + tagset = self.query.get('TagAnnotationI', tagsetId) + assert tagset is not None + assert tagset.ns.val == omero.constants.metadata.NSINSIGHTTAGSET + assert tagset.textValue.val == tsValue + # Add tag to the tagset request_url = reverse("manage_action_containers", args=["addnewcontainer", "tagset", tagsetId]) @@ -75,6 +86,45 @@ def test_create_tag_and_tagset(self): assert len(data['tags']) == 1 assert data['tags'][0]['id'] == tagId + @pytest.mark.parametrize("dtype", ["tagset", "tag"]) + def test_edit_tag_and_tagset(self, dtype): + """ + Creates Tag/Tagset and tests editing of name and description + """ + + request_url = reverse("manage_action_containers", + args=["addnewcontainer"]) + data = { + 'folder_type': dtype, + 'name': 'beforeEdit' + } + response = _csrf_post_response(self.django_client, request_url, data) + tagId = json.loads(response.content).get("id") + + # Edit name + request_url = reverse("manage_action_containers", + args=["savename", dtype, tagId]) + data = { + 'name': 'afterEdit' + } + response = _csrf_post_response(self.django_client, request_url, data) + + # Edit description + request_url = reverse("manage_action_containers", + args=["savedescription", dtype, tagId]) + data = { + 'description': 'New description after editing' + } + response = _csrf_post_response(self.django_client, request_url, data) + + # check edited name and description + tagset = self.query.get('TagAnnotationI', tagId) + assert tagset is not None + if dtype == "tagset": + assert tagset.ns.val == omero.constants.metadata.NSINSIGHTTAGSET + assert tagset.textValue.val == 'afterEdit' + assert tagset.description.val == 'New description after editing' + def test_add_edit_and_remove_tag(self): # Add tag @@ -97,10 +147,6 @@ def test_add_edit_and_remove_tag(self): _post_response(self.django_client, request_url, data) _csrf_post_response(self.django_client, request_url, data) - # Edit tag, see save container name and description - # http://localhost/webclient/action/savename/tag/ID/ - # http://localhost/webclient/action/savedescription/tag/ID/ - # Remove tag request_url = reverse("manage_action_containers", args=["remove", "tag", tag.id.val]) From 0cb5f5b9f24913615d1aac88118edb09b60022ec Mon Sep 17 00:00:00 2001 From: Will Moore Date: Fri, 18 Mar 2016 15:21:55 +0000 Subject: [PATCH 40/68] Add test_link_unlink_tagset_tags() to test_links.py --- .../OmeroWeb/test/integration/test_links.py | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/components/tools/OmeroWeb/test/integration/test_links.py b/components/tools/OmeroWeb/test/integration/test_links.py index a4fc03c491e..587b9f548a3 100644 --- a/components/tools/OmeroWeb/test/integration/test_links.py +++ b/components/tools/OmeroWeb/test/integration/test_links.py @@ -150,6 +150,48 @@ def test_link_datasets_images(self, datasets, images): assert rsp['images'][0]['id'] == iids[0] assert rsp['images'][1]['id'] == iids[1] + def test_link_unlink_tagset_tags(self): + """ + Tests linking of tagset to tag, then unlinking + """ + tag = self.make_tag() + tagset = self.make_tag(ns=omero.constants.metadata.NSINSIGHTTAGSET) + tagId = tag.id.val + tagsetId = tagset.id.val + + links_url = reverse("api_links") + # Link tagset to tag + data = { + 'tagset': {tagsetId: {'tag': [tagId]}} + } + rsp = _csrf_post_response_json(self.django_client, links_url, data) + assert rsp == {"success": True} + + # Check that tag is listed under tagset... + tags_url = reverse("api_tags_and_tagged") + r = _get_response_json(self.django_client, tags_url, {'id': tagsetId}) + assert len(r['tags']) == 1 + assert r['tags'][0]['id'] == tagId + + # Unlink first Tag from Tagset + # data {} is same as for creating link above + response = _csrf_delete_response(self.django_client, + links_url, + json.dumps(data), + content_type="application/json") + response = json.loads(response.content) + assert response["success"] + + # Since the Delete is ansync - need to check repeatedly for deletion + for i in range(10): + rsp = _get_response_json(self.django_client, + tags_url, {'id': tagsetId}) + if len(rsp['tags']) == 0: + break + sleep(0.5) + # Check that link has been deleted + assert len(rsp['tags']) == 0 + def test_unlink_screen_plate(self, screens, plates): # Link both plates to both screens request_url = reverse("api_links") From 7464dd243a7bf30c45ce6bfa985fc47f3b03cb18 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Fri, 18 Mar 2016 15:38:29 +0000 Subject: [PATCH 41/68] Move create_link() out of api_links() in views.py --- .../OmeroWeb/omeroweb/webclient/views.py | 79 ++++++++++--------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/views.py b/components/tools/OmeroWeb/omeroweb/webclient/views.py index eb69f0428ba..9a03d1983c8 100755 --- a/components/tools/OmeroWeb/omeroweb/webclient/views.py +++ b/components/tools/OmeroWeb/omeroweb/webclient/views.py @@ -857,6 +857,46 @@ def get_object_links(conn, parent_type, parent_id, child_type, child_ids): return link_type, res +def create_link(parent_type, parent_id, child_type, child_id): + """ This is just used internally by api_link DELETE below """ + if parent_type == 'experimenter': + if child_type == 'dataset' or child_type == 'plate': + # This is actually not a link that needs creating, this + # dataset/plate is an orphan + return 'orphan' + if parent_type == 'project': + project = ProjectI(long(parent_id), False) + if child_type == 'dataset': + dataset = DatasetI(long(child_id), False) + l = ProjectDatasetLinkI() + l.setParent(project) + l.setChild(dataset) + return l + elif parent_type == 'dataset': + dataset = DatasetI(long(parent_id), False) + if child_type == 'image': + image = ImageI(long(child_id), False) + l = DatasetImageLinkI() + l.setParent(dataset) + l.setChild(image) + return l + elif parent_type == 'screen': + screen = ScreenI(long(parent_id), False) + if child_type == 'plate': + plate = PlateI(long(child_id), False) + l = ScreenPlateLinkI() + l.setParent(screen) + l.setChild(plate) + return l + elif parent_type == 'tagset': + if child_type == 'tag': + l = AnnotationAnnotationLinkI() + l.setParent(TagAnnotationI(long(parent_id), False)) + l.setChild(TagAnnotationI(long(child_id), False)) + return l + return None + + @login_required() def api_links(request, conn=None, **kwargs): """ Creates or Deletes links between objects specified by a json @@ -866,45 +906,6 @@ def api_links(request, conn=None, **kwargs): (E.g. adding an image to a Dataset that already has that image). """ - def create_link(parent_type, parent_id, child_type, child_id): - # TODO Handle more types of link - if parent_type == 'experimenter': - if child_type == 'dataset' or child_type == 'plate': - # This is actually not a link that needs creating, this - # dataset/plate is an orphan - return 'orphan' - if parent_type == 'project': - project = ProjectI(long(parent_id), False) - if child_type == 'dataset': - dataset = DatasetI(long(child_id), False) - l = ProjectDatasetLinkI() - l.setParent(project) - l.setChild(dataset) - return l - elif parent_type == 'dataset': - dataset = DatasetI(long(parent_id), False) - if child_type == 'image': - image = ImageI(long(child_id), False) - l = DatasetImageLinkI() - l.setParent(dataset) - l.setChild(image) - return l - elif parent_type == 'screen': - screen = ScreenI(long(parent_id), False) - if child_type == 'plate': - plate = PlateI(long(child_id), False) - l = ScreenPlateLinkI() - l.setParent(screen) - l.setChild(plate) - return l - elif parent_type == 'tagset': - if child_type == 'tag': - l = AnnotationAnnotationLinkI() - l.setParent(TagAnnotationI(long(parent_id), False)) - l.setChild(TagAnnotationI(long(child_id), False)) - return l - return None - response = {'success': False} # Handle link creation/deletion From 4da68f85f11636151f6194bc482dd59bebb1a0d6 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Fri, 18 Mar 2016 15:54:27 +0000 Subject: [PATCH 42/68] Split api_links() to handle POST and DELETE separately --- .../OmeroWeb/omeroweb/webclient/views.py | 123 +++++++++++------- 1 file changed, 74 insertions(+), 49 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/views.py b/components/tools/OmeroWeb/omeroweb/webclient/views.py index 9a03d1983c8..2e8ea170c43 100755 --- a/components/tools/OmeroWeb/omeroweb/webclient/views.py +++ b/components/tools/OmeroWeb/omeroweb/webclient/views.py @@ -899,7 +899,22 @@ def create_link(parent_type, parent_id, child_type, child_id): @login_required() def api_links(request, conn=None, **kwargs): - """ Creates or Deletes links between objects specified by a json + """ + Entry point for the api_links methods. + We delegate depending on request method to + create or delete links between objects. + """ + # Handle link creation/deletion + json_data = json.loads(request.body) + + if request.method == 'POST': + return _api_links_POST(conn, json_data) + elif request.method == 'DELETE': + return _api_links_DELETE(conn, json_data) + + +def _api_links_POST(conn, json_data, **kwargs): + """ Creates links between objects specified by a json blob in the request body. e.g. {"dataset":{"10":{"image":[1,2,3]}}} When creating a link, fails silently if ValidationException @@ -908,9 +923,6 @@ def api_links(request, conn=None, **kwargs): response = {'success': False} - # Handle link creation/deletion - json_data = json.loads(request.body) - # json is [parent_type][parent_id][child_type][childIds] # e.g. {"dataset":{"10":{"image":[1,2,3]}}} @@ -920,46 +932,14 @@ def api_links(request, conn=None, **kwargs): continue for parent_id, children in parents.items(): for child_type, child_ids in children.items(): - if request.method == 'DELETE': - objLnks = get_object_links(conn, parent_type, - parent_id, - child_type, - child_ids) - if objLnks is None: - continue - linkType, links = objLnks - linkIds = [r.id.val for r in links] - logger.info("api_link: Deleting %s links" % len(linkIds)) - conn.deleteObjects(linkType, linkIds) - # webclient needs to know what is orphaned - linkType, remainingLinks = get_object_links(conn, - parent_type, - None, - child_type, - child_ids) - # return remaining links in same format as json above - # e.g. {"dataset":{"10":{"image":[1,2,3]}}} - for rl in remainingLinks: - pid = rl.parent.id.val - cid = rl.child.id.val - # Deleting links still in progress above - ignore these - if pid == int(parent_id): - continue - if parent_type not in response: - response[parent_type] = {} - if pid not in response[parent_type]: - response[parent_type][pid] = {child_type: []} - response[parent_type][pid][child_type].append(cid) - - elif request.method == 'POST': - for child_id in child_ids: - parent_id = int(parent_id) - link = create_link(parent_type, parent_id, - child_type, child_id) - if link and link != 'orphan': - linksToSave.append(link) - - if request.method == 'POST' and len(linksToSave) > 0: + for child_id in child_ids: + parent_id = int(parent_id) + link = create_link(parent_type, parent_id, + child_type, child_id) + if link and link != 'orphan': + linksToSave.append(link) + + if len(linksToSave) > 0: # Need to set context to correct group (E.g parent group) ptype = parent_type.title() if ptype in ["Tagset", "Tag"]: @@ -985,12 +965,57 @@ def api_links(request, conn=None, **kwargs): pass response['success'] = True - elif request.method == 'DELETE': - # If we got here, DELETE was OK - response['success'] = True + return HttpJsonResponse(response) + + +def _api_links_DELETE(conn, json_data): + """ Deletes links between objects specified by a json + blob in the request body. + e.g. {"dataset":{"10":{"image":[1,2,3]}}} + """ + + response = {'success': False} + + # json is [parent_type][parent_id][child_type][childIds] + # e.g. {"dataset":{"10":{"image":[1,2,3]}}} + for parent_type, parents in json_data.items(): + if parent_type == "orphaned": + continue + for parent_id, children in parents.items(): + for child_type, child_ids in children.items(): + objLnks = get_object_links(conn, parent_type, + parent_id, + child_type, + child_ids) + if objLnks is None: + continue + linkType, links = objLnks + linkIds = [r.id.val for r in links] + logger.info("api_link: Deleting %s links" % len(linkIds)) + conn.deleteObjects(linkType, linkIds) + # webclient needs to know what is orphaned + linkType, remainingLinks = get_object_links(conn, + parent_type, + None, + child_type, + child_ids) + # return remaining links in same format as json above + # e.g. {"dataset":{"10":{"image":[1,2,3]}}} + for rl in remainingLinks: + pid = rl.parent.id.val + cid = rl.child.id.val + # Deleting links still in progress above - ignore these + if pid == int(parent_id): + continue + if parent_type not in response: + response[parent_type] = {} + if pid not in response[parent_type]: + response[parent_type][pid] = {child_type: []} + response[parent_type][pid][child_type].append(cid) + + # If we got here, DELETE was OK + response['success'] = True - # Currently we don't use the response for anything. - # Could return more info in future if useful? return HttpJsonResponse(response) From 8c16c4fefacfaf5c035f0f07a159c37412a45438 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Fri, 18 Mar 2016 22:02:20 +0000 Subject: [PATCH 43/68] flake8 --- components/tools/OmeroWeb/test/integration/test_csrf.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/components/tools/OmeroWeb/test/integration/test_csrf.py b/components/tools/OmeroWeb/test/integration/test_csrf.py index 3ebef59c03d..d1a9c4f66ac 100644 --- a/components/tools/OmeroWeb/test/integration/test_csrf.py +++ b/components/tools/OmeroWeb/test/integration/test_csrf.py @@ -28,9 +28,6 @@ from weblibrary import IWebTest from weblibrary import _csrf_post_response, _post_response from weblibrary import _csrf_get_response, _get_response -from weblibrary import _csrf_delete_response, _delete_response - -import json from django.test import Client from django.core.urlresolvers import reverse From cd9c6c4a068c406275c0b39f0c89d9ef19084362 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Wed, 23 Mar 2016 12:00:50 +0000 Subject: [PATCH 44/68] jsTree sort plugin handles images and acquisitions (under tags) This fixes a very strange bug when copy and pasting a tag with images and runs from one Tagset to another. The bug occurred if the Run was not the last item (after images). As described in https://github.com/openmicroscopy/openmicroscopy/pull/4518#issuecomment-198308731 --- .../webclient/static/webclient/javascript/ome.tree.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js index e36aab574d1..81b12e099af 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js +++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.tree.js @@ -1096,8 +1096,12 @@ $(function() { return 6; } else if (node.type === 'orphaned') { return 7; - } else { + } else if (node.type === 'image') { return 8; + } else if (node.type === 'acquisition') { + return 9; + } else { + return 10; } } // If the nodes are the same type then just compare lexicographically From 99f7bfe1101feb24108c60f2c0a07f30bea52639 Mon Sep 17 00:00:00 2001 From: Colin Blackburn Date: Fri, 25 Mar 2016 11:16:11 +0000 Subject: [PATCH 45/68] Add test for regex import of nested directory --- .../test/integration/clitest/test_import.py | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/components/tools/OmeroPy/test/integration/clitest/test_import.py b/components/tools/OmeroPy/test/integration/clitest/test_import.py index 70f7df1341b..9ee328e03c5 100644 --- a/components/tools/OmeroPy/test/integration/clitest/test_import.py +++ b/components/tools/OmeroPy/test/integration/clitest/test_import.py @@ -156,6 +156,20 @@ def get_object(self, err, obj_type, query=None): return query.get(obj_type, int(match.group('id')), {"omero.group": "-1"}) + def get_objects(self, err, obj_type, query=None): + if not query: + query = self.query + """Retrieve the created objects by parsing the stderr output""" + pattern = re.compile('^%s:(?P\d+)$' % obj_type) + objs = [] + for line in reversed(err.split('\n')): + match = re.match(pattern, line) + if match: + objs.append( + query.get(obj_type, int(match.group('id')), + {"omero.group": "-1"})) + return objs + def get_linked_annotations(self, oid): """Retrieve the comment annotation linked to the image""" @@ -189,6 +203,14 @@ def get_screen(self, pid): query += " where l.child.id=:id and l.parent=d.id) " return self.query.findByQuery(query, params) + def get_container(self, pid, spw=False): + """Retrieve the single container linked to an image or plate""" + + if spw: + return self.get_screen(pid) + else: + return self.get_dataset(pid) + def get_screens(self, pid): """Retrieve the screens linked to the plate""" @@ -621,6 +643,42 @@ def testUniqueMultipleNameModelTargets(self, spw, tmpdir, capfd): with pytest.raises(NonZeroReturnCode): self.do_import(capfd) + @pytest.mark.parametrize("spw", (True, False)) + def testNestedNameTemplateTargetArgument( + self, spw, tmpdir, capfd): + + outer = "NestedNameTemplateTargetArgument-Test-" + self.uuid() + inner1 = "NestedNameTemplateTargetArgument-Test-" + self.uuid() + inner2 = "NestedNameTemplateTargetArgument-Test-" + self.uuid() + subdir = tmpdir.mkdir(outer) + if spw: + importType = "Plate" + fake = ("SPW&screens=0&plates=1&plateRows=1&plateCols=1&" + "fields=1&plateAcqs=1.fake") + else: + importType = "Image" + fake = "test.fake" + subdir.mkdir(inner1).join(fake).write('') + subdir.mkdir(inner2).join(fake).write('') + + self.args += ("-T", "regex:name:^.*%s/(?.*)" % outer) + self.args += [str(tmpdir)] + + # Now, run the import and check that two distinct + # containers are created and used. + o, e = self.do_import(capfd) + + objs = self.get_objects(e, importType) + assert len(objs) == 2 + container1 = self.get_container(objs[0].id.val, spw=spw) + container2 = self.get_container(objs[1].id.val, spw=spw) + assert container1.id.val != container2.id.val + if container1.name.val == inner1: + assert container2.name.val == inner2 + else: + assert container1.name.val == inner2 + assert container2.name.val == inner1 + @pytest.mark.parametrize("kls", ("Project", "Plate", "Image")) def testBadTargetArgument(self, kls, tmpdir, capfd): From edfebfa032fcd5f41a66b2abf5aafd6f8c61c72d Mon Sep 17 00:00:00 2001 From: Colin Blackburn Date: Thu, 24 Mar 2016 15:40:20 +0000 Subject: [PATCH 46/68] Use the annotation with the highest id --- .../blitz/repo/ManagedImportRequestI.java | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/components/blitz/src/ome/services/blitz/repo/ManagedImportRequestI.java b/components/blitz/src/ome/services/blitz/repo/ManagedImportRequestI.java index 52fc0120ac6..f3938fe78da 100644 --- a/components/blitz/src/ome/services/blitz/repo/ManagedImportRequestI.java +++ b/components/blitz/src/ome/services/blitz/repo/ManagedImportRequestI.java @@ -363,12 +363,15 @@ private void detectKnownAnnotations() throws Exception { return; } + ome.model.annotations.CommentAnnotation lastCA = null; + long maxCAId = 0; for (Annotation a : settings.userSpecifiedAnnotationList) { if (a == null) { continue; } ome.model.annotations.Annotation ann = null; + String ns = null; if (a.isLoaded()) { ns = a.getNs() == null ? null : a.getNs().getValue(); @@ -387,30 +390,36 @@ private void detectKnownAnnotations() throws Exception { if (NSAUTOCLOSE.value.equals(ns)) { autoClose = true; } else if (NSTARGETTEMPLATE.value.equals(ns)) { - ome.model.annotations.CommentAnnotation ca = - (ome.model.annotations.CommentAnnotation) ann; - if (settings.userSpecifiedTarget != null) { - // TODO: Exception - String kls = settings.userSpecifiedTarget.getClass().getSimpleName(); - long id = settings.userSpecifiedTarget.getId().getValue(); - log.error("User-specified template target '{}' AND {}:{}", - ca.getTextValue(), kls, id); - continue; + ome.model.annotations.CommentAnnotation + ca = (ome.model.annotations.CommentAnnotation) ann; + if (ca.getId().longValue() > maxCAId) { + maxCAId = ca.getId().longValue(); + lastCA = ca; } - + } + } + if (lastCA != null) { + if (settings.userSpecifiedTarget != null) { + // TODO: Exception + String kls = settings.userSpecifiedTarget.getClass().getSimpleName(); + long id = settings.userSpecifiedTarget.getId().getValue(); + log.error("User-specified template target '{}' AND {}:{}", + lastCA.getTextValue(), kls, id); + } else { // Path converted to unix slashes. - String path = ca.getDescription(); + String path = lastCA.getDescription(); File file = new File(path); for (int i = 0; i < location.omittedLevels; i++) { file = file.getParentFile(); } path = file.toString(); + log.debug("Using target path {}", path); // Here we use the client-side (but unix-separated) path, since // for simple imports, we don't have any context about the directory // from the client. ServerTemplateImportTarget target = new ServerTemplateImportTarget(path); - target.init(ca.getTextValue()); - settings.userSpecifiedTarget = target.load(store, reader.isSPWReader()); + target.init(lastCA.getTextValue()); + settings.userSpecifiedTarget = target.load(store, reader.isSPWReader()); } } } From 91e95a8352cf036f04d677c1f02077b1d062da25 Mon Sep 17 00:00:00 2001 From: Colin Blackburn Date: Fri, 25 Mar 2016 13:49:51 +0000 Subject: [PATCH 47/68] Add tests to check discriminator for regex templates --- .../test/integration/clitest/test_import.py | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/components/tools/OmeroPy/test/integration/clitest/test_import.py b/components/tools/OmeroPy/test/integration/clitest/test_import.py index 9ee328e03c5..beb1e93f596 100644 --- a/components/tools/OmeroPy/test/integration/clitest/test_import.py +++ b/components/tools/OmeroPy/test/integration/clitest/test_import.py @@ -679,6 +679,80 @@ def testNestedNameTemplateTargetArgument( assert container1.name.val == inner2 assert container2.name.val == inner1 + @pytest.mark.parametrize("spw", (True, False)) + @pytest.mark.parametrize("qualifier", ("", "+", "-", "@")) + def testMultipleNameTemplateTargetArgument( + self, spw, qualifier, tmpdir, capfd): + + outer = "MultipleNameTemplateTargetArgument-Test-" + self.uuid() + inner = "MultipleNameTemplateTargetArgument-Test-" + self.uuid() + oids = [] + for i in range(2): + if spw: + kls = "Screen" + else: + kls = "Dataset" + oid = self.create_object(kls, name=inner) + oids.append(oid) + + subdir = tmpdir.mkdir(outer) + if spw: + fake = ("SPW&screens=0&plates=1&plateRows=1&plateCols=1&" + "fields=1&plateAcqs=1.fake") + else: + fake = "test.fake" + subdir.mkdir(inner).join(fake).write('') + + self.args += ("-T", + ("regex:%sname:^.*%s/(?.*)" + % (qualifier, outer))) + self.args += [str(tmpdir)] + + # Now, run the import and check that the correct + # container is used or created and used. + found = self.parse_container(spw, capfd) + if qualifier == "-": + assert found == min(oids) + elif qualifier == "@": + assert found not in oids + else: + assert found == max(oids) + + @pytest.mark.parametrize("spw", (True, False)) + def testUniqueMultipleNameTemplateTargetArgument( + self, spw, tmpdir, capfd): + + outer = "UniqueMultipleNameTemplateTargetArgument-Test-" + self.uuid() + inner = "UniqueMultipleNameTemplateTargetArgument-Test-" + self.uuid() + oids = [] + for i in range(2): + if spw: + kls = "Screen" + else: + kls = "Dataset" + oid = self.create_object(kls, name=inner) + oids.append(oid) + + subdir = tmpdir.mkdir(outer) + if spw: + importType = "Plate" + fake = ("SPW&screens=0&plates=1&plateRows=1&plateCols=1&" + "fields=1&plateAcqs=1.fake") + else: + importType = "Image" + fake = "test.fake" + subdir.mkdir(inner).join(fake).write('') + + self.args += ("-T", "regex:%%name:^.*%s/(?.*)" % outer) + self.args += [str(tmpdir)] + + # Now, run the import and check that the imported object + # is not in a container. + o, e = self.do_import(capfd) + obj = self.get_object(e, importType) + container = self.get_container(obj.id.val, spw=spw) + assert container is None + @pytest.mark.parametrize("kls", ("Project", "Plate", "Image")) def testBadTargetArgument(self, kls, tmpdir, capfd): From d2eb0734a754f2ec141bd63d310ff5d4922acd5e Mon Sep 17 00:00:00 2001 From: Colin Blackburn Date: Fri, 25 Mar 2016 11:17:52 +0000 Subject: [PATCH 48/68] Fix bug in Dataset choice for regex import --- .../formats/importer/targets/ServerTemplateImportTarget.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/blitz/src/ome/formats/importer/targets/ServerTemplateImportTarget.java b/components/blitz/src/ome/formats/importer/targets/ServerTemplateImportTarget.java index d3d37ee044a..59f28a4d8d5 100644 --- a/components/blitz/src/ome/formats/importer/targets/ServerTemplateImportTarget.java +++ b/components/blitz/src/ome/formats/importer/targets/ServerTemplateImportTarget.java @@ -101,7 +101,7 @@ public IObject load(OMEROMetadataStoreClient client, boolean spw) throws Excepti Dataset dataset; List datasets = (List) query.findAllByQuery( "select o from Dataset as o where o.name = :name" - + " order by o.id desc", + + " order by o.id " + order, new ParametersI().add("name", rstring(name))); if (datasets.size() == 0 || getDiscriminator().startsWith("@")) { dataset = new DatasetI(); From b00d88370f1fdf954f131823ba19e3ef1cca0846 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Thu, 7 Apr 2016 12:53:39 +0100 Subject: [PATCH 49/68] Use image links for thumbnails and Full Viewer button --- .../webclient/annotations/includes/toolbar.html | 6 ++++-- .../webclient/data/icon_thumbnails_underscore.html | 9 ++++++--- .../webclient/data/includes/center_plugin.thumbs.js.html | 2 ++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/annotations/includes/toolbar.html b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/annotations/includes/toolbar.html index 683735e31c4..2e65d80f1cd 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/annotations/includes/toolbar.html +++ b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/annotations/includes/toolbar.html @@ -269,17 +269,19 @@ {% if image %} - + {% endif %}
diff --git a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/icon_thumbnails_underscore.html b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/icon_thumbnails_underscore.html index f442c43ac50..bc18aebac84 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/icon_thumbnails_underscore.html +++ b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/icon_thumbnails_underscore.html @@ -24,9 +24,12 @@ data-owned="">
- image + + + image +
diff --git a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/includes/center_plugin.thumbs.js.html b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/includes/center_plugin.thumbs.js.html index ad226c19a07..a6bb1689858 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/includes/center_plugin.thumbs.js.html +++ b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/includes/center_plugin.thumbs.js.html @@ -399,6 +399,8 @@ if (event.target.nodeName.toLowerCase() == 'li') { $targetIcon = $(event.target); } else if (event.target.nodeName.toLowerCase() == 'img') { + $targetIcon = $(event.target).parent().parent().parent(); + } else if (event.target.nodeName.toLowerCase() == 'a') { $targetIcon = $(event.target).parent().parent(); } else { $targetIcon = $(event.target).parent(); From 6965b1d813aa0b43e9590df26361a3e3292a7813 Mon Sep 17 00:00:00 2001 From: Will Moore Date: Thu, 7 Apr 2016 15:28:31 +0100 Subject: [PATCH 50/68] Preview 'Full Viewer' button also is a link --- .../webclient/annotations/metadata_preview.html | 14 ++++++++------ .../data/includes/center_plugin.thumbs.js.html | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/annotations/metadata_preview.html b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/annotations/metadata_preview.html index 56e57ca5548..663d09c2f83 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/annotations/metadata_preview.html +++ b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/annotations/metadata_preview.html @@ -329,10 +329,9 @@ {% endif %} - $("#preview_open_viewer").click(function(){ - - var url = "{% if share and not share.share.isOwned %}{% url 'web_image_viewer' share.share.id manager.image.id %}{% else %}{% url 'web_image_viewer' manager.image.id %}{% endif %}"; - + $("#preview_open_viewer").click(function(event){ + event.preventDefault(); + var url = $(this).attr('href'); var vpQuery = OME.preview_viewport.getQuery(); // remove &zm=50 vpQuery = vpQuery.replace("&zm=" + OME.preview_viewport.getZoom(), ""); @@ -351,11 +350,14 @@ - +
diff --git a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/includes/center_plugin.thumbs.js.html b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/includes/center_plugin.thumbs.js.html index a6bb1689858..de0ce32cf9b 100644 --- a/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/includes/center_plugin.thumbs.js.html +++ b/components/tools/OmeroWeb/omeroweb/webclient/templates/webclient/data/includes/center_plugin.thumbs.js.html @@ -106,6 +106,7 @@ // single click handler on image (container). Selection then update toolbar & metadata pane $( "#icon_table" ).on( "click", "li.row", function(event) { + event.preventDefault(); handleClickSelection(event); }); From 2d4dbd2f5897394080b7877e1178328d3ac2a73a Mon Sep 17 00:00:00 2001 From: Will Moore Date: Fri, 8 Apr 2016 15:54:37 +0100 Subject: [PATCH 51/68] Fix Robot tests with full viewer button --- components/tests/ui/resources/web/tree.txt | 14 +++++++------- components/tests/ui/testcases/web/rdef_test.txt | 12 ++++++------ components/tests/ui/testcases/web/view_image.txt | 2 +- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/components/tests/ui/resources/web/tree.txt b/components/tests/ui/resources/web/tree.txt index b5106be419a..9941e62ac1c 100644 --- a/components/tests/ui/resources/web/tree.txt +++ b/components/tests/ui/resources/web/tree.txt @@ -287,6 +287,12 @@ Wait Until Right Panel Loads Everything Run Keyword If '${containerType}' == 'Project' Wait Until Element Is Visible ${datasetDetails}//th[contains(text(),'Creation Date:')] Run Keyword If '${containerType}' == 'Dataset' Wait Until Element Is Visible ${datasetDetails}//th[contains(text(),'Creation Date:')] + Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Tags')] + Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Key-Value Pairs')] + Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Attachments')] + Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Ratings')] + Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Comments')] + Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible ${datasetDetails}//th[contains(text(),'Acquisition Date:')] Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible ${datasetDetails}//th[contains(text(),'Import Date:')] Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible ${datasetDetails}//th[contains(text(),'Dimensions (XY):')] @@ -295,13 +301,7 @@ Wait Until Right Panel Loads Everything Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible ${datasetDetails}//th[contains(text(),'Z-sections/Timepoints:')] Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible ${datasetDetails}//th[contains(text(),'Channels:')] Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible ${datasetDetails}//th[contains(text(),'ROI Count:')] - Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Tags')] - Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Key-Value Pairs')] - Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Attachments')] - Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Ratings')] - Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Comments')] - - Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible xpath=//*[@id="general_tab"]/div/button/span[contains(text(),'Full viewer')] + Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible xpath=//*[@id="general_tab"]/div/a/span[contains(text(),'Full viewer')] Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible xpath=//*[@id="general_tab"]/div/div/button[contains(@title,'Publishing Options')] Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible xpath=//*[@id="show_fs_files_btn"] Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible xpath=//*[@id="show_image_hierarchy"] diff --git a/components/tests/ui/testcases/web/rdef_test.txt b/components/tests/ui/testcases/web/rdef_test.txt index 36ef6cf3ef1..5bfb55c05cc 100644 --- a/components/tests/ui/testcases/web/rdef_test.txt +++ b/components/tests/ui/testcases/web/rdef_test.txt @@ -239,9 +239,9 @@ Test Owners Rdef # Test 'Paste and Save' with right-click on different Image in tree # (check thumb refresh by change of src) - ${thumbSrc}= Get Element Attribute xpath=//li[@id="image_icon-${imageId_2}"]/div[@class="image"]/img@src + ${thumbSrc}= Get Element Attribute xpath=//li[@id="image_icon-${imageId_2}"]/div[@class="image"]/a/img@src Right Click Image Rendering Settings ${imageId_2} Paste and Save - Wait Until Page Contains Element xpath=//li[@id="image_icon-${imageId_2}"]/div[@class="image"]/img[@src!='${thumbSrc}'] + Wait Until Page Contains Element xpath=//li[@id="image_icon-${imageId_2}"]/div[@class="image"]/a/img[@src!='${thumbSrc}'] # Check applied by refresh right panel Select Image By Id ${imageId_2} ${status} ${oldId} Wait For Preview Load ${status} ${oldId} @@ -249,9 +249,9 @@ Test Owners Rdef Textfield Value Should Be wblitz-ch0-cw-end 200 # Test Set Owner's in same way on first Image - ${thumbSrc}= Get Element Attribute xpath=//li[@id="image_icon-${imageId}"]/div[@class="image"]/img@src + ${thumbSrc}= Get Element Attribute xpath=//li[@id="image_icon-${imageId}"]/div[@class="image"]/a/img@src Right Click Image Rendering Settings ${imageId} Set Owner's and Save - Wait Until Page Contains Element xpath=//li[@id="image_icon-${imageId}"]/div[@class="image"]/img[@src!='${thumbSrc}'] + Wait Until Page Contains Element xpath=//li[@id="image_icon-${imageId}"]/div[@class="image"]/a/img[@src!='${thumbSrc}'] # Check applied by refresh right panel Click Element id=image_icon-${imageId} ${status} ${oldId} Wait For Preview Load ${status} ${oldId} @@ -259,9 +259,9 @@ Test Owners Rdef Textfield Value Should Be wblitz-ch0-cw-end 100 # Test "Set Imported" on first Image - ${thumbSrc}= Get Element Attribute xpath=//li[@id="image_icon-${imageId}"]/div[@class="image"]/img@src + ${thumbSrc}= Get Element Attribute xpath=//li[@id="image_icon-${imageId}"]/div[@class="image"]/a/img@src Right Click Image Rendering Settings ${imageId} Set Imported and Save - Wait Until Page Contains Element xpath=//li[@id="image_icon-${imageId}"]/div[@class="image"]/img[@src!='${thumbSrc}'] + Wait Until Page Contains Element xpath=//li[@id="image_icon-${imageId}"]/div[@class="image"]/a/img[@src!='${thumbSrc}'] # Check applied by refresh right panel Click Element id=image_icon-${imageId} ${status} ${oldId} Wait For Preview Load ${status} ${oldId} diff --git a/components/tests/ui/testcases/web/view_image.txt b/components/tests/ui/testcases/web/view_image.txt index 4b22532e12e..7a5ee8e2c6f 100644 --- a/components/tests/ui/testcases/web/view_image.txt +++ b/components/tests/ui/testcases/web/view_image.txt @@ -39,7 +39,7 @@ Test Open Viewer ${nodeId}= Wait For Image Node ${imageId} ${imageName}= Wait For General Panel And Return Name Image # Open Image Viewer 3 different ways and check - Click Element xpath=//button[@title='Open full image viewer in new window'] + Click Element xpath=//a[@title='Open full image viewer in new window'] Check Image Viewer ${imageName} Double Click Element xpath=//li[@id='image_icon-${imageId}']//img Check Image Viewer ${imageName} From cb092c9c5b9deadb3e33be1954a6268c3a5272ef Mon Sep 17 00:00:00 2001 From: Will Moore Date: Fri, 8 Apr 2016 16:05:10 +0100 Subject: [PATCH 52/68] Robot keyword cleanup --- components/tests/ui/resources/web/tree.txt | 57 +++++++++++----------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/components/tests/ui/resources/web/tree.txt b/components/tests/ui/resources/web/tree.txt index 9941e62ac1c..1cf61248772 100644 --- a/components/tests/ui/resources/web/tree.txt +++ b/components/tests/ui/resources/web/tree.txt @@ -11,7 +11,7 @@ ${orphanedIcon} webclient/image/folder_yellow16.png ${plateIcon} webclient/image/folder_plate16.png ${runIcon} webclient/image/run16.png -${datasetDetails} xpath=//*[@id="general_tab"]/h1[contains(@data-name,'details')]/following-sibling::div +${detailsPane} xpath=//*[@id="general_tab"]/h1[contains(@data-name,'details')]/following-sibling::div *** Keywords *** Get Dialog Button Xpath @@ -280,33 +280,34 @@ Wait Until Right Panel Loads Wait Until Element Is Visible xpath=//tr[contains(@class,'data_heading_id')]/td/strong[(text() = '${containerId}')] Wait Until Right Panel Loads Everything - [Arguments] ${containerType} ${containerId} - - Wait Until Right Panel Loads ${containerType} ${containerId} - Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'${containerType} Details')] - - Run Keyword If '${containerType}' == 'Project' Wait Until Element Is Visible ${datasetDetails}//th[contains(text(),'Creation Date:')] - Run Keyword If '${containerType}' == 'Dataset' Wait Until Element Is Visible ${datasetDetails}//th[contains(text(),'Creation Date:')] - Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Tags')] - Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Key-Value Pairs')] - Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Attachments')] - Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Ratings')] - Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Comments')] - - Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible ${datasetDetails}//th[contains(text(),'Acquisition Date:')] - Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible ${datasetDetails}//th[contains(text(),'Import Date:')] - Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible ${datasetDetails}//th[contains(text(),'Dimensions (XY):')] - Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible ${datasetDetails}//th[contains(text(),'Pixels Type:')] - Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible ${datasetDetails}//th[contains(text(),'Pixels Size (XYZ) (µm):')] - Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible ${datasetDetails}//th[contains(text(),'Z-sections/Timepoints:')] - Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible ${datasetDetails}//th[contains(text(),'Channels:')] - Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible ${datasetDetails}//th[contains(text(),'ROI Count:')] - Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible xpath=//*[@id="general_tab"]/div/a/span[contains(text(),'Full viewer')] - Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible xpath=//*[@id="general_tab"]/div/div/button[contains(@title,'Publishing Options')] - Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible xpath=//*[@id="show_fs_files_btn"] - Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible xpath=//*[@id="show_image_hierarchy"] - Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible xpath=//*[@id="show_link_btn"] - Run Keyword If '${containerType}' == 'Image' Wait Until Element Is Visible xpath=//*[@id="general_tab"]/div/div/button[contains(@title,'Download Image as...')] + [Arguments] ${containerType} ${containerId} + + Wait Until Right Panel Loads ${containerType} ${containerId} + Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'${containerType} Details')] + Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Tags')] + Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Key-Value Pairs')] + Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Attachments')] + Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Ratings')] + Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Comments')] + Run Keyword If '${containerType}' == 'Project' Wait Until Element Is Visible ${detailsPane}//th[contains(text(),'Creation Date:')] + Run Keyword If '${containerType}' == 'Dataset' Wait Until Element Is Visible ${detailsPane}//th[contains(text(),'Creation Date:')] + Run Keyword If '${containerType}' == 'Image' Check Right Panel Image + +Check Right Panel Image + Wait Until Element Is Visible ${detailsPane}//th[contains(text(),'Acquisition Date:')] + Wait Until Element Is Visible ${detailsPane}//th[contains(text(),'Import Date:')] + Wait Until Element Is Visible ${detailsPane}//th[contains(text(),'Dimensions (XY):')] + Wait Until Element Is Visible ${detailsPane}//th[contains(text(),'Pixels Type:')] + Wait Until Element Is Visible ${detailsPane}//th[contains(text(),'Pixels Size (XYZ) (µm):')] + Wait Until Element Is Visible ${detailsPane}//th[contains(text(),'Z-sections/Timepoints:')] + Wait Until Element Is Visible ${detailsPane}//th[contains(text(),'Channels:')] + Wait Until Element Is Visible ${detailsPane}//th[contains(text(),'ROI Count:')] + Wait Until Element Is Visible xpath=//*[@id="general_tab"]/div/a/span[contains(text(),'Full viewer')] + Wait Until Element Is Visible xpath=//*[@id="general_tab"]/div/div/button[contains(@title,'Publishing Options')] + Wait Until Element Is Visible xpath=//*[@id="show_fs_files_btn"] + Wait Until Element Is Visible xpath=//*[@id="show_image_hierarchy"] + Wait Until Element Is Visible xpath=//*[@id="show_link_btn"] + Wait Until Element Is Visible xpath=//*[@id="general_tab"]/div/div/button[contains(@title,'Download Image as...')] Wait Until Center Panel Loads [Arguments] ${containerType} From e18f3ead6b111d6aab808b7a1f4e2a21a1487833 Mon Sep 17 00:00:00 2001 From: Sebastien Besson Date: Mon, 11 Apr 2016 10:28:35 +0100 Subject: [PATCH 53/68] Copy ome.api.IMetadata Javadoc to omero.api.IMetadata --- .../blitz/resources/omero/api/IMetadata.ice | 177 +++++++++++++++++- 1 file changed, 176 insertions(+), 1 deletion(-) diff --git a/components/blitz/resources/omero/api/IMetadata.ice b/components/blitz/resources/omero/api/IMetadata.ice index 131edf1ea15..4117fa5b9e6 100644 --- a/components/blitz/resources/omero/api/IMetadata.ice +++ b/components/blitz/resources/omero/api/IMetadata.ice @@ -18,37 +18,212 @@ module omero { module api { /** - * See IMetadata.html + * Provides method to interact with acquisition metadata and + * annotations. **/ ["ami", "amd"] interface IMetadata extends ServiceInterface { + /** + * Loads the logical channels and the acquisition + * metadata related to them. + * + * @param ids The collection of logical channel's ids. + * Mustn't be null. + * @return The collection of loaded logical channels. + **/ idempotent LogicalChannelList loadChannelAcquisitionData(omero::sys::LongList ids) throws ServerError; + + /** + * Loads all the annotations of given types, that have been + * attached to the specified rootNodes for the + * specified annotatorIds. + * If no types specified, all annotations will be loaded. + * This method looks for the annotations that have been + * attached to each of the specified objects. It then maps + * each rootId onto the set of annotations + * that were found for that node. If no annotations were found + * for that node, then the entry will be null. + * Otherwise it will be a Map containing + * {@link omero.model.Annotation} objects. + * + * @param rootType + * The type of the nodes the annotations are linked to. + * Mustn't be null. + * @param rootIds + * Ids of the objects of type rootType. + * Mustn't be null. + * @param annotationType + * The types of annotation to retrieve. If + * null all annotations will be loaded. + * String of the type + * omero.model.annotations.*. + * @param annotatorIds + * Ids of the users for whom annotations should be + * retrieved. If null, all annotations + * returned. + * @param options + * @return A map whose key is rootId and value the + * Map of all annotations for that node + * or null. + **/ idempotent LongIObjectListMap loadAnnotations(string rootType, omero::sys::LongList rootIds, omero::api::StringSet annotationTypes, omero::sys::LongList annotatorIds, omero::sys::Parameters options) throws ServerError; + + /** + * Loads all the annotations of a given type. + * It is possible to filter the annotations by including or + * excluding name spaces set on the annotations. + * + * @param annotationType The type of annotations to load. + * @param include + * Include the annotations with the specified name spaces. + * @param exclude + * Exclude the annotations with the specified name spaces. + * @param options The POJO options. + * @return A collection of found annotations. + **/ idempotent AnnotationList loadSpecifiedAnnotations(string annotationType, omero::api::StringSet include, omero::api::StringSet exclude, omero::sys::Parameters options) throws ServerError; //idempotent omero::metadata::TagSetContainerList loadTagSets(long id, bool withObjects, omero::sys::Parameters options) throws ServerError; //idempotent omero::metadata::TagContainerList loadTags(long id, bool withObjects, omero::sys::Parameters options) throws ServerError; + + /** + * Loads the Tag Set if the id is specified otherwise loads + * all the Tag Set. + * + * @param ids The id of the tag to load or -1. + * @return Map whose key is a Tag/Tag Set and the + * value either a Map or a list of related + * DataObject. + **/ idempotent LongIObjectListMap loadTagContent(omero::sys::LongList ids, omero::sys::Parameters options) throws ServerError; + + /** + * Loads all the tag Sets. Returns a collection of + * AnnotationAnnotatioLink objects and, if the + * orphan parameters is true, the + * TagAnnotation object. + * Note that the difference between a Tag Set and a Tag is made + * using the NS_INSIGHT_TAG_SET namespace. + * + * @param options The POJO options. + * @return See above. + **/ idempotent IObjectList loadTagSets(omero::sys::Parameters options) throws ServerError; + + /** + * Returns a map whose key is a tag id and the value the + * number of Projects, Datasets, and Images linked to that tag. + * + * @param ids The collection of ids. + * @param options The POJO options. + * @return See above. + **/ idempotent omero::sys::CountMap getTaggedObjectsCount(omero::sys::LongList ids, omero::sys::Parameters options) throws ServerError; + + /** + * Counts the number of annotation of a given type. + * + * @param annotationType The type of annotations to load. + * @param include The collection of name space, one of the + * constants defined by this class. + * @param exclude The collection of name space, one of the + * constants defined by this class. + * @param options The POJO options. + * @return See above. + **/ omero::RLong countSpecifiedAnnotations(string annotationType, omero::api::StringSet include, omero::api::StringSet exclude, omero::sys::Parameters options) throws ServerError; + + /** + * Loads the specified annotations. + * + * @param annotationIds The collection of annotation ids. + * @return See above. + */ idempotent AnnotationList loadAnnotation(omero::sys::LongList annotationIds) throws ServerError; + + /** + * Loads the instrument and its components i.e. detectors, + * objectives, etc. + * + * @param id The id of the instrument to load. + * @return See above + */ idempotent omero::model::Instrument loadInstrument(long id) throws ServerError; + + /** + * Loads the annotations of a given type used by the specified + * user but not owned by the user. + * + * @param annotationType The type of annotations to load. + * @param userID The identifier of the user. + * @return See above. + */ idempotent IObjectList loadAnnotationsUsedNotOwned(string annotationType, long userID) throws ServerError; + + /** + * Counts the number of annotation of a given type used by the + * specified user but not owned by the user. + * + * @param annotationType The type of annotations to load. + * @param userID The identifier of the user. + * @return See above. + */ omero::RLong countAnnotationsUsedNotOwned(string annotationType, long userID) throws ServerError; + + /** + * Loads the annotations of a given type linked to the + * specified objects. It is possible to filter the annotations + * by including or excluding name spaces set on the + * annotations. + * + * This method looks for the annotations that have been + * attached to each of the specified objects. It then maps + * each rootNodeId onto the set of annotations + * that were found for that node. If no annotations were found + * for that node, the map will not contain an entry for that + * node. Otherwise it will be a Set containing + * {@link omero.model.Annotation} objects. + * The rootNodeType supported are: + * Project, Dataset, Image, Pixels, Screen, Plate, + * PlateAcquisition, Well, Fileset. + * + * @param annotationType The type of annotations to load. + * @param include + * Include the annotations with the specified name spaces. + * @param exclude + * Exclude the annotations with the specified name spaces. + * @param rootNodeType + * The type of objects the annotations are linked to. + * @param rootNodeIds The identifiers of the objects. + * @param options The POJO options. + * @return A collection of found annotations. + */ idempotent LongAnnotationListMap loadSpecifiedAnnotationsLinkedTo(string annotationType, omero::api::StringSet include, omero::api::StringSet exclude, string rootNodeType, omero::sys::LongList rootNodeIds, omero::sys::Parameters options) throws ServerError; + + /** + * Finds the original file IDs for the import logs + * corresponding to the given Image or Fileset IDs. + * + * @param rootNodeType + * the root node type, may be {@link omero.model.Image} + * or {@link omero.model.Fileset} + * @param ids + * the IDs of the entities for which the import log + * original file IDs are required + * @return the original file IDs of the import logs + **/ idempotent LongIObjectListMap loadLogFiles(string rootType, omero::sys::LongList ids) throws ServerError; }; From 7c070c111f1fa4e10c6bac920efed6beba5f53f8 Mon Sep 17 00:00:00 2001 From: Sebastien Besson Date: Mon, 11 Apr 2016 10:29:10 +0100 Subject: [PATCH 54/68] Copy ome.api.IPixels Javadoc to omero.api.IPixels --- .../blitz/resources/omero/api/IPixels.ice | 251 +++++++++++++++++- 1 file changed, 250 insertions(+), 1 deletion(-) diff --git a/components/blitz/resources/omero/api/IPixels.ice b/components/blitz/resources/omero/api/IPixels.ice index c1d5297d49f..29c91d96185 100644 --- a/components/blitz/resources/omero/api/IPixels.ice +++ b/components/blitz/resources/omero/api/IPixels.ice @@ -16,19 +16,190 @@ module omero { module api { /** - * See IPixels.html + * Metadata gateway for the {@link omero.api.RenderingEngine} and + * clients. This service provides all DB access that the rendering + * engine needs as well as Pixels services to a client. It also allows + * the rendering engine to also be run external to the server (e.g. + * client-side). + * **/ ["ami", "amd"] interface IPixels extends ServiceInterface { + /** + * Retrieves the pixels metadata. The following objects are + * pre-linked: + *
    + *
  • pixels.pixelsType
  • + *
  • pixels.pixelsDimensions
  • + *
  • pixels.channels
  • + *
  • pixels.channnels.statsInfo
  • + *
  • pixels.channnels.colorComponent
  • + *
  • pixels.channnels.logicalChannel
  • + *
  • pixels.channnels.logicalChannel.photometricInterpretation
  • + *
+ * + * @param pixId Pixels id. + * @return Pixels object which matches id. + **/ idempotent omero::model::Pixels retrievePixDescription(long pixId) throws ServerError; + + /** + * Retrieves the rendering settings for a given pixels set and + * the currently logged in user. If the current user has no + * {@link omero.model.RenderingDef}, and the user is an + * administrator, then a {@link omero.model.RenderingDef} may + * be returned for the owner of the + *{@link omero.model.Pixels}. This matches the behavior of the + * Rendering service. + * + * The following objects will be pre-linked: + *
    + *
  • renderingDef.quantization
  • + *
  • renderingDef.model
  • + *
  • renderingDef.waveRendering
  • + *
  • renderingDef.waveRendering.color
  • + *
  • renderingDef.waveRendering.family
  • + *
  • renderingDef.spatialDomainEnhancement
  • + *
+ * + * @param pixId Pixels id. + * @return Rendering definition. + */ idempotent omero::model::RenderingDef retrieveRndSettings(long pixId) throws ServerError; + + /** + * Retrieves the rendering settings for a given pixels set and + * the passed user. The following objects are pre-linked: + *
    + *
  • renderingDef.quantization
  • + *
  • renderingDef.model
  • + *
  • renderingDef.waveRendering
  • + *
  • renderingDef.waveRendering.color
  • + *
  • renderingDef.waveRendering.family
  • + *
  • renderingDef.spatialDomainEnhancement
  • + *
+ * + * @param pixId Pixels id. + * @param userID The id of the user. + * @return Rendering definition. + **/ idempotent omero::model::RenderingDef retrieveRndSettingsFor(long pixId, long userId) throws ServerError; + + /** + * Retrieves all the rendering settings for a given pixels set + * and the passed user. The following objects are pre-linked: + *
    + *
  • renderingDef.quantization
  • + *
  • renderingDef.model
  • + *
  • renderingDef.waveRendering
  • + *
  • renderingDef.waveRendering.color
  • + *
  • renderingDef.waveRendering.family
  • + *
  • renderingDef.spatialDomainEnhancement
  • + *
+ * + * @param pixId Pixels id. + * @param userId The id of the user. + * @return Rendering definition. + **/ idempotent IObjectList retrieveAllRndSettings(long pixId, long userId) throws ServerError; + + /** + * Loads a specific set of rendering settings. The + * following objects are pre-linked: + *
    + *
  • renderingDef.quantization
  • + *
  • renderingDef.model
  • + *
  • renderingDef.waveRendering
  • + *
  • renderingDef.waveRendering.color
  • + *
  • renderingDef.waveRendering.family
  • + *
  • renderingDef.spatialDomainEnhancement
  • + *
+ * + * @param renderingSettingsId Rendering definition id. + * @throws ValidationException If no RenderingDef + * matches the ID renderingDefId. + * @return Rendering definition. + **/ idempotent omero::model::RenderingDef loadRndSettings(long renderingSettingsId) throws ServerError; + + /** + * Saves the specified rendering settings. + * + * @param rndSettings Rendering settings. + **/ void saveRndSettings(omero::model::RenderingDef rndSettings) throws ServerError; + + /** + * Bit depth for a given pixel type. + * + * @param type Pixels type. + * @return Bit depth in bits. + **/ idempotent int getBitDepth(omero::model::PixelsType type) throws ServerError; + + /** + * Retrieves a particular enumeration for a given enumeration + * class. + * + * @param enumClass Enumeration class. + * @param value Enumeration string value. + * @return Enumeration object. + **/ idempotent omero::model::IObject getEnumeration(string enumClass, string value) throws ServerError; + + /** + * Retrieves the exhaustive list of enumerations for a given + * enumeration class. + * + * @param enumClass Enumeration class. + * @return List of all enumeration objects for the + * klass. + **/ idempotent IObjectList getAllEnumerations(string enumClass) throws ServerError; + + /** + * Copies the metadata, and only the metadata linked to + * a Pixels object into a new Pixels object of equal or + * differing size across one or many of its three physical + * dimensions or temporal dimension. + * It is beyond the scope of this method to handle updates or + * changes to the raw pixel data available through + * {@link omero.api.RawPixelsStore} or to add + * and link {@link omero.model.PlaneInfo} and/or other Pixels + * set specific metadata. + * It is also assumed that the caller wishes the pixels + * dimensions and {@link omero.model.PixelsType} to remain the + * same; changing these is outside the scope of this method. + * NOTE: As {@link omero.model.Channel} objects are + * only able to apply to a single set of Pixels any + * annotations or linkage to these objects will be lost. + * + * @param pixelsId The source Pixels set id. + * @param sizeX The new size across the X-axis. + * null if the copy should maintain + * the same size. + * @param sizeY The new size across the Y-axis. * null if the copy should maintain + * the same size. + * @param sizeZ The new size across the Z-axis. * null if the copy should maintain + * the same size. + * @param sizeT The new number of timepoints. + * null if the copy should maintain + * the same number. + * @param channelList The channels that should be copied into + * the new Pixels set. + * @param methodology An optional string signifying the + * methodology that will be used to produce + * this new Pixels set. + * @param copyStats Whether or not to copy the + * {@link omero.model.StatsInfo} for each + * channel. + * @return Id of the new Pixels object on success or + * null on failure. + * @throws ValidationException If the X, Y, Z, T or + * channelList dimensions are out of bounds or the + * Pixels object corresponding to + * pixelsId is unlocatable. + **/ omero::RLong copyAndResizePixels(long pixelsId, omero::RInt sizeX, omero::RInt sizeY, @@ -37,6 +208,50 @@ module omero { omero::sys::IntList channelList, string methodology, bool copyStats) throws ServerError; + + /** + * Copies the metadata, and only the metadata linked to + * a Image object into a new Image object of equal or + * differing size across one or many of its three physical + * dimensions or temporal dimension. + * It is beyond the scope of this method to handle updates or + * changes to the raw pixel data available through + * {@link omero.api.RawPixelsStore} or to add + * and link {@link omero.model.PlaneInfo} and/or other Pixels + * set specific metadata. + * It is also assumed that the caller wishes the pixels + * dimensions and {@link omero.model.PixelsType} to remain the + * same; changing these is outside the scope of this method. + * NOTE: As {@link omero.model.Channel} objects are + * only able to apply to a single set of Pixels any + * annotations or linkage to these objects will be lost. + * + * @param imageId The source Image id. + * @param sizeX The new size across the X-axis. + * null if the copy should maintain + * the same size. + * @param sizeY The new size across the Y-axis. + * null if the copy should maintain + * the same size. + * @param sizeZ The new size across the Z-axis. + * null if the copy should maintain + * the same size. + * @param sizeT The new number of timepoints. + * null if the copy should maintain + * the same number. + * @param channelList The channels that should be copied into + * the new Pixels set. + * @param methodology The name of the new Image. + * @param copyStats Whether or not to copy the + * {@link omero.model.StatsInfo} for each + * channel. + * @return Id of the new Pixels object on success or + * null on failure. + * @throws ValidationException If the X, Y, Z, T or + * channelList dimensions are out of bounds or the + * Pixels object corresponding to + * pixelsId is unlocatable. + */ omero::RLong copyAndResizeImage(long imageId, omero::RInt sizeX, omero::RInt sizeY, @@ -45,10 +260,44 @@ module omero { omero::sys::IntList channelList, string methodology, bool copyStats) throws ServerError; + + /** + * Creates the metadata, and only the metadata linked + * to an Image object. It is beyond the scope of this method + * to handle updates or changes to the raw pixel data + * available through {@link omero.api.RawPixelsStore} or to + * add and link {@link omero.model.PlaneInfo} or + * {@link omero.model.StatsInfo} objects and/or other Pixels + * set specific metadata. It is also up to the caller to + * update the pixels dimensions. + * + * @param sizeX The new size across the X-axis. + * @param sizeY The new size across the Y-axis. + * @param sizeZ The new size across the Z-axis. + * @param sizeT The new number of timepoints. + * @param pixelsType The pixelsType + * @param name The name of the new Image. + * @param description The description of the new Image. + * @return Id of the new Image object on success or + * null on failure. + * @throws ValidationException If the channel list is + * null or of size == 0. + **/ omero::RLong createImage(int sizeX, int sizeY, int sizeZ, int sizeT, omero::sys::IntList channelList, omero::model::PixelsType pixelsType, string name, string description) throws ServerError; + + /** + * Sets the channel global (all 2D optical sections + * corresponding to a particular channel) minimum and maximum + * for a Pixels set. + * + * @param pixelsId The source Pixels set id. + * @param channelIndex The channel index within the Pixels set. + * @param min The channel global minimum. + * @param max The channel global maximum. + **/ void setChannelGlobalMinMax(long pixelsId, int channelIndex, double min, double max) throws ServerError; }; }; From e51b23c94b9e65f49102b5a06a745511860a2b6a Mon Sep 17 00:00:00 2001 From: Sebastien Besson Date: Mon, 11 Apr 2016 10:29:32 +0100 Subject: [PATCH 55/68] Copy ome.api.IProjection Javadoc to omero.api.IProjection --- .../blitz/resources/omero/api/IProjection.ice | 108 +++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/components/blitz/resources/omero/api/IProjection.ice b/components/blitz/resources/omero/api/IProjection.ice index 668c1b56d35..a7d2d5e4a79 100644 --- a/components/blitz/resources/omero/api/IProjection.ice +++ b/components/blitz/resources/omero/api/IProjection.ice @@ -20,15 +20,121 @@ module omero { module api { /** - * See IProjection.html + * Provides methods for performing projections of Pixels sets. **/ ["ami", "amd"] interface IProjection extends ServiceInterface { + /** + * Performs a projection through the optical sections of a + * particular wavelength at a given time point of a Pixels set. + * @param pixelsId The source Pixels set Id. + * @param pixelsType The destination Pixels type. If + * null, the source Pixels set + * pixels type will be used. + * @param algorithm MAXIMUM_INTENSITY, + * MEAN_INTENSITY or + * SUM_INTENSITY. NOTE: + * When performing a + * SUM_INTENSITY projection, + * pixel values will be pinned to the + * maximum pixel value of the destination + * Pixels type. + * @param timepoint Timepoint to perform the projection. + * @param channelIndex Index of the channel to perform the + * projection. + * @param stepping Stepping value to use while calculating the + * projection. + * For example, stepping=1 will + * use every optical section from + * start to end where + * stepping=2 will use every + * other section from start to + * end to perform the projection. + * @param start Optical section to start projecting from. + * @param end Optical section to finish projecting. + * @return A byte array of projected pixel values whose length + * is equal to the Pixels set + 8 sizeX * sizeY * bytesPerPixel in + * big-endian format. + * @throws ValidationException Where: + *
    + *
  • algorithm is unknown
  • + *
  • timepoint is out of range
  • + *
  • channelIndex is out of range
  • + *
  • start is out of range
  • + *
  • end is out of range
  • + *
  • start > end
  • + *
  • the Pixels set qualified by pixelsId is + * unlocatable.
  • + *
+ * @see #projectPixels + **/ Ice::ByteSeq projectStack(long pixelsId, omero::model::PixelsType pixelsType, omero::constants::projection::ProjectionType algorithm, int timepoint, int channelIndex, int stepping, int start, int end) throws ServerError; + + /** + * Performs a projection through selected optical sections and + * optical sections for a given set of time points of a Pixels + * set. The Image which is linked to the Pixels set will be + * copied using + * {@link omero.api.IPixels#copyAndResizeImage}. + * + * @param pixelsId The source Pixels set Id. + * @param pixelsType The destination Pixels type. If + * null, the source Pixels set + * pixels type will be used. + * @param algorithm MAXIMUM_INTENSITY, + * MEAN_INTENSITY or + * SUM_INTENSITY. NOTE: + * When performing a + * SUM_INTENSITY projection, + * pixel values will be pinned to the + * maximum pixel value of the destination + * Pixels type. + * @param tStart Timepoint to start projecting from. + * @param tEnd Timepoint to finish projecting. + * @param channels List of the channel indexes to use while + * calculating the projection. + * @param stepping Stepping value to use while calculating the + * projection. For example, + * stepping=1 will use every + * optical section from start to + * end where + * stepping=2 will use every + * other section from start to + * end to perform the projection. + * @param zStart Optical section to start projecting from. + * @param zEnd Optical section to finish projecting. + * @param name Name for the newly created image. If + * null the name of the Image linked + * to the Pixels qualified by + * pixelsId will be used with a + * "Projection" suffix. For example, + * GFP-H2B Image of HeLa Cells will have an + * Image name of + * GFP-H2B Image of HeLa Cells Projection + * used for the projection. + * @return The Id of the newly created Image which has been + * projected. + * @throws ValidationException Where: + *
    + *
  • algorithm is unknown
  • + *
  • tStart is out of range
  • + *
  • tEnd is out of range
  • + *
  • tStart > tEnd
  • + *
  • channels is null or has indexes out of + * range
  • + *
  • zStart is out of range
  • + *
  • zEnd is out of range
  • + *
  • zStart > zEnd
  • + *
  • the Pixels set qualified by pixelsId is + * unlocatable.
  • + *
+ * @see #projectStack + **/ long projectPixels(long pixelsId, omero::model::PixelsType pixelsType, omero::constants::projection::ProjectionType algorithm, int tStart, int tEnd, From 13ff8ac51fc69e3f710960901bb9f48715a73ad1 Mon Sep 17 00:00:00 2001 From: Sebastien Besson Date: Mon, 11 Apr 2016 10:29:55 +0100 Subject: [PATCH 56/68] Copy ome.api.IQuery Javadoc to omero.api.IQuery --- .../blitz/resources/omero/api/IQuery.ice | 223 +++++++++++++++++- 1 file changed, 214 insertions(+), 9 deletions(-) diff --git a/components/blitz/resources/omero/api/IQuery.ice b/components/blitz/resources/omero/api/IQuery.ice index 3ee5671364f..bb239fa425c 100644 --- a/components/blitz/resources/omero/api/IQuery.ice +++ b/components/blitz/resources/omero/api/IQuery.ice @@ -20,32 +20,218 @@ module omero { module api { /** - * See IQuery.html + * Provides methods for directly querying object graphs. As far as is + * possible, IQuery should be considered the lowest level DB-access + * (SELECT) interface. + * Unlike the {@link omero.api.IUpdate} interface, using other methods + * will most likely not leave the database in an inconsistent state, + * but may provide stale data in some situations. + * + * By convention, all methods that begin with get will + * never return a null or empty {@link java.util.Collection}, but + * instead will throw a {@link omero.ValidationException}. + * **/ ["ami", "amd"] interface IQuery extends ServiceInterface { + + /** + * Looks up an entity by class and id. If no such object + * exists, an exception will be thrown. + * + * @param klass the type of the entity. Not null. + * @param id the entity's id + * @return an initialized entity + * @throws ValidationException if the id doesn't exist. + **/ idempotent omero::model::IObject get(string klass, long id) throws ServerError; + + + /** + * Looks up an entity by class and id. If no such objects + * exists, return a null. + * + * @param klass klass the type of the entity. Not null. + * @param id the entity's id + * @return an initialized entity or null if id doesn't exist. + **/ idempotent omero::model::IObject find(string klass, long id) throws ServerError; + + /** + * Looks up all entities that belong to this class and match + * filter. + * + * @param klass entity type to be searched. Not null. + * @param filter filters the result set. Can be null. + * @return a collection if initialized entities or an empty + * List if none exist. + **/ idempotent IObjectList findAll(string klass, omero::sys::Filter filter) throws ServerError; + + /** + * Searches based on provided example entity. The example + * entity should uniquely specify the entity or an + * exception will be thrown. + * + * Note: findByExample does not operate on the id + * field. For that, use {@link #find}, {@link #get}, + * {@link #findByQuery}, or {@link #findAllByQuery}. + * + * @param example Non-null example object. + * @return Possibly null IObject result. + * @throws ApiUsageException if more than one result is return. + **/ idempotent omero::model::IObject findByExample(omero::model::IObject example) throws ServerError; + + /** + * Searches based on provided example entity. The returned + * entities will be limited by the {@link omero.sys.Filter} + * object. + * + * Note: findAllbyExample does not operate on the + * id field. + * For that, use {@link #find}, {@link #get}, + * {@link #findByQuery}, or {@link #findAllByQuery} + * + * + * @param example Non-null example object. + * @param filter filters the result set. Can be null. + * @return Possibly empty List of IObject results. + **/ idempotent IObjectList findAllByExample(omero::model::IObject example, omero::sys::Filter filter) throws ServerError; + + /** + * Searches a given field matching against a String. Method + * does not allow for case sensitive or insensitive + * searching since this is essentially a lookup. The existence + * of more than one result will result in an exception. + * + * @param klass type of entity to be searched + * @param field the name of the field, either as simple string + * or as public final static from the entity + * class, e.g. {@link omero.model.Project#NAME} + * @param value String used for search. + * @return found entity or possibly null. + * @throws ome.conditions.ApiUsageException + * if more than one result. + **/ idempotent omero::model::IObject findByString(string klass, string field, string value) throws ServerError; + + /** + * Searches a given field matching against a String. Method + * allows for case sensitive or insensitive searching using + * the (I)LIKE comparators. Result set will be reduced by the + * {@link omero.sys.Filter} instance. + * + * @param klass type of entity to be searched. Not null. + * @param field the name of the field, either as simple string + * or as public final static from the entity + * class, e.g. {@link omero.model.Project#NAME}. + * Not null. + * @param value String used for search. Not null. + * @param caseSensitive whether to use LIKE or ILIKE + * @param filter filters the result set. Can be null. + * @return A list (possibly empty) with the results. + **/ idempotent IObjectList findAllByString(string klass, string field, string value, bool caseSensitive, omero::sys::Filter filter) throws ServerError; + + /** + * Executes the stored query with the given name. If a query + * with the name cannot be found, an exception will be thrown. + * + * The queryName parameter can be an actual query String if the + * StringQuerySource is configured on the server and the user + * running the query has proper permissions. + * + * @param query Query to execute + * @param params + * @return Possibly null IObject result. + * @throws ValidationException + **/ idempotent omero::model::IObject findByQuery(string query, omero::sys::Parameters params) throws ServerError; + + /** + * Executes the stored query with the given name. If a query + * with the name cannot be found, an exception will be thrown. + * + * The queryName parameter can be an actual query String if the + * StringQuerySource is configured on the server and the user + * running the query has proper permissions. + * + * Queries can only return lists of + * {@link omero.model.IObject} instances. This means + * all must be of the form: + * + *
+                 * select this from SomeModelClass this ...
+                 * 
+ * + * though the alias "this" is unimportant. Do not try to + * return multiple classes in one call like: + * + *
+                 * select this, that from SomeClass this, SomeOtherClass that ...
+                 * 
+ * + * nor to project values out of an object: + * + *
+                 * select this.name from SomeClass this ...
+                 * 
+ * + * If a page is desired, add it to the query parameters. + * + * @param query Query to execute. Not null. + * @param params + * @return Possibly empty List of IObject results. + */ idempotent IObjectList findAllByQuery(string query, omero::sys::Parameters params) throws ServerError; + + /** + * Executes a full text search based on Lucene. Each term in + * the query can also be prefixed by the name of the field to + * which is should be restricted. + * + * Examples: + *
    + *
  • owner:root AND annotation:someTag
  • + *
  • file:xml AND name:*hoechst*
  • + *
+ * + * For more information, see + * Query Parser Syntax + * + * The return values are first filtered by the security system. + * + * @param klass A non-null class specification of which type + * should be searched. + * @param query A non-null query string. An empty string will + * return no results. + * @param params + * Currently the parameters themselves are unused. + * But the {@link omero.sys.Parameters#theFilter} + * can be used to limit the number of results + * returned ({@link omero.sys.Filter#limit}) or the + * user for who the results will be found + * ({@link omero.sys.Filter#ownerId}). + * @return A list of loaded {@link omero.model.IObject} + * instances. Never null. + **/ idempotent IObjectList findAllByFullText(string klass, string query, omero::sys::Parameters params) throws ServerError; /** * Return a sequence of {@link omero.RType} sequences. * *

- * Each element of the outer sequence is one row in the return value. - * Each element of the inner sequence is one column specified in the HQL. + * Each element of the outer sequence is one row in the return + * value. + * Each element of the inner sequence is one column specified + * in the HQL. *

* *

* {@link omero.model.IObject} instances are returned wrapped - * in an {@link omero.rtype.RObject} instance. Primitives are + * in an {@link omero.RObject} instance. Primitives are * mapped to the expected {@link omero.RType} subclass. Types * without an {@link omero.RType} mapper if returned will * throw an exception if present in the select except where a @@ -54,17 +240,20 @@ module omero { * *

    *
  • - * {@link omero.model.Permissions} instances are serialized to an {@link omero.RMap} - * containing the keys: perms, canAnnotate, canEdit, canLink, canDelete + * {@link omero.model.Permissions} instances are + * serialized to an {@link omero.RMap} containing the + * keys: perms, canAnnotate, canEdit, canLink, canDelete *
  • *
  • - * The quantity types like {@link omero.model.Length} are serialized - * to an {@link omero.RMap} containing the keys: value, unit, symbol + * The quantity types like {@link omero.model.Length} are + * serialized to an {@link omero.RMap} containing the + * keys: value, unit, symbol *
  • *
* *

- * As with SQL, if an aggregation statement is used, a group by clause must be added. + * As with SQL, if an aggregation statement is used, a group + * by clause must be added. *

* *

@@ -81,6 +270,22 @@ module omero { **/ idempotent RTypeSeqSeq projection(string query, omero::sys::Parameters params) throws ServerError; + /** + * Refreshes an entire {@link omero.model.IObject} graph, + * recursive loading all data for the managed instances in the + * graph from the database. If any non-managed entities are + * detected (e.g. without ids), an + * {@link omero.ApiUsageException} will be thrown. + * + * @param iObject Non-null managed {@link omero.model.IObject} + * graph which should have all values + * re-assigned from the database + * @return a similar {@link omero.model.IObject} graph (with + * possible additions and deletions) which is in-sync + * with the database. + * @throws ApiUsageException if any non-managed entities are + * found. + **/ idempotent omero::model::IObject refresh(omero::model::IObject iObject) throws ServerError; }; From e4598201343c9465c367f2f3fb36bace5fc04c17 Mon Sep 17 00:00:00 2001 From: Sebastien Besson Date: Mon, 11 Apr 2016 10:30:33 +0100 Subject: [PATCH 57/68] Copy ome.api.IRepositoryInfo Javadoc to omero.api.IRepositoryInfo --- .../resources/omero/api/IRepositoryInfo.ice | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/components/blitz/resources/omero/api/IRepositoryInfo.ice b/components/blitz/resources/omero/api/IRepositoryInfo.ice index 236b4397d89..425b286ed4c 100644 --- a/components/blitz/resources/omero/api/IRepositoryInfo.ice +++ b/components/blitz/resources/omero/api/IRepositoryInfo.ice @@ -19,14 +19,70 @@ module omero { module api { /** - * See IRepositoryInfo.html + * Provides methods for obtaining information for server repository + * disk space allocation. Could be used generically to obtain usage + * information for any mount point, however, this interface is + * prepared for the API to provide methods to obtain usage info for + * the server filesystem containing the image uploads. For the OMERO + * server base this is /OMERO. For this implementation it could be + * anything e.g. /Data1. + * + * Methods that fail or cannot execute on the server will throw an + * InternalException. This would not be normal and would indicate some + * server or disk failure. **/ ["ami", "amd"] interface IRepositoryInfo extends ServiceInterface { + /** + * Returns the total space in bytes for this file system + * including nested subdirectories. + * + * @return Total space used on this file system. + * @throws ResourceError If there is a problem retrieving disk + * space used. + **/ idempotent long getUsedSpaceInKilobytes() throws ServerError; + + /** + * Returns the free or available space on this file system + * including nested subdirectories. + * + * @return Free space on this file system in KB. + * @throws ResourceError If there is a problem retrieving disk + * space free. + **/ idempotent long getFreeSpaceInKilobytes() throws ServerError; + + /** + * Returns a double of the used space divided by the free + * space. + * This method will be called by a client to watch the + * repository filesystem so that it doesn't exceed 95% full. + * + * @return Fraction of used/free. + * @throws ResourceError If there is a problem calculating the + * usage fraction. + **/ idempotent double getUsageFraction() throws ServerError; + + /** + * Checks that image data repository has not exceeded 95% disk + * space use level. + * @throws ResourceError If the repository usage has exceeded + * 95%. + * @throws InternalException If there is a critical failure + * while sanity checking the repository. + **/ void sanityCheckRepository() throws ServerError; + + /** + * Removes all files from the server that do not have an + * OriginalFile complement in the database, all the Pixels + * that do not have a complement in the database and all the + * Thumbnail's that do not have a complement in the database. + * + * @throws ResourceError If deletion fails. + **/ void removeUnusedFiles() throws ServerError; }; From b7e096c9610e03699d55e774248300ebfa0c2447 Mon Sep 17 00:00:00 2001 From: Sebastien Besson Date: Tue, 12 Apr 2016 16:43:31 +0100 Subject: [PATCH 58/68] Address Colin's comments --- components/blitz/resources/omero/api/IMetadata.ice | 12 ++++++------ components/blitz/resources/omero/api/IPixels.ice | 8 +++++--- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/components/blitz/resources/omero/api/IMetadata.ice b/components/blitz/resources/omero/api/IMetadata.ice index 4117fa5b9e6..eb2a97b8990 100644 --- a/components/blitz/resources/omero/api/IMetadata.ice +++ b/components/blitz/resources/omero/api/IMetadata.ice @@ -24,8 +24,8 @@ module omero { ["ami", "amd"] interface IMetadata extends ServiceInterface { /** - * Loads the logical channels and the acquisition - * metadata related to them. + * Loads the logical channels and the acquisition metadata + * related to them. * * @param ids The collection of logical channel's ids. * Mustn't be null. @@ -91,11 +91,11 @@ module omero { //idempotent omero::metadata::TagContainerList loadTags(long id, bool withObjects, omero::sys::Parameters options) throws ServerError; /** - * Loads the Tag Set if the id is specified otherwise loads - * all the Tag Set. + * Loads the TagSet if the id is specified otherwise loads + * all the TagSet. * * @param ids The id of the tag to load or -1. - * @return Map whose key is a Tag/Tag Set and the + * @return Map whose key is a Tag/TagSet and the * value either a Map or a list of related * DataObject. **/ @@ -106,7 +106,7 @@ module omero { * AnnotationAnnotatioLink objects and, if the * orphan parameters is true, the * TagAnnotation object. - * Note that the difference between a Tag Set and a Tag is made + * Note that the difference between a TagSet and a Tag is made * using the NS_INSIGHT_TAG_SET namespace. * * @param options The POJO options. diff --git a/components/blitz/resources/omero/api/IPixels.ice b/components/blitz/resources/omero/api/IPixels.ice index 29c91d96185..a13eaa5ef3e 100644 --- a/components/blitz/resources/omero/api/IPixels.ice +++ b/components/blitz/resources/omero/api/IPixels.ice @@ -153,7 +153,7 @@ module omero { * * @param enumClass Enumeration class. * @return List of all enumeration objects for the - * klass. + * enumClass. **/ idempotent IObjectList getAllEnumerations(string enumClass) throws ServerError; @@ -178,9 +178,11 @@ module omero { * @param sizeX The new size across the X-axis. * null if the copy should maintain * the same size. - * @param sizeY The new size across the Y-axis. * null if the copy should maintain + * @param sizeY The new size across the Y-axis. + * null if the copy should maintain * the same size. - * @param sizeZ The new size across the Z-axis. * null if the copy should maintain + * @param sizeZ The new size across the Z-axis. + * null if the copy should maintain * the same size. * @param sizeT The new number of timepoints. * null if the copy should maintain From 88ba3f2c3cae6238908a12a0455a9fed85c2b2be Mon Sep 17 00:00:00 2001 From: Sebastien Besson Date: Wed, 13 Apr 2016 13:06:44 +0100 Subject: [PATCH 59/68] Fix TagSets spelling --- components/blitz/resources/omero/api/IMetadata.ice | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/blitz/resources/omero/api/IMetadata.ice b/components/blitz/resources/omero/api/IMetadata.ice index eb2a97b8990..11bf4be434b 100644 --- a/components/blitz/resources/omero/api/IMetadata.ice +++ b/components/blitz/resources/omero/api/IMetadata.ice @@ -102,7 +102,7 @@ module omero { idempotent LongIObjectListMap loadTagContent(omero::sys::LongList ids, omero::sys::Parameters options) throws ServerError; /** - * Loads all the tag Sets. Returns a collection of + * Loads all the TagSets. Returns a collection of * AnnotationAnnotatioLink objects and, if the * orphan parameters is true, the * TagAnnotation object. From c8a933e4c8c5e1643b208823990866fa9fc79274 Mon Sep 17 00:00:00 2001 From: Ian Munro Date: Wed, 13 Apr 2016 18:51:37 +0100 Subject: [PATCH 60/68] Missing semi-colon --- .../tools/OmeroM/src/annotations/getFileAnnotationContent.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/tools/OmeroM/src/annotations/getFileAnnotationContent.m b/components/tools/OmeroM/src/annotations/getFileAnnotationContent.m index 9e552c33ba4..ae4a1cc4924 100644 --- a/components/tools/OmeroM/src/annotations/getFileAnnotationContent.m +++ b/components/tools/OmeroM/src/annotations/getFileAnnotationContent.m @@ -50,5 +50,5 @@ function getFileAnnotationContent(session, fileAnnotation, path) 'Could not load the file annotation: %u', faID); end -getOriginalFileContent(session, fileAnnotation.getFile(), path) +getOriginalFileContent(session, fileAnnotation.getFile(), path); From fc797e00c024b611e8bdb3b96560afb73616afea Mon Sep 17 00:00:00 2001 From: Ola Tarkowska Date: Sun, 17 Apr 2016 01:08:23 +0100 Subject: [PATCH 61/68] choose server id when logging in --- components/tests/ui/resources/web/login.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/components/tests/ui/resources/web/login.txt b/components/tests/ui/resources/web/login.txt index a71ffdbcdea..e560128e6df 100644 --- a/components/tests/ui/resources/web/login.txt +++ b/components/tests/ui/resources/web/login.txt @@ -12,6 +12,7 @@ Library Selenium2Library *** Keywords *** User "${username}" logs in with password "${password}" Open Browser To Login Page + Select Server ${SERVER_ID} Input username ${username} Input password ${password} Submit credentials From 27edb54ccb27c7ed81c7ae8aa37f0cd89676115c Mon Sep 17 00:00:00 2001 From: Will Moore Date: Mon, 18 Apr 2016 15:26:48 +0100 Subject: [PATCH 62/68] Script menu and activities panel handle long lists --- .../omeroweb/webgateway/static/webgateway/css/ome.header.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/components/tools/OmeroWeb/omeroweb/webgateway/static/webgateway/css/ome.header.css b/components/tools/OmeroWeb/omeroweb/webgateway/static/webgateway/css/ome.header.css index e80155ad756..2e0f8c6eea2 100644 --- a/components/tools/OmeroWeb/omeroweb/webgateway/static/webgateway/css/ome.header.css +++ b/components/tools/OmeroWeb/omeroweb/webgateway/static/webgateway/css/ome.header.css @@ -297,6 +297,8 @@ max-height: 500px; min-width: 300px !important; left:-90px; + overflow-y: auto; + overflow-x: hidden; display: none; } From c363a053648f6ab55d9269da6c9f7234754b7456 Mon Sep 17 00:00:00 2001 From: Mark Carroll Date: Wed, 20 Apr 2016 10:44:34 +0100 Subject: [PATCH 63/68] fix out-of-sync Javadoc --- components/blitz/src/omero/cmd/graphs/SkipHeadI.java | 1 - 1 file changed, 1 deletion(-) diff --git a/components/blitz/src/omero/cmd/graphs/SkipHeadI.java b/components/blitz/src/omero/cmd/graphs/SkipHeadI.java index 05a3b6661fa..efd5b70b550 100644 --- a/components/blitz/src/omero/cmd/graphs/SkipHeadI.java +++ b/components/blitz/src/omero/cmd/graphs/SkipHeadI.java @@ -78,7 +78,6 @@ public class SkipHeadI extends SkipHead implements IRequest { * Construct a new skip-head request; called from {@link GraphRequestFactory#getRequest(Class)}. * @param graphPathBean the graph path bean to use * @param graphRequestFactory a means of instantiating the sub-request - * @throws GraphException if the request was not of an appropriate type */ public SkipHeadI(GraphPathBean graphPathBean, GraphRequestFactory graphRequestFactory) { this.graphPathBean = graphPathBean; From 4d7cc9190911378f6724379ca15299bcdde812bf Mon Sep 17 00:00:00 2001 From: Mark Carroll Date: Wed, 20 Apr 2016 10:45:09 +0100 Subject: [PATCH 64/68] fix minor typo in comment --- components/server/src/ome/security/basic/CurrentDetails.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/server/src/ome/security/basic/CurrentDetails.java b/components/server/src/ome/security/basic/CurrentDetails.java index 7e2a9c4ac58..28f2537b09f 100644 --- a/components/server/src/ome/security/basic/CurrentDetails.java +++ b/components/server/src/ome/security/basic/CurrentDetails.java @@ -57,7 +57,7 @@ * * This information is stored in a Details object, but unlike Details which * assumes that an empty value signifies increased security levels, empty values - * here signifiy reduced security levels. E.g., + * here signify reduced security levels. E.g., * * Details: user == null ==> object belongs to root CurrentDetails: user == null * ==> current user is "nobody" (anonymous) From 7910e17d70a26937349c826067c30a5fbf98c10a Mon Sep 17 00:00:00 2001 From: Mark Carroll Date: Wed, 20 Apr 2016 11:33:07 +0100 Subject: [PATCH 65/68] add integration test for duplication via skip-head --- .../test/integration/DuplicationTest.java | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/components/tools/OmeroJava/test/integration/DuplicationTest.java b/components/tools/OmeroJava/test/integration/DuplicationTest.java index be8a6c12975..84ba0165cac 100644 --- a/components/tools/OmeroJava/test/integration/DuplicationTest.java +++ b/components/tools/OmeroJava/test/integration/DuplicationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 University of Dundee & Open Microscopy Environment. + * Copyright (C) 2015-2016 University of Dundee & Open Microscopy Environment. * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -38,11 +38,15 @@ import omero.cmd.Duplicate; import omero.cmd.DuplicateResponse; import omero.cmd.ERR; +import omero.cmd.SkipHead; import omero.gateway.util.Requests; import omero.model.Annotation; import omero.model.AnnotationAnnotationLink; import omero.model.AnnotationAnnotationLinkI; import omero.model.Channel; +import omero.model.Dataset; +import omero.model.DatasetImageLink; +import omero.model.DatasetImageLinkI; import omero.model.DoubleAnnotationI; import omero.model.Ellipse; import omero.model.EllipseI; @@ -1218,4 +1222,51 @@ public void testDuplicateImageWithOpposingTypeOptions() throws Exception { Assert.assertEquals(response.name, "bad-class"); } } + + /** + * Test duplication of a dataset's image. {@link SkipHead} is used to identify the image via its dataset. + * @throws Exception unexpected + */ + @Test(groups = "ticket:13197") + public void testDuplicateImageViaSkipHead() throws Exception { + newUserAndGroup("rwr---"); + + /* create a dataset with an image */ + + final Dataset originalDataset = mmFactory.simpleDataset(); + final Image originalImage = mmFactory.simpleImage(); + DatasetImageLink originalLink = new DatasetImageLinkI(); + originalLink.setParent(originalDataset); + originalLink.setChild(originalImage); + originalLink = (DatasetImageLink) iUpdate.saveAndReturnObject(originalLink); + + /* note the objects (and their IDs) that were thus created and saved */ + + final long originalDatasetId = originalLink.getParent().getId().getValue(); + final long originalImageId = originalLink.getChild().getId().getValue(); + final long originalLinkId = originalLink.getId().getValue(); + testImages.add(originalImageId); + + /* duplicate the image via SkipHead */ + + final SkipHead dup = Requests.skipHead().target("Dataset").id(originalDatasetId).startFrom("Image").request(Duplicate.class).build(); + final DuplicateResponse response = (DuplicateResponse) doChange(dup); + + /* check that the response includes duplication of only the image */ + + final List reportedDatasetIds = response.duplicates.get("ome.model.containers.Dataset"); + final List reportedImageIds = response.duplicates.get("ome.model.core.Image"); + final List reportedLinkIds = response.duplicates.get("ome.model.annotations.ImageAnnotationLink"); + + Assert.assertNull(reportedDatasetIds); + Assert.assertEquals(reportedImageIds.size(), 1); + Assert.assertNull(reportedLinkIds); + + /* check that the reported image has a new ID */ + + final long reportedImageId = reportedImageIds.get(0); + testImages.add(reportedImageId); + + Assert.assertNotEquals(originalImageId, reportedImageId); + } } From 12cc1fe65855a6c58f758d3dcf2f726acf4b2bd5 Mon Sep 17 00:00:00 2001 From: Mark Carroll Date: Wed, 20 Apr 2016 11:33:59 +0100 Subject: [PATCH 66/68] switch test teardown from using deprecated request builder --- .../tools/OmeroJava/test/integration/DuplicationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/tools/OmeroJava/test/integration/DuplicationTest.java b/components/tools/OmeroJava/test/integration/DuplicationTest.java index 84ba0165cac..728755f8e12 100644 --- a/components/tools/OmeroJava/test/integration/DuplicationTest.java +++ b/components/tools/OmeroJava/test/integration/DuplicationTest.java @@ -152,7 +152,7 @@ public void clearTestImages() { */ @AfterClass public void deleteTestImages() throws Exception { - final Delete2 delete = Requests.delete("Image", testImages); + final Delete2 delete = Requests.delete().target("Image").id(testImages).build(); doChange(root, root.getSession(), delete, true); clearTestImages(); } From ef019cc83a2e85cea07a6a1f9b585045707d540c Mon Sep 17 00:00:00 2001 From: Mark Carroll Date: Wed, 20 Apr 2016 11:34:16 +0100 Subject: [PATCH 67/68] have SkipHead pass through underlying request's call context --- components/blitz/src/omero/cmd/graphs/SkipHeadI.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/components/blitz/src/omero/cmd/graphs/SkipHeadI.java b/components/blitz/src/omero/cmd/graphs/SkipHeadI.java index efd5b70b550..b34b67d9d9e 100644 --- a/components/blitz/src/omero/cmd/graphs/SkipHeadI.java +++ b/components/blitz/src/omero/cmd/graphs/SkipHeadI.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014-2015 University of Dundee & Open Microscopy Environment. + * Copyright (C) 2014-2016 University of Dundee & Open Microscopy Environment. * All rights reserved. * * This program is free software; you can redistribute it and/or modify @@ -29,7 +29,6 @@ import com.google.common.base.Function; import com.google.common.collect.HashMultimap; -import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.SetMultimap; @@ -37,7 +36,6 @@ import ome.services.graphs.GraphException; import ome.services.graphs.GraphPathBean; import ome.services.graphs.GraphPolicy; -import ome.system.Login; import omero.cmd.HandleI.Cancel; import omero.cmd.ERR; import omero.cmd.Helper; @@ -58,8 +56,6 @@ public class SkipHeadI extends SkipHead implements IRequest { private static final Logger LOGGER = LoggerFactory.getLogger(SkipHeadI.class); - private static final ImmutableMap ALL_GROUPS_CONTEXT = ImmutableMap.of(Login.OMERO_GROUP, "-1"); - private static final ImmutableSet REQUEST_FAILURE_FLAGS = ImmutableSet.of(State.CANCELLED, State.FAILURE); private final GraphPathBean graphPathBean; @@ -86,7 +82,7 @@ public SkipHeadI(GraphPathBean graphPathBean, GraphRequestFactory graphRequestFa @Override public Map getCallContext() { - return new HashMap(ALL_GROUPS_CONTEXT); + return ((IRequest) request).getCallContext(); } @Override From 45e0b555f5e3cd6c7981d6e9c8ad879cb2a8bf68 Mon Sep 17 00:00:00 2001 From: Mark Carroll Date: Wed, 20 Apr 2016 11:40:02 +0100 Subject: [PATCH 68/68] break long line (whitespace only) --- .../tools/OmeroJava/test/integration/DuplicationTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/tools/OmeroJava/test/integration/DuplicationTest.java b/components/tools/OmeroJava/test/integration/DuplicationTest.java index 728755f8e12..8c422c00d4b 100644 --- a/components/tools/OmeroJava/test/integration/DuplicationTest.java +++ b/components/tools/OmeroJava/test/integration/DuplicationTest.java @@ -1249,7 +1249,8 @@ public void testDuplicateImageViaSkipHead() throws Exception { /* duplicate the image via SkipHead */ - final SkipHead dup = Requests.skipHead().target("Dataset").id(originalDatasetId).startFrom("Image").request(Duplicate.class).build(); + final SkipHead dup = + Requests.skipHead().target("Dataset").id(originalDatasetId).startFrom("Image").request(Duplicate.class).build(); final DuplicateResponse response = (DuplicateResponse) doChange(dup); /* check that the response includes duplication of only the image */