Show human-readable schedule timing on dashboard

Replace raw cron entries with descriptions like "Every hour, to rasp"
or "Daily at 02:00, all remotes". Add cron_to_human() to Cron.pm.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
shuki
2026-03-04 05:07:05 +02:00
parent 4759bb84b2
commit e6ac3dfe25
2 changed files with 55 additions and 3 deletions

View File

@@ -69,12 +69,12 @@ print qq{<div class="card bg-base-100 shadow-sm border border-base-300 mb-6">\n<
print qq{<h2 class="card-title text-sm">Active Cron Schedules</h2>\n};
if (keys %$schedules) {
print qq{<div class="overflow-x-auto rounded-box border border-base-content/5 bg-base-100"><table class="table">\n};
print qq{<thead><tr><th>Schedule</th><th>Cron Entry</th></tr></thead>\n};
print qq{<thead><tr><th>Schedule</th><th>Timing</th></tr></thead>\n};
print qq{<tbody>\n};
for my $name (sort keys %$schedules) {
my $esc_name = GnizaWHM::UI::esc($name);
my $esc_line = GnizaWHM::UI::esc($schedules->{$name});
print qq{<tr class="hover"><td>$esc_name</td><td><code>$esc_line</code></td></tr>\n};
my $human = GnizaWHM::UI::esc(GnizaWHM::Cron::cron_to_human($schedules->{$name}));
print qq{<tr class="hover"><td>$esc_name</td><td>$human</td></tr>\n};
}
print qq{</tbody>\n</table></div>\n};
} else {

View File

@@ -110,6 +110,58 @@ sub remove_schedule {
return _write_crontab($new_crontab);
}
# cron_to_human($cron_line)
# Converts a gniza cron line to a human-readable description.
# e.g. "0 * * * * /usr/local/bin/gniza backup --remote=rasp ..." => "Every hour, to rasp"
sub cron_to_human {
my ($cron_line) = @_;
# Extract the 5 cron fields
my @parts = split /\s+/, $cron_line, 6;
return $cron_line if @parts < 6;
my ($min, $hour, $dom, $mon, $dow) = @parts[0..4];
# Build timing description
my $timing;
my @dow_names = qw(Sunday Monday Tuesday Wednesday Thursday Friday Saturday);
if ($hour eq '*' && $dom eq '*' && $mon eq '*' && $dow eq '*') {
# Hourly
$timing = 'Every hour';
}
elsif ($dom eq '*' && $mon eq '*' && $dow eq '*') {
# Daily
$timing = sprintf('Daily at %02d:%02d', $hour, $min);
}
elsif ($dom eq '*' && $mon eq '*' && $dow =~ /^[0-6]$/) {
# Weekly
my $day_name = $dow_names[$dow] // "day $dow";
$timing = sprintf('Every %s at %02d:%02d', $day_name, $hour, $min);
}
elsif ($mon eq '*' && $dow eq '*' && $dom =~ /^\d+$/) {
# Monthly
my $suffix = ($dom == 1 || $dom == 21 || $dom == 31) ? 'st'
: ($dom == 2 || $dom == 22) ? 'nd'
: ($dom == 3 || $dom == 23) ? 'rd'
: 'th';
$timing = sprintf('%d%s of each month at %02d:%02d', $dom, $suffix, $hour, $min);
}
else {
$timing = "$min $hour $dom $mon $dow";
}
# Extract target remotes from --remote= flag
my $remotes = '';
if ($cron_line =~ /--remote=(\S+)/) {
$remotes = $1;
}
if ($remotes ne '') {
return "$timing, to $remotes";
}
return "$timing, all remotes";
}
# -- Private --
# Only these exact commands are allowed.