Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 93 additions & 53 deletions src/imcflibs/imagej/omerotools.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
[omero-insight]: https://github.com/ome/omero-insight
"""

import os

from fr.igred.omero import Client
from fr.igred.omero.annotations import (
MapAnnotationWrapper,
Expand All @@ -27,14 +29,77 @@
from omero.gateway.model import TableData, TableDataColumn


def parse_url_string(url_str):
"""Extract IDs from an OMERO URL.

Info: URLs can only be of the same type (Dataset, Project, Images).
Returns tuple[empty list, ""] if input str (URL) is not an OMERO URL.

Parameters
----------
url_str : str
OMERO URL, e.g. from the webclient.

Returns
-------
tuple[list[str], str]
- List of IDs extracted from the OMERO URL.
- Type of IDs (i.e. "project", "dataset", or "image")
"""
omero_types = ["dataset-", "project-", "image-"]

for omero_type in omero_types:
omero_ids = extract_ids_from_string(url_str, omero_type)
if len(omero_ids) > 0:
return omero_ids, omero_type.strip("-")
# If no IDs identified
return [], ""


def extract_ids_from_string(omero_str, omero_type):
"""Extract IDs from an OMERO URL for a given OMERO type.

Parameters
----------
omero_str : str
OMERO URL, e.g. from the webclient.
omero_type : str
OMERO type, i.e. "project", "Dataset", or "Image" # FIXME

Returns
-------
list[str]
List of IDs extracted from the OMERO URL.
"""
omero_type = omero_type.lower().strip("-")
if omero_type not in ["project", "dataset", "image"]:
raise ValueError("Unknown OMERO type: {}".format(omero_type))

omero_ids = []
omero_type = omero_type + "-"
if omero_type in omero_str:
# If there are multiple types
if "|" in omero_str:
parts = omero_str.split("|")
for part in parts:
if omero_type in part:
omero_id = part.split(omero_type)[1].split("/")[0]
omero_ids.append(omero_id)
else:
# If there is only one dataset
omero_id = omero_str.split(omero_type)[1].split("/")[0]
omero_ids.append(omero_id)
return omero_ids


def parse_url(client, omero_str):
"""Parse an OMERO URL / image IDs into the respective ImageWrapper objects.

Assemble a list of ImageWrapper objects from one of the following inputs:

- An OMERO URL (as retrieved e.g. from OMERO.web).
- An OMERO URL for Dataset(s) or Image(s)
(as retrieved e.g. from OMERO.web).
- One or more OMERO image IDs.
- An OMERO Dataset ID.

Parameters
----------
Expand Down Expand Up @@ -63,62 +128,31 @@ def parse_url(client, omero_str):
# Sanitize the string
omero_str = omero_str.strip()

image_ids = []
dataset_ids = []
image_wpr_list = []
# Get a list of str IDs and their OMERO type
omero_ids, omero_type = parse_url_string(omero_str)

# Check if the url is a dataset
if "dataset-" in omero_str:
# If there are multiple datasets
if "|" in omero_str:
parts = omero_str.split("|")
for part in parts:
if "dataset-" in part:
image_ids.extend(
[
image
for image in client.getDataset(
Long(part.split("dataset-")[1].split("/")[0])
).getImages()
]
)
dataset_id = Long(part.split("dataset-")[1].split("/")[0])
dataset_ids.append(dataset_id)
else:
image_ids.extend(
[
image
for image in client.getDataset(
Long(omero_str.split("dataset-")[1].split("/")[0])
).getImages()
]
)
# If there is only one dataset
dataset_id = Long(omero_str.split("dataset-")[1].split("/")[0])
dataset_ids.append(dataset_id)
if omero_type == "project":
raise NotImplementedError(
"Getting Image IDs from Project(s) is not implemented."
)

# Get the images from the dataset
for dataset_id in dataset_ids:
dataset_wpr = client.getDataset(dataset_id)
elif omero_type == "dataset":
image_wpr_list = []
# Get the images from the dataset(s)
for omero_id in omero_ids:
dataset_wpr = client.getDataset(Long(omero_id))
image_wpr_list.extend(dataset_wpr.getImages())

return image_wpr_list

