Собираем дашборд с Claude Code

В прогоне курса первое задание на дашборд агент выполнял 17,2 минуты. Отчёт пришёл бодрый: «часть 1 из 3 готова, продолжить?». Только работал он не в кабинете заявок. Агент нашёл на диске другой проект, где тоже были заявки, и 17 минут менял его файлы: добавил поля в чужую модель данных, создал четыре новых файла и даже завёл задачу в чужом списке дел. Откатывали руками. С этого урок и начинается: прежде чем просить дашборд, очерти агенту, где ему можно работать.

Чему научишься в этом уроке:
• ставить агенту задачу на дашборд с границей папки проекта;
• заполнять кабинет демо-данными, не ломая приём заявок;
• подключать графики файлом из проекта, без внешних серверов;
• сверять число на экране с числом, которое сам получил запросом к базе.

Перед стартом

  1. Открой Claude Code в папке кабинета заявок из П4 (у нас crm-kodium).
  2. Добавь в .env кабинета три строки из .env проекта П3: CLOSEROUTER_API_KEY=<твой ключ>, MODEL_MAIN=…, MODEL_FALLBACK=…. Копируешь сам, в чат агенту ключ не вставляешь. На сервере сделаешь то же перед выкладкой в уроке 6.4.
  3. Сохрани версию: git status чистый, последний коммит есть (урок 1.4). Если агент наломает, вернёшься одной командой.
  4. Если на диске есть другие твои проекты, запомни, что в них сейчас (git status в каждом). После работы агента проверишь, что там ничего не поменялось.
  5. Не разрешай агенту команды «на весь диск» без вопроса. Правила безопасной работы мы разбирали в уроке 1.7. В прогоне агенту было разрешено запускать Python где угодно, и этого хватило, чтобы уйти в чужую папку.

Откуда взять заявки для графиков

Настоящих заявок в учебном кабинете единицы, а на пяти точках график ничего не покажет. Нужны демо-данные: выдуманные заявки, как муляжи продуктов на витрине. Выглядят как настоящие, но все знают, что это муляж. В прогоне это 80 заявок за 8 недель с выдуманными именами и телефонами вида +7900000xxxx, у каждой пометка is_demo=1, и отдельная команда удаляет только их.

Есть соблазн проще: научить приём заявок принимать «прошлую дату», и пусть демо приходят через него. Это та же тестовая лазейка в боевом коде, которую мы вычищали в уроке 5.5. Поэтому демо пишет отдельная команда прямо в базу, а приём не меняется.

И ещё одна хитрость: в демо-данные нарочно закладываем закономерности. Сайт записывает хуже бота, один курс проседает, на 6-й неделе выросло время до первого контакта. Раз мы знаем, что спрятали, мы проверим, видит ли это дашборд, а в уроке 6.4 и модель.

Задание 1. Дашборд у себя на компьютере

Вот задание из прогона, второй заход, с границей папки. Вместо <путь к папке проекта> подставь свой. Пункт 3 про «Выводы недели» агент сделает сразу, а разбирать его будем в уроке 6.4: модель через CloseRouter ты подключал в уроке 4.4, ключ и имена моделей ты положил в .env кабинета перед стартом.

ВАЖНО: работай ТОЛЬКО в текущей папке проекта (<путь к папке проекта>) — это «Кабинет заявок» (main.py, database.py, auth.py, templates, test_main.py, manage.py). Не ищи, не читай и не меняй файлы за её пределами. Если чего-то нет в этой папке — сделай здесь.

