Skip to content

tracker

Full name: tenets.core.momentum.tracker

tracker

Velocity tracker module for development momentum analysis.

This module provides the main tracking functionality for development velocity and momentum. It analyzes git history to understand development patterns, team productivity, and project velocity trends over time.

The VelocityTracker class orchestrates the analysis of commits, code changes, and contributor activity to provide actionable insights into team momentum.

Classes

DailyVelocitydataclass

Python
DailyVelocity(date: datetime, commits: int = 0, lines_added: int = 0, lines_removed: int = 0, files_changed: int = 0, contributors: Set[str] = set(), pull_requests: int = 0, issues_closed: int = 0, velocity_points: float = 0.0, productivity_score: float = 0.0)

Velocity metrics for a single day.

Tracks development activity and productivity for a specific day, used for building velocity trends and burndown charts.

ATTRIBUTEDESCRIPTION
date

Date of activity

TYPE:datetime

commits

Number of commits

TYPE:int

lines_added

Lines of code added

TYPE:int

lines_removed

Lines of code removed

TYPE:int

files_changed

Number of files modified

TYPE:int

contributors

Set of active contributors

TYPE:Set[str]

pull_requests

Number of PRs merged

TYPE:int

issues_closed

Number of issues closed

TYPE:int

velocity_points

Calculated velocity points

TYPE:float

productivity_score

Daily productivity score

TYPE:float

Attributes
net_linesproperty
Python
net_lines: int

Calculate net lines changed.

RETURNSDESCRIPTION
int

Lines added minus lines removed

TYPE:int

contributor_countproperty
Python
contributor_count: int

Get number of unique contributors.

RETURNSDESCRIPTION
int

Unique contributor count

TYPE:int

is_activeproperty
Python
is_active: bool

Check if this was an active day.

RETURNSDESCRIPTION
bool

True if any activity occurred

TYPE:bool

WeeklyVelocitydataclass

Python
WeeklyVelocity(week_start: datetime, week_end: datetime, week_number: int, daily_velocities: List[DailyVelocity] = list(), total_commits: int = 0, total_lines_changed: int = 0, unique_contributors: Set[str] = set(), avg_daily_velocity: float = 0.0, velocity_variance: float = 0.0, sprint_completion: Optional[float] = None)

Velocity metrics aggregated by week.

Provides week-level velocity metrics for sprint tracking and longer-term trend analysis.

ATTRIBUTEDESCRIPTION
week_start

Start date of the week

TYPE:datetime

week_end

End date of the week

TYPE:datetime

week_number

Week number in year

TYPE:int

daily_velocities

List of daily velocities

TYPE:List[DailyVelocity]

total_commits

Total commits in week

TYPE:int

total_lines_changed

Total lines changed

TYPE:int

unique_contributors

Unique contributors in week

TYPE:Set[str]

avg_daily_velocity

Average daily velocity

TYPE:float

velocity_variance

Variance in daily velocity

TYPE:float

sprint_completion

Sprint completion percentage if applicable

TYPE:Optional[float]

Attributes
active_daysproperty
Python
active_days: int

Count active days in the week.

RETURNSDESCRIPTION
int

Number of days with activity

TYPE:int

productivity_scoreproperty
Python
productivity_score: float

Calculate weekly productivity score.

RETURNSDESCRIPTION
float

Productivity score (0-100)

TYPE:float

ContributorVelocitydataclass

Python
ContributorVelocity(name: str, email: str, commits: int = 0, lines_added: int = 0, lines_removed: int = 0, files_touched: Set[str] = set(), active_days: Set[str] = set(), first_commit: Optional[datetime] = None, last_commit: Optional[datetime] = None, velocity_trend: str = 'stable', productivity_score: float = 0.0, consistency_score: float = 0.0, impact_score: float = 0.0, collaboration_score: float = 0.0, specialization_areas: List[str] = list())

Velocity metrics for an individual contributor.

Tracks individual developer productivity and contribution patterns to understand team dynamics and individual performance.

ATTRIBUTEDESCRIPTION
name

Contributor name

TYPE:str

email

Contributor email

TYPE:str

commits

Total commits

TYPE:int

lines_added

Total lines added

TYPE:int

lines_removed

Total lines removed

TYPE:int

files_touched

Set of files modified

TYPE:Set[str]

active_days

Days with commits

TYPE:Set[str]

first_commit

First commit date in period

TYPE:Optional[datetime]

last_commit

Last commit date in period

