rbbist commited on
Commit
8617ae2
Β·
verified Β·
1 Parent(s): 14e5145

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +256 -0
app.py ADDED
@@ -0,0 +1,256 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import requests
3
+ from datetime import datetime, timezone, timedelta
4
+ import os
5
+ import google.generativeai as genai
6
+
7
+ def get_all_commits_in_range(owner, repo_name, start_date, end_date, token):
8
+ """
9
+ Fetch all unique commits across all branches within a date range.
10
+ Uses a more efficient approach by fetching commits once and filtering by date.
11
+ """
12
+ headers = {"Authorization": f"token {token}"}
13
+ base_url = f"https://api.github.com/repos/{owner}/{repo_name}"
14
+
15
+ # Get all branches
16
+ try:
17
+ branches_resp = requests.get(f"{base_url}/branches", headers=headers)
18
+ branches_resp.raise_for_status()
19
+ branches = [b['name'] for b in branches_resp.json()]
20
+ except requests.exceptions.RequestException as e:
21
+ return None, f"Error fetching branches: {str(e)}"
22
+
23
+ # Define date range
24
+ since = f"{start_date}T00:00:00Z"
25
+ until = f"{end_date}T23:59:59Z"
26
+
27
+ all_commits = {}
28
+
29
+ # Fetch commits from all branches
30
+ for branch in branches:
31
+ try:
32
+ params = {"sha": branch, "since": since, "until": until, "per_page": 100}
33
+ commits_resp = requests.get(f"{base_url}/commits", headers=headers, params=params)
34
+ commits_resp.raise_for_status()
35
+ commits = commits_resp.json()
36
+
37
+ # Add unique commits (keyed by SHA to avoid duplicates)
38
+ for commit in commits:
39
+ sha = commit["sha"]
40
+ if sha not in all_commits:
41
+ all_commits[sha] = commit
42
+ except requests.exceptions.RequestException as e:
43
+ continue # Skip branches that cause errors
44
+
45
+ # Sort by commit date
46
+ sorted_commits = sorted(
47
+ all_commits.values(),
48
+ key=lambda c: datetime.strptime(c["commit"]["author"]["date"], "%Y-%m-%dT%H:%M:%SZ")
49
+ )
50
+
51
+ return sorted_commits, None
52
+
53
+ def format_commits(commits, timezone_offset_hours=5, timezone_offset_minutes=45):
54
+ """
55
+ Format commits with timezone conversion and readable output.
56
+ """
57
+ formatted = []
58
+
59
+ for commit in commits:
60
+ sha = commit["sha"][:8] # Short SHA for readability
61
+ message = commit["commit"]["message"].split('\n')[0] # First line only
62
+ author = commit["commit"]["author"]["name"]
63
+ utc_time_str = commit["commit"]["author"]["date"]
64
+
65
+ # Parse and convert timezone
66
+ utc_time = datetime.strptime(utc_time_str, "%Y-%m-%dT%H:%M:%SZ")
67
+ utc_time = utc_time.replace(tzinfo=timezone.utc)
68
+
69
+ # Apply timezone offset
70
+ local_offset = timedelta(hours=timezone_offset_hours, minutes=timezone_offset_minutes)
71
+ local_time = utc_time.astimezone(timezone(local_offset))
72
+
73
+ formatted_line = (
74
+ f"{sha} | {author:20s} | {local_time.strftime('%Y-%m-%d %H:%M:%S')} | {message}"
75
+ )
76
+ formatted.append(formatted_line)
77
+
78
+ return "\n".join(formatted)
79
+
80
+ def summarize_with_gemini(repo_name, owner, commit_lines, start_date, end_date, api_key):
81
+ """
82
+ Generate a comprehensive summary using Gemini with an improved prompt.
83
+ """
84
+ genai.configure(api_key=api_key)
85
+ model = genai.GenerativeModel("gemini-2.0-flash-exp")
86
+
87
+ date_range = f"{start_date}" if start_date == end_date else f"{start_date} to {end_date}"
88
+
89
+ prompt = f"""You are a technical project analyst reviewing Git commit history.
90
+
91
+ Repository: {owner}/{repo_name}
92
+ Date Range: {date_range}
93
+ Total Commits: {len(commit_lines.split(chr(10)))}
94
+
95
+ Commit History:
96
+ {commit_lines}
97
+
98
+ Please provide a comprehensive analysis with the following sections:
99
+
100
+ 1. **Executive Summary**: A 2-3 sentence overview of the work accomplished during this period.
101
+
102
+ 2. **Key Developments**: Highlight the most significant changes, features, or fixes (3-5 bullet points).
103
+
104
+ 3. **Development Timeline**: Chronological narrative of how the work progressed throughout the day/period.
105
+
106
+ 4. **Author Contributions**: For each author, provide:
107
+ - Total number of commits
108
+ - Time range of their work
109
+ - Main focus areas and specific accomplishments
110
+ - Notable commits with their purpose
111
+
112
+ 5. **Technical Insights**: Identify patterns such as:
113
+ - Types of work (features, bugs, refactoring, documentation)
114
+ - Areas of the codebase that received attention
115
+ - Any potential concerns or areas needing follow-up
116
+
117
+ 6. **Impact Assessment**: Evaluate the overall progress and its significance to the project.
118
+
119
+ Format your response with clear headings and bullet points for readability."""
120
+
121
+ try:
122
+ response = model.generate_content(prompt)
123
+ return response.text
124
+ except Exception as e:
125
+ return f"Error generating summary: {str(e)}"
126
+
127
+ def analyze_commits(repo_url, start_date, end_date, github_token, gemini_key):
128
+ """
129
+ Main function to orchestrate the commit analysis.
130
+ """
131
+ # Parse repository URL
132
+ try:
133
+ if "github.com/" in repo_url:
134
+ parts = repo_url.rstrip('/').split('github.com/')[-1].split('/')
135
+ owner = parts[0]
136
+ repo_name = parts[1].replace('.git', '')
137
+ else:
138
+ # Assume format: owner/repo
139
+ owner, repo_name = repo_url.split('/')
140
+ except:
141
+ return "❌ Invalid repository format. Use 'owner/repo' or full GitHub URL."
142
+
143
+ # Validate tokens
144
+ if not github_token or not gemini_key:
145
+ return "❌ Please provide both GitHub and Gemini API tokens."
146
+
147
+ # Fetch commits
148
+ status_msg = f"πŸ” Fetching commits from {owner}/{repo_name}...\n"
149
+ commits, error = get_all_commits_in_range(owner, repo_name, start_date, end_date, github_token)
150
+
151
+ if error:
152
+ return f"{status_msg}❌ {error}"
153
+
154
+ if not commits:
155
+ return f"{status_msg}ℹ️ No commits found in the specified date range."
156
+
157
+ status_msg += f"βœ… Found {len(commits)} unique commits across all branches.\n\n"
158
+
159
+ # Format commits
160
+ formatted_commits = format_commits(commits)
161
+ status_msg += "πŸ“ Commit History:\n" + "="*80 + "\n"
162
+ status_msg += formatted_commits + "\n\n"
163
+
164
+ # Generate summary
165
+ status_msg += "πŸ€– Generating AI summary...\n" + "="*80 + "\n"
166
+ summary = summarize_with_gemini(repo_name, owner, formatted_commits, start_date, end_date, gemini_key)
167
+ status_msg += summary
168
+
169
+ return status_msg
170
+
171
+ # Gradio Interface
172
+ with gr.Blocks(title="Git Commit Story Analyzer", theme=gr.themes.Soft()) as app:
173
+ gr.Markdown("""
174
+ # πŸ“š Git Commit Story Analyzer
175
+
176
+ Transform your Git commit history into comprehensive narratives using AI.
177
+ This tool fetches commits from **all branches** and generates detailed insights.
178
+ """)
179
+
180
+ with gr.Row():
181
+ with gr.Column(scale=2):
182
+ repo_input = gr.Textbox(
183
+ label="Repository",
184
+ placeholder="owner/repo or https://github.com/owner/repo",
185
+ info="Enter GitHub repository in format 'owner/repo' or paste the full URL"
186
+ )
187
+
188
+ with gr.Row():
189
+ start_date_input = gr.Textbox(
190
+ label="Start Date",
191
+ value=datetime.now().strftime("%Y-%m-%d"),
192
+ placeholder="YYYY-MM-DD"
193
+ )
194
+ end_date_input = gr.Textbox(
195
+ label="End Date",
196
+ value=datetime.now().strftime("%Y-%m-%d"),
197
+ placeholder="YYYY-MM-DD"
198
+ )
199
+
200
+ github_token_input = gr.Textbox(
201
+ label="GitHub Personal Access Token",
202
+ type="password",
203
+ placeholder="ghp_xxxxxxxxxxxx",
204
+ info="Required for API access. Generate at: Settings β†’ Developer settings β†’ Personal access tokens"
205
+ )
206
+
207
+ gemini_key_input = gr.Textbox(
208
+ label="Gemini API Key",
209
+ type="password",
210
+ placeholder="AIzaSy...",
211
+ info="Get your API key from Google AI Studio: https://makersuite.google.com/app/apikey"
212
+ )
213
+
214
+ analyze_btn = gr.Button("πŸš€ Analyze Commits", variant="primary", size="lg")
215
+
216
+ with gr.Column(scale=1):
217
+ gr.Markdown("""
218
+ ### πŸ’‘ Tips
219
+
220
+ - **Single Day**: Set both dates to the same day
221
+ - **Date Range**: Use different start and end dates
222
+ - **All Branches**: Automatically analyzes commits from all branches
223
+ - **Timezone**: Displays times in Nepal timezone (UTC+5:45)
224
+
225
+ ### πŸ”’ Privacy
226
+
227
+ Your tokens are only used for this session and are never stored.
228
+ """)
229
+
230
+ output = gr.Textbox(
231
+ label="Analysis Results",
232
+ lines=25,
233
+ max_lines=50,
234
+ show_copy_button=True
235
+ )
236
+
237
+ analyze_btn.click(
238
+ fn=analyze_commits,
239
+ inputs=[repo_input, start_date_input, end_date_input, github_token_input, gemini_key_input],
240
+ outputs=output
241
+ )
242
+
243
+ gr.Markdown("""
244
+ ---
245
+ ### πŸ“– How to Use
246
+
247
+ 1. **Enter Repository**: Provide the GitHub repository (e.g., `torvalds/linux` or full URL)
248
+ 2. **Select Date Range**: Choose single day or date range for analysis
249
+ 3. **Add Tokens**: Provide your GitHub Personal Access Token and Gemini API Key
250
+ 4. **Analyze**: Click the button to generate your commit story
251
+
252
+ **Note**: The tool fetches commits from all branches and removes duplicates automatically.
253
+ """)
254
+
255
+ if __name__ == "__main__":
256
+ app.launch()