Skip to content

Commit

Permalink
Merge pull request #62 from dominikl/export
Browse files Browse the repository at this point in the history
Add export and impo(rt) commands
  • Loading branch information
jburel authored Dec 4, 2024
2 parents c0517ed + cce8000 commit ce6e13c
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 5 deletions.
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
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())
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

0 comments on commit ce6e13c

Please sign in to comment.