TYPE:Optional[datetime]

velocity_trend

Individual velocity trend

TYPE:str

productivity_score

Individual productivity score

TYPE:float

consistency_score

Consistency of contributions

TYPE:float

impact_score

Impact/influence score

TYPE:float

collaboration_score

Collaboration with others

TYPE:float

specialization_areas

Areas of expertise

TYPE:List[str]

Attributes
net_linesproperty
Python
net_lines: int

Calculate net lines contributed.

RETURNSDESCRIPTION
int

Lines added minus lines removed

TYPE:int

avg_commit_sizeproperty
Python
avg_commit_size: float

Calculate average commit size.

RETURNSDESCRIPTION
float

Average lines changed per commit

TYPE:float

daily_commit_rateproperty
Python
daily_commit_rate: float

Calculate average commits per active day.

RETURNSDESCRIPTION
float

Commits per active day

TYPE:float

MomentumReportdataclass

Python
MomentumReport(period_start: datetime, period_end: datetime, total_commits: int = 0, total_contributors: int = 0, active_contributors: int = 0, momentum_metrics: Optional[MomentumMetrics] = None, velocity_trend: Optional[VelocityTrend] = None, sprint_metrics: Optional[SprintMetrics] = None, team_metrics: Optional[TeamMetrics] = None, individual_velocities: List[ContributorVelocity] = list(), daily_breakdown: List[DailyVelocity] = list(), weekly_breakdown: List[WeeklyVelocity] = list(), productivity_metrics: Optional[ProductivityMetrics] = None, recommendations: List[str] = list(), health_score: float = 0.0)

Comprehensive momentum and velocity analysis report.

Aggregates all velocity metrics and trends to provide a complete picture of development momentum and team productivity.

ATTRIBUTEDESCRIPTION
period_start

Start date of analysis period

TYPE:datetime

period_end

End date of analysis period

TYPE:datetime

total_commits

Total commits in period

TYPE:int

total_contributors

Total unique contributors

TYPE:int

active_contributors

Currently active contributors

TYPE:int

momentum_metrics

Overall momentum metrics

TYPE:Optional[MomentumMetrics]

velocity_trend

Velocity trend analysis

TYPE:Optional[VelocityTrend]

sprint_metrics

Sprint-based metrics

TYPE:Optional[SprintMetrics]

team_metrics

Team-level metrics

TYPE:Optional[TeamMetrics]

individual_velocities

Individual contributor velocities

TYPE:List[ContributorVelocity]

daily_breakdown

Daily velocity breakdown

TYPE:List[DailyVelocity]

weekly_breakdown

Weekly velocity breakdown

TYPE:List[WeeklyVelocity]

productivity_metrics

Productivity analysis

TYPE:Optional[ProductivityMetrics]

recommendations

Actionable recommendations

TYPE:List[str]

health_score

Overall momentum health score

TYPE:float

Attributes
avg_daily_velocityproperty
Python
avg_daily_velocity: float

Calculate average daily velocity.

RETURNSDESCRIPTION
float

Average velocity per day

TYPE:float

velocity_stabilityproperty
Python
velocity_stability: float

Calculate velocity stability score.

Lower variance indicates more stable/predictable velocity.

RETURNSDESCRIPTION
float

Stability score (0-100)

TYPE:float

Functions
to_dict
Python
to_dict() -> Dict[str, Any]

Convert report to dictionary.

RETURNSDESCRIPTION
Dict[str, Any]

Dict[str, Any]: Dictionary representation

Source code in tenets/core/momentum/tracker.py
Python
def to_dict(self) -> Dict[str, Any]:
    """Convert report to dictionary.

    Returns:
        Dict[str, Any]: Dictionary representation
    """
    return {
        "period": {
            "start": self.period_start.isoformat(),
            "end": self.period_end.isoformat(),
            "days": (self.period_end - self.period_start).days,
        },
        "summary": {
            "total_commits": self.total_commits,
            "total_contributors": self.total_contributors,
            "active_contributors": self.active_contributors,
            "health_score": round(self.health_score, 1),
        },
        "momentum": self.momentum_metrics.to_dict() if self.momentum_metrics else {},
        "velocity_trend": self.velocity_trend.to_dict() if self.velocity_trend else {},
        "sprint_metrics": self.sprint_metrics.to_dict() if self.sprint_metrics else {},
        "team_metrics": self.team_metrics.to_dict() if self.team_metrics else {},
        "productivity": (
            self.productivity_metrics.to_dict() if self.productivity_metrics else {}
        ),
        "top_contributors": [
            {
                "name": c.name,
                "commits": c.commits,
                "productivity": round(c.productivity_score, 1),
            }
            for c in sorted(self.individual_velocities, key=lambda x: x.commits, reverse=True)[
                :10
            ]
        ],
        "recommendations": self.recommendations,
    }

