diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..e66ab0a
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,75 @@
+name: CI
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ services:
+ postgres:
+ image: postgres:17
+ env:
+ POSTGRES_USER: test
+ POSTGRES_PASSWORD: test
+ POSTGRES_DB: test
+ ports:
+ - 5432:5432
+ options: >-
+ --health-cmd "pg_isready -U test"
+ --health-interval 10s
+ --health-timeout 5s
+ --health-retries 5
+ env:
+ SECRET_KEY: ci-secret-key
+ DEBUG: "False"
+ ALLOWED_HOSTS: localhost,127.0.0.1
+ POSTGRES_DB: test
+ POSTGRES_USER: test
+ POSTGRES_PASSWORD: test
+ POSTGRES_HOST: localhost
+ POSTGRES_PORT: "5432"
+ CELERY_BROKER_URL: amqp://guest:guest@localhost:5672//
+ CELERY_RESULT_BACKEND: rpc://
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-python@v5
+ with:
+ python-version: "3.12"
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -r requirements.txt -r requirements-dev.txt
+ - name: Migrate
+ run: python manage.py migrate
+ - name: Lint
+ run: ruff check .
+ - name: Format check
+ run: ruff format --check .
+ - name: Tests
+ run: pytest --cov --cov-report=term-missing --cov-fail-under=55
+ - name: Coverage badge data
+ id: coverage
+ run: |
+ coverage json -o coverage.json
+ python - <<'PY' >> "$GITHUB_OUTPUT"
+ import json
+
+ totals = json.load(open("coverage.json"))["totals"]
+ percent = totals["percent_covered"]
+ color = "green" if percent >= 80 else "yellow" if percent >= 55 else "red"
+ print(f"total={totals['percent_covered_display']}%")
+ print(f"color={color}")
+ PY
+ - name: Update coverage badge
+ uses: schneegans/dynamic-badges-action@v1.7.0
+ continue-on-error: true
+ with:
+ auth: ${{ secrets.GIST_TOKEN }}
+ gistID: 097e2cc0afbb6ee7fdbfb4d2d67bb87d
+ filename: coverage.json
+ label: coverage
+ message: ${{ steps.coverage.outputs.total }}
+ color: ${{ steps.coverage.outputs.color }}
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..d126f82
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 Anna Putilovskaya
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index dd6d768..6d052eb 100644
--- a/README.md
+++ b/README.md
@@ -1,111 +1,127 @@
-# Сервис уведомлений
-
-### Задача:
-
-Создать сервис для отправки уведомлений пользователям.
-Пользователь может получать уведомления через:
-- Email
-- SMS
-- Telegram
-
-Нужно обеспечить надежную доставку - если один способ не сработал, попробовать другой.
-
----
-### Технический требования:
-
-- Язык программирования:
- - Python 3.12
-- Фреймворк:
- - Django Rest Framework
-- ORM:
- - Django ORM
-- Хранение данных:
- - PostgreSQL 17
-- Очереди сообщений:
- - Celery + RabbitMQ
-
----
-### Запуск проекта:
-
-- Клонируйте проект:
- ```bash
- git clone https://github.com/annaputilovskaya/NotificationService.git
- ```
-- Создайте файл .env в корне проекта по образцу (Файл .env.example) и заполните его данными.
-
- Команда для Unix-подобных систем для копирования файла:
- ```bash
- cp .env.example .env
- ```
-- Убедитесь, что на Вашем устройстве установлен Docker.
-
- Проверка версии Docker:
- ```bash
- docker --version
- ```
- Если докер не установлен, [осуществите его установку по инструкции с официального сайта](https://www.docker.com/)
-
-- Осуществите сборку образов и запуск контейнеров:
- ```bash
- docker compose up -d --build
- ```
-
----
-### Структура базы данных:
-
-
-
----
-### Функционал и бизнес-логика
-
-1. Пользователи:
- - Регистрация с email и паролем
- - Получение токена для авторизация
- - ОБновление токена авторизации
-
-
-
-
-
-2. Получатели
- - Создание получателя
- - Просмотр получателя
- - Просмотр списка получателей
- - Изменение получтаеля
- - Удаление получателя
-
-
-
-
-
-3. Уведомления
- - Создание уведомления
- - Просмотр уведомления
- - Просмотр списка уведомлений
-
-
-
-
- При создания уведомления создается задача по отправке уведомления.
- Происходит выбор стратегии отпраки уведомления в следующем порядке:
- - Email
- - Telegram
- - SMS
-
- В случае, если первая стратегия не сработала, переходим ко второй, далее к третьей.
- Если ни одна страегия отправки для каких-либо получателей не сработала, повторяем попытку отправки для этих получателей через определенный временной интервал.
-
- ---
-Сервис подходит для отправки разовых уведомлений. Если нужны повторяющиеся уведомления, возможна доработка.
-
-В проекте настроено логирование отправки уведомлений с отправкой логов в консоль и с сохранением в файл.
-
-Проведено ручное тестирование проекта с помощью Postman.
-
-Автоматизированное тестирование не проводилось.
-
-Для более подробной информации об API ознакомьтесь с интерактивной документацией.
-
-Если проект запущен на localhost, пройдите по [ссылке](http://localhost:8000/swagger/)
-
-
+# Notification Service
+
+[](https://github.com/annaputilovskaya/NotificationService/actions/workflows/ci.yml)
+[](https://github.com/annaputilovskaya/NotificationService/actions/workflows/ci.yml)
+[](./LICENSE)
+[](https://www.python.org/)
+
+An API for reliable delivery of notifications to users across multiple channels — Email, SMS, and Telegram — with automatic failover between channels.
+
+[Русская версия](README.ru.md)
+
+## Features
+
+- **Users** — email/password sign-up, issuing and refreshing JWT access tokens.
+
+
+
+- **Recipients** — create, view, update, and delete recipients.
+
+
+
+- **Notifications** — create a notification, view a single notification and a list.
+
+
+
+- **Reliable delivery (failover)** — creating a notification enqueues a Celery task. Strategies are tried in order **Email → Telegram → SMS**: when a channel fails, the next one is used. If no strategy succeeds for some recipients, delivery to those recipients is retried after a delay.
+- **Pagination** — list endpoints return a paginated `{"count": ..., "results": [...]}` payload (the `?page=` parameter).
+- **Logging** — delivery logs go to the console and to a file.
+- The service targets one-off notifications; recurring notifications are a possible extension.
+
+## Stack
+
+- Python 3.12
+- Django REST Framework (Django ORM)
+- PostgreSQL 17
+- Celery + RabbitMQ
+- drf-yasg (Swagger/Redoc)
+
+## Architecture
+
+```mermaid
+flowchart LR
+ Client["Client"] --> API["Django REST API"]
+ API -->|create notification| Task["Celery: send_notification"]
+ Task --> Email["Email strategy"]
+ Email -->|fail| Telegram["Telegram strategy"]
+ Telegram -->|fail| SMS["SMS strategy"]
+ Email --> SMTP[("SMTP")]
+ Telegram --> TG[("Telegram API")]
+ SMS --> SMSAPI[("sms.ru")]
+ Task --> DB[("PostgreSQL")]
+```
+
+## Data model
+
+
+
+## Getting started
+
+### Prerequisites
+
+- Docker and Docker Compose
+
+### Configuration
+
+Copy `.env.example` to `.env` and fill in the values:
+
+```bash
+cp .env.example .env
+```
+
+| Variable | Description | Example |
+|---|---|---|
+| `SECRET_KEY` | Django secret key | `django-insecure-...` |
+| `DEBUG` | Debug mode | `False` |
+| `ALLOWED_HOSTS` | Allowed hosts (comma-separated) | `localhost,127.0.0.1` |
+| `CORS_ALLOWED_ORIGINS` | Allowed CORS origins | `http://localhost:8000` |
+| `POSTGRES_*` | Database connection settings | `POSTGRES_DB=notifications` |
+| `CELERY_BROKER_URL` | Celery broker URL (RabbitMQ) | `amqp://user:pass@rabbitmq:5672//` |
+| `EMAIL_*` | Mail server settings | `EMAIL_HOST=smtp.example.com` |
+| `TELEGRAM_TOKEN` | Telegram bot token | `123456:ABC` |
+| `SMS_API_KEY` / `SMS_SENDER` | sms.ru credentials | `SMS_SENDER=Service` |
+| `SMS_TEST` | sms.ru test mode (does not send SMS) | `0` |
+
+### Install & run
+
+Clone the repository and start the services:
+
+```bash
+git clone https://github.com/annaputilovskaya/NotificationService.git
+cd NotificationService
+docker compose up -d --build
+```
+
+Verify Docker is installed (otherwise follow the [official instructions](https://www.docker.com/)):
+
+```bash
+docker --version
+```
+
+Interactive API documentation (Swagger): http://localhost:8000/swagger/
+
+## Tests
+
+Tests run against PostgreSQL. Start the database and run:
+
+```bash
+docker compose up -d db
+python -m pytest --cov
+```
+
+External services (SMTP, Telegram, sms.ru) are mocked. The project was originally checked manually via Postman; automated core and smoke tests are now in place.
+
+## Project structure
+
+```
+config/ # Django settings, Celery, urls
+notifications/ # notifications: model, strategies, Celery task
+recipients/ # recipients
+users/ # users, JWT
+conftest.py # shared pytest fixtures
+pyproject.toml # ruff/pytest/coverage config
+```
+
+## License
+
+Released under the MIT License — see [LICENSE](LICENSE).
diff --git a/README.ru.md b/README.ru.md
new file mode 100644
index 0000000..b0db662
--- /dev/null
+++ b/README.ru.md
@@ -0,0 +1,127 @@
+# Сервис уведомлений
+
+[](https://github.com/annaputilovskaya/NotificationService/actions/workflows/ci.yml)
+[](https://github.com/annaputilovskaya/NotificationService/actions/workflows/ci.yml)
+[](./LICENSE)
+[](https://www.python.org/)
+
+API для надёжной доставки уведомлений пользователям по нескольким каналам — Email, SMS и Telegram — с автоматическим переключением между каналами при сбое.
+
+[English version](README.md)
+
+## Возможности
+
+- **Пользователи** — регистрация по email и паролю, получение и обновление JWT-токена авторизации.
+
+
+
+- **Получатели** — создание, просмотр, изменение и удаление получателей.
+
+
+
+- **Уведомления** — создание уведомления, просмотр одного уведомления и списка.
+
+
+
+- **Надёжная доставка (failover)** — при создании уведомления ставится задача Celery. Стратегии перебираются по порядку **Email → Telegram → SMS**: если канал не сработал, используется следующий. Если ни одна стратегия не сработала для части получателей, попытка повторяется для этих получателей через заданный интервал.
+- **Пагинация** — эндпоинты списков отдают постраничный ответ `{"count": ..., "results": [...]}` (параметр `?page=`).
+- **Логирование** — логи отправки пишутся в консоль и сохраняются в файл.
+- Сервис ориентирован на разовые уведомления; поддержка повторяющихся уведомлений — возможная доработка.
+
+## Стек
+
+- Python 3.12
+- Django REST Framework (Django ORM)
+- PostgreSQL 17
+- Celery + RabbitMQ
+- drf-yasg (Swagger/Redoc)
+
+## Архитектура
+
+```mermaid
+flowchart LR
+ Client["Клиент"] --> API["Django REST API"]
+ API -->|создание уведомления| Task["Celery: send_notification"]
+ Task --> Email["Email strategy"]
+ Email -->|fail| Telegram["Telegram strategy"]
+ Telegram -->|fail| SMS["SMS strategy"]
+ Email --> SMTP[("SMTP")]
+ Telegram --> TG[("Telegram API")]
+ SMS --> SMSAPI[("sms.ru")]
+ Task --> DB[("PostgreSQL")]
+```
+
+## Модель данных
+
+
+
+## Начало работы
+
+### Требования
+
+- Docker и Docker Compose
+
+### Конфигурация
+
+Скопируйте `.env.example` в `.env` и заполните значения:
+
+```bash
+cp .env.example .env
+```
+
+| Переменная | Описание | Пример |
+|---|---|---|
+| `SECRET_KEY` | Секретный ключ Django | `django-insecure-...` |
+| `DEBUG` | Режим отладки | `False` |
+| `ALLOWED_HOSTS` | Разрешённые хосты (через запятую) | `localhost,127.0.0.1` |
+| `CORS_ALLOWED_ORIGINS` | Разрешённые CORS-origins | `http://localhost:8000` |
+| `POSTGRES_*` | Параметры подключения к БД | `POSTGRES_DB=notifications` |
+| `CELERY_BROKER_URL` | URL брокера Celery (RabbitMQ) | `amqp://user:pass@rabbitmq:5672//` |
+| `EMAIL_*` | Настройки почтового сервера | `EMAIL_HOST=smtp.example.com` |
+| `TELEGRAM_TOKEN` | Токен Telegram-бота | `123456:ABC` |
+| `SMS_API_KEY` / `SMS_SENDER` | Доступ к sms.ru | `SMS_SENDER=Service` |
+| `SMS_TEST` | Тестовый режим sms.ru (не отправляет СМС) | `0` |
+
+### Установка и запуск
+
+Склонируйте репозиторий и запустите сервисы:
+
+```bash
+git clone https://github.com/annaputilovskaya/NotificationService.git
+cd NotificationService
+docker compose up -d --build
+```
+
+Проверка версии Docker (если не установлен — [инструкция](https://www.docker.com/)):
+
+```bash
+docker --version
+```
+
+Интерактивная документация API (Swagger): http://localhost:8000/swagger/
+
+## Тесты
+
+Тесты запускаются на PostgreSQL. Поднимите БД и выполните:
+
+```bash
+docker compose up -d db
+python -m pytest --cov
+```
+
+Внешние сервисы (SMTP, Telegram, sms.ru) в тестах замоканы. Ранее проект проверялся вручную через Postman; теперь добавлены автоматизированные тесты ядра и smoke.
+
+## Структура проекта
+
+```
+config/ # настройки Django, Celery, urls
+notifications/ # уведомления: модель, стратегии, задача Celery
+recipients/ # получатели
+users/ # пользователи, JWT
+conftest.py # общие фикстуры pytest
+pyproject.toml # конфиг ruff/pytest/coverage
+```
+
+## Лицензия
+
+Проект распространяется под лицензией MIT — см. [LICENSE](LICENSE).
diff --git a/config/settings.py b/config/settings.py
index 0806c51..aca1454 100644
--- a/config/settings.py
+++ b/config/settings.py
@@ -178,6 +178,7 @@
# Logging
LOG_FILE_NAME = "notifications.log"
LOG_FILE_PATH = os.path.join(BASE_DIR, "logs", LOG_FILE_NAME)
+os.makedirs(os.path.dirname(LOG_FILE_PATH), exist_ok=True)
LOGGING = {
"version": 1,