tweak job runs
This commit is contained in:
parent
2b2a3f1cc0
commit
c210168d65
6 changed files with 94 additions and 12 deletions
|
|
@ -403,7 +403,7 @@ def status_badge(*, label: str, tone: str) -> Renderable:
|
|||
"scheduled": "bg-sky-100 text-sky-800",
|
||||
"idle": "bg-slate-200 text-slate-700",
|
||||
"failed": "bg-rose-100 text-rose-800",
|
||||
"done": "bg-amber-100 text-amber-800",
|
||||
"done": "bg-emerald-100 text-emerald-800",
|
||||
}
|
||||
return h.span(
|
||||
class_=f"inline-flex rounded-full px-2.5 py-1 text-xs font-semibold {tones[tone]}"
|
||||
|
|
|
|||
|
|
@ -504,10 +504,11 @@ def _project_upcoming_job(
|
|||
"slug": job.source.slug,
|
||||
"job_id": job_id,
|
||||
"next_run": (
|
||||
next_run.strftime("%Y-%m-%d %H:%M UTC")
|
||||
_humanize_future_time(reference_time, next_run)
|
||||
if next_run is not None
|
||||
else ("Running now" if running_execution is not None else "Not scheduled")
|
||||
),
|
||||
"next_run_at": next_run.isoformat() if next_run is not None else None,
|
||||
"schedule": " ".join(
|
||||
(
|
||||
str(job.cron_minute),
|
||||
|
|
@ -641,3 +642,22 @@ def _format_bytes(value: int) -> str:
|
|||
if value < 1024 * 1024 * 1024:
|
||||
return f"{value / (1024 * 1024):.1f} MB"
|
||||
return f"{value / (1024 * 1024 * 1024):.1f} GB"
|
||||
|
||||
|
||||
def _humanize_future_time(reference_time: datetime, target_time: datetime) -> str:
|
||||
delta_seconds = int(round((target_time - reference_time).total_seconds()))
|
||||
if delta_seconds <= 0:
|
||||
return "now"
|
||||
|
||||
units = (
|
||||
("day", 24 * 60 * 60),
|
||||
("hour", 60 * 60),
|
||||
("minute", 60),
|
||||
)
|
||||
for label, size in units:
|
||||
if delta_seconds >= size:
|
||||
count = max(1, round(delta_seconds / size))
|
||||
suffix = "" if count == 1 else "s"
|
||||
return f"in {count} {label}{suffix}"
|
||||
|
||||
return f"in {delta_seconds} seconds"
|
||||
|
|
|
|||
|
|
@ -70,9 +70,6 @@ def _running_row(execution: Mapping[str, object]) -> tuple[Node, ...]:
|
|||
h.p(class_="font-medium text-slate-900")[
|
||||
f"#{_text(execution, 'execution_id')}"
|
||||
],
|
||||
h.p(class_="mt-1 text-xs text-slate-500")[
|
||||
f"job {_text(execution, 'job_id')}"
|
||||
],
|
||||
],
|
||||
h.div[
|
||||
h.p(class_="font-medium text-slate-900")[_text(execution, "started_at")],
|
||||
|
|
@ -99,15 +96,26 @@ def _running_row(execution: Mapping[str, object]) -> tuple[Node, ...]:
|
|||
|
||||
|
||||
def _upcoming_row(job: Mapping[str, object]) -> tuple[Node, ...]:
|
||||
next_run_at = _maybe_text(job, "next_run_at")
|
||||
next_run_label: Node = h.p(class_="font-medium text-slate-900")[
|
||||
_text(job, "next_run")
|
||||
]
|
||||
if next_run_at is not None:
|
||||
next_run_label = h.time(
|
||||
{
|
||||
"data-next-run-at": next_run_at,
|
||||
"title": next_run_at,
|
||||
},
|
||||
datetime=next_run_at,
|
||||
class_="font-medium text-slate-900",
|
||||
)[_text(job, "next_run")]
|
||||
|
||||
return (
|
||||
h.div[
|
||||
h.div(class_="font-semibold text-slate-950")[_text(job, "source")],
|
||||
h.p(class_="mt-1 font-mono text-xs text-slate-500")[_text(job, "slug")],
|
||||
],
|
||||
h.div[
|
||||
h.p(class_="font-medium text-slate-900")[_text(job, "next_run")],
|
||||
h.p(class_="mt-1 text-xs text-slate-500")[f"job {_text(job, 'job_id')}"],
|
||||
],
|
||||
h.div[next_run_label,],
|
||||
h.p(class_="font-mono text-xs text-slate-600")[_text(job, "schedule")],
|
||||
status_badge(
|
||||
label=_text(job, "enabled_label"),
|
||||
|
|
@ -147,9 +155,6 @@ def _completed_row(execution: Mapping[str, object]) -> tuple[Node, ...]:
|
|||
h.p(class_="font-medium text-slate-900")[
|
||||
f"#{_text(execution, 'execution_id')}"
|
||||
],
|
||||
h.p(class_="mt-1 text-xs text-slate-500")[
|
||||
f"job {_text(execution, 'job_id')}"
|
||||
],
|
||||
],
|
||||
h.div[
|
||||
h.p(class_="font-medium text-slate-900")[_text(execution, "ended_at")],
|
||||
|
|
@ -232,6 +237,48 @@ def runs_page(
|
|||
),
|
||||
rows=completed_rows,
|
||||
),
|
||||
h.script[
|
||||
"""
|
||||
window.repubFormatNextRuns = window.repubFormatNextRuns || (() => {
|
||||
const relativeFormatter = new Intl.RelativeTimeFormat(undefined, { numeric: 'auto' });
|
||||
const absoluteFormatter = new Intl.DateTimeFormat(undefined, {
|
||||
dateStyle: 'medium',
|
||||
timeStyle: 'short',
|
||||
timeZoneName: 'short',
|
||||
});
|
||||
const formatRelative = (targetDate) => {
|
||||
const diffSeconds = Math.round((targetDate.getTime() - Date.now()) / 1000);
|
||||
const units = [
|
||||
['day', 86400],
|
||||
['hour', 3600],
|
||||
['minute', 60],
|
||||
['second', 1],
|
||||
];
|
||||
for (const [unit, size] of units) {
|
||||
if (Math.abs(diffSeconds) >= size || unit === 'second') {
|
||||
return relativeFormatter.format(Math.round(diffSeconds / size), unit);
|
||||
}
|
||||
}
|
||||
return relativeFormatter.format(0, 'second');
|
||||
};
|
||||
const format = () => {
|
||||
document.querySelectorAll('time[data-next-run-at]').forEach((element) => {
|
||||
const nextRunAt = element.getAttribute('data-next-run-at');
|
||||
if (!nextRunAt) return;
|
||||
const targetDate = new Date(nextRunAt);
|
||||
if (Number.isNaN(targetDate.getTime())) return;
|
||||
element.textContent = formatRelative(targetDate);
|
||||
element.title = absoluteFormatter.format(targetDate);
|
||||
});
|
||||
};
|
||||
format();
|
||||
if (!window.repubNextRunTimer) {
|
||||
window.repubNextRunTimer = window.setInterval(format, 30000);
|
||||
}
|
||||
});
|
||||
window.repubFormatNextRuns();
|
||||
"""
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
/*! tailwindcss v4.2.1 | MIT License | https://tailwindcss.com */
|
||||
@view-transition {
|
||||
navigation: auto;
|
||||
}
|
||||
|
||||
@layer properties;
|
||||
@layer theme, base, components, utilities;
|
||||
@layer theme {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue