Skip to content

Commit

Permalink
Initial prototype for visualization of pipeline processing status
Browse files Browse the repository at this point in the history
  • Loading branch information
enourbakhsh committed Dec 18, 2024
1 parent 269e412 commit 217fef6
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 5 deletions.
11 changes: 11 additions & 0 deletions python/lsst/pipe/base/pipeline_graph/visualization/_formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
from .._nodes import NodeKey, NodeType
from ._merge import MergedNodeKey
from ._options import NodeAttributeOptions
from ._status import NodeStatusOptions, TaskStatusInfo, DatasetTypeStatusInfo

Check warning on line 42 in python/lsst/pipe/base/pipeline_graph/visualization/_formatting.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_formatting.py#L42

Added line #L42 was not covered by tests

DisplayNodeKey = NodeKey | MergedNodeKey
"""Type alias for graph keys that may be original task, task init, or dataset
Expand Down Expand Up @@ -77,6 +78,10 @@ def get_node_symbol(node: DisplayNodeKey, x: int | None = None) -> str:
return "▤"
raise ValueError(f"Unexpected node key: {node} of type {type(node)}.")

# The text based one may only ever have the numbers, but we definitely want
# to have the colors for dot and mermaid.
def get_node_color(node: DisplayNodeKey, x: int | None = None) -> str:
pass

Check warning on line 84 in python/lsst/pipe/base/pipeline_graph/visualization/_formatting.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_formatting.py#L83-L84

Added lines #L83 - L84 were not covered by tests

class GetNodeText:
"""A callback for the `Printer` class's `get_text` callback that
Expand Down Expand Up @@ -231,3 +236,9 @@ def format_task_class(options: NodeAttributeOptions, task_class_name: str) -> st
case False:
return ""
raise ValueError(f"Invalid display option for task_classes: {options.task_classes!r}.")

def format_task_status(options: NodeStatusOptions, task_status: TaskStatusInfo) -> str:
return ""

Check warning on line 241 in python/lsst/pipe/base/pipeline_graph/visualization/_formatting.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_formatting.py#L240-L241

Added lines #L240 - L241 were not covered by tests

def format_dataset_type_status(options: NodeStatusOptions, dataset_type_status: DatasetTypeStatusInfo) -> str:
return ""

Check warning on line 244 in python/lsst/pipe/base/pipeline_graph/visualization/_formatting.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_formatting.py#L243-L244

Added lines #L243 - L244 were not covered by tests
13 changes: 10 additions & 3 deletions python/lsst/pipe/base/pipeline_graph/visualization/_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@

import dataclasses
from typing import Literal

from ._status import NodeStatusOptions

@dataclasses.dataclass
class NodeAttributeOptions:
Expand Down Expand Up @@ -69,10 +69,13 @@ class NodeAttributeOptions:
- `None`: context-dependent default behavior.
"""

status: NodeStatusOptions | None = None
"""Options for displaying execution status."""

Check warning on line 73 in python/lsst/pipe/base/pipeline_graph/visualization/_options.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_options.py#L72-L73

Added lines #L72 - L73 were not covered by tests

def __bool__(self) -> bool:
return bool(self.dimensions or self.storage_classes or self.task_classes)
return bool(self.dimensions or self.storage_classes or self.task_classes or self.status)

Check warning on line 76 in python/lsst/pipe/base/pipeline_graph/visualization/_options.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_options.py#L76

Added line #L76 was not covered by tests

def checked(self, is_resolved: bool) -> NodeAttributeOptions:
def checked(self, is_resolved: bool, has_status: bool = False) -> NodeAttributeOptions:

Check warning on line 78 in python/lsst/pipe/base/pipeline_graph/visualization/_options.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_options.py#L78

Added line #L78 was not covered by tests
"""Check these options against a pipeline graph's resolution status and
fill in defaults.
Expand All @@ -81,6 +84,9 @@ def checked(self, is_resolved: bool) -> NodeAttributeOptions:
is_resolved : `bool`
Whether the pipeline graph to be displayed is resolved
(`PipelineGraph.is_fully_resolved`).
has_status : `bool`
Whether the pipeline graph to be displayed has status information.
Defaults to `False`.
Returns
-------
Expand All @@ -106,4 +112,5 @@ def checked(self, is_resolved: bool) -> NodeAttributeOptions:
self.task_classes if self.task_classes is not None else ("concise" if is_resolved else False)
),
storage_classes=(self.storage_classes if self.storage_classes is not None else is_resolved),
status=self.status if has_status else None,
)
17 changes: 15 additions & 2 deletions python/lsst/pipe/base/pipeline_graph/visualization/_show.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
)
from ._options import NodeAttributeOptions
from ._printer import make_default_printer
from ._status import StatusAnnotator, NodeStatusOptions

Check warning on line 51 in python/lsst/pipe/base/pipeline_graph/visualization/_show.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_show.py#L51

Added line #L51 was not covered by tests

