О проекте
Команда: Nomad coders
Кейс HackAlem.ai: Логистика — Электрокомплект · ekt.kz
Стадия: Доведённый продукт
Задача кейса: Автоматический расчёт заказов поставщикам для пополнения склада, с утверждением менеджером
Суть
Веб-приложение для менеджера закупа. Оно импортирует шесть XLSX поставщика (IEK или Systeme Electric), рассчитывает объяснимую потребность к заказу и ведёт менеджера от рекомендации к версионированному черновику, согласованию и экспорту CSV.
Как устроено
Ingestion нормализует Excel. Движок demand/planning считает спрос: 12-месячный профиль сезонности, нормированный к среднему 1, тренд по очищенной истории, восстановление скрытого спроса только на подтверждённых интервалах stockout, исключение одиночных крупных покупок. Потребность = спрос × (срок + пересмотр) + страховой запас − остаток − приход, с округлением по MOQ и кратности. Строки без данных возвращают null и блокируются. Заказы с версиями и причинами правок хранятся в SQLite или PostgreSQL, исходная рекомендация не перезаписывается. LLM не используется.
Ключевые решения
- seasonality profile normalization
- trend estimation
- stockout demand reconstruction
- one-off outlier exclusion
- lead time + review period + safety stock
- MOQ/lot-size rounding
- versioned order drafts
- CSV export
Особенности
Полностью детерминированное и объяснимое решение без LLM. Ноль (заказ не нужен) и null (нет данных) принципиально различаются. В README есть пошаговый сценарий для жюри с ожидаемыми числами (SYN-1 = 144 шт, а при страховом запасе 14 дней — 216). Есть e2e-тесты на Playwright и скриншоты на 1366 и 390 px.
Стек
Python · React · TypeScript · Vite · SQLite · PostgreSQL · Docker · Playwright · GitHub Actions · PowerShell · Render
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-43bfd950-nomad-coders.
README
README.mdNomad Coders — объяснимое планирование закупок
HackAlem AI · кейс Электрокомплекта · веб-приложение для менеджера закупа
Nomad Coders помогает ответить на три практических вопроса: что заказать, сколько заказать и почему именно столько. Приложение объединяет историю продаж, остатки и ожидаемые поставки, рассчитывает потребность и проводит менеджера от рекомендации до согласованного заказа.
Каждое количество можно проверить по исходным величинам и правилам расчёта. Менеджер может изменить рекомендацию, указать причину и сохранить собственное решение. Исходные наблюдения и первоначальная рекомендация при этом не переписываются.
Проверка для комиссии
С чего начать
Приложение предназначено для самостоятельного запуска проверяющими. Подключение к компьютеру команды, Docker капитана и постоянно работающий сервер команды не требуются.
- Получите доступ к репозиторию и клонируйте
mainпо разделу «Локальный запуск с нуля». - Выберите запуск через Python/Node либо Docker. Docker сам собирает приложение и загружает синтетические примеры.
- Откройте
http://127.0.0.1:8000для Python/Node либоhttp://127.0.0.1:8080для Docker и пройдите сценарий ниже. - Если требуется общий доступ по HTTPS, используйте раздел «Самостоятельное развёртывание».
В приложении регистрация не требуется. Демонстрационные данные полностью вымышлены; рекомендации основного учебного набора вычисляет настоящий статистический движок. Оригиналы партнёра для этого сценария не нужны. Ранее использованный временный туннель не входит в требования запуска и не является основным способом проверки.
Проверить основной сценарий за 3–5 минут
Откройте «1. Данные и настройки». Если браузер восстановил прежний заказ, просто перейдите на этот экран.
| Шаг | Действие | Ожидаемый результат |
|---|---|---|
| 1 | Выберите «Расчёт закупки · синтетические наблюдения» | Видна пометка синтетических данных; настройки доступны для изменения |
| 2 | Установите дату 22.09.2026, срок 14, пересмотр 7, страховой запас 7, источник «Транзакции». Нажмите «Получить рекомендации» | SYN-1 — 144 шт; SYN-2 — 0 шт / «Заказывать не нужно»; SYN-3 — «Нужны данные», выбор в заказ отключён |
| 3 | Откройте объяснение SYN-1 | Видны спрос, остаток, приход, потребность до округления, кратность, график, решения по аномалиям и допущения |
| 4 | Проверьте поиск по SKU, фильтр срочности и сброс фильтров | Таблица меняется; нулевая потребность и неизвестное количество остаются разными состояниями |
| 5 | Нажмите «Создать черновик», измените SYN-1 с 144 на 0, укажите причину и нажмите «Сохранить правки» | Новая версия черновика содержит 0; исходная рекомендация остаётся 144 |
| 6 | Обновите страницу | Заказ и сохранённая правка восстанавливаются |
| 7 | Нажмите «Согласовать заказ», затем «Скачать CSV» | Браузер скачивает CSV согласованной версии с нулевым количеством и причиной правки |
| 8 | Измените согласованный заказ на 12 и сохраните | Заказ снова становится черновиком; для следующего экспорта требуется новое согласование |
| 9 | Вернитесь к данным, увеличьте страховой запас с 7 до 14 и выполните новый расчёт | Рекомендация SYN-1 увеличивается до 216 шт; прежний расчёт и заказ не переписываются |
В базовом примере спрос равен 10 шт/день. Горизонт — 21 день, страховой запас — ещё 7 дней, остаток — 100, подходящий приход — 40. Потребность: 210 + 70 − 100 − 40 = 140 шт; округление по кратности 12 даёт 144 шт. В истории отдельно присутствуют разовая покупка 10 000 шт и шесть подтверждённых дней отсутствия товара: объяснение показывает исключение выброса и восстановление 60 шт исторического спроса. Восстановленные продажи не прибавляются повторно к заказу как задолженность.
CSV обычно попадает в папку «Загрузки». Название — order-<идентификатор>-v<версия>.csv. Файл содержит UTF-8 BOM и разделитель ;; при необходимости выберите эти параметры при импорте в Excel. «Сохранить правки» сохраняет заказ в базе, а «Скачать CSV» создаёт файл на компьютере проверяющего.
Второй набор, «Учебный склад · синтетика», — фиксированный пример проверки соединения компонентов. Его настройки заблокированы намеренно. Для проверки пересчёта используйте первый набор.

