Skip to content

Commit 06f756f

Browse files
authored
feat(pyoso): add more helper function for analytics (#4731)
* feat(pyoso): add more helper function for analytics Moved the string logic to DataStatus and created helper function to get all the source nodes from the query * chore(pyoso): bump package version
1 parent eb7e211 commit 06f756f

File tree

4 files changed

+36
-22
lines changed

4 files changed

+36
-22
lines changed

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

warehouse/pyoso/pyoso/analytics.py

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,24 @@ class DataStatus(BaseModel):
3131
status: MaterializationStatus
3232
dependencies: list[str]
3333

34+
def __str__(self):
35+
status = self.status
36+
status_parts = []
37+
38+
if not status.latest_materialization and not status.partition_status:
39+
status_parts.append("No analytics data")
40+
if status.latest_materialization:
41+
status_parts.append(
42+
f"Last: {status.latest_materialization.strftime('%Y-%m-%d %H:%M:%S')}"
43+
)
44+
if status.partition_status:
45+
ps = status.partition_status
46+
status_parts.append(
47+
f"Partitions: {ps.num_materialized}/{ps.num_partitions}"
48+
)
49+
status_text = f" ({', '.join(status_parts)})" if status_parts else ""
50+
return f"{self.key}{status_text}"
51+
3452

3553
class DataAnalytics:
3654
"""Container for analytics data with tree-structured display methods."""
@@ -57,7 +75,7 @@ def _calculate_root_keys(self) -> list[str]:
5775

5876
def __iter__(self):
5977
"""Iterate over the analytics data keys."""
60-
return iter(self._analytics_data.keys())
78+
return iter(self._analytics_data.items())
6179

6280
def __contains__(self, key: str) -> bool:
6381
"""Check if a key exists in the analytics data."""
@@ -72,6 +90,15 @@ def root_keys(self) -> list[str]:
7290
"""Get the root keys (top-level nodes in the dependency tree)."""
7391
return self._root_keys.copy()
7492

93+
@property
94+
def sources(self) -> list[DataStatus]:
95+
"""Get all sources (keys with no dependencies)."""
96+
return [
97+
status
98+
for status in self._analytics_data.values()
99+
if len(status.dependencies) == 0
100+
]
101+
75102
def get(self, key: str) -> DataStatus | None:
76103
"""Get analytics data for a specific key."""
77104
return self._analytics_data.get(key)
@@ -104,27 +131,9 @@ def _print_analytics_tree(
104131
visited.add(key)
105132
data_status = self._analytics_data[key]
106133

107-
# Print the current node with inline status information
108-
status = data_status.status
109-
status_parts = []
110-
111-
if not status.latest_materialization and not status.partition_status:
112-
status_parts.append("No analytics data")
113-
if status.latest_materialization:
114-
status_parts.append(
115-
f"Last: {status.latest_materialization.strftime('%Y-%m-%d %H:%M:%S')}"
116-
)
117-
if status.partition_status:
118-
ps = status.partition_status
119-
status_parts.append(
120-
f"Partitions: {ps.num_materialized}/{ps.num_partitions}"
121-
)
122-
123-
status_text = f" ({', '.join(status_parts)})" if status_parts else ""
124-
125134
# Determine the prefix for the current node
126135
prefix = "└── " if is_last else "├── "
127-
print(f"{indent}{prefix}{key}{status_text}")
136+
print(f"{indent}{prefix}{data_status}")
128137

129138
# Print dependencies
130139
if data_status.dependencies:

warehouse/pyoso/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "pyoso"
3-
version = "0.6.0"
3+
version = "0.6.1"
44
description = ""
55
readme = "README.md"
66
requires-python = ">=3.8"

warehouse/pyoso/tests/test_analytics.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ def test_root_keys_property(self):
7575
nonexistent_data = analytics.get("nonexistent")
7676
self.assertIsNone(nonexistent_data)
7777

78+
sources = analytics.sources
79+
self.assertEqual(
80+
set([s.key for s in sources]), {"child2", "child3", "grandchild1"}
81+
)
82+
7883
def test_empty_analytics(self):
7984
"""Test DataAnalytics with empty data."""
8085
analytics = DataAnalytics({})

0 commit comments

Comments
 (0)