"""Manual dataset export and paused deletion of retained acquisitions.""" from __future__ import annotations from collections.abc import Callable from pathlib import Path from python_app.models.gui_profile_schema import ProfileWriter from python_app.gui.controllers.pipeline_controller import PipelineController from python_app.gui.controllers.sections.views import PrimaryView, DataView from python_app.models.run_config_schema import RunConfigModel from python_app.storage.npz.store import NpzStore class SnapshotController: """Save stage histories and hold deletion ownership through final redraw.""" def __init__( self, pipeline: PipelineController, store: NpzStore, write_profile: ProfileWriter, *, destination: Callable[[RunConfigModel], tuple[Path, str, str]], build_config: Callable[[], RunConfigModel], processing_settings: dict[int, dict], allowed: Callable[[bool], bool], log: Callable[[str], None], error: Callable[[str, Exception], None], ) -> None: self.pipeline, self.store = pipeline, store self._write_profile, self._log, self._error = write_profile, log, error self._destination, self._build_config = destination, build_config self._settings, self._allowed = processing_settings, allowed def bind_actions(self, primary: PrimaryView, data: DataView) -> None: primary.clear.clicked.connect(lambda: self.delete_history(remove_last=False)) data.remove_last.clicked.connect(lambda: self.delete_history(remove_last=True)) data.save.clicked.connect(lambda: self.save()) data.save_json.clicked.connect(lambda: self.save(json_export=True)) data.record.clicked.connect(lambda: self.start_recording(data.record_count.value())) def start_recording(self, count: int) -> None: if not self._allowed(False): return try: config = self.pipeline.active_config if self.pipeline.running else None self.pipeline.recording.start( *self._destination(config or self._build_config()), count, running=self.pipeline.running, start_acquisition=self.pipeline.start, ) except Exception as exc: self._error("Failed to start disk recording", exc) def save(self, *, json_export: bool = False) -> None: if not self._allowed(False): return action = "VNA history JSON" if json_export else "snapshot" try: active = self.pipeline.active_config root, name, prefix = self._destination(active or self._build_config()) self.pipeline.drain_snapshot() if self.pipeline.closing or self.pipeline.active_config is not active: return raw = list(self.pipeline.history.raw) preprocessed = list(self.pipeline.history.preprocessed) results = list(self.pipeline.history.results) if not (raw or preprocessed or results): raise ValueError("No runtime data is available for save") if json_export: paths = self.store.save_runtime_vna_history_json_batch( root, name, raw, preprocessed, results, primary_stage="preprocessed", name_prefix=prefix, ) directory = paths[0].parent profile_path = directory / "vna_config_profile.json" else: directory = self.store.save_runtime_snapshot_numpy( root, name, raw, preprocessed, results, name_prefix=prefix, processing_settings=self._settings, ) profile_path = directory / "config_profile.json" try: self._write_profile(profile_path, allow_overwrite=json_export, dataset=True) except Exception as exc: self._error( f"{action} data was saved in {directory}, but the adjacent config profile " f"could not be written: {profile_path}", exc, ) return self._log( f"Saved {action}: {directory} " f"(raw={len(raw)}, preprocessed={len(preprocessed)}, results={len(results)})" ) except Exception as exc: self._error(f"Failed to save {action}", exc) def delete_history(self, *, remove_last: bool) -> None: if not self._allowed(True): return action = "remove last runtime measurement" if remove_last else "clear runtime history" try: with self.pipeline.pause() as operation: if not operation.valid: return self.pipeline.discard_replay() dropped_raw, dropped_pre, dropped_results = self.pipeline.drop_pending() self.pipeline.history.delete(remove_last=remove_last) try: self.pipeline.publish_live() finally: self.pipeline.plots.clear() self.pipeline.settle_deleted_results() if not operation.valid: return self.pipeline.history_changed() self.pipeline.plots.redraw() self._log( f"{action}: dropped raw={dropped_raw}, " f"preprocessed={dropped_pre}, results={dropped_results}" ) except InterruptedError: self.pipeline.operation_cancelled() except Exception as exc: self._error(f"Failed to {action}", exc)