{% extends "base.html" %} {% from "_macros.html" import icon %} {% from "_macros_ui.html" import empty_state, task_status_badge, task_status_text %} {% block title %}Планировщик — TG Agent{% endblock %} {% block content %}

Планировщик

{% macro task_type_label(t) %} {% if t.task_type.value == 'channel_collect' %}Сбор канала {% elif t.task_type.value == 'stats_all' %}Статистика {% elif t.task_type.value == 'sq_stats' %}Статистика запроса {% elif t.task_type.value == 'photo_due' %}Фото {% elif t.task_type.value == 'photo_auto' %}Автофото {% else %}{{ t.task_type.value }} {% endif %} {% endmacro %} {# Preserve user's filter/page/limit on every POST — the route handlers read these via request.query_params and echo them in the redirect Location. Fix for #457 round 3: clicking "Запустить" used to reset the page to the default `status=all` (all 7000+ tasks, 143 pages) even if the user was on `status=active`. #} {% set _q_parts = [] %} {% if status_filter and status_filter != 'all' %}{% set _ = _q_parts.append('status=' ~ status_filter) %}{% endif %} {% if page and page != 1 %}{% set _ = _q_parts.append('page=' ~ page) %}{% endif %} {% if limit and limit != 50 %}{% set _ = _q_parts.append('limit=' ~ limit) %}{% endif %} {% set filter_qs = ('?' ~ _q_parts|join('&')) if _q_parts else '' %}
Планировщик ?
Автоматическое создание задач: сбор каждые {{ interval_minutes }} мин.

Статус: {{ "Запущен" if is_running else "Остановлен" }}

{% if not is_running %}
{% else %}
{% endif %}
{% if pending_collect_count > 0 %}
{% endif %}
{% if collector_health.state != 'healthy' or collector_health.load_level != 'ok' %}
Здоровье коллектора {% if collector_health.state == 'worker_down' %} Telegram-воркер не запущен {% elif collector_health.state == 'all_flooded' %} Все аккаунты во Flood Wait {% elif collector_health.state == 'no_clients' %} Нет доступных клиентов {% elif collector_health.state == 'degraded' %} Частичная деградация {% endif %} {% if collector_health.load_level == 'overload' %} Перегрузка {% elif collector_health.load_level == 'high' %} Высокая нагрузка {% endif %}
Аккаунты
{{ collector_health.available_accounts_now }} доступно сейчас
{{ collector_health.connected_accounts }} подключено
Flood Wait
{{ collector_health.flooded_accounts_count }} заблокировано
{% if collector_health.next_available_label %} {% set _secs = collector_health.retry_after_sec %}
Раньше всего: {{ collector_health.next_available_label }} {%- if _secs and _secs >= 60 %} {% set _h = (_secs // 3600) | int %} {% set _m = ((_secs % 3600) // 60) | int %} ({% if _h > 0 %}{{ _h }} ч {% endif %}{{ _m }} мин) {%- endif %}
{% endif %}
Нагрузка
{{ collector_health.active_unfiltered_channels }} активных каналов
Интервал: {{ collector_health.collect_interval_minutes }} мин
Симптом
{{ collector_health.recent_zero_collect_count }} недавних задач с нулевым результатом
{% if collector_health.is_running %}Коллектор занят{% else %}Коллектор ждёт{% endif %}
{% if collector_health.state == 'worker_down' %}

Почему сейчас не собираем: веб-процесс принимает задачи и пишет их в БД, но Telegram-коннекты держит отдельный воркер-процесс — он сейчас не запущен (нет свежего heartbeat в runtime_snapshots). Задачи из collection_tasks копятся в статусе pending, но никто их не исполняет.

Как починить: запустите воркер во втором терминале — он подхватит все накопившиеся задачи автоматически:

python -m src.main worker
{% endif %} {% if collector_health.flooded_accounts %}

Почему сейчас не собираем: доступных аккаунтов нет или их недостаточно. Flood Wait активен для: {% for item in collector_health.flooded_accounts %} {{ item.phone }}{% if not loop.last %}, {% endif %} {% endfor %}

{% endif %} {% if collector_health.recommendations %}

Что делать:

{% endif %} {% if collector_health.recent_unavailability_events %}

Последние причины недоступности:

{% endif %}
{% endif %} {% if scheduler_jobs %}
Джобы планировщика {{ scheduler_jobs|length }} {% if not is_running %} (будут зарегистрированы при запуске) {% endif %}
{% for j in scheduler_jobs %} {% endfor %}
Вкл. Джоб Интервал Следующий запуск
{{ j.label }} {% if j.interval_editable and j.interval_minutes is not none %}
{% elif j.interval_minutes %} {{ j.interval_minutes }} мин. {% else %} — {% endif %}
{% if j.next_run %} {{ j.next_run|local_dt("time") }} {% elif is_running and j.enabled %} — {% elif not j.enabled %} отключён {% else %} остановлен {% endif %}
{% endif %} {% if search_log %}
Лог поиска ?
{% for entry in search_log %} {% endfor %}
Запрос Аккаунт Результатов Дата
{{ entry.query }} {{ entry.phone }} {{ entry.results_count }} {{ entry.created_at|local_dt }}
{% for entry in search_log %}
{{ entry.query }} {{ entry.results_count }}
{{ entry.phone }} · {{ entry.created_at|local_dt }}
{% endfor %}
{% endif %} {% if all_count > 0 %}
Задачи

Все задачи: сбор каналов, статистика, фото.

{% if tasks %}
{% for t in tasks %} {% endfor %}
Тип Канал / Описание Статус {{ result_column_title }} Создана Завершена Действие
{{ task_type_label(t) }} {{ t.channel_title or t.channel_id or '—' }} {% if t.channel_username %}
@{{ t.channel_username }} {% endif %}
{{ task_status_text(t) }} {% set pipeline_meta = pipeline_result_meta.get(t.id) if pipeline_result_meta else None %} {% if t.task_type == 'stats_all' and t.payload and t.payload.channel_ids %} {{ t.messages_collected }}/{{ t.payload.channel_ids|length }} {% elif t.task_type.value == 'pipeline_run' and pipeline_meta %} {{ pipeline_meta.label }} {{ pipeline_meta.count }} {% if pipeline_meta.errors_count %} ⚠ {{ pipeline_meta.errors_count }} {% endif %} {% else %} {{ t.messages_collected }} {% endif %} {{ t.created_at|local_dt }} {{ t.completed_at|local_dt }} {% if t.status == 'running' %}
{% elif t.status == 'pending' %}
{% endif %}
{% for t in tasks %}
{{ task_type_label(t) }} {{ t.channel_title or t.channel_id or '—' }} {% if t.channel_username %}
@{{ t.channel_username }}{% endif %}
{{ task_status_badge(t) }}
{% set pipeline_meta = pipeline_result_meta.get(t.id) if pipeline_result_meta else None %} {% if t.task_type == 'stats_all' and t.payload and t.payload.channel_ids %} {{ t.messages_collected }}/{{ t.payload.channel_ids|length }} {% elif t.task_type.value == 'pipeline_run' and pipeline_meta %} {{ pipeline_meta.label }}: {{ pipeline_meta.count }} {% if pipeline_meta.errors_count %} ⚠ {{ pipeline_meta.errors_count }} {% endif %} {% else %} {{ t.messages_collected }} сообщ. {% endif %} {% if t.created_at %}· {{ t.created_at|local_dt }}{% endif %} {% if t.status == 'running' or t.status == 'pending' %}
{% endif %}
{% endfor %}
{% if total_pages > 1 %} {% endif %} {% else %} {{ empty_state("Нет задач с выбранным фильтром.") }} {% endif %}
{% endif %} {% endblock %}