AAYMA
RU
Войти

EventMatch AI

Веб-сервис подбирает до трёх event-подрядчиков по городу, дате, формату, бюджету, длительности и языку и объясняет каждый выбор.

Рабочий MVP
Команда «ctrl+C» 4💬 0 комментариевс 23 сентября 2026 г.

О проекте

Команда: ctrl+C
Кейс HackAlem.ai: Креативные индустрии — Firebird
Стадия: Рабочий MVP

Задача кейса: «#79-lite»: подобрать до 3 подрядчиков для мероприятия и объяснить выбор

Суть

Веб-сервис подбирает до трёх event-подрядчиков по городу, дате, формату, бюджету, длительности и языку и объясняет каждый выбор. Предусмотрены три исхода: success, category_not_found, no_matches.

Как устроено

CSV с 66 профилями (13 синтетических) импортируется в SQLite. Backend на FastAPI отбирает кандидатов по категории и городу, исключает занятых на дату, неподходящих по формату и превышающих бюджет больше чем на 30%. Остальным начисляются баллы (бюджет +10 с убыванием, длительность, язык), порог int(max×0.25), сортировка детерминированная, возвращается топ-3. Объяснение шаблонное, из цены, формата, доступности и фрагмента описания. LLM не используется.

Ключевые решения

  • rule-based scoring
  • hard filters
  • soft preferences
  • threshold 25% of max score
  • deterministic tie-break by ID

Особенности

Прозрачная балльная система с точными формулами и порогами в README. Все демо-сценарии из README проверяются автотестом test_readme_demo_requests.

Стек

Python · FastAPI · SQLite · HTML/CSS/JavaScript · Node test runner


Проект создан за 5 часов на финале хакатона HackAlem.ai (23 сентября 2026, Астана). Код — BAITC-Hacks/hack-9923a583-ctrl-c.

README

README.md

EventMatch AI — подбор event-подрядчиков

Проект команды ctrl+C для хакатона: пользователь задаёт параметры мероприятия и получает до трёх подрядчиков с объяснением каждого выбора.

Frontend: HTML, CSS и обычный JavaScript. Backend: Python, FastAPI и SQLite. Подбор выполняется по правилам и баллам; внешняя LLM и API-ключи не используются. В текущей версии frontend работает с настоящим API, mock-режима нет.

Быстрый запуск на Windows

Нужен установленный Python 3.10+ (проверено на 3.14). Все команды выполняются в PowerShell из корня проекта, где находятся README.md и index.html.

1. Подготовить окружение и зависимости

py --version
py -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r backend/requirements-dev.txt

requirements-dev.txt включает зависимости приложения и httpx для backend-тестов. Если нужны только запуск и отдельный test_hackathon.py, достаточно backend/requirements.txt.

Мы вызываем Python из .venv напрямую: активировать окружение или менять политику выполнения PowerShell не требуется.

2. Создать локальную базу из CSV

.\.venv\Scripts\python.exe backend/import_dataset.py

Импортируется файл hackathon dataset anonymized .csv из корня проекта: 66 профилей, включая 13 синтетических. Создаётся backend/data/vendors.sqlite3. Устанавливать отдельный сервер БД и получать пароль не нужно.

Повторный импорт обновляет записи с теми же ID, не создавая дубликатов. Он не удаляет старые ID, которых нет в новом CSV. После получения новой версии датасета повторите импорт. SQLite-файл не хранится в Git: на каждом компьютере его создают отдельно.

3. Запустить backend

.\.venv\Scripts\python.exe -m uvicorn backend.main:app --reload --host 127.0.0.1 --port 8000

Оставьте терминал открытым. Ожидается сообщение Uvicorn running on http://127.0.0.1:8000.

  • Проверка сервера и базы: http://127.0.0.1:8000/api/health — ожидается {"status":"ok","profiles":66}.
  • Интерактивная документация API: http://127.0.0.1:8000/docs.
  • Остановка сервера: Ctrl+C в его терминале.

4. Открыть frontend

В другом терминале, также из корня проекта:

.\.venv\Scripts\python.exe -m http.server 5500 --bind 127.0.0.1

Откройте http://127.0.0.1:5500/index.html. Альтернативы: расширение VS Code Live Server или открытие index.html напрямую. Для подбора backend должен оставаться запущенным.

Адрес API задаётся константой API_URL в js/api.js. Сейчас это http://127.0.0.1:8000/api/recommend. 127.0.0.1 означает компьютер, на котором открыт браузер; для демонстрации с другого устройства нужно отдельно настроить адрес сервера и сетевой доступ.

На Linux/macOS создайте окружение через python3 -m venv .venv, а в остальных командах используйте .venv/bin/python вместо .\.venv\Scripts\python.exe.