DisplayNodeKey = NodeKey | MergedNodeKey

Expand All @@ -64,6 +65,8 @@ def parse_display_args(
merge_output_trees: int = 4,
merge_intermediates: bool = True,
include_automatic_connections: bool = False,
status_annotator: StatusAnnotator | None = None,
status_options: NodeStatusOptions | None = None,
) -> tuple[networkx.DiGraph | networkx.MultiDiGraph, NodeAttributeOptions]:
"""Print a text-based ~.PipelineGraph` visualization.
Expand Down Expand Up @@ -126,21 +129,31 @@ def parse_display_args(
include_automatic_connections : `bool`, optional
Whether to include automatically-added connections like the config,
log, and metadata dataset types for each task. Default is `False`.
status_annotator : `StatusAnnotator`, optional
Annotator to add status information to the graph. Default is `None`.
status_options : `NodeStatusOptions`, optional
Options for displaying execution status. Default is `None`.
"""
if init is None:
if not dataset_types:
raise ValueError("Cannot show init and runtime graphs unless dataset types are shown.")
xgraph = pipeline_graph.make_xgraph()
if status_annotator is not None:
raise ValueError("Cannot show status with both init and runtime graphs.")

Check warning on line 142 in python/lsst/pipe/base/pipeline_graph/visualization/_show.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_show.py#L142

Added line #L142 was not covered by tests
elif dataset_types:
xgraph = pipeline_graph.make_bipartite_xgraph(init)
if status_annotator is not None:
status_annotator(xgraph, dataset_types=True)

Check warning on line 146 in python/lsst/pipe/base/pipeline_graph/visualization/_show.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_show.py#L146

Added line #L146 was not covered by tests
else:
xgraph = pipeline_graph.make_task_xgraph(init)
storage_classes = False
if status_annotator is not None:
status_annotator(xgraph, dataset_types=False)

Check warning on line 151 in python/lsst/pipe/base/pipeline_graph/visualization/_show.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_show.py#L151

Added line #L151 was not covered by tests

options = NodeAttributeOptions(
dimensions=dimensions, storage_classes=storage_classes, task_classes=task_classes
dimensions=dimensions, storage_classes=storage_classes, task_classes=task_classes, status=status_options
)
options = options.checked(pipeline_graph.is_fully_resolved)
options = options.checked(pipeline_graph.is_fully_resolved, has_status=status_annotator is not None)

Check warning on line 156 in python/lsst/pipe/base/pipeline_graph/visualization/_show.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_show.py#L156

Added line #L156 was not covered by tests

if dataset_types and not include_automatic_connections:
taskish_nodes: list[TaskNode | TaskInitNode] = []
Expand Down
135 changes: 135 additions & 0 deletions python/lsst/pipe/base/pipeline_graph/visualization/_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# This file is part of pipe_base.
#
# Developed for the LSST Data Management System.
# This product includes software developed by the LSST Project
# (http://www.lsst.org).
# See the COPYRIGHT file at the top-level directory of this distribution
# for details of code ownership.
#
# This software is dual licensed under the GNU General Public License and also
# under a 3-clause BSD license. Recipients may choose which of these licenses
# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
# respectively. If you choose the GPL option then the following text applies
# (but note that there is still no warranty even if you opt for BSD instead):
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from __future__ import annotations

__all__ = ("show", "parse_display_args")

import sys
from collections.abc import Sequence
from shutil import get_terminal_size
from typing import Any, Literal, TextIO

import networkx

from .._nodes import NodeKey
from .._pipeline_graph import PipelineGraph
from .._tasks import TaskInitNode, TaskNode
from ._formatting import GetNodeText, get_node_symbol
from ._layout import ColumnSelector, Layout
from ._merge import (

Check warning on line 43 in python/lsst/pipe/base/pipeline_graph/visualization/_status.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_status.py#L42-L43

Added lines #L42 - L43 were not covered by tests
MergedNodeKey,
merge_graph_input_trees,
merge_graph_intermediates,
merge_graph_output_trees,
)
from ._options import NodeAttributeOptions
from ._printer import make_default_printer
import dataclasses
from typing import Protocol, overload, Literal, TYPE_CHECKING
from .._nodes import NodeKey, NodeType

Check warning on line 53 in python/lsst/pipe/base/pipeline_graph/visualization/_status.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_status.py#L49-L53

Added lines #L49 - L53 were not covered by tests

if TYPE_CHECKING:
from ... import quantum_provenance_graph as qpg


@dataclasses.dataclass
class TaskStatusInfo:
expected: int
succeded: int
failed: int
blocked: int
ready: int | None = None
running: int | None = None
wonky: int | None = None

Check warning on line 67 in python/lsst/pipe/base/pipeline_graph/visualization/_status.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_status.py#L59-L67

Added lines #L59 - L67 were not covered by tests


@dataclasses.dataclass
class DatasetTypeStatusInfo:
expected: int
produced: int