Задача и границы решения
В предоставленных данных продажи, остатки, сезонность и ограничения заказа находятся в разных книгах Excel. Они описывают разные периоды и не всегда содержат все необходимые сведения. Простое суммирование отчётов или подстановка нулей вместо пропусков может дать необоснованное количество к закупке.
Мы разделили решение на три этапа: проверка и нормализация данных → воспроизводимый расчёт → решение менеджера. Приложение показывает проблемы входных данных и блокирует строки, для которых количество нельзя обосновать. Это инструмент поддержки закупки; окончательное решение остаётся за человеком.
Покрытие пяти требований кейса
| Требование | Реализация | Как проверить |
|---|---|---|
| Базовый заказ по данным поставщика | История спроса, доступный остаток, срок поставки, интервал пересмотра, буфер, категория, датированные приходы, минимум и кратность | Расчёт SYN-1 и числовое объяснение; локальный импорт известных XLSX |
| Сезонность и устойчивый рост | Профиль 12 месяцев нормируется к среднему 1; рост/снижение оценивается по очищенной истории полных месяцев | Компоненты и допущения ответа; автоматические тесты сезонности и устойчивого роста |
| Скрытый спрос при отсутствии товара | Восстановление только по подтверждённым интервалам stockout; пересекающиеся интервалы не учитываются дважды | Восстановленные 60 шт в учебном SYN-1; тесты пересечений и противоречий |
| Разовые крупные покупки | Анализ объёма документа, исключение одиночного всплеска, сохранение повторяемых покупок; ID клиента учитывается при наличии | Раздел «Крупные покупки и аномалии», документ 10 000 шт; тесты регулярных крупных покупок |
| Объяснимый заказ поставщику | Фильтры по поставщику, один поставщик на черновик, причины расчёта, правки, версии, согласование и CSV | Полный сценарий выше, включая сохранение нуля и повторное согласование |
0 и отсутствие значения принципиально различаются. Ноль означает, что по выбранным входам и настройкам новый заказ не требуется. null отображается как нехватка данных; такую строку нельзя включить в заказ.
Локальный запуск с нуля
1. Подготовить инструменты
Нужны Git, Python 3.12 и Node.js 22.12 или новее. Рабочее окружение команды: Python 3.12.14, Node.js 24.19.0, npm 10.9.0. Для воспроизведения удобно использовать Node 24. Docker, аккаунты облачных сервисов и ключи внешних API для обычного локального запуска не нужны.
После установки инструментов откройте новое окно PowerShell и проверьте:
git --version
py -3.12 --version
node --version
npm.cmd --version
Если git не найден — установите Git и заново откройте терминал. Если py -3.12 сообщает No suitable Python runtime found, установите именно Python 3.12. Setup также допускает явный путь: -Python 'C:\путь\к\python.exe'.
2. Получить репозиторий
Репозиторий приватный: для клонирования нужен доступ GitHub, предоставленный организаторами. После получения исходников запуск не зависит от аккаунта или компьютера капитана.
Команды ниже предназначены для новой папки, которой ещё нет:
cd "$env:USERPROFILE\Documents"
git clone https://github.com/BAITC-Hacks/hack-43bfd950-nomad-coders.git nomad-coders-review
cd nomad-coders-review
git switch main
3. Установить зависимости и подготовить демонстрацию
Все дальнейшие команды выполняются из корня репозитория:
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/setup.ps1 -SkipBrowser
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/seed.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/build.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/serve.ps1 -Demo
setup создаёт отдельное Python-окружение .venv, устанавливает зависимости из lock-файлов и проверяет совместимость версий. seed добавляет два явно синтетических набора; повторный запуск не создаёт дублей и не удаляет заказы. build собирает интерфейс, serve запускает API и собранный сайт.
4. Открыть приложение
- Приложение: http://127.0.0.1:8000
- Документация API: http://127.0.0.1:8000/docs
- Проверка сервера: http://127.0.0.1:8000/api/health
Оставьте терминал сервера открытым. Остановка — Ctrl+C. Заказы сохраняются в runtime/nomad.sqlite3 и переживают перезапуск. Адрес 127.0.0.1 доступен только на компьютере, где запущен сервер.
Альтернатива: запуск через Docker
Если установлен и запущен Docker с Linux-контейнерами, Python и Node на компьютере не требуются. Из корня клонированного репозитория:
docker build -t nomad-coders:demo .
docker run -d --name nomad-review -p 127.0.0.1:8080:8000 -e NOMAD_DEMO=1 --mount type=volume,source=nomad-review-data,target=/app/runtime nomad-coders:demo
Откройте http://127.0.0.1:8080. В этом локальном варианте SQLite сохраняется в Docker volume nomad-review-data. Команда docker run нужна только при первом создании контейнера; далее:
docker logs nomad-review
docker stop nomad-review
docker start nomad-review
Загрузка настоящих данных
Оригиналы партнёра не включены в репозиторий и публичную демонстрацию. Для проверки реальных входов нужны предоставленные организаторами файлы и локальный запуск.
В разделе «Загрузка Excel» выберите Systeme Electric или IEK и загрузите комплект из шести XLSX этого поставщика. Адаптеры рассчитаны на известные структуры исходных книг, а не на произвольную таблицу. Максимальный общий размер HTTP-загрузки — 30 МиБ. Набор получает версию и сведения об источниках; повторная загрузка одинакового содержимого возвращает существующий набор.
ZIP-архив можно импортировать локальной командой. Замените путь своим; архив распаковывать в репозиторий не нужно:
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/import.ps1 -Supplier systeme -Archive "$env:USERPROFILE\Downloads\Systeme electric.zip" -AsOf 2026-09-22
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/import.ps1 -Supplier iek -Archive "$env:USERPROFILE\Downloads\IEK.zip" -AsOf 2026-09-22
Оба исходных архива проверены локально: 724 товара и 77 312 транзакций Systeme; 3 185 товаров и 171 603 транзакции IEK. Эти числа описывают прочитанные записи, включая проблемные, а не количество безусловно готовых заказов.
При работе с источниками мы сохраняем следующие различия:
- Строковые SKU и артикулы сохраняют ведущие нули. Пропуск не становится нулём, отрицательная операция не становится положительной продажей.
- Месячные продажи и транзакции одного периода не суммируются. Расхождения отражаются в предупреждениях.
- У Systeme используется свободный остаток AZ; резерв не вычитается второй раз. Даты входящих поставок читаются в том числе из комментариев Excel.
- У IEK месячный остаток не является подтверждённым текущим снимком на 22 сентября. Такие рекомендации блокируются до уточнения остатка.
- Минимальная отгрузка и кратность упаковки — независимые ограничения. Неопределённое преобразование упаковок в метры не выполняется автоматически.
Подробное описание входов и ограничений: docs/DATA_NOTES.md.
Как рассчитывается рекомендация
Движок statistical-v1 выполняет воспроизводимый расчёт без обязательных вызовов LLM. При одинаковых нормализованных данных и настройках он возвращает одинаковые рекомендации; идентификаторы и время сохранения нового расчёта назначаются отдельно.
- Выбор истории. Используются транзакции либо полные месячные наблюдения. Неполные месяцы не выдаются за полные; неизвестные значения не включаются как нулевые продажи.
- Очистка крупных покупок. Строки агрегируются в документы. При достаточной истории порог кандидата в выброс —
max(Q3 + 3 × IQR, 5 × медиана), где IQR — разность 75-го и 25-го процентилей. Повторяемость проверяется отдельно: регулярные крупные покупки не исключаются только из-за объёма. Решение и причина сохраняются. - Сезонность и stockout. Сезонность снимается с истории и один раз применяется к прогнозным дням. Подтверждённые дни отсутствия товара исключаются из времени доступности при оценке спроса; потерянный исторический спрос восстанавливается для объяснения. Нулевого месячного остатка недостаточно, чтобы объявить весь месяц stockout.
- Устойчивое изменение спроса. При наличии шести последовательных полных месяцев сравниваются последние три с предыдущими тремя. Автоматический коэффициент ограничивается диапазоном 0,5–2 с предупреждением при ограничении. При недостаточной истории рост не придумывается.
- Потребность по дням. Горизонт равен сроку поставки плюс интервал пересмотра. Для каждого дня учитываются прогноз, буфер, доступный остаток и только поступившие к этому дню поставки. Берётся максимальная положительная потребность: поздний приход не может покрыть дефицит предыдущего дня. Дефицит до обычного срока поставки помечается как срочный.
- Ограничения заказа. При положительной потребности применяются минимальная отгрузка и округление вверх по подтверждённой кратности. Ноль остаётся нулём. При блокирующих проблемах возвращается неизвестное количество с объяснением.
Через API дополнительно доступны буфер по категории, явный текущий остаток и ручной коэффициент роста с причиной. Отдельные формы для всех этих переопределений пока не реализованы в интерфейсе.
Почему выбран этот стек
В условиях пятичасовой разработки мы выбрали один backend, один frontend и общий контракт данных. Это позволило разрабатывать интерфейс и расчёты параллельно, а после сборки обслуживать приложение по одному адресу.
| Технология | Роль и основание выбора |
|---|---|
| React + TypeScript | Формы, таблицы, фильтры и состояния заказа; типизация помогает согласовать интерфейс с API |
| Vite + Tailwind CSS | Быстрый цикл разработки и единое сдержанное оформление; Tailwind подключён официальным Vite-плагином |
| Recharts | Исходная, очищенная, восстановленная история и прогноз; график загружается отдельно от основного интерфейса |
| Python 3.12 + FastAPI + Uvicorn | HTTP API и интеграция импорта, расчёта и хранения в одном приложении |
| Pydantic + OpenAPI | Валидация данных и единый источник схем; TypeScript-типы генерируются автоматически |
| openpyxl | Чтение известных XLSX, включая комментарии с датами поставок |
| SQLite / PostgreSQL | SQLite даёт простой локальный запуск; PostgreSQL поддерживает хранение в отдельном сервисе и транзакционные версии заказов |
| pytest + HTTPX + Playwright | Проверка математики, HTTP-поведения, сохранения и настоящего браузерного сценария до скачивания CSV |
| Docker | Воспроизводимая Linux-сборка frontend и backend с зафиксированными зависимостями |
pandas и NumPy включены в подготовленное Python-окружение; текущая логика движка написана преимущественно на стандартной библиотеке, импорт — на openpyxl. Мы не заявляем их использование там, где расчёт обходится без них.
Статистический подход выбран ради проверяемых чисел, повторяемости и независимости от внешних ключей, задержек и генерации текста. Кодекс использовался как инструмент разработки и проверки, но не является сервисной зависимостью приложения.
Архитектура и контракт API
flowchart LR
A["XLSX поставщика"] --> B["Импорт и проверка"]
B --> C["Нормализованный набор с версией"]
C --> D["Статистический расчёт"]
D --> E["Рекомендации и объяснения"]
E --> F["Черновик и правки менеджера"]
F --> G["Согласованная версия → CSV"]
C --> H[("SQLite / PostgreSQL")]
D --> H
F --> H
Расчёт не читает файлы, базу или сеть: он получает нормализованный набор и настройки. HTTP, хранение, импорт и экспорт отделены от математических функций.
backend/app/
contracts/ Pydantic-модели
ingestion/ Распознавание и нормализация XLSX
engine/ Спрос, аномалии, сезонность и потребность
storage/ SQLite и PostgreSQL
export/ CSV и экранирование текстовых формул
service.py Связь компонентов и правила версий заказа
main.py HTTP API и собранный frontend
frontend/src/
pages/ Данные, рекомендации, заказ
components/ Объяснение, график и общие элементы
api/ Единый клиент и сгенерированные типы
contracts/ OpenAPI и синтетические JSON-примеры
scripts/ PowerShell-команды установки и проверки
docs/ Методика источников, проверка и деплой
| Маршрут | Назначение |
|---|---|
GET /api/health | Состояние сервера и режим демонстрации |
GET /api/datasets, POST /api/datasets | Список и загрузка наборов |
POST /api/calculations, GET /api/calculations/{id} | Новый расчёт и чтение сохранённого результата |
GET /api/calculations/{id}/items/{item_id} | Объяснение отдельного товара |
POST /api/orders, GET /api/orders/{id}, PATCH /api/orders/{id} | Создание, чтение и правки заказа |
POST /api/orders/{id}/approve | Согласование конкретной версии |
GET /api/orders/{id}/export?revision=3&format=csv | Экспорт текущей согласованной версии |
Правка передаёт ожидаемую revision, создаёт новую версию и снимает согласование. Согласование также увеличивает версию. Конкурирующая устаревшая правка возвращает 409, не перезаписывая чужое решение. 404 означает отсутствующий ресурс, 422 — неверные входы, 403 — запрет импорта в публичном демо. Изменение настроек фиксированного примера возвращает явную 501. Ошибки имеют общий формат error.code, error.message, error.details.
CSV формируется на сервере из согласованной версии. Текстовые поля экранируются от интерпретации как формул Excel. Автоматической отправки поставщику нет.
Проверки и воспроизводимость
Фактически выполнены:
| Проверка | Результат |
|---|---|
| Backend с настоящей локальной PostgreSQL | 53 passed |
| Backend без тестового PostgreSQL-подключения | 51 passed, 2 skipped |
| Объединённый frontend: локальные браузерные сценарии | 13 passed |
| Публичный HTTPS: пересчёт, сохранение, согласование и CSV | 1 passed |
| TypeScript, production-сборка и Linux Docker-образ | Успешно |
| Сохранение заказа после рестарта контейнера | Подтверждено |
Математические проверки покрывают поздние приходы, минимум и кратность, ноль и пропуск, знаки возвратов, единицы, разовые и регулярные покупки, рост, сезонность и stockout. Браузерные сценарии проверяют также конфликты версий, ошибки сети, таблицу из 1001 товара и размеры экрана от телефона до ноутбука. Успешная сборка не используется вместо проверки поведения.
Для повторения обычных проверок, из корня:
# Установить зависимости и браузер Chromium для Playwright
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/setup.ps1
# Backend, TypeScript, сборка и 13 локальных браузерных сценариев
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/check.ps1 -Browser
# Перегенерировать контракт и убедиться в отсутствии расхождений
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/contracts.ps1
git diff --exit-code -- contracts frontend/src/api/generated
Порт 8009 должен быть свободен: локальные браузерные проверки создают отдельный временный сервер и SQLite. Две проверки PostgreSQL требуют NOMAD_TEST_DATABASE_URL с адресом отдельной тестовой базы; они создают и удаляют только собственные случайные схемы. GitHub Actions настроен на проверки Windows и PostgreSQL под Linux; локальные результаты не означают автоматически успешный удалённый запуск CI.
Если вы самостоятельно развернули публичный режим, повторить проверку его HTTPS-страницы можно так. Замените адрес своим:
$env:NOMAD_E2E_URL = 'https://YOUR-SERVICE.onrender.com'
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/check.ps1 -Browser
Remove-Item Env:NOMAD_E2E_URL
При заданном адресе вместо 13 локальных сценариев выполняется один сценарий публичного демо. Он создаёт явно синтетический проверочный заказ. Отдельная HTTP-проверка без браузера:
.venv/Scripts/python.exe backend/scripts/smoke.py https://YOUR-SERVICE.onrender.com --state runtime/public-smoke.json
Режим разработки и настройки
После setup и seed откройте два терминала в корне проекта.
Терминал 1 — API:
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/dev.ps1 -Target backend -Demo
Терминал 2 — frontend:
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/dev.ps1 -Target frontend
Откройте http://127.0.0.1:5173. Vite направляет /api к FastAPI на 8000. Для проверки production-варианта остановите dev-backend, выполните scripts/build.ps1, затем scripts/serve.ps1 -Demo.
| Переменная | Назначение |
|---|---|
NOMAD_DB | Путь SQLite; по умолчанию runtime/nomad.sqlite3 |
DATABASE_URL | Подключение PostgreSQL; при наличии используется вместо SQLite |
NOMAD_DEMO=1 | Разрешить доступ к синтетическим наборам |
NOMAD_PUBLIC_DEMO=1 | Только синтетика; импорт и чтение настоящих данных закрыты. Требует PostgreSQL и включённого демо |
PORT | Порт контейнера; по умолчанию 8000 |
Приложение читает переменные окружения процесса. .env автоматически не загружается. Строки подключения не должны попадать в Git, frontend или публичные логи. Python-зависимости закреплены в backend/requirements.lock, frontend — в frontend/package-lock.json.
Ограничения и дальнейшее развитие
Результат хакатона — работающий прототип с объяснимым расчётом и проверенным сценарием заказа. Его ограничения обозначены явно:
- Нет оценки точности на отложенном периоде. Полнота журнала с начала 2025 года — допущение известного адаптера, отражённое в предупреждениях. Следующий шаг — временное тестирование прогноза и сравнение с базовыми методами.
- В оригиналах нет точных stockout-интервалов и ID клиентов. Соответствующая логика проверена на синтетике; отсутствующие реальные сведения не выдумываются. Поддержаны дополнительные
customer_idи книга с колонкамиsku/start/end. - Качество входов ограничивает расчёт. Устаревший остаток, неизвестные единицы и противоречия могут заблокировать строку. Загрузка файла не равна подтверждению его полноты и корректности.
- Не все настройки выведены в UI. Ручной остаток, рост и буферы категорий доступны через контракт API; их формы — следующий этап.
- Публичное демо не является личным кабинетом. Нет авторизации и разделения пользователей; демонстрационные заказы общие. Импорт реальных данных доступен локально.
- Экспорт — CSV. XLSX-экспорт, список всех сохранённых заказов, подтверждённый формат импорта 1С и автоматическая отправка поставщикам не реализованы. Браузер восстанавливает последний расчёт и заказ.
Самостоятельное развёртывание
Для проверки на одном компьютере достаточно Docker-команд выше: данные находятся в отдельном volume и сохраняются между остановками контейнера. Для общего HTTPS-адреса подготовлено размещение на Render Free + Neon Free. Это инструкция для проверяющего или администратора; существующий облачный сервис команды не требуется.
Вариант A: из репозитория
- Создайте проект PostgreSQL на Neon Free и получите строку подключения с
sslmode=require. - В Render выберите New → Web Service → Git Provider, подключите репозиторий и ветку
main. Для приватного репозитория организации её владелец должен разрешить доступ приложению Render. - Выберите Docker, регион Frankfurt, тариф Free. Root Directory оставьте пустым, Dockerfile Path —
./Dockerfile, Health Check Path —/api/health. Build/Start Command не переопределяйте: запуск задан в Dockerfile. - В секретах/переменных Render задайте
DATABASE_URLиз Neon,NOMAD_DEMO=1,NOMAD_PUBLIC_DEMO=1. - Нажмите Deploy Web Service, дождитесь Live и откройте выданный HTTPS-адрес. Выполните сценарий проверки из начала README.
Альтернативно Render Blueprint читает готовый render.yaml и запрашивает DATABASE_URL отдельно. Создавать оба варианта одновременно не нужно.
Вариант B: из приватного образа без интеграции GitHub
Если доступа Render к GitHub организации нет, заранее создайте Private-репозиторий nomad-coders в своём Docker Hub. Из корня исходников выполните, заменив YOUR_DOCKER_ID своим именем:
docker login
docker build --platform linux/amd64 -t YOUR_DOCKER_ID/nomad-coders:hackalem-v1 .
docker push YOUR_DOCKER_ID/nomad-coders:hackalem-v1
В Render выберите New → Web Service → Existing Image, укажите docker.io/YOUR_DOCKER_ID/nomad-coders:hackalem-v1 и доступ на чтение приватного образа. Далее выберите Free, настройте /api/health и три переменные из варианта A. Готовому образу не нужен доступ Render к GitHub; исходный репозиторий остаётся приватным. Для следующей версии после docker push требуется Manual Deploy → Deploy latest reference.
Секреты вводятся непосредственно в кабинеты сервисов. Не используйте локальный host.docker.internal как адрес базы в облаке. Подробная инструкция, включая права токена и проверку сохранения после рестарта: docs/DEPLOYMENT.md.
После размещения сайт работает на хостинге и не зависит от Docker на компьютере команды. На бесплатном Render возможен холодный запуск после простоя; PostgreSQL нужен потому, что локальные файлы бесплатного сервиса не сохраняются. До завершения оценки необходимо сохранять сервис, базу и доступ к приватному образу. Постоянное размещение на Render/Neon в составе сдачи не заявляется выполненным; проверены сборка, локальный PostgreSQL и HTTP/браузерный сценарий.
Работа команды
Разработка велась в Codex desktop с разделением ответственности: капитан — backend, данные, контракты и интеграция; тиммейт — frontend в отдельной рабочей папке и ветке. Общие схемы изменяет интегратор; TypeScript-типы генерируются из Pydantic. Изменения объединялись в main после проверок. Независимые проверки кода использовались для поиска ошибок в расчётах и сохранении заказов.
| Документ | Для чего читать |
|---|---|
| Проверка сайта | Расширенный сценарий приёмки и ожидаемые результаты |
| Контракт | Схемы, значения полей и правила версий |
| Источники данных | Наблюдения по исходным книгам и ограничения |
| План backend | Реализация и результаты проверок импорта и расчёта |
| Роли команды | Ответственность и порядок интеграции |
| Общий план | Ход реализации и принятые решения |
Публикация кода и демонстрационной ссылки не заменяет сдачу решения на платформе HackAlem. Команда отдельно передаёт материалы по правилам организаторов.
Авторы коммитов
История коммитов
- 2026-09-23 17:55docs: guide independent jury testing and deploymentAkai Mansur
- 2026-09-23 17:40docs: prepare complete jury guide and technical READMEAkai Mansur
- 2026-09-23 17:23docs: publish verified demo link and judge walkthroughAkai Mansur
- 2026-09-23 17:20feat: integrate frontend and verify public demo workflowAkai Mansur
- 2026-09-23 17:11Merge remote-tracking branch 'origin/codex/web-ui' into codex/api-integrationAkai Mansur
- 2026-09-23 17:11feat: prepare free persistent demo deploymentAkai Mansur
- 2026-09-23 17:07feat(web): deliver procurement UI with filters explanations and CSV workflowsatokazuma51
- 2026-09-23 17:00feat: import supplier workbooks and calculate replenishmentAkai Mansur
Все коммиты (15)
- 2026-09-23 16:16fix: fall back when Python launcher has no 3.12 runtimeAkai Mansur
- 2026-09-23 16:08docs: hand off frontend design and free deployment planAkai Mansur
- 2026-09-23 15:58feat: complete persistent demo ordering workflowAkai Mansur
- 2026-09-23 15:36feat: bootstrap shared API contracts and web workspaceAkai Mansur
- 2026-09-23 14:41docs: finalize team tasks and worktree setupAkai Mansur
- 2026-09-23 14:13docs: record completed repository preparationAkai Mansur
- 2026-09-23 14:12docs: prepare HackAlem plan and team workflowAkai Mansur
Структура репозитория (109 файлов)
.agent/PLANS.md .agent/SOURCES.md .dockerignore .env.example .github/workflows/check.yml .gitignore .node-version .python-version AGENTS.md Dockerfile README.md backend/app/__init__.py backend/app/api/__init__.py backend/app/api/errors.py backend/app/cloud.py backend/app/config.py backend/app/contracts/__init__.py backend/app/contracts/models.py backend/app/demo.py backend/app/engine/__init__.py backend/app/engine/demand.py backend/app/engine/planning.py backend/app/engine_demo.py backend/app/export/__init__.py backend/app/export/csv_export.py backend/app/import_local.py backend/app/ingestion/__init__.py backend/app/ingestion/excel.py backend/app/main.py backend/app/seed.py backend/app/service.py backend/app/storage/__init__.py backend/app/storage/postgres.py backend/app/storage/repository.py backend/pytest.ini backend/requirements.in backend/requirements.lock backend/scripts/e2e_server.py backend/scripts/export_contract.py backend/scripts/export_fixtures.py backend/scripts/smoke.py backend/tests/test_contract.py backend/tests/test_engine.py backend/tests/test_engine_demo.py backend/tests/test_ingestion.py backend/tests/test_postgres.py backend/tests/test_workflow.py contracts/README.md contracts/fixtures/calculation.json contracts/fixtures/dataset.json contracts/fixtures/error.json contracts/fixtures/explanation-1.json contracts/fixtures/explanation-2.json contracts/fixtures/explanation-3.json contracts/fixtures/normalized.json contracts/openapi.json docs/BACKEND_PLAN.md docs/CLOUD_PLAN.md docs/DATA_NOTES.md docs/DEPLOYMENT.md docs/PLAN_REVIEW.md docs/TASK_DATA.md docs/TASK_WEB.md docs/TEAM.md docs/TEST_DRIVE.md docs/WORKTREE_GUIDE.md frontend/WORK_PLAN.md frontend/index.html frontend/package-lock.json frontend/package.json frontend/playwright.config.ts frontend/review/HANDOFF.md frontend/review/screenshots/data-1366.png frontend/review/screenshots/data-390.png frontend/review/screenshots/explanation-1366.png frontend/review/screenshots/explanation-390.png frontend/review/screenshots/history-1366.png frontend/review/screenshots/history-390.png frontend/review/screenshots/order-1366.png frontend/review/screenshots/order-390.png frontend/review/screenshots/recommendations-1366.png frontend/review/screenshots/recommendations-390.png frontend/src/App.tsx frontend/src/api/client.ts frontend/src/api/generated/schema.d.ts frontend/src/components/ExplanationPanel.tsx frontend/src/components/HistoryChart.tsx frontend/src/components/common.tsx frontend/src/main.tsx frontend/src/pages/DataPage.tsx frontend/src/pages/OrderPage.tsx frontend/src/pages/RecommendationsPage.tsx frontend/src/styles.css frontend/tests/interface.spec.ts frontend/tests/public-demo.spec.ts frontend/tests/workflow.spec.ts frontend/tsconfig.json frontend/vite.config.ts planning.md render.yaml scripts/build.ps1 scripts/check.ps1 scripts/common.ps1 scripts/contracts.ps1 scripts/dev.ps1 scripts/import.ps1 scripts/seed.ps1 scripts/serve.ps1 scripts/setup.ps1
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.