feat: EOD 23:15 seal + midnight seed shell for video/fans
Move daily snapshot/fans to 23:15 and add 00:01 seed that clones last_completed ok=1 rows into today without advancing completed. Evening runs force through seed markers; growth previous is per-aid adjacent. Home shows short-update clock + 全站数据 date, and 收录视频 counts.
This commit is contained in:
1 parent
374b138041
commit
8a9cb21408
17 files changed
+520
-39
No files matched your search
@@ -284,6 +284,37 @@ class DashboardQueries:
|
||||
result[int(data["aid"])] = data
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def _snapshot_previous_relative(
|
||||
cls,
|
||||
conn: sqlite3.Connection,
|
||||
current: Mapping[int, Mapping[str, Any]],
|
||||
*,
|
||||
days_back: int,
|
||||
) -> dict[int, dict[str, Any]]:
|
||||
"""相对各片当前快照日取 previous:days_back=1 为相邻成功日,否则 <= current-N。"""
|
||||
if not current or days_back < 1:
|
||||
return {}
|
||||
by_date: dict[str, list[int]] = {}
|
||||
for aid, snap in current.items():
|
||||
day = snap.get("stat_date")
|
||||
if not day:
|
||||
continue
|
||||
by_date.setdefault(str(day), []).append(int(aid))
|
||||
result: dict[int, dict[str, Any]] = {}
|
||||
for day, aids in by_date.items():
|
||||
if days_back <= 1:
|
||||
prev = cls._snapshot_map(
|
||||
conn, aids, before=day, before_inclusive=False,
|
||||
)
|
||||
else:
|
||||
baseline = _date_minus(day, days_back)
|
||||
prev = cls._snapshot_map(
|
||||
conn, aids, before=baseline, before_inclusive=True,
|
||||
)
|
||||
result.update(prev)
|
||||
return result
|
||||
|
||||
@staticmethod
|
||||
def _videos(
|
||||
conn: sqlite3.Connection,
|
||||
@@ -443,7 +474,8 @@ class DashboardQueries:
|
||||
videos = self._videos(conn)
|
||||
aids = [int(video["aid"]) for video in videos]
|
||||
snapshots = self._snapshot_map(conn, aids)
|
||||
previous = self._snapshot_map(conn, aids, before=latest or self._today())
|
||||
# 播放增量:按片相对当前点的相邻成功日,避免 seed/recent 拉到 T-2。
|
||||
previous = self._snapshot_previous_relative(conn, snapshots, days_back=1)
|
||||
totals = {metric: 0 for metric in METRICS}
|
||||
for video in videos:
|
||||
metrics = _metric_map(snapshots.get(int(video["aid"])))
|
||||
@@ -521,6 +553,7 @@ class DashboardQueries:
|
||||
reverse=True,
|
||||
)
|
||||
tasks = self._task_records(conn)
|
||||
updated_at = self._latest_short_update_at(tasks)
|
||||
auth = {
|
||||
"ok": self._kv(conn, "auth:ok"),
|
||||
"message": self._kv(conn, "auth:message"),
|
||||
@@ -535,6 +568,7 @@ class DashboardQueries:
|
||||
"generated_at": int(self._now()),
|
||||
"today": self._today(),
|
||||
"data_date": latest,
|
||||
"updated_at": updated_at,
|
||||
"snapshot_days": snapshot_days,
|
||||
"first_seen_date": first_seen_date,
|
||||
"is_first_day": is_first_day,
|
||||
@@ -542,6 +576,7 @@ class DashboardQueries:
|
||||
"added_today": added_today,
|
||||
"counts": {
|
||||
**by_status,
|
||||
"total": sum(by_status.values()),
|
||||
"active_ups": int(conn.execute(
|
||||
"SELECT COUNT(DISTINCT up_mid) FROM video "
|
||||
"WHERE status IN ('active','unfaved') AND up_mid IS NOT NULL"
|
||||
@@ -571,6 +606,22 @@ class DashboardQueries:
|
||||
row = conn.execute("SELECT v FROM kv WHERE k = ?", (key,)).fetchone()
|
||||
return None if row is None else str(row["v"])
|
||||
|
||||
@staticmethod
|
||||
def _latest_short_update_at(tasks: Mapping[str, Any]) -> int | None:
|
||||
"""首页「更新」时刻:优先短更新任务,其次全日更 / 粉丝。"""
|
||||
for name in ("snapshot_recent", "snapshot", "fans"):
|
||||
raw = tasks.get(name)
|
||||
if not isinstance(raw, Mapping):
|
||||
continue
|
||||
finished = raw.get("finished_at")
|
||||
if finished is None:
|
||||
continue
|
||||
try:
|
||||
return int(finished)
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
return None
|
||||
|
||||
def _task_records(self, conn: sqlite3.Connection) -> dict[str, Any]:
|
||||
rows = conn.execute(
|
||||
"SELECT k, v FROM kv WHERE k LIKE 'task:last:%' ORDER BY k"
|
||||
@@ -802,8 +853,10 @@ class DashboardQueries:
|
||||
|
||||
previous: dict[int, dict[str, Any]] = {}
|
||||
if sort in {"growth", "growth_rate", "interaction"}:
|
||||
baseline_date = _date_minus(latest, growth_days)
|
||||
previous = self._snapshot_map(conn, aids, before=baseline_date, before_inclusive=True)
|
||||
# 相对各片当前快照日回退 growth_days,避免 seed/recent 相对 last_completed 拉长窗口。
|
||||
previous = self._snapshot_previous_relative(
|
||||
conn, snapshots, days_back=growth_days,
|
||||
)
|
||||
elif sort in {"heat", "view", "like", "coin", "favorite", "share", "reply", "danmaku"}:
|
||||
previous = {}
|
||||
|
||||
@@ -1780,14 +1833,16 @@ class DashboardQueries:
|
||||
next_runs = {
|
||||
"fans": int(next_daily_run(now, cfg.schedule.snapshot_at)),
|
||||
"snapshot": int(next_daily_run(now, cfg.schedule.snapshot_at)),
|
||||
"snapshot_seed": int(next_daily_run(now, cfg.schedule.seed_at)),
|
||||
"discover": int(now + discover_interval),
|
||||
"snapshot_recent": int(now + recent_interval),
|
||||
}
|
||||
for name, key in (
|
||||
("fans", "fans"),
|
||||
("snapshot", "snapshot"),
|
||||
("discover", "discovery"),
|
||||
("snapshot_recent", "snapshot_recent"),
|
||||
for name, key, at in (
|
||||
("fans", "fans", cfg.schedule.snapshot_at),
|
||||
("snapshot", "snapshot", cfg.schedule.snapshot_at),
|
||||
("snapshot_seed", "snapshot_seed", cfg.schedule.seed_at),
|
||||
("discover", "discovery", None),
|
||||
("snapshot_recent", "snapshot_recent", None),
|
||||
):
|
||||
record = tasks.get(key)
|
||||
if not (isinstance(record, dict) and record.get("finished_at")):
|
||||
@@ -1801,9 +1856,7 @@ class DashboardQueries:
|
||||
)
|
||||
next_runs[name] = finished_at + interval
|
||||
else:
|
||||
next_runs[name] = int(
|
||||
next_daily_run(finished_at, cfg.schedule.snapshot_at)
|
||||
)
|
||||
next_runs[name] = int(next_daily_run(finished_at, at))
|
||||
errors = [
|
||||
{"task": name, **record}
|
||||
for name, record in tasks.items()
|
||||
@@ -1840,6 +1893,7 @@ class DashboardQueries:
|
||||
"schedule": {
|
||||
"discover_minutes": cfg.schedule.discover_minutes,
|
||||
"snapshot_at": cfg.schedule.snapshot_at,
|
||||
"seed_at": cfg.schedule.seed_at,
|
||||
"candidate_ttl_days": cfg.schedule.candidate_ttl_days,
|
||||
"recent_snapshot_minutes": cfg.schedule.recent_snapshot_minutes,
|
||||
"recent_video_days": cfg.schedule.recent_video_days,
|
||||
|
||||
Reference in new issue
Block a user