From 0d2f3243244c5edc8a2f247196e50a82d8511516 Mon Sep 17 00:00:00 2001 From: Julian Stirling Date: Tue, 10 Jun 2025 10:56:14 +0100 Subject: [PATCH] Split creation of scan directory information into class with small testable methods --- .../things/smart_scan.py | 112 ++++++++++++------ 1 file changed, 79 insertions(+), 33 deletions(-) diff --git a/src/openflexure_microscope_server/things/smart_scan.py b/src/openflexure_microscope_server/things/smart_scan.py index b206058a..1720b52a 100644 --- a/src/openflexure_microscope_server/things/smart_scan.py +++ b/src/openflexure_microscope_server/things/smart_scan.py @@ -71,8 +71,8 @@ class ScanInfo(BaseModel): """Summary information about a scan folder""" name: str - created: datetime - modified: datetime + created: float + modified: float number_of_images: int stitch_available: bool dzi: Optional[str] @@ -93,6 +93,77 @@ SCAN_ZERO_PAD_DIGITS = 4 STITCHING_RESOLUTION = (820, 616) +class ScanDirectory: + """A class for handling interactions with scan directories + + Initalisation parameters: + dir_path: the directory of the outer scan directory + """ + + dir_path: str + + def __init__(self, dir_path: str): + if not os.path.isdir(dir_path): + raise FileNotFoundError(f"The scan directory {dir_path} cannot be found") + self.dir_path = dir_path + + @property + def images_dir(self) -> Optional[str]: + """ + The path to the images directory. None is returned is no images directory + was created + """ + im_path = os.path.join(self.dir_path, IMG_DIR_NAME) + if os.path.isdir(im_path): + return im_path + return None + + @property + def created_time(self) -> float: + """The time the directory was created on disk""" + return os.path.getctime(self.dir_path) + + @property + def name(self) -> str: + """The name of the scan directory""" + return os.path.basename(self.dir_path) + + def get_scan_files(self): + """Return a list of the files in the images dir""" + if self.images_dir is None: + return [] + return os.listdir(self.images_dir) + + def get_modified_time(self) -> float: + """Return the modified time of the directory""" + return max(os.stat(root).st_mtime for root, _, _ in os.walk(self.dir_path)) + + def get_scan_info(self): + """Return the inforomation for the scan directory as a ScanInfo object""" + folder_contents = self.get_scan_files() + if folder_contents: + scan_images = [i for i in folder_contents if IMAGE_REGEX.search(i)] + stitches = [i for i in folder_contents if i.endswith("_stitched.jpg")] + dzi_files = [i for i in folder_contents if i.endswith("dzi")] + + number_of_images = len(scan_images) + stitch_available = len(stitches) > 0 + dzi = None if not dzi_files else str(dzi_files[0]) + else: + number_of_images = 0 + stitch_available = False + dzi = None + + return ScanInfo( + name=self.name, + created=self.created_time, + modified=self.get_modified_time(), + number_of_images=number_of_images, + stitch_available=stitch_available, + dzi=dzi, + ) + + def _scan_running(method): """ This decorator is used by all methods in SmartScanThing that are using @@ -812,38 +883,13 @@ class SmartScanThing(Thing): scans: list[ScanInfo] = [] if not os.path.isdir(self.base_scan_dir): return scans - for f in os.listdir(self.base_scan_dir): - path = os.path.join(self.base_scan_dir, f) - if os.path.isdir(path): - images_folder = os.path.join(path, IMG_DIR_NAME) - if os.path.isdir(images_folder): - folder_contents = os.listdir(images_folder) - scan_images = [i for i in folder_contents if IMAGE_REGEX.search(i)] - stitches = [ - i for i in folder_contents if i.endswith("_stitched.jpg") - ] - number_of_images = len(scan_images) - stitch_available = len(stitches) > 0 - dzi = [i for i in folder_contents if i.endswith("dzi")] - if len(dzi) > 0: - dzi = str(dzi[0]) - else: - dzi = None - else: - number_of_images = 0 - stitch_available = False - modified = max(os.stat(root).st_mtime for root, _, _ in os.walk(path)) - scans.append( - ScanInfo( - name=f, - created=os.path.getctime(path), - modified=modified, - number_of_images=number_of_images, - stitch_available=stitch_available, - dzi=dzi, - ) - ) + for scan_dirname in os.listdir(self.base_scan_dir): + scan_path = os.path.join(self.base_scan_dir, scan_dirname) + if os.path.isdir(scan_path): + scan_dir = ScanDirectory(scan_path) + scans.append(scan_dir.get_scan_info()) + return scans @fastapi_endpoint(