paper_paths.py 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. """论文文件路径管理 —— PDF 存储路径生成与文件名规范化."""
  2. import hashlib
  3. import re
  4. LIBRARY_PDF_ROOT_DIR = "文献库"
  5. _UNCATEGORIZED_ALIASES = frozenset(
  6. {
  7. "无分类",
  8. "无分類",
  9. "无类目",
  10. "未归类",
  11. "不分類",
  12. "uncategorized",
  13. "uncategorised",
  14. "none",
  15. "n/a",
  16. "na",
  17. "null",
  18. "-",
  19. "—",
  20. }
  21. )
  22. def _segment_is_uncategorized_alias(seg: str) -> bool:
  23. t = (seg or "").strip()
  24. if not t:
  25. return True
  26. if t in _UNCATEGORIZED_ALIASES:
  27. return True
  28. tl = t.lower()
  29. return tl in _UNCATEGORIZED_ALIASES or tl in ("no category", "no cat", "not categorized")
  30. def normalize_library_category_display(display: str | None) -> str:
  31. raw0 = (display or "").strip()
  32. if not raw0 or _segment_is_uncategorized_alias(raw0):
  33. return "未分类"
  34. parts = [p.strip() for p in raw0.split("/") if p.strip()]
  35. cleaned: list[str] = []
  36. for p in parts[:4]:
  37. s = "".join(ch for ch in p if ch not in '/\\:*?"<>|')
  38. s = s.strip()
  39. if len(s) > 32:
  40. s = s[:32]
  41. if _segment_is_uncategorized_alias(s):
  42. s = "未分类"
  43. if s:
  44. cleaned.append(s)
  45. if not cleaned:
  46. return "未分类"
  47. return "/".join(cleaned)
  48. def _sanitize_filename(title: str | None, max_len: int = 80) -> str:
  49. if not title:
  50. return "untitled"
  51. safe_chars = []
  52. for ch in title.strip():
  53. if ch.isalnum() or ("\u4e00" <= ch <= "\u9fff"):
  54. safe_chars.append(ch)
  55. elif ch in (" ", "-", "_", "."):
  56. safe_chars.append("_")
  57. else:
  58. safe_chars.append("_")
  59. result = "".join(safe_chars)
  60. result = re.sub(r"_+", "_", result).strip("_")
  61. if len(result) > max_len:
  62. result = result[:max_len].rsplit("_", 1)[0]
  63. return result or "untitled"
  64. def category_slug_for_pdf_dir(display: str | None) -> str:
  65. seg = (display or "").strip()
  66. if _segment_is_uncategorized_alias(seg):
  67. seg = "未分类"
  68. t = seg or "未分类"
  69. raw = []
  70. for ch in t:
  71. if ch.isalnum() or ("\u4e00" <= ch <= "\u9fff"):
  72. raw.append(ch)
  73. elif ch in (" ", "-", "_", "."):
  74. raw.append("_")
  75. else:
  76. raw.append("_")
  77. s = "".join(raw)
  78. s = re.sub(r"_+", "_", s).strip("_")[:48]
  79. if len(s) >= 2:
  80. return s
  81. h = hashlib.sha256(t.encode("utf-8")).hexdigest()[:12]
  82. return f"cat_{h}"
  83. def library_pdf_relative_path(
  84. category_display: str | None, paper_id: int, title: str | None = None
  85. ) -> str:
  86. t = (category_display or "").strip() or "未分类"
  87. parts = [p.strip() for p in t.split("/") if p.strip()]
  88. if not parts:
  89. parts = ["未分类"]
  90. segs = [category_slug_for_pdf_dir(p) for p in parts]
  91. title_part = _sanitize_filename(title)
  92. short_id = hashlib.sha256(str(paper_id).encode()).hexdigest()[:6]
  93. filename = f"{title_part}_{short_id}.pdf"
  94. return "/".join([LIBRARY_PDF_ROOT_DIR] + segs + [filename])