{% extends "base.html" %} {% from "_macros.html" import help %} {% block title %}{{ job.name }} — BackupWatch{% endblock %} {% block content %} {# onboarding_state is written by the onboarding workflow; older rows may not carry one, so every use is guarded and falls back to the raw value rather than assuming the vocabulary. #} {% set STATE_LABELS = {'draft': 'Draft', 'observing': 'Observing', 'monitoring': 'Monitoring'} %} {% set STATE_BADGE = {'draft': 'b-none', 'observing': 'b-warning', 'monitoring': 'b-success'} %} {% set jst = job.onboarding_state %} ← {{ job.client.display_name }}

{{ job.name }} {{ job.vendor|vendor }}{% if job.target %} · {{ job.target }}{% endif %} {% if job.auto_created %}auto-created{% endif %} {% if job.silenced %}silenced{% endif %} {% if jst %}{{ STATE_LABELS.get(jst|string, jst|string) }}{% endif %}

{% if jst and jst|string != 'monitoring' %}

This job is {{ STATE_LABELS.get(jst|string, jst|string)|lower }}: it records matching reports and shows failures, but sends no alerts and raises no missed backups until it is activated in Onboarding.

{% endif %} {% if err %}{% endif %} {% if msg %}{% endif %} {% if user.role in ['admin', 'tech'] %}

Settings

Advanced — interval window
{# Run-on days sit outside the interval block: missed detection applies them to daily schedules too, so a weekday-only or weekly daily job has to be configurable here. #}
Run on

Applies to daily and interval schedules. Leave every day unticked to expect a backup every day; tick days for a weekday-only or weekly job.

{% for val, lbl in day_labels %}{% endfor %}
{% if schedule_timezone %}

Schedule times are read in {{ schedule_timezone }} — the same timezone the missed-backup check uses.

{% endif %}

Backup day: {{ backup_day_hint }} {{ help("A run landing anywhere in this window counts as that day's backup — on the history chart and for missed detection alike. Missed detection expects a SUCCESSFUL report inside the window: a failure is recorded and alerted on, but does not satisfy the day. The window closes at the expected time plus the grace, so an overnight run started last night is today's backup. Change the expected time or grace to move the boundary.") }}

Clone this job {{ help("Create a new job pre-filled from this one — edit the name, client, schedule and match rules before saving. Handy for a second job of the same kind (e.g. another Synology backup task).") }}

Maintenance {{ help("Muting suppresses missed-backup detection and failure alerts for this job during planned downtime. The job still records events; you just won't be paged.") }}

{% if muted %} muted until {{ job.muted_until|dt }} {% else %} Mute alerts for: {% endif %}

Silence {{ help("For ad-hoc or non-critical backups: the job keeps matching emails and recording events (history is preserved), but never alerts on failure or missed, is skipped by missed-detection, and is hidden from the dashboard and daily digest. Unlike muting, this is permanent until you turn it off.") }}

{% if job.silenced %}
silenced — tracked, no alerts, off-dashboard
{% else %}
Track this job without alerting or showing it on the dashboard:
{% endif %}

Match rules {{ help("Rules decide which emails belong to this job. AND rules must all match. Rules in the same OR group count as one condition satisfied by any of its members — from X AND (subject A OR subject B). Fully-OR rules match on any one — the add-job wizard's ANY mode writes those, and so does assigning an email from the Review queue (those carry a 'learned' chip).") }}

{% for r in job.rules|sort(attribute='or_group') %} {% set h = rule_hits.get(r.id) %} {% if edit_rid == r.id %} {% else %} {% endif %} {% else %} {% endfor %}
FieldOperatorValueCaseSource Matched {{ help("How many emails this rule has filed, and when it last matched. A rule that never matches may be dead; manual AND matches credit the first manual rule.") }}
cancel {% if r.learned %}saving makes this a manual (AND) rule{% endif %}
{{ r.field }}{{ r.operator }}{{ r.value }} {{ "Yes" if r.case_sensitive else "No" }} {% if r.is_or() %}OR · any matches{% if r.machine_learned() %}learned{% endif %}{% elif r.or_group %}OR group {{ r.or_group }} · one of these{% else %}AND · all must match{% endif %} {% if h %}{{ h[0] }} · last {{ h[1]|dt }}{% else %}never{% endif %} edit
No rules — this job will never auto-match.

AND rules must all match; rules in the same OR group count as one condition met by any of its members; fully-OR rules match on any one. The learned chip marks rules the engine wrote itself when an email was assigned in Review — the rest were written by hand.

{{ help("Runs this job's saved rules over the most recent report emails and shows what they would catch — including anything already filed to another client, which means the rules are too broad.") }}
{% endif %}

History

{% for e in events %} {% else %} {% endfor %}
WhenStatusVendorSubjectFrom
{{ e.received_at|dt }} {{ e.status }} {{ e.vendor_detected|vendor }} {{ e.email_subject }}{{ e.email_from }}
No events yet.
{% include "_sched_toggle.html" %} {% endblock %}