Платформа для таргетированной рекламы, оптимизирующая прибыль, релевантность и выполнение лимитов кампаний. Построена на Django Ninja с использованием PostgreSQL, Redis, Minio и интеграцией с Yandex GPT.
cd solution
docker-compose up --build- app: Django (порт 8080), Gunicorn через entrypoint.sh
- postgres: PostgreSQL 16.6 (порт 5432)
- redis: Redis 7.4 (порт 6379) — кэш текущего дня и тогл модерации
- minio: Minio (порт 9000) — хранение изображений
- prometheus: Prometheus (порт 9090) — мониторинг
- grafana: Grafana (порт 3000) — визуализация статистики
- DJANGO_DEBUG=
- POSTGRES_CONN=
- REDIS_HOST=
- MINIO_STORAGE_ENDPOINT=
- YANDEX_GPT_API_KEY=
Вы можете посмотреть openapi спецификацию по адресу http://localhost:8080/docs или скачать файл openapi.yaml, openapi.json
Запрос на создание клиента:
[
{
"client_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"login": "Alexandr Shahov",
"age": 28,
"location": "Moscow",
"gender": "MALE"
}
]Запрос на создание рекламной кампании:
{
"impressions_limit": 8,
"clicks_limit": 8,
"cost_per_impression": 1.50,
"cost_per_click": 2.50,
"ad_title": "Amazing Offer",
"ad_text": "Get great savings now!",
"start_date": 5,
"end_date": 15,
"targeting": {
"gender": "MALE",
"age_from": 25,
"age_to": 40,
"location": "Chicago"
}
}Остальные запросы, а также их подробное описание вы найдете в спецефикации.
Во время запросов также можно получить следующие ошибки:
400 - ошибка валидации данных. Формат ответа:
{
"message": "Ошибка в данных запроса."
}403 - клиент не может кликнуть на непросмотренное объявление. Формат ответа:
{
"message": "Запрещено"
}404 - клиент, кампания или рекламодатель не найден. Формат ответа:
{
"message": "Объект не найден"
}- Добавление изображений в рекламных объявлениях
/{advertiser_id}/campaigns/{campaign_id}/upload (POST)- эндпоинт для добавления изображения.
Изображения хранятся в self-hosted хранилище MinIO
Админ панель находится по адресу http://localhost/9001/login. Логин и пароль - minioadmin
Для того чтобы посмотреть статистику необходимо:
- Зайти на http://localhost:3000/
- Данные для авторизации - admin:admin
- Data sources -> add data source -> prometheus
- В поле connection вставить http://prometheus:9090
- Save & test
- Dashboards -> New -> Import
- Импортировать файл grafana_config -> load
- Select Data Source -> Prometheus -> Import
- Модерация текстов рекламных кампаний
Для модерации текстов используется YandexGPT Pro. Если LLM выявит нарушения, то клиент получит 400.
Модерацию можно включать и отключать следующими эндпоинтами:/moderation/enable (POST)- включение/moderation/disable (POST)- выключение/moderation (GET)- получение текущего состояния(хранится вRedis)
- Интеграция с LLM для генерации рекламных текстов
Для генерации рекламных текстов я использую YandexGPT Pro. Для того, чтобы сгенерировать текст надо отправить запрос на/advertisers/{advertiser_id}/campaigns/generation (POST).
Запрос:Ответ:{ "product_name": "Смартфон X", "target_action": "Купить", "target_audience": "Молодежь 18-25 лет" }{ "text": "Купи смартфон X со скидкой 20%! Идеально для молодежи, стильный и мощный!" }
Схему также можно посмотреть по ссылке
| Модель | Описание | Связи |
|---|---|---|
Client |
Пользователь | Campaign (ManyToMany через Impression, Click) |
Advertiser |
Рекламодатель | Campaign, MLScore (ForeignKey) |
Campaign |
Рекламная кампания | Client (ManyToMany), Advertiser, Targeting (ForeignKey) |
Targeting |
Параметры таргетинга | Campaign (ForeignKey) |
Impression |
Показы | Client, Campaign (ForeignKey) |
Click |
Клики | Client, Campaign (ForeignKey) |
Mlscore |
Релевантность | Client, Campaign (ForeignKey) |
Вы можете посмотреть openapi спецификацию по адресу http://localhost:8080/docs или скачать файл doc.json
Описание: Возвращает подходящее рекламное объявление для клиента.
Параметры:
client_id(UUID, обязательный): Идентификатор клиента.
Логика:
- Получает клиента по
client_id. - Фильтрует активные кампании по дате, таргетингу и лимитам (≤ 105%).
- Вызывает
select_best_campaignдля выбора кампании с максимальнымscore:- Рассчитывает
profit,relevance,limit_factor. - Нормализует метрики (
maxдля < 10 кампаний,linearдля ≥ 10). - Добавляет приоритетность тем кампаниям, которые скоро закончатся и не набрали необходимый процент показов.
- Вычисляет
score = 0.5 * profit_norm + 0.25 * relevance_norm + 0.15 * limit_factor.
- Рассчитывает
- Сохраняет показ в
Impressionи возвращает рекламу или 404.

-
Django Ninja: Использован как фреймворк для быстрого создания REST API с поддержкой OpenAPI. Выбран за простоту, производительность и интеграцию с Django -
PostgreSQL: Гибкая, надежная и отказоустойчивая СУБД. Можно легко масштабировать -
Redis: Использован для кэширования текущего дня (time_emulation.cache) и состояния модерации, обеспечивая низкую задержку и высокую скорость доступа. Выбран за простоту и популярность в задачах кэширования -
Minio: Очень быстрое и легковесное хранилище для изображений, совместимое с S3. Легко интегрируется в django благодаря django-minio-storage -
Prometheus + Grafana: Добавлены для мониторинга и визуализации статистики . Prometheus собирает метрики, а Grafana предоставляет интуитивные дашборды -
Yandex GPT: Интегрирован для генерации текстов кампаний и модерации. Выбран за доступность и неплохие показатели модели. Рассматривался OpenAI, но Yandex GPT предпочтительнее из-за локализации и стоимости
Перед запуском тестов необходимо создать .env файл, со следующим содержимым:
DJANGO_DEBUG=True
POSTGRES_CONN=postgres://postgres:postgres@localhost:5432/advertising_platform
REDIS_HOST=localhost
REDIS_PORT=6379
YANDEX_GPT_MODEL_TYPE=yandexgpt
YANDEX_GPT_CATALOG_ID=b1gp8s6k8vk82fpui3b5
YANDEX_GPT_API_KEY=AQVN2kt9IsahzhZ8yuca5bzI4qiUm5W6Nu9YXVgH
MINIO_STORAGE_ENDPOINT=localhost:9000
А также локально установить зависимости:
uv sync*Если uv не установлен https://docs.astral.sh/uv/getting-started/installation/
Для запуска тестов необходимо запустить контейнер с postgres и redis(обязательно отключить модерацию), и выполнить команду:
```bash
python manage.py test .