VelocityTracker

Python
VelocityTracker(config: TenetsConfig)

Main tracker for development velocity and momentum.

Orchestrates the analysis of git history to track development velocity, team productivity, and momentum trends over time.

ATTRIBUTEDESCRIPTION
config

Configuration object

logger

Logger instance

git_analyzer

Git analyzer instance

TYPE:Optional[GitAnalyzer]

Initialize velocity tracker.

PARAMETERDESCRIPTION
config

TenetsConfig instance

TYPE:TenetsConfig

Source code in tenets/core/momentum/tracker.py
Python
def __init__(self, config: TenetsConfig):
    """Initialize velocity tracker.

    Args:
        config: TenetsConfig instance
    """
    self.config = config
    self.logger = get_logger(__name__)
    self.git_analyzer: Optional[GitAnalyzer] = None
Functions
track_momentum
Python
track_momentum(repo_path: Path, period: str = 'last-month', team: bool = False, author: Optional[str] = None, team_mapping: Optional[Dict[str, List[str]]] = None, sprint_duration: int = 14, daily_breakdown: bool = False, interval: str = 'weekly', exclude_bots: bool = True, **kwargs) -> MomentumReport

Track development momentum for a repository.

Analyzes git history to calculate velocity metrics, identify trends, and provide insights into development momentum.

PARAMETERDESCRIPTION
repo_path

Path to git repository

TYPE:Path

period

Time period to analyze (e.g., "last-month", "30 days")

TYPE:strDEFAULT:'last-month'

team

Whether to include team-wide metrics

TYPE:boolDEFAULT:False

author

Specific author to analyze

TYPE:Optional[str]DEFAULT:None

team_mapping

Optional mapping of team names to members

TYPE:Optional[Dict[str, List[str]]]DEFAULT:None

sprint_duration

Sprint length in days for sprint metrics

TYPE:intDEFAULT:14

daily_breakdown

Whether to include daily velocity data

TYPE:boolDEFAULT:False

interval

Aggregation interval (daily, weekly, monthly)

TYPE:strDEFAULT:'weekly'

exclude_bots

Whether to exclude bot commits from analysis

TYPE:boolDEFAULT:True

RETURNSDESCRIPTION
MomentumReport

Comprehensive momentum analysis

TYPE:MomentumReport

Example

tracker = VelocityTracker(config) report = tracker.track_momentum( ... Path("."), ... period="last-quarter", ... team=True ... ) print(f"Team velocity: {report.avg_daily_velocity}")

