Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add export and impo(rt) commands #62

Merged
merged 6 commits into from
Dec 4, 2024
Merged
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ omero-cli-render-*
.*un~
*.pyc
.omero
*.iml
.idea

185 changes: 180 additions & 5 deletions src/omero_cli_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,23 @@
import time
import json
import yaml
import omero

from functools import wraps

from pathlib import Path

from omero.cli import BaseControl
from omero.cli import CLI
from omero.cli import ProxyStringType
from omero.gateway import BlitzGateway
from omero.gateway import BlitzGateway, DatasetWrapper
from omero.model import Image
from omero.model import Plate
from omero.model import Screen
from omero.model import Dataset
from omero.model import Project
from omero.model import StatsInfoI
from omero.rtypes import rint, rdouble
from omero.rtypes import rint, rdouble, rstring, rlong
from omero.util import pydict_text_io

from omero import UnloadedEntityException
Expand Down Expand Up @@ -145,6 +148,40 @@
omero render test Image:1
"""

EXPORT_HELP = """Export rendering settings as yaml files

Note: Does not for Screen/Plate yet!

Examples:
Export one yaml file for each Dataset in a Project (first image)
(into ./DATASET_NAME/IMAGE_NAME.yml):
omero export Project:1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be: omero render export no?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Argh, yes of course. I'll open follow-up PR.


Export one yaml file for each Image of a Dataset (into
./DATASET_NAME/IMAGE_NAME.yml):
omero export --traverse Dataset:1
"""

IMPO_HELP = """Import rendering settings as yaml files

Note: Does not for Screen/Plate yet!

Yaml files have to be in directories with the name of the dataset,
i.e. ./DATASET_NAME/IMAGE_NAME.yml !

If DATASET_NAME/IMAGE_NAME is not unique, the last imported
dataset/image will be used.

(channels argument has to be a yaml file for this command)

Examples:
Set the rendering settings for one image:
omero import DATASET_NAME/IMAGE_NAME.yml

