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) @router.get("/search-by-character", response_model=schemas.anime.AnimeSearchResults) def search_anime_by_character( *, db: Session = Depends(deps.get_db), character_name: Optional[str] = None, character_role: Optional[str] = None, character_gender: Optional[str] = None, include_anime_filters: bool = False, 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 based on character attributes. - **character_name**: Filter anime by character name (partial match) - **character_role**: Filter anime by character role (e.g., "Main", "Supporting") - **character_gender**: Filter anime by character gender - **include_anime_filters**: Include anime filters below in the search Optional anime filters (if include_anime_filters is True): - **title**: Filter by anime 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) Pagination parameters: - **skip**: Number of items to skip for pagination - **limit**: Maximum number of items to return """ anime = crud.anime.search_by_character( db, character_name=character_name, character_role=character_role, character_gender=character_gender, include_anime_filters=include_anime_filters, 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_by_character_count( db, character_name=character_name, character_role=character_role, character_gender=character_gender, include_anime_filters=include_anime_filters, 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 }