feat(growth): same-day 1-day baselines for new videos

Let 1-day view growth use a zero baseline for videos published today, and the first successful sample for older videos ingested today, without mixing those into 7/30-day rankings.
This commit is contained in:
kairee committed 2026-11-02 20:44:17 +08:00
1 parent 8e7ead1669
commit c5b7185675
21 files changed
+411 -59

No files matched your search

+99 -6
View File
@@ -15,7 +15,7 @@ from reaction_board.repository import Repository # noqa: E402
from reaction_board.timeutil import day_bounds # noqa: E402
from reaction_board.web.cache import DASHBOARD_CACHE # noqa: E402
from reaction_board.web.queries import DashboardQueries # noqa: E402
from tests.web_fixtures import DATES, build_db, write_config # noqa: E402
from tests.web_fixtures import DATES, FOLDER, build_db, resource, write_config # noqa: E402
FIXED_NOW = 1_793_459_000 # 2026-10-31 前后
@@ -46,7 +46,7 @@ def test_overview():
assert overview["counts"]["candidates_pending"] == 1
assert overview["totals"]["view"] == 1200 + 5200 + 120
assert overview["interactions"] > 0
assert overview["view_delta"] == (100 + 100 + 0) # 101/102 的日增量
assert overview["view_delta"] == (100 + 100 + 120) # 101/102 跨日 + 103 当日发布
assert overview["snapshot_health"]["total"] == 3
assert overview["folders"][0]["media_count"] == 3
assert overview["folders"][0]["ratio"] < 1
@@ -484,7 +484,7 @@ def test_about_payload():
assert about["snapshot_days"] == 3
assert about["snapshot_first_date"] == DATES[0]
assert about["fans_start_date"] == DATES[0]
assert about["schema_version"] == 7
assert about["schema_version"] == 8
assert about["analytics"] == {
"min_duration_seconds": 600,
"maturity_days": 2,
@@ -577,9 +577,13 @@ def test_growth_auto_falls_back_to_one_day():
assert auto["available"] is True
assert auto["growth_days"] == 1
assert auto["days"] == 1
assert {item["aid"] for item in auto["items"]} == {101, 102}
assert {item["aid"] for item in auto["items"]} == {101, 102, 103}
first = next(item for item in auto["items"] if item["aid"] == 101)
assert first["growth"]["delta"] == 100 # 相邻日
assert first["growth"]["baseline"] == "snapshot"
today = next(item for item in auto["items"] if item["aid"] == 103)
assert today["growth"]["baseline"] == "zero"
assert today["growth"]["delta"] == 120
forced7 = q.list_videos(sort="growth", growth_days=7)
assert forced7["available"] is False
@@ -667,9 +671,10 @@ def test_overview_adjacent_previous_with_seed_and_dual_fields():
+ overview["counts"]["unfaved"]
+ overview["counts"]["removed"]
)
# 当前=seed(=完成日数值),相邻 previous=完成日本身 → 增量 0
# 101/102:当前=seed(=完成日数值),相邻 previous=完成日本身 → 增量 0
# 103:当日发布,近 1 日相对 0,计入当前播放
assert overview["view_delta_available"] is True
assert overview["view_delta"] == 0
assert overview["view_delta"] == 120
growth = q.list_videos(sort="growth", growth_days=1)
assert growth["available"] is True
@@ -678,6 +683,93 @@ def test_overview_adjacent_previous_with_seed_and_dual_fields():
assert item["growth"]["available"] is True
assert item["growth"]["delta"] == 0
assert item["growth"]["gap_days"] == 1
assert item["growth"]["baseline"] == "snapshot"
finally:
db.close()
def _view_metrics(view: int) -> dict[str, int]:
return {
"view": view, "like": 0, "coin": 0, "favorite": 0,
"share": 0, "reply": 0, "danmaku": 0,
}
def test_same_day_growth_baselines():
"""近 1 日:当日发布相对 0;更早发布用 view_open;7 日不用这两条退路。"""
with tempfile.TemporaryDirectory() as tmp:
db, q = setup(tmp)
try:
repo = Repository(db)
today = DATES[-1]
start = int(day_bounds(today)[0])
old_pub = int(day_bounds("2026-09-01")[0])
repo.upsert_snapshot(
today, 103, metrics=_view_metrics(200),
ok=True, err=None, captured_at=start + 3600,
)
repo.upsert_from_fav(
resource(105, up_mid=1, up_name="UP A", pubdate=old_pub),
FOLDER.media_id, was_added=True, now=start + 10,
)
repo.upsert_snapshot(
today, 105, metrics=_view_metrics(500),
ok=True, err=None, captured_at=start + 10,
)
repo.upsert_snapshot(
today, 105, metrics=_view_metrics(800),
ok=True, err=None, captured_at=start + 20,
)
repo.upsert_from_fav(
resource(106, up_mid=1, up_name="UP A", pubdate=old_pub),
FOLDER.media_id, was_added=True, now=start + 30,
)
repo.upsert_snapshot(
today, 106, metrics=_view_metrics(900),
ok=True, err=None, captured_at=start + 30,
)
repo.upsert_from_fav(
resource(107, up_mid=1, up_name="UP A", pubdate=old_pub),
FOLDER.media_id, was_added=True, now=start + 40,
)
repo.upsert_snapshot(
today, 107, metrics=_view_metrics(50),
ok=True, err=None, captured_at=start + 40,
)
db.write(lambda conn: conn.execute(
"UPDATE video SET pubdate = 0 WHERE aid = 107"
))
DASHBOARD_CACHE.clear()
one = q.list_videos(sort="growth", growth_days=1, page_size=20)
by_aid = {item["aid"]: item for item in one["items"]}
assert 103 in by_aid and 105 in by_aid
assert 106 not in by_aid and 107 not in by_aid
assert by_aid[103]["growth"]["baseline"] == "zero"
assert by_aid[103]["growth"]["delta"] == 200
assert by_aid[103]["growth"]["gap_days"] == 0
assert by_aid[105]["growth"]["baseline"] == "open"
assert by_aid[105]["growth"]["delta"] == 300
assert by_aid[101]["growth"]["baseline"] == "snapshot"
assert by_aid[101]["growth"]["delta"] == 100
seven = q.list_videos(sort="growth", growth_days=7)
seven_aids = {item["aid"] for item in seven["items"]}
assert 103 not in seven_aids
assert 105 not in seven_aids
detail103 = q.video_detail(103)
assert detail103["growth"]["baseline"] == "zero"
assert detail103["growth"]["delta"] == 200
detail105 = q.video_detail(105)
assert detail105["growth"]["baseline"] == "open"
assert detail105["growth"]["delta"] == 300
assert q.video_detail(106)["growth"]["available"] is False
assert q.video_detail(107)["growth"]["available"] is False
overview = q.overview()
assert overview["view_delta"] == 100 + 100 + 200 + 300
finally:
db.close()
@@ -699,6 +791,7 @@ def main_test():
test_growth_auto_falls_back_to_one_day()
test_snapshot_recent_next_run_follows_completion()
test_overview_adjacent_previous_with_seed_and_dual_fields()
test_same_day_growth_baselines()
print("OK: Web 查询层总览/榜单/详情/UP/时长效率/粉丝/候选/系统/访问统计测试全部通过")