Add way for gallery providers to specify bulk actions.

This commit is contained in:
Julian Stirling 2026-06-25 16:52:44 +01:00
parent 3e38722d45
commit 2dcd386661
4 changed files with 57 additions and 0 deletions

View file

@ -332,6 +332,13 @@ class BaseCamera(OFMThing, ABC):
self.logger.info(f"Deleting: {capture}")
os.remove(capture)
def get_gallery_bulk_actions(self) -> list[ActionButton]:
"""Return the bulk gallery actions for cameras.
By default there are no bulk actions.
"""
return []
@lt.endpoint(
"delete",
"capture/{name}",

View file

@ -12,6 +12,7 @@ from pydantic import BaseModel
import labthings_fastapi as lt
from openflexure_microscope_server.things import OFMThing
from openflexure_microscope_server.ui import ActionButton
@runtime_checkable
@ -38,6 +39,8 @@ class GalleryCompatibleThing(Protocol):
def delete_all_gallery_items(self) -> None: ... # noqa: D102
def get_gallery_bulk_actions(self) -> list[ActionButton]: ... # noqa: D102
class GalleryThing(lt.Thing):
"""A Thing for communicating with the front end gallery."""
@ -125,6 +128,19 @@ class GalleryThing(lt.Thing):
raise RuntimeError("Cannot access card_types before server has started.")
return self._card_types
# Cache result after first call.
_bulk_actions: Optional[list[ActionButton]] = None
@lt.property
def bulk_actions(self) -> list[ActionButton]:
"""All bulk actions."""
if self._bulk_actions is None:
actions: list[ActionButton] = []
for thing in self.gallery_providing_things.values():
actions += thing.get_gallery_bulk_actions()
self._bulk_actions = actions
return self._bulk_actions
@lt.property
def list_data(self) -> list[dict[str, Any]]:
"""List the data from all registered things.

View file

@ -32,6 +32,7 @@ import labthings_fastapi as lt
from openflexure_microscope_server import scan_directories, stitching
from openflexure_microscope_server.things import OFMThing
from openflexure_microscope_server.ui import ActionButton, action_button_for
from openflexure_microscope_server.utilities import coerce_thing_selector
# Things
@ -199,6 +200,26 @@ class SmartScanThing(OFMThing):
self.logger.info(f"Deleting: {scan_name}")
self._delete_scan(scan_name)
def get_gallery_bulk_actions(self) -> list[ActionButton]:
"""Return the bulk gallery actions for smart scan."""
# Stitch all scans
return [
action_button_for(
self,
"stitch_all_scans",
submit_label="Stitch All Unstitched Scans",
can_terminate=True,
button_primary=False,
modal_progress=True,
requires_confirmation=True,
confirmation_message=(
"<h3>Stitch all unstitched scans?</h3>"
"<br>Depending on the number and size of scans, this may be slow, "
"and your microscope should not be used during the stitching.'"
),
)
]
# Note that the default detector name is set at init. This is over written if
# setting is loaded from disk.
@lt.setting

View file

@ -15,6 +15,7 @@ from openflexure_microscope_server.things.gallery import (
GalleryCompatibleThing,
GalleryThing,
)
from openflexure_microscope_server.ui import ActionButton
from ..shared_utils.lt_test_utils import LabThingsTestEnv
@ -63,6 +64,10 @@ class MinimalGalleryClass:
def delete_all_gallery_items(self) -> None:
"""Mock deleting all the data."""
def get_gallery_bulk_actions(self) -> list[ActionButton]:
"""No bulk actions."""
return []
class MinimalGallerySource(OFMThing, MinimalGalleryClass):
"""Minimal example of a Thing that can show data in the gallery.
@ -94,6 +99,10 @@ class BadGallerySource(OFMThing):
def delete_all_gallery_items(self) -> None:
"""Mock deleting all the data."""
def get_gallery_bulk_actions(self) -> list[ActionButton]:
"""No bulk actions."""
return []
class BadMockGalleryData(BaseModel):
"""Mock gallery data without a card type."""
@ -119,6 +128,10 @@ class BadGallerySource2(OFMThing):
def delete_all_gallery_items(self) -> None:
"""Mock deleting all the data."""
def get_gallery_bulk_actions(self) -> list[ActionButton]:
"""No bulk actions."""
return []
def test_gallery_compatible_protocol():
"""Check the gallery compatible protocol detects compatible classes only."""