Новая задача для «Кабинета заявок» (паттерн «Дашборд»): владелец хочет видеть, как идут дела, и получать выводы недели.
1. Демо-данные: команда `python manage.py seed-demo` пишет прямо в базу 80 выдуманных заявок за последние 8 недель (имена выдуманные, телефоны +7900000xxxx), разные источники (бот/сайт), курсы, статусы и записи журнала смены статусов с правдоподобными задержками; у всех пометка is_demo=1; команда `python manage.py clear-demo` удаляет только их. Заложи закономерности: сайт конвертирует в «записан» хуже бота; один курс проседает; на 6-й неделе выросло время до первого контакта. API приёма для этого НЕ менять и тестовых лазеек в боевой код не добавлять.
2. Страница «Дашборд» (только владелец; менеджеру — 403): 4 цифры недели с изменением к прошлой; воронка новая → в работе → записан/отказ; конверсия по источникам и по курсам; динамика по 8 неделям (Chart.js 4.5.1 — файл положи в static, без CDN); время до первого контакта по журналу (медиана и доля «быстрее 1 часа»). Недели — по МСК. Все цифры считает КОД (dashboard.py).
3. «Выводы недели»: кнопка «Обновить выводы». Модели через CloseRouter (CLOSEROUTER_API_KEY, MODEL_MAIN/MODEL_FALLBACK в .env, адрес https://api.closerouter.dev/v1) уходит JSON только со сводными числами — без имён, телефонов, комментариев и имён менеджеров; разрезы меньше 5 заявок не передавать. Модель пишет 3–5 выводов с опорой на числа. Выводы сохраняются в базу. Каждое число в выводе проверяется по сводке; не найдено — пометка «не подтверждён». В журнал — какая модель ответила и usage.
4. Тесты: подсчёты dashboard.py на известных данных; менеджер — 403; в промпте к модели нет ни одного телефона/имени из базы; проверка «не подтверждён». pytest -q — покажи полный итог (включая skipped). Удалять или пропускать старые тесты нельзя; боевой код не должен знать о тестах.
Пока только локально, на сервер не выкладывай. Выполни seed-demo локально и один раз «Обновить выводы» с настоящей моделью — покажи выводы и сводку, которую получила модель.

Что важно в тексте. Граница папки стоит первой строкой. Chart.js (библиотека, которая рисует графики в браузере) указан с точной версией 4.5.1 и «файл положи в static»: кабинет хранит телефоны клиентов, и подгружать в него код с чужого сервера (CDN, сеть раздачи файлов) незачем. Все цифры считает код в одном файле, а не шаблон страницы и не модель.

Что получилось в прогоне

С границей агент работал 12,2 минуты и за пределы папки не выходил: проверили по времени изменения файлов в соседних проектах. Сделал дашборд, seed-demo и clear-demo на 80 заявок, выводы и 15 тестов. Хорошо? Почти. При проверке нашлись две вещи, которых в отчёте не было.

Первая: графики грузились с внешних серверов, хотя в задании прямо сказано «без CDN». Вторая серьёзнее. В отчёте агент написал «бот 43,2% против сайта 26,5%». А в базе приложения последние выводы модели были построены на сводке, где всё равно нулю, и модель честно писала «показатели равны 0». Цифры отчёта агент взял из отдельного демонстрационного скрипта, а сам дашборд демо-заявок не видел. Красивый отчёт, пустая страница.

Поймали это одним способом: открыли то, что видит пользователь, а не то, что пишет агент. Задание на исправление:

Работай ТОЛЬКО в текущей папке проекта (<путь к папке проекта>).
Проверил результат сам — две проблемы:
1. В таблице insights последняя запись (модель google/gemini-3.6-flash) построена на сводке, где ВСЁ равно нулю: new=0 по всем 8 неделям, by_source и by_course пустые, funnel null — и модель честно пишет «показатели равны 0». А в твоём отчёте — «12 новых заявок, бот 43,2% vs сайт 26,5%». Значит, демонстрация (test_insights_demo.py) считала по другим данным, а дашборд по базе crm.db видит ноль. Найди причину (seed-demo пишет не в ту базу/таблицу? даты не попадают в окно недель? фильтр is_demo?), почини так, чтобы дашборд и «Обновить выводы» в приложении видели 80 демо-заявок. Покажи сводку из приложения (тот же JSON, что уходит модели) и выводы модели по ней. Проверь, нашла ли модель три заложенные закономерности (сайт хуже бота, проседающий курс, рост времени контакта на 6-й неделе) и нет ли в выводах неподтверждённых чисел.
2. Chart.js и htmx грузятся с CDN (jsdelivr, unpkg) — в кабинете с персональными данными внешние запросы не нужны. Положи chart.umd.min.js (4.5.1) и htmx в static и подключи локально.
Тесты: добавь тест «сводка по базе после seed-demo не нулевая». pytest -q — полный итог, без удаления/пропуска старых тестов. На сервер пока не выкладывай.

Причина нуля оказалась в фильтре: дашборд отбрасывал демо-заявки по пометке. Агент починил за 8,1 минуты, и сводка перестала быть нулевой. Но файлы библиотек не скачал: команда скачивания ему не была разрешена. Вместо этого появился файл DOWNLOAD_LIBS.md с инструкцией «скачать оператору». В отчёте шаблоны «переключены на static», а папка static пуста, графики не рисуются. Разрешили скачивание и дали третье задание:

Работай ТОЛЬКО в текущей папке проекта (<путь к папке проекта>).
1. Папка static пуста — графики не работают. Скачай сам (curl теперь разрешён) chart.umd.min.js версии 4.5.1 (https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chart.umd.min.js) и htmx (ту версию, что была в base.html раньше, 1.9.10, или 2.0.4 — главное, чтобы шаблоны работали) в static, проверь размер файлов и что страница дашборда их подключает. Удали DOWNLOAD_LIBS.md.
2. Третью закономерность (рост времени до первого контакта на 6-й неделе) модель найти не могла: в сводке только общая медиана. Добавь в сводку и на график время до первого контакта ПО НЕДЕЛЯМ (медиана за каждую из 8 недель), повтори «Обновить выводы» по базе приложения и покажи: сводку по неделям и выводы модели. Отметь, какие из трёх закономерностей модель назвала и есть ли «не подтверждён».
pytest -q — полный итог, старые тесты не удалять и не пропускать. На сервер пока не выкладывай.

За 6,7 минуты в static легли chart.umd.min.js на 208 522 байта и htmx.min.js на 47 755 байт. Размер проверяй сам: пустой или крошечный файл означает, что скачалась страница ошибки, а не библиотека. Второй пункт важен для всего паттерна: пока на графике была одна общая медиана, сбой на 6-й неделе растворялся в среднем по восьми. Закономерность, которой нет в разбивке, не увидит ни владелец, ни модель.

Снимок середины страницы «Дашборд»: таблица «Конверсия по источникам» (бот 44 заявки, 21 записан, 47.7%; сайт 36, 10, 27.8%), таблица «Конверсия по курсам» (Data Science 16, 4, 25.0%; JavaScript 10, 7, 70.0%; Python 19, 6, 31.6%; SQL 18, 7, 38.9%; Веб-дизайн 18, 7, 38.9%), ниже линейный график «Динамика по неделям» с линиями «Новые» и «Записано» по неделям с 17.08 по 05.10.
Снимок блока «Время до первого контакта»: слева «Медиана (за все недели): 2.1 часов», «Быстрее 1 часа: 22.5%»; справа график «Медиана по неделям» с 17.08 по 05.10, пик на неделе 21.09, обведён и подписан «сбой 21.09».

Сверка: число на экране равно числу из базы

Главная проверка урока занимает пять минут. Возьми три числа со страницы, например «бот 44», «сайт 36» и «записано через бота 21», и получи их сам запросом к базе. Запрос (вопрос к базе на языке SQL) даже не обязательно писать самому, но запускать его будешь ты, а не агент. В Python начиная с версии 3.12 есть встроенная консоль для таких запросов (документация Python). Проверь версию: python --version. Если ниже 3.12, попроси агента выполнить запрос скриптом и показать сам запрос и вывод. Если 3.12 или новее, в папке проекта:

python -m sqlite3 crm.db
SELECT name FROM sqlite_master WHERE type='table';

Это список таблиц. Попроси агента: «Напиши SQL-запрос, который считает заявки и записанных по источникам за те же 8 недель и с тем же отбором, что дашборд. Не запускай, только покажи». Запусти его в той же консоли и сравни с экраном. Совпало: число честное. Не совпало: ищи разницу в определении из урока 6.2 (период, тестовые, демо). Выйти из консоли: .quit.

Правило паттерна: отчёт агента не доказательство. Доказательство это число на странице, которую видит владелец, совпавшее с числом, которое ты сам получил из базы.

Если не получилось

  • Агент пишет про файлы, которых нет в твоей папке, или с чужими путями → остановить, проверить git status в соседних проектах, откатить чужие изменения; в начало задания границу папки.
  • Отчёт с цифрами, а на странице нули → цифры из отдельного скрипта; попроси показать сводку из самого приложения и причину (фильтр, база, окно недель).
  • Графики не рисуются, в static пусто или есть файл «скачать оператору» → разреши скачивание или скачай сам по ссылке из задания; проверь размер файлов.
  • В коде страницы адреса cdn.jsdelivr.net или unpkg.com → повтори «без CDN, файл в static».
  • Закономерность в демо есть, а на графике не видна → не хватает разбивки по неделям или по разрезу; добавь её в сводку и на график.
  • Число на экране не совпало с запросом → сравни определения: период, часовой пояс, тестовые и демо-заявки.

Контрольный вопрос. Агент отчитался «бот 43,2% против сайта 26,5%», а на странице «Дашборд» все числа нули. Чему веришь?

АОтчёту агента: он посчитал
БСтранице, а демо-данные удалю
ВМодели: она напишет правильные выводы
ГНи тому ни другому, пока не получу число запросом к базе и не найду, почему страница видит ноль

Подсказка: Что увидит владелец и откуда агент взял свои цифры?

На курсе задания проверяются автоматически. Решить первое задание бесплатно →  ·  Подобрать курс

Контрольный вопрос. Почему Chart.js кладём файлом в папку static, а не подключаем с внешнего сервера?

АТак графики красивее
БВ кабинете с телефонами клиентов незачем подгружать код с чужого сервера
ВВнешний сервер не отдаёт версию 4.5.1
ГБез файла в static htmx не работает

Подсказка: Какие данные лежат в кабинете, где стоит график?

На курсе задания проверяются автоматически. Решить первое задание бесплатно →  ·  Подобрать курс

Контрольный вопрос. В демо заложен рост времени до контакта на 6-й неделе, но на дашборде его не видно. Что добавить?

АЕщё 80 демо-заявок
БСреднее вместо медианы
ВМедиану времени до контакта по каждой неделе
ГНовую модель для выводов

Подсказка: Где может спрятаться одна плохая неделя из восьми?

На курсе задания проверяются автоматически. Решить первое задание бесплатно →  ·  Подобрать курс

Задание. МИССИЯ «Дашборд у себя». 1. Сделай коммит и запиши git status своих соседних проектов. 2. Дай агенту задание 1 из урока с границей папки. Для своего заказчика замени «Кабинет заявок», курсы и источники на свои, а 4 цифры недели и разрезы возьми из карточек миссии 6.2. Граница папки, демо с пометкой, «API приёма не менять», «файл в static, без CDN», «все цифры считает код» и пункт про тесты остаются как есть. 3. Открой страницу сам. Если видишь нули, пустые графики или внешние адреса библиотек, дай задание на исправление по образцу из урока. 4. Сверь 3 числа страницы запросом к базе (python -m sqlite3). 5. Сломай и почини: отправь в кабинет одну тестовую заявку с источником test. Форма и бот ставят «сайт» и «бот», поэтому попроси агента написать команду curl, которая отправит заявку через приём заявок с source=test. Ключ приёма подставь в команду сам из .env, в чат его не вставляй. Сравни «всего» на старой странице аналитики и на дашборде. Если разошлось, попроси агента, чтобы обе страницы считали «всего» одной функцией по определению из 6.2. Повтори сравнение. Потом удали тестовую заявку под владельцем кнопкой «Удалить» в кабинете (удаление есть только у владельца, урок 5.3). Принято, если: приложены снимок дашборда с графиками, вывод pytest -q, размеры файлов в static, три запроса с результатами рядом с числами страницы, git status соседних проектов без изменений и два снимка «всего» до и после исправления.

Подсказка: Если запрос и страница расходятся, сравни сначала период и часовой пояс, потом тестовые и демо-заявки.

✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.

На курсе задания проверяются автоматически. Решить первое задание бесплатно →  ·  Подобрать курс

Что дальше

Дашборд работает у тебя на компьютере, числа сверены. В следующем уроке разбираем «Выводы недели»: что можно отдать модели, как проверять её числа и почему проверка иногда ошибается. Там же выкладываем дашборд на сервер в Москве.

Итог:
• граница папки стоит в начале каждого задания, после работы проверяешь соседние проекты;
• демо-данные с пометкой и отдельной командой, приём заявок не трогаем;
• графики файлом в static, размер файлов проверен;
• число на экране сверено с числом из базы, а не с отчётом агента.

← Назад  ·  ↑ В начало урока  ·  ⌂ В начало курса  ·  Вперёд →

Школа Виктора Комлева