Ctrl K
ComunidadGuías…

scripts/xkb_batch_summary.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env python3
"""從 worker 的輸出讀出「這一次做了什麼」——而且讀不出來時要說。

原本這件事是 shell 裡的一行 sed:

    done_count=$(sed -n 's/.*done=\\([0-9][0-9]*\\).*/\\1/p' "$OUT" | tail -1)

它抓的是「最後一個含 done= 的數字」,而輸出裡有兩行含 done=:

    ✅ queue synced: ... (1591 items, todo=0, done=1575)   ← 佇列的累計總數
    📊 done=0  skipped=0  failed=9  remaining todo=0        ← 這一次的結果

平常第二行在後面,tail -1 剛好對。但佇列沒東西可做時 worker 印的是
「✅ No todo items found」,根本沒有第二行——於是這一次的產出被讀成 1575,
摘要說「產了 1575 張卡」,而且因為 done_count > 0,還會多跑一次向量索引。
2026-09-10 帳本接上去的第一次實跑就撞到這個。

錯的不只是那個正則,是「讀不出來就當 0」這個預設。讀不出結果和產出 0 是
兩件完全不同的事,一個是壞了、一個是沒事做,而 `${done_count:-0}` 讓它們
長得一模一樣——正是這整條管線八天沒被發現的那個毛病。

所以這裡只認 worker 自己的那行摘要(用 `remaining todo=` 認,那是語意不是
裝飾),並且把三種情況分開回報:

    done=N failed=N outcome=ran        讀到了
    done=0 failed=0 outcome=idle       worker 說沒東西可做
    (exit 2)                           有輸出但讀不出結果——呼叫端要當成故障

用法:
    python3 scripts/xkb_batch_summary.py < worker-output.txt
"""
from __future__ import annotations

import re
import sys

# worker 的摘要行。用 `remaining todo=` 當錨點:佇列同步那行也有 done= 與
# todo=,但沒有 remaining。
_SUMMARY = re.compile(r"remaining\s+todo=")
_DONE = re.compile(r"\bdone=(\d+)")
_FAILED = re.compile(r"\bfailed=(\d+)")

# worker 在無事可做時說的話。這是「這次沒事做」,不是「讀不出來」。
_IDLE = re.compile(r"No todo items found|沒有待處理")


def parse(text: str) -> tuple[int, int, str] | None:
    """回 (done, failed, outcome);讀不出結果時回 None。"""
    summary = [line for line in text.splitlines() if _SUMMARY.search(line)]
    if summary:
        line = summary[-1]
        done = _DONE.search(line)
        failed = _FAILED.search(line)
        if done:
            return int(done.group(1)), int(failed.group(1)) if failed else 0, "ran"
        return None
    if _IDLE.search(text):
        return 0, 0, "idle"
    return None


def main() -> int:
    parsed = parse(sys.stdin.read())
    if parsed is None:
        print("outcome=unreadable", file=sys.stderr)
        return 2
    done, failed, outcome = parsed
    print(f"done={done} failed={failed} outcome={outcome}")
    return 0


import xkb_usage  # noqa: E402  — 量測誰在跑,見 scripts/xkb_usage.py

if __name__ == "__main__":
    xkb_usage.record(__file__)
    raise SystemExit(main())