Skip to content
This repository was archived by the owner on May 28, 2026. It is now read-only.

Commit d8675b9

Browse files
committed
feat(app): add elasped time on dashboard
1 parent d4fb7ce commit d8675b9

2 files changed

Lines changed: 25 additions & 15 deletions

File tree

src/app/batch.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from datetime import datetime
1111
from pathlib import Path
1212

13-
from .colors import BOLD, CYAN, GREEN, MAGENTA, RESET, YELLOW, _paint
13+
from .colors import BOLD, CYAN, GREEN, RESET, YELLOW, _paint
1414
from .fs import _next_available_path, _preview_pythonpath
1515
from .keys import _KeyReader, _clear_screen
1616
from .metrics import CSV_FIELDS, _extract_attempt_metrics, _upsert_csv_row
@@ -71,6 +71,12 @@ def _window_title(attempt_number: int, total_attempts: int) -> str:
7171
return f"Pacman - Attempt {attempt_number}/{total_attempts}"
7272

7373

74+
def _format_duration_mm_ss(duration_seconds: float) -> str:
75+
total_seconds = max(0, int(duration_seconds))
76+
minutes, seconds = divmod(total_seconds, 60)
77+
return f"{minutes:02d}:{seconds:02d}"
78+
79+
7480
def _create_attempt_state(attempt_number: int, total_attempts: int) -> dict[str, object]:
7581
return {
7682
"attempt_display": _attempt_display(attempt_number, total_attempts),
@@ -79,8 +85,7 @@ def _create_attempt_state(attempt_number: int, total_attempts: int) -> dict[str,
7985
"stdout_lines": 0,
8086
"stderr_lines": 0,
8187
"started_at": "",
82-
"finished_at": "",
83-
"duration_seconds": "",
88+
"duration": "",
8489
"score": "",
8590
"result": "",
8691
"return_code": "",
@@ -124,6 +129,7 @@ def _refresh_batch_console(batch_artifacts: dict[str, object]) -> None:
124129
attempt_number: dict(state)
125130
for attempt_number, state in batch_artifacts["attempt_states"].items()
126131
}
132+
refresh_started_at = datetime.now()
127133

128134
counts = {"pending": 0, "running": 0, "completed": 0, "failed": 0, "interrupted": 0}
129135
for state in attempt_states.values():
@@ -173,10 +179,16 @@ def _refresh_batch_console(batch_artifacts: dict[str, object]) -> None:
173179

174180
for attempt_number in range(1, total_attempts + 1):
175181
state = attempt_states.get(attempt_number, _create_attempt_state(attempt_number, total_attempts))
182+
duration_display = str(state.get("duration", ""))
183+
if state.get("started_at") and state.get("status") == "running":
184+
started_at = datetime.fromisoformat(str(state["started_at"]))
185+
duration_display = _format_duration_mm_ss((refresh_started_at - started_at).total_seconds())
176186
line = (
177187
f"[{attempt_number}] {state['status']} | "
178188
f"stdout={state.get('stdout_lines', 0)} | stderr={state.get('stderr_lines', 0)}"
179189
)
190+
if duration_display:
191+
line += f" | elapsed={duration_display}"
180192
if state.get("return_code") not in ("", None):
181193
line += f" | exit={state['return_code']}"
182194
if state.get("score"):
@@ -441,15 +453,15 @@ def _run_game_attempt(
441453

442454
if cancel_event.is_set():
443455
finished_at = started_at
456+
duration_display = _format_duration_mm_ss((finished_at - started_at).total_seconds())
444457
status_label = "interrupted (launcher stop)"
445458
_update_attempt_state(
446459
batch_artifacts,
447460
attempt_number,
448461
status=status_label,
449462
return_code=130,
450463
started_at=started_at.isoformat(timespec="seconds"),
451-
finished_at=finished_at.isoformat(timespec="seconds"),
452-
duration_seconds="0.000",
464+
duration=duration_display,
453465
)
454466
_upsert_csv_row(
455467
csv_path,
@@ -464,8 +476,7 @@ def _run_game_attempt(
464476
"status": status_label,
465477
"return_code": 130,
466478
"started_at": started_at.isoformat(timespec="seconds"),
467-
"finished_at": finished_at.isoformat(timespec="seconds"),
468-
"duration_seconds": "0.000",
479+
"duration": duration_display,
469480
"agent": selected_agent,
470481
"layout": selected_layout,
471482
"ghosts": selected_ghosts,
@@ -483,7 +494,7 @@ def _run_game_attempt(
483494
"stdout": "",
484495
"stderr": "",
485496
"started_at": started_at,
486-
"finished_at": finished_at,
497+
"duration": duration_display,
487498
"interrupted": True,
488499
"window_title": window_title,
489500
}
@@ -605,7 +616,7 @@ def _run_game_attempt(
605616
else "failed"
606617
)
607618
metrics = _extract_attempt_metrics(stdout_text)
608-
duration_seconds = (finished_at - started_at).total_seconds()
619+
duration_display = _format_duration_mm_ss((finished_at - started_at).total_seconds())
609620

610621
_append_text(
611622
log_path,
@@ -616,7 +627,7 @@ def _run_game_attempt(
616627
f"Status: {status_label}",
617628
f"Return code: {return_code}",
618629
f"Finished: {finished_at.isoformat(timespec='seconds')}",
619-
f"Duration seconds: {duration_seconds:.3f}",
630+
f"Duration: {duration_display}",
620631
f"Score: {metrics['score'] or '<unknown>'}",
621632
f"Result: {metrics['result'] or '<unknown>'}",
622633
"",
@@ -637,8 +648,7 @@ def _run_game_attempt(
637648
"status": status_label,
638649
"return_code": return_code,
639650
"started_at": started_at.isoformat(timespec="seconds"),
640-
"finished_at": finished_at.isoformat(timespec="seconds"),
641-
"duration_seconds": f"{duration_seconds:.3f}",
651+
"duration": duration_display,
642652
"agent": selected_agent,
643653
"layout": selected_layout,
644654
"ghosts": selected_ghosts,
@@ -652,7 +662,7 @@ def _run_game_attempt(
652662
status=status_label,
653663
return_code=return_code,
654664
finished_at=finished_at.isoformat(timespec="seconds"),
655-
duration_seconds=f"{duration_seconds:.3f}",
665+
duration=duration_display,
656666
score=metrics["score"],
657667
result=metrics["result"],
658668
)
@@ -671,6 +681,7 @@ def _run_game_attempt(
671681
"stderr": stderr_text,
672682
"started_at": started_at,
673683
"finished_at": finished_at,
684+
"duration": duration_display,
674685
"interrupted": interrupted,
675686
"window_title": window_title,
676687
}

src/app/metrics.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212
"status",
1313
"return_code",
1414
"started_at",
15-
"finished_at",
16-
"duration_seconds",
15+
"duration",
1716
"agent",
1817
"layout",
1918
"ghosts",

0 commit comments

Comments
 (0)