From b06b9a873b4a4fedfa00fcd25ece2772fbcfd734 Mon Sep 17 00:00:00 2001 From: Dominik Lindner Date: Tue, 30 Jan 2024 11:23:49 +0000 Subject: [PATCH] Add export and impo(rt) commands --- src/omero_cli_render.py | 143 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 138 insertions(+), 5 deletions(-) diff --git a/src/omero_cli_render.py b/src/omero_cli_render.py index 97fc493..670d094 100755 --- a/src/omero_cli_render.py +++ b/src/omero_cli_render.py @@ -19,6 +19,8 @@ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from __future__ import print_function + +import omero from past.builtins import long from builtins import str from builtins import range @@ -33,14 +35,14 @@ 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 @@ -377,11 +379,13 @@ 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 rendering settings as yaml files") + impo = parser.add(sub, self.impo, "Import rendering settings from yaml files") 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") @@ -389,7 +393,7 @@ def _configure(self, parser): 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") @@ -406,7 +410,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") @@ -428,6 +432,14 @@ def _configure(self, parser): help="If underlying pixel data available test thumbnail retrieval" ) + export.add_argument("--sep", default="|", help="Separator character (default: |)") + export.add_argument("--traverse", action="store_true", help="Export settings for all images of a dataset (default: just first one)") + + impo.add_argument("--sep", default="|", help="Separator character (default: |)") + 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') @@ -537,6 +549,127 @@ 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""" + filename = args.channels + filename = filename.replace(".yml", "") + ds_name = filename.split(args.sep)[0] + img_name = filename.split(args.sep)[1] + + 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 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, sep): + """ + Exports the rendering settings of an image to a file + called .yml + :param ds: The dataset + :param img: The image + :param sep: The separator + :return: None + """ + try: + ro = RenderObject(img) + name = f"{ds.getName()}{sep}{img.getName()}.yml" + with open(name, "w") 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, args.sep) + 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, args.sep) + 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, args.sep) + + else: + self.ctx.err(f"{args.object.__class__.__name__} not supported yet ") + + @gateway_required def copy(self, args): """ Implements the 'copy' command """