Как устроен код

index.html                     Форма поиска и каркас страницы
css/style.css                  Оформление, мобильная версия, состояния
js/app.js                      Проверка формы, карточки, загрузка и ошибки
js/api.js                      POST-запрос, таймаут, проверка ответа
backend/
  main.py                      FastAPI: входные данные, маршруты, HTTP-ошибки
  recommender.py               Поиск кандидатов, сортировка, топ-3 и три исхода
  matching.py                  Отсев, баллы и объяснение для каждого профиля
  db.py                        Схема SQLite, подключения, готовность каталога
  import_dataset.py            Перенос CSV в SQLite
  requirements.txt            Зависимости приложения
  requirements-dev.txt        Дополнительные зависимости тестов
  data/vendors.sqlite3        Локальная база, появляется после импорта
  tests/test_regressions.py    Подробные регрессионные проверки backend
tests/frontend.test.cjs        Проверки JavaScript через Node.js
test_hackathon.py              Отдельные приёмочные тесты и вывод демо-запросов
hackathon dataset anonymized .csv

Путь запроса:

Форма → app.js → api.js → main.py → recommender.py
                                      ↓
                                SQLite + matching.py
                                      ↓
Карточки ← app.js ← JSON с результатами и объяснениями

CSV читается при импорте. При поиске backend обращается к SQLite; браузер напрямую к базе не подключается. Списки категорий, форматов, языков и занятых дат хранятся в JSON-столбцах таблицы vendors.

Правила подбора в текущей версии

  1. Найти категорию в выбранном городе. Если кандидатов нет — category_not_found.
  2. Исключить занятых на дату, не работающих с форматом, превышающих бюджет более чем на 30%.
  3. Начислить баллы оставшимся, исключить тех, кто ниже порога.
  4. Отсортировать по убыванию суммы, при равенстве — по ID. Вернуть не больше трёх.
УсловиеБаллы
Город, категория, свободная дата, подходящий форматПо +1; всего +4
Цена не превышает бюджет+10; более низкая цена не даёт дополнительных баллов
Превышение бюджета до 30% включительноОт +10 вычитается 1 за каждые начатые 3%; ровно +30% даёт 0
Цена неизвестна0 за бюджет; требуется уточнение
Длительность указана и подходит+10
Длительности не хватаетround(10 − 2.5 × недостающие часы), минимум −10
Язык указан+3 за совпадение, −1 за отсутствие

Без языка и длительности максимум 14, с обоими — 27. Порог вычисляется как int(max_score × 0.25): соответственно 3 и 6 баллов. Округление вниз — текущее правило реализации; это не строгие 25% без округления. Кандидат с баллом, равным порогу, допускается.

Бюджет допускает превышение до 30%; язык и длительность — мягкие пожелания. Поэтому в выдаче возможен более дорогой подрядчик или профиль с недостаточной длительностью/другим языком. Несовпадения описываются в объяснении. max_hours: null означает, что работа не привязана к присутствию на площадке.

Объяснение включает цену, формат, запрошенные дополнительные условия, дату доступности и фрагмент исходного описания. Синтетические профили помечаются. Повторный запрос на неизменной базе даёт тот же порядок.

API и три исхода

POST /api/recommend (также доступен /api/recommendations):

{
  "city": "Алматы",
  "date": "2026-11-14",
  "event_format": "свадьба",
  "category": "Ведущий",
  "budget": 1000000,
  "duration": 10,
  "language": "русский"
}

duration и language можно не передавать или указать null. Город: Астана, Алматы или Зарубежье. Форматы: свадьба, той, корпоратив, конференция, юбилей, день рождения. Даты: 23.09.2026–31.12.2026 включительно. Бюджет — положительное целое число; длительность — положительное конечное число.

HTTPstatusЗначение
200successНайдены 1–3 карточки
200category_not_foundВ городе такой категории нет
200no_matchesКатегория есть, но все кандидаты отсеяны; причина в message
422—Ошибка входных параметров, подробности в detail
503—Каталог отсутствует, пуст, повреждён или имеет неправильную схему

У обычного ответа есть message, results, total_candidates, eligible_candidates, minimum_score и счётчики причин в rejected. Порог может быть null, если оценка баллов не выполнялась. Карточка содержит имя, ID, категории, город, цену, языки, длительность, описание, признаки синтетического профиля, баллы, объяснение и теги matches.

Frontend различает пустые результаты и ошибку сервиса, ограничивает ожидание десятью секундами и позволяет повторить запрос. После импорта недостающей базы API начинает работать без перезапуска.

Сценарии для демонстрации

Первые три строки — обязательные сценарии хакатона; четвёртый показывает отдельный исход «категории нет».

