fix(system): three next-runs, completed-date data_date, running mark
Split /system schedule into fans / snapshot+UP / discovery next-runs. Drive data_date and snapshot_days from fully completed video snapshots only, keep prior success date on failure, show task failure records, and mark in-progress tasks as 运行中 without wall-clock next-run jumps.
This commit is contained in:
1 parent
0b92e10317
commit
af90ce8f87
9 files changed
+411
-52
No files matched your search
@@ -227,15 +227,20 @@ class DashboardQueries:
|
||||
|
||||
@staticmethod
|
||||
def _latest_date(conn: sqlite3.Connection) -> str | None:
|
||||
"""数据日期:仅取视频快照任务完整成功后的完成日,避免采集中途抢跑。"""
|
||||
row = conn.execute(
|
||||
"SELECT MAX(stat_date) AS d FROM snapshot WHERE ok = 1"
|
||||
"SELECT v FROM kv WHERE k = 'snapshot:last_completed'"
|
||||
).fetchone()
|
||||
return None if row is None or row["d"] is None else str(row["d"])
|
||||
if row is not None and row["v"]:
|
||||
return str(row["v"])
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _snapshot_days(conn: sqlite3.Connection) -> int:
|
||||
"""快照天数:统计已完整完成的采集日(completed=1),与数据日期口径一致。"""
|
||||
row = conn.execute(
|
||||
"SELECT COUNT(DISTINCT stat_date) AS n FROM snapshot WHERE ok = 1"
|
||||
"SELECT COUNT(*) AS n FROM kv "
|
||||
"WHERE k LIKE 'snapshot:completed:%' AND v = '1'"
|
||||
).fetchone()
|
||||
return int(row["n"] or 0)
|
||||
|
||||
@@ -580,6 +585,23 @@ class DashboardQueries:
|
||||
result[name] = value
|
||||
return result
|
||||
|
||||
def _task_running(self, conn: sqlite3.Connection) -> dict[str, Any]:
|
||||
rows = conn.execute(
|
||||
"SELECT k, v FROM kv WHERE k LIKE 'task:running:%' ORDER BY k"
|
||||
).fetchall()
|
||||
result: dict[str, Any] = {}
|
||||
for row in rows:
|
||||
name = str(row["k"]).removeprefix("task:running:")
|
||||
try:
|
||||
value = json.loads(str(row["v"]))
|
||||
except (TypeError, json.JSONDecodeError):
|
||||
value = {"raw": row["v"]}
|
||||
if not isinstance(value, dict):
|
||||
value = {"raw": value}
|
||||
value["running"] = True
|
||||
result[name] = value
|
||||
return result
|
||||
|
||||
def _folders_overview(self, conn: sqlite3.Connection) -> list[dict[str, Any]]:
|
||||
rows = conn.execute(
|
||||
"""
|
||||
@@ -1722,21 +1744,30 @@ class DashboardQueries:
|
||||
).fetchall()
|
||||
]
|
||||
tasks = self._task_records(conn)
|
||||
running = self._task_running(conn)
|
||||
now = self._now()
|
||||
discover_interval = int(cfg.schedule.discover_minutes) * 60
|
||||
next_runs = {
|
||||
"discover": int(now + cfg.schedule.discover_minutes * 60),
|
||||
"fans": int(next_daily_run(now, cfg.schedule.snapshot_at)),
|
||||
"snapshot": int(next_daily_run(now, cfg.schedule.snapshot_at)),
|
||||
"discover": int(now + discover_interval),
|
||||
}
|
||||
for name, key, interval_minutes in (
|
||||
("discover", "discovery", cfg.schedule.discover_minutes),
|
||||
for name, key in (
|
||||
("fans", "fans"),
|
||||
("snapshot", "snapshot"),
|
||||
("discover", "discovery"),
|
||||
):
|
||||
record = tasks.get(key)
|
||||
if isinstance(record, dict) and record.get("finished_at"):
|
||||
interval = int(interval_minutes) * 60
|
||||
candidate = int(record["finished_at"]) + interval
|
||||
while candidate <= now:
|
||||
candidate += interval
|
||||
next_runs[name] = candidate
|
||||
if not (isinstance(record, dict) and record.get("finished_at")):
|
||||
continue
|
||||
finished_at = int(record["finished_at"])
|
||||
if name == "discover":
|
||||
# 只按上次完成推下一档;进行中由 running 展示,不按墙钟抢跑。
|
||||
next_runs[name] = finished_at + discover_interval
|
||||
else:
|
||||
next_runs[name] = int(
|
||||
next_daily_run(finished_at, cfg.schedule.snapshot_at)
|
||||
)
|
||||
errors = [
|
||||
{"task": name, **record}
|
||||
for name, record in tasks.items()
|
||||
@@ -1754,6 +1785,7 @@ class DashboardQueries:
|
||||
"counts": counts,
|
||||
"blacklist": blacklist,
|
||||
"tasks": tasks,
|
||||
"running": running,
|
||||
"errors": errors,
|
||||
"next_runs": next_runs,
|
||||
"auth": {
|
||||
|
||||
Reference in new issue
Block a user