Skip to content

Commit 61df855

Browse files
committed
Break out the foil hole logic
1 parent 85081c4 commit 61df855

6 files changed

Lines changed: 339 additions & 271 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ TomographyMetadataContext = "murfey.client.contexts.tomo_metadata:TomographyMeta
124124
"spa.ctf_estimated" = "murfey.workflows.spa.ctf_estimation:ctf_estimated"
125125
"spa.flush_spa_preprocess" = "murfey.workflows.spa.flush_spa_preprocess:flush_spa_preprocess"
126126
"spa.motion_corrected" = "murfey.workflows.spa.motion_correction:motion_corrected"
127+
"spa.register_foil_holes" = "murfey.workflows.spa.register_foil_holes:register_foil_holes"
127128

128129
[tool.setuptools]
129130
package-dir = {"" = "src"}

src/murfey/client/contexts/spa_metadata.py

Lines changed: 97 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
import xml.etree.ElementTree as ET
23
from pathlib import Path
34
from typing import Dict, Optional
45

@@ -12,64 +13,84 @@
1213
)
1314
from murfey.client.instance_environment import MurfeyInstanceEnvironment
1415
from murfey.util.client import capture_post
16+
from murfey.util.models import FoilHoleParameters
1517
from murfey.util.spa_metadata import (
16-
FoilHoleInfo,
1718
get_grid_square_atlas_positions,
1819
grid_square_data,
1920
)
2021

2122
logger = logging.getLogger("murfey.client.contexts.spa_metadata")
2223

2324

24-
def _foil_hole_positions(xml_path: Path, grid_square: int) -> Dict[str, FoilHoleInfo]:
25-
with open(xml_path, "r") as xml:
26-
for_parsing = xml.read()
27-
data = xmltodict.parse(for_parsing)
28-
data = data["GridSquareXml"]
29-
if "TargetLocationsEfficient" in data["TargetLocations"].keys():
30-
# Grids with regular foil holes
31-
serialization_array = data["TargetLocations"]["TargetLocationsEfficient"][
32-
"a:m_serializationArray"
33-
]
34-
elif "TargetLocations" in data["TargetLocations"].keys():
35-
# Lacey grids
36-
serialization_array = data["TargetLocations"]["TargetLocations"][
37-
"a:m_serializationArray"
38-
]
39-
else:
25+
def _foil_hole_positions(
26+
xml_path: Path, tag: str, grid_square: int
27+
) -> Dict[str, FoilHoleParameters]:
28+
tree = ET.parse(xml_path)
29+
for elem in tree.iter():
30+
if "}" in elem.tag:
31+
elem.tag = elem.tag.split("}")[1]
32+
tree_root = tree.getroot()
33+
target_locations = tree_root.find("TargetLocations")
34+
serialization_array = None
35+
if target_locations:
36+
if eff_target_locations := target_locations.find("TargetLocationsEfficient"):
37+
# Grids with regular foil holes
38+
serialization_array = eff_target_locations.find("m_serializationArray")
39+
elif eff_target_locations := target_locations.find("TargetLocations"):
40+
# Lacey grids
41+
serialization_array = eff_target_locations.find("m_serializationArray")
42+
if not serialization_array:
4043
logger.warning(f"Target locations not found for {str(xml_path)}")
4144
return {}
42-
required_key = ""
43-
for key in serialization_array.keys():
44-
if key.startswith("b:KeyValuePairOfintTargetLocation"):
45-
required_key = key
46-
break
47-
if not required_key:
48-
logger.info(f"Required key not found for {str(xml_path)}")
49-
return {}
45+
46+
fh_blocks = [
47+
i
48+
for i in serialization_array.iter()
49+
if "KeyValuePairOfintTargetLocationXml" in i.tag
50+
]
5051
foil_holes = {}
51-
for fh_block in serialization_array[required_key]:
52-
if fh_block["b:value"]["IsNearGridBar"] == "false":
53-
image_paths = list(
54-
(xml_path.parent.parent).glob(
55-
f"Images-Disc*/GridSquare_{grid_square}/FoilHoles/FoilHole_{fh_block['b:key']}_*.jpg"
52+
for fh_block in fh_blocks:
53+
fh_block_values = fh_block.find("value")
54+
fh_key = fh_block.find("key")
55+
if fh_key and fh_key.text and fh_block_values:
56+
near_grid_bar = fh_block_values.find("IsNearGridBar")
57+
if near_grid_bar and near_grid_bar.text == "false":
58+
image_paths = list(
59+
(xml_path.parent.parent).glob(
60+
f"Images-Disc*/GridSquare_{grid_square}/FoilHoles/FoilHole_{fh_key.text}_*.jpg"
61+
)
5662
)
57-
)
58-
image_paths.sort(key=lambda x: x.stat().st_ctime)
59-
image_path: str = str(image_paths[-1]) if image_paths else ""
60-
pix_loc = fh_block["b:value"]["PixelCenter"]
61-
stage = fh_block["b:value"]["StagePosition"]
62-
diameter = fh_block["b:value"]["PixelWidthHeight"]["c:width"]
63-
foil_holes[fh_block["b:key"]] = FoilHoleInfo(
64-
id=int(fh_block["b:key"]),
65-
grid_square_id=grid_square,
66-
x_location=int(float(pix_loc["c:x"])),
67-
y_location=int(float(pix_loc["c:y"])),
68-
x_stage_position=float(stage["c:X"]),
69-
y_stage_position=float(stage["c:Y"]),
70-
image=str(image_path),
71-
diameter=int(float(diameter)),
72-
)
63+
image_paths.sort(key=lambda x: x.stat().st_ctime)
64+
image_path: str = str(image_paths[-1]) if image_paths else ""
65+
pix_loc = fh_block_values.find("PixelCenter")
66+
stage = fh_block_values.find("StagePosition")
67+
width_height = fh_block_values.find("PixelWidthHeight")
68+
diameter = width_height.find("width") if width_height else None
69+
if pix_loc and stage:
70+
x_pix = pix_loc.find("x")
71+
y_pix = pix_loc.find("y")
72+
x_stage = stage.find("X")
73+
y_stage = stage.find("Y")
74+
foil_holes[fh_key.text] = FoilHoleParameters(
75+
name=int(fh_key.text),
76+
tag=tag,
77+
x_location=int(float(x_pix.text))
78+
if (x_pix and x_pix.text)
79+
else None,
80+
y_location=int(float(y_pix.text))
81+
if (y_pix and y_pix.text)
82+
else None,
83+
x_stage_position=int(float(x_stage.text))
84+
if (x_stage and x_stage.text)
85+
else None,
86+
y_stage_position=int(float(y_stage.text))
87+
if (y_stage and y_stage.text)
88+
else None,
89+
image=str(image_path),
90+
diameter=int(float(diameter.text))
91+
if (diameter and diameter.text)
92+
else None,
93+
).model_dump()
7394
return foil_holes
7495

7596

@@ -193,7 +214,6 @@ def post_transfer(
193214
logger.info(
194215
f"Collecting foil hole positions for {str(transferred_file)} and grid square {gs_name}"
195216
)
196-
fh_positions = _foil_hole_positions(transferred_file, gs_name)
197217
visitless_source_search_dir = "/".join(
198218
[part for part in source.parts if part != environment.visit]
199219
).replace("//", "/")
@@ -209,6 +229,11 @@ def post_transfer(
209229
Path(visitless_source_search_dir) / "Images-Disc1"
210230
]
211231
visitless_source = str(visitless_source_images_dirs[-1])
232+
fh_positions = _foil_hole_positions(
233+
xml_path=transferred_file,
234+
tag=visitless_source,
235+
grid_square=gs_name,
236+
)
212237

213238
if fh_positions:
214239
gs_info = grid_square_data(
@@ -244,44 +269,28 @@ def post_transfer(
244269
},
245270
)
246271

247-
if gs_name not in self._registered_squares:
248-
for fh, fh_data in fh_positions.items():
249-
capture_post(
250-
base_url=str(environment.url.geturl()),
251-
router_name="session_control.spa_router",
252-
function_name="register_foil_hole",
253-
token=self._token,
254-
instrument_name=environment.instrument_name,
255-
session_id=environment.murfey_session,
256-
gs_name=gs_name,
257-
data={
258-
"name": fh,
259-
"x_location": fh_data.x_location,
260-
"y_location": fh_data.y_location,
261-
"x_stage_position": fh_data.x_stage_position,
262-
"y_stage_position": fh_data.y_stage_position,
263-
"readout_area_x": fh_data.readout_area_x,
264-
"readout_area_y": fh_data.readout_area_y,
265-
"thumbnail_size_x": fh_data.thumbnail_size_x,
266-
"thumbnail_size_y": fh_data.thumbnail_size_y,
267-
"pixel_size": fh_data.pixel_size,
268-
"diameter": fh_data.diameter,
269-
"tag": visitless_source,
270-
"image": fh_data.image,
271-
},
272-
)
273-
if fh_positions:
274-
capture_post(
275-
base_url=str(environment.url.geturl()),
276-
router_name="session_control.spa_router",
277-
function_name="register_square",
278-
token=self._token,
279-
instrument_name=environment.instrument_name,
280-
session_id=environment.murfey_session,
281-
gsid=gs_name,
282-
data={
283-
"tag": visitless_source,
284-
"count": len(self._registered_squares) + 1,
285-
},
286-
)
287-
self._registered_squares.add(gs_name)
272+
if fh_positions and gs_name not in self._registered_squares:
273+
capture_post(
274+
base_url=str(environment.url.geturl()),
275+
router_name="session_control.spa_router",
276+
function_name="register_foil_holes",
277+
token=self._token,
278+
instrument_name=environment.instrument_name,
279+
session_id=environment.murfey_session,
280+
gs_name=gs_name,
281+
data=fh_positions,
282+
)
283+
capture_post(
284+
base_url=str(environment.url.geturl()),
285+
router_name="session_control.spa_router",
286+
function_name="register_square",
287+
token=self._token,
288+
instrument_name=environment.instrument_name,
289+
session_id=environment.murfey_session,
290+
gsid=gs_name,
291+
data={
292+
"tag": visitless_source,
293+
"count": len(self._registered_squares) + 1,
294+
},
295+
)
296+
self._registered_squares.add(gs_name)

src/murfey/server/api/session_control.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@
6464
)
6565
from murfey.workflows.spa.atlas import atlas_jpg_from_mrc
6666
from murfey.workflows.spa.flush_spa_preprocess import (
67-
register_foil_hole as _register_foil_hole,
6867
register_grid_square as _register_grid_square,
6968
)
7069
from murfey.workflows.tomo.tomo_metadata import (
@@ -473,17 +472,25 @@ def register_grid_square(
473472
return _register_grid_square(session_id, gsid, grid_square_params, db)
474473

475474

476-
@spa_router.post("/sessions/{session_id}/grid_square/{gs_name}/foil_hole")
477-
def register_foil_hole(
475+
@spa_router.post("/sessions/{session_id}/grid_square/{gs_name}/foil_holes")
476+
def register_foil_holes(
478477
session_id: MurfeySessionID,
479478
gs_name: int,
480-
foil_hole_params: FoilHoleParameters,
479+
foil_hole_group: dict[str, FoilHoleParameters],
481480
db=murfey_db,
482481
):
483-
logger.info(
484-
f"Registering foil hole {foil_hole_params.name} with position {(foil_hole_params.x_location, foil_hole_params.y_location)}"
485-
)
486-
return _register_foil_hole(session_id, gs_name, foil_hole_params, db)
482+
logger.info(f"Registering foil holes for grid square {gs_name}")
483+
if _transport_object:
484+
_transport_object.send(
485+
_transport_object.feedback_queue,
486+
{
487+
"register": "spa.register_foil_holes",
488+
"session_id": session_id,
489+
"gs_name": gs_name,
490+
"foil_hole_group": foil_hole_group,
491+
},
492+
)
493+
return gs_name
487494

488495

489496
tomo_router = APIRouter(

src/murfey/util/route_manifest.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -941,8 +941,8 @@ murfey.server.api.session_control.spa_router:
941941
type: int
942942
methods:
943943
- POST
944-
- path: /session_control/spa/sessions/{session_id}/grid_square/{gs_name}/foil_hole
945-
function: register_foil_hole
944+
- path: /session_control/spa/sessions/{session_id}/grid_square/{gs_name}/foil_holes
945+
function: register_foil_holes
946946
path_params:
947947
- name: gs_name
948948
type: int

0 commit comments

Comments
 (0)