fix: restore backend code after stale checkout
This commit is contained in:
parent
4614810774
commit
cc677eecb1
|
|
@ -0,0 +1,37 @@
|
|||
import os
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
GITEA_URL = os.getenv("GITEA_URL", "http://127.0.0.1:3000").rstrip("/")
|
||||
GITEA_TOKEN = os.getenv("GITEA_TOKEN", "")
|
||||
|
||||
|
||||
def _auth() -> dict[str, str]:
|
||||
headers: dict[str, str] = {"Accept": "application/json"}
|
||||
if GITEA_TOKEN:
|
||||
headers["Authorization"] = f"token {GITEA_TOKEN}"
|
||||
return headers
|
||||
|
||||
|
||||
async def fetch(path: str) -> Any:
|
||||
async with httpx.AsyncClient(base_url=GITEA_URL, timeout=10) as client:
|
||||
r = await client.get(f"/api/v1/{path}", headers=_auth())
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
|
||||
|
||||
async def current_user() -> dict:
|
||||
return await fetch("user")
|
||||
|
||||
|
||||
async def repos() -> list[dict]:
|
||||
return await fetch("user/repos?limit=50")
|
||||
|
||||
|
||||
async def issues() -> list[dict]:
|
||||
return await fetch("user/issues?limit=50&type=all")
|
||||
|
||||
|
||||
async def pull_requests() -> list[dict]:
|
||||
return await fetch("user/pulls?limit=50")
|
||||
56
src/main.py
56
src/main.py
|
|
@ -0,0 +1,56 @@
|
|||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.responses import JSONResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from src.gitea_proxy import current_user, repos, issues, pull_requests
|
||||
from src.models import User, Repo, Issue, PullRequest
|
||||
from src.suggestion_engine import compute
|
||||
from src.views import router as frontend_router
|
||||
|
||||
app = FastAPI(title="Stackchain Dashboard")
|
||||
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=False,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
app.mount("/static", StaticFiles(directory="frontend/static"), name="static")
|
||||
app.include_router(frontend_router)
|
||||
|
||||
|
||||
@app.get("/api/v1/context")
|
||||
async def context() -> JSONResponse:
|
||||
user_data = await current_user()
|
||||
user_model = User(id=user_data["id"], login=user_data["login"], full_name=user_data.get("full_name", ""), email=user_data.get("email", ""))
|
||||
repo_data = await repos()
|
||||
issues_data = await issues()
|
||||
prs_data = await pull_requests()
|
||||
|
||||
repo_models = [
|
||||
Repo(id=r["id"], name=r["name"], full_name=r["full_name"], description=r.get("description", ""), url=r["html_url"], updated_at=r.get("updated_at", ""))
|
||||
for r in repo_data[:50]
|
||||
if isinstance(r, dict)
|
||||
]
|
||||
issue_models = [
|
||||
Issue(id=i["id"], number=i["number"], title=i["title"], state=i["state"], labels=[l.get("name", "") for l in i.get("labels", [])], assignees=[a.get("login", "") for a in i.get("assignees", [])], url=i["html_url"])
|
||||
for i in issues_data[:50]
|
||||
if isinstance(i, dict)
|
||||
]
|
||||
pr_models = [
|
||||
PullRequest(id=p["id"], number=p["number"], title=p["title"], state=p["state"], user=p.get("user", {}).get("login", ""), url=p["html_url"])
|
||||
for p in prs_data[:50]
|
||||
if isinstance(p, dict)
|
||||
]
|
||||
ctx = compute(user_model, repo_models, issue_models, pr_models)
|
||||
return JSONResponse({
|
||||
"user": ctx.user.dict(),
|
||||
"repos": [r.dict() for r in ctx.repos],
|
||||
"issues": [i.dict() for i in ctx.issues],
|
||||
"pull_requests": [p.dict() for p in ctx.pull_requests],
|
||||
"view": ctx.view,
|
||||
"deltas": [d.dict() for d in ctx.deltas],
|
||||
})
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
id: int
|
||||
login: str
|
||||
full_name: str = ""
|
||||
email: str = ""
|
||||
|
||||
|
||||
class Repo(BaseModel):
|
||||
id: int
|
||||
name: str
|
||||
full_name: str
|
||||
description: str = ""
|
||||
url: str
|
||||
updated_at: str = ""
|
||||
|
||||
|
||||
class Issue(BaseModel):
|
||||
id: int
|
||||
number: int
|
||||
title: str
|
||||
state: str
|
||||
labels: list[str] = []
|
||||
assignees: list[str] = []
|
||||
url: str
|
||||
|
||||
|
||||
class PullRequest(BaseModel):
|
||||
id: int
|
||||
number: int
|
||||
title: str
|
||||
state: str
|
||||
user: str
|
||||
url: str
|
||||
|
||||
|
||||
class ViewContext(BaseModel):
|
||||
user: User
|
||||
repos: list[Repo] = []
|
||||
issues: list[Issue] = []
|
||||
pull_requests: list[PullRequest] = []
|
||||
view: str = "dashboard"
|
||||
deltas: list[dict] = []
|
||||
|
||||
|
||||
class SuggestionDelta(BaseModel):
|
||||
panel: str
|
||||
action: str
|
||||
target: str = ""
|
||||
priority: str = "medium"
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
from src.models import Issue, PullRequest, Repo, SuggestionDelta, User, ViewContext
|
||||
|
||||
|
||||
def _load(user: User, repos: list[Repo], issues: list[Issue], prs: list[PullRequest], view: str) -> ViewContext:
|
||||
return ViewContext(user=user, repos=repos, issues=issues, pull_requests=prs, view=view)
|
||||
|
||||
|
||||
def _suggest_review(prs: list[PullRequest]) -> list[SuggestionDelta]:
|
||||
deltas: list[SuggestionDelta] = []
|
||||
for pr in prs:
|
||||
deltas.append(SuggestionDelta(panel="review", action="review", target=str(pr.number), priority="high"))
|
||||
return deltas[:5]
|
||||
|
||||
|
||||
def _suggest_triage(issues: list[Issue]) -> list[SuggestionDelta]:
|
||||
deltas: list[SuggestionDelta] = []
|
||||
for issue in issues:
|
||||
if issue.state != "open":
|
||||
continue
|
||||
priority = "high" if any(label.lower() in {"p0", "priority-high"} for label in issue.labels) else "medium"
|
||||
deltas.append(SuggestionDelta(panel="triage", action="open", target=str(issue.number), priority=priority))
|
||||
return deltas[:8]
|
||||
|
||||
|
||||
def _suggest_focus(repos: list[Repo], prs: list[PullRequest], issues: list[Issue]) -> list[SuggestionDelta]:
|
||||
if len(prs) > len(issues):
|
||||
return [SuggestionDelta(panel="layout", action="highlight", target="review", priority="medium")]
|
||||
if len(issues) > len(prs):
|
||||
return [SuggestionDelta(panel="layout", action="highlight", target="issues", priority="medium")]
|
||||
return [SuggestionDelta(panel="layout", action="highlight", target="dashboard", priority="low")]
|
||||
|
||||
|
||||
def compute(user: User, repos: list[Repo], issues: list[Issue], prs: list[PullRequest], view: str = "dashboard") -> ViewContext:
|
||||
ctx = _load(user, repos, issues, prs, view)
|
||||
ctx.deltas = _suggest_review(prs) + _suggest_triage(issues) + _suggest_focus(repos, prs, issues)
|
||||
return ctx
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
from fastapi import APIRouter
|
||||
from fastapi.responses import HTMLResponse
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/", response_class=HTMLResponse)
|
||||
async def dashboard() -> str:
|
||||
return open("frontend/index.html").read()
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
from src.models import Issue, PullRequest, Repo, User
|
||||
from src.suggestion_engine import compute
|
||||
|
||||
|
||||
def test_suggest_review_when_more_prs_than_issues():
|
||||
user = User(id=1, login="timmy", full_name="Timmy", email="timmy@example.com")
|
||||
prs = [PullRequest(id=1, number=1, title="p", state="open", user="timmy", url="http://example.com")]
|
||||
ctx = compute(user, [], [], prs)
|
||||
assert [d for d in getattr(ctx, "deltas", []) if d.panel == "review"]
|
||||
|
||||
|
||||
def test_suggest_triage_when_open_issues():
|
||||
user = User(id=1, login="timmy", full_name="Timmy", email="timmy@example.com")
|
||||
issues = [Issue(id=1, number=1, title="t", state="open", labels=["P0"], assignees=[], url="http://example.com")]
|
||||
ctx = compute(user, [], issues, [])
|
||||
assert [d for d in getattr(ctx, "deltas", []) if d.panel == "triage"]
|
||||
Loading…
Reference in New Issue
Block a user