diff --git a/crates/store/re_chunk_store/src/gc.rs b/crates/store/re_chunk_store/src/gc.rs index 2d98e3a66d60..2cf7baddb927 100644 --- a/crates/store/re_chunk_store/src/gc.rs +++ b/crates/store/re_chunk_store/src/gc.rs @@ -337,6 +337,7 @@ impl ChunkStore { let Self { id: _, + info: _, config: _, type_registry: _, chunks_per_chunk_id, diff --git a/crates/store/re_chunk_store/src/store.rs b/crates/store/re_chunk_store/src/store.rs index 11746ea443db..b95af5d4fa62 100644 --- a/crates/store/re_chunk_store/src/store.rs +++ b/crates/store/re_chunk_store/src/store.rs @@ -6,7 +6,7 @@ use arrow2::datatypes::DataType as ArrowDataType; use nohash_hasher::IntMap; use re_chunk::{Chunk, ChunkId, RowId, TransportChunk}; -use re_log_types::{EntityPath, StoreId, TimeInt, Timeline}; +use re_log_types::{EntityPath, StoreId, StoreInfo, TimeInt, Timeline}; use re_types_core::ComponentName; use crate::{ChunkStoreChunkStats, ChunkStoreError, ChunkStoreResult}; @@ -292,6 +292,7 @@ pub struct ChunkStoreGeneration { #[derive(Debug)] pub struct ChunkStore { pub(crate) id: StoreId, + pub(crate) info: Option, /// The configuration of the chunk store (e.g. compaction settings). pub(crate) config: ChunkStoreConfig, @@ -366,6 +367,7 @@ impl Clone for ChunkStore { fn clone(&self) -> Self { Self { id: self.id.clone(), + info: self.info.clone(), config: self.config.clone(), type_registry: self.type_registry.clone(), chunks_per_chunk_id: self.chunks_per_chunk_id.clone(), @@ -389,6 +391,7 @@ impl std::fmt::Display for ChunkStore { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let Self { id, + info: _, config, type_registry: _, chunks_per_chunk_id, @@ -443,6 +446,7 @@ impl ChunkStore { pub fn new(id: StoreId, config: ChunkStoreConfig) -> Self { Self { id, + info: None, config, type_registry: Default::default(), chunk_ids_per_min_row_id: Default::default(), @@ -464,6 +468,16 @@ impl ChunkStore { &self.id } + #[inline] + pub fn set_info(&mut self, info: StoreInfo) { + self.info = Some(info); + } + + #[inline] + pub fn info(&self) -> Option<&StoreInfo> { + self.info.as_ref() + } + /// Return the current [`ChunkStoreGeneration`]. This can be used to determine whether the /// database has been modified since the last time it was queried. #[inline] @@ -538,9 +552,11 @@ impl ChunkStore { let msg = res.with_context(|| format!("couldn't decode message {path_to_rrd:?} "))?; match msg { re_log_types::LogMsg::SetStoreInfo(info) => { - stores - .entry(info.info.store_id.clone()) - .or_insert_with(|| Self::new(info.info.store_id, store_config.clone())); + let store = stores.entry(info.info.store_id.clone()).or_insert_with(|| { + Self::new(info.info.store_id.clone(), store_config.clone()) + }); + + store.set_info(info.info); } re_log_types::LogMsg::ArrowMsg(store_id, msg) => { diff --git a/crates/store/re_chunk_store/src/writes.rs b/crates/store/re_chunk_store/src/writes.rs index acf20ea04d6a..6c574dc11bdd 100644 --- a/crates/store/re_chunk_store/src/writes.rs +++ b/crates/store/re_chunk_store/src/writes.rs @@ -482,6 +482,7 @@ impl ChunkStore { let Self { id, + info: _, config: _, type_registry: _, chunks_per_chunk_id, diff --git a/rerun_py/rerun_bindings/rerun_bindings.pyi b/rerun_py/rerun_bindings/rerun_bindings.pyi index a9d22b37fb28..2099b7491642 100644 --- a/rerun_py/rerun_bindings/rerun_bindings.pyi +++ b/rerun_py/rerun_bindings/rerun_bindings.pyi @@ -63,6 +63,8 @@ class Recording: def schema(self) -> Schema: ... def view(self, index: str, contents: ViewContentsLike) -> RecordingView: ... + def recording_id(self) -> str: ... + def application_id(self) -> str: ... class RRDArchive: """An archive loaded from an RRD, typically containing 1 or more recordings or blueprints.""" diff --git a/rerun_py/src/dataframe.rs b/rerun_py/src/dataframe.rs index 37501f48b0e6..5e32472a36e3 100644 --- a/rerun_py/src/dataframe.rs +++ b/rerun_py/src/dataframe.rs @@ -569,6 +569,22 @@ impl PyRecording { query_expression: query, }) } + + fn recording_id(&self) -> String { + self.store.id().as_str().to_owned() + } + + fn application_id(&self) -> PyResult { + Ok(self + .store + .info() + .ok_or(PyValueError::new_err( + "Recording is missing application id.", + ))? + .application_id + .as_str() + .to_owned()) + } } #[pyclass(frozen, name = "RRDArchive")] diff --git a/rerun_py/tests/unit/test_dataframe.py b/rerun_py/tests/unit/test_dataframe.py index 993269e0dfe3..8abc3c967654 100644 --- a/rerun_py/tests/unit/test_dataframe.py +++ b/rerun_py/tests/unit/test_dataframe.py @@ -1,14 +1,18 @@ from __future__ import annotations import tempfile +import uuid import pyarrow as pa import rerun as rr +APP_ID = "rerun_example_test_recording" +RECORDING_ID = uuid.uuid4() + class TestDataframe: def setup_method(self) -> None: - rr.init("rerun_example_test_recording") + rr.init(APP_ID, recording_id=RECORDING_ID) rr.set_time_sequence("my_index", 1) rr.log("points", rr.Points3D([[1, 2, 3], [4, 5, 6], [7, 8, 9]])) @@ -22,6 +26,10 @@ def setup_method(self) -> None: self.recording = rr.dataframe.load_recording(rrd) + def test_recording_info(self) -> None: + assert self.recording.application_id() == APP_ID + assert self.recording.recording_id() == str(RECORDING_ID) + def test_full_view(self) -> None: view = self.recording.view(index="my_index", contents="points")