Skip to content

momentum

Full name: tenets.viz.momentum

momentum

Momentum visualization module.

This module provides visualization capabilities for development momentum and velocity metrics, including burndown charts, velocity trends, and sprint analytics.

Classes

MomentumVisualizer

Python
MomentumVisualizer(chart_config: Optional[ChartConfig] = None, display_config: Optional[DisplayConfig] = None)

Bases: BaseVisualizer

Visualizer for momentum and velocity metrics.

Creates visualizations for development velocity, sprint progress, and team momentum analytics.

Initialize momentum visualizer.

PARAMETERDESCRIPTION
chart_config

Chart configuration

TYPE:Optional[ChartConfig]DEFAULT:None

display_config

Display configuration

TYPE:Optional[DisplayConfig]DEFAULT:None

Source code in tenets/viz/momentum.py
Python
def __init__(
    self,
    chart_config: Optional[ChartConfig] = None,
    display_config: Optional[DisplayConfig] = None,
):
    """Initialize momentum visualizer.

    Args:
        chart_config: Chart configuration
        display_config: Display configuration
    """
    super().__init__(chart_config, display_config)
    self.terminal_display = TerminalDisplay(display_config)
Functions
create_velocity_chart
Python
create_velocity_chart(velocity_data: List[Dict[str, Any]], show_trend: bool = True) -> Dict[str, Any]

Create velocity trend chart.

PARAMETERDESCRIPTION
velocity_data

List of velocity data points

TYPE:List[Dict[str, Any]]

show_trend

Whether to show trend line

TYPE:boolDEFAULT:True

RETURNSDESCRIPTION
Dict[str, Any]

Dict[str, Any]: Line chart configuration

Source code in tenets/viz/momentum.py
Python
def create_velocity_chart(
    self, velocity_data: List[Dict[str, Any]], show_trend: bool = True
) -> Dict[str, Any]:
    """Create velocity trend chart.

    Args:
        velocity_data: List of velocity data points
        show_trend: Whether to show trend line

    Returns:
        Dict[str, Any]: Line chart configuration
    """
    labels = []
    velocity = []

    for point in velocity_data:
        labels.append(point.get("period", ""))
        velocity.append(point.get("velocity", 0))

    datasets = [
        {
            "label": "Velocity",
            "data": velocity,
            "borderColor": ColorPalette.DEFAULT[0],
            "backgroundColor": ColorPalette.DEFAULT[0] + "20",
            "fill": True,
        }
    ]

    # Add trend line if requested
    if show_trend and len(velocity) > 1:
        trend_values = self._calculate_trend_line(velocity)
        datasets.append(
            {
                "label": "Trend",
                "data": trend_values,
                "borderColor": ColorPalette.DEFAULT[1],
                "borderDash": [5, 5],
                "fill": False,
                "pointRadius": 0,
            }
        )

    config = ChartConfig(type=ChartType.LINE, title="Development Velocity")

    return self.create_chart(ChartType.LINE, {"labels": labels, "datasets": datasets}, config)
create_burndown_chart
Python
create_burndown_chart(burndown_data: Dict[str, Any]) -> Dict[str, Any]

Create sprint burndown chart.

PARAMETERDESCRIPTION
burndown_data

Burndown data with ideal and actual lines

TYPE:Dict[str, Any]

RETURNSDESCRIPTION
Dict[str, Any]

Dict[str, Any]: Line chart configuration

Source code in tenets/viz/momentum.py
Python
def create_burndown_chart(self, burndown_data: Dict[str, Any]) -> Dict[str, Any]:
    """Create sprint burndown chart.

    Args:
        burndown_data: Burndown data with ideal and actual lines

    Returns:
        Dict[str, Any]: Line chart configuration
    """
    labels = burndown_data.get("dates", [])
    ideal = burndown_data.get("ideal_line", [])
    actual = burndown_data.get("actual_line", [])

    datasets = [
        {
            "label": "Ideal",
            "data": ideal,
            "borderColor": ColorPalette.DEFAULT[2],
            "borderDash": [10, 5],
            "fill": False,
        },
        {
            "label": "Actual",
            "data": actual,
            "borderColor": ColorPalette.DEFAULT[0],
            "backgroundColor": ColorPalette.DEFAULT[0] + "10",
            "fill": True,
        },
    ]

    # Add scope changes if present
    if "scope_changes" in burndown_data:
        datasets.append(
            {
                "label": "Scope Changes",
                "data": burndown_data["scope_changes"],
                "type": "bar",
                "backgroundColor": ColorPalette.SEVERITY["medium"] + "50",
            }
        )

    config = ChartConfig(type=ChartType.LINE, title="Sprint Burndown")

    return self.create_chart(ChartType.LINE, {"labels": labels, "datasets": datasets}, config)
