{# Тело страницы дня. Живое обновление подменяет только его, поэтому кнопки разбора и просмотрщик скриншотов живут снаружи — их обработчики настраиваются один раз. #} {# Живая пара «Идёт работа / Перерыв»: секунды тикают на клиенте (day.html), сервер даёт только якоря. Пока человек за делом, идут часы работы с начала отрезка; замолчал дольше порога простоя — работа застывает на длительности отрезка, и начинает идти перерыв от последней активности. #} {% if d.live_now %} {# Серверного «сейчас» здесь нарочно нет: оно менялось бы каждым опросом, и живая подмена дёргала бы DOM даже без новых данных. Сдвиг часов зрителя считается один раз при загрузке страницы (day.html). #}
{{ tr('Идёт работа') }}
—
{{ tr('отрезок с {t}').format(t=d.live_now.since_work) }}
{{ tr('Перерыв') }}
—
{{ tr('тишина с {t}').format(t=d.live_now.since_break) }}{% if not alive %} · {{ tr('машина не на связи') }}{% endif %}
{% endif %} {# Пять цифр читаются как одна фраза: пришёл — ушёл — из них работал столько, отдыхал столько, и вот с какой плотностью. «Активность» меньше «работы» на мелкие отвлечения внутри рабочих отрезков — это и есть плотность. #}
{{ d.first }}
{{ tr('Начало') }}
{{ d.last }}
{{ tr('Конец') }}
{{ d.work_human }}
{{ tr('Работа') }}
{{ d.break_human }}
{{ tr('Перерывы') }}
{{ d.total_human }}
{{ tr('Активность') }}
{{ d.focus }}%
{{ tr('Плотность') }}
{% if d.blocks %}
{# Дорожка — общая (.tl-ruler + .tl-track из base.html), только во всю ширину: здесь одна машина за один день, подписи слева не нужны. Линейка стоит НАД дорожкой, как в динамике и в карточке «Машины», и делит сутки теми же двумя часами, что и сетка внутри дорожки. #}
{% for h in range(0, 25, 2) %} {{ '%02d'|format(h if h < 24 else 0) }} {% endfor %}
{# Строится из самих замеров, а не из блоков: блок склеивает паузы до session_gap_min, и десятиминутный перерыв выглядел бы работой. #}
{% for s in d.segments %} {% endfor %}
{# Плотность по часам: высота — доля часа, занятая работой, а не доля от самого удачного часа этого дня. Иначе час с двадцатью минутами выглядит полным, и дни между собой не сравнить. #}
{% for i in range(24) %}{{ i if i % 3 == 0 else '' }}{% endfor %}

{{ tr('Столбик — доля часа, занятая работой: полная высота значит «весь час за делом».') }}

{% if d.spans %}

{{ tr('Работа и перерывы') }}

{{ tr('Отрезки дня') }} {{ tr('работа') }} {{ d.work_human }} · {{ tr('перерывы') }} {{ d.break_human }}
{% for s in d.spans %}
{{ s.from }} – {{ s.to }} {{ s.human }} {# «активно» пишем, только когда оно меньше самого отрезка: повторять ту же цифру дважды в строке — значит заставлять сравнивать их взглядом. #} {%- if s.kind == 'break' %}{{ tr('перерыв') }} {%- else %} {%- if s.active_human != s.human %}{{ tr('активно {t}').format(t=s.active_human) }}{% endif %} {%- if s.waiting_human %} · {{ tr('ждал машину {t}').format(t=s.waiting_human) }}{% endif %} {%- endif -%}
{% endfor %}
{# Итог отдельной строкой: она отвечает на вопрос, ради которого таблицу и открыли. Работа и перерывы в сумме дают время за компьютером — это ровно от первой отметки до последней. #}
{{ tr('итого') }} {{ tr('работа') }} {{ d.work_human }} {{ tr('перерывы') }} {{ d.break_human }}{% if d.breaks %} · {{ tr('{n} шт.').format(n=d.breaks) }}{% endif %} {{ tr('за компьютером') }} {{ d.span_human }} {% if d.waiting_sec %}{{ tr('ожидание машины') }} {{ d.waiting_human }}{% endif %} {{ tr('активность') }} {{ d.total_human }} · {{ tr('плотность') }} {{ d.focus }}%
{% endif %} {# Почему вместо рассказа модели сухой список приложений — говорим ЗДЕСЬ, а не только полосой наверху: человек смотрит день, прокрученный вниз, и молчаливая подмена читается как поломка продукта (живой случай, 28.08). #} {% if plan[0] in ('expired', 'blocked') %}

{{ tr('Как идёт день') if d.is_today else tr('Итог дня') }}

{{ tr('Разбор ИИ выключен: демо кончилось. Ниже — не рассказ модели, а список приложений и заголовков окон.') }} {{ tr('Оплатить →') }}
{% if d.summary %}

{{ d.summary }}

{% endif %} {% elif d.summary %}

{{ tr('Как идёт день') if d.is_today else tr('Итог дня') }}

{{ d.summary }}

{% endif %}

{{ tr('Приложения') }}

{% for a in d.apps %}
{{ a.label }} {{ a.human }}
{% endfor %}

{{ tr('Блоки активности') }}

{% if d.hour_marks %} {# Оглавление по часам: сколько наработано в каждом и прыжок к первому блоку. Час здесь — указатель, а не отдельная сводка: резать по часам сами блоки значило бы рвать сессию пополам. #}
{% for h in d.hour_marks %} {{ h.label }}{{ h.human }} {% endfor %}
{% endif %} {% for b in d.blocks %} {% if loop.first or b.hour != loop.previtem.hour %}
{{ '%02d'|format(b.hour) }}:00
{% endif %}
{{ b.start }} — {{ b.end }} {{ b.human }}{% if b.waiting_human %} + {{ tr('ждал машину {t}').format(t=b.waiting_human) }}{% endif %} {% if b.analyzed %}{{ tr(b.category_label) }}{% endif %} {% if not b.closed %}{{ tr('идёт сейчас') }}{% endif %}
{% if b.analyzed %}

{{ b.title }}

{{ b.summary }}

{% if b.details %}{% endif %} {% else %}

{{ tr('Ещё не разобрано — нажмите «Проанализировать день».') if b.closed else tr('Блок ещё не закрыт, разбор будет после паузы в работе.') }}

{% endif %}
{% for a in b.apps %}{{ a.label }} · {{ a.human }}{% endfor %}
{% if b.shots %}
{% for s in b.shots %} {# в сетке — миниатюра, в просмотрщике (href) — оригинал #} {{ s.time }} {{ s.time }} {% endfor %}
{% endif %}
{% endfor %} {% else %}
{{ tr('За этот день данных нет') }} {% if paused %}{{ tr('Машина стоит на паузе — сбор выключен, пока её не продолжат.') }} {% elif not alive %}{{ tr('Агент на этой машине давно не выходил на связь: проверьте, запущен ли он и есть ли у машины доступ к серверу.') }} {% else %}{{ tr('Либо за компьютером в этот день не работали, либо агент был выключен. Соседние дни листаются стрелками в шапке.') }}{% endif %}
{% endif %}