Download backend/backend_app/core/github_client.py from RishiXD/Datathon: direct link, hf CLI and curl.
- Browser
- Download file 6.47 kB
-
https://huggingface.co/spaces/RishiXD/Datathon/resolve/main/backend/backend_app/core/github_client.py
- Command line
-
hf download hf://spaces/RishiXD/Datathon/backend/backend_app/core/github_client.py
-
curl -L -o github_client.py https://huggingface.co/spaces/RishiXD/Datathon/resolve/main/backend/backend_app/core/github_client.py
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 | |