create_sprint_comparison
Python
create_sprint_comparison(sprint_data: List[Dict[str, Any]]) -> Dict[str, Any]

Create sprint comparison chart.

PARAMETERDESCRIPTION
sprint_data

List of sprint metrics

TYPE:List[Dict[str, Any]]

RETURNSDESCRIPTION
Dict[str, Any]

Dict[str, Any]: Grouped bar chart configuration

Source code in tenets/viz/momentum.py
Python
def create_sprint_comparison(self, sprint_data: List[Dict[str, Any]]) -> Dict[str, Any]:
    """Create sprint comparison chart.

    Args:
        sprint_data: List of sprint metrics

    Returns:
        Dict[str, Any]: Grouped bar chart configuration
    """
    labels = []
    planned = []
    completed = []
    carryover = []

    for sprint in sprint_data:
        labels.append(sprint.get("name", ""))
        planned.append(sprint.get("planned", 0))
        completed.append(sprint.get("completed", 0))
        carryover.append(sprint.get("carryover", 0))

    datasets = [
        {"label": "Planned", "data": planned, "backgroundColor": ColorPalette.DEFAULT[0]},
        {
            "label": "Completed",
            "data": completed,
            "backgroundColor": ColorPalette.HEALTH["good"],
        },
        {
            "label": "Carried Over",
            "data": carryover,
            "backgroundColor": ColorPalette.SEVERITY["medium"],
        },
    ]

    config = ChartConfig(type=ChartType.BAR, title="Sprint Comparison")

    return {
        "type": "bar",
        "data": {"labels": labels, "datasets": datasets},
        "options": self._get_chart_options(config),
    }
create_team_velocity_radar
Python
create_team_velocity_radar(team_metrics: Dict[str, float]) -> Dict[str, Any]

Create team velocity radar chart.

PARAMETERDESCRIPTION
team_metrics

Dictionary of metric name to value

TYPE:Dict[str, float]

RETURNSDESCRIPTION
Dict[str, Any]

Dict[str, Any]: Radar chart configuration

Source code in tenets/viz/momentum.py
Python
def create_team_velocity_radar(self, team_metrics: Dict[str, float]) -> Dict[str, Any]:
    """Create team velocity radar chart.

    Args:
        team_metrics: Dictionary of metric name to value

    Returns:
        Dict[str, Any]: Radar chart configuration
    """
    # Normalize metrics to 0-100 scale
    labels = []
    values = []

    metric_max = {
        "velocity": 100,
        "predictability": 100,
        "quality": 100,
        "collaboration": 100,
        "innovation": 100,
        "delivery": 100,
    }

    for metric, value in team_metrics.items():
        labels.append(metric.replace("_", " ").title())
        max_val = metric_max.get(metric, 100)
        normalized = min(100, (value / max_val) * 100)
        values.append(normalized)

    datasets = [
        {
            "label": "Current Sprint",
            "data": values,
            "borderColor": ColorPalette.DEFAULT[0],
            "backgroundColor": ColorPalette.DEFAULT[0] + "40",
        }
    ]

    # Add previous sprint if available
    if "previous" in team_metrics:
        prev_values = []
        for metric in team_metrics["previous"]:
            max_val = metric_max.get(metric, 100)
            normalized = min(100, (team_metrics["previous"][metric] / max_val) * 100)
            prev_values.append(normalized)

        datasets.append(
            {
                "label": "Previous Sprint",
                "data": prev_values,
                "borderColor": ColorPalette.DEFAULT[1],
                "backgroundColor": ColorPalette.DEFAULT[1] + "20",
            }
        )

    config = ChartConfig(type=ChartType.RADAR, title="Team Performance Metrics")

    return self.create_chart(ChartType.RADAR, {"labels": labels, "datasets": datasets}, config)
