Esempio n. 1
0
    def create_tag(self, name, description, text="tag"):
        """
        Creates a new tag object. Returns the new tag object.

        If either the name or description parameters are None, the user will be
        prompted to input them.

        The "text" parameter should be the text description to use upon user
        input. For example, if we were creating a tag, this would be "tag"
        (the default). If we were creating a tagset, this could be "tag set".
        """
        if name is None:
            name = raw_input("Please enter a name for this %s: " % text)

        if description is None:
            description = raw_input("Please enter a description for this %s: "
                                    % text)

        if name is not None and description is not None and name != '':
            tag = TagAnnotationI()
            tag.textValue = rstring(name)
            if description is not None and len(description) > 0:
                tag.description = rstring(description)

            return tag
        else:
            self.ctx.err("Tag/tagset name cannot be 'None' or empty.")
            sys.exit(1)
Esempio n. 2
0
    def test_container_tags_update(self, user1, dtype):
        """
        Test updating a Object without losing linked Tags.

        If we load a Object without loading Annotations, then update
        and save the Object, we don't want to lose Annotation links
        """
        conn = get_connection(user1)
        user_name = conn.getUser().getName()
        django_client = self.new_django_client(user_name, user_name)

        container = dtype[1]()
        container.name = rstring('test_container_tags_update')
        tag = TagAnnotationI()
        tag.textValue = rstring('tag')
        container.linkAnnotation(tag)
        container = get_update_service(user1).saveAndReturnObject(container)

        version = api_settings.API_VERSIONS[-1]
        object_url = reverse('api_%s' % dtype[0],
                             kwargs={
                                 'api_version': version,
                                 'object_id': container.id.val
                             })
        save_url = reverse('api_save', kwargs={'api_version': version})
        # Get container, update and save back
        rsp = get_json(django_client, object_url)
        object_json = rsp['data']
        object_json['Name'] = 'renamed container'
        put_json(django_client, save_url, object_json)

        # Check container has been updated and still has annotation links
        proj = conn.getObject(dtype[0], container.id.val)
        assert proj.getName() == 'renamed container'
        assert len(list(proj.listAnnotations())) == 1
def createTag(name, description=None):
    print "Create Tag:", name
    tag = TagAnnotationI()
    tag.textValue = rstring(name)
    if description is not None:
        tag.description = rstring(description)
    return tag
Esempio n. 4
0
    def test_validation_exception(self, user_A):
        """Test handling when we try to save something invalid."""
        conn = get_connection(user_A)
        group = conn.getEventContext().groupId
        userName = conn.getUser().getName()
        django_client = self.new_django_client(userName, userName)
        version = api_settings.API_VERSIONS[-1]
        save_url = reverse('api_save', kwargs={'api_version': version})
        save_url += '?group=' + native_str(group)

        # Create Tag
        tag = TagAnnotationI()
        tag.textValue = rstring('test_tag')
        tag = conn.getUpdateService().saveAndReturnObject(tag)
        tag_json = {'Value': 'test_tag',
                    '@id': tag.id.val,
                    '@type': OME_SCHEMA_URL + '#TagAnnotation'}
        # Add Tag twice to Project to get Validation Exception
        payload = {'Name': 'test_validation',
                   '@type': OME_SCHEMA_URL + '#Project',
                   'Annotations': [tag_json, tag_json]}
        rsp = post_json(django_client, save_url, payload, status_code=400)
        # NB: message contains whole stack trace
        assert "ValidationException" in rsp['message']
        assert rsp['stacktrace'].startswith(
            'Traceback (most recent call last):')
Esempio n. 5
0
    def create_tag(self, name, description, text="tag"):
        """
        Creates a new tag object. Returns the new tag object.

        If either the name or description parameters are None, the user will be
        prompted to input them.

        The "text" parameter should be the text description to use upon user
        input. For example, if we were creating a tag, this would be "tag"
        (the default). If we were creating a tagset, this could be "tag set".
        """
        if name is None:
            name = raw_input("Please enter a name for this %s: " % text)

        if description is None:
            description = raw_input(
                "Please enter a description for this %s: " % text)

        if name is not None and description is not None and name != '':
            tag = TagAnnotationI()
            tag.textValue = rstring(name)
            if description is not None and len(description) > 0:
                tag.description = rstring(description)

            return tag
        else:
            self.ctx.err("Tag/tagset name cannot be 'None' or empty.")
            sys.exit(1)
