Skip to content

Commit b8eb968

Browse files
authored
Migration of CLEM functions from murfey to cryoem-services (#323)
* Removed command line entry points for image stack creation * Removed local CLEM image processing functions and 'murfey.workflows' usage of them * Removed CLEM package dependencies * Refactored and renamed functions still used by Murfey for its part of the CLEM workflow
1 parent 2312fd7 commit b8eb968

File tree

12 files changed

+51
-1031
lines changed

12 files changed

+51
-1031
lines changed

pyproject.toml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,8 @@ server = [
6565
"pillow",
6666
"prometheus_client",
6767
"python-jose[cryptography]",
68-
"readlif", # Specific to cryo-CLEM workflow
6968
"sqlmodel",
7069
"stomp-py<=8.1.0", # 8.1.1 (released 2024-04-06) doesn't work with our project
71-
"tifffile", # Specific to cryo-CLEM workflow
7270
"uvicorn[standard]",
7371
"zocalo",
7472
]
@@ -84,16 +82,14 @@ murfey = "murfey.client:run"
8482
"murfey.decrypt_password" = "murfey.cli.decrypt_db_password:run"
8583
"murfey.generate_key" = "murfey.cli.generate_crypto_key:run"
8684
"murfey.generate_password" = "murfey.cli.generate_db_password:run"
87-
"murfey.lif_to_tiff" = "murfey.cli.lif_to_tiff:run"
8885
"murfey.server" = "murfey.server:run"
8986
"murfey.sessions" = "murfey.cli.db_sessions:run"
9087
"murfey.simulate" = "murfey.cli.dummy:run"
9188
"murfey.spa_inject" = "murfey.cli.inject_spa_processing:run"
9289
"murfey.spa_ispyb_entries" = "murfey.cli.spa_ispyb_messages:run"
93-
"murfey.tiff_to_stack" = "murfey.cli.tiff_to_stack:run"
9490
"murfey.transfer" = "murfey.cli.transfer:run"
9591
[project.entry-points."murfey.workflows"]
96-
"lif_to_tiff" = "murfey.workflows.lif_to_tiff:zocalo_cluster_request"
92+
"lif_to_stack" = "murfey.workflows.lif_to_stack:zocalo_cluster_request"
9793
"tiff_to_stack" = "murfey.workflows.tiff_to_stack:zocalo_cluster_request"
9894

9995
[tool.setuptools]

src/murfey/cli/lif_to_tiff.py

Lines changed: 0 additions & 33 deletions
This file was deleted.

src/murfey/cli/tiff_to_stack.py

Lines changed: 0 additions & 57 deletions
This file was deleted.

src/murfey/client/contexts/clem.py

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
import logging
77
from datetime import datetime
88
from pathlib import Path
9-
from typing import Dict, List, Optional
9+
from typing import Dict, Generator, List, Optional
10+
from xml.etree import ElementTree as ET
1011

1112
from defusedxml.ElementTree import parse
1213

1314
from murfey.client.context import Context
1415
from murfey.client.instance_environment import MurfeyInstanceEnvironment
1516
from murfey.util import capture_post, get_machine_config
16-
from murfey.util.clem.xml import get_image_elements
1717

1818
# Create logger object
1919
logger = logging.getLogger("murfey.client.contexts.clem")
@@ -52,7 +52,40 @@ def _get_source(
5252
return None
5353

5454

55-
# WORK IN PROGRESS
55+
def _get_image_elements(root: ET.Element) -> List[ET.Element]:
56+
"""
57+
Searches the XML metadata recursively to find the nodes tagged as "Element" that
58+
have image-related tags. Some LIF datasets have layers of nested elements, so a
59+
recursive approach is needed to avoid certain datasets breaking it.
60+
"""
61+
62+
# Nested function which generates list of elements
63+
def _find_elements_recursively(
64+
node: ET.Element,
65+
) -> Generator[ET.Element, None, None]:
66+
67+
# Find items labelled "Element" under current node
68+
elem_list = node.findall("./Children/Element")
69+
if len(elem_list) < 1: # Try alternative path for top-level of XML tree
70+
elem_list = node.findall("./Element")
71+
72+
# Recursively search for items tagged as Element under child branches
73+
for elem in elem_list:
74+
yield elem
75+
new_node = elem # New starting point for the search
76+
new_elem_list = _find_elements_recursively(new_node) # Call self
77+
for new_elem in new_elem_list:
78+
yield new_elem
79+
80+
# Get initial list of elements
81+
elem_list = list(_find_elements_recursively(root))
82+
83+
# Keep only the element nodes that have image-related tags
84+
elem_list = [elem for elem in elem_list if elem.find("./Data/Image")]
85+
86+
return elem_list
87+
88+
5689
class CLEMContext(Context):
5790
def __init__(self, acquisition_software: str, basepath: Path):
5891
super().__init__("CLEM", acquisition_software)
@@ -205,7 +238,7 @@ def post_transfer(
205238

206239
# Extract metadata to get the expected size of the series
207240
metadata = parse(transferred_file).getroot()
208-
metadata = get_image_elements(metadata)[0]
241+
metadata = _get_image_elements(metadata)[0]
209242

210243
# Get channel and dimension information
211244
channels = metadata.findall(
@@ -294,7 +327,7 @@ def post_transfer(
294327
)
295328

296329
# Construct the URL for the Murfey server to communicate with
297-
url = f"{str(environment.url.geturl())}/sessions/{environment.murfey_session}/lif_to_tiff"
330+
url = f"{str(environment.url.geturl())}/sessions/{environment.murfey_session}/lif_to_stack"
298331
# Type checking to satisfy MyPy
299332
if not url:
300333
logger.warning("No URL found for the environment")

src/murfey/server/clem/api.py

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
from fastapi import APIRouter
66

77
from murfey.server import _transport_object
8-
from murfey.util.clem.lif import convert_lif_to_tiff
9-
from murfey.util.clem.tiff import convert_tiff_to_stack
108
from murfey.util.models import LifFileInfo, TiffSeriesInfo
119

1210
# Use backport from importlib_metadata for Python <3.10
@@ -19,30 +17,27 @@
1917
router = APIRouter()
2018

2119

22-
@router.post("/sessions/{session_id}/lif_to_tiff") # API posts to this URL
23-
def lif_to_tiff(
20+
@router.post("/sessions/{session_id}/lif_to_stack") # API posts to this URL
21+
def lif_to_stack(
2422
session_id: int, # Used by the decorator
2523
lif_info: LifFileInfo,
2624
):
2725
# Get command line entry point
2826
murfey_workflows = entry_points().select(
29-
group="murfey.workflows", name="lif_to_tiff"
27+
group="murfey.workflows", name="lif_to_stack"
3028
)
3129

3230
# Use entry point if found
3331
if murfey_workflows:
3432
murfey_workflows[0].load()(
35-
# Match the arguments found in murfey.workflows.lif_to_tiff
33+
# Match the arguments found in murfey.workflows.lif_to_stack
3634
file=lif_info.name,
3735
root_folder="images",
3836
messenger=_transport_object,
3937
)
40-
# Call function directly otherwise
38+
# Raise error if Murfey workflow not found
4139
else:
42-
convert_lif_to_tiff(
43-
file=lif_info.name,
44-
root_folder="images",
45-
)
40+
raise RuntimeError("The relevant Murfey workflow was not found")
4641

4742

4843
@router.post("/sessions/{session_id}/tiff_to_stack")
@@ -52,7 +47,7 @@ def tiff_to_stack(
5247
):
5348
# Get command line entry point
5449
murfey_workflows = entry_points().select(
55-
group="murfey.workflows", name="lif_to_tiff"
50+
group="murfey.workflows", name="tiff_to_stack"
5651
)
5752

5853
# Use entry point if found
@@ -64,10 +59,6 @@ def tiff_to_stack(
6459
metadata=tiff_info.series_metadata,
6560
messenger=_transport_object,
6661
)
67-
# Call function directly otherwise
62+
# Raise error if Murfey workflow not found
6863
else:
69-
convert_tiff_to_stack(
70-
tiff_list=tiff_info.tiff_files,
71-
root_folder="images",
72-
metadata_file=tiff_info.series_metadata,
73-
)
64+
raise RuntimeError("The relevant Murfey workflow was not found")

src/murfey/util/clem/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)