A configuration-driven Python script that fetches GitHub commits and lines of code metrics for specified users within an organization, and generates comparative visualizations showing daily activity.
Configuration via src/config.py with optional CLI overrides for automation and flexibility.
- Configuration-Driven: All settings in
src/config.py- just edit and run - CLI Support: Override mode and parameters via command-line arguments
- Secure Secret Management: Ansible Vault for encrypted credential storage
- Concurrent API Fetching: Configurable thread pool (default 4 threads)
- Smart Caching: Day-wise JSON caching with selective refresh
- Bot Filtering: Automatically excludes bot commits via API type checking
- All Branches Included: Fetches commits from all branches, not just default branch
- Incremental Processing: Skips already-processed dates unless explicitly refreshed
- Dual Chart Output: Interactive HTML (Plotly) + Static PNG/PDF (Matplotlib)
- Line Charts: Visualization with line charts for better trend analysis
- Dated Reports: Reports organized in dated folders (YYYYMMDD format)
- Comprehensive Metrics: Tracks additions, deletions, total LOC, and commits separately
- User Comparison: Side-by-side visualization of multiple users
- Combined Mode: FETCH_AND_CHART mode for automated pipelines
- Comprehensive Testing: Unit and integration tests with pytest
- Automated CI/CD: Nightly reports at 12 AM IST via GitHub Actions
- Weekly Releases: Automated weekly summary releases
- Data Persistence: Cache, output, and reports committed to repository for tracking
- Python 3.7 or higher
- Ansible (for secret management):
pip install ansible - GitHub Personal Access Token with
repoandread:orgscopes - Access to the target GitHub organization (dolr-ai)
# 1. Clone and install
git clone https://github.com/dolr-ai/github-report-script.git
cd github-report-script
pip install -r requirements.txt
# 2. Setup Ansible Vault (one-time setup)
cd ansible
./init_vault.sh
# Follow the prompts to set vault password and GitHub token
cd ..
# 3. Run with defaults (fetch and chart last 30 days)
python src/main.py
# Or customize with CLI arguments
python src/main.py --mode refresh --days 90 # Refresh last 90 days
python src/main.py --mode chart --days 7 # Chart last 7 days
python src/main.py --mode status # Check status# Use defaults from config.py (fetch_and_chart mode, 30 days)
python src/main.py
# Override execution mode
python src/main.py --mode fetch # Fetch only
python src/main.py --mode refresh --days 90 # Force refresh 90 days
python src/main.py --mode chart # Generate charts only
python src/main.py --mode status # Check status
# Get help
python src/main.py --helpAvailable Modes:
fetch- Fetch and process new datarefresh- Re-fetch and overwrite existing cachechart- Generate visualizations from existing datastatus- Show cache status and API rate limitsfetch_and_chart- Fetch + process + generate charts (default)
For persistent settings, edit src/config.py:
from src.config import ExecutionMode, DateRangeMode
# Default execution mode (can be overridden with --mode)
MODE = ExecutionMode.FETCH_AND_CHART
# Date range settings (DAYS_BACK can be overridden with --days)
DATE_RANGE_MODE = DateRangeMode.LAST_N_DAYS
DAYS_BACK = 30The script supports multiple execution modes that can be set in src/config.py or via CLI:
from src.config import ExecutionMode, DateRangeMode
# What should the script do?
MODE = ExecutionMode.FETCH_AND_CHART # Default: Fetch + Chart
# MODE = ExecutionMode.FETCH # Fetch and process new data
# MODE = ExecutionMode.REFRESH # Re-fetch specific dates (overwrites cache)
# MODE = ExecutionMode.CHART # Generate visualizations
# MODE = ExecutionMode.STATUS # Show status and rate limitsMode Descriptions:
- FETCH_AND_CHART: Fetch new commits, process metrics, and generate charts (default)
- FETCH: Fetch commits from GitHub API and process them into metrics
- REFRESH: Re-fetch and overwrite existing cached data for specific dates
- CHART: Generate visualization charts from existing processed data
- STATUS: Display GitHub API rate limit status and cache statistics
- FETCH_AND_CHART: Combined mode that fetches, processes, and generates charts (ideal for automation/CI)
# How to determine date range?
DATE_RANGE_MODE = DateRangeMode.LAST_N_DAYS
DAYS_BACK = 7
# For custom ranges:
# DATE_RANGE_MODE = DateRangeMode.CUSTOM_RANGE
# START_DATE = '2026-01-01'
# END_DATE = '2026-01-31'
# For single date:
# DATE_RANGE_MODE = DateRangeMode.SPECIFIC_DATE
# START_DATE = '2026-02-01'
# For charting all cached data:
# DATE_RANGE_MODE = DateRangeMode.ALL_CACHED# GitHub usernames to track
USER_IDS = [
'saikatdas0790',
'gravityvi',
# Add more usernames here
]# Number of concurrent threads
THREAD_COUNT = 4 # Conservative (default)
# THREAD_COUNT = 8 # Aggressive (faster, may hit rate limits)
# THREAD_COUNT = 1 # Debugging# Set logging verbosity
LOG_LEVEL = LogLevel.INFO # Show progress and important messages (default)
# LOG_LEVEL = LogLevel.DEBUG # Show detailed debug information
# LOG_LEVEL = LogLevel.WARNING # Show only warnings and errors
# LOG_LEVEL = LogLevel.ERROR # Show only errorsLog Output Format:
2026-02-03 13:41:16 | INFO | src.github_fetcher | Fetching commits from 2026-01-28 to 2026-02-03
2026-02-03 13:41:16 | INFO | src.github_fetcher | Tracking 11 users: saikatdas0790, gravityvi, jay-dhanwant-yral...
2026-02-03 13:41:16 | INFO | src.github_fetcher | Using 4 concurrent threads
- INFO: Progress updates and key milestones
- DEBUG: Detailed operations (cache reads, individual commits, etc.)
- WARNING: Issues that don't stop execution (rate limits, missing cache)
- ERROR: Critical failures
See src/config.py for complete configuration options with examples.
Important: Run all commands from the project root directory.
python src/main.pyAll configuration is read from src/config.py - no command-line arguments needed.
- Edit
src/config.py:MODE = ExecutionMode.FETCH DATE_RANGE_MODE = DateRangeMode.LAST_N_DAYS DAYS_BACK = 1 # Yesterday only
- Run:
python src/main.py
- Edit
src/config.py:MODE = ExecutionMode.FETCH DATE_RANGE_MODE = DateRangeMode.LAST_N_DAYS DAYS_BACK = 7
- Run:
python src/main.py - Edit
src/config.py:MODE = ExecutionMode.CHART
- Run:
python src/main.py
- Edit
src/config.py:MODE = ExecutionMode.REFRESH DATE_RANGE_MODE = DateRangeMode.CUSTOM_RANGE START_DATE = '2026-01-27' END_DATE = '2026-01-28'
- Run:
python src/main.py
- Edit
src/config.py:MODE = ExecutionMode.STATUS
- Run:
python src/main.py
- Edit
src/config.py:MODE = ExecutionMode.FETCH_AND_CHART DATE_RANGE_MODE = DateRangeMode.LAST_N_DAYS DAYS_BACK = 7
- Run:
python src/main.py
This mode combines fetching, processing, and charting in a single run - ideal for automated workflows.
cd ansible
./init_vault.shThis will:
- Create vault password file (
.vault_pass) - Prompt for GitHub Personal Access Token
- Create and encrypt secrets (
vars/vault.yml) - Generate
.envfile
# View encrypted secrets
ansible-vault view vars/vault.yml
# Edit secrets
ansible-vault edit vars/vault.yml
# Regenerate .env file
ansible-playbook setup_env.yml
# Change vault password
ansible-vault rekey vars/vault.ymlSee ansible/README.md for detailed vault management guide.
vars/main.yml (visible, committed):
github_token: "{{ vault_github_token }}"
github_org: "dolr-ai"vars/vault.yml (encrypted, committed):
vault_github_token: "ghp_1234567890abcdef..."github-report-script/
├── src/
│ ├── config.py # ⚙️ All configuration settings (edit this!)
│ ├── main.py # Entry point (no args needed)
│ ├── cache_manager.py # Caching logic
│ ├── github_fetcher.py # GitHub API interaction
│ ├── data_processor.py # Data aggregation
│ └── chart_generator.py # Visualization
├── tests/ # 🧪 Test suite
│ ├── conftest.py # Pytest fixtures
│ ├── test_github_fetcher.py # GitHub fetching tests
│ ├── test_config.py # Configuration tests
│ └── test_data_processor.py # Data processing tests
├── .github/
│ ├── workflows/
│ │ └── nightly-report.yml # 🤖 CI/CD workflow
│ └── scripts/
│ └── setup-ci.sh # CI setup script
├── docs/
│ └── CI_SETUP.md # 📖 CI/CD documentation
├── ansible/
│ ├── init_vault.sh # 🔐 Vault setup script
│ ├── setup_env.yml # Playbook to generate .env
│ ├── vars/
│ │ ├── main.yml # Visible variable names
│ │ └── vault.yml # Encrypted secrets
│ └── README.md # Vault management guide
├── cache/ # 📦 Raw commit data (committed)
│ └── commits/
│ └── YYYY-MM-DD.json
├── output/ # 📊 Processed metrics (committed)
│ └── {username}/
│ └── YYYY-MM-DD.json
├── reports/ # 📈 Generated charts (committed)
│ └── YYYYMMDD/ # Dated folders
│ ├── report.html
│ ├── report.png
│ └── report.pdf
├── requirements.txt
├── pytest.ini
└── README.md
- Configuration →
src/config.pysets MODE and date range - Secrets → Ansible Vault decrypts to
.env - Fetch → GitHub API →
cache/commits/{date}.json - Process → Aggregate →
output/{user}/{date}.json - Chart → Visualize →
reports/report_*.{html,png,pdf}
Each report contains a 2×2 grid:
| Daily Additions (lines added) | Daily Deletions (lines removed) |
|---|---|
| Daily Total LOC (added + deleted) | Daily Commit Count |
All users displayed side-by-side with grouped bars for easy comparison.
- HTML (Plotly): Interactive with hover tooltips, zoom, pan
- PNG: High-resolution (300 DPI) static image
- PDF: Print-ready document format
# src/config.py
MODE = ExecutionMode.FETCH
DATE_RANGE_MODE = DateRangeMode.LAST_N_DAYS
DAYS_BACK = 7
THREAD_COUNT = 4
USER_IDS = [
'saikatdas0790',
'gravityvi',
'jay-dhanwant-yral',
# ... all users
]MODE = ExecutionMode.FETCH
DATE_RANGE_MODE = DateRangeMode.CUSTOM_RANGE
START_DATE = '2026-01-01'
END_DATE = '2026-01-31'
THREAD_COUNT = 8 # Faster for large date rangesMODE = ExecutionMode.FETCH
DATE_RANGE_MODE = DateRangeMode.SPECIFIC_DATE
START_DATE = '2026-02-01'
USER_IDS = ['saikatdas0790'] # Single user onlyMODE = ExecutionMode.CHART
DATE_RANGE_MODE = DateRangeMode.ALL_CACHED
# Uses all dates found in cache/GitHub API limits:
- Authenticated: 5,000 requests/hour
- Per repository: ~1-5 requests
- Concurrent threads: Default 4 (adjustable)
The script automatically:
- ✓ Monitors rate limits
- ✓ Waits when limit is low (<100 remaining)
- ✓ Uses caching to minimize requests
- ✓ Skips already-processed dates
Check current status:
# src/config.py
MODE = ExecutionMode.STATUSThen run: python src/main.py
Solution: Run Ansible playbook to generate .env:
cd ansible
ansible-playbook setup_env.ymlSolution: Edit src/config.py and add usernames:
USER_IDS = ['username1', 'username2']Solution: Initialize vault:
cd ansible
./init_vault.shSolution: Run fetch mode first:
# src/config.py
MODE = ExecutionMode.FETCHSolution:
- Check status:
MODE = ExecutionMode.STATUS - Reduce threads:
THREAD_COUNT = 2 - Wait for reset (shown in status output)
- Use cached data (avoid REFRESH mode)
Solution: Install dependencies:
pip install -r requirements.txtSolution: For detailed troubleshooting, enable DEBUG logging in src/config.py:
LOG_LEVEL = LogLevel.DEBUGThis shows:
- Individual cache reads/writes
- Each commit being processed
- API rate limit checks
- Repository-level operations
- Detailed error traces
Run with debug output redirected to file:
python src/main.py 2>&1 | tee debug.logname: Daily Report
on:
schedule:
- cron: '0 0 * * *' # Daily at midnight
jobs:
generate-report:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install ansible
- name: Setup secrets
env:
VAULT_PASSWORD: ${{ secrets.ANSIBLE_VAULT_PASSWORD }}
run: |
cd ansible
echo "$VAULT_PASSWORD" > .vault_pass
ansible-playbook setup_env.yml
- name: Run report
run: python src/main.py
- name: Commit results
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git add cache/ output/ reports/
git commit -m "Daily report $(date +%Y-%m-%d)" || true
git push-
Add enum (if applicable) in
src/config.py:class NewFeature(Enum): OPTION_A = "option_a" OPTION_B = "option_b"
-
Add configuration variable:
NEW_SETTING = NewFeature.OPTION_A """Documentation for NEW_SETTING"""
-
Add validation in
validate_config():if not isinstance(NEW_SETTING, NewFeature): errors.append("NEW_SETTING must be a NewFeature enum")
-
Use in code:
from src.config import NEW_SETTING, NewFeature if NEW_SETTING == NewFeature.OPTION_A: # Do something
-
Edit
ansible/vars/main.yml:new_api_key: "{{ vault_new_api_key }}"
-
Edit vault:
ansible-vault edit ansible/vars/vault.yml
Add:
vault_new_api_key: "your_secret"
-
Update template
ansible/templates/env.j2:NEW_API_KEY={{ new_api_key }} -
Regenerate .env:
cd ansible && ansible-playbook setup_env.yml
The project includes a comprehensive test suite with both unit tests (fast, mocked) and integration tests (real GitHub API).
# Run all tests
pytest
# Run only unit tests (fast, no API calls)
pytest -m unit
# Run only integration tests (requires GITHUB_TOKEN)
pytest -m integration
# Run with coverage report
pytest --cov=src --cov-report=html
# Run verbose with detailed output
pytest -vTests are configured via pytest.ini which automatically adds the workspace root to Python path. This means test imports work correctly regardless of how your code formatter reorganizes them.
- tests/conftest.py: Shared fixtures and test configuration
- tests/test_github_fetcher.py: Tests for GitHub API interaction
- Unit tests: Mocked API calls, verify bot filtering and org filtering
- Integration tests: Real API calls to dolr-ai org, verify all branches included
- tests/test_config.py: Tests for configuration and date range logic
- Validates that LAST_N_DAYS properly excludes today
- Ensures date ranges are correctly calculated
- tests/test_data_processor.py: Tests for data aggregation and metrics
Tests are marked with pytest markers for selective execution:
@pytest.mark.unit- Fast unit tests with mocked dependencies@pytest.mark.integration- Integration tests requiring GitHub API access@pytest.mark.slow- Tests that take significant time
Integration tests require:
- GitHub Personal Access Token set in
.envfile - Access to the dolr-ai organization
- Internet connectivity
If GITHUB_TOKEN is not available, integration tests are automatically skipped.
The repository includes GitHub Actions workflows that automatically generate reports every night at 12:00 AM IST (6:30 PM UTC).
Nightly Schedule:
- Runs automatically: Every day at 12:00 AM IST
- Execution mode:
FETCH_AND_CHART(fetch + process + generate charts) - Uses dev container: Same environment as local development
- Uploads artifacts: Reports retained for 90 days
Weekly Releases:
- Runs automatically: Every Sunday at 12:00 AM IST
- Creates GitHub release with weekly aggregated reports
- Tag format:
weekly-{run_number}
-
Add Required Secret:
- Go to repository Settings → Secrets and variables → Actions
- Add secret:
ANSIBLE_VAULT_PASSWORD(your vault password)
-
Verify Workflow:
- Go to Actions tab
- Check "Nightly GitHub Activity Report" workflow
- Reports will be generated automatically
-
Manual Trigger:
- Actions → Nightly GitHub Activity Report → Run workflow
- Select branch and click "Run workflow"
Daily Artifacts:
- Go to Actions → Select a workflow run
- Scroll to "Artifacts" section
- Download
github-activity-report-{run_number}.zip
Weekly Releases:
- Go to Releases section
- Find
weekly-{run_number}release - Download attached report files
- ✅ Consistent Environment: Uses devcontainers/ci for reproducibility
- ✅ Secure Secrets: Ansible Vault with encrypted credentials
- ✅ Automatic Uploads: Reports uploaded as artifacts (90-day retention)
- ✅ Weekly Summaries: Aggregated releases every Sunday
- ✅ Manual Triggers: Run on-demand via workflow_dispatch
- ✅ Branch Logging: Explicitly logs that all branches are fetched
For detailed CI/CD setup, troubleshooting, and configuration:
- See docs/CI_SETUP.md
MIT License - See LICENSE file for details.
- Fork the repository
- Create a feature branch
- Make your changes
- Test with different configurations
- Submit a pull request
- Issues: https://github.com/dolr-ai/github-report-script/issues
- Vault Guide: ansible/README.md
- Config Reference: See comments in
src/config.py
Quick Reference:
| Task | Configuration | Command |
|---|---|---|
| Setup vault | N/A | cd ansible && ./init_vault.sh && cd .. |
| Fetch last 7 days | MODE = FETCH, DAYS_BACK = 7 |
python src/main.py |
| Refresh specific dates | MODE = REFRESH, set START/END_DATE |
python src/main.py |
| Generate charts | MODE = CHART |
python src/main.py |
| Check status | MODE = STATUS |
python src/main.py |
| Automated pipeline | MODE = FETCH_AND_CHART |
python src/main.py |
| Run tests | N/A | pytest |
| Run unit tests only | N/A | pytest -m unit |
| Run integration tests | N/A | pytest -m integration |
| Edit secrets | N/A | ansible-vault edit ansible/vars/vault.yml |
| View secrets | N/A | ansible-vault view ansible/vars/vault.yml |