Skip to content

Commit

Permalink
adds get_comment_annotation_ids, get_comment_annotation, `post_co…
Browse files Browse the repository at this point in the history
…mment_annotation` (#79)

* added `get_comment_ids`

* added `get_comment`

* added `post_comment_annotation`

* added `test_get_comment_annotation_and_ids`

* added `test_post_get_comment_annotation`, all passing
  • Loading branch information
erickmartins authored Jul 5, 2023
1 parent b0eeca8 commit 3dd46b1
Show file tree
Hide file tree
Showing 5 changed files with 277 additions and 4 deletions.
6 changes: 6 additions & 0 deletions ezomero/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
post_image,
post_map_annotation,
post_file_annotation,
post_comment_annotation,
post_project,
post_screen,
post_roi,
Expand All @@ -35,6 +36,8 @@
get_file_annotation,
get_tag_ids,
get_tag,
get_comment_annotation_ids,
get_comment_annotation,
get_group_id,
get_user_id,
get_original_filepaths,
Expand All @@ -45,6 +48,7 @@
__all__ = ['post_dataset',
'post_image',
'post_map_annotation',
'post_comment_annotation',
'post_file_annotation',
'post_project',
'post_screen',
Expand All @@ -63,6 +67,8 @@
'get_file_annotation',
'get_tag_ids',
'get_tag',
'get_comment_annotation_ids',
'get_comment_annotation',
'get_group_id',
'get_user_id',
'get_original_filepaths',
Expand Down
89 changes: 86 additions & 3 deletions ezomero/_gets.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from omero.gateway import FileAnnotationWrapper, BlitzGateway, ImageWrapper
from omero import ApiUsageException, InternalException
from omero.model import MapAnnotationI, TagAnnotationI, Shape
from omero.model import CommentAnnotationI
from omero.grid import Table
from omero.rtypes import rint, rlong
from omero.sys import Parameters
Expand Down Expand Up @@ -563,7 +564,7 @@ def get_map_annotation_ids(conn: BlitzGateway, object_type: str,
OMERO object type, passed to ``BlitzGateway.getObject``
object_id : int
ID of object of ``object_type``.
ns : str
ns : str, optional
Namespace with which to filter results
across_groups : bool, optional
Defines cross-group behavior of function - set to
Expand Down Expand Up @@ -612,7 +613,7 @@ def get_tag_ids(conn: BlitzGateway, object_type: str, object_id: int,
OMERO object type, passed to ``BlitzGateway.getObject``
object_id : int
ID of object of ``object_type``.
ns : str
ns : str, optional
Namespace with which to filter results
across_groups : bool, optional
Defines cross-group behavior of function - set to
Expand Down Expand Up @@ -647,6 +648,56 @@ def get_tag_ids(conn: BlitzGateway, object_type: str, object_id: int,
return tag_ids


@do_across_groups
def get_comment_annotation_ids(conn: BlitzGateway, object_type: str,
object_id: int, ns: Optional[str] = None,
across_groups: Optional[bool] = True
) -> List[int]:
"""Get IDs of comment annotations associated with an object
Parameters
----------
conn : ``omero.gateway.BlitzGateway`` object
OMERO connection.
object_type : str
OMERO object type, passed to ``BlitzGateway.getObject``
object_id : int
ID of object of ``object_type``.
ns : str, optional
Namespace with which to filter results
across_groups : bool, optional
Defines cross-group behavior of function - set to
``False`` to disable it.
Returns
-------
comment_ids : list of ints
Examples
--------
# Return IDs of all comments linked to an image:
>>> comment_ids = get_comment_ids(conn, 'Image', 42)
# Return IDs of comments with namespace "test" linked to a Dataset:
>>> tag_ids = get_tag_ids(conn, 'Dataset', 16, ns='test')
"""
if type(object_type) is not str:
raise TypeError('Object type must be a string')
if type(object_id) is not int:
raise TypeError('Object id must be an integer')
if ns is not None and type(ns) is not str:
raise TypeError('Namespace must be a string or None')

target_object = conn.getObject(object_type, object_id)
comment_ids = []
for ann in target_object.listAnnotations(ns):
if ann.OMERO_TYPE is CommentAnnotationI:
comment_ids.append(ann.getId())
return comment_ids


@do_across_groups
def get_file_annotation_ids(conn: BlitzGateway, object_type: str,
object_id: int, ns: Optional[str] = None,
Expand All @@ -661,7 +712,7 @@ def get_file_annotation_ids(conn: BlitzGateway, object_type: str,
OMERO object type, passed to ``BlitzGateway.getObject``
object_id : int
ID of object of ``object_type``.
ns : str
ns : str, optional
Namespace with which to filter results
across_groups : bool, optional
Defines cross-group behavior of function - set to
Expand Down Expand Up @@ -885,6 +936,38 @@ def get_tag(conn: BlitzGateway, tag_id: int,
return conn.getObject('TagAnnotation', tag_id).getValue()


@do_across_groups
def get_comment_annotation(conn: BlitzGateway, comment_id: int,
across_groups: Optional[bool] = True) -> str:
"""Get the value of a comment annotation object
Parameters
----------
conn : ``omero.gateway.BlitzGateway`` object
OMERO connection.
comment_id : int
ID of comment annotation to get.
across_groups : bool, optional
Defines cross-group behavior of function - set to
``False`` to disable it.
Returns
-------
comment : str
The value of the specified tag annotation object.
Examples
--------
>>> comment = get_tag(conn, 62)
>>> print(comment)
This is a comment
"""
if type(comment_id) is not int:
raise TypeError('Comment ID must be an integer')

return conn.getObject('CommentAnnotation', comment_id).getValue()


@do_across_groups
def get_file_annotation(conn: BlitzGateway, file_ann_id: int,
folder_path: Optional[str] = None,
Expand Down
79 changes: 78 additions & 1 deletion ezomero/_posts.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from omero.gateway import ProjectWrapper, DatasetWrapper
from omero.gateway import ScreenWrapper, FileAnnotationWrapper
from omero.gateway import MapAnnotationWrapper, OriginalFileWrapper
from omero.gateway import CommentAnnotationWrapper
from omero.rtypes import rstring, rint, rdouble
from .rois import Point, Line, Rectangle, Ellipse
from .rois import Polygon, Polyline, Label
Expand Down Expand Up @@ -221,7 +222,7 @@ def post_map_annotation(conn: BlitzGateway, object_type: str, object_id: int,
kv_dict: dict, ns: str,
across_groups: Optional[bool] = True
) -> Union[int, None]:
"""Create new MapAnnotation and link to images.
"""Create new MapAnnotation and link to an object.
Parameters
----------
Expand Down Expand Up @@ -298,6 +299,82 @@ def post_map_annotation(conn: BlitzGateway, object_type: str, object_id: int,
return map_ann.getId()


@do_across_groups
def post_comment_annotation(conn: BlitzGateway, object_type: str,
object_id: int,
comment: str, ns: Optional[str] = None,
across_groups: Optional[bool] = True
) -> Union[int, None]:
"""Create new MapAnnotation and link to an object.
Parameters
----------
conn : ``omero.gateway.BlitzGateway`` object
OMERO connection.
object_type : str
OMERO object type, passed to ``BlitzGateway.getObjects``
object_id : int
ID of object to which the new CommentAnnotation will be linked.
comment : str
string that will be passed as the CommentAnnotation value
ns : str, optional
Namespace for the CommentAnnotation
across_groups : bool, optional
Defines cross-group behavior of function - set to
``False`` to disable it.
Returns
-------
comment_ann_id : int
IDs of newly created CommentAnnotation
Examples
--------
>>> ns = 'jax.org/jax/example/namespace'
>>> d = {'species': 'human',
... 'occupation': 'time traveler'
... 'first name': 'Kyle',
... 'surname': 'Reese'}
>>> post_map_annotation(conn, "Image", 56, d, ns)
234
"""
if type(comment) is not str:
raise TypeError('Comment must be a string')
if type(object_type) is not str:
raise TypeError('Object type must be a string')

obj = None
if object_id is not None:
if type(object_id) is not int:
raise TypeError('object_ids must be integer')
obj = conn.getObject(object_type, object_id)
if obj is not None:
ret = set_group(conn, obj.getDetails().group.id.val)
if ret is False:
logging.warning('Cannot change into group '
f'where object {object_id} is.')
return None
else:
logging.warning(f'Object {object_id} could not be found '
'(check if you have permissions to it)')
return None
else:
raise TypeError('Object ID cannot be empty')
comment_ann = CommentAnnotationWrapper(conn)
if ns:
comment_ann.setNs(str(ns))
comment_ann.setValue(comment)
comment_ann.save()
try:
obj.linkAnnotation(comment_ann)
except ValueError: # fix this bare exception
logging.warning(f'Cannot link to object {object_id} - '
'check if you have permissions to do so')
return None

return comment_ann.getId()


@do_across_groups
def post_file_annotation(conn: BlitzGateway, object_type: str, object_id: int,
file_path: str, ns: str,
Expand Down
44 changes: 44 additions & 0 deletions tests/test_gets.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,50 @@ def test_get_map_annotation_and_ids(conn, project_structure):
wait=True)


def test_get_comment_annotation_and_ids(conn, project_structure):
comment = "comment annotation test"
ns = "jax.org/omeroutils/tests/v0"
image_info = project_structure[2]
im_id = image_info[0][1]

# Test sanitizing inputs
with pytest.raises(TypeError):
_ = ezomero.get_comment_annotation_ids(conn, 10, 10)
with pytest.raises(TypeError):
_ = ezomero.get_comment_annotation_ids(conn, 'Image', '10')
with pytest.raises(TypeError):
_ = ezomero.get_comment_annotation_ids(conn, 'Image', 10, ns=10)
with pytest.raises(TypeError):
_ = ezomero.get_comment_annotation(conn, 'Image')

comm_ann_id = ezomero.post_comment_annotation(conn, "Image", im_id,
comment, ns)
comm_ann_id2 = ezomero.post_comment_annotation(conn, "Image", im_id,
comment, ns)
comm_ann_id3 = ezomero.post_comment_annotation(conn, "Image", im_id,
comment, ns)
ns2 = "different namespace"
comm_ann_id4 = ezomero.post_comment_annotation(conn, "Image", im_id,
comment, ns2)
comm_ann_ids = ezomero.get_comment_annotation_ids(conn, "Image", im_id,
ns=ns)

good_ids = [comm_ann_id, comm_ann_id2, comm_ann_id3]
assert all([mid in comm_ann_ids for mid in good_ids])
assert comm_ann_id4 not in comm_ann_ids

# Test sanitizing input
with pytest.raises(TypeError):
_ = ezomero.get_comment_annotation(conn, '10')
mpann = ezomero.get_comment_annotation(conn, comm_ann_ids[0])
assert mpann == comment
conn.deleteObjects("Annotation",
[comm_ann_id, comm_ann_id2, comm_ann_id3, comm_ann_id4],
deleteAnns=True,
deleteChildren=True,
wait=True)


def test_get_file_annotation_and_ids(conn, project_structure, tmp_path):
image_info = project_structure[2]
im_id = image_info[0][1]
Expand Down
63 changes: 63 additions & 0 deletions tests/test_posts.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,69 @@ def test_post_get_map_annotation(conn, project_structure, users_groups):
deleteAnns=True, deleteChildren=True, wait=True)


def test_post_get_comment_annotation(conn, project_structure, users_groups):
image_info = project_structure[2]
im_id = image_info[0][1]
# This test both ezomero.post_comment_annotation
# and ezomero.get_comment_annotation
comment = "sample comment for testing"
ns = "jax.org/omeroutils/tests/v0"

# test sanitized input on post
with pytest.raises(TypeError):
_ = ezomero.post_comment_annotation(conn, "Image", im_id, 1, ns)
with pytest.raises(TypeError):
_ = ezomero.post_comment_annotation(conn, 1, '10', comment, ns)
with pytest.raises(TypeError):
_ = ezomero.post_comment_annotation(conn, "Image", None, comment, ns)

comm_ann_id = ezomero.post_comment_annotation(conn, "Image", im_id,
comment, ns)
comm = ezomero.get_comment_annotation(conn, comm_ann_id)
assert comm == comment

# Test posting to non-existing object
im_id2 = 999999999
comm_ann_id2 = ezomero.post_comment_annotation(conn, "Image", im_id2,
comment, ns)
assert comm_ann_id2 is None

# Test posting cross-group
username = users_groups[1][0][0] # test_user1
groupname = users_groups[0][0][0] # test_group_1
current_conn = conn.suConn(username, groupname)
im_id3 = image_info[2][1] # im2, in test_group_2
comm_ann_id3 = ezomero.post_comment_annotation(current_conn, "Image",
im_id3, comment, ns)
comm = ezomero.get_comment_annotation(current_conn, comm_ann_id3)
assert comm == comment
current_conn.close()

# Test posting to an invalid cross-group
username = users_groups[1][2][0] # test_user3
groupname = users_groups[0][1][0] # test_group_2
current_conn = conn.suConn(username, groupname)
im_id4 = image_info[1][1] # im1(in test_group_1)
comm_ann_id4 = ezomero.post_comment_annotation(current_conn, "Image",
im_id4, comment, ns)
assert comm_ann_id4 is None
current_conn.close()

# Test posting cross-group, across_groups unset
username = users_groups[1][0][0] # test_user1
groupname = users_groups[0][0][0] # test_group_1
current_conn = conn.suConn(username, groupname)
im_id5 = image_info[2][1] # im2, in test_group_2
comm_ann_id5 = ezomero.post_comment_annotation(current_conn, "Image",
im_id5, comment, ns,
across_groups=False)
assert comm_ann_id5 is None
current_conn.close()

conn.deleteObjects("Annotation", [comm_ann_id, comm_ann_id3],
deleteAnns=True, deleteChildren=True, wait=True)


def test_post_get_file_annotation(conn, project_structure, users_groups,
tmp_path):

Expand Down

0 comments on commit 3dd46b1

Please sign in to comment.