Skip to content

Commit 0f62899

Browse files
authored
CLEM Workflow Update (#669)
* Fixed minor missed issues to the CLEM workflow after the recent patch bump. * Updated the Analyser and Context logic for the CLEM workflow to identify and process TIFF datasets for any combination of tiles, frames, and colour channels * Updated and streamlined the feedback-callback workflows for the CLEM workflow to reflect the change in structure of the received RabbitMQ messages.
1 parent 72290f9 commit 0f62899

File tree

9 files changed

+189
-345
lines changed

9 files changed

+189
-345
lines changed

pyproject.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,7 @@ GitHub = "https://github.com/DiamondLightSource/python-murfey"
104104
"clem.process_raw_lifs" = "murfey.workflows.clem.process_raw_lifs:zocalo_cluster_request"
105105
"clem.process_raw_tiffs" = "murfey.workflows.clem.process_raw_tiffs:zocalo_cluster_request"
106106
"clem.register_align_and_merge_result" = "murfey.workflows.clem.register_align_and_merge_results:register_align_and_merge_result"
107-
"clem.register_lif_preprocessing_result" = "murfey.workflows.clem.register_preprocessing_results:register_lif_preprocessing_result"
108-
"clem.register_tiff_preprocessing_result" = "murfey.workflows.clem.register_preprocessing_results:register_tiff_preprocessing_result"
107+
"clem.register_preprocessing_result" = "murfey.workflows.clem.register_preprocessing_results:run"
109108
"pato" = "murfey.workflows.notifications:notification_setup"
110109
"picked_particles" = "murfey.workflows.spa.picking:particles_picked"
111110
"spa.flush_spa_preprocess" = "murfey.workflows.spa.flush_spa_preprocess:flush_spa_preprocess"

src/murfey/client/analyser.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,9 @@ def _find_context(self, file_path: Path) -> bool:
129129
self._context = CLEMContext("leica", self._basepath, self._token)
130130
return True
131131
# Look for TIFF files associated with CLEM workflow
132-
# Leica's autosave mode seems to name the TIFFs in the format
133-
# PostionXX--ZXX--CXX.tif
134-
if all(
135-
pattern in file_path.name for pattern in ("--Z", "--C")
132+
# CLEM TIFF files will have "--Stage", "--Z", and/or "--C" in their file stem
133+
if any(
134+
pattern in file_path.stem for pattern in ("--Stage", "--Z", "--C")
136135
) and file_path.suffix in (".tiff", ".tif"):
137136
self._context = CLEMContext("leica", self._basepath, self._token)
138137
return True

src/murfey/client/contexts/clem.py

Lines changed: 44 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import logging
77
from pathlib import Path
88
from typing import Dict, Generator, List, Optional
9-
from urllib.parse import quote
109
from xml.etree import ElementTree as ET
1110

1211
from defusedxml.ElementTree import parse
@@ -146,58 +145,40 @@ def post_transfer(
146145

147146
# Process TIF/TIFF files
148147
if transferred_file.suffix in (".tif", ".tiff"):
149-
150-
# Files should be named "PositionX--ZXX--CXX.tif" by default
151-
# If Position is repeated, it will add an additional --00X to the end
152-
if len(transferred_file.stem.split("--")) not in [3, 4]:
148+
# CLEM TIFF files will have "--Stage", "--C", and/or "--Z" in their file stem
149+
if not any(
150+
pattern in transferred_file.stem
151+
for pattern in ("--Stage", "--Z", "--C")
152+
):
153153
logger.warning(
154154
f"File {transferred_file.name!r} is likely not part of the CLEM workflow"
155155
)
156156
return False
157-
158157
logger.debug(
159158
f"File {transferred_file.name!r} is part of a TIFF image series"
160159
)
161160

162161
# Create a unique name for the series
163-
# For standard file name
164-
if len(transferred_file.stem.split("--")) == 3:
165-
series_name = "--".join(
166-
[
167-
*destination_file.parent.parts[
168-
-2:
169-
], # Upper 2 parent directories
170-
destination_file.stem.split("--")[0],
171-
]
172-
)
173-
# When this a repeated position
174-
elif len(transferred_file.stem.split("--")) == 4:
175-
series_name = "--".join(
176-
[
177-
*destination_file.parent.parts[
178-
-2:
179-
], # Upper 2 parent directories
180-
"--".join(
181-
destination_file.stem.split("--")[i] for i in [0, -1]
182-
),
183-
]
184-
)
185-
else:
186-
logger.error(
187-
f"Series name could not be generated from file {transferred_file.name!r}"
188-
)
189-
return False
190-
logger.debug(
191-
f"File {transferred_file.name!r} given the series name {series_name!r}"
162+
series_name = "--".join(
163+
[
164+
*destination_file.parent.parts[
165+
-2:
166+
], # Upper 2 parent directories
167+
destination_file.stem.split("--")[0].replace(" ", "_"),
168+
]
192169
)
193170

194171
# Create key-value pairs containing empty list if not already present
195172
if series_name not in self._tiff_series.keys():
196173
self._tiff_series[series_name] = []
174+
logger.debug(
175+
f"Created new dictionary entry for TIFF series {series_name!r}"
176+
)
177+
197178
# Append information to list
198179
self._tiff_series[series_name].append(str(destination_file))
199180
logger.debug(
200-
f"Created TIFF file dictionary entries for {series_name!r}"
181+
f"File {transferred_file.name!r} added to series {series_name!r}"
201182
)
202183

203184
# Register the TIFF file in the database
@@ -231,43 +212,40 @@ def post_transfer(
231212
# XLIF files don't have the "--ZXX--CXX" additions in the file name
232213
# But they have "/Metadata/" as the immediate parent
233214
series_name = "--".join(
234-
[*destination_file.parent.parent.parts[-2:], destination_file.stem]
215+
[
216+
*destination_file.parent.parent.parts[-2:],
217+
destination_file.stem.replace(" ", "_"),
218+
]
235219
) # The previous 2 parent directories should be unique enough
236-
logger.debug(
237-
f"File {transferred_file.name!r} given the series name {series_name!r}"
238-
)
239220

240221
# Extract metadata to get the expected size of the series
241222
metadata = parse(transferred_file).getroot()
242223
metadata = _get_image_elements(metadata)[0]
243224

244225
# Get channel and dimension information
245-
channels = metadata.findall(
246-
"Data/Image/ImageDescription/Channels/ChannelDescription"
247-
)
248-
dimensions = metadata.findall(
249-
"Data/Image/ImageDescription/Dimensions/DimensionDescription"
250-
)
226+
channels = metadata.findall(".//ChannelDescription")
227+
dimensions = metadata.findall(".//DimensionDescription")
251228

252229
# Calculate expected number of files for this series
253230
num_channels = len(channels)
254-
num_frames = (
255-
int(dimensions[2].attrib["NumberOfElements"])
256-
if len(dimensions) > 2
257-
else 1
258-
)
259-
num_files = num_channels * num_frames
231+
num_frames = 1
232+
num_tiles = 1
233+
for dim in dimensions:
234+
if dim.attrib["DimID"] == "3":
235+
num_frames = int(dim.attrib["NumberOfElements"])
236+
if dim.attrib["DimID"] == "10":
237+
num_tiles = int(dim.attrib["NumberOfElements"])
238+
num_files = num_channels * num_frames * num_tiles
260239
logger.debug(
261240
f"Expected number of files in {series_name!r}: {num_files}"
262241
)
263242

264243
# Update dictionary entries
265244
self._files_in_series[series_name] = num_files
266245
self._series_metadata[series_name] = str(destination_file)
267-
logger.debug(f"Created dictionary entries for {series_name!r} metadata")
268-
269-
# A new copy of the metadata file is created in 'processed', so no need
270-
# to register this instance of it
246+
logger.debug(
247+
f"File {transferred_file.name!r} added to series {series_name!r}"
248+
)
271249

272250
# Post message if all files for the associated series have been collected
273251
# .get(series_name, 0) returns 0 if no associated key is found
@@ -281,21 +259,26 @@ def post_transfer(
281259
return True
282260
elif len(
283261
self._tiff_series.get(series_name, [])
284-
) == self._files_in_series.get(series_name, 0):
262+
) >= self._files_in_series.get(series_name, 0):
285263
logger.debug(
286264
f"Collected expected number of TIFF files for series {series_name!r}; posting job to server"
287265
)
288266

289267
# Post the message and log any errors that arise
290268
tiff_dataset = {
291269
"series_name": series_name,
292-
"tiff_files": self._tiff_series[series_name],
270+
"tiff_files": self._tiff_series[series_name][0:1],
293271
"series_metadata": self._series_metadata[series_name],
294272
}
295273
post_result = self.process_tiff_series(tiff_dataset, environment)
296274
if post_result is False:
297275
return False
298276
logger.info(f"Started preprocessing of TIFF series {series_name!r}")
277+
278+
# Clean up memory after posting
279+
del self._tiff_series[series_name]
280+
del self._series_metadata[series_name]
281+
del self._files_in_series[series_name]
299282
else:
300283
logger.debug(f"TIFF series {series_name!r} is still being processed")
301284

@@ -361,7 +344,7 @@ def register_lif_file(
361344
function_name="register_lif_file",
362345
token=self._token,
363346
session_id=environment.murfey_session,
364-
data={"lif_file": quote(str(lif_file), safe="")},
347+
data={"lif_file": str(lif_file)},
365348
)
366349
return True
367350
except Exception as e:
@@ -387,7 +370,7 @@ def process_lif_file(
387370
function_name="process_raw_lifs",
388371
token=self._token,
389372
session_id=environment.murfey_session,
390-
data={"lif_file": quote(str(lif_file), safe="")},
373+
data={"lif_file": str(lif_file)},
391374
)
392375
return True
393376
except Exception as e:
@@ -411,7 +394,7 @@ def register_tiff_file(
411394
function_name="register_tiff_file",
412395
token=self._token,
413396
session_id=environment.murfey_session,
414-
data={"tiff_file": quote(str(tiff_file), safe="")},
397+
data={"tiff_file": str(tiff_file)},
415398
)
416399
return True
417400
except Exception as e:

src/murfey/server/api/clem.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -204,13 +204,6 @@ class LifInfo(BaseModel):
204204
child_stacks: list[Path] = []
205205

206206

207-
class TiffInfo(BaseModel):
208-
tiff_file: Path
209-
associated_metadata: Optional[Path] = None
210-
associated_series: Optional[str] = None
211-
associated_stack: Optional[Path] = None
212-
213-
214207
@router.post("/sessions/{session_id}/clem/lif_files")
215208
def register_lif_file(
216209
lif_file: LifInfo,
@@ -314,6 +307,13 @@ def register_lif_file(
314307
return True
315308

316309

310+
class TiffInfo(BaseModel):
311+
tiff_file: Path
312+
associated_metadata: Optional[Path] = None
313+
associated_series: Optional[str] = None
314+
associated_stack: Optional[Path] = None
315+
316+
317317
@router.post("/sessions/{session_id}/clem/tiff_files")
318318
def register_tiff_file(
319319
tiff_file: TiffInfo,

src/murfey/workflows/clem/process_raw_lifs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def zocalo_cluster_request(
4848

4949
# Construct recipe and submit it for processing
5050
recipe = {
51-
"recipes": ["clem-lif-to-stack"],
51+
"recipes": ["clem-process-raw-lifs"],
5252
"parameters": {
5353
# Job parameters
5454
"lif_file": f"{str(file)}",

src/murfey/workflows/clem/process_raw_tiffs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def zocalo_cluster_request(
5555

5656
# Construct recipe and submit it for processing
5757
recipe = {
58-
"recipes": ["clem-tiff-to-stack"],
58+
"recipes": ["clem-process-raw-tiffs"],
5959
"parameters": {
6060
# Job parameters
6161
"tiff_list": "null",

0 commit comments

Comments
 (0)