Datathon / backend /backend_app /core /github_client.py
RishiXD's picture
Upload 67 files
b23ff00 verified
from datetime import datetime, timezone
from typing import List, Dict, Any, Optional
import requests
import json
import random # Fallback for story points
from backend_app.core.models import RawCommit, RawPR, RawReview
from backend_app.core.planning_models import RawIssue, RawIssueEvent, RawSprint
# Base URL for the custom GitHub App/API
BASE_URL = "https://samyak000-github-app.hf.space/insights"
class GitHubClient:
def __init__(self, org: str, repo: str):
self.org = org
self.repo = repo
def _parse_ts(self, ts_str: Optional[str]) -> datetime:
if not ts_str:
return datetime.now(timezone.utc)
try:
return datetime.fromisoformat(ts_str.replace("Z", "+00:00"))
except:
return datetime.now(timezone.utc)
def fetch_commits(self) -> List[RawCommit]:
url = f"{BASE_URL}/commits"
payload = {"org": self.org, "repo": self.repo}
try:
resp = requests.post(url, json=payload, timeout=10)
if resp.status_code != 200: return []
data = resp.json()
commits = []
for item in data.get("commits", []):
try:
c = item.get("commit", {})
author_info = c.get("author", {})
ts = self._parse_ts(author_info.get("date"))
author_name = author_info.get("name", "Unknown")
if item.get("author") and "login" in item["author"]:
author_name = item["author"]["login"]
files = []
if "files" in item:
files = [f.get("filename") for f in item["files"] if "filename" in f]
commits.append(RawCommit(
commit_id=item.get("sha", ""),
author=author_name,
timestamp=ts,
files_changed=files
))
except Exception: continue
return commits
except Exception: return []
def fetch_prs(self) -> List[RawPR]:
url = f"{BASE_URL}/pull-requests"
payload = {"org": self.org, "repo": self.repo}
try:
resp = requests.post(url, json=payload, timeout=15)
if resp.status_code != 200: return []
data = resp.json()
# Adjust based on actual key.
# If endpoint is /pull-requests, maybe key is "pull_requests" or "prs"?
# I'll check generic keys if specific fails
raw_list = data.get("pull_requests", data.get("prs", []))
prs = []
for item in raw_list:
try:
# Generic structure mapping
pid = str(item.get("number", item.get("id", "unknown")))
user = item.get("user", {})
author = user.get("login", "unknown")
created = self._parse_ts(item.get("created_at"))
merged = self._parse_ts(item.get("merged_at")) if item.get("merged_at") else None
# Files? Usually not in list view.
# If this API is "smart", maybe it includes them?
# If not, we assume empty or try "files" key
files = [] # item.get("files", []) if we're lucky
prs.append(RawPR(
pr_id=pid,
author=author,
created_at=created,
merged_at=merged,
files_changed=files
))
except: continue
return prs
except Exception: return []
def fetch_issues(self) -> List[RawIssue]:
url = f"{BASE_URL}/pull-issues"
payload = {"org": self.org, "repo": self.repo}
try:
resp = requests.post(url, json=payload, timeout=15)
if resp.status_code != 200: return []
data = resp.json()
raw_list = data.get("issues", [])
issues = []
for item in raw_list:
try:
# Skip PRs if they come through this endpoint
if "pull_request" in item and item["pull_request"]:
continue
iid = f"GH-{item.get('number')}"
title = item.get("title", "")
# Map to Planning Model (Jira-style)
# We need to fabricate some data for the Planning Engine to work
assignees = item.get("assignees", [])
assignee = assignees[0].get("login") if assignees else "unassigned"
# Module? Try label
labels = [l.get("name") for l in item.get("labels", [])]
module_id = "general"
for l in labels:
if "module:" in l: # Convention?
module_id = l.replace("module:", "")
break
# Sprint? Milestone?
sprint_id = "SPR-LIVE" # Default bucket
if item.get("milestone"):
sprint_id = f"SPR-{item['milestone'].get('title')}"
issues.append(RawIssue(
issue_id=iid,
sprint_id=sprint_id,
title=title,
issue_type="Story", # Default
story_points=1, # Default
assignee=assignee,
module_id=module_id,
created_at=self._parse_ts(item.get("created_at"))
))
except: continue
return issues
except Exception: return []
def fetch_activity(self) -> List[RawIssueEvent]:
# Maps activity timeline to issue events (transitions)
return [] # Placeholder, complex to map generic activity stream to "status changes" reliably without more info