1+ import xml .etree .ElementTree as ET
12from pathlib import Path
3+ from unittest .mock import MagicMock
24
35import pytest
46from pytest_mock import MockerFixture
57from sqlmodel import Session , select
68
79import murfey .util .db as MurfeyDB
8- from murfey .workflows .fib .register_atlas import FIBAtlasMetadata , run
10+ from murfey .workflows .fib .register_atlas import FIBAtlasMetadata , _parse_metadata , run
911
1012session_id = 10
1113visit_name = "cm12345-6"
@@ -19,8 +21,259 @@ def visit_dir(tmp_path: Path):
1921 return visit_dir
2022
2123
22- def test_parse_metadata ():
23- pass
24+ def create_electron_snapshot_metadata (
25+ voltage : float ,
26+ shift_x : float ,
27+ shift_y : float ,
28+ len_x : float ,
29+ len_y : float ,
30+ pos_x : float ,
31+ pos_y : float ,
32+ pos_z : float ,
33+ rotation : float ,
34+ tilt_alpha : float ,
35+ tilt_beta : float ,
36+ pixels_x : int ,
37+ pixels_y : int ,
38+ pixel_size_x : float ,
39+ pixel_size_y : float ,
40+ ):
41+ # Create the XML Element structure present in the file
42+ root = ET .Element ("Metadata" )
43+
44+ # ------
45+ # Optics
46+ # ------
47+ optics_node = ET .Element ("Optics" )
48+
49+ voltage_node = ET .Element ("AccelerationVoltage" )
50+ voltage_node .text = str (voltage )
51+ optics_node .append (voltage_node )
52+
53+ beam_shift_node = ET .Element ("BeamShift" )
54+ shift_x_node = ET .Element ("X" )
55+ shift_x_node .text = str (shift_x )
56+ beam_shift_node .append (shift_x_node )
57+ shift_y_node = ET .Element ("Y" )
58+ shift_y_node .text = str (shift_y )
59+ beam_shift_node .append (shift_y_node )
60+ optics_node .append (beam_shift_node )
61+
62+ fov_node = ET .Element ("ScanFieldOfView" )
63+ len_x_node = ET .Element ("X" )
64+ len_x_node .text = str (len_x )
65+ fov_node .append (len_x_node )
66+ len_y_node = ET .Element ("Y" )
67+ len_y_node .text = str (len_y )
68+ fov_node .append (len_y_node )
69+ optics_node .append (fov_node )
70+
71+ root .append (optics_node )
72+
73+ # -------------
74+ # StageSettings
75+ # -------------
76+ stage_settings_node = ET .Element ("StageSettings" )
77+ # x, y, z
78+ stage_node = ET .Element ("StagePosition" )
79+ pos_x_node = ET .Element ("X" )
80+ pos_x_node .text = str (pos_x )
81+ stage_node .append (pos_x_node )
82+ pos_y_node = ET .Element ("Y" )
83+ pos_y_node .text = str (pos_y )
84+ stage_node .append (pos_y_node )
85+ pos_z_node = ET .Element ("Z" )
86+ pos_z_node .text = str (pos_z )
87+ stage_node .append (pos_z_node )
88+ rotation_node = ET .Element ("Rotation" )
89+ rotation_node .text = str (rotation )
90+ stage_node .append (rotation_node )
91+ # Angles
92+ tilt_node = ET .Element ("Tilt" )
93+ tilt_alpha_node = ET .Element ("Alpha" )
94+ tilt_alpha_node .text = str (tilt_alpha )
95+ tilt_node .append (tilt_alpha_node )
96+ tilt_beta_node = ET .Element ("Beta" )
97+ tilt_beta_node .text = str (tilt_beta )
98+ tilt_node .append (tilt_beta_node )
99+ stage_node .append (tilt_node )
100+
101+ stage_settings_node .append (stage_node )
102+ root .append (stage_settings_node )
103+
104+ # ------------
105+ # BinaryResult
106+ # ------------
107+ binary_result_node = ET .Element ("BinaryResult" )
108+ # ImageSize
109+ image_size_node = ET .Element ("ImageSize" )
110+ pixels_x_node = ET .Element ("X" )
111+ pixels_x_node .text = str (pixels_x )
112+ image_size_node .append (pixels_x_node )
113+ pixels_y_node = ET .Element ("Y" )
114+ pixels_y_node .text = str (pixels_y )
115+ image_size_node .append (pixels_y_node )
116+ binary_result_node .append (image_size_node )
117+ # PixelSize
118+ pixel_size_node = ET .Element ("PixelSize" )
119+ pixel_size_x_node = ET .Element ("X" )
120+ pixel_size_x_node .text = str (pixel_size_x )
121+ pixel_size_node .append (pixel_size_x_node )
122+ pixel_size_y_node = ET .Element ("Y" )
123+ pixel_size_y_node .text = str (pixel_size_y )
124+ pixel_size_node .append (pixel_size_y_node )
125+ binary_result_node .append (pixel_size_node )
126+
127+ root .append (binary_result_node )
128+
129+ xml_string = ET .tostring (root , encoding = "unicode" , xml_declaration = True )
130+ return xml_string
131+
132+
133+ @pytest .mark .parametrize (
134+ "test_params" ,
135+ (
136+ (
137+ "Electron Snapshot" ,
138+ "some_project" ,
139+ 2000 , # Voltage
140+ 0 , # Beam shift X
141+ 0 , # Y
142+ 0.003072 , # Field of view X
143+ 0.002048 , # Y
144+ 0.003 , # Stage X
145+ 0.0003 , # Y
146+ 0.01 , # Z
147+ - 1.309 , # Rotation
148+ 0.8 , # Alpha tilt
149+ 0 , # Beta tilt
150+ 3072 , # Image size X
151+ 2048 , # Y
152+ 1e-6 , # Pixel size X
153+ 1e-6 , # Y
154+ ),
155+ (
156+ "Electron Snapshot (2)" ,
157+ "another_project" ,
158+ 2000 , # Voltage
159+ 0 , # Beam shift X
160+ 0 , # Y
161+ 0.003072 , # Field of view X
162+ 0.002048 , # Y
163+ - 0.003 , # Stage X
164+ 0.0003 , # Y
165+ 0.01 , # Z
166+ 1.309 , # Rotation
167+ 0 , # Alpha tilt
168+ 0 , # Beta tilt
169+ 3072 , # Image size X
170+ 2048 , # Y
171+ 1e-6 , # Pixel size X
172+ 1e-6 , # Y
173+ ),
174+ ),
175+ )
176+ def test_parse_metadata (
177+ mocker : MockerFixture ,
178+ test_params : tuple [
179+ str ,
180+ str ,
181+ float ,
182+ float ,
183+ float ,
184+ float ,
185+ float ,
186+ float ,
187+ float ,
188+ float ,
189+ float ,
190+ float ,
191+ float ,
192+ int ,
193+ int ,
194+ float ,
195+ float ,
196+ ],
197+ visit_dir : Path ,
198+ ):
199+ # Unpack test params
200+ (
201+ image_name ,
202+ project_name ,
203+ voltage ,
204+ shift_x ,
205+ shift_y ,
206+ len_x ,
207+ len_y ,
208+ pos_x ,
209+ pos_y ,
210+ pos_z ,
211+ rotation ,
212+ tilt_alpha ,
213+ tilt_beta ,
214+ pixels_x ,
215+ pixels_y ,
216+ pixel_size_x ,
217+ pixel_size_y ,
218+ ) = test_params
219+ file = (
220+ visit_dir
221+ / "maps"
222+ / project_name
223+ / "LayersData/Layer"
224+ / image_name
225+ / f"{ image_name } .tiff"
226+ )
227+ slot_number = 1 if pos_x < 0 else 2
228+
229+ # Mock the results of opening an image file
230+ xml_string = create_electron_snapshot_metadata (
231+ voltage ,
232+ shift_x ,
233+ shift_y ,
234+ len_x ,
235+ len_y ,
236+ pos_x ,
237+ pos_y ,
238+ pos_z ,
239+ rotation ,
240+ tilt_alpha ,
241+ tilt_beta ,
242+ pixels_x ,
243+ pixels_y ,
244+ pixel_size_x ,
245+ pixel_size_y ,
246+ )
247+ mock_image = MagicMock ()
248+ mock_image .tag_v2 = {34683 : xml_string }
249+ mocker .patch (
250+ "murfey.workflows.fib.register_atlas.PIL.Image.open" ,
251+ return_value = mock_image ,
252+ )
253+
254+ # Run the function and check that output is correct
255+ parsed = _parse_metadata (file , visit_name )
256+
257+ assert parsed .visit_name == visit_name
258+ assert parsed .file == file
259+ assert parsed .voltage == voltage
260+ assert parsed .shift_x == shift_x
261+ assert parsed .shift_y == shift_y
262+ assert parsed .len_x == len_x
263+ assert parsed .len_y == len_y
264+ assert parsed .pos_x == pos_x
265+ assert parsed .pos_y == pos_y
266+ assert parsed .pos_z == pos_z
267+ assert parsed .rotation == rotation
268+ assert parsed .tilt_alpha == tilt_alpha
269+ assert parsed .tilt_beta == tilt_beta
270+ assert parsed .pixels_x == pixels_x
271+ assert parsed .pixels_y == pixels_y
272+ assert parsed .pixel_size_x == pixel_size_x
273+ assert parsed .pixel_size_y == pixel_size_y
274+ assert parsed .slot_number == slot_number
275+ assert parsed .site_name == f"{ project_name } --slot_{ slot_number } "
276+ assert parsed .pixel_size == 0.5 * (pixel_size_x + pixel_size_y )
24277
25278
26279def test_register_fib_imaging_site ():
0 commit comments