Skip to content

Commit 9ee8044

Browse files
committed
Add ref_key method to DataReference for generating sanitized keys from metadata attributes; update related documentation and examples
1 parent 3d6c129 commit 9ee8044

5 files changed

Lines changed: 398 additions & 261 deletions

File tree

dvue/catalog.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,34 @@ def matches(self, **criteria: Any) -> bool:
144144
return False
145145
return True
146146

147+
def ref_key(self) -> str:
148+
"""Return a string key derived from this reference's metadata attributes.
149+
150+
The default implementation joins all string-representable attribute values
151+
with ``"_"`` separators, sanitising spaces and non-identifier characters
152+
to underscores. The result is intended to be a valid Python identifier
153+
so it can be used as a variable name inside :class:`MathDataReference`
154+
expression strings.
155+
156+
Override in subclasses to produce a more readable, domain-specific key
157+
from a chosen subset of attributes.
158+
159+
Examples
160+
--------
161+
>>> ref = DataReference(df, name="r", station="A", variable="wind", interval="hourly")
162+
>>> ref.ref_key()
163+
'A_wind_hourly'
164+
"""
165+
parts = []
166+
for value in self._attributes.values():
167+
if not isinstance(value, (str, int, float, bool)):
168+
continue
169+
sanitized = re.sub(r"[^a-zA-Z0-9]+", "_", str(value).strip())
170+
sanitized = sanitized.strip("_")
171+
if sanitized:
172+
parts.append(sanitized)
173+
return "_".join(parts)
174+
147175
# ------------------------------------------------------------------
148176
# Data loading
149177
# ------------------------------------------------------------------

dvue/dataui.py

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ class DataProvider(param.Parameterized):
5757
5858
Catalog integration
5959
-------------------
60-
The preferred approach is to override the :attr:`catalog` property to
60+
The preferred approach is to override the :attr:`data_catalog` property to
6161
return a :class:`~dvue.catalog.DataCatalog`. When set:
6262
6363
* :meth:`get_data_catalog` automatically calls
64-
``catalog.to_dataframe().reset_index()``.
64+
``data_catalog.to_dataframe().reset_index()``.
6565
* :meth:`get_data_reference` automatically looks up
6666
:class:`~dvue.catalog.DataReference` objects by name.
6767
* :meth:`get_data` automatically yields ``ref.getData()`` for each
@@ -70,7 +70,11 @@ class DataProvider(param.Parameterized):
7070
Manual override
7171
---------------
7272
Override :meth:`get_data_catalog` and :meth:`get_data` directly for
73-
full control without a DataCatalog.
73+
full control without a DataCatalog. This is the pattern used by
74+
existing subclasses such as ``TimeSeriesDataUIManager`` subclasses that
75+
store the catalog as a plain :class:`pandas.DataFrame` attribute and
76+
override ``get_data_catalog()`` to return it directly — that pattern
77+
continues to work unchanged.
7478
7579
Example – catalog-based provider
7680
---------------------------------
@@ -86,7 +90,7 @@ def __init__(self, data_dir, **params):
8690
)
8791
8892
@property
89-
def catalog(self):
93+
def data_catalog(self):
9094
return self._catalog
9195
9296
provider = HydroProvider("/data/hydro")
@@ -96,12 +100,19 @@ def catalog(self):
96100
"""
97101

98102
@property
99-
def catalog(self) -> DataCatalog | None:
103+
def data_catalog(self) -> DataCatalog | None:
100104
"""Return the underlying :class:`~dvue.catalog.DataCatalog`, or ``None``.
101105
102106
Override this property to expose a :class:`~dvue.catalog.DataCatalog`
103107
to the UI layer. When ``None`` (the default), :meth:`get_data_catalog`
104108
and :meth:`get_data` must be overridden manually.
109+
110+
.. note::
111+
This property is intentionally named ``data_catalog`` (not
112+
``catalog``) so that subclasses remain free to use ``self.catalog``
113+
as a plain instance attribute — which is the established pattern
114+
for :class:`~dvue.tsdataui.TimeSeriesDataUIManager` subclasses
115+
that store the catalog as a :class:`pandas.DataFrame`.
105116
"""
106117
return None
107118

@@ -119,9 +130,9 @@ def get_data_catalog(self) -> pd.DataFrame:
119130
Raises
120131
------
121132
NotImplementedError
122-
When neither :attr:`catalog` nor a subclass override are provided.
133+
When neither :attr:`data_catalog` nor a subclass override are provided.
123134
"""
124-
cat = self.catalog
135+
cat = self.data_catalog
125136
if cat is not None:
126137
return cat.to_dataframe().reset_index()
127138
raise NotImplementedError(
@@ -150,13 +161,13 @@ def get_data_reference(self, row: pd.Series) -> DataReference:
150161
Raises
151162
------
152163
NotImplementedError
153-
When :attr:`catalog` is not set and this method is not overridden.
164+
When :attr:`data_catalog` is not set and this method is not overridden.
154165
KeyError
155166
When the resolved name is not in the catalog.
156167
ValueError
157168
When the reference name cannot be determined from *row*.
158169
"""
159-
cat = self.catalog
170+
cat = self.data_catalog
160171
if cat is None:
161172
raise NotImplementedError(
162173
"Override get_data_reference() or implement the catalog property in your subclass."
@@ -235,10 +246,11 @@ class DataUIManager(DataProvider):
235246
-------------------------------------------------
236247
Override **one** of the following strategies:
237248
238-
* Set :attr:`~DataProvider.catalog` property → automatic DataFrame + data
239-
retrieval from a :class:`~dvue.catalog.DataCatalog`.
249+
* Set :attr:`~DataProvider.data_catalog` property → automatic DataFrame +
250+
data retrieval from a :class:`~dvue.catalog.DataCatalog`.
240251
* Override :meth:`~DataProvider.get_data_catalog` directly → supply your
241-
own DataFrame.
252+
own DataFrame (the established pattern for
253+
:class:`~dvue.tsdataui.TimeSeriesDataUIManager` subclasses).
242254
243255
Additional overrides available:
244256

examples/ex_catalog_reader.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
PatternCSVDirectoryReader.
77
2. Writing a minimal custom DataCatalogReader for a new source type.
88
3. Connecting a DataCatalog to a DataUIManager (and DataUI) via the
9-
DataProvider.catalog property.
9+
DataProvider.data_catalog property.
1010
4. Using DataProvider standalone (no UI) for scripted data access.
1111
1212
Run individual sections with ``# %%`` in VS Code (Jupyter-style cells).
@@ -111,7 +111,7 @@ def __init__(self, data_store: dict, **params):
111111
self._cat = DataCatalog().add_source(data_store)
112112

113113
@property
114-
def catalog(self) -> DataCatalog:
114+
def data_catalog(self) -> DataCatalog:
115115
return self._cat
116116

117117
def build_station_name(self, r: pd.Series) -> str:
@@ -158,7 +158,7 @@ def build_station_name(self, r: pd.Series) -> str:
158158
#
159159
# # ── DataProvider (data layer) ──────────────────────────────────────────
160160
# @property
161-
# def catalog(self) -> DataCatalog:
161+
# def data_catalog(self) -> DataCatalog:
162162
# return self._catalog
163163
#
164164
# def build_station_name(self, r):

0 commit comments

Comments
 (0)