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 avg_duration_today = (sum(durations_today) / len(durations_today)) if durations_today else None
longest_today = None longest_today = None
longest_today_seconds = 0.0
if today_jobs: if today_jobs:
finished_today = [ for j in today_jobs:
j for j in today_jobs if j.start_datetime and j.finish_datetime if j.start_datetime and j.finish_datetime:
] secs = (j.finish_datetime - j.start_datetime).total_seconds()
if finished_today: if longest_today is None or secs > longest_today_seconds:
longest_today = max( longest_today = j
finished_today, longest_today_seconds = secs
key=lambda j: (j.finish_datetime - j.start_datetime).total_seconds(),
)
# On-time = job started within 15 min of due_datetime (when both known) # On-time = job started within 15 min of due_datetime (when both known)
on_time = late = 0 on_time = late = 0
@@ -222,8 +221,8 @@ def _compute_metrics(
# --- Upcoming today --- # --- Upcoming today ---
upcoming_today = sorted( upcoming_today = sorted(
(j for j in today_jobs (j for j in today_jobs
if j.start_datetime is None and j.due_datetime and j.due_datetime >= now), if j.start_datetime is None and j.due_datetime is not None and j.due_datetime >= now),
key=lambda j: j.due_datetime, key=lambda j: j.due_datetime or datetime.max,
)[:5] )[:5]
return { 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, "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_name": longest_today.name if longest_today else None,
"longest_today_duration": ( "longest_today_duration": (
_fmt_duration((longest_today.finish_datetime - longest_today.start_datetime).total_seconds()) _fmt_duration(longest_today_seconds) if longest_today else None
if longest_today else None
), ),
"on_time_rate_today": on_time_rate_today, "on_time_rate_today": on_time_rate_today,
"on_time": on_time, "on_time": on_time,
@@ -269,8 +267,11 @@ def _compute_metrics(
"name": j.name, "name": j.name,
"as_at": j.as_at_date.isoformat(), "as_at": j.as_at_date.isoformat(),
"reason": j.status_reason or "", "reason": j.status_reason or "",
"when": (j.finish_datetime or j.start_datetime).isoformat(timespec="minutes") "when": (
if (j.finish_datetime or j.start_datetime) else "", _when.isoformat(timespec="minutes")
if (_when := j.finish_datetime or j.start_datetime) is not None
else ""
),
} }
for j in recent_failures for j in recent_failures
], ],
@@ -278,7 +279,7 @@ def _compute_metrics(
{ {
"id": j.id, "id": j.id,
"name": j.name, "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 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" lateness_str = f"{_fmt_duration(delta)} late"
# Aggregate stats across history # Aggregate stats across history
finished_related = [j for j in related if j.start_datetime and j.finish_datetime] finished_related = [
avg_dur = ( j for j in related
sum((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
/ len(finished_related) ]
) if finished_related else 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") success_count = sum(1 for j in related if j.status == "completed")
fail_count = sum(1 for j in related if j.status == "failed") fail_count = sum(1 for j in related if j.status == "failed")
finished_count = success_count + fail_count finished_count = success_count + fail_count