templates: support include_total for search and categories endpoints; update docs; add tests
This commit is contained in:
@@ -11,7 +11,7 @@ Endpoints:
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional, Dict, Any
|
||||
from typing import List, Optional, Dict, Any, Union
|
||||
from fastapi import APIRouter, Depends, HTTPException, status, UploadFile, File, Form, Query
|
||||
from sqlalchemy.orm import Session
|
||||
from sqlalchemy import func, or_, exists
|
||||
@@ -23,6 +23,7 @@ from app.models.user import User
|
||||
from app.models.templates import DocumentTemplate, DocumentTemplateVersion, TemplateKeyword
|
||||
from app.services.storage import get_default_storage
|
||||
from app.services.template_merge import extract_tokens_from_bytes, build_context, resolve_tokens, render_docx
|
||||
from app.services.query_utils import paginate_with_total
|
||||
|
||||
|
||||
router = APIRouter()
|
||||
@@ -84,6 +85,16 @@ class CategoryCount(BaseModel):
|
||||
count: int
|
||||
|
||||
|
||||
class PaginatedSearchResponse(BaseModel):
|
||||
items: List[SearchResponseItem]
|
||||
total: int
|
||||
|
||||
|
||||
class PaginatedCategoriesResponse(BaseModel):
|
||||
items: List[CategoryCount]
|
||||
total: int
|
||||
|
||||
|
||||
@router.post("/upload", response_model=TemplateResponse)
|
||||
async def upload_template(
|
||||
name: str = Form(...),
|
||||
@@ -136,7 +147,7 @@ async def upload_template(
|
||||
)
|
||||
|
||||
|
||||
@router.get("/search", response_model=List[SearchResponseItem])
|
||||
@router.get("/search", response_model=Union[List[SearchResponseItem], PaginatedSearchResponse])
|
||||
async def search_templates(
|
||||
q: Optional[str] = None,
|
||||
category: Optional[List[str]] = Query(
|
||||
@@ -160,6 +171,7 @@ async def search_templates(
|
||||
sort_by: Optional[str] = Query("name", description="Sort by: name | category | updated"),
|
||||
sort_dir: Optional[str] = Query("asc", description="Sort direction: asc or desc"),
|
||||
active_only: bool = Query(True, description="When true (default), only active templates are returned"),
|
||||
include_total: bool = Query(False, description="When true, returns {items, total} instead of a plain list"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
@@ -227,8 +239,8 @@ async def search_templates(
|
||||
else:
|
||||
query = query.order_by(order_col.desc())
|
||||
|
||||
# Pagination
|
||||
templates = query.offset(skip).limit(limit).all()
|
||||
# Pagination with optional total
|
||||
templates, total = paginate_with_total(query, skip, limit, include_total)
|
||||
items: List[SearchResponseItem] = []
|
||||
for tpl in templates:
|
||||
latest_version = None
|
||||
@@ -245,12 +257,15 @@ async def search_templates(
|
||||
latest_version=latest_version,
|
||||
)
|
||||
)
|
||||
if include_total:
|
||||
return {"items": items, "total": int(total or 0)}
|
||||
return items
|
||||
|
||||
|
||||
@router.get("/categories", response_model=List[CategoryCount])
|
||||
@router.get("/categories", response_model=Union[List[CategoryCount], PaginatedCategoriesResponse])
|
||||
async def list_template_categories(
|
||||
active_only: bool = Query(True, description="When true (default), only active templates are counted"),
|
||||
include_total: bool = Query(False, description="When true, returns {items, total} instead of a plain list"),
|
||||
db: Session = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
@@ -258,7 +273,10 @@ async def list_template_categories(
|
||||
if active_only:
|
||||
query = query.filter(DocumentTemplate.active == True)
|
||||
rows = query.group_by(DocumentTemplate.category).order_by(DocumentTemplate.category.asc()).all()
|
||||
return [CategoryCount(category=row[0], count=row[1]) for row in rows]
|
||||
items = [CategoryCount(category=row[0], count=row[1]) for row in rows]
|
||||
if include_total:
|
||||
return {"items": items, "total": len(items)}
|
||||
return items
|
||||
|
||||
|
||||
@router.get("/{template_id}", response_model=TemplateResponse)
|
||||
|
||||
Reference in New Issue
Block a user