Source code in tenets/core/momentum/tracker.py
Python
def track_momentum(
    self,
    repo_path: Path,
    period: str = "last-month",
    team: bool = False,
    author: Optional[str] = None,
    team_mapping: Optional[Dict[str, List[str]]] = None,
    sprint_duration: int = 14,
    daily_breakdown: bool = False,
    interval: str = "weekly",
    exclude_bots: bool = True,
    **kwargs,  # Accept additional parameters for compatibility
) -> MomentumReport:
    """Track development momentum for a repository.

    Analyzes git history to calculate velocity metrics, identify trends,
    and provide insights into development momentum.

    Args:
        repo_path: Path to git repository
        period: Time period to analyze (e.g., "last-month", "30 days")
        team: Whether to include team-wide metrics
        author: Specific author to analyze
        team_mapping: Optional mapping of team names to members
        sprint_duration: Sprint length in days for sprint metrics
        daily_breakdown: Whether to include daily velocity data
        interval: Aggregation interval (daily, weekly, monthly)
        exclude_bots: Whether to exclude bot commits from analysis

    Returns:
        MomentumReport: Comprehensive momentum analysis

    Example:
        >>> tracker = VelocityTracker(config)
        >>> report = tracker.track_momentum(
        ...     Path("."),
        ...     period="last-quarter",
        ...     team=True
        ... )
        >>> print(f"Team velocity: {report.avg_daily_velocity}")
    """
    self.logger.debug(f"Tracking momentum for {repo_path} over {period}")
    import time

    start_time = time.time()

    # Initialize git analyzer
    self.git_analyzer = GitAnalyzer(repo_path)

    if not self.git_analyzer.is_repo():
        self.logger.warning(f"Not a git repository: {repo_path}")
        return MomentumReport(period_start=datetime.now(), period_end=datetime.now())

    # Parse period - check if since/until are provided in kwargs
    if "since" in kwargs and "until" in kwargs:
        period_start = kwargs["since"]
        period_end = kwargs["until"]
    elif "since" in kwargs:
        period_start = kwargs["since"]
        period_end = datetime.now()
    else:
        period_start, period_end = self._parse_period(period)

    # Initialize report
    report = MomentumReport(period_start=period_start, period_end=period_end)

    # Get commit data
    self.logger.info(f"Fetching commits from {period_start} to {period_end}")
    fetch_start = time.time()
    commits = self._get_commits_in_period(period_start, period_end, author, exclude_bots)
    self.logger.info(f"Fetched {len(commits)} commits in {time.time() - fetch_start:.2f}s")

    if not commits:
        self.logger.info("No commits found in period")
        return report

    # Analyze daily velocity
    analyze_start = time.time()
    daily_data = self._analyze_daily_velocity(commits, period_start, period_end)
    # Human-friendly timing (avoid confusing 0.00s output)
    _elapsed = time.time() - analyze_start
    _elapsed_str = "<0.01s" if _elapsed < 0.01 else f"{_elapsed:.2f}s"
    self.logger.info(f"Analyzed daily velocity in {_elapsed_str}")
    if daily_breakdown:
        report.daily_breakdown = daily_data

    # Analyze weekly velocity
    if interval in ["weekly", "sprint"]:
        report.weekly_breakdown = self._analyze_weekly_velocity(daily_data)

    # Analyze individual velocities
    report.individual_velocities = self._analyze_individual_velocities(
        commits, period_start, period_end
    )

    # Calculate overall metrics
    report.total_commits = len(commits)
    report.total_contributors = len(
        set(
            c.author.email
            for c in commits
            if hasattr(c, "author")
            and hasattr(c.author, "email")
            and not is_bot_commit(getattr(c.author, "name", ""), getattr(c.author, "email", ""))
        )
    )
    report.active_contributors = sum(
        1
        for v in report.individual_velocities
        if v.last_commit and (datetime.now() - v.last_commit).days <= 7
    )

    # Calculate momentum metrics
    report.momentum_metrics = calculate_momentum_metrics(
        daily_data, report.individual_velocities
    )

    # Analyze velocity trend
    report.velocity_trend = self._analyze_velocity_trend(daily_data, report.weekly_breakdown)

    # Calculate sprint metrics if requested
    if sprint_duration > 0:
        report.sprint_metrics = self._calculate_sprint_metrics(daily_data, sprint_duration)

    # Calculate team metrics if requested
    if team:
        report.team_metrics = self._calculate_team_metrics(
            report.individual_velocities, team_mapping
        )

    # Calculate productivity metrics
    report.productivity_metrics = self._calculate_productivity_metrics(report)

    # Calculate health score
    report.health_score = self._calculate_health_score(report)

    # Generate recommendations
    report.recommendations = self._generate_recommendations(report)

    self.logger.debug(
        f"Momentum tracking complete: {report.total_commits} commits, "
        f"{report.total_contributors} contributors"
    )

    return report

Functions

is_bot_commit

Python
is_bot_commit(author_name: str, author_email: str) -> bool

Check if a commit is from a bot or automated system.

PARAMETERDESCRIPTION
author_name

Commit author name

TYPE:str

author_email

Commit author email

TYPE:str

RETURNSDESCRIPTION
bool

True if commit appears to be from a bot

TYPE:bool

Source code in tenets/core/momentum/tracker.py
Python
def is_bot_commit(author_name: str, author_email: str) -> bool:
    """Check if a commit is from a bot or automated system.

    Args:
        author_name: Commit author name
        author_email: Commit author email

    Returns:
        bool: True if commit appears to be from a bot
    """
    # Convert to lowercase for case-insensitive comparison
    name_lower = author_name.lower() if author_name else ""
    email_lower = author_email.lower() if author_email else ""

    # Check exact matches
    if name_lower in BOT_PATTERNS or email_lower in BOT_PATTERNS:
        return True

    # Check patterns in name
    bot_indicators = ["[bot]", "bot", "automation", "ci", "release", "deploy"]
    for indicator in bot_indicators:
        if indicator in name_lower:
            return True

    # Check email patterns
    if "noreply" in email_lower or "bot@" in email_lower or "automated" in email_lower:
        return True

    # Check for GitHub/GitLab automated emails
    if email_lower.endswith("@users.noreply.github.com"):
        # Could be a real user, check if it has bot indicators
        if any(bot in name_lower for bot in ["bot", "action", "automated"]):
            return True

    return False

