Merge branch 'cleanup_empty_scans' into 'v3'

Added purge_empty_scans() method

Closes #370

See merge request openflexure/openflexure-microscope-server!257
This commit is contained in:
Julian Stirling 2025-05-20 18:26:00 +00:00
commit c793620fcd
2 changed files with 58 additions and 9 deletions

View file

@ -588,6 +588,9 @@ class SmartScanThing(Thing):
self._return_to_starting_position()
self._perform_final_stitch()
# Remove any scan folders containing zero images
self.purge_empty_scans(logger=self._scan_logger)
@_scan_running
def _main_scan_loop(self):
"""
@ -868,22 +871,26 @@ class SmartScanThing(Thing):
404: {"description": "Scan not found"},
},
)
def delete_scan(self, scan_name: str) -> None:
"""Delete all files from a scan.
def delete_scan(self, scan_name: str, logger: InvocationLogger) -> None:
"""Delete the folder for the specified scan.
This endpoint allows scans to be deleted from disk.
Takes the scan name to delete, and the Invocation Logger
"""
path = os.path.join(self.base_scan_dir, scan_name)
if not os.path.isdir(path):
print(f"can't find {path}")
raise HTTPException(404, "Scan not found")
shutil.rmtree(path)
logger.info(f"can't find {path}")
raise HTTPException(400, "Scan not found")
deleted_scan_success = self._delete_scan(path, logger)
if not deleted_scan_success:
raise HTTPException(400, "Couldn't delete scan, check log for details")
@fastapi_endpoint(
"delete",
"scans",
)
def delete_all_scans(self) -> None:
def delete_all_scans(self, logger: InvocationLogger) -> None:
"""Delete all the scans on the microscope
**This will irreversibly remove all scanned data from the
@ -891,7 +898,20 @@ class SmartScanThing(Thing):
Use with extreme caution.
"""
for scan in self.scans:
self.delete_scan(scan.name)
path = os.path.join(self.base_scan_dir, scan.name)
self._delete_scan(path, logger)
@thing_action
def _delete_scan(self, scan_path, logger: InvocationLogger) -> bool:
try:
shutil.rmtree(scan_path)
return True
except Exception as e:
logger.warning(
"Attempted to delete scan " + scan_path + ", which failed."
" Server sent response" + str(e)
)
return False
@property
def latest_preview_stitch_path(self):
@ -1179,3 +1199,26 @@ class SmartScanThing(Thing):
"""List the relative paths of all files and folders in the zip folder specified"""
scan_zip = zipfile.ZipFile(zip_path)
return [os.path.normpath(i) for i in scan_zip.namelist()]
@thing_action
def purge_empty_scans(self, logger: InvocationLogger) -> None:
"""
Delete all scan folders containing no images at the top level
"""
scan_list = self.scans
# Filter out scans with no top level files, ignoring JSON files
# JSON is ignored as it's created before any images are captured
for scan in scan_list:
scan_folder = os.path.join(self.base_scan_dir, scan.name, IMG_DIR_NAME)
images_found = False
for fname in os.listdir(scan_folder):
fpath = os.path.join(scan_folder, fname)
if os.path.isfile(fpath) and IMAGE_REGEX.search(fname):
images_found = True
# break as soon as an image is found.
break
if not images_found:
path = os.path.join(self.base_scan_dir, scan.name)
self._delete_scan(path, logger)