fix: stop recording how often or how long a session was paused

Pausing is for life happening - a baby needing attention - not a metric worth
keeping, and the numbers were meaningless anyway: the last recorded attempt read
12 pauses / 90,755 seconds, almost all of it sessions left paused overnight.

Removed:
  - the st["pauses"] counter in set_paused()
  - "pauses" and "paused_seconds" from the history entry in record_attempt()
  - "pauses" from the web API session payload
  - the "· paused 3× (12:04)" segment on the results screen

Kept, because both are load-bearing rather than telemetry:
  - paused_total, which remaining() adds back to the deadline so paused time is
    not silently deducted from the clock, and which the web clock reads to do the
    same countdown math
  - paused_at, which is just "is the session paused right now" and drives the ⏸
    indicator

Neither is written to history or surfaced in any report; they exist only for the
lifetime of a live session.

Note for anyone reading a long time_used later: it is computed as
ended_at - started_at - paused_total, and paused_total only accrues while
actually paused, so a session resumed and then left running overnight inflates
it. Attempt #11 logs 28:59:01 for that reason, not a timing bug.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NziZdvBvy7CpEAAbZNgp8i
This commit is contained in:
vumast3r 2026-09-20 17:35:40 -06:00
parent 269f4d4ebb
commit 01877f0ae1
2 changed files with 3 additions and 4 deletions

5
exam
View File

@ -181,7 +181,7 @@ def remaining(st: dict[str, Any] | None) -> float:
def set_paused(st: dict[str, Any], on: bool) -> None:
if on and not st.get("paused_at"):
st["paused_at"] = time.time(); st["pauses"] = int(st.get("pauses", 0)) + 1
st["paused_at"] = time.time()
elif not on and st.get("paused_at"):
st["paused_total"] = float(st.get("paused_total", 0)) + (time.time() - float(st["paused_at"])); st["paused_at"] = None
save_state(st)
@ -297,7 +297,6 @@ def record_attempt(st):
entry = {"at": datetime.now().isoformat(timespec="minutes"), "mode": st.get("mode", "exam"), "hard": st.get("hard", False),
"ids": [q["id"] for q in session_questions(st)], "score_pct": pct, "earned": earned, "total": total,
"time_used": int(st["ended_at"] - st["started_at"] - paused_total(st)),
"pauses": st.get("pauses", 0), "paused_seconds": int(paused_total(st)),
"results": {k: {"pass": v["pass"], "earned": v["earned"], "total": v["total"],
"failed": [ch["name"] for ch in v["checks"] if not ch["pass"]]}
for k, v in st["results"].items()}}
@ -1178,7 +1177,7 @@ class Api(http.server.BaseHTTPRequestHandler):
payload["session"] = {"mode": st.get("mode"), "hard": st.get("hard", False), "started_at": st["started_at"], "duration": st.get("duration", EXAM_SECONDS),
"ends_at": st["started_at"] + st.get("duration", EXAM_SECONDS), "ended_at": st.get("ended_at"),
"over_time": st.get("over_time", False), "flags": st.get("flags", []),
"paused": bool(st.get("paused_at")), "paused_total": paused_total(st), "pauses": st.get("pauses", 0),
"paused": bool(st.get("paused_at")), "paused_total": paused_total(st),
"untimed": untimed(st), "notes": st.get("notes", ""),
"results": st["results"], "score": {"earned": earned, "total": total, "pct": pct, "pass_pct": PASS_PCT},
"domains": self.domain_scores(st), "server_time": time.time()}

View File

@ -422,7 +422,7 @@
function showResults() {
const s = S.session, sc = s.score;
$('#results').dataset.shown = 1;
$('#resSub').textContent = `${s.mode === 'drill' ? 'Drill' : 'Full exam'} · ${fmt(s.ended_at - s.started_at - s.paused_total)} used${s.over_time ? ' (over time)' : ''}${s.pauses ? ` · paused ${s.pauses}× (${fmt(s.paused_total)})` : ''}`;
$('#resSub').textContent = `${s.mode === 'drill' ? 'Drill' : 'Full exam'} · ${fmt(s.ended_at - s.started_at - s.paused_total)} used${s.over_time ? ' (over time)' : ''}`;
$('#resScore').innerHTML = `${sc.pct}% <small>${sc.earned} / ${sc.total} points</small>`;
const v = $('#resVerdict'); v.textContent = sc.pct >= sc.pass_pct ? 'Pass' : 'Fail'; v.className = 'verdict ' + (sc.pct >= sc.pass_pct ? 'pass' : 'fail');
$('#resDomains').innerHTML = Object.entries(s.domains).map(([d, x]) =>