AdvChkSys/scripts/track_progress/git_analyzer.py
Stan44 1b7601120c Completed progress tracking system with multiple features
[feature:track_progress] Implemented main progress tracking system
[feature:git_integration] Added Git history analysis with commit tag parsing
[feature:template_engine] Created flexible template engine for documentation generation
[feature:feature_tracking] Implemented feature status tracking and updates
[feature:changelog_generation] Added automatic changelog generation from commits
[feature:code_stats] Added code statistics and metrics collection
[feature:roadmap_generation] Implemented roadmap generation

[new-feature:launcher_scripts:Created cross-platform launcher scripts for easy execution]
[new-feature:executable_build:Added Nuitka-based executable build system]
[new-feature:project_installation:Created setup script for easy installation to projects]

[status:track_progress_and_feature_statuses] Completed automatic status document generation
[status:git_integration] Completed parsing Git logs for status updates
[status:doc_generation] Completed auto-generation of status docs and changelog

[changelog:Added comprehensive progress tracking system]
[changelog:Added feature status tracking with Git integration]
[changelog:Added customizable template system for documentation]
[changelog:Added automatic changelog generation from commit messages]
[changelog:Added code statistics and metrics collection]
[changelog:Added cross-platform launcher scripts]
[changelog:Added executable build system using Nuitka]
[changelog:Added project installation script]

[milestone:v1.0] Completed initial release of progress tracking system
2025-05-12 18:37:02 -05:00

239 lines
8.0 KiB
Python

import os
import re
import subprocess
from datetime import datetime
from typing import Dict, Any, List, Optional, Tuple
class GitAnalyzer:
"""Analyze Git repository for project status information."""
def __init__(self, repo_path: str = "."):
"""Initialize with repository path."""
self.repo_path = repo_path
def _run_git_command(self, command: List[str]) -> str:
"""Run a Git command and return the output."""
try:
result = subprocess.run(
["git"] + command,
cwd=self.repo_path,
capture_output=True,
text=True,
check=True,
)
return result.stdout.strip()
except subprocess.CalledProcessError:
return ""
def get_repo_info(self) -> Dict[str, Any]:
"""Get basic repository information."""
info = {}
# Get remote URL
remote_url = self._run_git_command(["remote", "get-url", "origin"])
info["remote_url"] = remote_url
# Get current branch
branch = self._run_git_command(["branch", "--show-current"])
info["branch"] = branch
# Get last commit
last_commit = self._run_git_command(
["log", "-1", "--pretty=format:%h|%s|%ad|%an", "--date=short"]
)
if last_commit:
parts = last_commit.split("|")
if len(parts) >= 4:
info["last_commit"] = {
"hash": parts[0],
"message": parts[1],
"date": parts[2],
"author": parts[3],
}
return info
def analyze_commits(
self, limit: int = 100, untagged_limit: int = 50
) -> Dict[str, Any]:
"""Analyze Git commits for feature updates, changelog entries, etc."""
result = {
"feature_updates": {},
"new_features": {},
"changelog_entries": [],
"fixes": [],
"issues": [],
"untagged_commits": [],
"milestones": {},
"roadmap_items": {},
}
# Regular expressions for parsing commit messages
status_re = re.compile(r"\[status:(\w+)\]")
feature_re = re.compile(r"\[feature:(\w+)\]")
new_feature_re = re.compile(r"\[new-feature:(\w+):(.+?)\]")
changelog_re = re.compile(r"\[changelog:(.+?)\]")
fix_re = re.compile(r"\[fix:(.+?)\]")
issue_re = re.compile(r"\[issue:(.+?)\]")
milestone_re = re.compile(r"\[milestone:(\w+)\]")
roadmap_re = re.compile(r"\[roadmap:(\w+):(.+?)\]")
# Get Git log
git_log = self._run_git_command(
[
"log",
f"-n{limit}",
"--pretty=format:%h|%s|%ad|%an",
"--date=short",
]
)
untagged_count = 0
# Parse commits
for line in git_log.splitlines():
parts = line.split("|")
if len(parts) < 4:
continue
commit_hash, subject, date, author = parts
# Check for tags
status_match = status_re.search(subject)
feature_match = feature_re.search(subject)
new_feature_match = new_feature_re.search(subject)
changelog_match = changelog_re.search(subject)
fix_match = fix_re.search(subject)
issue_match = issue_re.search(subject)
milestone_match = milestone_re.search(subject)
roadmap_match = roadmap_re.search(subject)
# Process status updates
if status_match:
status_key = status_match.group(1)
result["feature_updates"][status_key] = {
"status": "completed",
"date": date,
"author": author,
}
# Process feature updates
if feature_match:
feature_key = feature_match.group(1)
result["feature_updates"][feature_key] = {
"status": "completed",
"date": date,
"author": author,
}
# Process new features
if new_feature_match:
feature_key = new_feature_match.group(1)
feature_desc = new_feature_match.group(2)
result["new_features"][feature_key] = {
"status": "planned",
"description": feature_desc,
"date": date,
"author": author,
}
# Process changelog entries
if changelog_match:
result["changelog_entries"].append(
{
"message": changelog_match.group(1),
"date": date,
"commit": commit_hash,
"author": author,
}
)
# Process fixes
if fix_match:
result["fixes"].append(
{
"message": fix_match.group(1),
"date": date,
"commit": commit_hash,
"author": author,
}
)
# Process issues
if issue_match:
result["issues"].append(
{
"message": issue_match.group(1),
"date": date,
"commit": commit_hash,
"author": author,
}
)
# Process milestones
if milestone_match:
milestone_key = milestone_match.group(1)
if milestone_key not in result["milestones"]:
result["milestones"][milestone_key] = {
"first_date": date,
"last_date": date,
"commits": [],
}
else:
result["milestones"][milestone_key]["last_date"] = date
result["milestones"][milestone_key]["commits"].append(
commit_hash
)
# Process roadmap items
if roadmap_match:
milestone_key = roadmap_match.group(1)
item_desc = roadmap_match.group(2)
if milestone_key not in result["roadmap_items"]:
result["roadmap_items"][milestone_key] = []
result["roadmap_items"][milestone_key].append(
{
"description": item_desc,
"date": date,
"commit": commit_hash,
"author": author,
}
)
# Process untagged commits
if not any(
[
status_match,
feature_match,
new_feature_match,
changelog_match,
fix_match,
issue_match,
milestone_match,
roadmap_match,
]
):
if untagged_count < untagged_limit:
# Skip merge commits and very short messages
if not subject.startswith("Merge ") and len(subject) > 5:
# Extract the first sentence or up to 100 chars
commit_desc = subject.split(".")[0]
if len(commit_desc) > 100:
commit_desc = commit_desc[:97] + "..."
result["untagged_commits"].append(
{
"message": commit_desc,
"date": date,
"commit": commit_hash,
"author": author,
}
)
untagged_count += 1
return result