Skip to content

Commit 10b61ca

Browse files
committed
Add a way of reading tomo metadata files
1 parent d93f384 commit 10b61ca

File tree

1 file changed

+316
-0
lines changed

1 file changed

+316
-0
lines changed
Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
import logging
2+
from pathlib import Path
3+
from typing import Optional
4+
5+
import requests
6+
import xmltodict
7+
8+
from murfey.client.context import Context
9+
from murfey.client.contexts.spa import _file_transferred_to, _get_source
10+
from murfey.client.contexts.spa_metadata import _atlas_destination
11+
from murfey.client.instance_environment import MurfeyInstanceEnvironment, SampleInfo
12+
from murfey.util.api import url_path_for
13+
from murfey.util.client import authorised_requests, capture_post
14+
15+
logger = logging.getLogger("murfey.client.contexts.tomo_metadata")
16+
17+
requests.get, requests.post, requests.put, requests.delete = authorised_requests()
18+
19+
20+
def get_visitless_source(
21+
transferred_file: Path, environment: MurfeyInstanceEnvironment
22+
) -> Optional[str]:
23+
source = _get_source(transferred_file, environment=environment)
24+
visitless_source_search_dir = str(source).replace(f"/{environment.visit}", "")
25+
visitless_source_images_dirs = sorted(
26+
Path(visitless_source_search_dir).glob("Images-Disc*"),
27+
key=lambda x: x.stat().st_ctime,
28+
)
29+
if not visitless_source_images_dirs:
30+
logger.warning(f"Cannot find Images-Disc* in {visitless_source_search_dir}")
31+
return None
32+
visitless_source = str(visitless_source_images_dirs[-1])
33+
return visitless_source
34+
35+
36+
class TomographyMetadataContext(Context):
37+
def __init__(self, acquisition_software: str, basepath: Path):
38+
super().__init__("Tomography_metadata", acquisition_software)
39+
self._basepath = basepath
40+
41+
def post_transfer(
42+
self,
43+
transferred_file: Path,
44+
environment: Optional[MurfeyInstanceEnvironment] = None,
45+
**kwargs,
46+
):
47+
super().post_transfer(
48+
transferred_file=transferred_file,
49+
environment=environment,
50+
**kwargs,
51+
)
52+
53+
if transferred_file.name == "Session.xml" and environment:
54+
logger.info("Tomography session metadata found")
55+
with open(transferred_file, "r") as session_xml:
56+
session_data = xmltodict.parse(session_xml.read())
57+
58+
windows_path = session_data["TomographySession"]["AtlasId"]["#text"]
59+
logger.info(f"Windows path to atlas metadata found: {windows_path}")
60+
visit_index = windows_path.split("\\").index(environment.visit)
61+
partial_path = "/".join(windows_path.split("\\")[visit_index + 1 :])
62+
logger.info("Partial Linux path successfully constructed from Windows path")
63+
64+
source = _get_source(transferred_file, environment)
65+
if not source:
66+
logger.warning(
67+
f"Source could not be identified for {str(transferred_file)}"
68+
)
69+
return
70+
71+
source_visit_dir = source.parent
72+
73+
logger.info(
74+
f"Looking for atlas XML file in metadata directory {str((source_visit_dir / partial_path).parent)}"
75+
)
76+
atlas_xml_path = list(
77+
(source_visit_dir / partial_path).parent.glob("Atlas_*.xml")
78+
)[0]
79+
logger.info(f"Atlas XML path {str(atlas_xml_path)} found")
80+
with open(atlas_xml_path, "rb") as atlas_xml:
81+
atlas_xml_data = xmltodict.parse(atlas_xml)
82+
atlas_pixel_size = float(
83+
atlas_xml_data["MicroscopeImage"]["SpatialScale"]["pixelSize"]["x"][
84+
"numericValue"
85+
]
86+
)
87+
atlas_binning = int(
88+
atlas_xml_data["MicroscopeImage"]["microscopeData"]["acquisition"][
89+
"camera"
90+
]["Binning"]["a:x"]
91+
)
92+
93+
for p in partial_path.split("/"):
94+
if p.startswith("Sample"):
95+
sample = int(p.replace("Sample", ""))
96+
break
97+
else:
98+
logger.warning(f"Sample could not be identified for {transferred_file}")
99+
return
100+
if source:
101+
environment.samples[source] = SampleInfo(
102+
atlas=Path(partial_path), sample=sample
103+
)
104+
url = f"{str(environment.url.geturl())}{url_path_for('workflow.router', 'register_dc_group', visit_name=environment.visit, session_id=environment.murfey_session)}"
105+
dcg_search_dir = "/".join(
106+
p for p in transferred_file.parent.parts if p != environment.visit
107+
)
108+
dcg_search_dir = (
109+
dcg_search_dir[1:]
110+
if dcg_search_dir.startswith("//")
111+
else dcg_search_dir
112+
)
113+
dcg_images_dirs = sorted(
114+
Path(dcg_search_dir).glob("Images-Disc*"),
115+
key=lambda x: x.stat().st_ctime,
116+
)
117+
if not dcg_images_dirs:
118+
logger.warning(f"Cannot find Images-Disc* in {dcg_search_dir}")
119+
return
120+
dcg_tag = str(dcg_images_dirs[-1])
121+
dcg_data = {
122+
"experiment_type": "tomo",
123+
"experiment_type_id": 36,
124+
"tag": dcg_tag,
125+
"atlas": str(
126+
_atlas_destination(environment, source, transferred_file)
127+
/ environment.samples[source].atlas.parent
128+
/ atlas_xml_path.with_suffix(".jpg").name
129+
),
130+
"sample": environment.samples[source].sample,
131+
"atlas_pixel_size": atlas_pixel_size,
132+
"atlas_binning": atlas_binning,
133+
}
134+
capture_post(url, json=dcg_data)
135+
136+
elif transferred_file.name == "SearchMap.xml" and environment:
137+
logger.info("Tomography session search map xml found")
138+
with open(transferred_file, "r") as sm_xml:
139+
sm_data = xmltodict.parse(sm_xml.read())
140+
141+
# This bit gets SearchMap location on Atlas
142+
sm_pixel_size = float(
143+
sm_data["MicroscopeImage"]["SpatialScale"]["pixelSize"]["x"][
144+
"numericValue"
145+
]
146+
)
147+
stage_position = sm_data["MicroscopeImage"]["microscopeData"]["stage"][
148+
"Position"
149+
]
150+
sm_binning = float(
151+
sm_data["MicroscopeImage"]["microscopeData"]["acquisition"]["camera"][
152+
"Binning"
153+
]["a:x"]
154+
)
155+
readout_area = sm_data["MicroscopeImage"]["microscopeData"]["acquisition"][
156+
"camera"
157+
]["ReadoutArea"]
158+
159+
# Get the stage transformation
160+
sm_transformations = sm_data["MicroscopeImage"]["CustomData"][
161+
"a:KeyValueOfstringanyType"
162+
]
163+
stage_matrix: dict[str, float] = {}
164+
image_matrix: dict[str, float] = {}
165+
for key_val in sm_transformations:
166+
if key_val["a:Key"] == "ReferenceCorrectionForStage":
167+
stage_matrix = {
168+
"m11": float(key_val["a:Value"]["b:_m11"]),
169+
"m12": float(key_val["a:Value"]["b:_m12"]),
170+
"m21": float(key_val["a:Value"]["b:_m21"]),
171+
"m22": float(key_val["a:Value"]["b:_m22"]),
172+
}
173+
elif key_val["a:Key"] == "ReferenceCorrectionForImageShift":
174+
image_matrix = {
175+
"m11": float(key_val["a:Value"]["b:_m11"]),
176+
"m12": float(key_val["a:Value"]["b:_m12"]),
177+
"m21": float(key_val["a:Value"]["b:_m21"]),
178+
"m22": float(key_val["a:Value"]["b:_m22"]),
179+
}
180+
if not stage_matrix or not image_matrix:
181+
print("No matrix found")
182+
183+
ref_matrix = {
184+
"m11": float(
185+
sm_data["MicroscopeImage"]["ReferenceTransformation"]["matrix"][
186+
"a:_m11"
187+
]
188+
),
189+
"m12": float(
190+
sm_data["MicroscopeImage"]["ReferenceTransformation"]["matrix"][
191+
"a:_m12"
192+
]
193+
),
194+
"m21": float(
195+
sm_data["MicroscopeImage"]["ReferenceTransformation"]["matrix"][
196+
"a:_m21"
197+
]
198+
),
199+
"m22": float(
200+
sm_data["MicroscopeImage"]["ReferenceTransformation"]["matrix"][
201+
"a:_m22"
202+
]
203+
),
204+
}
205+
206+
visitless_source = get_visitless_source(transferred_file, environment)
207+
if not visitless_source:
208+
return
209+
210+
sm_url = f"{str(environment.url.geturl())}{url_path_for('session_control.tomography_router', 'register_search_map', session_id=environment.murfey_session, sm_name=transferred_file.stem)}"
211+
source = _get_source(transferred_file, environment=environment)
212+
image_path = (
213+
_file_transferred_to(
214+
environment, source, transferred_file.parent / "SearchMap.jpg"
215+
)
216+
if source
217+
else ""
218+
)
219+
capture_post(
220+
sm_url,
221+
json={
222+
"tag": visitless_source,
223+
"x_stage_position": float(stage_position["X"]),
224+
"y_stage_position": float(stage_position["Y"]),
225+
"readout_area_x": readout_area[0],
226+
"readout_area_y": readout_area[1],
227+
"thumbnail_size_x": int(
228+
(512 / max(readout_area)) * readout_area[0]
229+
),
230+
"thumbnail_size_y": int(
231+
(512 / max(readout_area)) * readout_area[1]
232+
),
233+
"pixel_size": sm_pixel_size,
234+
"image": str(image_path),
235+
"binning": sm_binning,
236+
"reference_matrix": ref_matrix,
237+
"stage_correction": stage_matrix,
238+
"image_shift_correction": image_matrix,
239+
},
240+
)
241+
242+
elif transferred_file.name == "SearchMap.dm" and environment:
243+
logger.info("Tomography session search map dm found")
244+
with open(transferred_file, "r") as sm_xml:
245+
sm_data = xmltodict.parse(sm_xml.read())
246+
247+
visitless_source = get_visitless_source(transferred_file, environment)
248+
if not visitless_source:
249+
return
250+
251+
# This bit gets SearchMap location on Atlas
252+
sm_width = int(sm_data["TileSetXml"]["ImageSize"]["a:width"])
253+
sm_height = int(sm_data["TileSetXml"]["ImageSize"]["a:height"])
254+
255+
sm_url = f"{str(environment.url.geturl())}{url_path_for('session_control.tomography_router', 'register_search_map', session_id=environment.murfey_session, sm_name=transferred_file.stem)}"
256+
capture_post(
257+
sm_url,
258+
json={
259+
"tag": visitless_source,
260+
"height": sm_height,
261+
"width": sm_width,
262+
},
263+
)
264+
265+
elif transferred_file.name == "BatchPositionsList.xml" and environment:
266+
with open(transferred_file) as xml:
267+
for_parsing = xml.read()
268+
batch_xml = xmltodict.parse(for_parsing)
269+
visitless_source = get_visitless_source(transferred_file, environment)
270+
if not visitless_source:
271+
return
272+
273+
for batch_position in batch_xml["BatchPositionsList"]["BatchPositions"][
274+
"BatchPositionParameters"
275+
]:
276+
batch_name = batch_position["Name"]
277+
search_map_name = batch_position["PositionOnTileSet"]["TileSetName"]
278+
batch_stage_location_x = float(
279+
batch_position["PositionOnTileSet"]["StagePositionX"]
280+
)
281+
batch_stage_location_y = float(
282+
batch_position["PositionOnTileSet"]["StagePositionY"]
283+
)
284+
bp_url = f"{str(environment.url.geturl())}{url_path_for('session_control.tomography_router', 'register_batch_position', session_id=environment.murfey_session, batch_name=batch_name)}"
285+
capture_post(
286+
bp_url,
287+
json={
288+
"tag": visitless_source,
289+
"stage_position_x": batch_stage_location_x,
290+
"stage_position_y": batch_stage_location_y,
291+
"search_map": search_map_name,
292+
},
293+
)
294+
295+
# Beamshifts
296+
if batch_position["AdditionalExposureTemplateAreas"]:
297+
beamshifts = batch_position["AdditionalExposureTemplateAreas"][
298+
"ExposureTemplateAreaParameters"
299+
]
300+
if type(beamshifts) is dict:
301+
beamshifts = [beamshifts]
302+
for beamshift in beamshifts:
303+
beamshift_name = beamshift["Name"]
304+
beamshift_position_x = float(beamshift["PositionX"])
305+
beamshift_position_y = float(beamshift["PositionY"])
306+
307+
bp_url = f"{str(environment.url.geturl())}{url_path_for('session_control.tomography_router', 'register_batch_position', session_id=environment.murfey_session, batch_name=beamshift_name)}"
308+
capture_post(
309+
bp_url,
310+
json={
311+
"tag": visitless_source,
312+
"stage_position_x": beamshift_position_x,
313+
"stage_position_y": beamshift_position_y,
314+
"search_map": search_map_name,
315+
},
316+
)

0 commit comments

Comments
 (0)