|
| 1 | +from fastapi import FastAPI, HTTPException |
| 2 | +from pydantic import BaseModel |
| 3 | +import requests |
| 4 | +import os |
| 5 | +from urllib.parse import urlparse |
| 6 | +from dotenv import load_dotenv |
| 7 | + |
| 8 | +load_dotenv() # Load environment variables |
| 9 | + |
| 10 | +app = FastAPI() |
| 11 | + |
| 12 | +class RepoRequest(BaseModel): |
| 13 | + repo_url: str |
| 14 | + |
| 15 | +def parse_github_url(url: str) -> tuple: |
| 16 | + """Extract owner/repo from GitHub URL""" |
| 17 | + parsed = urlparse(url) |
| 18 | + path = parsed.path.strip('/').split('/') |
| 19 | + if len(path) < 2: |
| 20 | + raise ValueError("Invalid GitHub URL") |
| 21 | + return path[0], path[1] |
| 22 | + |
| 23 | +def github_api_request(endpoint: str) -> dict: |
| 24 | + """Make authenticated GitHub API request""" |
| 25 | + headers = { |
| 26 | + "Authorization": f"token {os.getenv('GITHUB_TOKEN')}", |
| 27 | + "Accept": "application/vnd.github.v3+json" |
| 28 | + } |
| 29 | + response = requests.get(f"https://api.github.com{endpoint}", headers=headers) |
| 30 | + response.raise_for_status() |
| 31 | + return response.json() |
| 32 | + |
| 33 | + |
| 34 | +@app.post("/repo-stats") |
| 35 | +async def get_repo_stats(repo_url: str): |
| 36 | + try: |
| 37 | + owner, repo_name = parse_github_url(repo_url) |
| 38 | + # Rest of your function... |
| 39 | + # Get basic repo info |
| 40 | + repo_info = github_api_request(f"/repos/{owner}/{repo_name}") |
| 41 | + |
| 42 | + # Get contributors |
| 43 | + contributors = github_api_request(f"/repos/{owner}/{repo_name}/contributors") |
| 44 | + |
| 45 | + # Get pull requests |
| 46 | + prs = github_api_request(f"/repos/{owner}/{repo_name}/pulls?state=all") |
| 47 | + |
| 48 | + # Get issues |
| 49 | + issues = github_api_request(f"/repos/{owner}/{repo_name}/issues?state=all") |
| 50 | + |
| 51 | + community_profile = github_api_request(f"/repos/{owner}/{repo_name}/community/profile") |
| 52 | + |
| 53 | + # Recent commits (last 5) |
| 54 | + commits = github_api_request(f"/repos/{owner}/{repo_name}/commits?per_page=5") |
| 55 | + |
| 56 | + code_frequency = github_api_request(f"/repos/{owner}/{repo_name}/stats/code_frequency") |
| 57 | + |
| 58 | + return { |
| 59 | + "name": repo_info["full_name"], |
| 60 | + "stars": repo_info["stargazers_count"], |
| 61 | + "forks": repo_info["forks_count"], |
| 62 | + "watchers": repo_info["subscribers_count"], |
| 63 | + "created_at": repo_info["created_at"], |
| 64 | + "updated_at": repo_info["updated_at"], |
| 65 | + # Licensing and topics |
| 66 | + # "license": repo_info.get("license", {}).get("spdx_id", "No License"), |
| 67 | + |
| 68 | + "topics": repo_info.get("topics", []), |
| 69 | + |
| 70 | + "contributors": [{ |
| 71 | + "login": c["login"], |
| 72 | + "contributions": c["contributions"], |
| 73 | + "avatar_url": c["avatar_url"] |
| 74 | + } for c in contributors], |
| 75 | + "recent_commits": [{ |
| 76 | + "sha": commit["sha"][:7], |
| 77 | + "author": commit["commit"]["author"]["name"], |
| 78 | + "message": commit["commit"]["message"], |
| 79 | + "date": commit["commit"]["author"]["date"] |
| 80 | + } for commit in commits], |
| 81 | + |
| 82 | + |
| 83 | + "community": { |
| 84 | + "health_percentage": community_profile["health_percentage"], |
| 85 | + "code_of_conduct": community_profile.get("files", {}).get("code_of_conduct") is not None, |
| 86 | + "license": community_profile.get("files", {}).get("license") is not None, |
| 87 | + "readme": community_profile.get("files", {}).get("readme") is not None |
| 88 | + }, |
| 89 | + # Issues |
| 90 | + "issues": { |
| 91 | + "total": len(issues), |
| 92 | + "open": sum(1 for issue in issues if issue["state"] == "open"), |
| 93 | + "closed": sum(1 for issue in issues if issue["state"] == "closed"), |
| 94 | + "labels": list({label["name"] for issue in issues for label in issue["labels"]}) |
| 95 | + }, |
| 96 | + |
| 97 | + # Code statistics |
| 98 | + "code_activity": { |
| 99 | + "weekly_commits": len(code_frequency) if isinstance(code_frequency, list) else 0, |
| 100 | + "total_additions": sum(week[1] for week in code_frequency) if isinstance(code_frequency, list) else 0, |
| 101 | + "total_deletions": sum(abs(week[2]) for week in code_frequency) if isinstance(code_frequency, list) else 0 |
| 102 | + }, |
| 103 | + |
| 104 | + # Pull Requests |
| 105 | + "pull_requests": { |
| 106 | + "total": len(prs), |
| 107 | + "merged": sum(1 for pr in prs if pr["merged_at"]), |
| 108 | + "draft": sum(1 for pr in prs if pr["draft"]), |
| 109 | + "by_state": { |
| 110 | + "open": sum(1 for pr in prs if pr["state"] == "open"), |
| 111 | + "closed": sum(1 for pr in prs if pr["state"] == "closed") |
| 112 | + } |
| 113 | + }, |
| 114 | + } |
| 115 | + |
| 116 | + except requests.HTTPError as e: |
| 117 | + raise HTTPException(status_code=e.response.status_code, |
| 118 | + detail="GitHub API error") |
| 119 | + except ValueError: |
| 120 | + raise HTTPException(status_code=400, |
| 121 | + detail="Invalid GitHub URL format") |
0 commit comments