# Check if the url is an image
elif "image-" in omero_str:
image_ids = omero_str.split("image-")
image_ids.pop(0)
image_ids = [s.split("%")[0].replace("|", "") for s in image_ids]
elif omero_type == "image":
# omero_ids will be processed at the end
pass

else:
image_ids = (
[s.split("%")[0].replace("|", "") for s in omero_str.split("image-")[1:]]
if "image-" in omero_str
else omero_str.split(",")
)
Comment thread
ehrenfeu marked this conversation as resolved.
# If it is a list of IDs separated by commas
image_ids = omero_str.split(",")
# (omero_type == "") supposedly a list of IDs separated by commas
omero_ids = omero_str.split(",")

return [client.getImage(Long(image_id)) for image_id in image_ids]
return [client.getImage(Long(image_id)) for image_id in omero_ids]


def connect(host, port, username, password):
Expand Down Expand Up @@ -190,6 +224,12 @@ def upload_image_to_omero(user_client, path, dataset_id):
Long
ID of the uploaded image
"""
if not os.path.exists(path):
try:
raise FileNotFoundError("Could not find file: {}".format(path))
except NameError:
raise IOError("Could not find file: {}".format(path))

return user_client.getDataset(Long(dataset_id)).importImage(user_client, path)[0]


Expand All @@ -201,7 +241,7 @@ def add_keyvalue_annotation(client, repository_wpr, annotations, header):
client : fr.igred.omero.Client
The client object used to connect to the OMERO server.
repository_wpr : fr.igred.omero.repositor.GenericRepositoryObjectWrapper
Wrapper to the object for the anotation.
Wrapper to the object for the annotation.
annotations : dict
Dictionary with the annotation to add.
header : str
Expand All @@ -222,7 +262,7 @@ def delete_keyvalue_annotations(user_client, object_wrapper):
user_client : fr.igred.omero.Client
Client used for login to OMERO.
object_wrapper : fr.igred.omero.repositor.GenericRepositoryObjectWrapper
Wrapper to the object for the anotation.
Wrapper to the object for the annotation.

"""
kv_pairs = object_wrapper.getMapAnnotations(user_client)
Expand Down
58 changes: 58 additions & 0 deletions tests/interactive-imagej/omerotools/parse_url/test_parse_url_01.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# @ String (label="OMERO HOST") host
# @ Integer (label="OMERO PORT", value=4064) port
# @ String (label="OMERO User") user
# @ String (label="OMERO Password", style="password") pwd
# @ String (label="Provide OMERO Project URL") url_project
# @ String (label="Provide OMERO 'Dataset A' URL") url_dataset
# @ String (label="Provide OMERO URL for ALL images in 'Dataset A'") url_images
# Examples
# url_project: https://omero.biozentrum.unibas.ch/webclient/?show=project-25155
# url_dataset: https://omero.biozentrum.unibas.ch/webclient/?show=dataset-82170
# url_images: https://omero.biozentrum.unibas.ch/webclient/?show=image-3454870|image-3454871|image-3454872|image-3454873|image-3454874


from imcflibs.imagej import omerotools

# Parse the url_images to get the IDs
image_ids = []
parts = url_images.split("|")
for part in parts:
if "image-" in part:
image_id = part.split("image-")[1].split("/")[0]
image_ids.append(image_id)
assert len(image_ids) > 0, "No image ids in image url!"

# Test
try:
user_client = omerotools.connect(host, port, user, pwd)

# Project URLs are not implemented
ids = None
try:
ids = omerotools.parse_url(user_client, url_project)
except NotImplementedError:
pass
assert ids is None

# Single Dataset
ids = omerotools.parse_url(user_client, url_dataset)
assert len(ids) == len(image_ids)

# Images url
ids = omerotools.parse_url(user_client, url_images)
assert len(ids) == len(image_ids)
for cur_id in ids:
cur_id = cur_id.getId()
assert str(cur_id) in image_ids

# Image IDs as comma-separated string
ids = omerotools.parse_url(user_client, ",".join(image_ids))
assert len(ids) == len(image_ids)
for cur_id in ids:
cur_id = cur_id.getId()
assert str(cur_id) in image_ids
finally:
if user_client is not user_client:
user_client.disconnect()

