Files
re-xxm/tests/test_classify.py
T
kairee 337e379514 feat(classify): super-linear positive weighting by hit count
正向改为按命中次数 n(require_title + reaction + charge + weak)做平方加权,使多信号同现的真 reaction 不被单个负向词打穿(1+1>2、1+1+1>5);标题不含 require_title 直接 0 分。库内 796 条有效视频复测:线以下误伤由 47 降至 14。
2026-11-01 19:08:59 +08:00

136 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""候选分类纯函数测试: uv run python tests/test_classify.py"""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
sys.stdout.reconfigure(encoding="utf-8")
from reaction_board.classify import ( # noqa: E402
clean_title,
contains_any,
date_hits,
matched_words,
normalize_title,
parse_duration,
passes_required_title,
score_item,
)
CLASSIFY = {
"require_title": "咻咻满",
"self_mid": 37754047,
"reaction": ["reaction", "反应"],
"charge": ["加急", "特急", "充电", "专场", "转场"],
"weak_positive": ["双人", "路人", "第一次", "首次"],
"negative": ["歌切", "切片", "纯享", "搬运", "完整版", "录播", "录像"],
"date_negative": True,
"duration_boost": {"gt_30min": 1, "gt_60min": 2},
}
def score(title, up_mid=100, duration=0, classify=CLASSIFY):
return score_item(
title=title, up_mid=up_mid,
duration_seconds=duration, classify=classify,
)
def test_title_helpers():
assert normalize_title("咻咻满 REACTION") == "咻咻满 reaction"
assert clean_title('<em class="keyword">咻咻满</em> reaction') == "咻咻满 reaction"
assert passes_required_title("【咻咻满】初听", "咻咻满")
assert passes_required_title("咻咻满", "咻咻满")
assert not passes_required_title("其他 UP 的 reaction", "咻咻满")
assert contains_any("咻咻满 REACTION", ["reaction"])
assert not contains_any("咻咻满", [])
assert matched_words("咻咻满 REACTION 反应", ["reaction", "反应"]) == ["reaction", "反应"]
assert matched_words("咻咻满", []) == []
def test_parse_duration():
assert parse_duration("105:5") == 105 * 60 + 5
assert parse_duration("1:02:03") == 3723
assert parse_duration("15:30") == 930
assert parse_duration(600) == 600
assert parse_duration("bad") == 0
assert parse_duration(None) == 0
assert parse_duration(-5) == 0
def test_date_hits():
assert date_hits("咻咻满-2026-10-28-素颜满") == ["2026-10-28"]
assert date_hits("【咻咻满】10月25日直播片段") == ["10月25日"]
assert date_hits("咻咻满 ☆ 2026.10.26") == ["2026.10.26"]
assert date_hits("【咻咻满】2026/10/25直播回放") == ["2026/10/25"]
assert date_hits("咻咻满:《发如雪》2024年10月5日直播歌切") == ["2024年10月5日"]
assert date_hits("咻咻满 reaction") == []
def test_hit_count_is_superlinear():
# 正向命中次数 n:require_title + reaction + charge + weak,得分 = n^2
# 1 -> 1, 2 -> 4, 3 -> 9(满足 1+1>2、1+1+1>5)
assert score("咻咻满", duration=60) == 1
assert score("咻咻满 reaction", duration=60) == 4
assert score("咻咻满 反应", duration=60) == 4
assert score("咻咻满 reaction 充电", duration=60) == 9
assert score("咻咻满 reaction 加急充电专场", duration=60) == 25
assert score("咻咻满 充电", duration=60) == 4
assert score("咻咻满 加急专场", duration=60) == 9
assert score("咻咻满 双人", duration=60) == 4
assert score("咻咻满 reaction 双人", duration=60) == 9
def test_reaction_requires_title():
# 标题不含 require_title 直接 0 分,即使命中 reaction
assert score("其他 UP 的 reaction", duration=61 * 60) == 0
def test_duration_boost():
assert score("咻咻满 reaction", duration=61 * 60) == 6
assert score("咻咻满 reaction", duration=31 * 60) == 5
assert score("咻咻满 reaction", duration=30 * 60) == 4
# 只有 require_title 一个命中时,时长仍计入(基础分偏低,不足以过线)
assert score("咻咻满", duration=0) == 1
assert score("咻咻满", duration=61 * 60) == 3
assert score("咻咻满 双人", duration=61 * 60) == 6
def test_negative_and_self():
assert score("咻咻满 切片", duration=60) == -5
assert score("咻咻满 reaction 切片", duration=60) == -2
assert score("咻咻满 reaction 直播录像", duration=60) == -2
assert score("咻咻满 reaction", up_mid=37754047, duration=60) == 1
def test_date_negative():
assert score("咻咻满 reaction 2026-10-28 完整录播", duration=60) == -8
assert score("咻咻满 reaction 2026-10-28", duration=60) == -2
assert score("咻咻满 充电 10月25日", duration=60) == -2
disabled = dict(CLASSIFY, date_negative=False)
assert score("咻咻满 reaction 2026-10-28", duration=60, classify=disabled) == 4
def test_multi_hit_survives_single_negative():
# 回归:命中 咻咻满+reaction+充电 的 204min reaction 标题,扣掉「切片」后仍应过线
title = "【充电2.0 | 咻咻满reaction】翻唱和直播切片完全是在开盲盒呀!不知道今天会开到哪一款"
assert score(title, duration=204 * 60) == 5
assert score(title, duration=60) == 3
def main():
test_title_helpers()
test_parse_duration()
test_date_hits()
test_hit_count_is_superlinear()
test_reaction_requires_title()
test_duration_boost()
test_negative_and_self()
test_date_negative()
test_multi_hit_survives_single_negative()
print("OK: 候选标题归一化、时长解析与分类打分测试全部通过")
if __name__ == "__main__":
main()