generated from john/python-template
203 lines
6.2 KiB
Python
203 lines
6.2 KiB
Python
"""API routes for relationship and classification registries."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Annotated
|
|
from uuid import UUID
|
|
|
|
from fastapi import APIRouter
|
|
from fastapi import Depends
|
|
from fastapi import Request
|
|
from fastapi import Response
|
|
from pydantic import BaseModel
|
|
from pydantic import ConfigDict
|
|
from pydantic import Field
|
|
|
|
from transcription.db.models import Document
|
|
from transcription.db.models import DocumentPerson
|
|
from transcription.db.models import DocumentType
|
|
from transcription.db.models import PersonRole
|
|
from transcription.services import DocumentService
|
|
from transcription.services import PeopleService
|
|
|
|
router = APIRouter(prefix="/api", tags=["documents"])
|
|
|
|
|
|
class ApiModel(BaseModel):
|
|
model_config = ConfigDict(extra="forbid", frozen=True, str_strip_whitespace=True)
|
|
|
|
|
|
class DocumentTypeRead(ApiModel):
|
|
id: UUID
|
|
label: str
|
|
is_active: bool
|
|
|
|
|
|
class PersonRoleRead(ApiModel):
|
|
id: UUID
|
|
label: str
|
|
is_active: bool
|
|
|
|
|
|
class DocumentTypeWriteRequest(ApiModel):
|
|
document_type_id: UUID
|
|
|
|
|
|
class DocumentTypeWriteResponse(ApiModel):
|
|
document_id: UUID
|
|
document_type_id: UUID
|
|
|
|
|
|
class DocumentPersonWriteRequest(ApiModel):
|
|
person_id: UUID
|
|
role_id: UUID
|
|
|
|
|
|
class DocumentPersonRoleUpdateRequest(ApiModel):
|
|
role_id: UUID
|
|
|
|
|
|
class DocumentPersonRead(ApiModel):
|
|
id: UUID
|
|
document_id: UUID
|
|
person_id: UUID
|
|
role_id: UUID
|
|
role_label: str | None = None
|
|
person_name: str | None = None
|
|
|
|
|
|
class DocumentPeopleResponse(ApiModel):
|
|
document_id: UUID
|
|
links: list[DocumentPersonRead] = Field(default_factory=list)
|
|
|
|
|
|
def _document_type_to_read(item: DocumentType) -> DocumentTypeRead:
|
|
item_id, label, is_active = _registry_read_values(item)
|
|
return DocumentTypeRead(id=item_id, label=label, is_active=is_active)
|
|
|
|
|
|
def _person_role_to_read(item: PersonRole) -> PersonRoleRead:
|
|
item_id, label, is_active = _registry_read_values(item)
|
|
return PersonRoleRead(id=item_id, label=label, is_active=is_active)
|
|
|
|
|
|
def _registry_read_values(item: DocumentType | PersonRole) -> tuple[UUID, str, bool]:
|
|
return item.id, item.label, item.is_active
|
|
|
|
|
|
def _document_person_to_read(item: DocumentPerson) -> DocumentPersonRead:
|
|
person_name = item.person.full_name if item.person is not None else None
|
|
return DocumentPersonRead(
|
|
id=item.id,
|
|
document_id=item.document_id,
|
|
person_id=item.person_id,
|
|
role_id=item.role_id,
|
|
role_label=item.role_ref.label if item.role_ref is not None else None,
|
|
person_name=person_name,
|
|
)
|
|
|
|
|
|
def _document_to_type_response(item: Document) -> DocumentTypeWriteResponse:
|
|
if item.document_type_id is None:
|
|
raise ValueError("Document Type assignment did not persist")
|
|
return DocumentTypeWriteResponse(
|
|
document_id=item.id,
|
|
document_type_id=item.document_type_id,
|
|
)
|
|
|
|
|
|
def get_document_service(request: Request) -> DocumentService:
|
|
"""Resolve the document service from app lifespan state when available."""
|
|
services = getattr(request.app.state, "services", None)
|
|
if services is not None:
|
|
return services.documents
|
|
return DocumentService()
|
|
|
|
|
|
def get_people_service(request: Request) -> PeopleService:
|
|
"""Resolve the People service from app lifespan state when available."""
|
|
services = getattr(request.app.state, "services", None)
|
|
if services is not None:
|
|
return services.people
|
|
return PeopleService()
|
|
|
|
|
|
DocumentServiceDependency = Annotated[DocumentService, Depends(get_document_service)]
|
|
PeopleServiceDependency = Annotated[PeopleService, Depends(get_people_service)]
|
|
|
|
|
|
@router.get("/document-types", response_model=list[DocumentTypeRead])
|
|
async def list_document_types(
|
|
service: DocumentServiceDependency,
|
|
active_only: bool = True,
|
|
) -> list[DocumentTypeRead]:
|
|
items = await service.list_document_types(active_only=active_only)
|
|
return [_document_type_to_read(item) for item in items]
|
|
|
|
|
|
@router.get("/person-roles", response_model=list[PersonRoleRead])
|
|
async def list_person_roles(
|
|
service: PeopleServiceDependency,
|
|
active_only: bool = True,
|
|
) -> list[PersonRoleRead]:
|
|
items = await service.list_person_roles(active_only=active_only)
|
|
return [_person_role_to_read(item) for item in items]
|
|
|
|
|
|
@router.put("/documents/{document_id}/type", response_model=DocumentTypeWriteResponse)
|
|
async def set_document_type(
|
|
document_id: UUID,
|
|
payload: DocumentTypeWriteRequest,
|
|
service: DocumentServiceDependency,
|
|
) -> DocumentTypeWriteResponse:
|
|
document = await service.set_document_type(
|
|
document_id=document_id,
|
|
document_type_id=payload.document_type_id,
|
|
)
|
|
return _document_to_type_response(document)
|
|
|
|
|
|
@router.get("/documents/{document_id}/people", response_model=DocumentPeopleResponse)
|
|
async def list_document_people(
|
|
document_id: UUID,
|
|
service: PeopleServiceDependency,
|
|
) -> DocumentPeopleResponse:
|
|
links = await service.list_document_people(document_id=document_id)
|
|
return DocumentPeopleResponse(document_id=document_id, links=[_document_person_to_read(item) for item in links])
|
|
|
|
|
|
@router.post("/documents/{document_id}/people", response_model=DocumentPersonRead)
|
|
async def add_document_person_link(
|
|
document_id: UUID,
|
|
payload: DocumentPersonWriteRequest,
|
|
service: PeopleServiceDependency,
|
|
) -> DocumentPersonRead:
|
|
link = await service.add_document_person_link(
|
|
document_id=document_id,
|
|
person_id=payload.person_id,
|
|
role_id=payload.role_id,
|
|
)
|
|
return _document_person_to_read(link)
|
|
|
|
|
|
@router.patch("/document-people/{document_person_id}", response_model=DocumentPersonRead)
|
|
async def set_document_person_role(
|
|
document_person_id: UUID,
|
|
payload: DocumentPersonRoleUpdateRequest,
|
|
service: PeopleServiceDependency,
|
|
) -> DocumentPersonRead:
|
|
link = await service.set_document_person_role(
|
|
document_person_id=document_person_id,
|
|
role_id=payload.role_id,
|
|
)
|
|
return _document_person_to_read(link)
|
|
|
|
|
|
@router.delete("/document-people/{document_person_id}", status_code=204)
|
|
async def delete_document_person_link(
|
|
document_person_id: UUID,
|
|
service: PeopleServiceDependency,
|
|
) -> Response:
|
|
await service.remove_document_person_link(document_person_id=document_person_id)
|
|
return Response(status_code=204)
|