DIVYANSHI SINGH
🚀 Initial Commit: GitPulse
fcfc3c8
Raw
History Blame Contribute Delete
7.23 kB
import asyncio
from typing import List, Optional
from fastapi import APIRouter, HTTPException, Query, Header
from fastapi.responses import ORJSONResponse
from core.models import UserSearchResult
from core.cache import cache_get, cache_set
from core.github import get_github_data, GITHUB_API, http_client
from core.utils import validate_username, validate_param
router = APIRouter(default_response_class=ORJSONResponse)
@router.get("/api/user/{username}")
async def get_user_profile(username: str, x_github_token: str = Header(None)):
validate_username(username)
cached = cache_get(f"user:{username}")
if cached:
return cached
res = await get_github_data(f"{GITHUB_API}/users/{username}", user_token=x_github_token)
if res.status_code == 404:
raise HTTPException(status_code=404, detail="GitHub user not found.")
if res.status_code != 200:
raise HTTPException(status_code=502, detail="GitHub API error.")
data = res.json()
cache_set(f"user:{username}", data, ttl=300)
return data
@router.get("/api/search", response_model=UserSearchResult)
async def search_users(
roles: List[str] = Query(None),
location: Optional[str] = None,
sort: Optional[str] = None,
page: int = 1,
x_github_token: str = Header(None)
):
if not roles:
raise HTTPException(status_code=400, detail="Please select at least one role.")
role_query = " OR ".join([f'"{r}"' for r in roles])
query = f"{role_query} in:bio"
if location:
loc = validate_param(location, "location")
query += f' location:"{loc}"'
params: dict = {"q": query, "per_page": 12, "page": page}
if sort in ("followers", "repositories", "joined"):
params["sort"] = sort
params["order"] = "desc"
cache_key = f"search:{query}:{page}:{sort}"
cached = cache_get(cache_key)
if cached:
return cached
res = await get_github_data(f"{GITHUB_API}/search/users", params=params, user_token=x_github_token)
if res.status_code != 200:
raise HTTPException(status_code=502, detail=res.json().get("message", "GitHub API error"))
data = res.json()
items = data.get("items", [])
# Parallel detail fetch
async def fetch_detail(item):
try:
# We use the generic wrapper mapped to the user token
r = await get_github_data(item["url"], user_token=x_github_token)
return r.json() if r.status_code == 200 else item
except Exception:
return item
users = await asyncio.gather(*[fetch_detail(item) for item in items])
result = {"total_count": data.get("total_count", 0), "users": list(users), "page": page}
cache_set(cache_key, result, ttl=60)
return result
@router.get("/api/similar_candidates/{username}")
async def similar_candidates(username: str, x_github_token: str = Header(None)):
validate_username(username)
res = await get_github_data(f"{GITHUB_API}/users/{username}/repos?per_page=10", user_token=x_github_token)
if res.status_code != 200:
raise HTTPException(status_code=502, detail="Could not fetch repos.")
langs: dict = {}
for r in res.json():
lang = r.get("language")
if lang:
langs[lang] = langs.get(lang, 0) + 1
top_lang = max(langs, key=langs.get) if langs else "Python"
s_res = await get_github_data(
f"{GITHUB_API}/search/users",
params={"q": f"language:{top_lang}", "per_page": 5},
user_token=x_github_token
)
if s_res.status_code != 200:
raise HTTPException(status_code=502, detail="Similar candidate search failed.")
items = s_res.json().get("items", [])
similar = [i for i in items if i["login"].lower() != username.lower()][:4]
return {"top_language": top_lang, "similar_users": similar}
@router.get("/api/repos/{username}")
async def get_repos(username: str, x_github_token: str = Header(None)):
validate_username(username)
cached = cache_get(f"repos:{username}")
if cached:
return cached
res = await get_github_data(
f"{GITHUB_API}/users/{username}/repos",
params={"per_page": 9, "sort": "updated"},
user_token=x_github_token
)
if res.status_code != 200:
raise HTTPException(status_code=502, detail="Could not fetch repos.")
repos = [
{
"name": r.get("name"),
"description": r.get("description") or "",
"stars": r.get("stargazers_count", 0),
"forks": r.get("forks_count", 0),
"language": r.get("language") or "",
"url": r.get("html_url"),
"updated": (r.get("updated_at") or "")[:10],
}
for r in res.json()
]
result = {"repos": repos}
cache_set(f"repos:{username}", result, ttl=300)
return result
@router.get("/api/skill_radar/{username}")
async def skill_radar(username: str, x_github_token: str = Header(None)):
validate_username(username)
cached = cache_get(f"radar:{username}")
if cached:
return cached
res = await get_github_data(
f"{GITHUB_API}/users/{username}/repos",
params={"per_page": 50, "sort": "updated"},
user_token=x_github_token
)
if res.status_code != 200:
raise HTTPException(status_code=502, detail="Could not fetch repos for radar.")
repos = res.json()
scores = {"Frontend": 0, "Backend": 0, "ML/AI": 0, "Data": 0, "DevOps": 0, "Mobile": 0}
topics_list = []
for r in repos:
lang = str(r.get("language") or "").lower()
topics = [str(t).lower() for t in r.get("topics") or []]
all_terms = [lang] + topics
topics_list.extend(topics)
# Basic heuristic mapping
if any(t in all_terms for t in ["javascript", "typescript", "react", "vue", "css", "html", "svelte", "frontend"]):
scores["Frontend"] += 1
if any(t in all_terms for t in ["python", "java", "go", "ruby", "c#", "php", "backend", "nodejs", "express"]):
scores["Backend"] += 1
if any(t in all_terms for t in ["pytorch", "tensorflow", "keras", "machine-learning", "ai", "deep-learning", "nlp"]):
scores["ML/AI"] += 1
if any(t in all_terms for t in ["sql", "data-science", "pandas", "numpy", "database", "analytics", "hadoop"]):
scores["Data"] += 1
if any(t in all_terms for t in ["docker", "kubernetes", "aws", "terraform", "ci-cd", "devops", "bash"]):
scores["DevOps"] += 1
if any(t in all_terms for t in ["swift", "kotlin", "android", "ios", "flutter", "react-native"]):
scores["Mobile"] += 1
# Normalize max score to 100 for visual appeal
max_score = max(scores.values()) if max(scores.values()) > 0 else 1
scores = {k: int((v / max_score) * 100) for k, v in scores.items()}
# Generate simple summary based on top skill
top_skill = max(scores, key=scores.get) if max(scores.values()) > 0 else "Generalist"
summary = f"Highly proficient in {top_skill} driven by recent project activity."
result = {"scores": scores, "summary": summary}
cache_set(f"radar:{username}", result, ttl=600)
return result