Skip to content

contributors

Full name: tenets.viz.contributors

contributors

Contributors visualization module.

This module provides visualization capabilities for contributor metrics, including contribution distribution, collaboration patterns, and contributor activity visualizations.

Classes

ContributorVisualizer

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

Bases: BaseVisualizer

Visualizer for contributor metrics.

Creates visualizations for contributor analysis including activity charts, collaboration networks, and contribution distributions.

Initialize contributor 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/contributors.py
Python
def __init__(
    self,
    chart_config: Optional[ChartConfig] = None,
    display_config: Optional[DisplayConfig] = None,
):
    """Initialize contributor visualizer.

    Args:
        chart_config: Chart configuration
        display_config: Display configuration
    """
    super().__init__(chart_config, display_config)
    self.terminal_display = TerminalDisplay(display_config)
Functions
create_contribution_chart
Python
create_contribution_chart(contributors: List[Dict[str, Any]], metric: str = 'commits', limit: int = 10) -> Dict[str, Any]

Create contributor contribution chart.

PARAMETERDESCRIPTION
contributors

List of contributor data

TYPE:List[Dict[str, Any]]

metric

Metric to visualize (commits, lines, files)

TYPE:strDEFAULT:'commits'

limit

Maximum contributors to show

TYPE:intDEFAULT:10

RETURNSDESCRIPTION
Dict[str, Any]

Dict[str, Any]: Chart configuration

Source code in tenets/viz/contributors.py
Python
def create_contribution_chart(
    self, contributors: List[Dict[str, Any]], metric: str = "commits", limit: int = 10
) -> Dict[str, Any]:
    """Create contributor contribution chart.

    Args:
        contributors: List of contributor data
        metric: Metric to visualize (commits, lines, files)
        limit: Maximum contributors to show

    Returns:
        Dict[str, Any]: Chart configuration
    """
    # Sort by metric
    sorted_contributors = sorted(contributors, key=lambda x: x.get(metric, 0), reverse=True)[
        :limit
    ]

    labels = []
    values = []

    for contributor in sorted_contributors:
        name = contributor.get("name", contributor.get("email", "Unknown"))
        # Truncate long names
        if len(name) > 20:
            name = name[:17] + "..."
        labels.append(name)
        values.append(contributor.get(metric, 0))

    # Color based on contribution level
    total = sum(values) if values else 1
    colors = []
    for value in values:
        percentage = (value / total) * 100 if total > 0 else 0
        if percentage > 30:
            colors.append(ColorPalette.HEALTH["excellent"])
        elif percentage > 15:
            colors.append(ColorPalette.HEALTH["good"])
        elif percentage > 5:
            colors.append(ColorPalette.HEALTH["fair"])
        else:
            colors.append(ColorPalette.DEFAULT[len(colors) % len(ColorPalette.DEFAULT)])

    title_map = {
        "commits": "Commits by Contributor",
        "lines": "Lines Changed by Contributor",
        "files": "Files Touched by Contributor",
    }

    config = ChartConfig(
        type=ChartType.BAR,
        title=title_map.get(metric, f"{metric.title()} by Contributor"),
        colors=colors,
    )

    return self.create_chart(ChartType.BAR, {"labels": labels, "values": values}, config)
create_activity_timeline
Python
create_activity_timeline(activity_data: List[Dict[str, Any]], contributor: Optional[str] = None) -> Dict[str, Any]

Create contributor activity timeline.

PARAMETERDESCRIPTION
activity_data

Activity data points with dates

TYPE:List[Dict[str, Any]]

contributor

Specific contributor to highlight

TYPE:Optional[str]DEFAULT:None

RETURNSDESCRIPTION
Dict[str, Any]

Dict[str, Any]: Line chart configuration

Source code in tenets/viz/contributors.py
Python
def create_activity_timeline(
    self, activity_data: List[Dict[str, Any]], contributor: Optional[str] = None
) -> Dict[str, Any]:
    """Create contributor activity timeline.

    Args:
        activity_data: Activity data points with dates
        contributor: Specific contributor to highlight

    Returns:
        Dict[str, Any]: Line chart configuration
    """
    # Group by date
    date_activity = {}

    for activity in activity_data:
        date = activity.get("date", "")
        if not date:
            continue

        if date not in date_activity:
            date_activity[date] = {"commits": 0, "contributors": set()}

        date_activity[date]["commits"] += activity.get("commits", 0)
        if "contributor" in activity:
            date_activity[date]["contributors"].add(activity["contributor"])

    # Sort by date
    sorted_dates = sorted(date_activity.keys())

    labels = sorted_dates
    commits = [date_activity[d]["commits"] for d in sorted_dates]
    active_contributors = [len(date_activity[d]["contributors"]) for d in sorted_dates]

    datasets = [
        {
            "label": "Commits",
            "data": commits,
            "borderColor": ColorPalette.DEFAULT[0],
            "yAxisID": "y",
        },
        {
            "label": "Active Contributors",
            "data": active_contributors,
            "borderColor": ColorPalette.DEFAULT[1],
            "yAxisID": "y1",
        },
    ]

    config = ChartConfig(type=ChartType.LINE, title="Contributor Activity Over Time")

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

    # Add dual y-axis configuration
    chart_config["options"]["scales"] = {
        "y": {
            "type": "linear",
            "display": True,
            "position": "left",
            "title": {"display": True, "text": "Commits"},
        },
        "y1": {
            "type": "linear",
            "display": True,
            "position": "right",
            "title": {"display": True, "text": "Contributors"},
            "grid": {"drawOnChartArea": False},
        },
    }

    return chart_config
