diff --git a/server/app.py b/server/app.py index 507bbd2..a95ff1a 100644 --- a/server/app.py +++ b/server/app.py @@ -1,4 +1,3 @@ -import difflib import html import mimetypes import os @@ -8,14 +7,12 @@ import asyncpg import litestar -import uuid6 from litestar import Response from litestar.config.csrf import CSRFConfig from litestar.contrib.jinja import JinjaTemplateEngine from litestar.datastructures import State from litestar.exceptions import ( HTTPException, - InternalServerException, NotFoundException, ) from litestar.response import Template @@ -32,10 +29,10 @@ REDIS_DSN, UTC, ) -from server import auth, contrib, review, tmpl +from server import auth, contrib, patch, review, tmpl from server.auth import require_user_login, session_auth_config from server.base import Request, http_client, pg, pg_pool_startup -from server.model import Patch, PatchState +from server.model import PatchState from server.router import Router @@ -176,80 +173,6 @@ def __index_row_sorter(r: asyncpg.Record) -> tuple[int, datetime]: return 0, r["updated_at"] -@router -@litestar.get("/patch/{patch_id:str}") -async def get_patch(patch_id: str, request: Request) -> Template: - try: - uuid6.UUID(hex=patch_id) - except ValueError as e: - # not valid uuid string, just raise not-found - raise NotFoundException() from e - - p = await pg.fetchrow( - """select * from patch where id = $1 and deleted_at is NULL limit 1""", patch_id - ) - if not p: - raise NotFoundException() - - patch = Patch(**p) - - name_patch = "" - if patch.name is not None: - name_patch = "".join( - difflib.unified_diff([patch.original_name + "\n"], [patch.name + "\n"], "name", "name") - ) - - infobox_patch = "" - if patch.infobox is not None: - if patch.original_infobox is None: - logger.error("broken patch {!r}", patch_id) - raise InternalServerException - infobox_patch = "".join( - difflib.unified_diff( - patch.original_infobox.splitlines(True), - patch.infobox.splitlines(True), - "infobox", - "infobox", - n=5, - ) - ) - - summary_patch = "" - if patch.summary is not None: - if patch.original_summary is None: - logger.error("broken patch {!r}", patch_id) - raise InternalServerException - summary_patch = "".join( - difflib.unified_diff( - (patch.original_summary + "\n").splitlines(True), - (patch.summary + "\n").splitlines(True), - "summary", - "summary", - ) - ) - - reviewer = None - if patch.state != PatchState.Pending: - reviewer = await pg.fetchrow( - "select * from patch_users where user_id=$1", patch.wiki_user_id - ) - - submitter = await pg.fetchrow("select * from patch_users where user_id=$1", patch.from_user_id) - - return Template( - "patch.html.jinja2", - context={ - "patch": p, - "auth": request.auth, - "name_patch": name_patch, - "infobox_patch": infobox_patch, - "summary_patch": summary_patch, - "reviewer": reviewer, - "submitter": submitter, - }, - ) - - def before_req(req: litestar.Request[None, None, State]) -> None: req.state["now"] = datetime.now(tz=UTC) @@ -310,6 +233,7 @@ async def startup_fetch_missing_users(*args: Any, **kwargs: Any) -> None: *auth.router, *contrib.router, *review.router, + *patch.router, *router, ], template_config=TemplateConfig( diff --git a/server/contrib.py b/server/contrib.py index 2d9d579..6016e50 100644 --- a/server/contrib.py +++ b/server/contrib.py @@ -45,7 +45,7 @@ async def suggest_ui(request: Request, subject_id: int = 0) -> Response[Any]: ) -@dataclass(frozen=True, slots=True) +@dataclass(frozen=True, slots=True, kw_only=True) class CreateSuggestion: name: str infobox: str diff --git a/server/patch.py b/server/patch.py new file mode 100644 index 0000000..4fd7621 --- /dev/null +++ b/server/patch.py @@ -0,0 +1,89 @@ +import difflib + +import litestar +import uuid6 +from litestar.exceptions import InternalServerException, NotFoundException +from litestar.response import Template +from loguru import logger + +from server.base import Request, pg +from server.model import Patch, PatchState +from server.router import Router + + +router = Router() + + +@router +@litestar.get("/patch/{patch_id:str}") +async def get_patch(patch_id: str, request: Request) -> Template: + try: + uuid6.UUID(hex=patch_id) + except ValueError as e: + # not valid uuid string, just raise not-found + raise NotFoundException() from e + + p = await pg.fetchrow( + """select * from patch where id = $1 and deleted_at is NULL limit 1""", patch_id + ) + if not p: + raise NotFoundException() + + patch = Patch(**p) + + name_patch = "" + if patch.name is not None: + name_patch = "".join( + difflib.unified_diff([patch.original_name + "\n"], [patch.name + "\n"], "name", "name") + ) + + infobox_patch = "" + if patch.infobox is not None: + if patch.original_infobox is None: + logger.error("broken patch {!r}", patch_id) + raise InternalServerException + infobox_patch = "".join( + difflib.unified_diff( + (patch.original_infobox + "\n").splitlines(True), + (patch.infobox + "\n").splitlines(True), + "infobox", + "infobox", + n=5, + ) + ) + + summary_patch = "" + if patch.summary is not None: + if patch.original_summary is None: + logger.error("broken patch {!r}", patch_id) + raise InternalServerException + summary_patch = "".join( + # need a tailing new line to generate correct diff + difflib.unified_diff( + (patch.original_summary + "\n").splitlines(True), + (patch.summary + "\n").splitlines(True), + "summary", + "summary", + ) + ) + + reviewer = None + if patch.state != PatchState.Pending: + reviewer = await pg.fetchrow( + "select * from patch_users where user_id=$1", patch.wiki_user_id + ) + + submitter = await pg.fetchrow("select * from patch_users where user_id=$1", patch.from_user_id) + + return Template( + "patch.html.jinja2", + context={ + "patch": p, + "auth": request.auth, + "name_patch": name_patch, + "infobox_patch": infobox_patch, + "summary_patch": summary_patch, + "reviewer": reviewer, + "submitter": submitter, + }, + )