0d293422ac
- Replace image_extractor with pdf_image_extractor service - Enhance pi_client with expanded API capabilities - Improve summarizer service with additional features - Update admin routes with more endpoints - Add login page template - Enhance detail page with comprehensive layout - Improve search and trends pages - Update base template with additional elements - Refactor tests for better coverage - Add validate_summary script - Update project configuration and dependencies
86 lines
3.0 KiB
Python
86 lines
3.0 KiB
Python
"""公共工具 — 消除各模块间的重复代码。"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from datetime import datetime, timezone
|
||
from pathlib import Path
|
||
from zoneinfo import ZoneInfo
|
||
|
||
import httpx
|
||
from fastapi.templating import Jinja2Templates
|
||
|
||
from app.config import settings
|
||
|
||
# ── 路径常量 ──────────────────────────────────────────────────────────
|
||
|
||
DATA_DIR = Path("data")
|
||
PAPERS_DIR = DATA_DIR / "papers"
|
||
TMP_DIR = DATA_DIR / "tmp"
|
||
|
||
# ── 模板单例 ──────────────────────────────────────────────────────────
|
||
|
||
|
||
class _Templates(Jinja2Templates):
|
||
"""自动注入 is_admin 到模板上下文的 Jinja2Templates 子类。"""
|
||
|
||
def TemplateResponse(self, request, name, context=None, **kwargs):
|
||
context = context or {}
|
||
context.setdefault("is_admin", request.session.get("is_admin", False))
|
||
return super().TemplateResponse(request, name, context, **kwargs)
|
||
|
||
|
||
templates = _Templates(directory="app/templates")
|
||
|
||
|
||
# ── 时区工具 ──────────────────────────────────────────────────────────
|
||
|
||
|
||
def today_str() -> str:
|
||
"""当前日期字符串(按 APP_TIMEZONE)。"""
|
||
tz = ZoneInfo(settings.APP_TIMEZONE)
|
||
return datetime.now(tz).strftime("%Y-%m-%d")
|
||
|
||
|
||
# ── 锁释放 ────────────────────────────────────────────────────────────
|
||
|
||
|
||
def release_lock(db, lock) -> None:
|
||
"""释放 TaskLock。"""
|
||
try:
|
||
lock.status = "finished"
|
||
lock.released_at = datetime.now(timezone.utc)
|
||
db.commit()
|
||
except Exception:
|
||
db.rollback()
|
||
|
||
|
||
# ── HTTP 客户端工厂 ───────────────────────────────────────────────────
|
||
|
||
|
||
def make_http_client(
|
||
*, sync: bool = False, follow_redirects: bool = False, **kwargs
|
||
) -> httpx.AsyncClient | httpx.Client:
|
||
"""创建带 proxy 和默认配置的 httpx 客户端。
|
||
|
||
Args:
|
||
sync: True 返回同步 Client,False 返回 AsyncClient
|
||
follow_redirects: 是否跟随重定向
|
||
**kwargs: 覆盖默认参数
|
||
"""
|
||
defaults: dict = {
|
||
"timeout": settings.HTTP_TIMEOUT_SECONDS,
|
||
"headers": {"User-Agent": settings.HTTP_USER_AGENT},
|
||
"follow_redirects": follow_redirects,
|
||
}
|
||
if settings.http_proxy:
|
||
defaults["transport"] = (
|
||
httpx.HTTPTransport(proxy=settings.http_proxy)
|
||
if sync
|
||
else httpx.AsyncHTTPTransport(proxy=settings.http_proxy)
|
||
)
|
||
defaults.update(kwargs)
|
||
|
||
if sync:
|
||
return httpx.Client(**defaults)
|
||
return httpx.AsyncClient(**defaults)
|