Datathon / backend /backend_app /core /github_client.py
RishiXD's picture
Upload 67 files
b23ff00 verified
Raw History Blame Contribute Delete
6.47 kB
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