Set the rendering settings for the whole dataset:
omero import --dataset DATASET_NAME/IMAGE_NAME.yml
"""

# Current version for specifying rendering settings
# in the yaml / json files
SPEC_VERSION = 2
Expand Down Expand Up @@ -370,19 +407,21 @@ def _configure(self, parser):
set_cmd = parser.add(sub, self.set, SET_HELP)
edit = parser.add(sub, self.edit, EDIT_HELP)
test = parser.add(sub, self.test, TEST_HELP)
export = parser.add(sub, self.export, EXPORT_HELP)
impo = parser.add(sub, self.impo, IMPO_HELP)

render_type = ProxyStringType("Image")
src_help = ("Rendering settings source")

for x in (get, info, copy, test):
for x in (get, info, copy, test, export):
x.add_argument("object", type=render_type, help=src_help)

tgt_help = ("Objects to apply the rendering settings to")
for x in (set_cmd, edit):
x.add_argument("object", type=render_type, help=tgt_help,
nargs="+")

for x in (copy, set_cmd, edit):
for x in (copy, set_cmd, edit, impo):
x.add_argument(
"--skipthumbs", help="Do not regenerate thumbnails "
"immediately", action="store_true")
Expand All @@ -399,7 +438,7 @@ def _configure(self, parser):
copy.add_argument("target", type=render_type, help=tgt_help,
nargs="+")

for x in (set_cmd, edit):
for x in (set_cmd, edit, impo):
x.add_argument(
"--disable", help="Disable non specified channels ",
action="store_true")
Expand All @@ -421,6 +460,17 @@ def _configure(self, parser):
help="If underlying pixel data available test thumbnail retrieval"
)

export.add_argument("--traverse", action="store_true",
help="Export settings for all images of a "
"dataset (default: just first one)")

impo.add_argument("--dataset", action="store_true",
help="Rendering settings should be applied to "
"the whole dataset")
impo.add_argument("--spw", action="store_true",
help="If target is Screen/Plate/Well "
"(NOT SUPPORTED YET)")

def _lookup(self, gateway, type, oid):
# TODO: move _lookup to a _configure type
gateway.SERVICE_OPTS.setOmeroGroup('-1')
Expand Down Expand Up @@ -530,6 +580,131 @@ def __info(self, args):
finally:
img._closeRE()

def _get_datasets_for_image(self, gateway, img_id):
"""
Get all datasets an image is part of.
:param gateway: Ref to gateway
:param img: The image
:return: The datasets
"""
query = "select i from Image i join fetch i.datasetLinks idl " \
"join fetch idl.parent d where i.id = :id"
params = omero.sys.ParametersI()
params.map['id'] = rlong(img_id)
img = gateway.getQueryService().findByQuery(query, params)
datasets = []
for link in img.iterateDatasetLinks():
datasets.append(link.parent)
return datasets

@gateway_required
def impo(self, args):
"""Implements the impo(rt) command"""
yml_path = Path(args.channels)
ds_name = yml_path.parent.name
img_name = yml_path.name.replace(".yml", "")

set_args = args
if args.dataset:
ds_query = "select d from Dataset d where d.name = :name"
params = omero.sys.ParametersI()
params.map['name'] = rstring(ds_name)
datasets = self.gateway.getQueryService().findAllByQuery(ds_query,
params)
if not datasets:
self.ctx.err(f"No dataset with name {ds_name} found.")
return
elif len(datasets) > 1:
self.ctx.dbg("More than one dataset found, using latest one")
target_ds = max(datasets, key=lambda ds: ds.getId().getValue())
will-moore marked this conversation as resolved.
Show resolved Hide resolved
else:
target_ds = datasets[0]
set_args.object = target_ds
self.set(set_args)
else:
img_query = "select c from DatasetImageLink l " \
"join l.child as c " \
"join l.parent as p " \
"where p.name = :name1 and c.name = :name2"
params = omero.sys.ParametersI()
params.map['name1'] = rstring(ds_name)
params.map['name2'] = rstring(img_name)
images = self.gateway.getQueryService().findAllByQuery(img_query,
params)
if not images:
self.ctx.err(f"No image with name {img_name} found "
f"within a dataset {ds_name}.")
return
elif len(images) > 1:
self.ctx.dbg("More than one images found, "
"using latest one.")
target_img = max(images,
key=lambda img: img.getId().getValue())
else:
target_img = images[0]
set_args.object = target_img
self.set(set_args)

def __do_export(self, ds, img):
"""
Exports the rendering settings of an image to a file
called <IMAGE_NAME><SEPARATOR><DATASET_NAME>.yml
:param ds: The dataset
:param img: The image
:param sep: The separator
:return: None
"""
try:
ro = RenderObject(img)
target_dir = Path(ds.getName())
target_dir.mkdir(exist_ok=True)
target = target_dir / Path(f"{img.getName()}.yml")
with target.open(mode="w", encoding="utf-8") as out:
out.write(yaml.dump(ro.to_dict(),
explicit_start=True,
width=80, indent=4,
default_flow_style=False).rstrip())
except Exception as e:
self.ctx.err('ERROR: %s' % e)
finally:
img._closeRE()

@gateway_required
def export(self, args):
"""Implements the export command"""
if isinstance(args.object, Project):
prj = self._lookup(self.gateway, "Project", args.object.id)
for ds in prj.listChildren():
ds = self._lookup(self.gateway, "Dataset", ds.id)
for img in ds.listChildren():
self.__do_export(ds, img)
if not args.traverse:
break

elif isinstance(args.object, Dataset):
ds = self._lookup(self.gateway, "Dataset", args.object.id)
for img in ds.listChildren():
self.__do_export(ds, img)
if not args.traverse:
break

elif isinstance(args.object, Image):
img = self._lookup(self.gateway, "Image", args.object.id)
ds = self._get_datasets_for_image(self.gateway,
args.object.id.getValue())
if len(ds) > 1:
self.ctx.out("Warning, more than one dataset found, using "
"latest one")
res = max(ds, key=lambda d: d.getId().getValue())
else:
res = ds[0]
res = DatasetWrapper(conn=self.gateway, obj=res)
self.__do_export(res, img)

else:
self.ctx.err(f"{args.object.__class__.__name__} not supported "
f"yet")

@gateway_required
def copy(self, args):
""" Implements the 'copy' command """
Expand Down
Loading