124 lines
6.8 KiB
Python
124 lines
6.8 KiB
Python
"""Ansicht „Änderungen“ im Chat und die Aktionen dazu (→ Git-Workflow).
|
|
|
|
Jede Aktion löst ein Mitglied des Workspace aus; per HTMX kommt die aktualisierte
|
|
Ansicht zurück, Fehler erscheinen darin.
|
|
"""
|
|
|
|
from collections.abc import Callable
|
|
from typing import Annotated
|
|
|
|
from fastapi import APIRouter, Form, Request
|
|
from fastapi.responses import HTMLResponse, Response
|
|
|
|
from app import web
|
|
from app.changes import service as changes
|
|
from app.changes.models import ChatBranch
|
|
from app.chat import service as chats
|
|
from app.core.errors import Invalid, NotFound
|
|
from app.data_sources.connectors.git_repo import GitError
|
|
|
|
Text = Annotated[str, Form()]
|
|
|
|
|
|
def router() -> APIRouter:
|
|
r = APIRouter()
|
|
|
|
def panel(request: Request, principal, chat_id: int, error: str = "", notice: str = "",
|
|
status: int = 200) -> HTMLResponse:
|
|
with web.db(request).session() as session:
|
|
chat = chats.get(session, principal, chat_id)
|
|
views = changes.views(session, web.settings(request), web.vault(request), chat.id)
|
|
return web.render(request, "_changes.html", {"chat": chat, "views": views, "error": error,
|
|
"notice": notice}, status)
|
|
|
|
def act(request: Request, principal, branch_id: int, action: Callable, notice: str) -> Response:
|
|
with web.db(request).session() as session:
|
|
branch = session.get(ChatBranch, branch_id)
|
|
if branch is None:
|
|
raise NotFound
|
|
chat_id = branch.chat_session_id
|
|
try:
|
|
with web.db(request).session() as session:
|
|
action(session)
|
|
except (Invalid, GitError) as e:
|
|
return panel(request, principal, chat_id, error=str(e), status=400)
|
|
if request.headers.get("HX-Request"):
|
|
return panel(request, principal, chat_id, notice=notice)
|
|
return web.redirect(f"/chats/{chat_id}#changes")
|
|
|
|
@r.get("/chats/{chat_id}/changes")
|
|
def show(request: Request, principal: web.User, chat_id: int) -> HTMLResponse:
|
|
return panel(request, principal, chat_id)
|
|
|
|
@r.post("/branches/{branch_id}/push")
|
|
def push(request: Request, principal: web.Checked, branch_id: int) -> Response:
|
|
return act(request, principal, branch_id, lambda s: changes.push(
|
|
s, principal, web.settings(request), web.vault(request), branch_id), "Übertragen.")
|
|
|
|
@r.post("/branches/{branch_id}/pull-request")
|
|
def pull_request(request: Request, principal: web.Checked, branch_id: int, title: Text,
|
|
description: Text = "", draft: Annotated[bool, Form()] = False) -> Response:
|
|
def create(session) -> None:
|
|
branch = changes.create_pull_request(session, principal, web.settings(request), web.vault(request),
|
|
branch_id, title, description, draft)
|
|
if not branch.pr_id:
|
|
raise Invalid(f"Branch {branch.name} ist gepusht. Für diese Quelle ist kein Pull-Request-Anbieter "
|
|
"eingestellt; den Pull Request bitte im Git-Server anlegen.")
|
|
return act(request, principal, branch_id, create, "Pull Request erstellt.")
|
|
|
|
@r.post("/branches/{branch_id}/ready")
|
|
def ready(request: Request, principal: web.Checked, branch_id: int) -> Response:
|
|
return act(request, principal, branch_id, lambda s: changes.publish(
|
|
s, principal, web.settings(request), web.vault(request), branch_id), "Als bereit zum Review markiert.")
|
|
|
|
@r.post("/branches/{branch_id}/undo")
|
|
def undo(request: Request, principal: web.Checked, branch_id: int) -> Response:
|
|
return act(request, principal, branch_id, lambda s: changes.undo(
|
|
s, principal, web.settings(request), web.vault(request), branch_id), "Letzten Commit zurückgenommen.")
|
|
|
|
@r.post("/branches/{branch_id}/refresh")
|
|
def refresh(request: Request, principal: web.Checked, branch_id: int) -> Response:
|
|
def update(session) -> None:
|
|
t = changes.load(session, principal, web.settings(request), web.vault(request), branch_id)
|
|
if not t.branch.pr_id:
|
|
raise Invalid("Es gibt noch keinen Pull Request.")
|
|
for event in changes.refresh(session, web.settings(request), web.vault(request), t.branch):
|
|
if event != "comments":
|
|
changes.notify_event(session, t.branch, event)
|
|
return act(request, principal, branch_id, update, "Stand aus Bitbucket aktualisiert.")
|
|
|
|
@r.post("/branches/{branch_id}/sync")
|
|
def sync(request: Request, principal: web.Checked, branch_id: int) -> Response:
|
|
"""Mit dem Ziel-Branch abgleichen (Merge-Commit); nur konfliktfrei, Konflikte löst die KI."""
|
|
return act(request, principal, branch_id, lambda s: changes.sync_target(
|
|
web.db(request), principal, web.settings(request), web.vault(request), branch_id),
|
|
"Mit dem Ziel-Branch abgeglichen (Merge-Commit, noch nicht übertragen).")
|
|
|
|
@r.post("/branches/{branch_id}/resolve")
|
|
def resolve(request: Request, principal: web.Checked, branch_id: int) -> Response:
|
|
"""Konflikte von der KI lösen lassen: eine Nachricht im Chat, die KI nutzt merge_target."""
|
|
with web.db(request).session() as session:
|
|
t = changes.load(session, principal, web.settings(request), web.vault(request), branch_id, lock=False)
|
|
changes.check_open(t.branch)
|
|
chats.send(session, principal, t.chat.id,
|
|
f"Bitte gleiche den Branch {t.branch.name} mit {t.branch.target} ab: rufe merge_target auf "
|
|
f"und löse die Konflikte so, dass die Änderungen aus {t.branch.target} erhalten bleiben und "
|
|
"unsere Änderungen weiter wirken. Fasse danach zusammen, wie du die Konflikte gelöst hast.")
|
|
chat_id = t.chat.id
|
|
return web.redirect(f"/chats/{chat_id}#messages-end")
|
|
|
|
@r.post("/branches/{branch_id}/review-comments")
|
|
def review_comments(request: Request, principal: web.Checked, branch_id: int) -> Response:
|
|
"""Kommentare einarbeiten: eine Nachricht im Chat, die KI liest die Kommentare mit git_pr_comments."""
|
|
with web.db(request).session() as session:
|
|
t = changes.load(session, principal, web.settings(request), web.vault(request), branch_id, lock=False)
|
|
if not t.branch.pr_id or t.branch.closed:
|
|
raise Invalid("Es gibt keinen offenen Pull Request.")
|
|
chats.send(session, principal, t.chat.id,
|
|
f"Bitte arbeite die Review-Kommentare aus Pull Request #{t.branch.pr_id} ein: lies sie mit "
|
|
"git_pr_comments, setze berechtigte Hinweise mit commit_change um und fasse zusammen, was du "
|
|
"geändert hast und was offen bleibt.")
|
|
chat_id = t.chat.id
|
|
return web.redirect(f"/chats/{chat_id}#messages-end")
|
|
|
|
return r
|