О проекте
Команда: Raki
Кейс HackAlem.ai: Спецтрек — Astana Innovations
Стадия: Рабочий MVP
Задача кейса: «Аким на 5 часов»: городской симулятор, распределяющий бюджет по 5 направлениям и 5 районам
Суть
Симулятор городских решений: пользователь выбирает 5 мер из каталога 14 для 5 районов Астаны в пределах бюджета 100 и видит, как меняется Astana Quality of Life Score по районам.
Как устроено
Районы (10 показателей), меры, синергии, несовместимости и веса Score лежат в JSON в data/. React-фронт собирает сценарий и отправляет POST /api/simulate. Python-движок в services/ проверяет правила (бюджет, повторы, лимит мер на направление, несовместимость), применяет эффекты с лагом на горизонте 8 кварталов и синергии, затем считает Score и критические показатели. Supabase отвечает за авторизацию и сохранение сценариев; AI-анализ через OpenAI написан, но к сценарию не подключён.
Ключевые решения
- rule-based scenario validation
- effect lag modeling
- synergy calculation
- weighted QoL score
Метрики
Контрольный сценарий: стоимость 95, остаток 5, Score 52.56 → ~56.5, 0 критических показателей
Особенности
Эффекты мер проявляются с задержкой на горизонте 8 кварталов, между мерами есть синергии и несовместимости. Контрольный сценарий для жюри закреплён в backend-тесте. README честно перечисляет ограничения: AI не подключён, backend требует Supabase.
Стек
Python · FastAPI · Pydantic · React 19 · TypeScript · Vite · Supabase · Vercel
Модели
gpt-5-mini
Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-c26997ee-raki.
README
README.mdАким на 5 часов (Abeke)
Аким на 5 часов — симулятор городских решений для Астаны. Он помогает сравнить наборы мероприятий для разных районов при ограниченном бюджете и увидеть, как выбранный сценарий меняет показатели качества жизни в рамках модели проекта. Интерфейс рассчитан на пользователя, который хочет изучить меры и оценить последствия выбора до его реализации. Результат симуляции — расчёт по данным и правилам из репозитория, а не прогноз на основе данных в реальном времени.
Что реализовано
- Каталог из 14 городских мероприятий с фильтрацией по направлениям, стоимостью, сроком начала эффекта и ожидаемым влиянием на показатели.
- Выбор ровно пяти решений для города и его пяти районов. Для районных мер указывается район; общегородские применяются ко всем районам.
- Проверка бюджета (лимит 100 единиц), повторного выбора, количества мер одного направления и несовместимых сочетаний.
- Расчёт показателей по районам, синергий и Astana Quality of Life Score на Python backend. Движок учитывает лаг эффекта в пределах горизонта восемь кварталов.
- Экран результатов с исходным и итоговым Score, затратами, остатком бюджета, критическими показателями, сравнением значений по районам и сработавшими синергиями. Кнопка «Изменить сценарий» возвращает к сохранённому в состоянии интерфейса выбору.
- Регистрация, вход и выход через Supabase Auth, если для frontend заданы публичные настройки Supabase. Без них интерфейс симулятора доступен локально без входа.
Как работает решение
- Интерфейс читает районы, мероприятия и правила из JSON-файлов в
data/и предлагает выбрать пять мер. - Frontend проверяет выбор и отправляет
POST /api/simulate; API-слой преобразуетmeasureId/districtIdвmeasure_id/district_id. - FastAPI передаёт решения существующему движку в
services/. Движок проверяет правила, применяет эффекты и синергии, рассчитывает показатели районов и итоговый Score. - Если сценарий невалиден, пользователь видит причины. Если валиден, экран результатов показывает ответ backend без самостоятельного расчёта Score во frontend.
POST /api/simulate не требует авторизации и не сохраняет сценарий в Supabase. При этом текущий backend проверяет настройки Supabase при запуске приложения.
Технологии
| Часть | Технологии | Использование |
|---|---|---|
| Frontend | React 19, TypeScript, Vite | Каталог, сбор сценария, запрос к API и отображение результата |
| Backend | Python, FastAPI, Pydantic, Uvicorn | HTTP API, проверка формы запроса и запуск движка |
| Расчёты | Python, JSON | Валидация, эффекты мер, синергии и Score |
| Авторизация и хранение | Supabase Auth, Supabase, @supabase/supabase-js | Вход во frontend и отдельные защищённые API-маршруты для данных и сценариев |
| Развёртывание | Vercel entrypoint api/index.py | Переиспользует backend.main:app; зависимости Python подключены через корневой requirements.txt |
В репозитории также есть сервис подготовки AI-анализа через OpenAI API с резервным анализом без модели. В коде для него указан gpt-5-mini по умолчанию, если OPENAI_MODEL не задан. Этот сервис сейчас не вызывается из /api/simulate и не отображается во frontend; для демонстрации симулятора ключ OpenAI не нужен.
Архитектура проекта
data/*.json ──→ React (src/) ── POST /api/simulate ──→ FastAPI (backend/main.py)
│ │
└──────────────────────────────────────────────→ services/ (валидация и расчёт)
│
ResultsPage ←── JSON-ответ ─┘
Supabase Auth ←→ React (при настройке)
Supabase DB ←→ защищённые маршруты backend (отдельно от /api/simulate)
data/districts.json— пять районов, исходные значения десяти показателей и их описания.data/measures.json— 14 мер, бюджет, горизонт расчёта, синергии и несовместимости.data/scoring.json— веса и правила Score, порог критических показателей и контрольный сценарий.src/api/— HTTP-клиент, типы ответа backend и преобразование в модель frontend.
src/pages/SimulatorPage.tsx и src/pages/ResultsPage.tsx — выбор решений и просмотр результата.
backend/main.py— маршруты FastAPI;services/— расчётный движок.api/index.py— точка входа Python для Vercel; новые маршруты или второй FastAPI app там не создаются.
Установка и локальный запуск
Нужны Node.js с npm, Python 3.12 и проект Supabase. Команды ниже предназначены для PowerShell и выполняются из корня репозитория. Backend и frontend запускаются в разных терминалах.
1. Установите зависимости backend
py -3.12 -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
Создайте backend/.env (образец имён переменных — backend/.env.example) и заполните:
SUPABASE_URL=https://<ваш-проект>.supabase.co
SUPABASE_ANON_KEY=<публичный-ключ-проекта>
FRONTEND_URL=http://localhost:5173
Backend вызывает get_supabase() при старте, поэтому SUPABASE_URL и SUPABASE_ANON_KEY обязательны даже для запроса симуляции. Файл backend/.env.local автоматически не загружается: backend/config.py вызывает load_dotenv() для .env. Не коммитьте заполненные env-файлы.
2. Настройте и запустите frontend
Создайте в корне проекта .env.local:
VITE_API_URL=http://localhost:8000
Для входа через Supabase дополнительно задайте в этом файле VITE_SUPABASE_URL и VITE_SUPABASE_PUBLISHABLE_KEY. Это публичные значения для браузера; приватные ключи туда помещать нельзя. Если не задавать VITE_API_URL, API-клиент использует тот же origin, что и frontend; для раздельных локальных серверов переменная нужна.
npm.cmd install
npm.cmd run dev
3. Запустите backend во втором терминале
.\.venv\Scripts\python.exe -m uvicorn backend.main:app --reload --port 8000
Откройте frontend по адресу http://localhost:5173 (если порт свободен). Проверка backend: http://localhost:8000/health; интерактивная документация API: http://localhost:8000/docs.
Команды для проверки проекта:
npm.cmd run typecheck
npm.cmd run build
.\.venv\Scripts\python.exe -B -m unittest discover -s tests -v
Контрольный сценарий для жюри
В каталоге выберите следующие меры и районы, затем нажмите «Запустить симуляцию»:
| Мера | Район |
|---|---|
| M7 — Школа + детсад | Нура (nura) |
| M8 — Центр семейного здоровья | Нура (nura) |
| M10 — Освещение и камеры | Нура (nura) |
| M12 — Цифровая платформа обращений | Весь город (null) |
| M5 — Переход на чистое топливо | Сарыарка (saryarka) |
В backend-тесте для этого набора проверяются HTTP 200, valid: true, стоимость 95, остаток бюджета 5, итоговый Score около 56,5 и 0 критических показателей после сценария. Исходный Score 52,56 указан в data/scoring.json. Экран результатов должен показать сравнение показателей районов; возврат по кнопке «Изменить сценарий» сохраняет выбранные меры.
Тот же запрос можно проверить напрямую:
$body = '{"decisions":[{"measure_id":"M7","district_id":"nura"},{"measure_id":"M8","district_id":"nura"},{"measure_id":"M10","district_id":"nura"},{"measure_id":"M12","district_id":null},{"measure_id":"M5","district_id":"saryarka"}]}'
Invoke-RestMethod -Uri 'http://localhost:8000/api/simulate' -Method Post -ContentType 'application/json' -Body $body
Данные и интеграции
Расчёт симуляции использует локальные JSON-файлы в data/; обращений к внешним источникам городских показателей в этом пути нет. Supabase используется для авторизации в браузере и в отдельных защищённых маршрутах backend: GET /api/me, /api/districts, /api/measures, /api/scenarios и POST /api/scenarios. Они требуют Authorization: Bearer <access token>. Схема таблиц находится в supabase/schema.sql; frontend сейчас не вызывает маршрут сохранения сценария.
Ограничения текущей версии
- Модель опирается на фиксированные данные и коэффициенты из репозитория; она не получает городские данные в реальном времени и не подтверждена как прогноз фактических изменений.
- Симуляция не сохраняется автоматически. Отдельный API-маршрут сохранения существует, но кнопка запуска его не вызывает.
- AI-анализ и leaderboard не подключены к текущему пользовательскому сценарию. В ответе симуляции также нет ID «самого слабого района».
- При ошибке правил движок возвращает HTTP 200 с
valid: falseи причинами; экран результатов не открывается. - Для запуска текущего FastAPI приложения нужны настройки Supabase, несмотря на отсутствие авторизации у
/api/simulate.
Развёрнутая версия
В репозитории есть точка входа для Vercel, но URL опубликованной версии здесь не указан. Для развёртывания потребуются переменные окружения Supabase. В production VITE_API_URL можно не задавать: frontend обращается к /api/simulate на том же домене.
Авторы коммитов
История коммитов
- 2026-09-23 17:57Update README.mdmxx567
- 2026-09-23 17:45Merge pull request #2 from BAITC-Hacks/feat/frontend-api-integrationhaittsu
- 2026-09-23 17:40chore: prepare app for Vercel deploymenthaittsu
- 2026-09-23 17:40feat: integrate simulation API and results pagehaittsu
- 2026-09-23 17:22feat: expose simulation engine APIhaittsu
- 2026-09-23 17:05Merge pull request #1 from BAITC-Hacks/feat/frontendhaittsu
- 2026-09-23 17:01merge: integrate backend and React frontendhaittsu
- 2026-09-23 16:42style: polish simulator UIhaittsu
Все коммиты (28)
- 2026-09-23 16:34Merge branch 'main' of https://github.com/BAITC-Hacks/hack-c26997ee-rakimasterok
- 2026-09-23 16:34made an score calculator and readjusted the simulation_enginemasterok
- 2026-09-23 16:34changed namemasterok
- 2026-09-23 16:31added calculators and added more constrains for the validatormasterok
- 2026-09-23 16:30deleted testsmxx567
- 2026-09-23 16:23Fallback service has been implementedmxx567
- 2026-09-23 16:14OpenAI API response has been integratedmxx567
- 2026-09-23 15:52Merge remote changes into mainmxx567
- 2026-09-23 15:50feat: add scenario builder and validationhaittsu
- 2026-09-23 15:47Backend with FastAPI + Supabase + Added scenario upload to dbmxx567
- 2026-09-23 15:32feat: add measure catalog UIhaittsu
- 2026-09-23 15:26Merge branch 'main' of https://github.com/BAITC-Hacks/hack-c26997ee-rakimasterok
- 2026-09-23 15:11Update README.mdmxx567
- 2026-09-23 15:08JSON files createdmxx567
- 2026-09-23 15:08refactor: use official JSON dataset in frontendhaittsu
- 2026-09-23 14:51Login Screen + backend integrationmxx567
- 2026-09-23 14:47basic validation and loader with data modelmasterok
- 2026-09-23 14:41feat: add React foundation and official hackathon datasethaittsu
- 2026-09-23 13:36JSON files createdmxx567
- 2026-09-23 13:11framework creationmxx567
Структура репозитория (86 файлов)
.env.example .gitignore README.md api/index.py app/__init__.py app/ai/__init__.py app/ai/openai_client.py app/ai/prompts.py app/ai/schemas.py app/ai/services/__init__.py app/ai/services/ai_service.py app/ai/services/fallback_service.py app/services/__init__.py app/services/fallback_service.py backend/.env.example backend/__init__.py backend/auth.py backend/config.py backend/main.py backend/requirements.txt backend/scenarios.py backend/supabase.py data/districts.json data/measures.json data/scoring.json index.html package-lock.json package.json requirements.txt services/data_loader.py services/effect_calculator.py services/models.py services/score_calculator.py services/simulation_engine.py services/synergy_calculator.py services/validator.py src/App.tsx src/api/.gitkeep src/api/client.ts src/api/simulation.ts src/api/types.ts src/components/auth/AuthPage.tsx src/components/district/.gitkeep src/components/district/DistrictSelector.tsx src/components/measures/.gitkeep src/components/measures/DirectionFilter.tsx src/components/measures/MeasureCard.tsx src/components/measures/MeasureCatalog.tsx src/components/results/.gitkeep src/components/scenario/.gitkeep src/components/scenario/BudgetMeter.tsx src/components/scenario/ScenarioPanel.tsx src/components/scenario/ValidationErrors.tsx src/data/.gitkeep src/data/districts.ts src/data/index.ts src/data/indicators.ts src/data/measures.ts src/data/normalization.ts src/data/project-rules.ts src/lib/router.ts src/lib/supabase.ts src/main.tsx src/pages/.gitkeep src/pages/ResultsPage.tsx src/pages/SimulatorPage.tsx src/styles/auth.css src/styles/global.css src/styles/results.css src/styles/simulator.css src/types/direction.ts src/types/district.ts src/types/index.ts src/types/indicators.ts src/types/measure-scope.ts src/types/measure.ts src/types/scenario.ts src/types/simulation-result.ts src/ui/direction-labels.ts src/validation/scenario.ts supabase/schema.sql test_backend_core.py tests/__init__.py tests/test_fallback_service.py tests/test_simulate_api.py tsconfig.json
Обсуждение 0 комментариев
Войдите, чтобы комментироватьПока нет комментариев — будьте первым.