changeset 4:25d2036b9b8b draft default tip

planemo upload for repository https://github.com/Helmholtz-UFZ/galaxy-tools/tree/main/tools/omero commit 233f0e70cb20a02ec8530dbcfd5c7e70eef74476
author ufz
date Mon, 26 Jan 2026 15:03:40 +0000
parents c2d2f6ae0564
children
files README.md connect_omero.py macros.xml omero_dataset_to_plate.py omero_filter.py omero_filter.xml omero_get_id.py omero_get_value.py omero_metadata_upload.py omero_roi_upload.py test-data/omero_output_2.json
diffstat 11 files changed, 504 insertions(+), 200 deletions(-) [+]
line wrap: on
line diff
--- a/README.md	Fri Jun 13 20:45:58 2025 +0000
+++ b/README.md	Mon Jan 26 15:03:40 2026 +0000
@@ -1,4 +1,4 @@
-# OMERO import images
+# OMERO suite toolbox
 
 ## Set up user credentials on Galaxy to connect to other omero instance
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/connect_omero.py	Mon Jan 26 15:03:40 2026 +0000
@@ -0,0 +1,15 @@
+import sys
+
+import ezomero as ez
+from omero.gateway import BlitzGateway
+
+
+def establish_connection(uuid_key, usr, psw, host, port):
+    if uuid_key is not None:
+        conn = BlitzGateway(username="", passwd="", host=host, port=port, secure=True)
+        conn.connect(sUuid=uuid_key)
+    else:
+        conn = ez.connect(usr, psw, "", host, port, secure=True)
+    if not conn.connect():
+        sys.exit("ERROR: Failed to connect to OMERO server")
+    return conn
--- a/macros.xml	Fri Jun 13 20:45:58 2025 +0000
+++ b/macros.xml	Mon Jan 26 15:03:40 2026 +0000
@@ -2,13 +2,14 @@
     <!-- for historic reasons the omero-py version is used as the version for all tools -->
     <token name="@TOOL_VERSION@">5.18.0</token>
     <token name="@EZOMERO_VERSION@">3.0.1</token>
-    <token name="@PROFILE@">23.0</token>
+    <token name="@PROFILE@">25.1</token>
 
     <xml name="ezomero_requirements">
         <requirements>
             <requirement type="package" version="@EZOMERO_VERSION@">ezomero</requirement>
             <requirement type="package" version="2.2.2">pandas</requirement>
             <yield/>
+            <expand macro="omero_credentials"/>
         </requirements>
     </xml>
 
@@ -18,9 +19,17 @@
             <!-- openjdk is needed: https://github.com/conda-forge/omero-py-feedstock/pull/16 -->
             <requirement type="package" version="21.0.2">openjdk</requirement>
             <yield/>
+            <expand macro="omero_credentials"/>
         </requirements>
     </xml>
-    
+
+    <xml name="omero_credentials">
+        <credentials name="OMERO" version="0.1" label="OMERO Credentials" description="Input a set of credentials to access a target OMERO.server">
+            <variable name="username" inject_as_env="OMERO_USER" optional="true" label="OMERO username" description="OMERO username" />
+            <secret name="password" inject_as_env="OMERO_PASSWORD" optional="true" label="OMERO password" description="OMERO password" />
+            <secret name="uuid-key" inject_as_env="UUID_SESSION_KEY" optional="true" label="OMERO UUID Session Key" description="Input your session key here to connect using an existing OMERO session key. More information on how to find a session key: https://omero.readthedocs.io/en/stable/users/cli/sessions.html" />
+        </credentials>
+    </xml>
 
     <xml name="host_port">
         <param name="omero_host" type="text" label="OMERO host URL">
@@ -30,27 +39,49 @@
         <param argument="omero_port" type="integer" optional="false" value="4064" label="OMERO port"/>
         <param name="test_username" type="hidden" value=""/>
         <param name="test_password" type="hidden" value=""/>
+        <conditional name="session_id">
+            <param name="session_id_input" type="select" label="Create an OMERO session using a UUID key?" help="Connect to OMERO trough an existing connection (no password/username input)">
+                <option value="no">No</option>
+                <option value="yes">Yes</option>
+            </param>
+            <when value="yes">
+                <param name="close_connection" type="boolean" truevalue="True" falsevalue="False" label="Close the connection after executing the tool?"
+                help="If yes, the current OMERO connection will be closed after tools execution. UUID key cannot be used again in further tools."/>
+            </when>
+            <when value="no">
+            </when>
+        </conditional>
     </xml>
+
     <token name="@HOST_PORT@">
         --host '$omero_host'
         --port $omero_port
     </token>
 
-    <xml name="credentials">
-        <configfile name="credentials"><![CDATA[
-{
-    "username": "$__user__.extra_preferences.get('omero_account|username', $test_username)",
-    "password": "$__user__.extra_preferences.get('omero_account|password', $test_password)"
-}
-        ]]></configfile>
-    </xml>
+    <token name="@SESSION_ID@">
+        #if $session_id.session_id_input== "yes"
+            --session_close $close_connection
+        #else
+            --session_close "True"
+        #end if
+    </token>
+
 
     <token name="@SECURITY_DISCLAIMER@">
