Skip to content

Commit 01403d8

Browse files
committed
Initial setup for sxt context
1 parent bdcb6d6 commit 01403d8

9 files changed

Lines changed: 396 additions & 47 deletions

File tree

src/murfey/client/analyser.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from murfey.client.contexts.clem import CLEMContext
2020
from murfey.client.contexts.spa import SPAModularContext
2121
from murfey.client.contexts.spa_metadata import SPAMetadataContext
22+
from murfey.client.contexts.sxt import SXTContext
2223
from murfey.client.contexts.tomo import TomographyContext
2324
from murfey.client.contexts.tomo_metadata import TomographyMetadataContext
2425
from murfey.client.destinations import find_longest_data_directory
@@ -110,8 +111,8 @@ def _find_extension(self, file_path: Path) -> bool:
110111
if subframe_path := mdoc_data_block.get("SubFramePath"):
111112
self._extension = Path(subframe_path).suffix
112113
return True
113-
# Check for LIF files separately
114-
elif file_path.suffix == ".lif":
114+
# Check for LIF files and TXRM files separately
115+
elif file_path.suffix == ".lif" or file_path.suffix == ".txrm":
115116
self._extension = file_path.suffix
116117
return True
117118
return False
@@ -138,6 +139,11 @@ def _find_context(self, file_path: Path) -> bool:
138139
self._context = CLEMContext("leica", self._basepath, self._token)
139140
return True
140141

142+
# SXT workflow checks
143+
if file_path.suffix == ".txrm":
144+
self._context = SXTContext("zeiss", self._basepath, self._token)
145+
return True
146+
141147
# Tomography and SPA workflow checks
142148
if "atlas" in file_path.parts:
143149
self._context = AtlasContext(
@@ -321,6 +327,10 @@ def _analyse(self):
321327
)
322328
self.post_transfer(transferred_file)
323329

330+
elif isinstance(self._context, SXTContext):
331+
logger.debug(f"File {transferred_file.name!r} is an SXT file")
332+
self.post_transfer(transferred_file)
333+
324334
elif isinstance(self._context, AtlasContext):
325335
logger.debug(f"File {transferred_file.name!r} is part of the atlas")
326336
self.post_transfer(transferred_file)

src/murfey/client/context.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,44 @@
1313
logger = logging.getLogger("murfey.client.context")
1414

1515

16+
def _file_transferred_to(
17+
environment: MurfeyInstanceEnvironment, source: Path, file_path: Path, token: str
18+
):
19+
machine_config = get_machine_config_client(
20+
str(environment.url.geturl()),
21+
token,
22+
instrument_name=environment.instrument_name,
23+
)
24+
if environment.visit in environment.default_destinations[source]:
25+
return (
26+
Path(machine_config.get("rsync_basepath", ""))
27+
/ Path(environment.default_destinations[source])
28+
/ file_path.relative_to(source) # need to strip out the rsync_module name
29+
)
30+
return (
31+
Path(machine_config.get("rsync_basepath", ""))
32+
/ Path(environment.default_destinations[source])
33+
/ environment.visit
34+
/ file_path.relative_to(source)
35+
)
36+
37+
38+
def _get_source(file_path: Path, environment: MurfeyInstanceEnvironment) -> Path | None:
39+
possible_sources = []
40+
for s in environment.sources:
41+
if file_path.is_relative_to(s):
42+
possible_sources.append(s)
43+
if not possible_sources:
44+
return None
45+
elif len(possible_sources) == 1:
46+
return possible_sources[0]
47+
source = possible_sources[0]
48+
for extra_source in possible_sources[1:]:
49+
if extra_source.is_relative_to(source):
50+
source = extra_source
51+
return source
52+
53+
1654
def _atlas_destination(
1755
environment: MurfeyInstanceEnvironment, source: Path, token: str
1856
) -> Path:

src/murfey/client/contexts/atlas.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44

55
import xmltodict
66

7-
from murfey.client.context import Context, _atlas_destination
8-
from murfey.client.contexts.spa import _get_source
7+
from murfey.client.context import Context, _atlas_destination, _get_source
98
from murfey.client.instance_environment import MurfeyInstanceEnvironment
109
from murfey.util.client import capture_post
1110

src/murfey/client/contexts/spa.py

Lines changed: 6 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@
77

88
import xmltodict
99

