Refactor _compute_metrics to improve longest job duration calculation and handle None values in due dates

This commit is contained in:
2026-05-25 19:13:10 +12:00
parent 82c7712613
commit 3be35f6b0a
+26 -20
View File
@@ -178,15 +178,14 @@ def _compute_metrics(
avg_duration_today = (sum(durations_today) / len(durations_today)) if durations_today else None
longest_today = None
longest_today_seconds = 0.0
if today_jobs:
finished_today = [
j for j in today_jobs if j.start_datetime and j.finish_datetime
]
if finished_today:
longest_today = max(
finished_today,
key=lambda j: (j.finish_datetime - j.start_datetime).total_seconds(),
)
for j in today_jobs:
if j.start_datetime and j.finish_datetime:
secs = (j.finish_datetime - j.start_datetime).total_seconds()
if longest_today is None or secs > longest_today_seconds:
longest_today = j
longest_today_seconds = secs
# On-time = job started within 15 min of due_datetime (when both known)
on_time = late = 0
@@ -222,8 +221,8 @@ def _compute_metrics(
# --- Upcoming today ---
upcoming_today = sorted(
(j for j in today_jobs
if j.start_datetime is None and j.due_datetime and j.due_datetime >= now),
key=lambda j: j.due_datetime,
if j.start_datetime is None and j.due_datetime is not None and j.due_datetime >= now),
key=lambda j: j.due_datetime or datetime.max,
)[:5]
return {
@@ -248,8 +247,7 @@ def _compute_metrics(
"avg_duration_today": _fmt_duration(avg_duration_today) if avg_duration_today is not None else None,
"longest_today_name": longest_today.name if longest_today else None,
"longest_today_duration": (
_fmt_duration((longest_today.finish_datetime - longest_today.start_datetime).total_seconds())
if longest_today else None
_fmt_duration(longest_today_seconds) if longest_today else None
),
"on_time_rate_today": on_time_rate_today,
"on_time": on_time,
@@ -269,8 +267,11 @@ def _compute_metrics(
"name": j.name,
"as_at": j.as_at_date.isoformat(),
"reason": j.status_reason or "",
"when": (j.finish_datetime or j.start_datetime).isoformat(timespec="minutes")
if (j.finish_datetime or j.start_datetime) else "",
"when": (
_when.isoformat(timespec="minutes")
if (_when := j.finish_datetime or j.start_datetime) is not None
else ""
),
}
for j in recent_failures
],
@@ -278,7 +279,7 @@ def _compute_metrics(
{
"id": j.id,
"name": j.name,
"due": j.due_datetime.strftime("%H:%M"),
"due": j.due_datetime.strftime("%H:%M") if j.due_datetime else "",
}
for j in upcoming_today
],
@@ -449,11 +450,16 @@ async def job_detail_view(request: Request, job_id: int):
lateness_str = f"{_fmt_duration(delta)} late"
# Aggregate stats across history
finished_related = [j for j in related if j.start_datetime and j.finish_datetime]
avg_dur = (
sum((j.finish_datetime - j.start_datetime).total_seconds() for j in finished_related)
/ len(finished_related)
) if finished_related else None
finished_related = [
j for j in related
if j.start_datetime is not None and j.finish_datetime is not None
]
finished_durations = [
(j.finish_datetime - j.start_datetime).total_seconds()
for j in finished_related
if j.start_datetime is not None and j.finish_datetime is not None
]
avg_dur = (sum(finished_durations) / len(finished_durations)) if finished_durations else None
success_count = sum(1 for j in related if j.status == "completed")
fail_count = sum(1 for j in related if j.status == "failed")
finished_count = success_count + fail_count