{#- Trend dashboard for multi-snapshot Coverity metrics comparison. Rendered by ``coverity_metrics/cli/delta.py`` with a single ``delta`` dict (schema v1, trend shape) plus ``inline_css``. Deliberately dependency-free — no Plotly, no external JS, no CDN. Line charts and sparklines are hand-rolled inline SVG. Tables use the ``data-table`` class so the sortable-header autoloader at the bottom wires ▲▼ arrows onto every column at load time (repo convention, see .github/copilot-instructions.md). The ``per_project`` sparkline column exposes a numeric ``data-sort-value`` (the first→last active_defects Δ) so the autoloader's generic sort still produces a meaningful order on that column. -#} {#- ---------- Reusable SVG chart macros ---------- -#} {% macro line_chart(series, labels, width=520, height=140, color='#4a90e2', y_label='') -%} {#- Renders a fixed-viewBox SVG line chart. ``series`` may contain None to represent missing data points; the polyline is broken into segments at those positions. Y-axis min/max labels sit outside the plot area on the left. Each marker gets a native SVG tooltip showing "label: value" on hover. -#} {% set numeric = [] %} {% for v in series %}{% if v is not none %}{% set _ = numeric.append(v) %}{% endif %}{% endfor %} {% if not numeric %} <div class="chart-empty">No data.</div> {% else %} {% set y_min = numeric|min %} {% set y_max = numeric|max %} {% set span = (y_max - y_min) if y_max != y_min else 1 %} {% set pad_left = 44 %} {% set pad_right = 12 %} {% set pad_top = 12 %} {% set pad_bottom = 28 %} {% set plot_w = width - pad_left - pad_right %} {% set plot_h = height - pad_top - pad_bottom %} {% set n = series|length %} {% set step = (plot_w / (n - 1)) if n > 1 else 0 %} <svg class="line-chart" viewBox="0 0 {{ width }} {{ height }}" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Line chart"> {#- Axes -#} <line x1="{{ pad_left }}" y1="{{ pad_top }}" x2="{{ pad_left }}" y2="{{ pad_top + plot_h }}" stroke="#cfd8dc" stroke-width="1"/> <line x1="{{ pad_left }}" y1="{{ pad_top + plot_h }}" x2="{{ pad_left + plot_w }}" y2="{{ pad_top + plot_h }}" stroke="#cfd8dc" stroke-width="1"/> {#- Y-axis labels (top = max, bottom = min) -#} <text x="{{ pad_left - 6 }}" y="{{ pad_top + 4 }}" font-size="10" text-anchor="end" fill="#7f8c8d">{{ '{:g}'.format(y_max) }}</text> <text x="{{ pad_left - 6 }}" y="{{ pad_top + plot_h + 3 }}" font-size="10" text-anchor="end" fill="#7f8c8d">{{ '{:g}'.format(y_min) }}</text> {#- Points kept as (x, y, value, label) so tooltips can access them. Segments are lists of point indices into ``points``. -#} {% set points = [] %} {% set segments = [] %} {% set current = [] %} {% for v in series %} {% if v is none %} {% if current %}{% set _ = segments.append(current) %}{% set current = [] %}{% endif %} {% else %} {% set x = pad_left + (loop.index0 * step) %} {% set y = pad_top + plot_h - ((v - y_min) / span * plot_h) %} {% set lbl = labels[loop.index0] if loop.index0 < labels|length else '' %} {% set _ = points.append((x, y, v, lbl)) %} {% set _ = current.append(points|length - 1) %} {% endif %} {% endfor %} {% if current %}{% set _ = segments.append(current) %}{% endif %} {% for seg in segments %} {% if seg|length > 1 %} <polyline fill="none" stroke="{{ color }}" stroke-width="2" stroke-linejoin="round" stroke-linecap="round" points="{% for idx in seg %}{{ '%.1f,%.1f'|format(points[idx][0], points[idx][1]) }} {% endfor %}"/> {% endif %} {% endfor %} {#- Markers rendered last so they sit on top of the polyline and their <title> tooltips always win the hover test. A larger transparent hit-area behind each visible dot makes the tooltip easier to trigger. -#} {% for pt in points %} {% set x, y, v, lbl = pt %} <g> <title>{{ lbl }}{% if lbl %}: {% endif %}{{ '{:g}'.format(v) }} {% endfor %} {#- X-axis labels below the plot -#} {% for lbl in labels %} {% set x = pad_left + (loop.index0 * step) %} {{ lbl }} {% endfor %} {% endif %} {%- endmacro %} {% macro sparkline(series, labels=[], width=140, height=28, color='#4a90e2') -%} {#- Compact inline SVG sparkline for use inside table cells. Missing data points break the line into segments. No axes, no labels. Each marker gets a native SVG tooltip so hovering a data point reveals the label and value. -#} {% set numeric = [] %} {% for v in series %}{% if v is not none %}{% set _ = numeric.append(v) %}{% endif %}{% endfor %} {% if not numeric %} <span class="spark-empty">—</span> {% else %} {% set y_min = numeric|min %} {% set y_max = numeric|max %} {% set span = (y_max - y_min) if y_max != y_min else 1 %} {% set pad = 3 %} {% set plot_w = width - 2 * pad %} {% set plot_h = height - 2 * pad %} {% set n = series|length %} {% set step = (plot_w / (n - 1)) if n > 1 else 0 %} <svg class="sparkline" viewBox="0 0 {{ width }} {{ height }}" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Sparkline"> {% set points = [] %} {% set segments = [] %} {% set current = [] %} {% for v in series %} {% if v is none %} {% if current %}{% set _ = segments.append(current) %}{% set current = [] %}{% endif %} {% else %} {% set x = pad + (loop.index0 * step) %} {% set y = pad + plot_h - ((v - y_min) / span * plot_h) %} {% set lbl = labels[loop.index0] if loop.index0 < labels|length else '' %} {% set _ = points.append((x, y, v, lbl)) %} {% set _ = current.append(points|length - 1) %} {% endif %} {% endfor %} {% if current %}{% set _ = segments.append(current) %}{% endif %} {% for seg in segments %} {% if seg|length > 1 %} <polyline fill="none" stroke="{{ color }}" stroke-width="1.5" stroke-linejoin="round" stroke-linecap="round" points="{% for idx in seg %}{{ '%.1f,%.1f'|format(points[idx][0], points[idx][1]) }} {% endfor %}"/> {% endif %} {% endfor %} {% for pt in points %} {% set x, y, v, lbl = pt %} <g> <title>{{ lbl }}{% if lbl %}: {% endif %}{{ '{:g}'.format(v) }} {% endfor %} {% endif %} {%- endmacro %} {%- set snap_labels = delta.snapshots | map(attribute='label') | list -%} Coverity Delta Report — {{ snap_labels[0] }} → {{ snap_labels[-1] }}

Coverity Delta Trend Report

Multi-snapshot adoption comparison
{% for lbl in snap_labels %} {{ lbl }}{% if not loop.last %}{% endif %} {% endfor %}
{{ delta.snapshots | length }} snapshots  •  windows: {% for s in delta.snapshots %} --days {{ s.days }}{% if not loop.last %},{% endif %} {% endfor %}
{% for s in delta.snapshots %} {{ s.label }} ({{ s.export_date[:10] }}) {% if not loop.last %}{% endif %} {% endfor %}
Generated: {{ generated }}
{% if delta.warnings %}
⚠ {{ delta.warnings|length }} warning{{ '' if delta.warnings|length == 1 else 's' }} — read before interpreting the trend
    {% for w in delta.warnings %}
  • {{ w.code }}: {{ w.message }}
  • {% endfor %}
{% endif %} {% set window_mismatch = delta.warnings|selectattr('code', 'equalto', 'window_mismatch')|list|length > 0 %} {% for instance_name, inst in delta.instances.items() %}

Instance: {{ instance_name }}

{# -------- Section 1: Projects -------- #}

Projects

{% set p = inst.projects %}
Projects (last)
{{ p.last_count }}
was {{ p.first_count }} ( {{ '{:+d}'.format(p.count_delta) }} )
Added
{{ p.added_first_to_last|length }}
appeared during the window
Dropped
{{ p.dropped_first_to_last|length }}
removed during the window
Retained
{{ p.retained_count }}
present in both ends
Project count over time
{{ line_chart(p.count_series, snap_labels) }}
{% if p.added_first_to_last or p.dropped_first_to_last %}

Added during window

{% if p.added_first_to_last %}
    {% for evt in p.added_first_to_last %} {% set joined = snap_labels[evt.first_seen_index] %} {% set last_seen = snap_labels[evt.last_seen_index] %} {% set still_present = evt.last_seen_index == snap_labels|length - 1 %}
  • {{ evt.name }} {% if still_present %} joined {{ joined }} {% else %} {{ joined }} → {{ last_seen }} (came & went) {% endif %}
  • {% endfor %}
{% else %}None.{% endif %}

Dropped during window

{% if p.dropped_first_to_last %}
    {% for evt in p.dropped_first_to_last %} {% set joined = snap_labels[evt.first_seen_index] %} {% set last_seen = snap_labels[evt.last_seen_index] %} {% set was_at_start = evt.first_seen_index == 0 %}
  • {{ evt.name }} {% if was_at_start %} last seen {{ last_seen }} {% else %} {{ joined }} → {{ last_seen }} (came & went) {% endif %}
  • {% endfor %}
{% else %}None.{% endif %}
{% endif %}
{# -------- Section 2: Active users -------- #}

Active users

{% for key, label in [ ('total_licensed_users', 'Licensed users'), ('users_with_login', 'Users with login'), ('active_users', 'Active users'), ('active_user_percentage', 'Active user %'), ('login_user_percentage', 'Login user %'), ] %} {% set s = inst.active_users.stats[key] %} {% set is_pct = key.endswith('_percentage') %}
{{ label }}
{% if s.last is none %}— {% elif is_pct %}{{ '%.1f'|format(s.last) }} {% else %}{{ s.last|int }}{% endif %}
was {% if s.first is none %}— {% elif is_pct %}{{ '%.1f'|format(s.first) }} {% else %}{{ s.first|int }}{% endif %} {% if s.delta is not none %} ( {{ '{:+.1f}'.format(s.delta) if is_pct else '{:+d}'.format(s.delta|int) }} {% if s.pct_delta is not none %}, {{ '{:+.1f}%'.format(s.pct_delta) }}{% endif %}) {% endif %}
{% endfor %}
{% for key, label in [ ('total_licensed_users', 'Licensed users'), ('users_with_login', 'Users with login'), ('active_users', 'Active users'), ('active_user_percentage', 'Active user %'), ('login_user_percentage', 'Login user %'), ] %} {% set s = inst.active_users.stats[key] %}
{{ label }}
{{ line_chart(s.series, snap_labels) }}
{% endfor %}
{% set tup = inst.active_users.get('top_users_by_fixes_first_to_last') %} {% if tup and (tup.added or tup.dropped) %}

Top fixers — new entrants ({{ snap_labels[0] }} → {{ snap_labels[-1] }})

{% if tup.added %}
    {% for u in tup.added %}
  • {{ u }}
  • {% endfor %}
{% else %}None.{% endif %}

Top fixers — dropped out ({{ snap_labels[0] }} → {{ snap_labels[-1] }})

{% if tup.dropped %}
    {% for u in tup.dropped %}
  • {{ u }}
  • {% endfor %}
{% else %}None.{% endif %}
{% endif %}
{# -------- Section 3: Scan activity -------- #}

Scan activity {% if window_mismatch %}⚠ WINDOW MISMATCH{% endif %}

{% for key, label in [ ('scan_count', 'Snapshots (scans)'), ('total_files_analyzed', 'Files analyzed'), ('total_new_defects', 'New defects introduced'), ('total_eliminated_defects', 'Defects eliminated'), ] %} {% set t = inst.scan_activity.totals[key] %}
{{ label }}
{{ line_chart(t.series, snap_labels) }}
first {{ t.first|int }} → last {{ t.last|int }} ( {{ '{:+d}'.format(t.delta|int) }} {% if t.pct_delta is not none %}, {{ '{:+.1f}%'.format(t.pct_delta) }}{% endif %})
{% endfor %}
{% for lbl in snap_labels %}{% endfor %} {% for key, label in [ ('scan_count', 'Snapshots (scans)'), ('total_files_analyzed', 'Files analyzed'), ('total_new_defects', 'New defects introduced'), ('total_eliminated_defects', 'Defects eliminated'), ] %} {% set t = inst.scan_activity.totals[key] %} {% for v in t.series %}{% endfor %} {% endfor %}
Metric{{ lbl }}First → Last Δ
{{ label }}{{ v|int }} {{ '{:+d}'.format(t.delta|int) }} {% if t.pct_delta is not none %}{{ '{:+.1f}%'.format(t.pct_delta) }}{% else %}—{% endif %}
{% if window_mismatch and inst.scan_activity.normalized_per_day %}

Normalized per day ⚠ window mismatch

{% for key, label in [ ('scan_count', 'Snapshots / day'), ('total_files_analyzed', 'Files analyzed / day'), ('total_new_defects', 'New defects / day'), ('total_eliminated_defects', 'Defects eliminated / day'), ] %} {% set n = inst.scan_activity.normalized_per_day[key] %}
{{ label }}
{{ line_chart(n.series, snap_labels, color='#8e44ad') }}
first {{ n.first }} → last {{ n.last }}
{% endfor %}
{% endif %}
{# -------- Section 4: Stream activity -------- #}

Stream activity

{% set sc = inst.snapshot_cadence %}
{% set stream_delta = sc.last_stream_count - sc.first_stream_count %}
Active streams (last)
{{ sc.last_stream_count }}
was {{ sc.first_stream_count }} ( {{ '{:+d}'.format(stream_delta) }} )
Newly active
{{ sc.newly_active_first_to_last|length }}
appeared during the window
Went dark
{{ sc.went_dark_first_to_last|length }}
no longer active by {{ snap_labels[-1] }}
Retained
{{ sc.retained_count }}
present at both ends
Active streams over time
{{ line_chart(sc.active_stream_count_series, snap_labels) }}
{% if sc.newly_active_first_to_last or sc.went_dark_first_to_last %}

Newly active during window

{% if sc.newly_active_first_to_last %}
    {% for evt in sc.newly_active_first_to_last %} {% set joined = snap_labels[evt.first_seen_index] %} {% set last_seen = snap_labels[evt.last_seen_index] %} {% set still_present = evt.last_seen_index == snap_labels|length - 1 %}
  • {{ evt.name }} {% if still_present %} first active {{ joined }} {% else %} {{ joined }} → {{ last_seen }} (came & went) {% endif %}
  • {% endfor %}
{% else %}None.{% endif %}

Went dark during window

{% if sc.went_dark_first_to_last %}
    {% for evt in sc.went_dark_first_to_last %} {% set joined = snap_labels[evt.first_seen_index] %} {% set last_seen = snap_labels[evt.last_seen_index] %} {% set was_at_start = evt.first_seen_index == 0 %}
  • {{ evt.name }} {% if was_at_start %} last active {{ last_seen }} {% else %} {{ joined }} → {{ last_seen }} (came & went) {% endif %}
  • {% endfor %}
{% else %}None.{% endif %}
{% endif %}

How this is computed: {{ sc.note }}

{# -------- Section 5: Defects by project (with sparkline + ranking movement) -------- #}

Defects by project

{% if inst.defects_by_project.per_project %}

Sparkline shows active-defect count across the {{ delta.snapshots|length }} snapshots ({{ snap_labels[0] }} → {{ snap_labels[-1] }}). Ranking is on outstanding active defects per snapshot (#1 = most active defects). ▲ means the project moved up the ranking (better, fewer active defects than before), ▼ means it moved down. Sort is by first→last active-defect Δ by default.

{% for r in inst.defects_by_project.per_project %} {% set sort_val = r.active_delta if r.active_delta is not none else 0 %} {% endfor %}
Project Active trend ({{ snap_labels|length }} points) Active first Active last Δ active Rank first Rank last Δ rank
{{ r.project_name }} {{ sparkline(r.active_series, snap_labels) }} {{ r.active_first if r.active_first is not none else '—' }} {{ r.active_last if r.active_last is not none else '—' }} {% if r.active_delta is not none %}{{ '{:+d}'.format(r.active_delta|int) }}{% else %}—{% endif %} {% if r.active_pct_delta is not none %}{{ '{:+.1f}%'.format(r.active_pct_delta) }}{% else %}—{% endif %} {{ r.rank_first if r.rank_first is not none else '—' }} {{ r.rank_last if r.rank_last is not none else '—' }} {% if r.rank_first is none and r.rank_last is none %} came & went {% elif r.rank_first is none and r.rank_last is not none %} new {% elif r.rank_first is not none and r.rank_last is none %} dropped {% elif r.rank_delta is not none and r.rank_delta > 0 %} ▲{{ r.rank_delta|int }} {% elif r.rank_delta is not none and r.rank_delta < 0 %} ▼{{ (r.rank_delta * -1)|int }} {% else %} 0 {% endif %}
{% else %} No per-project defect data available. {% endif %}
{% endfor %}