Commit 049dc070 authored by 郭阔's avatar 郭阔

feat(latest): 最近抓取改为最近24小时每次运行各一张卡片

原先每源只显示最新一场,补跑+定时连跑时前一场被顶下,用户误以为覆盖。
现同源多场并列(按运行时间倒序,上限80张),卡片带运行类型(续采/修复);
文章明细提取拆为 _articles_from_data 纯函数供多场复用。
parent 5af8e7dd
...@@ -1400,12 +1400,9 @@ def dashboard(): ...@@ -1400,12 +1400,9 @@ def dashboard():
fail_top=sorted(fail_top.items(), key=lambda kv: -kv[1])[:6]) fail_top=sorted(fail_top.items(), key=lambda kv: -kv[1])[:6])
def _latest_run_articles(name): def _articles_from_data(data):
"""源最近一次运行的文章明细(标题/链接/状态/失败原因),供最近抓取页与调度中心详情抽屉共用。""" """从一次运行的归一化 JSON 提取文章明细(标题/链接/状态/失败原因)。"""
runs = get_history_runs(name, include_data=True) biz, _legacy = get_business_result(data)
if not runs:
return []
biz, _legacy = get_business_result(runs[0]["data"])
if not biz: if not biz:
return [] return []
return [{ return [{
...@@ -1417,44 +1414,43 @@ def _latest_run_articles(name): ...@@ -1417,44 +1414,43 @@ def _latest_run_articles(name):
} for a in biz.get("articles", [])] } for a in biz.get("articles", [])]
def _latest_run_articles(name):
"""源最近一次运行的文章明细,供调度中心详情弹窗用。"""
runs = get_history_runs(name, include_data=True)
return _articles_from_data(runs[0]["data"]) if runs else []
@app.route("/latest") @app.route("/latest")
@require_auth @require_auth
def latest_content(): def latest_content():
"""最近抓取:每个源最近一次运行抓到的文章标题与状态,快速盘点内容。""" """最近抓取:最近 24 小时内每次运行各一张卡片(同源多场都展示,按时间倒序)。"""
skills = pl.skill_sources() skills = pl.skill_sources()
names = sorted((set(skills) | set(pl.list_schedules())) - {"digest"}) names = sorted((set(skills) | set(pl.list_schedules())) - {"digest"})
names = [n for n in names names = [n for n in names
if skills.get(n, {}).get("enabled", False) if skills.get(n, {}).get("enabled", False)
or pl.schedule_state(n) == "active"] or pl.schedule_state(n) == "active"]
cutoff = datetime.now() - timedelta(hours=24)
cards = [] cards = []
for n in names: for n in names:
runs = get_history_runs(n, include_data=True) display = skills.get(n, {}).get("name") or n
if not runs: for run in get_history_runs(n, include_data=True):
cards.append({"name": n, "display": skills.get(n, {}).get("name") or n, try:
"run_at": None, "articles": [], "succeeded": None, run_dt = datetime.strptime(run["run_at"], "%Y-%m-%d %H:%M:%S")
"failed": None, "exhausted": False}) except ValueError:
continue continue
run = runs[0] if run_dt < cutoff:
biz, _legacy = get_business_result(run["data"]) break # 按时间倒序,再往前的都不在窗口内
articles = [] cards.append({
if biz: "name": n, "display": display,
for a in biz.get("articles", []): "run_at": run["run_at"], "mode": run["mode"],
articles.append({ "articles": _articles_from_data(run["data"]),
"title": a.get("title") or a.get("url", ""), "succeeded": run["succeeded_count"],
"url": a.get("url", ""), "failed": run["failed_count"],
"status": a.get("status", ""), "exhausted": (lambda b: bool(b.get("source_exhausted")) if b else False)(
}) get_business_result(run["data"])[0]),
cards.append({ })
"name": n, cards.sort(key=lambda c: c["run_at"], reverse=True)
"display": skills.get(n, {}).get("name") or n, return render_template("latest.html", cards=cards[:80])
"run_at": run["run_at"],
"articles": articles,
"succeeded": run["succeeded_count"],
"failed": run["failed_count"],
"exhausted": bool(biz.get("source_exhausted")) if biz else False,
})
cards.sort(key=lambda c: c["run_at"] or "", reverse=True)
return render_template("latest.html", cards=cards)
@app.route("/schedules") @app.route("/schedules")
......
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
<h1>最近抓取</h1> <h1>最近抓取</h1>
<p style="color:#666;font-size:13px"> <p style="color:#666;font-size:13px">
每个源最近一次运行抓到的文章标题与状态,按运行时间倒序;点击标题打开原文链接。 最近 24 小时内每次运行各一张卡片(同源多场并列,如补跑+定时各一张),按运行时间倒序;点击标题打开原文链接。
带已取尽标记=该源近期无新文章(正常),空列表=本次未交付(看上次运行详情排查)。 带已取尽标记=该源近期无新文章(正常),空列表=本次未交付(看上次运行详情排查)。
</p> </p>
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
<summary> <summary>
<code>{{ c.name }}</code> <code>{{ c.name }}</code>
{% if c.display and c.display != c.name %}<span style="color:#888">{{ c.display }}</span>{% endif %} {% if c.display and c.display != c.name %}<span style="color:#888">{{ c.display }}</span>{% endif %}
<span class="runinfo">{{ c.run_at or '从未运行' }}</span> <span class="runinfo">{{ c.run_at or '从未运行' }}{% if c.mode and c.mode != '常规采集' %} · {{ c.mode }}{% endif %}</span>
{% if c.succeeded is not none %} {% if c.succeeded is not none %}
<span class="badge ok">✓ {{ c.succeeded }}</span> <span class="badge ok">✓ {{ c.succeeded }}</span>
{% if c.failed %}<span class="badge fail">✗ {{ c.failed }}</span>{% endif %} {% if c.failed %}<span class="badge fail">✗ {{ c.failed }}</span>{% endif %}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment