Skip to content

Commit 6aef2e6

Browse files
Merge pull request #3 from LachlanGray/code-analyzer
GH code stats
2 parents 287fb2d + c3946ca commit 6aef2e6

File tree

17 files changed

+818
-0
lines changed

17 files changed

+818
-0
lines changed

showcase/code-analysis/.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
__pycache__/
2+
*.py[cod]
3+
*$py.class
4+
.env
5+
.venv/
6+
venv/
7+
.DS_Store
8+
*.log
9+
dist/
10+
build/
11+
*.egg-info/

showcase/code-analysis/README.md

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# GitHub Repository Analyzer
2+
3+
A Chrome extension that analyzes GitHub repositories for development patterns and metrics!
4+
5+
<p align="center">
6+
<img src="images/img1.png" width="45%" />
7+
<img src="images/img2.png" width="45%" />
8+
</p>
9+
10+
11+
## Features
12+
- Commit frequency analysis
13+
- Contributor activity tracking
14+
- Commit patterns by weekday
15+
- Average commit size metrics
16+
17+
## Prerequisites
18+
- Python 3.8 or higher
19+
- Google Chrome browser
20+
- Git
21+
22+
## Setup Instructions
23+
24+
### 1. Clone the Repository
25+
```bash
26+
git clone <your-repo-url>
27+
cd code-analysis
28+
```
29+
30+
### 2. Set Up Python Environment
31+
```bash
32+
# Create and activate virtual environment
33+
python -m venv .venv
34+
source .venv/bin/activate # On Windows use: .venv\Scripts\activate
35+
36+
# Install dependencies
37+
pip install -e .
38+
39+
# Install development dependencies (optional)
40+
pip install -e ".[dev]"
41+
```
42+
43+
### 3. Start the Backend Server
44+
```bash
45+
# Start the FastAPI server
46+
./start_server.sh
47+
```
48+
The server will run at http://localhost:8000
49+
50+
### 4. Install Chrome Extension
51+
1. Open Chrome and navigate to `chrome://extensions/`
52+
2. Enable "Developer mode" in the top right
53+
3. Click "Load unpacked"
54+
4. Select the `extension` directory from this project
55+
56+
### 5. Using the Extension
57+
1. Navigate to any GitHub repository
58+
2. Click the extension icon in Chrome's toolbar
59+
3. Click "Analyze Repository" to see metrics
60+
61+
## Development
62+
63+
### Running Type Checks
64+
```bash
65+
mypy cli app
66+
```
67+
68+
### Format Code
69+
```bash
70+
black cli app
71+
```
72+
73+
### API Documentation
74+
When the server is running, visit:
75+
- http://localhost:8000/docs for interactive API documentation
76+
- http://localhost:8000/redoc for alternative documentation view
77+
78+
## Troubleshooting
79+
80+
### Common Issues
81+
1. If the extension shows "No repository detected":
82+
- Refresh the GitHub page
83+
- Make sure you're on a repository page
84+
85+
2. If analysis fails:
86+
- Check that the backend server is running
87+
- Look for errors in the server logs
88+
89+
3. If the extension doesn't load:
90+
- Check Chrome's extension page for errors
91+
- Try reloading the extension
92+
93+
### Server Logs
94+
The FastAPI server logs all operations. Check the terminal where you ran `start_server.sh` for detailed error messages.
95+
96+
## Architecture
97+
- `cli/`: Command-line interface for git analysis
98+
- `app/`: FastAPI web service
99+
- `extension/`: Chrome extension files
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"""FastAPI application for git repository analysis."""