СценарийГород / категорияДатаФормат / бюджетДополнительноОжидается
Плотная категорияАлматы / Ведущий13.10.2026Корпоратив / 800 000 ₸8 ч, русский3 карточки
Редкая категорияАлматы / Флорист23.09.2026Свадьба / 2 000 000 ₸Неважно2 карточки
Нет подходящихАлматы / Флорист26.09.2026Свадьба / 20 000 ₸Неважноno_matches: занятость и бюджет
Нет категорииАстана / Декоратор14.11.2026Свадьба / 1 000 000 ₸Неважноcategory_not_found

Все строки проверяются автотестами. Для проверки влияния даты: Астана / Флорист / Свадьба / 1 000 000 ₸, без дополнительных условий — 14.11.2026 даёт одну карточку, 26.12.2026 даёт no_matches, профиль занят.

Автотесты

Сервер запускать не нужно. Python-тесты импортируют CSV во временную SQLite-базу и не используют рабочую БД.

# Отдельный тест хакатона: прежние проверки + новые правила и пороги
.\.venv\Scripts\python.exe -B test_hackathon.py

# Готовые JSON-запросы и фактические ответы для четырёх контрольных сценариев
.\.venv\Scripts\python.exe -B test_hackathon.py --demo

# Подробные backend-тесты из новой версии
.\.venv\Scripts\python.exe -B -m unittest discover -s backend/tests -v

test_hackathon.py проверяет API, импорт, три исхода, детерминизм, занятость, объяснения, валидацию, допуск бюджета, мягкие предпочтения, прохождение порога и восстановление после импорта. --demo выводит контрольные сценарии из массива DEMOS: Алматы / Ведущий 14 ноября, редкий флорист в Астане, его занятость и отсутствие декоратора. Таблица выше дополнительно проверяется тестом test_readme_demo_requests.

Для frontend-тестов нужен Node.js 20+; npm install не требуется:

node --test tests/frontend.test.cjs

Ожидаемый итог Python-тестов — OK, frontend — fail 0. FAIL означает несовпадение с ожидаемым поведением, ERROR — ошибку выполнения теста или окружения. Отдельный тест проверяет FastAPI через ASGI внутри процесса; frontend-тесты используют имитацию DOM в Node. Они не проверяют реальную сеть, браузерную отрисовку и адаптивность — форму и три состояния нужно также показать в браузере.

Если не запускается

СимптомЧто сделать
Команда python выводит только Python и сразу завершаетсяИспользовать py, затем Python из .venv, как в инструкции выше
py не найденУстановить Python с Python Launcher, открыть новый терминал
No module named fastapi, uvicorn или httpxУстановить backend/requirements-dev.txt именно через Python из .venv
HTTP 503 / каталог не готовВыполнить импорт CSV, открыть /api/health
Ошибка подключения / Failed to fetchЗапустить backend, проверить порт 8000 и API_URL
Порт 8000 занятПроверить уже работающий сервер или выбрать другой порт и обновить API_URL
HTTP 422Проверить дату, положительный бюджет, формат и дополнительные поля
no_matchesПрочитать причину; изменить дату, формат, бюджет или пожелания
Изменения страницы не видныОбновить Ctrl+F5, проверить, что открыта нужная папка проекта

Для проверки своей версии: git status --short --branch и git log -1 --oneline. Получение кода из Git не запускает сервер и не импортирует базу автоматически.

Звёзды
⭐ 0
Форки
0
Язык
Python
Последний push
в прошлом месяце
Коммиты
7
Авторы
2
Файлы
19
Ветки
4

Авторы коммитов

Ykpk660 5hitch 2

История коммитов

  • 2026-09-23 17:21Finalize hackathon testinghitch
  • 2026-09-23 16:57Final'v1'[W/V]Ykpk660
  • 2026-09-23 15:54Score'v2'[W/V]Ykpk660
  • 2026-09-23 15:01Score'v1'[W/V]Ykpk660
  • 2026-09-23 14:42Skelet[W/V]Ykpk660
  • 2026-09-23 14:07First check [nw/v]hitch
  • 2026-09-23 14:04DataBase[W/V]Ykpk660
Структура репозитория (19 файлов)
.gitignore
README.md
backend/.gitignore
backend/__init__.py
backend/db.py
backend/import_dataset.py
backend/main.py
backend/matching.py
backend/recommender.py
backend/requirements-dev.txt
backend/requirements.txt
backend/tests/test_regressions.py
css/style.css
hackathon dataset anonymized .csv
index.html
js/api.js
js/app.js
test_hackathon.py
tests/frontend.test.cjs
Ветки (4)

Обсуждение 0 комментариев

Войдите, чтобы комментировать

Пока нет комментариев — будьте первым.