create_collaboration_network
Python
create_collaboration_network(collaboration_data: Dict[Tuple[str, str], int], min_weight: int = 2) -> Dict[str, Any]

Create collaboration network graph.

PARAMETERDESCRIPTION
collaboration_data

Dictionary of (contributor1, contributor2) -> weight

TYPE:Dict[Tuple[str, str], int]

min_weight

Minimum collaboration weight to include

TYPE:intDEFAULT:2

RETURNSDESCRIPTION
Dict[str, Any]

Dict[str, Any]: Network graph configuration

Source code in tenets/viz/contributors.py
Python
def create_collaboration_network(
    self, collaboration_data: Dict[Tuple[str, str], int], min_weight: int = 2
) -> Dict[str, Any]:
    """Create collaboration network graph.

    Args:
        collaboration_data: Dictionary of (contributor1, contributor2) -> weight
        min_weight: Minimum collaboration weight to include

    Returns:
        Dict[str, Any]: Network graph configuration
    """
    # Build nodes and edges
    nodes = set()
    edges = []

    for (contributor1, contributor2), weight in collaboration_data.items():
        if weight >= min_weight:
            nodes.add(contributor1)
            nodes.add(contributor2)
            edges.append({"source": contributor1, "target": contributor2, "weight": weight})

    # Create node list with sizing based on degree
    node_degree = {}
    for edge in edges:
        node_degree[edge["source"]] = node_degree.get(edge["source"], 0) + edge["weight"]
        node_degree[edge["target"]] = node_degree.get(edge["target"], 0) + edge["weight"]

    node_list = []
    for node in nodes:
        degree = node_degree.get(node, 1)
        node_list.append(
            {
                "id": node,
                "label": node[:20] + "..." if len(node) > 20 else node,
                "size": min(50, 10 + degree * 2),
            }
        )

    config = ChartConfig(type=ChartType.NETWORK, title="Contributor Collaboration Network")

    return self.create_chart(
        ChartType.NETWORK, {"nodes": node_list, "edges": edges, "layout": "force"}, config
    )
create_distribution_pie
Python
create_distribution_pie(contributors: List[Dict[str, Any]], metric: str = 'commits', top_n: int = 5) -> Dict[str, Any]

Create contribution distribution pie chart.

PARAMETERDESCRIPTION
contributors

List of contributor data

TYPE:List[Dict[str, Any]]

metric

Metric to visualize

TYPE:strDEFAULT:'commits'

top_n

Number of top contributors to show individually

TYPE:intDEFAULT:5

RETURNSDESCRIPTION
Dict[str, Any]

Dict[str, Any]: Pie chart configuration

Source code in tenets/viz/contributors.py
Python
def create_distribution_pie(
    self, contributors: List[Dict[str, Any]], metric: str = "commits", top_n: int = 5
) -> Dict[str, Any]:
    """Create contribution distribution pie chart.

    Args:
        contributors: List of contributor data
        metric: Metric to visualize
        top_n: Number of top contributors to show individually

    Returns:
        Dict[str, Any]: Pie chart configuration
    """
    # Sort by metric
    sorted_contributors = sorted(contributors, key=lambda x: x.get(metric, 0), reverse=True)

    labels = []
    values = []

    # Add top contributors
    for contributor in sorted_contributors[:top_n]:
        name = contributor.get("name", contributor.get("email", "Unknown"))
        if len(name) > 15:
            name = name[:12] + "..."
        labels.append(name)
        values.append(contributor.get(metric, 0))

    # Add "Others" if there are more contributors
    if len(sorted_contributors) > top_n:
        others_value = sum(c.get(metric, 0) for c in sorted_contributors[top_n:])
        if others_value > 0:
            labels.append("Others")
            values.append(others_value)

    config = ChartConfig(type=ChartType.PIE, title=f"{metric.title()} Distribution")

    return self.create_chart(ChartType.PIE, {"labels": labels, "values": values}, config)
