83 lines
4.2 KiB
Python
83 lines
4.2 KiB
Python
"""Freigaben in der App (→ Konzept 4.5, 10.5): Liste, Antrag mit echtem Diff, Entscheidung."""
|
|
|
|
import uuid
|
|
from typing import Annotated
|
|
|
|
from fastapi import APIRouter, Form, Request
|
|
from fastapi.responses import HTMLResponse, Response
|
|
from sqlalchemy import select
|
|
|
|
from app import web
|
|
from app.approvals import service as approvals
|
|
from app.approvals.models import LABELS, ApprovalRequest
|
|
from app.data_sources.models import DataSource
|
|
from app.identity.models import User
|
|
from app.tasks import engine
|
|
from app.tasks.models import TaskRun
|
|
from app.workspaces.models import Workspace
|
|
|
|
|
|
def names(session, approvals_: list[ApprovalRequest]) -> dict[str, dict[int, str]]:
|
|
"""Anzeigenamen für Nutzer, Workspaces und Quellen der Anträge."""
|
|
users = {a.requested_by for a in approvals_} | {a.decided_by for a in approvals_ if a.decided_by}
|
|
return {
|
|
"users": dict(session.execute(select(User.id, User.display_name).where(User.id.in_(users))).all()),
|
|
"workspaces": dict(session.execute(select(Workspace.id, Workspace.name).where(
|
|
Workspace.id.in_({a.workspace_id for a in approvals_}))).all()),
|
|
"sources": dict(session.execute(select(DataSource.id, DataSource.name).where(
|
|
DataSource.id.in_({a.source_id for a in approvals_ if a.source_id}))).all()),
|
|
}
|
|
|
|
|
|
def router() -> APIRouter:
|
|
r = APIRouter()
|
|
|
|
@r.get("/approvals")
|
|
def overview(request: Request, principal: web.User) -> HTMLResponse:
|
|
with web.db(request).session() as session:
|
|
open_, recent = approvals.overview(session, principal)
|
|
context = {"open": open_, "recent": recent, "labels": LABELS, **names(session, open_ + recent)}
|
|
return web.render(request, "approvals.html", context)
|
|
|
|
@r.get("/approvals/count")
|
|
def count(request: Request, principal: web.User) -> HTMLResponse:
|
|
with web.db(request).session() as session:
|
|
return web.render(request, "_approval_badge.html", {"open_approvals": approvals.count_open(session, principal)})
|
|
|
|
@r.get("/approvals/{approval_id}")
|
|
def detail(request: Request, principal: web.User, approval_id: int) -> HTMLResponse:
|
|
with web.db(request).session() as session:
|
|
approvals.expire_due_session(session)
|
|
approval, may_approve = approvals.get(session, principal, approval_id)
|
|
blocked = approvals.blocked(session, principal.user_id, approval)
|
|
run = session.get(TaskRun, approval.run_id)
|
|
replaced_by = session.scalar(select(ApprovalRequest.id).where(ApprovalRequest.replaces_id == approval.id))
|
|
context = {"a": approval, "may_decide": not blocked, "blocked": blocked, "may_approve": may_approve,
|
|
"run": run, "labels": LABELS,
|
|
"replaced_by": replaced_by, "current_workspace": session.get(Workspace, approval.workspace_id),
|
|
**names(session, [approval])}
|
|
return web.render(request, "approval.html", context)
|
|
|
|
@r.post("/approvals/{approval_id}/decide")
|
|
def decide(request: Request, principal: web.Checked, approval_id: int, decision: Annotated[str, Form()],
|
|
comment: Annotated[str, Form()] = "") -> Response:
|
|
with web.db(request).session() as session:
|
|
approvals.decide(session, principal, approval_id, decision == "approve", comment)
|
|
return web.redirect(f"/approvals/{approval_id}")
|
|
|
|
@r.post("/approvals/{approval_id}/rerequest")
|
|
def rerequest(request: Request, principal: web.Checked, approval_id: int) -> Response:
|
|
with web.db(request).session() as session:
|
|
run = approvals.rerequest(session, principal, approval_id)
|
|
chat_id = run.chat_session_id
|
|
return web.redirect(f"/chats/{chat_id}" if chat_id else "/approvals")
|
|
|
|
@r.post("/runs/{run_id}/reconcile")
|
|
def reconcile(request: Request, principal: web.Checked, run_id: uuid.UUID,
|
|
give_up: Annotated[bool, Form()] = False) -> Response:
|
|
with web.db(request).session() as session:
|
|
run = engine.reconcile(session, principal, run_id, give_up)
|
|
chat_id = run.chat_session_id
|
|
return web.redirect(f"/chats/{chat_id}" if chat_id else "/workspaces")
|
|
|
|
return r
|