create_cumulative_flow
Python
create_cumulative_flow(flow_data: Dict[str, List[int]]) -> Dict[str, Any]

Create cumulative flow diagram.

PARAMETERDESCRIPTION
flow_data

Dictionary of status to daily counts

TYPE:Dict[str, List[int]]

RETURNSDESCRIPTION
Dict[str, Any]

Dict[str, Any]: Stacked area chart configuration

Source code in tenets/viz/momentum.py
Python
def create_cumulative_flow(self, flow_data: Dict[str, List[int]]) -> Dict[str, Any]:
    """Create cumulative flow diagram.

    Args:
        flow_data: Dictionary of status to daily counts

    Returns:
        Dict[str, Any]: Stacked area chart configuration
    """
    # Assume first list defines the time axis
    days = len(next(iter(flow_data.values())))
    labels = [f"Day {i + 1}" for i in range(days)]

    datasets = []
    colors = {
        "todo": ColorPalette.DEFAULT[2],
        "in_progress": ColorPalette.DEFAULT[0],
        "review": ColorPalette.DEFAULT[1],
        "done": ColorPalette.HEALTH["good"],
        "blocked": ColorPalette.SEVERITY["high"],
    }

    for status, values in flow_data.items():
        datasets.append(
            {
                "label": status.replace("_", " ").title(),
                "data": values,
                "backgroundColor": colors.get(
                    status, ColorPalette.DEFAULT[len(datasets) % len(ColorPalette.DEFAULT)]
                ),
                "fill": True,
            }
        )

    config = ChartConfig(type=ChartType.LINE, title="Cumulative Flow Diagram")

    chart_config = self.create_chart(
        ChartType.LINE, {"labels": labels, "datasets": datasets}, config
    )

    # Make it stacked
    chart_config["options"]["scales"] = {"y": {"stacked": True}}

    return chart_config
create_productivity_gauge
Python
create_productivity_gauge(productivity_score: float) -> Dict[str, Any]

Create productivity gauge chart.

PARAMETERDESCRIPTION
productivity_score

Productivity score (0-100)

TYPE:float

RETURNSDESCRIPTION
Dict[str, Any]

Dict[str, Any]: Gauge chart configuration

Source code in tenets/viz/momentum.py
Python
def create_productivity_gauge(self, productivity_score: float) -> Dict[str, Any]:
    """Create productivity gauge chart.

    Args:
        productivity_score: Productivity score (0-100)

    Returns:
        Dict[str, Any]: Gauge chart configuration
    """
    # Determine color based on score
    if productivity_score >= 80:
        color = ColorPalette.HEALTH["excellent"]
    elif productivity_score >= 60:
        color = ColorPalette.HEALTH["good"]
    elif productivity_score >= 40:
        color = ColorPalette.HEALTH["fair"]
    else:
        color = ColorPalette.SEVERITY["high"]

    config = ChartConfig(
        type=ChartType.GAUGE,
        title=f"Team Productivity: {productivity_score:.0f}%",
        colors=[color],
    )

    return self.create_chart(ChartType.GAUGE, {"value": productivity_score, "max": 100}, config)
display_terminal
Python
display_terminal(momentum_data: Dict[str, Any], show_details: bool = True) -> None

Display momentum analysis in terminal.

PARAMETERDESCRIPTION
momentum_data

Momentum analysis data

TYPE:Dict[str, Any]

show_details

Whether to show detailed breakdown

TYPE:boolDEFAULT:True