print("Test completed!")
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from imcflibs.imagej import omerotools


error_type = "FileNotFoundError"
try:
FileNotFoundError
except NameError:
FileNotFoundError = IOError
error_type = "IOError"

path = "/usr/fancy/path/pony-with-one-horn.tiff"

try:
# No client needed. Only testing the exception
omerotools.upload_image_to_omero(None, path, None)
except FileNotFoundError:
print("Got the expected {}".format(error_type))

print("Test completed!")
110 changes: 110 additions & 0 deletions tests/test_omerotools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
"""Tests for the imcflibs.imagej.omerotools module."""

import pytest

from imcflibs.imagej.omerotools import (
extract_ids_from_string,
parse_url,
parse_url_string,
upload_image_to_omero,
)

# Enusre Python 2.7 and 3.x compability
try:
FileNotFoundError
except NameError:
FileNotFoundError = IOError


def test_upload_image_to_omero():
"""Test `upload_image_to_omero()`.

Tests only the case for non-existing files.
"""
path = "/usr/random/path/questionable/file.tiff"
with pytest.raises(FileNotFoundError, match=r"Could not find file"):
upload_image_to_omero(None, path, None)


def test_extract_ids_from_string():
"""Test `extract_ids_from_string()`."""

url_proj = "webclient/?show=project-25406"
# Expect error with wrong omero_type
with pytest.raises(ValueError, match=r"Unknown OMERO type"):
extract_ids_from_string(url_proj, "unicorn")

assert len(extract_ids_from_string(url_proj, "Project")) == 1
assert len(extract_ids_from_string(url_proj, "Project-")) == 1
assert len(extract_ids_from_string(url_proj, "Dataset")) == 0

url_dataset = "webclient/?show=dataset-82753"
assert len(extract_ids_from_string(url_dataset, "dataset")) == 1

url_image = "webclient/?show=image-3598144"
assert len(extract_ids_from_string(url_image, "image")) == 1

url_multi_image = (
"webclient/?show=image-3598144|image-3598145|image-3598146|image-3598147"
)
assert len(extract_ids_from_string(url_multi_image, "image-")) == 4
assert len(extract_ids_from_string(url_multi_image, "Project")) == 0

url_bad = "https://github.com/imcf/python-imcflibs"
assert len(extract_ids_from_string(url_bad, "image")) == 0


def test_parse_url_string():
"""Test `parse_url_string()`."""
url_proj = "webclient/?show=project-25406"
omero_ids, omero_type = parse_url_string(url_proj)
assert len(omero_ids) == 1
assert omero_type == "project"
assert "25406" in omero_ids

url_dataset = "webclient/?show=dataset-82753"
omero_ids, omero_type = parse_url_string(url_dataset)
assert len(omero_ids) == 1
assert omero_type == "dataset"
assert "82753" in omero_ids

url_image = "webclient/?show=image-3598144"
omero_ids, omero_type = parse_url_string(url_image)
assert len(omero_ids) == 1
assert omero_type == "image"
assert "3598144" in omero_ids

url_multi_image = (
"webclient/?show=image-3598144|image-3598145|image-3598146|image-3598147"
)
omero_ids, omero_type = parse_url_string(url_multi_image)
assert len(omero_ids) == 4
assert omero_type == "image"
assert "3598144" in omero_ids
assert "3598145" in omero_ids
assert "3598146" in omero_ids
assert "3598147" in omero_ids

url_bad = "https://github.com/imcf/python-imcflibs"
omero_ids, omero_type = parse_url_string(url_bad)
assert len(omero_ids) == 0
assert omero_type == ""


def test_parse_url():
"""Test `parse_url()`.

Only tests the first part of the function
(NotImplementedError on Project-URLs).
Since the reset of the function depends on OMERO libraries.
"""
url_empty = ""
with pytest.raises(ValueError, match=r"No OMERO link or image ID prov"):
parse_url(None, url_empty)

url_proj = "webclient/?show=project-25406"
with pytest.raises(
NotImplementedError,
match=r"Getting Image IDs from Project",
):
parse_url(None, url_proj)
Loading