-    **OMERO-suite Security Diclaimer:** To utilize the OMERO tools, the user must trust Galaxy instances.
-    The configuration file, which contains your OMERO password and username, is stored in the job working directory.
-    This directory only exists during the runtime of the job and should only be accessible by the system user that runs the job.
-    However, please be aware that your username and password may be exposed to users with administrative rights.
-    We are working on increasing the security of the OMERO suite
+    **OMERO-suite Security Notice**
+
+    To use the OMERO tools, you must trust the Galaxy instance hosting your job.
+    Your OMERO username and password are stored in a temporary configuration file during job execution.
+
+    This file is accessible only to the system account running the job,
+    but may in principle be viewed by Galaxy administrators with elevated rights.
+
+    The file is removed after job completion and is not persistently stored.
+    **We recommend using service-specific or temporary OMERO credentials whenever possible.**
+
+    We are actively working to further improve security,
+    for example by enabling the UUID-key authentication.
+    The European Galaxy Server is operated in compliance with the EU General Data Protection Regulation (GDPR).
     </token>
 
 </macros>
\ No newline at end of file
--- a/omero_dataset_to_plate.py	Fri Jun 13 20:45:58 2025 +0000
+++ b/omero_dataset_to_plate.py	Mon Jan 26 15:03:40 2026 +0000
@@ -1,23 +1,60 @@
 import argparse
 import csv
-import json
+import os
 import re
 import sys
 from collections import defaultdict
-
+from pathlib import Path
+from typing import Optional
 
 import omero
-from omero.gateway import BlitzGateway
+from connect_omero import establish_connection
 from omero.rtypes import rint, rstring
 
+# Import environmental variables
+usr = os.getenv("OMERO_USER")
+psw = os.getenv("OMERO_PASSWORD")
+uuid_key = os.getenv("UUID_SESSION_KEY")
+
 
-def convert_dataset_to_plate(host, user, pws, port, dataset_id, log_file, mapping_file, delete_dataset):
+def convert_dataset_to_plate(
+    host: str,
+    port: str,
+    dataset_id: str,
+    log_file: Path,
+    mapping_file: str,
+    delete_dataset: bool,
+    uuid_key: Optional[str] = None,
+    ses_close: Optional[bool] = True
+) -> str:
     """
     Connect to OMERO server, convert a dataset to a plate using the specified well mapping file
+
+    Parameters
+    ----------
+     host : str
+        OMERO server host (i.e. OMERO address or domain name)"
+    port : int
+        OMERO server port (default:4064)
+    dataset_id : str
+        Dataset ID to convert plate
+    log_file : str
+        Output path for the log file
+    mapping_file: str
+        Tabular file mapping filenames to well positions (2 columns: filename, Well)
+    delete_dataset: bool
+        Input to delete the original dataset convert to plate or not
+    uuid_key : str, optional
+        OMERO UUID session key to connect without password
+    ses_close : bool
+        Decide if close or not the section after executing the script. Defaulf value is true, useful when connecting with the UUID session key.
+
+    Returns
+    -------
+    str
+        Return log file with info on the conversion
     """
-    conn = BlitzGateway(user, pws, host=host, port=port, secure=True)
-    if not conn.connect():
-        sys.exit("ERROR: Failed to connect to OMERO server")
+    conn = establish_connection(uuid_key, usr, psw, host, port)
 
     def log_message(message, status="INFO"):
         with open(log_file, 'w') as f:
@@ -94,34 +131,28 @@
     if delete_dataset is True:
         conn.deleteObjects("Dataset", [dataset_id], wait=True)
     log_message(f"Images from Dataset {dataset_id} successfully added to Plate {plate.id.val}")
-    conn.close()
+    if ses_close:
+        conn.close()
 
 
 if __name__ == "__main__":
     parser = argparse.ArgumentParser(description="Convert an OMERO dataset to a plate.")
-    parser.add_argument("--credential-file", dest="credential_file", type=str, required=True,
-                        help="Credential file (JSON file with username and password for OMERO)")
-    parser.add_argument('--host', required=True, help='OMERO host')
-    parser.add_argument('--port', required=True, type=int, help='OMERO port')
+    parser.add_argument('--host', required=True, help="OMERO server host (i.e. OMERO address or domain name)")
+    parser.add_argument('--port', required=True, type=int, help="OMERO server port (default:4064)")
     parser.add_argument('--dataset_id', type=int, required=True, help="Dataset ID to convert plate")
-    parser.add_argument('--log_file', default='metadata_import_log.txt',
-                        help='Path to the log file')
-    parser.add_argument('--mapping_file',
-                        help='Tabular file mapping filenames to well positions (2 columns: filename, Well)')
-    parser.add_argument('--delete_dataset', action='store_true',
-                        help='Flag to delete the original dataset')
+    parser.add_argument('--log_file', default='metadata_import_log.txt', help="Output path for the log file")
+    parser.add_argument('--mapping_file', help='Tabular file mapping filenames to well positions (2 columns: filename, Well)')
+    parser.add_argument('--session_close', required=False, help='Namespace or title for the annotation')
+    parser.add_argument('--delete_dataset', action='store_true', help='Flag to delete the original dataset')
+
     args = parser.parse_args()
 
