reader_opening_cache.py 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
  1. """论文打开缓存 —— 首次打开论文时的预处理结果缓存."""
  2. from __future__ import annotations
  3. import sqlite3
  4. import time
  5. from contextlib import contextmanager
  6. @contextmanager
  7. def _conn(db_path: str):
  8. conn = sqlite3.connect(db_path)
  9. try:
  10. yield conn
  11. conn.commit()
  12. finally:
  13. conn.close()
  14. def _ensure_table(conn: sqlite3.Connection) -> None:
  15. conn.execute(
  16. "CREATE TABLE IF NOT EXISTS paper_opening_cache(paper_id INTEGER PRIMARY KEY,opening TEXT,updated_at INTEGER,hit_count INTEGER DEFAULT 0,miss_count INTEGER DEFAULT 0,last_hit_at INTEGER,last_miss_at INTEGER)"
  17. )
  18. def get_cached_opening(db_path: str, paper_id: int, max_age_hours: int = 72) -> tuple[str | None, bool]:
  19. if not db_path:
  20. return None, False
  21. now = int(time.time())
  22. try:
  23. with _conn(db_path) as conn:
  24. _ensure_table(conn)
  25. row = conn.execute("SELECT opening,updated_at FROM paper_opening_cache WHERE paper_id=?", (int(paper_id),)).fetchone()
  26. if not row:
  27. conn.execute(
  28. "INSERT OR IGNORE INTO paper_opening_cache(paper_id,opening,updated_at,miss_count,last_miss_at) VALUES(?,?,?,?,?)",
  29. (int(paper_id), "", 0, 1, now),
  30. )
  31. return None, False
  32. opening = (row[0] or "").strip()
  33. updated_at = int(row[1] or 0)
  34. fresh = bool(opening and updated_at and (now - updated_at) <= int(max_age_hours) * 3600)
  35. if opening:
  36. conn.execute(
  37. "UPDATE paper_opening_cache SET hit_count=hit_count+1,last_hit_at=? WHERE paper_id=?",
  38. (now, int(paper_id)),
  39. )
  40. else:
  41. conn.execute(
  42. "UPDATE paper_opening_cache SET miss_count=miss_count+1,last_miss_at=? WHERE paper_id=?",
  43. (now, int(paper_id)),
  44. )
  45. return (opening or None), fresh
  46. except Exception:
  47. return None, False
  48. def set_cached_opening(db_path: str, paper_id: int, opening: str) -> None:
  49. if not db_path:
  50. return
  51. now = int(time.time())
  52. try:
  53. with _conn(db_path) as conn:
  54. _ensure_table(conn)
  55. conn.execute(
  56. "INSERT INTO paper_opening_cache(paper_id,opening,updated_at) VALUES(?,?,?) ON CONFLICT(paper_id) DO UPDATE SET opening=excluded.opening,updated_at=excluded.updated_at",
  57. (int(paper_id), str(opening or "").strip(), now),
  58. )
  59. except Exception:
  60. return