"""Top-level, read-only scope fit guide at ``/scopes``. The site's primary view: a table of scopes with their specs, plus one column per non-Scope part type listing the accessories that fit each scope. Supports HTMX search/filter over scope attributes and required accessory fits. """ from fastapi import APIRouter, Depends, Request from fastapi.responses import HTMLResponse from sqlalchemy.ext.asyncio import AsyncSession from app.core.database import get_db from app.crud import manufacturer as manufacturer_crud from app.crud import part_type as part_type_crud from app.crud import scope_model as scope_model_crud from app.crud import sub_part_type as sub_part_type_crud from app.models.user import User from app.services import scope_guide as scope_guide_service from app.web.admin import web_require from app.web.templates import templates router = APIRouter(include_in_schema=False) def _to_int(value: str) -> int | None: return int(value) if value.strip() else None async def _build(db: AsyncSession, filters: dict) -> dict: """Run the guide query for the given raw (string) filter values.""" return await scope_guide_service.build( db, description=filters["description"], manufacturer_id=_to_int(filters["manufacturer_id"]), scope_model_id=_to_int(filters["scope_model_id"]), sub_part_type_id=_to_int(filters["sub_part_type_id"]), fits_part_type_id=_to_int(filters["fits_part_type_id"]), ) async def _filter_options(db: AsyncSession) -> dict: """Dropdown option lists for the filter bar.""" scope_type = await part_type_crud.get_by_name( db, scope_guide_service.SCOPE_TYPE_NAME ) sub_part_types = [ spt for spt in await sub_part_type_crud.list_all(db, limit=1000) if scope_type is not None and spt.part_type_id == scope_type.id ] return { "manufacturers": await manufacturer_crud.list_all(db, limit=1000), "scope_models": await scope_model_crud.list_all(db, limit=1000), "sub_part_types": sub_part_types, } @router.get("/scopes", response_class=HTMLResponse) async def scopes_page( request: Request, description: str = "", manufacturer_id: str = "", scope_model_id: str = "", sub_part_type_id: str = "", fits_part_type_id: str = "", db: AsyncSession = Depends(get_db), user: User = Depends(web_require("augmented_parts:read", "fits:read")), ): filters = { "description": description, "manufacturer_id": manufacturer_id, "scope_model_id": scope_model_id, "sub_part_type_id": sub_part_type_id, "fits_part_type_id": fits_part_type_id, } guide = await _build(db, filters) return templates.TemplateResponse( request, "scopes.html", {"user": user, "filters": filters, **guide, **(await _filter_options(db))}, ) @router.get("/scopes/filter", response_class=HTMLResponse) async def scopes_filter( request: Request, description: str = "", manufacturer_id: str = "", scope_model_id: str = "", sub_part_type_id: str = "", fits_part_type_id: str = "", db: AsyncSession = Depends(get_db), _: User = Depends(web_require("augmented_parts:read", "fits:read")), ): filters = { "description": description, "manufacturer_id": manufacturer_id, "scope_model_id": scope_model_id, "sub_part_type_id": sub_part_type_id, "fits_part_type_id": fits_part_type_id, } guide = await _build(db, filters) return templates.TemplateResponse(request, "_scopes_table_card.html", guide)