-    with open(args.credential_file, 'r') as f:
-        crds = json.load(f)
-
     convert_dataset_to_plate(
-        user=crds['username'],
-        pws=crds['password'],
         host=args.host,
         port=args.port,
         dataset_id=args.dataset_id,
         log_file=args.log_file,
         mapping_file=args.mapping_file,
+        ses_close=args.session_close,
         delete_dataset=args.delete_dataset
     )
--- a/omero_filter.py	Fri Jun 13 20:45:58 2025 +0000
+++ b/omero_filter.py	Mon Jan 26 15:03:40 2026 +0000
@@ -1,16 +1,59 @@
 import argparse
 import csv
-import json
+import os
 import sys
+from typing import Optional
 
 import ezomero as ez
+from connect_omero import establish_connection
+
+# Import environmental variables
+usr = os.getenv("OMERO_USER")
+psw = os.getenv("OMERO_PASSWORD")
+uuid_key = os.getenv("UUID_SESSION_KEY")
 
 
-def filter_ids_ezo(user, pws, host, port, filter, id, value1, value2=None, tsv_file="filter_list.tsv"):
+def filter_ids_ezo(
+        host: str,
+        port: int,
+        filter: str,
+        id: list,
+        value1: str,
+        value2: Optional[str] = None,
+        uuid_key: Optional[str] = None,
+        tsv_file: str = "filter_list.tsv",
+        ses_close: Optional[bool] = True
+) -> int:
+    """
+
+    Apply filter_by_filename, filter_by_kv or filter_by_tag_value from the ezomero module to a list of images ID.
 
-    # Transform the id input in a list of integer
-    id = id.split(',')
-    id = list(map(int, id))
+    Parameters
+    ----------
+    host : str
+        OMERO server host (i.e. OMERO address or domain name)"
+    port : int
+        OMERO server port (default:4064)
+    filter : str
+        Filter to apply to the IDs list (Filename, Key-Value pairs or Tags)
+    id : int
+        A list of image IDs
+    value1 : str
+        Primary filter value.
+    value2 : str, optional
+        Optional secondary filter value.
+    uuuid_key : str, optional
+        OMERO UUID session key to connect without password
+    tsv_file : str, optional
+        Output TSV filename. Default is "filter_list.tsv".
+    ses_close : bool
+        Decide if close or not the section after executing the script. Defaulf value is true, useful when connecting with the UUID session key.
+
+    Returns
+    -------
+    csv.writer
+        A CSV writer object configured to write TSV data. Contain a list of IDs with the filtered IDs
+    """
 
     # Function to write tabular file from the ezomero output
     def write_ids_to_tsv(data):
@@ -19,8 +62,15 @@
             for item in data:
                 writer.writerow([item])  # Write each ID
 
-    with ez.connect(user, pws, "", host, port, secure=True) as conn:
+    # Try to connect with UUID or with username and password
+    conn = establish_connection(uuid_key, usr, psw, host, port)
 
+    # Transform the id input in a list of integer
+    id = id.split(',')
+    id = list(map(int, id))
+
+    try:
+        # Apply different filters to the image ID list
         if filter == "filename":
             fn_ids = ez.filter_by_filename(conn, id, value1)
             write_ids_to_tsv(fn_ids)
@@ -39,39 +89,34 @@
         else:
             sys.exit(f"Unsupported object type: {filter}")
 
+    finally:
+        if ses_close:
+            conn.close()
 
-# Argument parsing
+
 if __name__ == "__main__":
     parser = argparse.ArgumentParser(description="Fetch and save data as TSV based on object type.")
-    parser.add_argument("--credential-file", dest="credential_file", type=str, required=True,
-                        help="Credential file (JSON file with username and password for OMERO)")
-    parser.add_argument('--host', required=True,
-                        help="Host server address.")
-    parser.add_argument('--port', required=True, type=int,
-                        help='OMERO port')
-    parser.add_argument('--filter', required=True,
-                        help="Filter type - Filename, Key-Value Pairs, Tag")
-    parser.add_argument('--id', required=True,
-                        help="List of images IDs")
-    parser.add_argument('--value1', required=True,
-                        help="First searching values - Filename, Key, Tag")
+    parser.add_argument('--host', required=True, help="OMERO server host (i.e. OMERO address or domain name)")
+    parser.add_argument('--port', required=True, type=int, help="OMERO server port (default:4064)")
+    parser.add_argument('--filter', required=True, help="Filter type - Filename, Key-Value Pairs, Tag")
+    parser.add_argument('--id', required=True, help="List of images IDs")
+    parser.add_argument('--value1', required=True, help="First searching values - Filename, Key, Tag")
     parser.add_argument('--value2', required=False,
                         help="Second searching values - Value (necessary just for Key-Value Pairs filter")
-    parser.add_argument('--tsv_file', default='filter_list.tsv',
-                        help="Output TSV file path.")
+    parser.add_argument('--session_close', required=False, help='Namespace or title for the annotation')
+    parser.add_argument('--tsv_file', default='filter_list.tsv', help="Output TSV file path.")
+
     args = parser.parse_args()
 
     if args.filter == "KP" and args.value2 is None:
         raise ValueError("'--value 2' is necessary to retrieve KP")
 
-    with open(args.credential_file, 'r') as f:
-        crds = json.load(f)
-
     # Call the main function to get the object and save it as a TSV
