Merge branch 'stitch-all-logging' into 'v3'
Log during stitch all and handle errors Closes #710 See merge request openflexure/openflexure-microscope-server!593
This commit is contained in:
commit
4e58d613d0
4 changed files with 75 additions and 16 deletions
|
|
@ -11,7 +11,6 @@ import os
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from subprocess import SubprocessError
|
|
||||||
from types import TracebackType
|
from types import TracebackType
|
||||||
from typing import (
|
from typing import (
|
||||||
Annotated,
|
Annotated,
|
||||||
|
|
@ -717,7 +716,7 @@ class SmartScanThing(OFMThing):
|
||||||
except lt.exceptions.InvocationCancelledError:
|
except lt.exceptions.InvocationCancelledError:
|
||||||
# Sleep for 1 second just to allow invocation logs to pass to user.
|
# Sleep for 1 second just to allow invocation logs to pass to user.
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
except SubprocessError as e:
|
except ChildProcessError as e:
|
||||||
self.logger.error(f"Stitching failed: {e}", exc_info=e)
|
self.logger.error(f"Stitching failed: {e}", exc_info=e)
|
||||||
|
|
||||||
@lt.action(use_global_lock=False)
|
@lt.action(use_global_lock=False)
|
||||||
|
|
@ -744,4 +743,5 @@ class SmartScanThing(OFMThing):
|
||||||
# need stitching.
|
# need stitching.
|
||||||
for scan in self.get_data_for_gallery():
|
for scan in self.get_data_for_gallery():
|
||||||
if scan.dzi is None:
|
if scan.dzi is None:
|
||||||
|
self.logger.info(f"Stitching {scan.name}")
|
||||||
self.stitch_scan(scan_name=scan.name)
|
self.stitch_scan(scan_name=scan.name)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import tempfile
|
||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
@ -12,6 +13,10 @@ import pytest
|
||||||
|
|
||||||
from labthings_fastapi.testing import create_thing_without_server
|
from labthings_fastapi.testing import create_thing_without_server
|
||||||
|
|
||||||
|
from openflexure_microscope_server.things.smart_scan import (
|
||||||
|
SmartScanThing,
|
||||||
|
)
|
||||||
|
|
||||||
from .shared_utils.lt_test_utils import LabThingsTestEnv
|
from .shared_utils.lt_test_utils import LabThingsTestEnv
|
||||||
|
|
||||||
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
|
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
@ -105,3 +110,17 @@ def mock_picam_thing(mocker):
|
||||||
from openflexure_microscope_server.things.camera.picamera import StreamingPiCamera2
|
from openflexure_microscope_server.things.camera.picamera import StreamingPiCamera2
|
||||||
|
|
||||||
return create_thing_without_server(StreamingPiCamera2)
|
return create_thing_without_server(StreamingPiCamera2)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def smart_scan_thing(mocker):
|
||||||
|
"""Return a smart scan thing as a fixture."""
|
||||||
|
thing = create_thing_without_server(
|
||||||
|
SmartScanThing,
|
||||||
|
default_workflow="mock-_all_workflows",
|
||||||
|
mock_all_slots=True,
|
||||||
|
)
|
||||||
|
type(thing._thing_server_interface).application_config = mocker.PropertyMock(
|
||||||
|
return_value={"data_folder": tempfile.gettempdir()}
|
||||||
|
)
|
||||||
|
return thing
|
||||||
|
|
|
||||||
|
|
@ -50,20 +50,6 @@ def _clear_scan_dir() -> None:
|
||||||
shutil.rmtree(SCAN_DIR)
|
shutil.rmtree(SCAN_DIR)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
|
||||||
def smart_scan_thing(mocker):
|
|
||||||
"""Return a smart scan thing as a fixture."""
|
|
||||||
thing = create_thing_without_server(
|
|
||||||
SmartScanThing,
|
|
||||||
default_workflow="mock-_all_workflows",
|
|
||||||
mock_all_slots=True,
|
|
||||||
)
|
|
||||||
type(thing._thing_server_interface).application_config = mocker.PropertyMock(
|
|
||||||
return_value={"data_folder": tempfile.gettempdir()}
|
|
||||||
)
|
|
||||||
return thing
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def entered_smart_scan_thing(smart_scan_thing):
|
def entered_smart_scan_thing(smart_scan_thing):
|
||||||
"""Yield a smart scan thing as a fixture that has been entred.
|
"""Yield a smart scan thing as a fixture that has been entred.
|
||||||
|
|
|
||||||
|
|
@ -325,3 +325,57 @@ def test_final_stitching_command_error(mocker):
|
||||||
stitcher._extra_args = ["ERROR"]
|
stitcher._extra_args = ["ERROR"]
|
||||||
with pytest.raises(ChildProcessError):
|
with pytest.raises(ChildProcessError):
|
||||||
stitcher.run()
|
stitcher.run()
|
||||||
|
|
||||||
|
|
||||||
|
def test_stitch_all_scans_continues_after_error(mocker, caplog, smart_scan_thing):
|
||||||
|
"""Test stitching all continues after an error and logs as expected."""
|
||||||
|
scans = [
|
||||||
|
mocker.Mock(name="scan1", dzi=None),
|
||||||
|
mocker.Mock(name="scan2", dzi=None),
|
||||||
|
mocker.Mock(name="scan3", dzi=None),
|
||||||
|
]
|
||||||
|
scans[0].name = "scan1"
|
||||||
|
scans[1].name = "scan2"
|
||||||
|
scans[2].name = "scan3"
|
||||||
|
|
||||||
|
mocker.patch.object(
|
||||||
|
smart_scan_thing,
|
||||||
|
"get_data_for_gallery",
|
||||||
|
return_value=scans,
|
||||||
|
)
|
||||||
|
|
||||||
|
# ensure scan data exists for all scans
|
||||||
|
scan_data = mocker.Mock()
|
||||||
|
scan_data.stitching_settings = StitchingSettings(
|
||||||
|
overlap=0.45,
|
||||||
|
correlation_resize=0.5,
|
||||||
|
)
|
||||||
|
|
||||||
|
with smart_scan_thing:
|
||||||
|
mocker.patch.object(
|
||||||
|
smart_scan_thing._scan_dir_manager,
|
||||||
|
"get_scan_data",
|
||||||
|
return_value=scan_data,
|
||||||
|
)
|
||||||
|
|
||||||
|
def run_side_effect(self):
|
||||||
|
if "scan2" in str(self.images_dir):
|
||||||
|
raise ChildProcessError("test_message")
|
||||||
|
|
||||||
|
mocker.patch(
|
||||||
|
"openflexure_microscope_server.stitching.FinalStitcher.run",
|
||||||
|
autospec=True,
|
||||||
|
side_effect=run_side_effect,
|
||||||
|
)
|
||||||
|
|
||||||
|
with caplog.at_level("INFO"):
|
||||||
|
smart_scan_thing.stitch_all_scans()
|
||||||
|
|
||||||
|
messages = [r.message for r in caplog.records]
|
||||||
|
|
||||||
|
assert messages == [
|
||||||
|
"Stitching scan1",
|
||||||
|
"Stitching scan2",
|
||||||
|
"Stitching failed: test_message",
|
||||||
|
"Stitching scan3",
|
||||||
|
]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue