"""Minutes domain API and page controllers."""
from __future__ import annotations
import datetime
from typing import Annotated
from uuid import UUID
from advanced_alchemy.filters import LimitOffset
from litestar import Controller, delete, get, post, put
from litestar.exceptions import NotFoundException
from litestar.params import Body, Parameter
from litestar.response import Template
from pydotorg.domains.minutes.schemas import (
MinutesCreate,
MinutesList,
MinutesRead,
MinutesUpdate,
)
from pydotorg.domains.minutes.services import MinutesService
[docs]
class MinutesController(Controller):
"""Controller for Minutes CRUD operations."""
path = "/api/v1/minutes"
tags = ["Minutes"]
@get("/")
async def list_minutes(
self,
minutes_service: MinutesService,
limit_offset: LimitOffset,
) -> list[MinutesList]:
"""List all minutes with pagination."""
minutes, _total = await minutes_service.list_and_count(limit_offset)
return [MinutesList.model_validate(minute) for minute in minutes]
@get("/{minutes_id:uuid}")
async def get_minutes(
self,
minutes_service: MinutesService,
minutes_id: Annotated[UUID, Parameter(title="Minutes ID", description="The minutes ID")],
) -> MinutesRead:
"""Get minutes by ID."""
minutes = await minutes_service.get(minutes_id)
return MinutesRead.model_validate(minutes)
@get("/slug/{slug:str}")
async def get_minutes_by_slug(
self,
minutes_service: MinutesService,
slug: Annotated[str, Parameter(title="Slug", description="The minutes slug")],
) -> MinutesRead:
"""Get minutes by slug."""
minutes = await minutes_service.get_by_slug(slug)
if not minutes:
raise NotFoundException(f"Minutes with slug {slug} not found")
return MinutesRead.model_validate(minutes)
@get("/published")
async def list_published_minutes(
self,
minutes_service: MinutesService,
limit: Annotated[int, Parameter(ge=1, le=1000)] = 100,
offset: Annotated[int, Parameter(ge=0)] = 0,
) -> list[MinutesList]:
"""List published minutes."""
minutes = await minutes_service.get_published_minutes(limit=limit, offset=offset)
return [MinutesList.model_validate(minute) for minute in minutes]
@get("/date/{date:str}")
async def get_minutes_by_date(
self,
minutes_service: MinutesService,
date: Annotated[datetime.date, Parameter(title="Date", description="The meeting date (YYYY-MM-DD)")],
) -> MinutesRead:
"""Get minutes by date."""
minutes = await minutes_service.get_by_date(date)
if not minutes:
raise NotFoundException(f"Minutes for date {date} not found")
return MinutesRead.model_validate(minutes)
@post("/")
async def create_minutes(
self,
minutes_service: MinutesService,
data: Annotated[MinutesCreate, Body(title="Minutes", description="Meeting minutes to create")],
) -> MinutesRead:
"""Create new minutes."""
minutes = await minutes_service.create(data.model_dump())
return MinutesRead.model_validate(minutes)
@put("/{minutes_id:uuid}")
async def update_minutes(
self,
minutes_service: MinutesService,
data: Annotated[MinutesUpdate, Body(title="Minutes", description="Meeting minutes data to update")],
minutes_id: Annotated[UUID, Parameter(title="Minutes ID", description="The minutes ID")],
) -> MinutesRead:
"""Update minutes."""
update_data = data.model_dump(exclude_unset=True)
minutes = await minutes_service.update(minutes_id, update_data)
return MinutesRead.model_validate(minutes)
@delete("/{minutes_id:uuid}")
async def delete_minutes(
self,
minutes_service: MinutesService,
minutes_id: Annotated[UUID, Parameter(title="Minutes ID", description="The minutes ID")],
) -> None:
"""Delete minutes."""
await minutes_service.delete(minutes_id)
[docs]
class MinutesPageController(Controller):
"""Controller for minutes HTML pages."""
path = "/psf/records/board/minutes"
include_in_schema = False
@get("/")
async def minutes_index(
self,
minutes_service: MinutesService,
) -> Template:
"""Render the PSF board minutes index page."""
minutes_list = await minutes_service.get_published_minutes(limit=100)
return Template(
template_name="minutes/index.html.jinja2",
context={
"minutes_list": minutes_list,
"page_title": "PSF Board Meeting Minutes",
},
)
@get("/{slug:str}/")
async def minutes_detail(
self,
minutes_service: MinutesService,
slug: str,
) -> Template:
"""Render the minutes detail page."""
minutes = await minutes_service.get_by_slug(slug)
if not minutes:
raise NotFoundException(f"Minutes with slug {slug} not found")
return Template(
template_name="minutes/detail.html.jinja2",
context={
"minutes": minutes,
"page_title": f"Minutes - {minutes.date}",
},
)