"""页面路由 — 首页、日期页、论文详情。""" from datetime import date, datetime, timedelta from zoneinfo import ZoneInfo from fastapi import APIRouter, Depends, HTTPException, Request from fastapi.responses import RedirectResponse from fastapi.templating import Jinja2Templates from sqlalchemy.orm import Session, joinedload from app.config import settings from app.database import get_db from app.models import Paper router = APIRouter() templates = Jinja2Templates(directory="app/templates") def _today() -> str: tz = ZoneInfo(settings.APP_TIMEZONE) return datetime.now(tz).strftime("%Y-%m-%d") @router.get("/") def index(request: Request): """重定向到 /day/{today}。""" return RedirectResponse(url=f"/day/{_today()}") @router.get("/day/{date_str}") def day_page(date_str: str, request: Request, db: Session = Depends(get_db)): """指定日期论文列表。""" try: target = date.fromisoformat(date_str) except ValueError: raise HTTPException(status_code=404, detail="Invalid date format") prev_day = (target - timedelta(days=1)).isoformat() next_day = (target + timedelta(days=1)).isoformat() today_str = _today() papers = ( db.query(Paper) .filter(Paper.paper_date == date_str) .options( joinedload(Paper.authors), joinedload(Paper.tags), joinedload(Paper.summary_status), joinedload(Paper.bookmark), ) .order_by(Paper.upvotes.desc()) .all() ) dates_raw = ( db.query(Paper.paper_date) .distinct() .order_by(Paper.paper_date.desc()) .limit(30) .all() ) available_dates = [d[0].isoformat() if isinstance(d[0], date) else str(d[0]) for d in dates_raw] return templates.TemplateResponse( request, "index.html", { "papers": papers, "current_date": date_str, "prev_day": prev_day, "next_day": next_day, "today": today_str, "available_dates": available_dates, "page_title": f"{date_str} 论文列表", }, ) @router.get("/paper/{arxiv_id}") def paper_detail(arxiv_id: str, request: Request, db: Session = Depends(get_db)): """论文详情页。""" paper = ( db.query(Paper) .filter(Paper.arxiv_id == arxiv_id) .options( joinedload(Paper.authors), joinedload(Paper.tags), joinedload(Paper.summary), joinedload(Paper.summary_status), joinedload(Paper.bookmark), joinedload(Paper.reading_status), joinedload(Paper.note), ) .first() ) if not paper: raise HTTPException(status_code=404, detail="Paper not found") summary_state = "none" if paper.summary_status: summary_state = paper.summary_status.status return templates.TemplateResponse( request, "detail.html", { "paper": paper, "summary_state": summary_state, "page_title": paper.title_zh or paper.title_en, }, )