rbbist's picture
Create app.py
8617ae2 verified
Raw
History Blame Contribute Delete
9.55 kB
import gradio as gr
import requests
from datetime import datetime, timezone, timedelta
import os
import google.generativeai as genai
def get_all_commits_in_range(owner, repo_name, start_date, end_date, token):
"""
Fetch all unique commits across all branches within a date range.
Uses a more efficient approach by fetching commits once and filtering by date.
"""
headers = {"Authorization": f"token {token}"}
base_url = f"https://api.github.com/repos/{owner}/{repo_name}"
# Get all branches
try:
branches_resp = requests.get(f"{base_url}/branches", headers=headers)
branches_resp.raise_for_status()
branches = [b['name'] for b in branches_resp.json()]
except requests.exceptions.RequestException as e:
return None, f"Error fetching branches: {str(e)}"
# Define date range
since = f"{start_date}T00:00:00Z"
until = f"{end_date}T23:59:59Z"
all_commits = {}
# Fetch commits from all branches
for branch in branches:
try:
params = {"sha": branch, "since": since, "until": until, "per_page": 100}
commits_resp = requests.get(f"{base_url}/commits", headers=headers, params=params)
commits_resp.raise_for_status()
commits = commits_resp.json()
# Add unique commits (keyed by SHA to avoid duplicates)
for commit in commits:
sha = commit["sha"]
if sha not in all_commits:
all_commits[sha] = commit
except requests.exceptions.RequestException as e:
continue # Skip branches that cause errors
# Sort by commit date
sorted_commits = sorted(
all_commits.values(),
key=lambda c: datetime.strptime(c["commit"]["author"]["date"], "%Y-%m-%dT%H:%M:%SZ")
)
return sorted_commits, None
def format_commits(commits, timezone_offset_hours=5, timezone_offset_minutes=45):
"""
Format commits with timezone conversion and readable output.
"""
formatted = []
for commit in commits:
sha = commit["sha"][:8] # Short SHA for readability
message = commit["commit"]["message"].split('\n')[0] # First line only
author = commit["commit"]["author"]["name"]
utc_time_str = commit["commit"]["author"]["date"]
# Parse and convert timezone
utc_time = datetime.strptime(utc_time_str, "%Y-%m-%dT%H:%M:%SZ")
utc_time = utc_time.replace(tzinfo=timezone.utc)
# Apply timezone offset
local_offset = timedelta(hours=timezone_offset_hours, minutes=timezone_offset_minutes)
local_time = utc_time.astimezone(timezone(local_offset))
formatted_line = (
f"{sha} | {author:20s} | {local_time.strftime('%Y-%m-%d %H:%M:%S')} | {message}"
)
formatted.append(formatted_line)
return "\n".join(formatted)
def summarize_with_gemini(repo_name, owner, commit_lines, start_date, end_date, api_key):
"""
Generate a comprehensive summary using Gemini with an improved prompt.
"""
genai.configure(api_key=api_key)
model = genai.GenerativeModel("gemini-2.0-flash-exp")
date_range = f"{start_date}" if start_date == end_date else f"{start_date} to {end_date}"
prompt = f"""You are a technical project analyst reviewing Git commit history.
Repository: {owner}/{repo_name}
Date Range: {date_range}
Total Commits: {len(commit_lines.split(chr(10)))}
Commit History:
{commit_lines}
Please provide a comprehensive analysis with the following sections:
1. **Executive Summary**: A 2-3 sentence overview of the work accomplished during this period.
2. **Key Developments**: Highlight the most significant changes, features, or fixes (3-5 bullet points).
3. **Development Timeline**: Chronological narrative of how the work progressed throughout the day/period.
4. **Author Contributions**: For each author, provide:
- Total number of commits
- Time range of their work
- Main focus areas and specific accomplishments
- Notable commits with their purpose
5. **Technical Insights**: Identify patterns such as:
- Types of work (features, bugs, refactoring, documentation)
- Areas of the codebase that received attention
- Any potential concerns or areas needing follow-up
6. **Impact Assessment**: Evaluate the overall progress and its significance to the project.
Format your response with clear headings and bullet points for readability."""
try:
response = model.generate_content(prompt)
return response.text
except Exception as e:
return f"Error generating summary: {str(e)}"
def analyze_commits(repo_url, start_date, end_date, github_token, gemini_key):
"""
Main function to orchestrate the commit analysis.
"""
# Parse repository URL
try:
if "github.com/" in repo_url:
parts = repo_url.rstrip('/').split('github.com/')[-1].split('/')
owner = parts[0]
repo_name = parts[1].replace('.git', '')
else:
# Assume format: owner/repo
owner, repo_name = repo_url.split('/')
except:
return "❌ Invalid repository format. Use 'owner/repo' or full GitHub URL."
# Validate tokens
if not github_token or not gemini_key:
return "❌ Please provide both GitHub and Gemini API tokens."
# Fetch commits
status_msg = f"πŸ” Fetching commits from {owner}/{repo_name}...\n"
commits, error = get_all_commits_in_range(owner, repo_name, start_date, end_date, github_token)
if error:
return f"{status_msg}❌ {error}"
if not commits:
return f"{status_msg}ℹ️ No commits found in the specified date range."
status_msg += f"βœ… Found {len(commits)} unique commits across all branches.\n\n"
# Format commits
formatted_commits = format_commits(commits)
status_msg += "πŸ“ Commit History:\n" + "="*80 + "\n"
status_msg += formatted_commits + "\n\n"
# Generate summary
status_msg += "πŸ€– Generating AI summary...\n" + "="*80 + "\n"
summary = summarize_with_gemini(repo_name, owner, formatted_commits, start_date, end_date, gemini_key)
status_msg += summary
return status_msg
# Gradio Interface
with gr.Blocks(title="Git Commit Story Analyzer", theme=gr.themes.Soft()) as app:
gr.Markdown("""
# πŸ“š Git Commit Story Analyzer
Transform your Git commit history into comprehensive narratives using AI.
This tool fetches commits from **all branches** and generates detailed insights.
""")
with gr.Row():
with gr.Column(scale=2):
repo_input = gr.Textbox(
label="Repository",
placeholder="owner/repo or https://github.com/owner/repo",
info="Enter GitHub repository in format 'owner/repo' or paste the full URL"
)
with gr.Row():
start_date_input = gr.Textbox(
label="Start Date",
value=datetime.now().strftime("%Y-%m-%d"),
placeholder="YYYY-MM-DD"
)
end_date_input = gr.Textbox(
label="End Date",
value=datetime.now().strftime("%Y-%m-%d"),
placeholder="YYYY-MM-DD"
)
github_token_input = gr.Textbox(
label="GitHub Personal Access Token",
type="password",
placeholder="ghp_xxxxxxxxxxxx",
info="Required for API access. Generate at: Settings β†’ Developer settings β†’ Personal access tokens"
)
gemini_key_input = gr.Textbox(
label="Gemini API Key",
type="password",
placeholder="AIzaSy...",
info="Get your API key from Google AI Studio: https://makersuite.google.com/app/apikey"
)
analyze_btn = gr.Button("πŸš€ Analyze Commits", variant="primary", size="lg")
with gr.Column(scale=1):
gr.Markdown("""
### πŸ’‘ Tips
- **Single Day**: Set both dates to the same day
- **Date Range**: Use different start and end dates
- **All Branches**: Automatically analyzes commits from all branches
- **Timezone**: Displays times in Nepal timezone (UTC+5:45)
### πŸ”’ Privacy
Your tokens are only used for this session and are never stored.
""")
output = gr.Textbox(
label="Analysis Results",
lines=25,
max_lines=50,
show_copy_button=True
)
analyze_btn.click(
fn=analyze_commits,
inputs=[repo_input, start_date_input, end_date_input, github_token_input, gemini_key_input],
outputs=output
)
gr.Markdown("""
---
### πŸ“– How to Use
1. **Enter Repository**: Provide the GitHub repository (e.g., `torvalds/linux` or full URL)
2. **Select Date Range**: Choose single day or date range for analysis
3. **Add Tokens**: Provide your GitHub Personal Access Token and Gemini API Key
4. **Analyze**: Click the button to generate your commit story
**Note**: The tool fetches commits from all branches and removes duplicates automatically.
""")
if __name__ == "__main__":
app.launch()