Skip to content

Instantly share code, notes, and snippets.

@iraycd
Created April 2, 2025 14:53
Show Gist options
  • Save iraycd/49d6bc8f38ab41ffa8240206d5ed02e1 to your computer and use it in GitHub Desktop.
Save iraycd/49d6bc8f38ab41ffa8240206d5ed02e1 to your computer and use it in GitHub Desktop.
Github Contritbutions

Git Contribution Analyzer

A Python tool that analyzes git contributions by developers and estimates working days based on lines of code modified.

Overview

This tool analyzes git commit history for a specific developer to provide insights into their contribution patterns, including:

  • Total commits made
  • Lines of code added and deleted
  • Estimated working days based on lines of code per day
  • Monthly distribution of work
  • Top contributions by impact

Requirements

  • Python 3.6+
  • Git repository with commit history

Installation

No installation required. Simply download the script and run it with Python:

# Make the script executable (optional)
chmod +x git_contribution_analyzer.py

Usage

python git_contribution_analyzer.py --author "developer-name" --since "YYYY-MM-DD" [options]

Arguments

Argument Required Description Default
--author Yes Git author name or email pattern -
--since Yes Start date for commit analysis (YYYY-MM-DD) -
--until No End date for commit analysis (YYYY-MM-DD) Current date
--lines-per-day No Assumed lines of code per working day 200
--output No Output directory for reports ./reports

Examples

Basic usage - analyze a developer's contributions for the current year

python git_contribution_analyzer.py --author "john.doe" --since "2025-01-01"

Specify date range and custom lines per day

python git_contribution_analyzer.py --author "john.doe" --since "2025-01-01" --until "2025-03-31" --lines-per-day 150

Analyze contributions with partial name matching

python git_contribution_analyzer.py --author "john" --since "2025-01-01" --output "./john-reports"

Output

The tool generates two report files:

  1. Text Report ({author}-commit-report.txt):

    • Summary statistics
    • Daily work breakdown
    • Monthly distribution of work
  2. Markdown Report ({author}-contribution-summary.md):

    • Formatted summary with statistics
    • Monthly distribution table
    • Top contributions by impact
    • Conclusion with estimated working days

Example Report

Here's a sample of what the markdown report might look like:

# John Doe Contribution Analysis

## Overview

This report analyzes the git commits made by John Doe, calculating estimated working days based on an assumption of 200 lines of code modified per working day.

## Summary Statistics

- **Total Commits:** 45
- **Total Lines Added:** 5,230
- **Total Lines Deleted:** 1,845
- **Total Lines Modified:** 7,075
- **Estimated Working Days:** 35.38 days (at 200 lines/day)

## Monthly Distribution

| Month         | Lines Modified | Est. Working Days | Percentage |
| ------------- | -------------- | ----------------- | ---------- |
| January 2025  | 2,540          | 12.70             | 35.89%     |
| February 2025 | 1,745          | 8.73              | 24.66%     |
| March 2025    | 2,790          | 13.95             | 39.44%     |

## Top Contributions by Impact

| Date       | Description               | Lines Modified | Est. Days |
| ---------- | ------------------------- | -------------- | --------- |
| 2025-01-15 | Feature X implementation  | 1,245          | 6.23      |
| 2025-03-10 | API refactoring           | 950            | 4.75      |
| 2025-02-22 | UI components update      | 805            | 4.03      |
| 2025-03-25 | Performance optimizations | 760            | 3.80      |
| 2025-01-07 | Database schema migration | 710            | 3.55      |

Tips

  • For more accurate results, use a specific author pattern that matches only the developer you're interested in
  • Consider what constitutes a "working day" for your team when setting the --lines-per-day parameter
  • Run the analysis in the root directory of your git repository