Esempio n. 6
0
 def tag(self, request):
     """Returns a new OMERO TagAnnotation with required fields set."""
     name = rstring(self.uuid())
     for index in range(request.param):
         tag = TagAnnotationI()
         tag.textValue = name
         tag = self.update.saveAndReturnObject(tag)
     return tag
Esempio n. 7
0
def tag(request, itest, update_service):
    """Returns a new OMERO TagAnnotation with required fields set."""
    name = rstring(itest.uuid())
    for index in range(request.param):
        tag = TagAnnotationI()
        tag.textValue = name
        tag = update_service.saveAndReturnObject(tag)
    return tag
def tags_userA_userB(request, userA, userB, groupA):
    """
    Returns new OMERO Tags
    """
    tags = []
    ctx = {'omero.group': str(groupA.id.val)}
    for name, user in zip(["userAtag", "userBtag"], [userA, userB]):
        tag = TagAnnotationI()
        tag.textValue = rstring(name)
        tag = get_update_service(user).saveAndReturnObject(tag, ctx)
        tags.append(tag)
    tags.sort(cmp_id)
    return tags
Esempio n. 9
0
    def test_tag_annotation(self):
        """Tests AnnotationWrapper methods return strings"""
        ns = u'πλζ.test.ζ'
        text_value = u'Tαg - ℗'
        obj = TagAnnotationI()
        obj.textValue = rstring(text_value)
        obj.ns = rstring(ns)

        tag = MockConnection(obj).getObject("Annotation", 1)
        assert tag.getValue() == text_value.encode('utf8')
        assert tag.textValue == text_value
        assert tag.getNs() == ns.encode('utf8')
        assert tag.ns == ns
def tags_userA_userB(request, userA, userB, groupA):
    """
    Returns new OMERO Tags
    """
    tags = []
    ctx = {'omero.group': str(groupA.id.val)}
    for name, user in zip(["userAtag", "userBtag"], [userA, userB]):
        tag = TagAnnotationI()
        tag.textValue = rstring(name)
        tag = get_update_service(user).saveAndReturnObject(tag, ctx)
        tags.append(tag)
    tags.sort(cmp_id)
    return tags
Esempio n. 11
0
def tagset_tag(request, itest, update_service):
    """
    Returns a new OMERO TagAnnotation, with the OMERO.insight tagset
    namespace set, and linked TagAnnotation with required fields set.
    """
    tagset = TagAnnotationI()
    tagset.ns = rstring(omero.constants.metadata.NSINSIGHTTAGSET)
    tagset.textValue = rstring(itest.uuid())
    tag = TagAnnotationI()
    tag.textValue = rstring(itest.uuid())
    tagset.linkAnnotation(tag)
    return update_service.saveAndReturnObject(tagset)
Esempio n. 12
0
def tags_userA_userB(request, userA, userB, groupA):
    """
    Returns new OMERO Tags with descriptions
    """
    tags = []
    ctx = {'omero.group': native_str(groupA.id.val)}
    for name, user in zip(["userAtag", "userBtag"], [userA, userB]):
        tag = TagAnnotationI()
        tag.textValue = rstring(name)
        # Only add description to first tag
        if name == "userAtag":
            tag.description = rstring('tag description')
        tag = get_update_service(user).saveAndReturnObject(tag, ctx)
        tags.append(tag)
    tags.sort(key=lambda x: unwrap(x.id))
    return tags
Esempio n. 13
0
    def createset(self, args):
        """
        Create a tag set command.
        """
        tags = []
        if args.tag:
            if type(args.tag) is list:
                tags = args.tag
            else:
                tags = [args.tag]
        else:
            # Should not happen
            self.ctx.err("Missing tag parameter")
            sys.exit(1)

        tag = self.create_tag(args.name, args.desc, text="tag set")
        tag.ns = rstring(omero.constants.metadata.NSINSIGHTTAGSET)
        links = []
        for t in tags:
            link = AnnotationAnnotationLinkI()
            link.parent = tag
            link.child = TagAnnotationI(rlong(int(t)), False)
            links.append(link)
        client = self.ctx.conn(args)
        session = client.getSession()
        update_service = session.getUpdateService()
        try:
            links = update_service.saveAndReturnArray(links)
            self.ctx.out("TagAnnotation:%s" % links[0].parent.id.val)
        except omero.ValidationException as e:
            self.ctx.err(e.message)
            self.ctx.err("Check that tag '%s' exists." % t)
            sys.exit(1)
