114 lines
3.6 KiB
Python
114 lines
3.6 KiB
Python
from fastapi import APIRouter, Depends, Request, Query
|
|
from fastapi.responses import HTMLResponse
|
|
from fastapi.templating import Jinja2Templates
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from sqlalchemy.orm import selectinload
|
|
|
|
from ..database import get_db
|
|
from ..models import Set, Card
|
|
|
|
router = APIRouter()
|
|
templates = Jinja2Templates(directory="app/templates")
|
|
|
|
|
|
@router.get("/", response_class=HTMLResponse)
|
|
async def index(request: Request, db: AsyncSession = Depends(get_db)):
|
|
result = await db.execute(select(Set).order_by(Set.code))
|
|
sets = result.scalars().all()
|
|
return templates.TemplateResponse("index.html", {"request": request, "sets": sets})
|
|
|
|
|
|
@router.get("/sets", response_class=HTMLResponse)
|
|
async def list_sets(request: Request, db: AsyncSession = Depends(get_db)):
|
|
result = await db.execute(select(Set).order_by(Set.code))
|
|
sets = result.scalars().all()
|
|
return templates.TemplateResponse("sets.html", {"request": request, "sets": sets})
|
|
|
|
|
|
@router.get("/sets/{set_code}", response_class=HTMLResponse)
|
|
async def set_detail(request: Request, set_code: str, db: AsyncSession = Depends(get_db)):
|
|
result = await db.execute(
|
|
select(Set).where(Set.code == set_code.upper()).options(selectinload(Set.cards))
|
|
)
|
|
s = result.scalar_one_or_none()
|
|
if not s:
|
|
return templates.TemplateResponse("404.html", {"request": request}, status_code=404)
|
|
return templates.TemplateResponse("set_detail.html", {"request": request, "set": s})
|
|
|
|
|
|
@router.get("/search", response_class=HTMLResponse)
|
|
async def search_page(request: Request):
|
|
return templates.TemplateResponse("search.html", {"request": request})
|
|
|
|
|
|
@router.get("/api/search")
|
|
async def search_cards(
|
|
q: str = Query(""),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""Search cards by full code (e.g. CRI-068) or by name."""
|
|
q = q.strip()
|
|
|
|
# Try full-code search: CODE-NUMBER
|
|
if "-" in q:
|
|
parts = q.split("-", 1)
|
|
code_part = parts[0].strip().upper()
|
|
num_part = parts[1].strip().lstrip("0") # remove leading zeros
|
|
stmt = (
|
|
select(Card)
|
|
.join(Card.set)
|
|
.where(Set.code == code_part, Card.number == num_part)
|
|
.options(selectinload(Card.set))
|
|
)
|
|
else:
|
|
# Search by name
|
|
stmt = (
|
|
select(Card)
|
|
.join(Card.set)
|
|
.where(Card.name.ilike(f"%{q}%"))
|
|
.options(selectinload(Card.set))
|
|
.limit(50)
|
|
)
|
|
|
|
result = await db.execute(stmt)
|
|
cards = result.scalars().all()
|
|
|
|
return [
|
|
{
|
|
"id": c.id,
|
|
"full_code": c.full_code,
|
|
"name": c.name,
|
|
"type": c.type or "",
|
|
"rarity": c.rarity or "",
|
|
"set_name": c.set.name,
|
|
}
|
|
for c in cards
|
|
]
|
|
|
|
|
|
@router.get("/api/sets")
|
|
async def api_list_sets(db: AsyncSession = Depends(get_db)):
|
|
result = await db.execute(select(Set).order_by(Set.code))
|
|
sets = result.scalars().all()
|
|
return [
|
|
{"code": s.code, "name": s.name, "release_date": s.release_date or "",
|
|
"card_count": len(s.cards)}
|
|
for s in sets
|
|
]
|
|
|
|
|
|
@router.get("/api/sets/{set_code}/cards")
|
|
async def api_set_cards(set_code: str, db: AsyncSession = Depends(get_db)):
|
|
result = await db.execute(
|
|
select(Set).where(Set.code == set_code.upper()).options(selectinload(Set.cards))
|
|
)
|
|
s = result.scalar_one_or_none()
|
|
if not s:
|
|
return []
|
|
return [
|
|
{"id": c.id, "number": c.number, "name": c.name, "type": c.type or "",
|
|
"rarity": c.rarity or "", "hp": c.hp, "full_code": c.full_code}
|
|
for c in s.cards
|
|
]
|