munch-ease-backend/api/persons.py

94 lines
2.8 KiB
Python
Raw Permalink Normal View History

2025-10-18 05:50:43 +00:00
from __future__ import annotations
from typing import List, Optional
import aiosqlite
2025-10-18 07:21:04 +00:00
from fastapi import APIRouter, Depends, Query, Response
2025-10-18 05:50:43 +00:00
import persons
Squashed commit of the following: commit fcd005b8624023547f28b7b28e59e6099bcfc7d4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 20:24:07 2025 +1100 Openapi tightening commit f93bd8f641d561052c7bd075bae321b4ff3b676d Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 19:03:52 2025 +1100 Removed refactor strategy doc commit 0c5a61092f522be0c47cbbe86917c8a7e4e2d339 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 17:48:33 2025 +1100 mypy & ruff checks commit 23d66d6b18984127e17c73c3063f6120385935e9 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 16:49:35 2025 +1100 Final removal of db.py files commit f454aed1ca9783cc558cc203f29a7fe31b62a975 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:42:31 2025 +1100 Finalise restructure, remove db.py files commit 7187f6dd89489521538791c6bdebb426514beb99 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:34:54 2025 +1100 commit 6fea227ae20d32b8eb1e7a4885006a620fcc7bb1 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:32:53 2025 +1100 commit 27415e7e02d89195ad514cb017a9dbbf84d7a5e4 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:31:10 2025 +1100 commit b773428033d855f9ad82005602e049c1a2e3c585 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:28:58 2025 +1100 commit 116592c95278d995f4c516e87f2cea43cf5b7735 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:25:21 2025 +1100 commit 03ec565faea088971968ee2f9bb83e2de16b21f3 Author: jableader <jacobdunk@gmail.com> Date: Sun Oct 19 15:21:29 2025 +1100 Plan
2025-10-19 09:24:23 +00:00
from api.deps import get_db
2025-10-18 06:00:01 +00:00
from common import Page
2025-10-18 05:50:43 +00:00
router = APIRouter(prefix="/persons", tags=["persons"])
2025-10-18 06:00:01 +00:00
@router.get(
"",
operation_id="listPersons",
response_model=Page[persons.Person],
summary="List persons (paginated)",
responses={
200: {
"description": "A page of persons",
"content": {
"application/json": {
"example": {
"items": [{"id": 1, "name": "Ada Lovelace"}],
2025-10-18 06:00:01 +00:00
"nextCursor": "2",
"prevCursor": "0",
"total": 1,
2025-10-18 06:00:01 +00:00
}
}
},
}
},
)
2025-10-18 05:50:43 +00:00
async def list_persons(
2025-10-18 06:00:01 +00:00
q: Optional[str] = Query(
default=None,
description="Optional case-insensitive name filter (applied after page read; may be pushed to SQL in the future).",
),
cursor: Optional[str] = Query(
default=None,
description="Opaque cursor for pagination. Pass the value returned in nextCursor to fetch the next page.",
),
limit: int = Query(
50,
ge=1,
le=200,
description="Maximum number of items to return (1-200).",
),
2025-10-18 05:50:43 +00:00
conn: aiosqlite.Connection = Depends(get_db),
) -> Page[persons.Person]:
2025-10-18 06:00:01 +00:00
# v1: DB-backed pagination
last_id = None
if cursor:
try:
last_id = int(cursor)
except ValueError:
last_id = None
2025-10-18 05:50:43 +00:00
2025-10-18 06:00:01 +00:00
fetch_limit = limit + 1
paged: List[persons.Person] = []
if q:
async for p in persons.search_by_name_paged(conn, q, last_id, fetch_limit):
paged.append(p)
else:
async for p in persons.get_all_paged(conn, last_id, fetch_limit):
paged.append(p)
2025-10-18 05:50:43 +00:00
2025-10-18 06:00:01 +00:00
has_more = len(paged) > limit
items = paged[:limit]
next_cursor = str(items[-1].id) if has_more and items else None
# Compute prevCursor via DB helper
prev_cursor: Optional[str] = None
if items:
first_id = items[0].id
prev_cursor = await persons.compute_prev_cursor(conn, first_id, limit, q)
total = await (persons.count_by_name(conn, q) if q else persons.count_all(conn))
return Page(items=items, nextCursor=next_cursor, prevCursor=prev_cursor, total=total)
@router.post(
"",
operation_id="createPerson",
summary="Create a person",
2025-10-18 07:21:04 +00:00
response_model=persons.Person,
2025-10-18 06:00:01 +00:00
)
2025-10-18 05:50:43 +00:00
async def create_person(
2025-10-18 07:21:04 +00:00
person: persons.Person, response: Response, conn: aiosqlite.Connection = Depends(get_db)
2025-10-18 05:50:43 +00:00
) -> persons.Person:
2025-10-18 06:00:01 +00:00
await persons.insert_person(conn, person)
2025-10-18 07:21:04 +00:00
response.headers["Location"] = f"/api/v1/persons/{person.id}"
2025-10-18 06:00:01 +00:00
return person