def run(password, admin_name, target, tag, host, port):

    for i in range(1, 51):

        username = "******" % i
        print(username)
        conn = BlitzGateway(username, password, host=host, port=port)
        try:
            conn.connect()
            updateService = conn.getUpdateService()
            ds = conn.getObject("Dataset",
                                attributes={'name': target},
                                opts={'owner': conn.getUserId()})
            if ds is None:
                print("No dataset with name %s found" % target)
                continue
            params = omero.sys.ParametersI()
            params.addString('username', admin_name)
            query = "from TagAnnotation where textvalue='%s' \
                    AND details.owner.omeName=:username" % tag
            query_service = conn.getQueryService()
            tags = query_service.findAllByQuery(query, params,
                                                conn.SERVICE_OPTS)
            if len(tags) == 0:
                print("No tag with name %s found" % tag)
                continue
            tag_id = tags[0].id.getValue()
            print(tag_id)
            links = []
            for image in ds.listChildren():
                name = image.getName()
                if name in images_to_tag:
                    # Check first that the image is not tagged
                    params = omero.sys.ParametersI()
                    params.addLong('parent', image.id)
                    params.addLong('child', tag_id)
                    query = "select link from ImageAnnotationLink as link \
                             where link.parent.id=:parent \
                             AND link.child.id=:child"

                    values = query_service.findAllByQuery(
                        query, params, conn.SERVICE_OPTS)
                    if len(values) == 0:
                        link = ImageAnnotationLinkI()
                        link.parent = ImageI(image.id, False)
                        link.child = TagAnnotationI(tag_id, False)
                        links.append(link)
                    else:
                        print("Tag %s already linked to %s" % (tag, name))
            if len(links) > 0:
                updateService.saveArray(links)
        except Exception as exc:
            print("Error when tagging the images: %s" % str(exc))
        finally:
            conn.close()
Esempio n. 15
0
 def projects_dataset_image_tag(self):
     """
     Returns 2 new OMERO Projects, linked Dataset and linked Image populated
     by an L{test.integration.library.ITest} instance with required fields
     set. Also a Tag linked to both Projects.
     """
     project1 = ProjectI()
     project1.name = rstring(self.uuid())
     project2 = ProjectI()
     project2.name = rstring(self.uuid())
     dataset = DatasetI()
     dataset.name = rstring(self.uuid())
     image = self.new_image(name=self.uuid())
     dataset.linkImage(image)
     project1.linkDataset(dataset)
     project2.linkDataset(dataset)
     tag = TagAnnotationI()
     tag.textValue = rstring("ChgrpTag")
     project1.linkAnnotation(tag)
     project2.linkAnnotation(tag)
     return self.update.saveAndReturnArray([project1, project2])
Esempio n. 16
0
 def projects_dataset_image_tag(self):
     """
     Returns 2 new OMERO Projects, linked Dataset and linked Image populated
     by an L{test.integration.library.ITest} instance with required fields
     set. Also a Tag linked to both Projects.
     """
     project1 = ProjectI()
     project1.name = rstring(f'P1_{self.uuid()}')
     project2 = ProjectI()
     project2.name = rstring(f'P2_{self.uuid()}')
     dataset = DatasetI()
     dataset.name = rstring(f'D{self.uuid()}')
     image = self.new_image(f'I{self.uuid()}')
     dataset.linkImage(image)
     project1.linkDataset(dataset)
     project2.linkDataset(dataset)
     tag = TagAnnotationI()
     tag.textValue = rstring("ChgrpTag")
     project1.linkAnnotation(tag)
     project2.linkAnnotation(tag)
     return self.update.saveAndReturnArray([project1, project2])
Esempio n. 17
0
 def tagset_tag(self):
     """
     Returns a new OMERO TagAnnotation, with the OMERO.insight tagset
     namespace set, and linked TagAnnotation with required fields set.
     """
     tagset = TagAnnotationI()
     tagset.ns = rstring(omero.constants.metadata.NSINSIGHTTAGSET)
     tagset.textValue = rstring(self.uuid())
     tag = TagAnnotationI()
     tag.textValue = rstring(self.uuid())
     tagset.linkAnnotation(tag)
     return self.update.saveAndReturnObject(tagset)
