Skip to content

Add CellProfiler 5 OMERO plugins #220

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

Open
wants to merge 34 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
be46f3b
Initial OMERO reader
DavidStirling Jul 25, 2023
31e4bb5
Add OMERO browser
DavidStirling Jul 27, 2023
193dcd0
General improvements
DavidStirling Jul 28, 2023
35d66eb
Add user filter
DavidStirling Jul 29, 2023
1b13edc
Add screens support
DavidStirling Aug 1, 2023
37c3933
Better drag selection
DavidStirling Aug 1, 2023
e2d4320
Move GUI/login components to subdirectory
DavidStirling Aug 1, 2023
279877b
Tweaks and docs
DavidStirling Aug 2, 2023
779837d
Docs
DavidStirling Aug 2, 2023
404a869
Move thumbnail loading onto a thread
DavidStirling Aug 3, 2023
934de8e
Add install system and docs
DavidStirling Aug 4, 2023
52b644f
Require OMERO token, fix analysis mode by passing session to workers …
DavidStirling Aug 6, 2023
dc9cf06
Add ExportToOMEROTable module
DavidStirling Aug 8, 2023
0b9b90a
Add SaveImagesToOMERO plugin
DavidStirling Aug 8, 2023
7a75654
Formatting tweak
DavidStirling Aug 8, 2023
4c1dec9
Add relationships table support
DavidStirling Aug 9, 2023
f634a2d
Safer home dir manipulation
DavidStirling Sep 6, 2023
021f183
Handle blanked OMERO server config key
DavidStirling Sep 6, 2023
c02c498
Apply to username too
DavidStirling Sep 6, 2023
0118748
Username detection fix
DavidStirling Sep 6, 2023
424841b
Explicitly set browse dlg choices in browse dialog
DavidStirling Sep 6, 2023
2c65a84
Fix shutdown bug
DavidStirling Sep 20, 2023
a1d73cf
Add expanded docs
DavidStirling Sep 20, 2023
8fedc5f
Fix tests (#210)
bethac07 Aug 1, 2023
267662d
RunCellpose via docker (#203)
callum-jpg Aug 4, 2023
a92bf84
Update _toc.yml (#212)
bethac07 Aug 4, 2023
836a73a
Doc updates (#211)
bethac07 Aug 8, 2023
eca8fae
Update runstardist.py (#205)
mccruz07 Aug 15, 2023
2bbb329
Update runstardist.py (#214)
bethac07 Aug 22, 2023
b710fc4
Update supported_plugins.md (#215)
bethac07 Sep 7, 2023
b7578cc
CP-IJ: add workaround for Java names (#219)
hinerm Sep 21, 2023
587dd06
Fix table markdown
DavidStirling Sep 25, 2023
ab0528f
Merge branch 'master' into omero
DavidStirling Sep 25, 2023
defd324
Merge branch 'master' into omero
DavidStirling Oct 4, 2023
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
896 changes: 896 additions & 0 deletions active_plugins/exporttoomerotable.py

Large diffs are not rendered by default.

Empty file.
223 changes: 223 additions & 0 deletions active_plugins/omero_helper/connect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
import atexit
import os
import logging
import sys
import tempfile

from cellprofiler_core.preferences import config_read_typed, get_headless, get_temporary_directory
from cellprofiler_core.preferences import get_omero_server, get_omero_port, get_omero_user
import omero
from omero.gateway import BlitzGateway
import omero_user_token

LOGGER = logging.getLogger(__name__)
OMERO_CREDENTIAL_FILE = os.path.join(get_temporary_directory(), "OMERO_CP.token")


def login(e=None, server=None, token_path=None):
# Attempt to connect to the server, first using a token, then via GUI
CREDENTIALS.get_tokens(token_path)
if CREDENTIALS.tokens:
if server is None:
# URL didn't specify which server we want. Just try whichever token is available
server = list(CREDENTIALS.tokens.keys())[0]
connected = CREDENTIALS.try_token(server)
else:
connected = CREDENTIALS.client is not None
if get_headless():
if connected:
LOGGER.info(f"Connected to {CREDENTIALS.server}")
elif CREDENTIALS.try_temp_token():
connected = True
LOGGER.info(f"Connected to {CREDENTIALS.server}")
else:
LOGGER.warning("Failed to connect, was user token invalid?")
return connected
else:
from .gui import login_gui, configure_for_safe_shutdown
if not CREDENTIALS.bound:
configure_for_safe_shutdown()
CREDENTIALS.bound = True
login_gui(connected, server=None)


def get_temporary_dir():
temporary_directory = get_temporary_directory()
if not (
os.path.exists(temporary_directory) and os.access(temporary_directory, os.W_OK)
):
temporary_directory = tempfile.gettempdir()
return temporary_directory


def clear_temporary_file():
LOGGER.debug("Checking for OMERO credential file to delete")
if os.path.exists(OMERO_CREDENTIAL_FILE):
os.unlink(OMERO_CREDENTIAL_FILE)
LOGGER.debug(f"Cleared {OMERO_CREDENTIAL_FILE}")


if not get_headless():
if os.path.exists(OMERO_CREDENTIAL_FILE):
LOGGER.warning("Existing credential file was found")
# Main GUI process should clear any temporary tokens
atexit.register(clear_temporary_file)


class LoginHelper:
"""
This class stores our working set of OMERO credentials and connection objects.

It behaves as a singleton, so multiple OMERO-using plugins will share credentials.
"""
_instance = None

def __new__(cls):
# We only allow one instance of this class within CellProfiler
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance

def __init__(self):
self.server = get_omero_server()
self.port = get_omero_port()
self.username = get_omero_user()
self.passwd = ""
self.session_key = None
self.session = None
self.client = None
self.gateway = None
self.container_service = None
self.tokens = {}
# Whether we've already hooked into the main GUI frame
self.bound = False
# Any OMERO browser GUI which is connected to this object
self.browser_window = None
atexit.register(self.shutdown)

def get_gateway(self):
if self.client is None:
raise Exception("Client connection not initialised")
if self.gateway is None:
LOGGER.debug("Constructing BlitzGateway")
self.gateway = BlitzGateway(client_obj=self.client)
return self.gateway

def get_tokens(self, path=None):
# Load all tokens from omero_user_token
self.tokens.clear()
# Future versions of omero_user_token may support multiple tokens, so we code with that in mind.
# Check the reader setting which disables tokens.
tokens_enabled = config_read_typed(f"Reader.OMERO.allow_token", bool)
if tokens_enabled is not None and not tokens_enabled:
return
# User tokens sadly default to the home directory. This would override that location.
home_key = "USERPROFILE" if sys.platform == "win32" else "HOME"
py_home = os.environ.get(home_key, "")
if path is not None:
os.environ[home_key] = path
try:
LOGGER.info("Requesting token info")
token = omero_user_token.get_token()
server, port = token[token.find('@') + 1:].split(':')
port = int(port)
LOGGER.info("Connection to {}:{}".format(server, port))
session_key = token[:token.find('@')]
self.tokens[server] = (server, port, session_key)
except Exception:
LOGGER.error("Failed to get user token", exc_info=True)
if path is not None:
os.environ[home_key] = py_home

def try_token(self, address):
# Attempt to use an omero token to connect to a specific server
if address not in self.tokens:
LOGGER.error(f"Token {address} not found")
return False
else:
server, port, session_key = self.tokens[address]
return self.login(server=server, port=port, session_key=session_key)

def create_temp_token(self):
# Store a temporary OMERO token based on our active session
# This allows the workers to use that session in Analysis mode.
if self.client is None:
raise ValueError("Client not initialised, cannot make token")
if os.path.exists(OMERO_CREDENTIAL_FILE):
LOGGER.warning(f"Token already exists at {OMERO_CREDENTIAL_FILE}, overwriting")
os.unlink(OMERO_CREDENTIAL_FILE)
try:
token = f"{self.session_key}@{self.server}:{self.port}"
with open(OMERO_CREDENTIAL_FILE, 'w') as token_file:
token_file.write(token)
LOGGER.debug(f"Made temp token for {self.server}")
except:
LOGGER.error("Unable to write temporary token", exc_info=True)

def try_temp_token(self):
# Look for and attempt to connect to OMERO using a temporary token.
if not os.path.exists(OMERO_CREDENTIAL_FILE):
LOGGER.error(f"No temporary OMERO token found. Cannot connect to server.")
return False
with open(OMERO_CREDENTIAL_FILE, 'r') as token_path:
token = token_path.read().strip()
server, port = token[token.find('@') + 1:].split(':')
port = int(port)
session_key = token[:token.find('@')]
LOGGER.info(f"Using connection details for {self.server}")
return self.login(server=server, port=port, session_key=session_key)

def login(self, server=None, port=None, user=None, passwd=None, session_key=None):
# Attempt to connect to the server using provided connection credentials
self.client = omero.client(host=server, port=port)
if session_key is not None:
try:
self.session = self.client.joinSession(session_key)
self.client.enableKeepAlive(60)
self.session.detachOnDestroy()
self.server = server
self.port = port
self.session_key = session_key
except Exception as e:
LOGGER.error(f"Failed to join session, token may have expired: {e}")
self.client = None
self.session = None
return False
elif user is not None:
try:
self.session = self.client.createSession(
username=user, password=passwd)
self.client.enableKeepAlive(60)
self.session.detachOnDestroy()
self.session_key = self.client.getSessionId()
self.server = server
self.port = port
self.username = user
self.passwd = passwd
except Exception as e:
LOGGER.error(f"Failed to create session: {e}")
self.client = None
self.session = None
return False
else:
self.client = None
self.session = None
raise Exception(
"Not enough details to create a server connection.")
self.container_service = self.session.getContainerService()
return True

def handle_exit(self, e):
self.shutdown()
e.Skip()

def shutdown(self):
# Disconnect from the server
if self.client is not None:
try:
self.client.closeSession()
except Exception as e:
LOGGER.error("Failed to close OMERO session - ", e)


CREDENTIALS = LoginHelper()
Loading