Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import requests | |
| import os | |
| from dotenv import load_dotenv | |
| load_dotenv() | |
| GITHUB_CLIENT_ID = os.getenv("GITHUB_CLIENT_ID") | |
| GITHUB_CLIENT_SECRET = os.getenv("GITHUB_CLIENT_SECRET") | |
| GITHUB_GRAPHQL = "https://api.github.com/graphql" | |
| # ---------------- OAuth helpers ---------------- | |
| def exchange_code_for_token(code: str) -> str: | |
| url = "https://github.com/login/oauth/access_token" | |
| headers = {"Accept": "application/json"} | |
| data = { | |
| "client_id": GITHUB_CLIENT_ID, | |
| "client_secret": GITHUB_CLIENT_SECRET, | |
| "code": code, | |
| } | |
| r = requests.post(url, headers=headers, data=data, timeout=20) | |
| r.raise_for_status() | |
| token = r.json().get("access_token") | |
| if not token: | |
| raise RuntimeError("GitHub OAuth failed: no access token returned") | |
| return token | |
| # ---------------- GitHub API ---------------- | |
| def github_graphql(query, token): | |
| headers = { | |
| "Authorization": f"Bearer {token}", | |
| "Content-Type": "application/json", | |
| } | |
| r = requests.post(GITHUB_GRAPHQL, json={"query": query}, headers=headers, timeout=30) | |
| r.raise_for_status() | |
| return r.json() | |
| def fetch_github_wrapped(token): | |
| query = """ | |
| query { | |
| viewer { | |
| login | |
| contributionsCollection( | |
| from: "2025-01-01T00:00:00Z", | |
| to: "2025-12-31T23:59:59Z" | |
| ) { | |
| contributionCalendar { totalContributions } | |
| totalCommitContributions | |
| totalPullRequestContributions | |
| totalIssueContributions | |
| } | |
| repositories( | |
| first: 100, | |
| ownerAffiliations: OWNER, | |
| orderBy: { field: STARGAZERS, direction: DESC } | |
| ) { | |
| nodes { | |
| name | |
| stargazerCount | |
| primaryLanguage { name } | |
| } | |
| } | |
| } | |
| } | |
| """ | |
| viewer = github_graphql(query, token)["data"]["viewer"] | |
| repos = viewer["repositories"]["nodes"] | |
| languages = {} | |
| for r in repos: | |
| if r["primaryLanguage"]: | |
| lang = r["primaryLanguage"]["name"] | |
| languages[lang] = languages.get(lang, 0) + 1 | |
| return { | |
| "username": viewer["login"], | |
| "total": viewer["contributionsCollection"]["contributionCalendar"]["totalContributions"], | |
| "commits": viewer["contributionsCollection"]["totalCommitContributions"], | |
| "prs": viewer["contributionsCollection"]["totalPullRequestContributions"], | |
| "issues": viewer["contributionsCollection"]["totalIssueContributions"], | |
| "top_language": max(languages, key=languages.get) if languages else "Unknown", | |
| "top_repo": repos[0]["name"] if repos else "None", | |
| } | |
| # ---------------- UI ---------------- | |
| def render_wrapped(stats): | |
| return f""" | |
| <div style=" | |
| font-family: Poppins, sans-serif; | |
| padding: 40px; | |
| background: linear-gradient(135deg,#0f2027,#203a43,#2c5364); | |
| color: white; | |
| border-radius: 24px; | |
| max-width: 900px; | |
| margin: auto; | |
| "> | |
| <h1 style="text-align:center;font-size:3em;">π GitHub Wrapped 2025</h1> | |
| <h2 style="text-align:center;">@{stats['username']}</h2> | |
| <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:20px;margin-top:40px;"> | |
| <div class="card">π₯ {stats['total']} Contributions</div> | |
| <div class="card">π» {stats['commits']} Commits</div> | |
| <div class="card">π {stats['prs']} PRs</div> | |
| <div class="card">π {stats['issues']} Issues</div> | |
| </div> | |
| <div style="margin-top:40px;"> | |
| <h3>β¨ Your Coding Style</h3> | |
| <p><b>Favorite Language:</b> {stats['top_language']}</p> | |
| <p><b>Top Starred Repo:</b> {stats['top_repo']}</p> | |
| </div> | |
| <p style="text-align:center;margin-top:40px;"> | |
| Keep shipping amazing code in 2026 π | |
| </p> | |
| </div> | |
| <style> | |
| .card {{ | |
| background: rgba(255,255,255,0.15); | |
| padding: 26px; | |
| border-radius: 18px; | |
| font-size: 1.4em; | |
| font-weight: 600; | |
| text-align: center; | |
| backdrop-filter: blur(8px); | |
| }} | |
| </style> | |
| """ | |
| def router(request: gr.Request): | |
| code = request.query_params.get("code") | |
| if not code: | |
| return f""" | |
| <div style="text-align:center;padding:40px;"> | |
| <h1>π GitHub Wrapped 2025</h1> | |
| <a href="https://github.com/login/oauth/authorize | |
| ?client_id={GITHUB_CLIENT_ID} | |
| &scope=read:user repo"> | |
| <button style="font-size:18px;padding:14px 24px;border-radius:10px;"> | |
| Login with GitHub | |
| </button> | |
| </a> | |
| </div> | |
| """ | |
| token = exchange_code_for_token(code) | |
| stats = fetch_github_wrapped(token) | |
| return render_wrapped(stats) | |
| with gr.Blocks(theme=gr.themes.Soft()) as demo: | |
| output = gr.HTML() | |
| demo.load(router, None, output) | |
| demo.launch(ssr_mode=False,debug=True) |