Esempio n. 18
0
def link_tags(conn, datasetname, image_tag_links, image_ratings):

    for i in range(1, 51):
        username = "******" % i
        print(username)
        exp = conn.getAdminService().lookupExperimenter(username)
        exp_id = exp.id.val

        dataset = conn.getObject("Dataset",
                                 attributes={'name': datasetname},
                                 opts={'owner': exp_id})
        if dataset is None:
            print("Dataset not found")
            continue
        links = []
        for image in dataset.listChildren():
            name = image.name
            if name in image_tag_links:
                for tag_id in image_tag_links[name]:
                    link = ImageAnnotationLinkI()
                    link.parent = ImageI(image.id, False)
                    link.child = TagAnnotationI(tag_id, False)
                    link.details.owner = ExperimenterI(exp_id, False)
                    links.append(link)
            if name in image_ratings:
                link = ImageAnnotationLinkI()
                link.parent = ImageI(image.id, False)
                r = LongAnnotationI()
                r.ns = rstring(RATING_NS)
                r.longValue = rlong(image_ratings[name])
                link.child = r
                link.details.owner = ExperimenterI(exp_id, False)
                links.append(link)

        print('links', len(links))
        group_id = dataset.getDetails().getGroup().id
        conn.SERVICE_OPTS.setOmeroGroup(group_id)
        try:
            conn.getUpdateService().saveArray(links, conn.SERVICE_OPTS)
        except ValidationException:
            print("Failed to link for %s" % username)
Esempio n. 19
0
def add_annotations(o):
    '''
    Annotation
        BasicAnnotation
            BooleanAnnotation
                BooleanAnnotationI
            NumericAnnotation
                DoubleAnnotation
                    DoubleAnnotationI
                LongAnnotation
                    LongAnnotationI
            TermAnnotation
                TermAnnotationI
            TimestampAnnotation
                TimestampAnnotationI
        ListAnnotation
            ListAnnotationI
        MapAnnotation
            MapAnnotationI
        TextAnnotation
            CommentAnnotation
                CommentAnnotationI
            TagAnnotation
                TagAnnotationI
            XmlAnnotation
                XmlAnnotationI
        TypeAnnotation
            FileAnnotation
                FileAnnotationI
    '''
    annotation = BooleanAnnotationI()
    annotation.description = rstring('the_description')
    annotation.ns = rstring('boolean_annotation')
    annotation.boolValue = rbool(True)
    o.linkAnnotation(annotation)
    annotation = CommentAnnotationI()
    annotation.description = rstring('the_description')
    annotation.ns = rstring('comment_annotation')
    annotation.textValue = rstring('text_value')
    o.linkAnnotation(annotation)
    annotation = DoubleAnnotationI()
    annotation.description = rstring('the_description')
    annotation.ns = rstring('double_annotation')
    annotation.doubleValue = rdouble(1.0)
    o.linkAnnotation(annotation)
    annotation = LongAnnotationI()
    annotation.description = rstring('the_description')
    annotation.ns = rstring('long_annotation')
    annotation.longValue = rlong(1L)
    o.linkAnnotation(annotation)
    annotation = MapAnnotationI()
    annotation.description = rstring('the_description')
    annotation.ns = rstring('map_annotation')
    annotation.setMapValue([NamedValue('a', '1'), NamedValue('b', '2')])
    o.linkAnnotation(annotation)
    annotation = TagAnnotationI()
    annotation.description = rstring('the_description')
    annotation.ns = rstring('tag_annotation')
    annotation.textValue = rstring('tag_value')
    o.linkAnnotation(annotation)
    annotation = TermAnnotationI()
    annotation.description = rstring('the_description')
    annotation.ns = rstring('term_annotation')
    annotation.termValue = rstring('term_value')
    o.linkAnnotation(annotation)
    annotation = TimestampAnnotationI()
    annotation.description = rstring('the_description')
    annotation.ns = rstring('timestamp_annotation')
    annotation.timeValue = rtime(1)
    o.linkAnnotation(annotation)
    annotation = XmlAnnotationI()
    annotation.description = rstring('the_description')
    annotation.ns = rstring('xml_annotation')
    annotation.textValue = rstring('<xml_value></xml_value>')
    o.linkAnnotation(annotation)