Source code in tenets/viz/momentum.py
Python
def display_terminal(self, momentum_data: Dict[str, Any], show_details: bool = True) -> None:
    """Display momentum analysis in terminal.

    Args:
        momentum_data: Momentum analysis data
        show_details: Whether to show detailed breakdown
    """
    # Display header
    self.terminal_display.display_header("Momentum Analysis", style="double")

    # Display current sprint summary
    if "current_sprint" in momentum_data:
        sprint = momentum_data["current_sprint"]
        summary_data = {
            "Sprint": sprint.get("name", "Current"),
            "Velocity": sprint.get("velocity", 0),
            "Completed": f"{sprint.get('completed', 0)}/{sprint.get('planned', 0)}",
            "Days Remaining": sprint.get("days_remaining", 0),
        }

        self.terminal_display.display_metrics(summary_data, title="Current Sprint")

    # Display velocity trend
    if "velocity_trend" in momentum_data:
        trend = momentum_data["velocity_trend"]
        trend_symbol = "↑" if trend > 0 else "↓" if trend < 0 else "→"
        trend_color = "green" if trend > 0 else "red" if trend < 0 else "yellow"

        print(
            f"\nVelocity Trend: {self.terminal_display.colorize(trend_symbol, trend_color)} {abs(trend):.1f}%"
        )

    # Display team metrics
    if show_details and "team_metrics" in momentum_data:
        headers = ["Metric", "Value", "Target", "Status"]
        rows = []

        for metric in momentum_data["team_metrics"]:
            value = metric.get("value", 0)
            target = metric.get("target", 0)
            status = "✓" if value >= target else "✗"
            status_color = "green" if value >= target else "red"

            rows.append(
                [
                    metric.get("name", ""),
                    self.format_number(value, precision=1),
                    self.format_number(target, precision=1),
                    self.terminal_display.colorize(status, status_color),
                ]
            )

        self.terminal_display.display_table(headers, rows, title="Team Performance Metrics")

    # Display burndown status
    if "burndown" in momentum_data:
        burndown = momentum_data["burndown"]
        on_track = burndown.get("on_track", False)
        completion = burndown.get("completion_percentage", 0)

        status_text = "On Track" if on_track else "Behind Schedule"
        status_color = "green" if on_track else "red"

        print(f"\nBurndown Status: {self.terminal_display.colorize(status_text, status_color)}")
        print(f"Completion: {self.terminal_display.create_progress_bar(completion, 100)}")

    # Display recommendations
    if "recommendations" in momentum_data:
        self.terminal_display.display_list(
            momentum_data["recommendations"], title="Recommendations", style="numbered"
        )
create_contributor_velocity
Python
create_contributor_velocity(contributor_data: List[Dict[str, Any]], limit: int = 10) -> Dict[str, Any]

Create contributor velocity chart.

PARAMETERDESCRIPTION
contributor_data

List of contributor velocity data

TYPE:List[Dict[str, Any]]

limit

Maximum contributors to show

TYPE:intDEFAULT:10

RETURNSDESCRIPTION
Dict[str, Any]

Dict[str, Any]: Bar chart configuration

Source code in tenets/viz/momentum.py
Python
def create_contributor_velocity(
    self, contributor_data: List[Dict[str, Any]], limit: int = 10
) -> Dict[str, Any]:
    """Create contributor velocity chart.

    Args:
        contributor_data: List of contributor velocity data
        limit: Maximum contributors to show

    Returns:
        Dict[str, Any]: Bar chart configuration
    """
    # Sort by velocity
    sorted_data = sorted(contributor_data, key=lambda x: x.get("velocity", 0), reverse=True)[
        :limit
    ]

    labels = []
    velocity = []
    colors = []

    for contributor in sorted_data:
        labels.append(contributor.get("name", "Unknown"))
        velocity.append(contributor.get("velocity", 0))

        # Color based on trend
        trend = contributor.get("trend", "stable")
        if trend == "increasing":
            colors.append(ColorPalette.HEALTH["good"])
        elif trend == "decreasing":
            colors.append(ColorPalette.SEVERITY["medium"])
        else:
            colors.append(ColorPalette.DEFAULT[0])

    config = ChartConfig(
        type=ChartType.HORIZONTAL_BAR, title="Individual Velocity", colors=colors
    )

    return self.create_chart(
        ChartType.HORIZONTAL_BAR, {"labels": labels, "values": velocity}, config
    )