{% from '_status.html' import status_dot, status_bar %} {# Landing-page body, in order: activity, backends, current runs, recent runs. Rendered standalone by /overview/stream, so it must not depend on anything overview.html sets up beyond the context keys. #} {# Activity heatmap. Indigo rather than GitHub's green: in this app green already means "completed", and indigo is what running work is drawn in everywhere else — so shade reads as "how much ran", not "how much passed". #} {% set LEVELS = [ 'bg-gray-100', 'bg-indigo-200', 'bg-indigo-400', 'bg-indigo-600', 'bg-indigo-800', ] %}

Activity · last {{ activity.window_label }}

CPU-hours per day
{% for col, label in activity.month_labels %} {{ label }} {% endfor %}
{% for label in ['Mon', '', 'Wed', '', 'Fri', '', ''] %} {{ label }} {% endfor %}
{% for week in activity.weeks %}
{% for day in week %} {% if day %}
{% else %} {# Before the window starts, or later this week. #}
{% endif %} {% endfor %}
{% endfor %}
Less {% for cls in LEVELS %} {% endfor %} More
{{ '%.0f'|format(activity.total_cpu_hours) }}
CPU-hours
{{ activity.active_days }}/{{ activity.window_days }}
Active days
{% if activity.busiest %}
{{ activity.busiest.day.strftime('%d %b') }}
Busiest · {{ '%.0f'|format(activity.busiest.cpu_hours) }} CPU-h
{% endif %}
{# Today's shape, beside the year. Stacked by source so a spike is attributable; colours are slots 1-5 of the validated categorical palette plus a neutral for the "Other" rollup, assigned busiest-source-first so a source keeps its colour as the mix changes. Three of those hues sit under 3:1 on white, so the legend carries a name and a number per series — identity is never colour alone. #}

Last {{ hourly.window_hours }} hours

CPU-hours by source · local time
{% if hourly.is_empty %}
Nothing ran in the last {{ hourly.window_hours }} hours.
{% else %} {# Without a scale the bars are only comparable to each other; the peak names what full height is worth. #}
peak {{ '%.0f'|format(hourly.max_total) if hourly.max_total >= 1 else '%.1f'|format(hourly.max_total) }} CPU-h/h
{# Clock times, not bare numbers — "16" alone reads as a count. Every 4h: 24 labels would collide, but at ~76px apart "16:00" has room. The label overflows its one-hour column and centres on it, which is why nothing here clips. #}
{% for hour in hourly.hours %}
{% if loop.index0 % 4 == 0 %}{{ hour.start.strftime('%H:%M') }}{% endif %}
{% endfor %}
{% for source in hourly.sources %} {{ source }} {{ '%.1f'|format(hourly.totals[source]) }} {% endfor %}
{% endif %}
{# One run card, shared by the "Current runs" and "Recent runs" sections below. Finished runs render dimmed so a glance separates them from live work without needing to read the status pill. #} {% macro run_card(run) %} {% set is_active = run.status.value in ('running', 'pending') %}
{% if run.source_name %} {{ run.source_name }} {% else %} no source {% endif %} {{ status_dot(run.status.value) }} {{ run.status.value }}

{{ run.display_name }}

{{ status_bar(run.status_counts, 'flex-1 h-2') }} {{ run.progress[0] }}/{{ run.progress[1] }}
{% set c = run.status_counts %}
{% if c.get('running', 0) %} {{ status_dot('running') }}{{ c.get('running') }} running {% endif %} {% set waiting = c.get('queued', 0) + c.get('submitted', 0) %} {% if waiting %} {{ status_dot('queued') }}{{ waiting }} queued {% endif %} {% if c.get('pending', 0) %} {{ status_dot('pending') }}{{ c.get('pending') }} waiting {% endif %} {% if c.get('failed', 0) or c.get('dep_failed', 0) %} {{ status_dot('failed') }}{{ c.get('failed', 0) + c.get('dep_failed', 0) }} failed {% endif %}
{{ run.backend_name }} · {# Same slot, two meanings now that finished cards sit beside live ones: "running for 13s" vs "started 8d ago". Without the suffix a finished run looks like it took 8 days. #} {% if not is_active %} ago{% endif %} {% if run.total_cpu_hours > 0 %} · {% if run.total_cpu_hours >= 1 %}{{ "%.1f"|format(run.total_cpu_hours) }} CPU-h {% else %}{{ "%.0f"|format(run.total_cpu_hours * 60) }} CPU-min{% endif %} {% endif %}
{% endmacro %}

Backends

Details →
{% if backends %}
{% for name, backend in backends.items() %} {% set u = backend_usage.get(name, {}) %} {% set q = backend.status.cluster_info.user_quota if backend.status.cluster_info else None %}
{{ backend.backend_type }}
{% if not backend.enabled %}

Disabled

{% elif not backend.status.connected %}

Disconnected

{% if backend.status.error %}

{{ backend.status.error }}

{% endif %} {% else %}
{{ u.get('cpus', 0) }}
CPUs running
{{ u.get('cpus_queued', 0) }}
CPUs queued
{{ u.get('jobs', 0) }}{% if q and q.jobs_max %}/{{ q.jobs_max }}{% endif %}
My jobs
{% if backend.status.cpus_total %} {% set cpus_busy = backend.status.cpus_total - backend.status.cpus_idle %} {% set cluster_pct = (cpus_busy * 100 / backend.status.cpus_total) | round(0, 'floor') | int %}
Cluster {{ cluster_pct }}% busy · {{ backend.status.cpus_idle }} CPUs free
{% endif %} {% if backend.status.disk_total_bytes and backend.status.disk_avail_bytes is not none %} {% set disk_pct = ((backend.status.disk_total_bytes - backend.status.disk_avail_bytes) * 100 / backend.status.disk_total_bytes) | round(0, 'floor') | int %}
Disk {{ disk_pct }}% used · {{ backend.status.disk_avail_bytes | disk_bytes }} free
{% endif %}
{% if q and (q.fair_share is not none or q.account) %}
{% if q.fair_share is not none %} fair-share {{ '%.2f'|format(q.fair_share) }} {% endif %} {% if q.account %} account {{ q.account }} {% endif %}
{% endif %} {% endif %}
{% endfor %}
{% else %}

No backends configured

{% endif %}

Current runs

All runs →
{% if active_runs %}
{% for run in active_runs %}{{ run_card(run) }}{% endfor %}
{% elif recent_runs %} {# Something has run before, so keep this short — the history is right below rather than repeating the onboarding pitch. #}
Nothing running right now.
{% else %}

Nothing has run yet

Pick a workflow on the Sources page to start one.

{% endif %}
{# Only rendered once there is history; on a fresh install the "Current runs" empty state above already covers the page. #} {% if recent_runs %}

Recent runs

All runs →
{% for run in recent_runs %}{{ run_card(run) }}{% endfor %}
{% endif %}