create_bus_factor_gauge
Python
create_bus_factor_gauge(bus_factor: int, total_contributors: int) -> Dict[str, Any]

Create bus factor gauge chart.

PARAMETERDESCRIPTION
bus_factor

Current bus factor

TYPE:int

total_contributors

Total number of contributors

TYPE:int

RETURNSDESCRIPTION
Dict[str, Any]

Dict[str, Any]: Gauge chart configuration

Source code in tenets/viz/contributors.py
Python
def create_bus_factor_gauge(self, bus_factor: int, total_contributors: int) -> Dict[str, Any]:
    """Create bus factor gauge chart.

    Args:
        bus_factor: Current bus factor
        total_contributors: Total number of contributors

    Returns:
        Dict[str, Any]: Gauge chart configuration
    """
    # Calculate percentage (higher is better)
    percentage = (bus_factor / max(1, total_contributors)) * 100

    # Determine color based on bus factor
    if bus_factor <= 1:
        color = ColorPalette.SEVERITY["critical"]
    elif bus_factor <= 2:
        color = ColorPalette.SEVERITY["high"]
    elif bus_factor <= 3:
        color = ColorPalette.SEVERITY["medium"]
    else:
        color = ColorPalette.HEALTH["excellent"]

    config = ChartConfig(
        type=ChartType.GAUGE, title=f"Bus Factor: {bus_factor}", colors=[color]
    )

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

Display contributor analysis in terminal.

PARAMETERDESCRIPTION
contributor_data

Contributor analysis data

TYPE:Dict[str, Any]

show_details

Whether to show detailed breakdown

TYPE:boolDEFAULT:True

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

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

    # Display summary metrics
    summary_data = {
        "Total Contributors": contributor_data.get("total_contributors", 0),
        "Active Contributors": contributor_data.get("active_contributors", 0),
        "Bus Factor": contributor_data.get("bus_factor", 0),
        "Avg Commits/Contributor": self.format_number(
            contributor_data.get("avg_commits_per_contributor", 0), precision=1
        ),
    }

    self.terminal_display.display_metrics(summary_data, title="Summary")

    # Display top contributors table
    if show_details and "contributors" in contributor_data:
        headers = ["Contributor", "Commits", "Lines", "Files", "Activity"]
        rows = []

        for contributor in contributor_data["contributors"][:10]:
            activity = self._get_activity_indicator(
                contributor.get("last_commit_days_ago", 999)
            )
            rows.append(
                [
                    contributor.get("name", "Unknown")[:30],
                    str(contributor.get("commits", 0)),
                    self.format_number(contributor.get("lines", 0)),
                    str(contributor.get("files", 0)),
                    activity,
                ]
            )

        self.terminal_display.display_table(headers, rows, title="Top Contributors")

    # Display collaboration matrix if available
    if "collaboration_matrix" in contributor_data:
        self._display_collaboration_matrix(contributor_data["collaboration_matrix"])

    # Display warnings
    warnings = []
    bus_factor = contributor_data.get("bus_factor", 0)
    if bus_factor <= 1:
        warnings.append("⚠️  Critical: Bus factor is 1 - single point of failure")
    elif bus_factor <= 2:
        warnings.append("⚠️  Warning: Low bus factor - knowledge concentration risk")

    if warnings:
        self.terminal_display.display_list(warnings, title="Warnings", style="bullet")
create_retention_chart
Python
create_retention_chart(retention_data: List[Dict[str, Any]]) -> Dict[str, Any]

Create contributor retention chart.

PARAMETERDESCRIPTION
retention_data

Retention data over time

TYPE:List[Dict[str, Any]]

RETURNSDESCRIPTION
Dict[str, Any]

Dict[str, Any]: Line chart configuration

Source code in tenets/viz/contributors.py
Python
def create_retention_chart(self, retention_data: List[Dict[str, Any]]) -> Dict[str, Any]:
    """Create contributor retention chart.

    Args:
        retention_data: Retention data over time

    Returns:
        Dict[str, Any]: Line chart configuration
    """
    labels = []
    active = []
    new = []
    left = []

    for point in retention_data:
        labels.append(point.get("period", ""))
        active.append(point.get("active", 0))
        new.append(point.get("new", 0))
        left.append(point.get("left", 0))

    datasets = [
        {
            "label": "Active",
            "data": active,
            "borderColor": ColorPalette.HEALTH["excellent"],
            "backgroundColor": ColorPalette.HEALTH["excellent"] + "20",
            "fill": True,
        },
        {
            "label": "New",
            "data": new,
            "borderColor": ColorPalette.HEALTH["good"],
            "fill": False,
        },
        {
            "label": "Left",
            "data": left,
            "borderColor": ColorPalette.SEVERITY["high"],
            "fill": False,
        },
    ]

    config = ChartConfig(type=ChartType.LINE, title="Contributor Retention")

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