The retry badge stops offering jobs the human retired
Marking the D&D blue box "local" removed its job from the queue — but the badge still counted its old failure and the checkbox still offered to retry it, because the count read only upload_log.csv. A failure is retryable only if the job is still queued AND still endorsed by matches.csv; on Eric's data that's the difference between 2 and 1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jXZFSTZQKzAC8fqpWSz9g
This commit is contained in:
co-authored by
Claude Fable 5
parent
99c1fbf02d
commit
74ecd5059b
+22
-7
@@ -117,14 +117,29 @@ def stale_jobs(queue_rows: list[dict], match_rows: list[dict]) -> dict[str, str]
|
||||
return stale
|
||||
|
||||
|
||||
def outstanding_failures(log_rows: list[dict]) -> int:
|
||||
"""Jobs whose LATEST attempt failed. upload_log.csv is an append-only
|
||||
audit trail, so counting every 'failed' row ever written would keep
|
||||
growing after a successful retry."""
|
||||
last: dict[tuple[str, str, str], str] = {}
|
||||
def outstanding_failures(
|
||||
log_rows: list[dict],
|
||||
queue_rows: list[dict] | None = None,
|
||||
match_rows: list[dict] | None = None,
|
||||
) -> int:
|
||||
"""Failures still worth retrying: the job's LATEST attempt failed AND
|
||||
it is still queued and still endorsed by matches.csv.
|
||||
|
||||
Two ways to over-count. upload_log.csv is append-only, so a successful
|
||||
retry leaves its old 'failed' line behind; and a failed job whose
|
||||
review decision has since changed (marked local, rejected) is not
|
||||
pending work at all — it will never run again, so offering to retry it
|
||||
is a lie."""
|
||||
last: dict[tuple[str, str, str], tuple[str, str]] = {}
|
||||
for row in log_rows:
|
||||
last[_job_key(row)] = row["status"]
|
||||
return sum(1 for status in last.values() if status == "failed")
|
||||
last[_job_key(row)] = (row["status"], row.get("bgg_id", ""))
|
||||
failed = [bgg_id for status, bgg_id in last.values() if status == "failed"]
|
||||
if queue_rows is None:
|
||||
return len(failed)
|
||||
live = {r.get("bgg_id", "") for r in queue_rows} - set(
|
||||
stale_jobs(queue_rows, match_rows or [])
|
||||
)
|
||||
return sum(1 for bgg_id in failed if bgg_id in live)
|
||||
|
||||
|
||||
def _read_csv(path: Path) -> list[dict]:
|
||||
|
||||
@@ -793,7 +793,18 @@ def create_app(
|
||||
with log_path.open(newline="") as f:
|
||||
log_rows = list(csv.DictReader(f))
|
||||
log_counts = Counter(row["status"] for row in log_rows)
|
||||
failed_now = outstanding_failures(log_rows)
|
||||
|
||||
def _csv_rows(path: Path) -> list[dict]:
|
||||
if not path.exists():
|
||||
return []
|
||||
with path.open(newline="") as handle:
|
||||
return list(csv.DictReader(handle))
|
||||
|
||||
failed_now = outstanding_failures(
|
||||
log_rows,
|
||||
_csv_rows(cfg.to_add_path) + _csv_rows(cfg.to_update_path),
|
||||
session.rows,
|
||||
)
|
||||
games = len(read_games())
|
||||
return {
|
||||
"boot": boot,
|
||||
|
||||
Reference in New Issue
Block a user