Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions cloud_pipelines_backend/api_server_sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -522,10 +522,10 @@ class ArtifactNodeIdResponse:
id: bts.IdType


@dataclasses.dataclass
@dataclasses.dataclass(kw_only=True)
class GetGraphExecutionStateResponse:
child_execution_status_stats: dict[bts.IdType, dict[str, int]]
pass
summary: ExecutionStatusSummary


@dataclasses.dataclass(kw_only=True)
Expand Down Expand Up @@ -692,14 +692,18 @@ def get_graph_execution_state(
) + tuple(child_container_execution_stat_rows)
child_execution_status_stats: dict[bts.IdType, dict[str, int]] = {}

summary = ExecutionStatusSummary()
for row in child_execution_stat_rows:
child_execution_id, status, count = row.tuple()
child_execution_id, status, count = row._tuple()
status_stats = child_execution_status_stats.setdefault(
child_execution_id, {}
)
status_stats[status.value] = count
summary.count_node_status(status=status, count=count)

return GetGraphExecutionStateResponse(
child_execution_status_stats=child_execution_status_stats,
summary=summary,
)

def get_container_execution_state(
Expand Down
22 changes: 22 additions & 0 deletions tests/test_execution_nodes_api_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ def test_no_children_returns_empty_stats(self):

assert isinstance(result, GetGraphExecutionStateResponse)
assert result.child_execution_status_stats == {}
assert result.summary.total_nodes == 0
assert result.summary.ended_nodes == 0
assert result.summary.has_ended is False

def test_children_with_no_status_are_excluded(self):
"""Children whose container_execution_status is None are not counted."""
Expand All @@ -87,6 +90,9 @@ def test_children_with_no_status_are_excluded(self):
result = self.service.get_graph_execution_state(session, parent.id)

assert result.child_execution_status_stats == {}
assert result.summary.total_nodes == 0
assert result.summary.ended_nodes == 0
assert result.summary.has_ended is False

def test_direct_container_children(self):
"""Children that are direct container nodes (no descendants via ancestor links)."""
Expand Down Expand Up @@ -117,6 +123,9 @@ def test_direct_container_children(self):
assert stats[child1.id] == {"SUCCEEDED": 1}
assert child2.id in stats
assert stats[child2.id] == {"RUNNING": 1}
assert result.summary.total_nodes == 2
assert result.summary.ended_nodes == 1
assert result.summary.has_ended is False

def test_three_level_mixed_stats(self):
"""3-level deep graph with direct tasks and nested sub-graphs.
Expand Down Expand Up @@ -302,6 +311,19 @@ def test_three_level_mixed_stats(self):
# appear as a key in the stats
assert sub_graph_a_b.id not in stats

# -- Summary: total_nodes and ended_nodes --
# Direct children (Query 2): task_1(1), task_2(1), task_3(1) = 3 nodes
# Descendants of sub_graph_a (Query 1): 6 nodes
# CANCELLED(1), SKIPPED(1), RUNNING(1), INVALID(1), SYSTEM_ERROR(1), SUCCEEDED(1)
# Total = 3 + 6 = 9
assert result.summary.total_nodes == 9
# Ended statuses: SUCCEEDED(task_1) + FAILED(task_2) = 2 from Query 2
# + CANCELLED(1) + SKIPPED(1) + INVALID(1) + SYSTEM_ERROR(1) + SUCCEEDED(1) = 5 from Query 1
# QUEUED(task_3) and RUNNING(task_sg_a_3) are NOT ended
# Total ended = 2 + 5 = 7
assert result.summary.ended_nodes == 7
assert result.summary.has_ended is False


if __name__ == "__main__":
pytest.main()