1010import logging
1111from datetime import datetime
1212from pathlib import Path
13- from typing import Optional
13+ from typing import Dict , List , Optional
14+
15+ from defusedxml .ElementTree import parse
1416
1517from murfey .client .context import Context
1618from murfey .client .instance_environment import MurfeyInstanceEnvironment
17- from murfey .util import capture_post , get_machine_config
19+ from murfey .util import capture_post , get_machine_config , sanitise
20+ from murfey .util .clem import xml
1821
1922# Create logger object
2023logger = logging .getLogger ("murfey.client.contexts.clem" )
@@ -50,10 +53,20 @@ def _get_source(
5053 return None
5154
5255
56+ # WORK IN PROGRESS
57+ # Will need to add context for TIFF files associated with CLEM
5358class CLEMContext (Context ):
5459 def __init__ (self , acquisition_software : str , basepath : Path ):
5560 super ().__init__ ("CLEM" , acquisition_software )
5661 self ._basepath = basepath
62+ # CLEM contexts for "auto-save" acquisition mode
63+ self ._tiff_series : Dict [str , List [str ]] = {} # Series name : List of TIFF paths
64+ self ._tiff_timestamps : Dict [str , List [float ]] = {} # Series name: Timestamps
65+ self ._tiff_sizes : Dict [str , List [int ]] = {} # Series name: File sizes
66+ self ._series_metadata : Dict [str , str ] = {} # Series name: Metadata file path
67+ self ._metadata_timestamp : Dict [str , float ] = {} # Series name: Timestamp
68+ self ._metadata_size : Dict [str , int ] = {} # Series name: File size
69+ self ._files_in_series : Dict [str , int ] = {} # Series name : Total TIFFs
5770
5871 def post_transfer (
5972 self ,
@@ -65,7 +78,126 @@ def post_transfer(
6578 super ().post_transfer (
6679 transferred_file , role = role , environment = environment , ** kwargs
6780 )
68- # Check if file is a LIF file
81+
82+ # Process files generated by "auto-save" acquisition mode
83+ # These include TIF/TIFF and XLIF files
84+ if transferred_file .suffix in (".tif" , ".tiff" , ".xlif" ):
85+ # Type checking to satisfy MyPy
86+ if not environment :
87+ logger .warning ("No environment passed in" )
88+ return True
89+
90+ # Location of the file on the client PC
91+ source = _get_source (transferred_file , environment )
92+ # Type checking to satisfy MyPy
93+ if not source :
94+ logger .warning (f"No source found for file { transferred_file } " )
95+ return True
96+
97+ # Get the Path on the DLS file system
98+ file_path = _file_transferred_to (
99+ environment = environment ,
100+ source = source ,
101+ file_path = transferred_file ,
102+ )
103+ if not file_path :
104+ logger .warning (
105+ f"File associated with { sanitise (str (transferred_file ))} not found on the storage system"
106+ )
107+ return False
108+
109+ # Process TIF/TIFF files
110+ if any (transferred_file .suffix == s for s in [".tif" , ".tiff" ]):
111+ # Files should be named "PositionX--ZXX--CXX.tif" by default
112+ if not len (transferred_file .stem .split ("--" )) == 3 :
113+ logger .warning (
114+ "This TIFF file is likely not part of the CLEM workflow"
115+ )
116+ return False # Not sure if None, False, or True is most appropriate
117+
118+ # Get series name from file name
119+ series_name = "/" .join (
120+ [* file_path .parent .parts [- 2 :], file_path .stem .split ("--" )[0 ]]
121+ ) # The previous 2 parent directories should be unique enough
122+
123+ # Create key-value pairs containing empty list if not already present
124+ if series_name not in self ._tiff_series .keys ():
125+ self ._tiff_series [series_name ] = []
126+ if series_name not in self ._tiff_sizes .keys ():
127+ self ._tiff_sizes [series_name ] = []
128+ if series_name not in self ._tiff_timestamps .keys ():
129+ self ._tiff_timestamps [series_name ] = []
130+ # Append information to list
131+ self ._tiff_series [series_name ].append (str (file_path ))
132+ self ._tiff_sizes [series_name ].append (transferred_file .stat ().st_size )
133+ self ._tiff_timestamps [series_name ].append (
134+ transferred_file .stat ().st_ctime
135+ )
136+
137+ # Process XLIF files
138+ if transferred_file .suffix == ".xlif" :
139+
140+ # XLIF files don't have the "--ZXX--CXX" additions in the file name
141+ # But they have "/Metadata/" as the immediate parent
142+ series_name = "/" .join (
143+ [* file_path .parent .parent .parts [- 2 :], file_path .stem ]
144+ ) # The previous 2 parent directories should be unique enough
145+
146+ # Extract metadata to get the expected size of the series
147+ metadata = parse (file_path ).getroot ()
148+ metadata = xml .get_image_elements (metadata )[0 ]
149+
150+ # Get channel and dimension information
151+ channels = metadata .findall (
152+ "Data/Image/ImageDescription/Channels/ChannelDescription"
153+ )
154+ dimensions = metadata .findall (
155+ "Data/Image/ImageDescription/Dimensions/DimensionDescription"
156+ )
157+
158+ # Calculate expected number of files for this series
159+ num_channels = len (channels )
160+ num_frames = (
161+ int (dimensions [2 ].attrib ["NumberOfElements" ])
162+ if len (dimensions ) > 2
163+ else 1
164+ )
165+ num_files = num_channels * num_frames
166+
167+ # Update dictionary entries
168+ self ._files_in_series [series_name ] = num_files
169+ self ._series_metadata [series_name ] = str (file_path )
170+ self ._metadata_size [series_name ] = transferred_file .stat ().st_size
171+ self ._metadata_timestamp [series_name ] = transferred_file .stat ().st_ctime
172+
173+ # Post message if all files for the associated series have been collected
174+ if (
175+ len (self ._tiff_series [series_name ])
176+ == self ._files_in_series [series_name ]
177+ ):
178+
179+ # Construct URL for Murfey server to communicate with
180+ url = f"{ str (environment .url .geturl ())} /sessions/{ environment .murfey_session } /tiff_to_stack"
181+ if not url :
182+ logger .warning ("No URL found for the environment" )
183+ return True
184+
185+ # Post the message and log any errors that arise
186+ capture_post (
187+ url ,
188+ json = {
189+ "series_name" : series_name ,
190+ "tiff_files" : self ._tiff_series [series_name ],
191+ "tiff_sizes" : self ._tiff_sizes [series_name ],
192+ "tiff_timestamps" : self ._tiff_timestamps [series_name ],
193+ "series_metadata" : self ._series_metadata [series_name ],
194+ "metadata_size" : self ._metadata_size [series_name ],
195+ "metadata_timestamp" : self ._metadata_timestamp [series_name ],
196+ "description" : "" ,
197+ },
198+ )
199+
200+ # Process LIF files
69201 if transferred_file .suffix == ".lif" :
70202 # Type checking to satisfy MyPy
71203 if not environment :
@@ -83,7 +215,7 @@ def post_transfer(
83215 url = f"{ str (environment .url .geturl ())} /sessions/{ environment .murfey_session } /lif_to_tiff"
84216 # Type checking to satisfy MyPy
85217 if not url :
86- logger .warning ("No url found for the environment" )
218+ logger .warning ("No URL found for the environment" )
87219 return True
88220
89221 # Get the Path on the DLS file system
0 commit comments