Esempio n. 20
0
for exp in allUsers:
    username = exp.getOmeName()
    print username
    userConn = BlitzGateway(username, "ome")
    userConn.connect()
    for g in userConn.getGroupsMemberOf():
        if g.getName() == "user":
            continue
        print " ", g.getName()
        userConn.SERVICE_OPTS.setOmeroGroup(g.getId())
        params = omero.sys.Parameters()
        params.theFilter = omero.sys.Filter()
        params.theFilter.ownerId = rlong(exp.getId())
        tags = list(userConn.getObjects("TagAnnotation", params=params))
        for i in range(TAG_COUNT - len(tags)):
            t = TagAnnotationI()
            newTagVal = "%s_%s_TEST" % (username, g.getName())
            print "creating TAG:", newTagVal
            t.textValue = wrap(str(newTagVal))
            userConn.getUpdateService().saveObject(t, userConn.SERVICE_OPTS)
        # for t in tags:
        #     print "    TAG", t.getId(), t.getTextValue()
    userConn.c.closeSession()

print "\n---- DOING ANNOTATING... ----\n"
# We want to Tag loads of stuff with OUR tags and Others' tags
for exp in allUsers:
    username = exp.getOmeName()
    if username not in USER_NAMES:
        continue
    print "\n\n------------ USER:"******"------------"
Esempio n. 21
0
    def testQueryTaggedUnique(self):

        # get group we're working on...
        ctx = self.client.sf.getAdminService().getEventContext()
        groupId = ctx.groupId
        print 'groupId', groupId

        # Admin sets permissions to read-ann
        admin = self.root.sf.getAdminService()
        rootUpdate = self.root.sf.getUpdateService()
        gr = admin.getGroup(groupId)
        p = PermissionsI()
        p.setUserRead(True)
        p.setUserWrite(True)
        p.setGroupRead(True)
        p.setGroupAnnotate(True)
        p.setGroupWrite(False)
        p.setWorldRead(False)
        p.setWorldAnnotate(False)
        p.setWorldWrite(False)
        gr.details.permissions = p
        admin.updateGroup(gr)

        # Update context for user
        ctx = self.client.sf.getAdminService().getEventContext()
        update = self.client.sf.getUpdateService()
        queryService = self.client.sf.getQueryService()
        tagCount = 5
        # User creates tag linked to images
        tag = TagAnnotationI()
        tag.textValue = wrap("test_iQuerySpeed")
        links = []

        for i in range(tagCount):
            iid = createImageWithPixels(self.client, self.uuid())
            link = ImageAnnotationLinkI()
            link.parent = ImageI(iid, False)
            link.child = tag
            links.append(link)
        links = update.saveAndReturnArray(links)
        tag = links[0].child
        # check permissions
        p = tag.getDetails().getPermissions()
        assert p.isGroupRead()
        assert p.isGroupAnnotate()

        # Root also links user's tag to images
        rootLinks = []
        for l in links:
            link = ImageAnnotationLinkI()
            link.parent = ImageI(l.parent.id, False)
            link.child = TagAnnotationI(l.child.id, False)
            rootLinks.append(link)
        rootUpdate.saveAndReturnArray(rootLinks, {'omero.group': str(groupId)})

        q = """select distinct new map(obj.id as id,
               obj.name as name,
               obj.details.owner.id as ownerId,
               obj as image_details_permissions,
               obj.fileset.id as filesetId,
               lower(obj.name) as n
             ,
             pix.sizeX as sizeX,
             pix.sizeY as sizeY,
             pix.sizeZ as sizeZ
             )
            from Image obj  left outer join obj.pixels pix
            join obj.annotationLinks alink
            where %s
            order by lower(obj.name), obj.id """

        params = ParametersI()
        params.add('tid', tag.id)

        # We can get all the tagged images like this.
        # We use an additional select statement to give 'unique' results
        uniqueClause = """alink.id = (select max(alink.id)
                from ImageAnnotationLink alink
                where alink.child.id=:tid and alink.parent.id=obj.id)"""
        query = q % uniqueClause
        result1 = queryService.projection(query, params,
                                          {'omero.group': str(groupId)})
        assert len(result1) == tagCount

        # Without the select statement, we get the same image returned
        # multiple times if there is no 'distinct'
        clause = "alink.child.id=:tid"
        query = q % clause
        result2 = queryService.projection(query, params,
                                          {'omero.group': str(groupId)})
        assert len(result2) == tagCount
        for idx in range(len(result1)-1):
            # Omit final since == isn't defined for Ice objects.
            assert result1[idx] == result2[idx]