showcase/code-analysis/app/main.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import os
2+
import tempfile
3+
import logging
4+
from git import Repo
5+
from fastapi import FastAPI, HTTPException
6+
from fastapi.middleware.cors import CORSMiddleware
7+
from typing import Dict, Union
8+
from pydantic import BaseModel
9+
from cli.git_analysis import (
10+
analyze_commit_frequency,
11+
analyze_contributor_activity,
12+
analyze_commit_frequency_by_weekday,
13+
analyze_commit_frequency_by_hour,
14+
analyze_average_commit_size,
15+
analyze_file_change_frequency,
16+
)
17+
18+
# Configure logging
19+
logging.basicConfig(level=logging.INFO)
20+
logger = logging.getLogger(__name__)
21+
22+
app = FastAPI(title="Git Analysis API")
23+
24+
# Enable CORS
25+
app.add_middleware(
26+
CORSMiddleware,
27+
allow_origins=["chrome-extension://*"], # Allow requests from any Chrome extension
28+
allow_credentials=True,
29+
allow_methods=["*"],
30+
allow_headers=["*"],
31+
)
32+
33+
# Store repo paths temporarily
34+
repo_cache: Dict[str, str] = {}
35+
36+
37+
class CloneRequest(BaseModel):
38+
url: str
39+
40+
41+
@app.post("/clone")
42+
async def clone_repository(request: CloneRequest) -> Dict[str, str]:
43+
"""Clone a git repository and return a temporary ID to reference it."""
44+
try:
45+
# Create a temporary directory
46+
temp_dir = tempfile.mkdtemp()
47+
logger.info(f"Created temp directory: {temp_dir}")
48+
49+
# Clone the repository
50+
logger.info(f"Cloning repository from {request.url}")
51+
repo = Repo.clone_from(request.url, temp_dir)
52+
53+
# Generate a simple ID (you might want to use UUID in production)
54+
repo_id = str(hash(request.url))
55+
56+
# Store the mapping
57+
repo_cache[repo_id] = temp_dir
58+
logger.info(f"Successfully cloned repository. ID: {repo_id}")
59+
60+
return {"repo_id": repo_id}
61+
except Exception as e:
62+
logger.error(f"Error cloning repository: {str(e)}", exc_info=True)
63+
raise HTTPException(status_code=500, detail=str(e))
64+
65+
66+
@app.get("/analyze/{analysis_type}")
67+
async def analyze_repo(
68+
analysis_type: str, repo_id: str
69+
) -> Dict[str, Union[int, float]]:
70+
"""Analyze a git repository using the specified analysis type."""
71+
try:
72+
# Get repo path from cache
73+
repo_path = repo_cache.get(repo_id)
74+
if not repo_path:
75+
logger.error(f"Repository not found for ID: {repo_id}")
76+
raise HTTPException(
77+
status_code=404, detail="Repository not found. Please clone it first."
78+
)
79+
80+
logger.info(f"Analyzing repository at {repo_path}")
81+
82+
analysis_functions = {
83+
"commit-frequency": analyze_commit_frequency,
84+
"contributor-activity": analyze_contributor_activity,
85+
"commit-frequency-by-weekday": analyze_commit_frequency_by_weekday,
86+
"commit-frequency-by-hour": analyze_commit_frequency_by_hour,
87+
"average-commit-size": analyze_average_commit_size,
88+
"file-change-frequency": analyze_file_change_frequency,
89+
}
90+
91+
if analysis_type not in analysis_functions:
92+
logger.error(f"Invalid analysis type: {analysis_type}")
93+
raise HTTPException(
94+
status_code=400,
95+
detail=f"Invalid analysis type. Must be one of: {', '.join(analysis_functions.keys())}",
96+
) # Execute analysis
97+
logger.info(f"Running {analysis_type} analysis")
98+
results = analysis_functions[analysis_type](repo_path)
99+
logger.info(f"Analysis complete: {results}")
100+
101+
# Cast the dictionary to the expected type
102+
if not isinstance(results, dict):
103+
raise HTTPException(
104+
status_code=500, detail="Analysis returned invalid type"
105+
)
106+
return results
107+
except Exception as e:
108+
logger.error(f"Error during analysis: {str(e)}", exc_info=True)
109+
raise HTTPException(status_code=500, detail=str(e))
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""A Python CLI application."""
2+
3+
__version__ = "0.1.0"
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#!/usr/bin/env python3
2+
from typing import Any, Callable, Dict
3+
import argparse
4+
from cli.git_analysis import (
5+
analyze_commit_frequency,
6+
analyze_contributor_activity,
7+
analyze_commit_frequency_by_weekday,
8+
analyze_commit_frequency_by_hour,
9+
analyze_average_commit_size,
10+
analyze_file_change_frequency,
11+
)
12+
13+
# Dictionary mapping command names to their corresponding functions
14+
COMMANDS: Dict[str, Callable[..., Dict[str, Any]]] = {
15+
"analyze_commit_frequency": analyze_commit_frequency,
16+
"analyze_contributor_activity": analyze_contributor_activity,
17+
"analyze_commit_frequency_by_weekday": analyze_commit_frequency_by_weekday,
18+
"analyze_commit_frequency_by_hour": analyze_commit_frequency_by_hour,
19+
"analyze_average_commit_size": analyze_average_commit_size,
20+
"analyze_file_change_frequency": analyze_file_change_frequency,
21+
}
22+
23+
24+
def main() -> None:
25+
parser = argparse.ArgumentParser(description="Analyze GitHub repository complexity")
26+
parser.add_argument(
27+
"command",
28+
choices=list(COMMANDS.keys()),
29+
help="The analysis command to run",
30+
)
31+
parser.add_argument(
32+
"repo_path",
33+
help="Path to the git repository to analyze",
34+
)
35+
parser.add_argument(
36+
"--json",
37+
action="store_true",
38+
help="Output results in JSON format",
39+
)
40+
41+
args = parser.parse_args()
42+
43+
try:
44+
# Get the function corresponding to the command
45+
command_func = COMMANDS[args.command]
46+
47+
# Execute the command and get the results
48+
results = command_func(args.repo_path)
49+
50+
# Print results
51+
if args.json:
52+
import json
53+
54+
print(json.dumps(results, indent=2))
55+
else:
56+
print(f"\n{args.command} results:")
57+
for key, value in sorted(results.items()):
58+
print(f"{key}: {value}")
59+
60+
except Exception as e:
61+
print(f"Error analyzing repository: {e}")
62+
63+
64+
if __name__ == "__main__":
65+
main()

0 commit comments

Comments
 (0)