f1be24ab83
- Add FastAPI app with paper browsing UI and REST API - Add crawler service and database models - Add scripts for DB init and manual crawl - Add docs (api-and-ui, data-model, services) - Add requirements and project config
110 lines
3.1 KiB
Python
110 lines
3.1 KiB
Python
"""页面路由 — 首页、日期页、论文详情。"""
|
|
|
|
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,
|
|
},
|
|
)
|