diff --git a/detectmatelibrary_tests/test_utils/test_persistency_saver.py b/detectmatelibrary_tests/test_utils/test_persistency_saver.py index c3df1448..09bc7511 100644 --- a/detectmatelibrary_tests/test_utils/test_persistency_saver.py +++ b/detectmatelibrary_tests/test_utils/test_persistency_saver.py @@ -233,6 +233,23 @@ def test_auto_load_on_init_no_state_starts_fresh(self): assert saver is not None +class TestPersistencySaverGetStatus: + def test_config_fields(self): + p = EventPersistency(event_data_class=EventDataFrame) + cfg = PersistencySaverConfig( + path="memory://status_test3/state", + save_interval_seconds=120, + events_until_save=500, + auto_load=True, + ) + saver = PersistencySaver(p, cfg) + status = saver.get_status() + assert status["path"] == "memory://status_test3/state" + assert status["save_interval_seconds"] == 120 + assert status["events_until_save"] == 500 + assert status["auto_load"] is True + + class TestPersistencySaverIntegration: def test_full_cycle_dataframe_backend(self): """Train → save → restore → verify data identical.""" diff --git a/src/detectmatelibrary/utils/persistency/event_persistency.py b/src/detectmatelibrary/utils/persistency/event_persistency.py index 2ad1b52f..4b1a4137 100644 --- a/src/detectmatelibrary/utils/persistency/event_persistency.py +++ b/src/detectmatelibrary/utils/persistency/event_persistency.py @@ -59,6 +59,11 @@ def ingest_event( data = data_structure.to_data(all_variables) data_structure.add_data(data) + @property + def events_since_save(self) -> int: + """Number of events ingested since the last successful save.""" + return self._events_since_save + def reset_events_since_save(self) -> None: """Reset the events-since-save counter after a successful save.""" self._events_since_save = 0 diff --git a/src/detectmatelibrary/utils/persistency/persistency_saver.py b/src/detectmatelibrary/utils/persistency/persistency_saver.py index ff603c62..3af535bf 100644 --- a/src/detectmatelibrary/utils/persistency/persistency_saver.py +++ b/src/detectmatelibrary/utils/persistency/persistency_saver.py @@ -192,6 +192,33 @@ def stop(self) -> None: atexit.unregister(self.stop) self.save() + def get_status(self) -> dict[str, Any]: + """Return a snapshot of the current persistency state. + + Reads last_saved_at from metadata.json if present. A missing or + unreadable metadata file is tolerated + """ + ep = self._persistency + last_saved_at: str | None = None + try: + meta_path = f"{self._root}/metadata.json" + if self._fs.exists(meta_path): + with self._fs.open(meta_path, "r") as f: + meta = json.load(f) + last_saved_at = meta.get("saved_at") + except Exception as e: + logger.warning(f"PersistencySaver.get_status: could not read metadata.json — {e}") + return { + "path": self._config.path, + "save_interval_seconds": self._config.save_interval_seconds, + "events_until_save": self._config.events_until_save, + "auto_load": self._config.auto_load, + "events_seen_count": len(ep.get_events_seen()), + "events_with_data_count": len(ep.get_events_data()), + "events_since_save": ep.events_since_save, + "last_saved_at": last_saved_at, + } + def _safe_event_data_kwargs(self) -> dict[str, Any]: """Return event_data_kwargs with non-JSON-serializable values excluded."""