-    filter_ids_ezo(user=crds['username'], pws=crds['password'], host=args.host,
+    filter_ids_ezo(host=args.host,
                    port=args.port,
                    filter=args.filter,
                    value1=args.value1,
                    value2=args.value2,
                    id=args.id,
+                   ses_close=args.session_close,
                    tsv_file=args.tsv_file)
--- a/omero_filter.xml	Fri Jun 13 20:45:58 2025 +0000
+++ b/omero_filter.xml	Mon Jan 26 15:03:40 2026 +0000
@@ -2,15 +2,23 @@
     <description> with ezomero </description>
     <macros>
         <import>macros.xml</import>
-        <token name="@VERSION_SUFFIX@">1</token>
+        <token name="@VERSION_SUFFIX@">2</token>
     </macros>
     <xrefs>
         <xref type="bio.tools">omero</xref>
     </xrefs>
     <expand macro="ezomero_requirements"/>
+    <required_files>
+        <include path="connect_omero.py" />
+        <include path="omero_filter.py" />
+    </required_files>
     <command detect_errors="exit_code"><![CDATA[
+        #if $test_username != ""
+            export OMERO_USER="$test_username" &&
+            export OMERO_PASSWORD="$test_password" &&
+       #end if
+
         python '$__tool_directory__'/omero_filter.py
-        --credential-file '$credentials'
         @HOST_PORT@
         --filter $filter
         --value1 '$value1'
@@ -19,11 +27,8 @@
         #if $filter == "KP"
         --value2 '$value2'
         #end if
-
+        @SESSION_ID@
     ]]></command>
-    <configfiles>
-        <expand macro="credentials"/>
-    </configfiles>
     <inputs>
         <expand macro="host_port"/>
         <conditional name = "filter_type">
--- a/omero_get_id.py	Fri Jun 13 20:45:58 2025 +0000
+++ b/omero_get_id.py	Mon Jan 26 15:03:40 2026 +0000
@@ -1,12 +1,57 @@
 import argparse
 import csv
-import json
+import os
 import sys
+from typing import Optional
 
 import ezomero as ez
+from connect_omero import establish_connection
+
+# Import environmental variables
+usr = os.getenv("OMERO_USER")
+psw = os.getenv("OMERO_PASSWORD")
+uuid_key = os.getenv("UUID_SESSION_KEY")
 
 
-def get_ids_ezo(user, pws, host, port, final_obj_type, parent_obj_type, parent_id=None, tsv_file="id_list.tsv"):
+def get_ids_ezo(
+        host: str,
+        port: int,
+        final_obj_type: str,
+        parent_obj_type: str,
+        parent_id: Optional[int] = None,
+        uuid_key: Optional[str] = None,
+        tsv_file: str = "filter_list.tsv",
+        ses_close: Optional[bool] = True
+) -> int:
+    """
+    Fetch OMERO object IDs (Project, Dataset, Image, Annotation, Tag, ROI, or Table) as TSV from parent object (roject, Dataset, Plate, Well, Image)
+
+    Parameters
+    ----------
+    host : str
+        OMERO server host (i.e. OMERO address or domain name)"
+    port : int
+        OMERO server port (default:4064)
+    final_obj_type : str
+        Type of object to fetch ID: Project, Dataset, Image, Annotation, Tag, ROI, or Table.
+    parent_obj_type : int
+        Type of object from which you fetch IDs: Project, Dataset, Plate, Well, Image (or 'All' if you want to get all objects).
+    parent_id : str, optional
+        ID of the OMERO object in `--parent_obj_type`, not required if you used `--parent_obj_type All`.
+    uuid_key : str, optional
+        OMERO UUID session key to connect without password
+    tsv_file : str, optional
+        Output TSV filename. Default is "filter_list.tsv".
+    ses_close : bool
+        Decide if close or not the section after executing the script. Defaulf value is true, useful when connecting with the UUID session key.
+
+    Returns
+    -------
+    csv.writer
+        A CSV writer object configured to write TSV data. Contain a list of IDs.
+    """
+
+    conn = establish_connection(uuid_key, usr, psw, host, port)
 
     # Function to write tabular file from the ezomero output
     def write_ids_to_tsv(data):
@@ -15,8 +60,8 @@
             for item in data:
                 writer.writerow([item])  # Write each ID
 
-    with ez.connect(user, pws, "", host, port, secure=True) as conn:
-
+    try:
+        # Fetch different object according to the user input
         if final_obj_type == "Project":
             proj_ids = ez.get_project_ids(conn)
             write_ids_to_tsv(proj_ids)
@@ -75,42 +120,40 @@
         else:
             sys.exit(f"Unsupported object type: {filter}")
 
+    finally:
+        if ses_close:
+            conn.close()
 
-# Argument parsing
+
 if __name__ == "__main__":
     parser = argparse.ArgumentParser(description="Fetch OMERO object IDs as TSV from parent object.")
-    parser.add_argument("--credential-file", dest="credential_file", type=str,
-                        required=True, help="Credential file (JSON file with username and password for OMERO)")
-    parser.add_argument('--host', required=True,
-                        help="Host server address.")
-    parser.add_argument('--port', required=True, type=int,
-                        help='OMERO port')
+    parser.add_argument('--host', required=True, help="OMERO server host (i.e. OMERO address or domain name)")
+    parser.add_argument('--port', required=True, type=int, help="OMERO server port (default:4064)")
     parser.add_argument('--final_obj_type', required=True,
                         help="Type of object to fetch ID: Project, Dataset, Image, Annotation, Tag, Roi, or Table.")
     parser.add_argument('--parent_obj_type', required=True,
                         help="Type of object from which you fetch IDs: Project, Dataset, Plate, Well, Image (or 'All' if you want to get all objects).")
     parser.add_argument('--parent_id', required=False, type=int,
                         help="ID of the OMERO object in `--parent_obj_type`, not required if you used `--parent_obj_type All`.")
-    parser.add_argument('--tsv_file', default='id_list.tsv',
-                        help="Output TSV file path.")
+    parser.add_argument('--session_close', required=False, help='Namespace or title for the annotation')
+    parser.add_argument('--tsv_file', default='id_list.tsv', help="Output TSV file path.")
+
     args = parser.parse_args()
 
     if args.parent_id is None and args.parent_obj_type != "All":
         raise ValueError("ID is only optional is you use `--parent_obj_type All`")
 
     if args.final_obj_type == "Roi" and args.parent_obj_type != "Image":
-        raise ValueError("Roi IDs can only be retrived from images, use `--parent_obj_type Image`")
+        raise ValueError("ROI IDs can only be retrived from images, use `--parent_obj_type Image`")
 
     if args.parent_obj_type == "All" and args.final_obj_type not in ["Image", "Dataset", "Project"]:
         raise ValueError("Only Images, Datasets and Projects is compatible with `--parent_obj_type All`")
 
-    with open(args.credential_file, 'r') as f:
-        crds = json.load(f)
-
     # Call the main function to get the object and save it as a TSV
-    get_ids_ezo(user=crds['username'], pws=crds['password'], host=args.host,
+    get_ids_ezo(host=args.host,
                 port=args.port,
                 final_obj_type=args.final_obj_type,
                 parent_obj_type=args.parent_obj_type,
                 parent_id=args.parent_id,
+                ses_close=args.session_close,
                 tsv_file=args.tsv_file)
--- a/omero_get_value.py	Fri Jun 13 20:45:58 2025 +0000
+++ b/omero_get_value.py	Mon Jan 26 15:03:40 2026 +0000
@@ -1,14 +1,56 @@
 import argparse
 import csv
-import json
 import os
 import sys
+from typing import Optional
 
 import ezomero as ez
 import pandas as pd
+from connect_omero import establish_connection
+
+# Import environmental variables
+usr = os.getenv("OMERO_USER")
+psw = os.getenv("OMERO_PASSWORD")
+uuid_key = os.getenv("UUID_SESSION_KEY")
 
 
-def get_object_ezo(user, pws, host, port, obj_type, ids, out_dir):
+def get_object_ezo(
+        host: str,
+        port: int,
+        obj_type: str,
+        ids: list,
+        out_dir: str,
+        uuid_key: Optional[str] = None,
+        ses_close: Optional[bool] = True
+) -> str | dict:
+
+    """
+Fetch OMERO objects (Annotation, Table and Key-Value Pairs list) and save them as TSV based on object type.
+
+Parameters
+----------
+host : str
+    OMERO server host (i.e. OMERO address or domain name)"
+port : int
+    OMERO server port (default:4064)
+obj_type : str
+    Type of object to fetch ID: Project, Dataset, Image, Annotation, Tag, ROI, or Table.
+ids : list
+    IDs of the OMERO objects.
+out_dir : str
+    Output path of the file
+uuid_key : str, optional
+    OMERO UUID session key to connect without password
+ses_close : bool
+    Decide if close or not the section after executing the script. Defaulf value is true, useful when connecting with the UUID session key.
+Returns
+-------
+csv.writer
+    A CSV writer object configured to write TSV data.
+"""
+
+    conn = establish_connection(uuid_key, usr, psw, host, port)
+
     # Function to write tabular file from the ezomero output
     def write_values_to_tsv(data, header):
         with open("output.tsv", 'w', newline='') as f:
@@ -31,7 +73,8 @@
             for row in data:
                 f.write('\t'.join([str(val) for val in row]) + '\n')
 
-    with ez.connect(user, pws, "", host, port, secure=True) as conn:
+    try:
+        # Fetch different object according to the user input
         if obj_type == "Annotation":
             ma_dict = {}
             for maid in ids:
@@ -61,25 +104,22 @@
         else:
             sys.exit(f"Unsupported object type: {filter}")
 
+    finally:
+        if ses_close:
+            conn.close()
 
-# Argument parsing
+
 if __name__ == "__main__":
     parser = argparse.ArgumentParser(description="Fetch and save data as TSV based on object type.")
-    parser.add_argument("--credential-file", dest="credential_file", type=str,
-                        required=True, help="Credential file (JSON file with username and password for OMERO)")
-    parser.add_argument('--host', required=True,
-                        help="Host server address.")
-    parser.add_argument('--port', required=True, type=int,
-                        help='OMERO port')
-    parser.add_argument('--obj_type', required=True,
-                        help="Type of object to fetch: Annotation, Table or Tag.")
+    parser.add_argument('--host', required=True, help="OMERO server host (i.e. OMERO address or domain name)")
+    parser.add_argument('--port', required=True, type=int, help="OMERO server port (default:4064)")
+    parser.add_argument('--obj_type', required=True, help="Type of object to fetch: Annotation, Table or Tag.")
     group = parser.add_mutually_exclusive_group()
-    group.add_argument('--ids', nargs='+', type=int,
-                       help="IDs of the OMERO objects.")
-    group.add_argument('--ids_path',
-                       help="File with IDs of the OMERO objects (one per line).")
-    parser.add_argument('--out_dir', required=True,
-                        help="Output path.")
+    group.add_argument('--ids', nargs='+', type=int, help="IDs of the OMERO objects.")
+    group.add_argument('--ids_path', help="File with IDs of the OMERO objects (one per line).")
+    parser.add_argument('--session_close', required=False, help='Namespace or title for the annotation')
+    parser.add_argument('--out_dir', required=True, help="Output path.")
+
     args = parser.parse_args()
 
     if args.ids_path:
@@ -93,12 +133,10 @@
         if len(args.ids) == 0:
             raise ValueError("Cound not find a single ID in the file.")
 
-    with open(args.credential_file, 'r') as f:
-        crds = json.load(f)
-
     # Call the main function to get the object and save it as a TSV
-    get_object_ezo(user=crds['username'], pws=crds['password'], host=args.host,
+    get_object_ezo(host=args.host,
                    port=args.port,
                    obj_type=args.obj_type,
                    ids=args.ids,
+                   ses_close=args.session_close,
                    out_dir=args.out_dir)
--- a/omero_metadata_upload.py	Fri Jun 13 20:45:58 2025 +0000
+++ b/omero_metadata_upload.py	Mon Jan 26 15:03:40 2026 +0000
@@ -1,13 +1,66 @@
 import argparse
-import json
+import os
 from datetime import datetime
+from pathlib import Path
+from typing import Optional
 
 import ezomero as ez
 import pandas as pd
+from connect_omero import establish_connection
+
+# Import environmental variables
+usr = os.getenv("OMERO_USER")
+psw = os.getenv("OMERO_PASSWORD")
+uuid = os.getenv("UUID_SESSION_KEY")
 
 
-def metadata_import_ezo(user, pws, host, port, obj_type, did=None, ann_type="table", ann_file=None, an_name=None,
-                        log_file='metadata_import_log.txt'):
+def metadata_import_ezo(
+        host: str,
+        port: int,
+        obj_type: str,
+        ann_type: [str] = "table",
+        ann_file: Path = None,
+        an_name: [str] = None,
+        did: Optional[int] = None,
+        uuid_key: Optional[str] = None,
+        log_file: [str] = 'metadata_import_log.txt',
+        ses_close: Optional[bool] = True,
+) -> str:
+
+    '''
+    Import metadata into OMERO as form of OMERO.table or Key-Value Pairs.
+
+    Parameters
+    ----------
+    host : str
+        OMERO server host (i.e. OMERO address or domain name)"
+    port : int
+        OMERO server port (default:4064)
+    did: list
+        ID of the object (if it exists)
+    obj_type : str
+        Annotation type meaning Table or Key-Value pairs
+    ann_type: str
+        Path to the annotation file
+    ann_file: [Path]=None
+        Path to the annotation file
+    an_name : str
+        Namespace or title for the annotation
+    uuid_key : str, optional
+        OMERO UUID session key to connect without password
+    log_file : str
+        Output path for the log file
+    ses_close : bool
+        Decide if close or not the section after executing the script. Defaulf value is true, useful when connecting with the UUID session key.
+
+    Returns
+    -------
+    csv.writer
+        A CSV writer object configured to write TSV data.
+    '''
+
+    conn = establish_connection(uuid_key, usr, psw, host, port)
+
     def upload_metadata(conn, obj_type, did, data_dict, df, ann_type, an_name):
         try:
             if ann_type == "KV":
@@ -48,59 +101,60 @@
         data_dict = ann_file
 
     try:
-        with ez.connect(user, pws, "", host, port, secure=True) as conn:
-            if obj_type == "project":
-                if did is None:
-                    did = ez.post_project(conn, project_name=str(datetime.now()))
-                result = upload_metadata(conn, "Project", did, data_dict, df, ann_type, an_name)
-            elif obj_type == "screen":
-                if did is None:
-                    did = ez.post_screen(conn, screen_name=str(datetime.now()))
-                result = upload_metadata(conn, "Screen", did, data_dict, df, ann_type, an_name)
-            elif obj_type == "dataset":
-                if did is None:
-                    did = ez.post_dataset(conn, dataset_name=str(datetime.now()))
-                result = upload_metadata(conn, "Dataset", did, data_dict, df, ann_type, an_name)
-            elif obj_type == "plate":
-                result = upload_metadata(conn, "Plate", did, data_dict, df, ann_type, an_name)
-            elif obj_type == "well":
-                result = upload_metadata(conn, "Well", did, data_dict, df, ann_type, an_name)
-            elif obj_type == "image":
-                result = upload_metadata(conn, "Image", did, data_dict, df, ann_type, an_name)
-            else:
-                raise ValueError("Unsupported object type provided: {}".format(obj_type))
-
-            if result is not None:
-                log_success(f"Successfully uploaded metadata for {obj_type} with ID {did}. Result: {result}")
-            else:
-                log_error(f"Failed to upload metadata for {obj_type} with ID {did}.")
-
-        conn.close()
-
-    except Exception as e:
-        log_error(f"Connection error: {str(e)}")
+        if obj_type == "project":
+            if did is None:
+                did = ez.post_project(conn, project_name=str(datetime.now()))
+            result = upload_metadata(conn, "Project", did, data_dict, df, ann_type, an_name)
+        elif obj_type == "screen":
+            if did is None:
+                did = ez.post_screen(conn, screen_name=str(datetime.now()))
+            result = upload_metadata(conn, "Screen", did, data_dict, df, ann_type, an_name)
+        elif obj_type == "dataset":
+            if did is None:
+                did = ez.post_dataset(conn, dataset_name=str(datetime.now()))
+            result = upload_metadata(conn, "Dataset", did, data_dict, df, ann_type, an_name)
+        elif obj_type == "plate":
+            result = upload_metadata(conn, "Plate", did, data_dict, df, ann_type, an_name)
+        elif obj_type == "well":
+            result = upload_metadata(conn, "Well", did, data_dict, df, ann_type, an_name)
+        elif obj_type == "image":
+            result = upload_metadata(conn, "Image", did, data_dict, df, ann_type, an_name)
+        else:
+            raise ValueError("Unsupported object type provided: {}".format(obj_type))
+    finally:
+        if result is not None:
+            log_success(f"Successfully uploaded metadata for {obj_type} with ID {did}. Result: {result}")
+            if ses_close:
+                conn.close()
+        else:
+            log_error(f"Failed to upload metadata for {obj_type} with ID {did}.")
+            if ses_close:
+                conn.close()
 
 
 if __name__ == "__main__":
-    parser = argparse.ArgumentParser(description='Import metadata into OMERO.')
-    parser.add_argument("--credential-file", dest="credential_file", type=str, required=True,
-                        help="Credential file (JSON file with username and password for OMERO)")
-    parser.add_argument('--host', required=True, help='OMERO host')
-    parser.add_argument('--port', required=True, type=int, help='OMERO port')
-    parser.add_argument('--obj_type', required=True, choices=['project', 'screen', 'dataset', 'plate',
-                                                              'well ', 'image'],
+    parser = argparse.ArgumentParser(
+        description='Import metadata into OMERO as form of OMERO.table or Key-Value Pairs.')
+    parser.add_argument('--host', required=True, help="OMERO server host (i.e. OMERO address or domain name)")
+    parser.add_argument('--port', required=True, type=int, help="OMERO server port (default:4064)")
+    parser.add_argument('--obj_type', required=True,
+                        choices=['project', 'screen', 'dataset', 'plate', 'well ', 'image'],
                         help='Type of OMERO object')
     parser.add_argument('--did', type=int, help='ID of the object (if it exists)')
     parser.add_argument('--ann_type', required=True, choices=['table', 'KV', "attachement"], help='Annotation type')
     parser.add_argument('--ann_file', required=True, help='Path to the annotation file')
     parser.add_argument('--an_name', required=True, help='Namespace or title for the annotation')
+    parser.add_argument('--session_close', required=False, help='Namespace or title for the annotation')
     parser.add_argument('--log_file', default='metadata_import_log.txt', help='Path to the log file')
 
     args = parser.parse_args()
 
-    with open(args.credential_file, 'r') as f:
-        crds = json.load(f)
-
-    metadata_import_ezo(user=crds['username'], pws=crds['password'], host=args.host, port=args.port,
-                        obj_type=args.obj_type, did=args.did, ann_type=args.ann_type,
-                        ann_file=args.ann_file, an_name=args.an_name, log_file=args.log_file)
+    metadata_import_ezo(host=args.host,
+                        port=args.port,
+                        obj_type=args.obj_type,
+                        did=args.did,
+                        ann_type=args.ann_type,
+                        ann_file=args.ann_file,
+                        an_name=args.an_name,
+                        ses_close=args.session_close,
+                        log_file=args.log_file)
--- a/omero_roi_upload.py	Fri Jun 13 20:45:58 2025 +0000
+++ b/omero_roi_upload.py	Mon Jan 26 15:03:40 2026 +0000
@@ -1,12 +1,20 @@
 import argparse
-import json
+import os
 import re
+from pathlib import Path
+from typing import Optional
 
+import ezomero as ez
 import numpy as np
 import pandas as pd
-from ezomero import connect, post_roi
+from connect_omero import establish_connection
 from ezomero.rois import Ellipse, Label, Line, Point, Polygon, Polyline, Rectangle
 
+# Import environmental variables
+usr = os.getenv("OMERO_USER")
+psw = os.getenv("OMERO_PASSWORD")
+uuid_key = os.getenv("UUID_SESSION_KEY")
+
 
 def parse_color(color_str):
     if not color_str:
@@ -24,6 +32,7 @@
     return [tuple(map(float, point.split(','))) for point in points]
 
 
+# function to create different shapes
 def create_shape(row):
     shape_type = row['shape']
     shape = None
@@ -122,55 +131,85 @@
     return shape
 
 
-def main(input_file, conn, image_id, log_file):
+def import_rois(
+    host: str,
+    port: int,
+    input_file: Path,
+    image_id: int,
+    log_file: Path,
+    uuid_key: Optional[str] = None,
+    ses_close: Optional[bool] = True,
+) -> str | int:
+
+    """
+    Create shapes from a tabular file and upload them as an ROI to OMERO.
+
+    Parameters
+    ----------
+    host : str
+        OMERO server host (i.e. OMERO address or domain name)"
+    port : int
+        OMERO server port (default:4064)
+    image_id : str
+        ID of the image to which the ROI will be linked
+    input_file: Path
+        Path to the input tabular file
+    log_file : str
+        Output path for the log file
+    uuid_key : str, optional
+        OMERO UUID session key to connect without password
+    ses_close : bool
+        Decide if close or not the section after executing the script. Defaulf value is true, useful when connecting with the UUID session key.
+    Returns
+    -------
+    str | int
+        A CSV writer object configured to write TSV data and ID of newly created ROI
+    """
+
+    # Try to connect with UUID or with username and password
+    conn = establish_connection(uuid_key, usr, psw, host, port)
+
     # Open log file
-    with open(log_file, 'w') as log:
-        df = pd.read_csv(input_file, sep='\t')
-        # Replace nan to none
-        df = df.replace({np.nan: None})
-        for index, row in df.iterrows():
-            msg = f"Processing row {index + 1}/{len(df)}: {row.to_dict()}"
-            print(msg)
-            log.write(msg + "\n")
-            shape = create_shape(row)
-            if shape:
-                roi_name = row['roi_name'] if 'roi_name' in row else None
-                roi_description = row['roi_description'] if 'roi_description' in row else None
-                roi_id = post_roi(conn, image_id, [shape], name=roi_name, description=roi_description)
-                msg = f"ROI ID: {roi_id} for row {index + 1}"
+    try:
+        with open(log_file, 'w') as log:
+            df = pd.read_csv(input_file, sep='\t')
+            # Replace nan to none
+            df = df.replace({np.nan: None})
+            for index, row in df.iterrows():
+                msg = f"Processing row {index + 1}/{len(df)}: {row.to_dict()}"
                 print(msg)
                 log.write(msg + "\n")
-            else:
-                msg = f"Skipping row {index + 1}: Unable to create shape"
-                print(msg)
-                log.write(msg + "\n")
+                shape = create_shape(row)
+                if shape:
+                    roi_name = row['roi_name'] if 'roi_name' in row else None
+                    roi_description = row['roi_description'] if 'roi_description' in row else None
+                    roi_id = ez.post_roi(conn, image_id, [shape], name=roi_name, description=roi_description)
+                    msg = f"ROI ID: {roi_id} for row {index + 1}"
+                    print(msg)
+                    log.write(msg + "\n")
+                else:
+                    msg = f"Skipping row {index + 1}: Unable to create shape"
+                    print(msg)
+                    log.write(msg + "\n")
+    finally:
+        if ses_close:
+            conn.close()
 
 
 if __name__ == "__main__":
-    parser = argparse.ArgumentParser(
-        description="Create shapes from a tabular file and optionally post them as an ROI to OMERO.")
+    parser = argparse.ArgumentParser(description="Create shapes from a tabular file and post them as an ROI to OMERO.")
+    parser.add_argument("--host", type=str, required=True, help="OMERO server host (i.e. OMERO address or domain name)")
+    parser.add_argument("--port", type=int, default=4064, help="OMERO server port (default:4064)")
     parser.add_argument("--input_file", help="Path to the input tabular file.")
     parser.add_argument("--image_id", type=int, required=True, help="ID of the image to which the ROI will be linked")
-    parser.add_argument("--host", type=str, required=True, help="OMERO server host")
-    parser.add_argument("--credential-file", dest="credential_file", type=str, required=True, help="Credential file (JSON file with username and password for OMERO)")
-    parser.add_argument("--port", type=int, default=4064, help="OMERO server port")
-    parser.add_argument("--log_file", type=str, default="process.txt", help="Log file path")
+    parser.add_argument('--session_close', required=False, help='Namespace or title for the annotation')
+    parser.add_argument("--log_file", type=str, default="process.txt", help="Output path for the log file")
 
     args = parser.parse_args()
 
-    with open(args.credential_file, 'r') as f:
-        crds = json.load(f)
-
-    conn = connect(
-        host=args.host,
-        user=crds['username'],
-        password=crds['password'],
-        port=args.port,
-        group="",
-        secure=True
-    )
-
-    try:
-        main(args.input_file, conn, args.image_id, args.log_file)
-    finally:
-        conn.close()
+    import_rois(host=args.host,
+                port=args.port,
+                input_file=args.input_file,
+                image_id=args.image_id,
+                ses_close=args.session_close,
+                log_file=args.log_file)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/omero_output_2.json	Mon Jan 26 15:03:40 2026 +0000
@@ -0,0 +1,3 @@
+[{"#": "0", "Class": "DatasetI", "Id": "3", "details": "owner=0;group=0", "name": "galaxy_test_upload"},
+{"#": "1", "Class": "DatasetI", "Id": "2", "details": "owner=0;group=0", "name": "test_hcs_dts"},
+{"#": "2", "Class": "DatasetI", "Id": "1", "details": "owner=0;group=0", "name": "test_dts"}]