Check warning on line 73 in python/lsst/pipe/base/pipeline_graph/visualization/_status.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_status.py#L70-L73

Added lines #L70 - L73 were not covered by tests


@dataclasses.dataclass
class NodeStatusOptions:

Check warning on line 77 in python/lsst/pipe/base/pipeline_graph/visualization/_status.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_status.py#L76-L77

Added lines #L76 - L77 were not covered by tests
# Add colors here.
pass

Check warning on line 79 in python/lsst/pipe/base/pipeline_graph/visualization/_status.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_status.py#L79

Added line #L79 was not covered by tests


class StatusAnnotator(Protocol):

Check warning on line 82 in python/lsst/pipe/base/pipeline_graph/visualization/_status.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_status.py#L82

Added line #L82 was not covered by tests
"""Annotate a networkx graph of tasks and possibly dataset types with
status information."""

@overload
def __call__(self, xgraph: networkx.DiGraph, dataset_types: Literal[False]) -> None:
...

Check warning on line 88 in python/lsst/pipe/base/pipeline_graph/visualization/_status.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_status.py#L86-L88

Added lines #L86 - L88 were not covered by tests

@overload
def __call__(self, xgraph: networkx.MultiDiGraph, dataset_types: Literal[True]) -> None:
...

Check warning on line 92 in python/lsst/pipe/base/pipeline_graph/visualization/_status.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_status.py#L90-L92

Added lines #L90 - L92 were not covered by tests

def __call__(self, xgraph: networkx.DiGraph | networkx.MultiDiGraph, dataset_types: bool) -> None:
...

Check warning on line 95 in python/lsst/pipe/base/pipeline_graph/visualization/_status.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_status.py#L94-L95

Added lines #L94 - L95 were not covered by tests


class QuantumProvenanceGraphStatusAnnotator:

Check warning on line 98 in python/lsst/pipe/base/pipeline_graph/visualization/_status.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_status.py#L98

Added line #L98 was not covered by tests
"""..."""

def __init__(self, qpg_summary: qpg.Summary) -> None:
self.qpg_summary = qpg_summary

Check warning on line 102 in python/lsst/pipe/base/pipeline_graph/visualization/_status.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_status.py#L101-L102

Added lines #L101 - L102 were not covered by tests

@overload
def __call__(self, xgraph: networkx.DiGraph, dataset_types: Literal[False]) -> None:
...

Check warning on line 106 in python/lsst/pipe/base/pipeline_graph/visualization/_status.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_status.py#L104-L106

Added lines #L104 - L106 were not covered by tests

@overload
def __call__(self, xgraph: networkx.MultiDiGraph, dataset_types: Literal[True]) -> None:
...

Check warning on line 110 in python/lsst/pipe/base/pipeline_graph/visualization/_status.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_status.py#L108-L110

Added lines #L108 - L110 were not covered by tests

def __call__(self, xgraph: networkx.DiGraph | networkx.MultiDiGraph, dataset_types: bool) -> None:

Check warning on line 112 in python/lsst/pipe/base/pipeline_graph/visualization/_status.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_status.py#L112

Added line #L112 was not covered by tests
for task_label, task_summary in self.qpg_summary.tasks.items():
task_status_info = TaskStatusInfo(

Check warning on line 114 in python/lsst/pipe/base/pipeline_graph/visualization/_status.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_status.py#L114

Added line #L114 was not covered by tests
expected=task_summary.n_expected,
succeded=task_summary.n_successful,
failed=task_summary.n_failed,
blocked=task_summary.n_blocked,
wonky=task_summary.n_wonky,
)
# Note: `ready` and `running` are for bps! For bps, we want to add
# `pending` to `ready`.

key = NodeKey(NodeType.TASK, task_label)
xgraph.nodes[key]["status"] = task_status_info

Check warning on line 125 in python/lsst/pipe/base/pipeline_graph/visualization/_status.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_status.py#L124-L125

Added lines #L124 - L125 were not covered by tests

if dataset_types:
for dataset_type_name, dataset_type_summary in self.qpg_summary.datasets.items():
dataset_type_status_info = DatasetTypeStatusInfo(

Check warning on line 129 in python/lsst/pipe/base/pipeline_graph/visualization/_status.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_status.py#L129

Added line #L129 was not covered by tests
expected=dataset_type_summary.n_expected,
produced=dataset_type_summary.n_visible + dataset_type_summary.n_shadowed,
)

key = NodeKey(NodeType.DATASET_TYPE, dataset_type_name)
xgraph.nodes[key]["status"] = dataset_type_status_info

Check warning on line 135 in python/lsst/pipe/base/pipeline_graph/visualization/_status.py

View check run for this annotation

Codecov / codecov/patch

python/lsst/pipe/base/pipeline_graph/visualization/_status.py#L134-L135

Added lines #L134 - L135 were not covered by tests

0 comments on commit 217fef6

Please sign in to comment.