helpers.py 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. # coding=utf-8
  2. """
  3. 报告辅助函数模块
  4. 提供报告生成相关的通用辅助函数
  5. """
  6. import re
  7. from typing import List
  8. def clean_title(title: str) -> str:
  9. """清理标题中的特殊字符
  10. 清理规则:
  11. - 将换行符(\n, \r)替换为空格
  12. - 将多个连续空白字符合并为单个空格
  13. - 去除首尾空白
  14. Args:
  15. title: 原始标题字符串
  16. Returns:
  17. 清理后的标题字符串
  18. """
  19. if not isinstance(title, str):
  20. title = str(title)
  21. cleaned_title = title.replace("\n", " ").replace("\r", " ")
  22. cleaned_title = re.sub(r"\s+", " ", cleaned_title)
  23. cleaned_title = cleaned_title.strip()
  24. return cleaned_title
  25. def html_escape(text: str) -> str:
  26. """HTML特殊字符转义
  27. 转义规则(按顺序):
  28. - & → &
  29. - < → &lt;
  30. - > → &gt;
  31. - " → &quot;
  32. - ' → &#x27;
  33. Args:
  34. text: 原始文本
  35. Returns:
  36. 转义后的文本
  37. """
  38. if not isinstance(text, str):
  39. text = str(text)
  40. return (
  41. text.replace("&", "&amp;")
  42. .replace("<", "&lt;")
  43. .replace(">", "&gt;")
  44. .replace('"', "&quot;")
  45. .replace("'", "&#x27;")
  46. )
  47. def format_rank_display(ranks: List[int], rank_threshold: int, format_type: str) -> str:
  48. """格式化排名显示
  49. 根据不同平台类型生成对应格式的排名字符串。
  50. 当最小排名小于等于阈值时,使用高亮格式。
  51. Args:
  52. ranks: 排名列表(可能包含重复值)
  53. rank_threshold: 高亮阈值,小于等于此值的排名会高亮显示
  54. format_type: 平台类型,支持:
  55. - "html": HTML格式
  56. - "feishu": 飞书格式
  57. - "dingtalk": 钉钉格式
  58. - "wework": 企业微信格式
  59. - "telegram": Telegram格式
  60. - "slack": Slack格式
  61. - 其他: 默认markdown格式
  62. Returns:
  63. 格式化后的排名字符串,如 "[1]" 或 "[1 - 5]"
  64. 如果排名列表为空,返回空字符串
  65. """
  66. if not ranks:
  67. return ""
  68. unique_ranks = sorted(set(ranks))
  69. min_rank = unique_ranks[0]
  70. max_rank = unique_ranks[-1]
  71. # 根据平台类型选择高亮格式
  72. if format_type == "html":
  73. highlight_start = "<font color='red'><strong>"
  74. highlight_end = "</strong></font>"
  75. elif format_type == "feishu":
  76. highlight_start = "<font color='red'>**"
  77. highlight_end = "**</font>"
  78. elif format_type == "dingtalk":
  79. highlight_start = "**"
  80. highlight_end = "**"
  81. elif format_type == "wework":
  82. highlight_start = "**"
  83. highlight_end = "**"
  84. elif format_type == "telegram":
  85. highlight_start = "<b>"
  86. highlight_end = "</b>"
  87. elif format_type == "slack":
  88. highlight_start = "*"
  89. highlight_end = "*"
  90. else:
  91. # 默认 markdown 格式
  92. highlight_start = "**"
  93. highlight_end = "**"
  94. # 生成排名显示
  95. rank_str = ""
  96. if min_rank <= rank_threshold:
  97. if min_rank == max_rank:
  98. rank_str = f"{highlight_start}[{min_rank}]{highlight_end}"
  99. else:
  100. rank_str = f"{highlight_start}[{min_rank} - {max_rank}]{highlight_end}"
  101. else:
  102. if min_rank == max_rank:
  103. rank_str = f"[{min_rank}]"
  104. else:
  105. rank_str = f"[{min_rank} - {max_rank}]"
  106. # 计算热度趋势
  107. trend_arrow = ""
  108. if len(ranks) >= 2:
  109. prev_rank = ranks[-2]
  110. curr_rank = ranks[-1]
  111. if curr_rank < prev_rank:
  112. trend_arrow = "🔺" # 排名上升(数值变小)
  113. elif curr_rank > prev_rank:
  114. trend_arrow = "🔻" # 排名下降(数值变大)
  115. else:
  116. trend_arrow = "➖" # 排名持平
  117. # len(ranks) == 1 时不显示趋势箭头(新上榜由 is_new 字段在 formatter.py 中处理)
  118. return f"{rank_str} {trend_arrow}" if trend_arrow else rank_str