Esempio n. 22
0
for exp in allUsers:
    username = exp.getOmeName()
    print username
    userConn = BlitzGateway(username, "ome")
    userConn.connect()
    for g in userConn.getGroupsMemberOf():
        if g.getName() == "user":
            continue
        print " ", g.getName()
        userConn.SERVICE_OPTS.setOmeroGroup(g.getId())
        params = omero.sys.Parameters()
        params.theFilter = omero.sys.Filter()
        params.theFilter.ownerId = rlong(exp.getId())
        tags = list( userConn.getObjects("TagAnnotation", params=params) )
        for i in range( TAG_COUNT-len(tags) ):
            t = TagAnnotationI()
            newTagVal = "%s_%s_TEST" % (username, g.getName())
            print "creating TAG:", newTagVal
            t.textValue = wrap(str(newTagVal))
            userConn.getUpdateService().saveObject(t, userConn.SERVICE_OPTS)
        # for t in tags:
        #     print "    TAG", t.getId(), t.getTextValue()
    userConn.c.closeSession()



print "\n---- DOING ANNOTATING... ----\n"
# We want to Tag loads of stuff with OUR tags and Others' tags
for exp in allUsers:
    username = exp.getOmeName()
    if username not in USER_NAMES:
Esempio n. 23
0
    def testQueryTaggedUnique(self):

        # get group we're working on...
        ctx = self.client.sf.getAdminService().getEventContext()
        groupId = ctx.groupId
        print 'groupId', groupId

        # Admin sets permissions to read-ann
        admin = self.root.sf.getAdminService()
        rootUpdate = self.root.sf.getUpdateService()
        gr = admin.getGroup(groupId)
        p = PermissionsI()
        p.setUserRead(True)
        p.setUserWrite(True)
        p.setGroupRead(True)
        p.setGroupAnnotate(True)
        p.setGroupWrite(False)
        p.setWorldRead(False)
        p.setWorldAnnotate(False)
        p.setWorldWrite(False)
        gr.details.permissions = p
        admin.updateGroup(gr)

        # Update context for user
        ctx = self.client.sf.getAdminService().getEventContext()
        update = self.client.sf.getUpdateService()
        queryService = self.client.sf.getQueryService()
        tagCount = 5
        # User creates tag linked to images
        tag = TagAnnotationI()
        tag.textValue = wrap("test_iQuerySpeed")
        links = []

        for i in range(tagCount):
            iid = createImageWithPixels(self.client, self.uuid())
            link = ImageAnnotationLinkI()
            link.parent = ImageI(iid, False)
            link.child = tag
            links.append(link)
        links = update.saveAndReturnArray(links)
        tag = links[0].child
        # check permissions
        p = tag.getDetails().getPermissions()
        assert p.isGroupRead()
        assert p.isGroupAnnotate()

        # Root also links user's tag to images
        rootLinks = []
        for l in links:
            link = ImageAnnotationLinkI()
            link.parent = ImageI(l.parent.id, False)
            link.child = TagAnnotationI(l.child.id, False)
            rootLinks.append(link)
        rootUpdate.saveAndReturnArray(rootLinks, {'omero.group': str(groupId)})

        q = """select distinct new map(obj.id as id,
               obj.name as name,
               obj.details.owner.id as ownerId,
               obj as image_details_permissions,
               obj.fileset.id as filesetId,
               lower(obj.name) as n
             ,
             pix.sizeX as sizeX,
             pix.sizeY as sizeY,
             pix.sizeZ as sizeZ
             )
            from Image obj  left outer join obj.pixels pix
            join obj.annotationLinks alink
            where %s
            order by lower(obj.name), obj.id """

        params = ParametersI()
        params.add('tid', tag.id)

        # We can get all the tagged images like this.
        # We use an additional select statement to give 'unique' results
        uniqueClause = """alink.id = (select max(alink.id)
                from ImageAnnotationLink alink
                where alink.child.id=:tid and alink.parent.id=obj.id)"""
        query = q % uniqueClause
        result1 = queryService.projection(query, params,
                                          {'omero.group': str(groupId)})
        assert len(result1) == tagCount

        # Without the select statement, we get the same image returned
        # multiple times if there is no 'distinct'
        clause = "alink.child.id=:tid"
        query = q % clause
        result2 = queryService.projection(query, params,
                                          {'omero.group': str(groupId)})
        assert len(result2) == tagCount
        for idx in range(len(result1)-1):
            # Omit final since == isn't defined for Ice objects.
            assert result1[idx] == result2[idx]