10-
from murfey.client.context import Context, ProcessingParameter
10+
from murfey.client.context import (
11+
Context,
12+
ProcessingParameter,
13+
_file_transferred_to,
14+
_get_source,
15+
)
1116
from murfey.client.destinations import find_longest_data_directory
1217
from murfey.client.instance_environment import (
1318
MovieTracker,
@@ -26,28 +31,6 @@
2631
logger = logging.getLogger("murfey.client.contexts.spa")
2732

2833

29-
def _file_transferred_to(
30-
environment: MurfeyInstanceEnvironment, source: Path, file_path: Path, token: str
31-
):
32-
machine_config = get_machine_config_client(
33-
str(environment.url.geturl()),
34-
token,
35-
instrument_name=environment.instrument_name,
36-
)
37-
if environment.visit in environment.default_destinations[source]:
38-
return (
39-
Path(machine_config.get("rsync_basepath", ""))
40-
/ Path(environment.default_destinations[source])
41-
/ file_path.relative_to(source) # need to strip out the rsync_module name
42-
)
43-
return (
44-
Path(machine_config.get("rsync_basepath", ""))
45-
/ Path(environment.default_destinations[source])
46-
/ environment.visit
47-
/ file_path.relative_to(source)
48-
)
49-
50-
5134
def _grid_square_metadata_file(
5235
f: Path, data_directories: list[Path], visit: str, grid_square: int
5336
) -> Path:
@@ -66,22 +49,6 @@ def _grid_square_metadata_file(
6649
return metadata_file
6750

6851

69-
def _get_source(file_path: Path, environment: MurfeyInstanceEnvironment) -> Path | None:
70-
possible_sources = []
71-
for s in environment.sources:
72-
if file_path.is_relative_to(s):
73-
possible_sources.append(s)
74-
if not possible_sources:
75-
return None
76-
elif len(possible_sources) == 1:
77-
return possible_sources[0]
78-
source = possible_sources[0]
79-
for extra_source in possible_sources[1:]:
80-
if extra_source.is_relative_to(source):
81-
source = extra_source
82-
return source
83-
84-
8552
def _get_xml_list_index(key: str, xml_list: list) -> int:
8653
for i, elem in enumerate(xml_list):
8754
if elem["a:Key"] == key:

src/murfey/client/contexts/spa_metadata.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,12 @@
44

55
import xmltodict
66

7-
from murfey.client.context import Context, ensure_dcg_exists
8-
from murfey.client.contexts.spa import _file_transferred_to, _get_source
7+
from murfey.client.context import (
8+
Context,
9+
_file_transferred_to,
10+
_get_source,
11+
ensure_dcg_exists,
12+
)
913
from murfey.client.instance_environment import MurfeyInstanceEnvironment
1014
from murfey.util.client import capture_post
1115
from murfey.util.spa_metadata import (

src/murfey/client/contexts/sxt.py

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
import logging
2+
from pathlib import Path
3+
from typing import Any
4+
5+
from txrm2tiff.inspector import Inspector
6+
from txrm2tiff.txrm import open_txrm
7+
from txrm2tiff.txrm_functions.general import read_stream
8+
from txrm2tiff.xradia_properties.enums import XrmDataTypes
9+
10+
from murfey.client.context import (
11+
Context,
12+
_file_transferred_to,
13+
_get_source,
14+
ensure_dcg_exists,
15+
)
16+
from murfey.client.instance_environment import MurfeyInstanceEnvironment
17+
from murfey.util.client import capture_post
18+
from murfey.util.tomo import midpoint
19+
20+
logger = logging.getLogger("murfey.client.contexts.sxt")
21+
22+
23+
class SXTContext(Context):
24+
def __init__(self, acquisition_software: str, basepath: Path, token: str):
25+
super().__init__("SXT", acquisition_software, token)
26+
self._basepath = basepath
27+
28+
def register_sxt_data_collection(
29+
self,
30+
tilt_series: str,
31+
data_collection_parameters: dict,
32+
file_extension: str,
33+
image_directory: str | Path,
34+
environment: MurfeyInstanceEnvironment | None = None,
35+
):
36+
if not environment:
37+
logger.error(
38+
"No environment passed to register tomography data collections"
39+
)
40+
return
41+
try:
42+
metadata_source = (
43+
self._basepath.parent / environment.visit / self._basepath.name
44+
)
45+
ensure_dcg_exists(
46+
collection_type="sxt",
47+
metadata_source=metadata_source,
48+
environment=environment,
49+
token=self._token,
50+
)
51+
52+
dc_data: dict[str, Any] = {
53+
"experiment_type": "sxt",
54+
"file_extension": file_extension,
55+
"acquisition_software": self._acquisition_software,
56+
"image_directory": str(image_directory),
57+
"data_collection_tag": tilt_series,
58+
"source": str(self._basepath),
59+
"tag": tilt_series,
60+
}
61+
62+
# Once mdoc parameters are known register processing jobs
63+
dc_data.update(
64+
{
65+
"pixel_size_on_image": data_collection_parameters.get(
66+
"pixel_size_on_image"
67+
),
68+
"image_size_x": data_collection_parameters.get("image_size_x"),
69+
"image_size_y": data_collection_parameters.get("image_size_y"),
70+
"magnification": data_collection_parameters.get("magnification"),
71+
}
72+
)
73+
capture_post(
74+
base_url=str(environment.url.geturl()),
75+
router_name="workflow.router",
76+
function_name="start_dc",
77+
token=self._token,
78+
visit_name=environment.visit,
79+
session_id=environment.murfey_session,
80+
data=dc_data,
81+
)
82+
83+
recipes_to_assign_pjids = [
84+
"sxt-tomo-align",
85+
]
86+
for recipe in recipes_to_assign_pjids:
87+
capture_post(
88+
base_url=str(environment.url.geturl()),
89+
router_name="workflow.router",
90+
function_name="register_proc",
91+
token=self._token,
92+
visit_name=environment.visit,
93+
session_id=environment.murfey_session,
94+
data={
95+
"tag": tilt_series,
96+
"source": str(self._basepath),
97+
"recipe": recipe,
98+
"experiment_type": "sxt",
99+
},
100+
)
101+
except Exception as e:
102+
logger.error(f"ERROR {e}, {data_collection_parameters}", exc_info=True)
103+
104+
def post_transfer(
105+
self,
106+
transferred_file: Path,
107+
environment: MurfeyInstanceEnvironment | None = None,
108+
**kwargs,
109+
) -> bool:
110+
super().post_transfer(
111+
transferred_file=transferred_file,
112+
environment=environment,
113+
**kwargs,
114+
)
115+
116+
data_suffixes = [".txrm"]
117+
118+
if transferred_file.suffix in data_suffixes and environment:
119+
source = _get_source(transferred_file, environment)
120+
if not source:
121+
logger.warning(f"No source found for file {transferred_file}")
122+
return False
123+
124+
# Read the tilt angles and pixel size from the txrm
125+
metadata = {
126+
"source": str(self._basepath),
127+
"tilt_series_tag": transferred_file.stem,
128+
}
129+
with open_txrm(
130+
transferred_file, load_images=False, load_reference=False, strict=False
131+
) as txrm:
132+
inspector = Inspector(txrm)
133+
angles = read_stream(
134+
inspector.txrm.ole,
135+
"ImageInfo/Angles",
136+
XrmDataTypes.XRM_FLOAT,
137+
strict=True,
138+
)
139+
metadata["minimum_angle"] = min(angles)
140+
metadata["maximum_angle"] = max(angles)
141+
metadata["pixel_size_microns"] = read_stream(
142+
inspector.txrm.ole,
143+
"ImageInfo/PixelSize",
144+
XrmDataTypes.XRM_FLOAT,
145+
strict=True,
146+
)[0]
147+
metadata["image_size_x"] = read_stream(
148+
inspector.txrm.ole,
149+
"ImageInfo/ImageWidth",
150+
XrmDataTypes.XRM_INT,
151+
strict=True,
152+
)[0]
153+
metadata["image_size_y"] = read_stream(
154+
inspector.txrm.ole,
155+
"ImageInfo/ImageHeight",
156+
XrmDataTypes.XRM_INT,
157+
strict=True,
158+
)[0]
159+
metadata["exposure_time"] = read_stream(
160+
inspector.txrm.ole,
161+
"ImageInfo/ExpTime",
162+
XrmDataTypes.XRM_FLOAT,
163+
strict=True,
164+
)
165+
metadata["magnification"] = read_stream(
166+
inspector.txrm.ole,
167+
"ImageInfo/XrayMagnification",
168+
XrmDataTypes.XRM_FLOAT,
169+
strict=True,
170+
)
171+
metadata["tilt_count"] = read_stream(
172+
inspector.txrm.ole,
173+
"ImageInfo/ImagesTaken",
174+
XrmDataTypes.XRM_INT,
175+
strict=True,
176+
)[0]
177+
178+
self.register_sxt_data_collection(
179+
tilt_series=transferred_file.stem,
180+
data_collection_parameters=metadata,
181+
file_extension=transferred_file.suffix,
182+
image_directory=environment.default_destinations.get(
183+
transferred_file.parent, transferred_file.parent
184+
),
185+
environment=environment,
186+
)
187+
188+
logger.info(
189+
f"The following tilt series will be processed: {transferred_file.stem}"
190+
)
191+
file_transferred_to = _file_transferred_to(
192+
environment, source, transferred_file, self._token
193+
)
194+
capture_post(
195+
base_url=str(environment.url.geturl()),
196+
router_name="workflow.sxt_router",
197+
function_name="process_sxt_tilt_series",
198+
token=self._token,
199+
visit_name=environment.visit,
200+
session_id=environment.murfey_session,
201+
data={
202+
"session_id": environment.murfey_session,
203+
"tag": transferred_file.stem,
204+
"source": str(transferred_file.parent),
205+
"pixel_size": metadata.get("pixel_size", 100),
206+
"tilt_offset": midpoint(angles),
207+
"txrm": str(file_transferred_to),
208+
},
209+
)
210+
return True
211+
212+
def post_first_transfer(
213+
self,
214+
transferred_file: Path,
215+
environment: MurfeyInstanceEnvironment | None = None,
216+
**kwargs,
217+
):
218+
self.post_transfer(transferred_file, environment=environment, **kwargs)

0 commit comments

Comments
 (0)