track_momentum

Python
track_momentum(repo_path: Path, period: str = 'last-month', config: Optional[TenetsConfig] = None, **kwargs) -> MomentumReport

Convenience function to track momentum.

PARAMETERDESCRIPTION
repo_path

Path to repository

TYPE:Path

period

Time period to analyze

TYPE:strDEFAULT:'last-month'

config

Optional configuration

TYPE:Optional[TenetsConfig]DEFAULT:None

**kwargs

Additional arguments for tracker

DEFAULT:{}

RETURNSDESCRIPTION
MomentumReport

Momentum analysis

TYPE:MomentumReport

Source code in tenets/core/momentum/tracker.py
Python
def track_momentum(
    repo_path: Path, period: str = "last-month", config: Optional[TenetsConfig] = None, **kwargs
) -> MomentumReport:
    """Convenience function to track momentum.

    Args:
        repo_path: Path to repository
        period: Time period to analyze
        config: Optional configuration
        **kwargs: Additional arguments for tracker

    Returns:
        MomentumReport: Momentum analysis
    """
    if config is None:
        config = TenetsConfig()

    tracker = VelocityTracker(config)
    return tracker.track_momentum(repo_path, period, **kwargs)

track_team_velocity

Python
track_team_velocity(repo_path: Path, period: str = 'last-month', team_mapping: Optional[Dict[str, List[str]]] = None, config: Optional[TenetsConfig] = None) -> TeamMetrics

Track team velocity metrics.

PARAMETERDESCRIPTION
repo_path

Path to repository

TYPE:Path

period

Time period to analyze

TYPE:strDEFAULT:'last-month'

team_mapping

Team structure mapping

TYPE:Optional[Dict[str, List[str]]]DEFAULT:None

config

Optional configuration

TYPE:Optional[TenetsConfig]DEFAULT:None

RETURNSDESCRIPTION
TeamMetrics

Team velocity metrics

TYPE:TeamMetrics

Source code in tenets/core/momentum/tracker.py
Python
def track_team_velocity(
    repo_path: Path,
    period: str = "last-month",
    team_mapping: Optional[Dict[str, List[str]]] = None,
    config: Optional[TenetsConfig] = None,
) -> TeamMetrics:
    """Track team velocity metrics.

    Args:
        repo_path: Path to repository
        period: Time period to analyze
        team_mapping: Team structure mapping
        config: Optional configuration

    Returns:
        TeamMetrics: Team velocity metrics
    """
    if config is None:
        config = TenetsConfig()

    tracker = VelocityTracker(config)
    report = tracker.track_momentum(repo_path, period, team=True, team_mapping=team_mapping)

    return report.team_metrics

track_individual_velocity

Python
track_individual_velocity(repo_path: Path, author: str, period: str = 'last-month', config: Optional[TenetsConfig] = None) -> Optional[ContributorVelocity]

Track individual contributor velocity.

PARAMETERDESCRIPTION
repo_path

Path to repository

TYPE:Path

author

Author name or email

TYPE:str

period

Time period to analyze

TYPE:strDEFAULT:'last-month'

config

Optional configuration

TYPE:Optional[TenetsConfig]DEFAULT:None

RETURNSDESCRIPTION
Optional[ContributorVelocity]

Optional[ContributorVelocity]: Individual velocity metrics

Source code in tenets/core/momentum/tracker.py
Python
def track_individual_velocity(
    repo_path: Path, author: str, period: str = "last-month", config: Optional[TenetsConfig] = None
) -> Optional[ContributorVelocity]:
    """Track individual contributor velocity.

    Args:
        repo_path: Path to repository
        author: Author name or email
        period: Time period to analyze
        config: Optional configuration

    Returns:
        Optional[ContributorVelocity]: Individual velocity metrics
    """
    if config is None:
        config = TenetsConfig()

    tracker = VelocityTracker(config)
    report = tracker.track_momentum(repo_path, period, author=author)

    # Find the specific contributor
    for velocity in report.individual_velocities:
        if author.lower() in velocity.name.lower() or author.lower() in velocity.email.lower():
            return velocity

    return None