Files
FabledSteward/steward/templates/alerts/_history_page.html
T
bvandeusenandClaude Opus 5.5 531a193bd1
CI / lint (push) Successful in 3s
CI / integration (push) Failing after 54s
CI / unit (push) Successful in 1m34s
CI / publish (push) Skipped
feat(alerts): history reads as episodes; tests follow the confirm dialog (#5316, #5314)
History (#5316):
- Transitions fold into episodes (steward/alerts/episodes.py): each stretch
  out of quiet, the worst state it reached, how long it lasted, its worst
  reading against the rule ('3m 21s (fires above 3m)'), who was told across
  its changes, and its Ansible runs. The changes themselves fold under it.
- An episode cut by paging says so ('cleared later (above)', 'earlier').
- For one rule: a verdict ('Fired 4 times in the last 7d, 1h 2m in all') and
  no Rule column. Notified and Action show only when the range has any.

Tests:
- The confirm guard looks for a confirm( call, not the word in the comment
  that says it is gone (lesson #4365).
- The Hosts viewer test looks for the add-host dialog by id, since every page
  now carries the confirm dialog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 23:02:01 -04:00

75 lines
4.0 KiB
HTML

{# alerts/_history_page.html — one page of alert episodes (#5316), then the
sentinel that loads the next page as the reader nears it (core.paging,
pref 172). An episode is one stretch out of quiet: what it reached, for how
long, its worst reading, who was told; its transitions fold under it. #}
{% from "_macros.html" import status_badge, scroll_more, scroll_end %}
{% set cols = 3 + (0 if chosen else 1) + (1 if show_notified else 0) + (1 if show_action else 0) %}
{% for ep in episodes %}
<tr>
<td class="nowrap">
{%- if ep.began_here %}{{ ep.start|ago }}{% else %}<span class="tone-dim" title="It began before this part of the list">earlier</span>{% endif -%}
</td>
{% if not chosen %}
<td class="cell-name">
<a href="/alerts/history?rule={{ ep.rule.id }}&range={{ current_range }}" title="{{ ep.rule.name }}">{{ ep.rule.name }}</a>
<div class="cell-sub">{{ source_label(ep.rule.source_module) }} · {{ ep.rule.resource_name }}</div>
</td>
{% endif %}
<td>
{% if not ep.began_here and ep.steps|length == 1 %}
{%- set s = ep.steps[0] %}{{ status_badge(s.from_state) }} <span class="tone-dim">→</span> {{ status_badge(s.to_state) }}
{% else %}
{{ status_badge(ep.worst) }}
{%- if ep.ongoing %} <span class="tone-crit">{% if ep.began_here %}for {{ ep.duration|duration }}, {% endif %}still going</span>
{%- elif ep.continues %} <span class="tone-muted">cleared later (above)</span>
{%- elif ep.began_here %} for {{ ep.duration|duration }}<span class="tone-dim"> · cleared {{ ep.end|ago }}</span>
{%- else %} <span class="tone-dim">cleared {{ ep.end|ago }}</span>{% endif %}
{% endif %}
{% for n in ep.notes %}<div class="cell-note">{{ n }}</div>{% endfor %}
{% if ep.steps|length > 1 %}
<details class="episode-steps">
<summary>{{ ep.steps|length }} changes of state</summary>
<ol>
{% for s in ep.steps %}
<li>{{ status_badge(s.from_state) }} <span class="tone-dim">→</span> {{ status_badge(s.to_state) }}
<span class="tone-muted">{{ s.value }}</span> <span class="tone-dim">· {{ s.at|ago }}</span></li>
{% endfor %}
</ol>
</details>
{% endif %}
</td>
<td class="nowrap">
{%- if ep.reading %}{{ ep.reading }} <span class="tone-dim">({{ ep.fires_when }})</span>
{%- else %}<span class="tone-dim">no reading</span> <span class="tone-dim">({{ ep.fires_when }})</span>{% endif -%}
</td>
{% if show_notified %}
<td>
{% if ep.muted_by %}
<span class="badge badge-yellow" title="A maintenance window muted this notification">muted · {{ ep.muted_by }}</span>
{% if ep.released %}<div class="cell-note">notified when the window ended</div>{% endif %}
{% endif %}
{% for label, sent, error in ep.deliveries %}
<div>
{% if sent %}<span class="badge badge-green">{{ label }} sent</span>
{% else %}<span class="badge badge-red">{{ label }} failed</span>
{% if error %}<div class="cell-note" style="max-width:28rem;overflow-wrap:anywhere;">{{ error }}</div>{% endif %}
{% endif %}
</div>
{% endfor %}
{% if not ep.muted_by and not ep.deliveries %}<span class="tone-dim" title="No notification was sent for this">—</span>{% endif %}
</td>
{% endif %}
{% if show_action %}
<td>
{% for run_id in ep.runs %}<div><a href="/ansible/runs/{{ run_id }}">Ansible run</a></div>
{% else %}<span class="tone-dim">—</span>{% endfor %}
</td>
{% endif %}
</tr>
{% endfor %}
{% if after %}
{{ scroll_more("/alerts/history?before=" ~ (after|urlencode) ~ "&range=" ~ (current_range|urlencode) ~ (("&rule=" ~ (chosen.id|urlencode)) if chosen else ""), cols=cols) }}
{% else %}
{{ scroll_end(cols=cols) }}
{% endif %}