#!/usr/bin/env python3
import argparse
import subprocess
import os
import datetime
import sys
import calendar
from collections import defaultdict
import matplotlib.pyplot as plt
import numpy as np
def run_command(cmd):
"""Execute a command and return the output."""
try:
result = subprocess.run(
cmd, shell=True, check=True, text=True, capture_output=True)
return result.stdout
except subprocess.CalledProcessError as e:
print(f"Error executing command: {e}", file=sys.stderr)
print(f"Command output: {e.stderr}", file=sys.stderr)
return None
def generate_team_dashboard(authors, since_date, until_date=None, lines_per_day=200, output_dir="./reports"):
"""Generate a dashboard comparing multiple contributors."""
os.makedirs(output_dir, exist_ok=True)
# First, run the individual analysis for each author
results = []
for author in authors:
cmd = f'python3 git_contribution_analyzer.py --author "{author}" --since "{since_date}"'
if until_date:
cmd += f' --until "{until_date}"'
cmd += f' --lines-per-day {lines_per_day} --output "{output_dir}"'
print(f"\nAnalyzing {author}...")
output = run_command(cmd)
if output:
# Extract key metrics from output
lines = output.strip().split('\n')
working_days = float(lines[-3].split(':')[1].strip().split(' ')[0])
business_days = int(lines[-2].split(':')[1].strip().split(' ')[0])
utilization = float(lines[-1].split(':')[1].strip().rstrip('%'))
# Read the generated report to get monthly breakdown
report_path = os.path.join(
output_dir, f"{author.replace(' ', '_')}-contribution-summary.md")
monthly_data = extract_monthly_data(report_path)
results.append({
'author': author,
'working_days': working_days,
'business_days': business_days,
'utilization': utilization,
'monthly_data': monthly_data
})
# Generate comparative report
create_comparative_report(
results, since_date, until_date, lines_per_day, output_dir)
# Generate visualizations
create_visualizations(results, output_dir)
return os.path.join(output_dir, "team_dashboard.md")
def extract_monthly_data(report_path):
"""Extract monthly metrics from a contributor's report."""
monthly_data = []
try:
with open(report_path, 'r') as f:
lines = f.readlines()
# Find the Monthly Distribution table
started = False
header_count = 0
for line in lines:
line = line.strip()
if "Monthly Distribution" in line:
started = True
continue
if not started:
continue
if started and line.startswith('|'):
if header_count < 2:
header_count += 1
continue
if "Top Contributions" in line:
break
# Parse the table row
parts = [part.strip() for part in line.split('|')[1:-1]]
if len(parts) >= 6:
month = parts[0]
lines_modified = int(parts[1].replace(',', ''))
working_days = float(parts[2])
business_days = int(parts[3])
utilization = float(parts[4].rstrip('%'))
percentage = float(parts[5].rstrip('%'))
monthly_data.append({
'month': month,
'lines_modified': lines_modified,
'working_days': working_days,
'business_days': business_days,
'utilization': utilization,
'percentage': percentage
})
except Exception as e:
print(f"Error extracting monthly data from {report_path}: {e}")
return monthly_data
def create_comparative_report(results, since_date, until_date, lines_per_day, output_dir):
"""Create a markdown report comparing all contributors."""
report_path = os.path.join(output_dir, "team_dashboard.md")
with open(report_path, 'w') as f:
# Header
until_str = until_date if until_date else "now"
f.write(f"# Team Contribution Dashboard\n\n")
f.write(f"**Period:** {since_date} to {until_str} \n")
f.write(f"**Lines Per Day Assumption:** {lines_per_day} \n")
f.write(f"**Total Contributors Analyzed:** {len(results)} \n\n")
# Summary table
f.write("## Team Summary\n\n")
f.write(
"| Contributor | Est. Working Days | Business Days | Calendar Utilization |\n")
f.write(
"|-------------|------------------|---------------|---------------------|\n")
# Sort contributors by working days descending
results.sort(key=lambda x: x['working_days'], reverse=True)
total_working_days = sum(r['working_days'] for r in results)
for result in results:
f.write(f"| {result['author']} | {result['working_days']:.2f} | ")
f.write(
f"{result['business_days']} | {result['utilization']:.2f}% |\n")
# Team total
business_days = results[0]['business_days'] if results else 0
team_utilization = (total_working_days / (business_days * len(results))
) * 100 if business_days * len(results) > 0 else 0
f.write(
f"| **TEAM TOTAL** | **{total_working_days:.2f}** | **{business_days * len(results)}** | **{team_utilization:.2f}%** |\n\n")
# Monthly breakdown
f.write("## Monthly Contribution Breakdown\n\n")
# Collect all unique months
all_months = set()
for result in results:
for month_data in result['monthly_data']:
all_months.add(month_data['month'])
all_months = sorted(list(all_months))
# Create a table for each month
for month in all_months:
f.write(f"### {month}\n\n")
f.write(
"| Contributor | Lines Modified | Est. Working Days | Utilization |\n")
f.write(
"|-------------|----------------|------------------|-------------|\n")
month_contributors = []
for result in results:
# Find this month's data for this contributor
month_data = next(
(m for m in result['monthly_data'] if m['month'] == month), None)
if month_data:
month_contributors.append({
'author': result['author'],
'lines_modified': month_data['lines_modified'],
'working_days': month_data['working_days'],
'utilization': month_data['utilization']
})
# Sort contributors by working days for this month
month_contributors.sort(
key=lambda x: x['working_days'], reverse=True)
for contrib in month_contributors:
f.write(
f"| {contrib['author']} | {contrib['lines_modified']:,} | ")
f.write(
f"{contrib['working_days']:.2f} | {contrib['utilization']:.2f}% |\n")
# Month totals
total_month_working_days = sum(
c['working_days'] for c in month_contributors)
total_month_lines = sum(c['lines_modified']
for c in month_contributors)
avg_month_utilization = sum(c['utilization'] for c in month_contributors) / len(
month_contributors) if month_contributors else 0
f.write(
f"| **MONTH TOTAL** | **{total_month_lines:,}** | **{total_month_working_days:.2f}** | **{avg_month_utilization:.2f}%** |\n\n")
# Individual contributor links
f.write("## Individual Contributor Reports\n\n")
for result in results:
author_file = f"{result['author'].replace(' ', '_')}-contribution-summary.md"
f.write(f"- [{result['author']}](./{author_file})\n")
def create_visualizations(results, output_dir):
"""Create data visualizations comparing contributors."""
try:
# Prepare data
authors = [r['author'] for r in results]
working_days = [r['working_days'] for r in results]
utilization = [r['utilization'] for r in results]
# Create figure with subplots
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 7))
# Working days bar chart
ax1.bar(authors, working_days, color='skyblue')
ax1.set_title('Estimated Working Days by Contributor')
ax1.set_ylabel('Days (at lines_per_day)')
ax1.tick_params(axis='x', rotation=45)
# Utilization bar chart
ax2.bar(authors, utilization, color='lightgreen')
ax2.set_title('Calendar Utilization by Contributor')
ax2.set_ylabel('Utilization (%)')
ax2.axhline(y=100, color='r', linestyle='--',
alpha=0.7) # 100% reference line
ax2.tick_params(axis='x', rotation=45)
plt.tight_layout()
plt.savefig(os.path.join(output_dir, 'team_comparison.png'))
# Monthly breakdown visualizations
create_monthly_visualization(results, output_dir)
except Exception as e:
print(f"Error creating visualizations: {e}")
def create_monthly_visualization(results, output_dir):
"""Create a visualization showing monthly contributions by team members."""
try:
# Collect all unique months and contributors
all_months = set()
for result in results:
for month_data in result['monthly_data']:
all_months.add(month_data['month'])
all_months = sorted(list(all_months))
contributors = [r['author'] for r in results]
# Create a matrix of working days [months x contributors]
data = np.zeros((len(all_months), len(contributors)))
for i, month in enumerate(all_months):
for j, contributor in enumerate(contributors):
# Find this contributor's data for this month
result = next(
(r for r in results if r['author'] == contributor), None)
if result:
month_data = next(
(m for m in result['monthly_data'] if m['month'] == month), None)
if month_data:
data[i, j] = month_data['working_days']
# Create a stacked bar chart
fig, ax = plt.subplots(figsize=(12, 8))
bottom = np.zeros(len(all_months))
for j, contributor in enumerate(contributors):
ax.bar(all_months, data[:, j], bottom=bottom, label=contributor)
bottom += data[:, j]
ax.set_title('Monthly Working Days by Contributor')
ax.set_ylabel('Working Days')
ax.set_xlabel('Month')
ax.legend(loc='upper left', bbox_to_anchor=(1, 1))
ax.tick_params(axis='x', rotation=45)
plt.tight_layout()
plt.savefig(os.path.join(output_dir, 'monthly_breakdown.png'))
except Exception as e:
print(f"Error creating monthly visualization: {e}")
def main():
parser = argparse.ArgumentParser(
description="Generate a dashboard comparing multiple git contributors.")
parser.add_argument(
"--authors",
required=True,
help="Comma-separated list of git author names or email patterns"
)
parser.add_argument(
"--since",
required=True,
help="Start date for commit analysis (YYYY-MM-DD)"
)
parser.add_argument(
"--until",
help="End date for commit analysis (YYYY-MM-DD)"
)
parser.add_argument(
"--lines-per-day",
type=int,
default=200,
help="Assumed lines of code per working day (default: 200)"
)
parser.add_argument(
"--output",
default="./reports",
help="Output directory for reports (default: ./reports)"
)
args = parser.parse_args()
authors = [a.strip() for a in args.authors.split(',')]
print(f"Generating team dashboard for: {', '.join(authors)}")
print(f"Date range: {args.since} to {args.until if args.until else 'now'}")
print(f"Lines per day assumption: {args.lines_per_day}")
dashboard_path = generate_team_dashboard(
authors,
args.since,
args.until,
args.lines_per_day,
args.output
)
print(f"\nDashboard generation complete!")
print(f"Team dashboard: {dashboard_path}")
print(f"Charts saved to: {args.output}")
if __name__ == "__main__":
main()
#!/usr/bin/env python3
import argparse
import subprocess
import os
import datetime
import sys
import calendar
from collections import defaultdict
def run_git_command(cmd):
"""Execute a git command and return the output."""
try:
result = subprocess.run(
cmd, shell=True, check=True, text=True, capture_output=True)
return result.stdout
except subprocess.CalledProcessError as e:
print(f"Error executing git command: {e}", file=sys.stderr)
print(f"Command output: {e.stderr}", file=sys.stderr)
sys.exit(1)
def get_commits(author, since, until=None):
"""Get git commits for a specific author within a date range."""
cmd = f'git log --author="{author}" --since="{since}"'
if until:
cmd += f' --until="{until}"'
cmd += ' --pretty=format:"%h|%ad|%s" --date=format:\'%Y-%m-%d\' --numstat'
return run_git_command(cmd)
def parse_commit_data(git_output):
"""Parse git log output into structured data."""
commits = []
current_commit = None
for line in git_output.strip().split('\n'):
if not line.strip():
continue
if '|' in line: # This is a commit header line
if current_commit:
commits.append(current_commit)
parts = line.split('|')
current_commit = {
'hash': parts[0],
'date': parts[1],
'subject': parts[2],
'files': [],
'added': 0,
'deleted': 0
}
elif current_commit is not None: # This is a file stats line
parts = line.split('\t')
if len(parts) >= 3:
try:
added = int(parts[0]) if parts[0] != '-' else 0
deleted = int(parts[1]) if parts[1] != '-' else 0
current_commit['added'] += added
current_commit['deleted'] += deleted
current_commit['files'].append({
'file': parts[2],
'added': added,
'deleted': deleted
})
except ValueError:
# Skip lines that don't match our expected format
continue
# Add the last commit
if current_commit:
commits.append(current_commit)
return commits
def get_business_days_in_month(year, month):
"""Calculate the number of business days (Mon-Fri) in a given month."""
cal = calendar.monthcalendar(year, month)
return sum(1 for week in cal for day in range(5) if week[day] != 0)
def analyze_commits(commits, since_date, until_date=None):
"""Analyze commit data and generate statistics."""
total_added = sum(commit['added'] for commit in commits)
total_deleted = sum(commit['deleted'] for commit in commits)
total_modified = total_added + total_deleted
# Aggregate commits by date
daily_stats = defaultdict(lambda: {'added': 0, 'deleted': 0, 'commits': 0})
for commit in commits:
date = commit['date']
daily_stats[date]['added'] += commit['added']
daily_stats[date]['deleted'] += commit['deleted']
daily_stats[date]['commits'] += 1
# Organize by month
monthly_stats = defaultdict(
lambda: {'added': 0, 'deleted': 0, 'commits': 0, 'business_days': 0})
# Parse the since and until dates
if isinstance(since_date, str):
since_date = datetime.datetime.strptime(since_date, "%Y-%m-%d").date()
if until_date and isinstance(until_date, str):
until_date = datetime.datetime.strptime(until_date, "%Y-%m-%d").date()
else:
until_date = datetime.datetime.now().date()
# Calculate business days for each month in the range
current_date = datetime.date(since_date.year, since_date.month, 1)
while current_date <= until_date:
year_month = current_date.strftime("%Y-%m")
monthly_stats[year_month]['business_days'] = get_business_days_in_month(
current_date.year, current_date.month)
# Move to next month
if current_date.month == 12:
current_date = datetime.date(current_date.year + 1, 1, 1)
else:
current_date = datetime.date(
current_date.year, current_date.month + 1, 1)
# Aggregate commit stats by month
for date, stats in daily_stats.items():
year_month = date[:7] # YYYY-MM
monthly_stats[year_month]['added'] += stats['added']
monthly_stats[year_month]['deleted'] += stats['deleted']
monthly_stats[year_month]['commits'] += stats['commits']
# Calculate total business days
total_business_days = sum(stats['business_days']
for stats in monthly_stats.values())
return {
'total_commits': len(commits),
'total_added': total_added,
'total_deleted': total_deleted,
'total_modified': total_modified,
'daily_stats': dict(daily_stats),
'monthly_stats': dict(monthly_stats),
'total_business_days': total_business_days
}
def generate_text_report(author, analysis, lines_per_day, output_dir):
"""Generate a plain text report of the analysis."""
os.makedirs(output_dir, exist_ok=True)
report_path = os.path.join(
output_dir, f"{author.replace(' ', '_')}-commit-report.txt")
with open(report_path, 'w') as f:
f.write(f"Summary of Commits by {author}\n")
f.write("------------------------------------------\n\n")
f.write("Summary Statistics:\n")
f.write(f"Total Commits: {analysis['total_commits']}\n")
f.write(f"Total Lines Added: {analysis['total_added']}\n")
f.write(f"Total Lines Deleted: {analysis['total_deleted']}\n")
f.write(f"Total Lines Modified: {analysis['total_modified']}\n")
days_worked = analysis['total_modified'] / lines_per_day
f.write(
f"Estimated Working Days: {days_worked:.2f} days (at {lines_per_day} lines/day)\n")
f.write(
f"Total Business Days in Period: {analysis['total_business_days']} days\n")
utilization = (days_worked / analysis['total_business_days']) * \
100 if analysis['total_business_days'] > 0 else 0
f.write(f"Calendar Utilization: {utilization:.2f}%\n\n")
f.write("Daily Work Breakdown:\n")
f.write(
"| Date | Lines Added | Lines Deleted | Total Lines | Est. Days |\n")
f.write(
"|------------|-------------|---------------|-------------|----------|\n")
for date, stats in sorted(analysis['daily_stats'].items()):
total = stats['added'] + stats['deleted']
f.write(
f"| {date} | {stats['added']} | {stats['deleted']} | {total} | {total / lines_per_day:.2f} |\n")
f.write("\nMonthly Distribution of Work:\n")
f.write(
"| Month | Lines | Est. Days | Business Days | Utilization | % of Total Work |\n")
f.write(
"|----------|-------|-----------|--------------|-------------|----------------|\n")
for month, stats in sorted(analysis['monthly_stats'].items()):
total = stats['added'] + stats['deleted']
est_days = total / lines_per_day
business_days = stats['business_days']
utilization = (est_days / business_days) * \
100 if business_days > 0 else 0
work_percentage = 0
if analysis['total_modified'] > 0:
work_percentage = (total / analysis['total_modified']) * 100
month_name = datetime.datetime.strptime(
month, "%Y-%m").strftime("%B %Y")
f.write(
f"| {month_name} | {total} | {est_days:.2f} | {business_days} | "
f"{utilization:.2f}% | {work_percentage:.2f}% |\n")
return report_path
def generate_markdown_report(author, analysis, lines_per_day, output_dir):
"""Generate a markdown report of the analysis."""
os.makedirs(output_dir, exist_ok=True)
report_path = os.path.join(
output_dir, f"{author.replace(' ', '_')}-contribution-summary.md")
with open(report_path, 'w') as f:
f.write(f"# {author} Contribution Analysis\n\n")
f.write("## Overview\n\n")
f.write(f"This report analyzes the git commits made by {author}, ")
f.write(
f"calculating estimated working days based on an assumption of {lines_per_day} lines of code modified per working day.\n\n")
f.write("## Summary Statistics\n\n")
f.write(f"- **Total Commits:** {analysis['total_commits']}\n")
f.write(f"- **Total Lines Added:** {analysis['total_added']:,}\n")
f.write(f"- **Total Lines Deleted:** {analysis['total_deleted']:,}\n")
f.write(
f"- **Total Lines Modified:** {analysis['total_modified']:,}\n")
days_worked = analysis['total_modified'] / lines_per_day
f.write(
f"- **Estimated Working Days:** {days_worked:.2f} days "
f"(at {lines_per_day} lines/day)\n")
f.write(
f"- **Total Business Days in Period:** {analysis['total_business_days']} days\n")
utilization = (days_worked / analysis['total_business_days']) * \
100 if analysis['total_business_days'] > 0 else 0
f.write(f"- **Calendar Utilization:** {utilization:.2f}%\n\n")
f.write("## Monthly Distribution\n\n")
f.write(
"| Month | Lines Modified | Est. Working Days | Business Days | Utilization | % of Total Work |\n")
f.write(
"|-------|---------------|-----------------|--------------|-------------|----------------|\n")
for month, stats in sorted(analysis['monthly_stats'].items()):
total = stats['added'] + stats['deleted']
est_days = total / lines_per_day
business_days = stats['business_days']
utilization = (est_days / business_days) * \
100 if business_days > 0 else 0
work_percentage = 0
if analysis['total_modified'] > 0:
work_percentage = (total / analysis['total_modified']) * 100
month_name = datetime.datetime.strptime(
month, "%Y-%m").strftime("%B %Y")
f.write(
f"| {month_name} | {total:,} | {est_days:.2f} | {business_days} | "
f"{utilization:.2f}% | {work_percentage:.2f}% |\n")
f.write("\n## Top Contributions by Impact\n\n")
f.write(
"| Date | Description | Lines Modified | Est. Days | % of Total Work |\n")
f.write("|------|-------------|---------------|----------|----------------|\n")
# Sort commits by impact (lines modified)
commits_by_impact = []
for date, stats in analysis['daily_stats'].items():
# Find a commit message for this date
message = next(
(commit['subject'] for commit in analysis['raw_commits'] if commit['date'] == date), "")
total_lines = stats['added'] + stats['deleted']
commits_by_impact.append({
'date': date,
'message': message,
'total': total_lines,
'est_days': total_lines / lines_per_day,
'percentage': (total_lines / analysis['total_modified'] * 100) if analysis['total_modified'] > 0 else 0
})
# Sort by total lines modified, descending
commits_by_impact.sort(key=lambda x: x['total'], reverse=True)
# Display top 5 commits or all if less than 5
for commit in commits_by_impact[:5]:
f.write(
f"| {commit['date']} | {commit['message']} | {commit['total']:,} | "
f"{commit['est_days']:.2f} | {commit['percentage']:.2f}% |\n")
f.write("\n## Conclusion\n\n")
days_worked = analysis['total_modified'] / lines_per_day
f.write(
f"Based on the assumption of {lines_per_day} lines of code per working day, ")
f.write(
f"{author} has contributed approximately **{days_worked:.2f} working days** ")
f.write(
f"worth of development effort during this period, across {analysis['total_business_days']} ")
f.write(
f"available business days ({utilization:.2f}% calendar utilization).")
if commits_by_impact:
f.write(
f" The most significant contribution was \"{commits_by_impact[0]['message']}\" "
f"on {commits_by_impact[0]['date']}, ")
f.write(
f"which represented {commits_by_impact[0]['percentage']:.2f}% of the total work done during this period.")
return report_path
def main():
parser = argparse.ArgumentParser(
description="Analyze git contributions by a developer.")
parser.add_argument(
"--author",
required=True,
help="Git author name or email pattern"
)
parser.add_argument(
"--since",
required=True,
help="Start date for commit analysis (YYYY-MM-DD)"
)
parser.add_argument(
"--until",
help="End date for commit analysis (YYYY-MM-DD)"
)
parser.add_argument(
"--lines-per-day",
type=int,
default=200,
help="Assumed lines of code per working day (default: 200)"
)
parser.add_argument(
"--output",
default="./reports",
help="Output directory for reports (default: ./reports)"
)
args = parser.parse_args()
print(f"Analyzing contributions for author: {args.author}")
until_date = args.until if args.until else 'now'
print(f"Date range: {args.since} to {until_date}")
print(f"Lines per day assumption: {args.lines_per_day}")
# Get commit data
print("Retrieving git commit history...")
git_output = get_commits(args.author, args.since, args.until)
# Parse and analyze the data
print("Analyzing commit data...")
commits = parse_commit_data(git_output)
if not commits:
print(
f"No commits found for {args.author} in the specified date range.")
sys.exit(0)
analysis = analyze_commits(commits, args.since, args.until)
# Add raw commits for use in report generation
analysis['raw_commits'] = commits
# Generate reports
print("Generating reports...")
text_report = generate_text_report(
args.author, analysis, args.lines_per_day, args.output)
markdown_report = generate_markdown_report(
args.author, analysis, args.lines_per_day, args.output)
print(f"\nAnalysis complete!")
print(f"Text report: {text_report}")
print(f"Markdown report: {markdown_report}")
days_worked = analysis['total_modified'] / args.lines_per_day
utilization = (days_worked / analysis['total_business_days']) * \
100 if analysis['total_business_days'] > 0 else 0
print(f"\nTotal estimated working days: {days_worked:.2f} days")
print(
f"Total business days in period: {analysis['total_business_days']} days")
print(f"Calendar utilization: {utilization:.2f}%")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment