11import logging
2+ import os
23import traceback
34import xml .etree .ElementTree as ET
45from functools import cached_property
1213from sqlmodel import Session , select
1314
1415import murfey .util .db as MurfeyDB
16+ from murfey .util .config import get_machine_config
1517from murfey .util .fib import number_from_name
1618
1719logger = logging .getLogger ("murfey.workflows.fib.register_atlas" )
@@ -25,6 +27,7 @@ class FIBAtlasMetadata(BaseModel):
2527
2628 visit_name : str
2729 file : Path
30+ thumbnail_path : Path | None = None
2831 # Acceleration voltage
2932 voltage : float
3033 # Beam shifts
@@ -79,16 +82,24 @@ def slot_number(self) -> int:
7982 # mypy doesn't support decorators on @property
8083 @computed_field # type: ignore
8184 @cached_property
82- def site_name (self ) -> str :
85+ def project_name (self ) -> str :
8386 """
84- Create a site name for the current image based on the project name
85- and its slot number. This assumes a specific folder structure of
86- {visit_name}/maps/{project_name}
87+ Extract the project name from the file path. This assumes a specific
88+ folder structure of '{visit_name}/maps/{project_name}'.
8789 """
8890 path_parts = self .file .parts
8991 visit_idx = path_parts .index (self .visit_name )
90- project_name = path_parts [visit_idx + 2 ] # {visit}/maps/{project_name}
91- return f"{ project_name } --slot_{ self .slot_number } "
92+ return path_parts [visit_idx + 2 ] # {visit}/maps/{project_name}
93+
94+ # mypy doesn't support decorators on @property
95+ @computed_field # type: ignore
96+ @cached_property
97+ def site_name (self ) -> str :
98+ """
99+ Create a site name for the current image based on the project name
100+ and its slot number.
101+ """
102+ return f"{ self .project_name } --slot_{ self .slot_number } "
92103
93104
94105def _parse_metadata (file : Path , visit_name : str ):
@@ -146,6 +157,35 @@ def _parse_metadata(file: Path, visit_name: str):
146157 )
147158
148159
160+ def _make_thumbnail (file : Path , metadata : FIBAtlasMetadata , visit_name : str , mode : int ):
161+ img = PIL .Image .open (file )
162+ img .thumbnail ((512 , 512 ))
163+
164+ # Find visit directory path
165+ visit_idx = file .parts .index (visit_name )
166+ visit_dir = list (reversed (file .parents ))[visit_idx ]
167+
168+ # Construct processed directory and set permissions
169+ processed_dir = visit_dir / "processed"
170+ processed_dir .mkdir (exist_ok = True )
171+ os .chmod (processed_dir , mode = mode )
172+
173+ # Construct path to thumbnail
174+ image_number = number_from_name (file .stem )
175+ save_path = (
176+ processed_dir
177+ / metadata .project_name
178+ / f"grid_{ metadata .slot_number } "
179+ / "atlas"
180+ / f"atlas_{ str (image_number ).zfill (2 )} .png"
181+ )
182+ save_path .parent .mkdir (parents = True , exist_ok = True )
183+
184+ # Save the thumbnail
185+ img .save (save_path )
186+ return save_path
187+
188+
149189def _register_fib_imaging_site (
150190 session_id : int ,
151191 metadata : FIBAtlasMetadata ,
@@ -172,6 +212,13 @@ def _update_entry(
172212 imaging_site .image_pixels_y = metadata .pixels_y
173213 imaging_site .image_pixel_size = metadata .pixel_size
174214
215+ if metadata .thumbnail_path is not None :
216+ scale = 512 / (max (metadata .pixels_x , metadata .pixels_y ) or 1 )
217+ imaging_site .thumbnail_path = str (metadata .thumbnail_path )
218+ imaging_site .thumbnail_pixels_x = int (round (metadata .pixels_x * scale )) or 1
219+ imaging_site .thumbnail_pixels_y = int (round (metadata .pixels_y * scale )) or 1
220+ imaging_site .thumbnail_pixel_size = metadata .pixel_size / scale
221+
175222 return imaging_site
176223
177224 if (
@@ -202,7 +249,7 @@ def _update_entry(
202249 else :
203250 current_number = 0
204251 # Update if incoming one is newer
205- if incoming_number > current_number :
252+ if incoming_number >= current_number :
206253 fib_imaging_site = _update_entry (fib_imaging_site , metadata )
207254
208255 murfey_db .add (fib_imaging_site )
@@ -323,6 +370,8 @@ def run(
323370 )
324371 ).one ()
325372 visit_name = murfey_session .visit
373+ instrument_name = murfey_session .instrument_name
374+ machine_config = get_machine_config (instrument_name )[instrument_name ]
326375 except Exception :
327376 logger .error (
328377 "Exception encountered while querying Murfey database" , exc_info = True
@@ -339,6 +388,19 @@ def run(
339388 )
340389 return {"success" : False , "requeue" : False }
341390
391+ try :
392+ # Make a thumbnail of the image and update metadata accordingly
393+ metadata .thumbnail_path = _make_thumbnail (
394+ file = metadata .file ,
395+ metadata = metadata ,
396+ visit_name = visit_name ,
397+ mode = machine_config .mkdir_chmod ,
398+ )
399+ except Exception :
400+ logger .warning (
401+ f"Error creating thumbnail of file { fib_info .atlas_file } " , exc_info = True
402+ )
403+
342404 try :
343405 # Register imaging site in Murfey, or update existing one
344406 fib_imaging_site = _register_fib_imaging_site (
0 commit comments