from typing import Any, Optional from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from app import crud, schemas from app.api import deps router = APIRouter() @router.get("/", response_model=schemas.anime.AnimeSearchResults) def search_anime( *, db: Session = Depends(deps.get_db), title: Optional[str] = None, genre_id: Optional[int] = None, status: Optional[str] = None, year_from: Optional[int] = None, year_to: Optional[int] = None, score_min: Optional[float] = None, score_max: Optional[float] = None, source: Optional[str] = None, studio: Optional[str] = None, sort_by: Optional[str] = None, sort_order: Optional[str] = "asc", skip: int = 0, limit: int = 100, ) -> Any: """ Search for anime with advanced filters. - **title**: Filter by title (partial match) - **genre_id**: Filter by genre ID - **status**: Filter by anime status (airing, finished, upcoming) - **year_from**: Filter by starting year (inclusive) - **year_to**: Filter by ending year (inclusive) - **score_min**: Filter by minimum score (inclusive) - **score_max**: Filter by maximum score (inclusive) - **source**: Filter by source material (manga, light novel, etc.) - **studio**: Filter by studio name (partial match) - **sort_by**: Sort by field (id, title, score, popularity, etc.) - **sort_order**: Sort order (asc, desc) - **skip**: Number of items to skip for pagination - **limit**: Maximum number of items to return """ anime = crud.anime.search( db, title=title, genre_id=genre_id, status=status, year_from=year_from, year_to=year_to, score_min=score_min, score_max=score_max, source=source, studio=studio, sort_by=sort_by, sort_order=sort_order, skip=skip, limit=limit ) total = crud.anime.search_count( db, title=title, genre_id=genre_id, status=status, year_from=year_from, year_to=year_to, score_min=score_min, score_max=score_max, source=source, studio=studio ) # Calculate pagination metadata page = skip // limit + 1 if limit > 0 else 1 total_pages = (total + limit - 1) // limit if limit > 0 else 1 has_next = page < total_pages has_prev = page > 1 return { "results": anime, "total": total, "page": page, "size": limit, "pages": total_pages, "has_next": has_next, "has_prev": has_prev, "next_page": page + 1 if has_next else None, "prev_page": page - 1 if has_prev else None } @router.post("/", response_model=schemas.anime.Anime) def create_anime( *, db: Session = Depends(deps.get_db), anime_in: schemas.anime.AnimeCreate, ) -> Any: """ Create new anime. """ return crud.anime.create_with_genres(db=db, obj_in=anime_in) @router.get("/{id}", response_model=schemas.anime.Anime) def read_anime( *, db: Session = Depends(deps.get_db), id: int, ) -> Any: """ Get anime by ID. """ anime = crud.anime.get(db=db, id=id) if not anime: raise HTTPException(status_code=404, detail="Anime not found") return anime @router.put("/{id}", response_model=schemas.anime.Anime) def update_anime( *, db: Session = Depends(deps.get_db), id: int, anime_in: schemas.anime.AnimeUpdate, ) -> Any: """ Update an anime. """ anime = crud.anime.get(db=db, id=id) if not anime: raise HTTPException(status_code=404, detail="Anime not found") anime = crud.anime.update_with_genres(db=db, db_obj=anime, obj_in=anime_in) return anime @router.delete("/{id}", response_model=None, status_code=204) def delete_anime( *, db: Session = Depends(deps.get_db), id: int, ) -> Any: """ Delete an anime. """ anime = crud.anime.get(db=db, id=id) if not anime: raise HTTPException(status_code=404, detail="Anime not found") crud.anime.remove(db=db, id=id) return None @router.get("/statistics", response_model=schemas.anime.AnimeStatistics) def get_anime_statistics( db: Session = Depends(deps.get_db), ) -> Any: """ Get statistics about the anime collection. Returns various statistical data including: - Total anime count - Average score and episode count - Distribution by status, source, and studio - Yearly distribution of releases - Score distribution - Top genres """ return crud.anime.get_statistics(db=db)