Files
devplacepy/devplacepy/routers/issues/comment.py
T
retoorandClaude Sonnet 5 57087536e5 Attribute Devii AI spend to its invoking action, fix quiz question-at-a-time review, DB API/isslop result routes, workspace docs, and drop redundant docstrings
- Route Devii-driven AI gateway cost to the action/tool that triggered
  it instead of a blanket "internal" bucket, so per-feature AI spend
  is attributable.
- Fix the quiz attempt review to show one previously-answered question
  at a time instead of all of them at once, and stop a quiz endpoint
  linked from the quiz flow from responding with raw JSON.
- Add DB API async query result route and AI Usage Analyzer annotated
  source/media routes, with traversal-safe uid/path handling and
  matching tests.
- Add Code Farm action audit logging (plant/harvest/buy-plot/upgrade/
  fertilize) and related admin workspace/services/trash/gateway route
  and doc touch-ups.
- Drop redundant docstrings from access_tokens.py per the no-comments
  convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VL9Xn57W5UR3HZbbuuzxdK
2026-09-03 08:47:57 +02:00

90 lines
3.1 KiB
Python

# retoor <retoor@molodetz.nl>
import logging
from typing import Annotated
from fastapi import Depends, APIRouter, Request
from devplacepy.attachments import (
get_orphan_attachments_batch,
link_attachments,
mirror_attachment_to_gitea,
split_attachment_uids,
)
from devplacepy.database import get_table
from devplacepy.models import IssueCommentForm
from devplacepy.responses import action_result, json_error
from devplacepy.services.audit import record as audit
from devplacepy.services.gitea import runtime, store
from devplacepy.services.gitea.client import GiteaError
from devplacepy.services.gitea.config import gitea_config
from devplacepy.services.background import background
from devplacepy.utils import create_notification, require_user
from devplacepy.dependencies import json_or_form
logger = logging.getLogger(__name__)
router = APIRouter()
def _notify_admins(actor: dict, number: int) -> None:
for admin in get_table("users").find(role="Admin"):
if admin["uid"] == actor["uid"]:
continue
create_notification(
admin["uid"],
"issue",
f"{actor['username']} commented on issue #{number}",
str(number),
f"/issues/{number}",
)
@router.post("/{number}/comment")
async def comment_issue(
request: Request, number: int, data: Annotated[IssueCommentForm, Depends(json_or_form(IssueCommentForm))]
):
user = require_user(request)
if not gitea_config().is_configured:
return json_error(503, "The issue tracker is not configured")
client = runtime.get_client()
body = (
f"{data.body.strip()}\n\n---\n"
f"*Posted by **{user['username']}** via DevPlace.*"
)
try:
comment = await client.create_comment(number, body)
except GiteaError as exc:
logger.warning("Could not post comment on #%s: %s", number, exc)
audit.record(
request,
"issue.comment",
user=user,
result="failure",
target_type="issue",
target_uid=str(number),
summary=f"Failed to comment on issue #{number}: {exc}",
)
return json_error(exc.status or 502, "Could not post the comment")
comment_id = int(comment.get("id", 0))
store.record_comment_author(comment_id, number, user["uid"])
owned = get_orphan_attachments_batch(
split_attachment_uids(data.attachment_uids), user
)
if owned:
link_attachments(owned, "issue_comment", str(comment_id))
for uid in owned:
await mirror_attachment_to_gitea(uid)
background.submit(_notify_admins, user, number)
audit.record(
request,
"issue.comment",
user=user,
target_type="issue",
target_uid=str(number),
summary=f"{user['username']} commented on issue #{number}",
metadata={"number": number, "comment_id": comment_id},
links=[audit.target("issue", str(number))],
)
logger.info("Comment posted on issue #%s by %s", number, user["username"])
return action_result(request, f"/issues/{number}", data={"comment_id": comment_id})