Skyeng-test

Данный проект реализует тестовую задачу от skyeng и не может использоваться в качестве рабочего приложения.

Задача

Нужно повысить качество передаваемого кода на ревью, чтобы ускорить процесс разработки и поставки более качественного кода в проекты. Для этого создайте сервис проверки файлов с выполненными задачами.

Основные модули, из которых состоит система:

  1. Модуль авторизации и регистрации пользователя.
  2. Пользователь должен иметь возможность зарегистрироваться в системе по паре почта и пароль, а также войти по этим данным.
  3. Модуль загрузки файлов с исходным кодом. Авторизованный пользователь должен иметь возможность загрузить файл в систему, при этом информация о загруженном файле должна сохраниться в базу данных с пометкой о том, что файл новый. Помимо этого, пользователь должен иметь возможность удалить файл или перезаписать, таким образом, в базе данных должны быть соответствующие пометки. Очень важно не давать загружать файлы, у которых расширение не равно “.py”.
  4. Модуль проверки соответствия кода общепринятым правилам. По расписанию выполняется задача на автоматическую проверку кода для новых загруженных или перезагруженных файлов. По итогу проверки необходимо сформировать отложенную задачу на отправку письма пользователю с информацией о проведенной проверке. Важно хранить лог каждой проверки для каждого файла, который находится в списке файлов у пользователя в системе.
  5. Модуль отправки письма с уведомлением пользователю. Необходимо реализовать обработку задач из очереди на отправку уведомлений пользователю о результате проверки его файла. При этом, важно в логах проверки отмечать факт отправки сообщения пользователю.
  6. Модуль отчета о проведенных проверках

В системе также должен быть интерфейс, в котором пользователь может просмотреть результаты выполненных проверок с пометками об отправке отчета пользователю на почту. Рекомендации:

  • реализовать интерфейс можно с помощью uikit Bootstrap
  • для работы с очередями лучше использовать celery в связке с redis
  • для запуска периодических задач можно использовать celery beat

Критерии решения:

  • Интерфейс системы должен содержать следующие экраны: вход, регистрация, список загруженных файлов, отчет проверки по каждому файлу отдельно, изменение файла, удаление файла
  • Реализованы все 5 модулей
  • Для разных сервисов созданы отдельные контейнеры (django, postgresql, redis, celery, при необходимости список можно самостоятельно расширять), все оформлено в docker-compose файле, при необходимости можно создавать вспомогательные Dockerfile
  • Проект готов быть размещен на удаленном сервере
  • Интерфейс понятен и соответствует базовым требованиям системы
  • Решение выложено на github.com

Сделано

Контейнеры:

  • mongodb
  • mongo-express
  • redis
  • celery
  • flower
  • api (fastapi)
  • web (streamlit)

Логика:

  • user (регистрация/авторизация по email)
  • интеграция с почтовым клиентом
  • загрузка файла и сохранение файла в бд (допустимо сохранять только .py)
  • удаление файла
  • изменение файла
  • проверка файла в очереди
  • помещение в очередь через шедалер
  • получение результата
  • запись в бд результата проверки
  • отправка письма юзеру с результатом проверки
  • вести лог - когда проверено и что письмо отправлено
  • интерфейс юзера
  • аутентификация/авторизация
  • список файлов
  • удаление
  • исправление
  • скачивание
  • результат проверки
  • отметка об отправке сообщения

Особенности решения

  • в качестве БД выбран mongodb, который лучше соответствует идее хранения документов
  • серверный апи реализован на fastapi
  • серверное приложения асинхронное, в т.ч. и отправка email
  • веб-интерфейс сделан на базе streamlit (дашбор с интерфейсом на python) - т.е. это прототип, а я питонист, то возиться в непитоньем контексте считаю неправильныым в рамках тестовой задачи
  • файлы, отправляемые пользователям, не хранятся на диске ни в какой из моментов на пути данных

Известные проблемы и что требует доработки

  • не реализована валидация email пользователя при регистрации. Так-же нет смены/изменений данных пользователя
  • брокер в celery реализован на redis, а бекенд - на mongodb. Технически я мог быы отказаться от redis в этой задаче и реализовать все целиком на mongodb. Но, хотелось предоставить админку очереди (flower) в отдельном контейнере. Т.к. я не знаю банального способа получать логи в flower из mongo, брокером сделан redis. Альтернативным решением было бы запускать дашборд очереди в контейнере celery и предоставлять порт наружу.
  • выполнен только development вариант для работы локально, т.к. из задания неясно как и где будет деплоиться приложение. Соответственно приложение не готово к реальному деплою.
  • во всем стеке нет тестов. Ну на тесты задачи не было, что-быы не терять время, они не делались. Соответственно работоспособность стека не гарантируется.
  • ряд операций над данными требует транзакций (это отмечено в коде) - кпримеру запись в бд и отправка email. Для mongodb нет тривиального решения для транзакций - требуется запуск бд в режиме репликации. Это можно сделать, но в данном контексте это уже избуточная задача.
  • требуется реализация более корректного контроля версии проверяемого файла (сейчас мы просто обновляем данные при каждом аплайе со стороны пользователя)
  • как таковая проверка в очереди не делается, т.к. в задаче не указано что конкретно м собираемся проверять
  • celery синхронный, а функции приложения асинхронные. На самом деле, к примеру отправку email можно реализовать внутри серверной части в виде middleware... но т.к. в задаче значился celery, то сделан самый простой вариант - в контейнере с celery подняты зависимости для контейнера с api.

Примеры интерфейса

Логин/регистрация

1694628427543

Авторизованный пользователь

1694629405350

Загружен файл и ожидает проверки

1694629454006

Проверка выполнена

1694629529485

Запуск локально

Для запуска необходимо клонирвоать репозиторий и поместить в корень репозитория .env файл следующего содержания

# mongo
DEV_ROOT_USERNAME=skyeng-mongo
DEV_ROOT_PASSWORD=mybrilliantpasss
MONGODB_URL=mongodb://${DEV_ROOT_USERNAME}:${DEV_ROOT_PASSWORD}@skyeng-mongo:27017/
DB_NAME=skyeng-mongo

# mongo expres
ADMINUSERNAME=admin
ADMINPASSWORD=mybrilliantpassword

# broker
TIME_TO_CHECK=60

# redis broker url
CELERY_BROKER_URL=redis://skyeng-redis:6379/0

# mailing
MAIL_USERNAME="my email login"
MAIL_PASSWORD="my email passsword"
MAIL_FROM="my email from"
MAIL_PORT=587
MAIL_SERVER="smtp.gmail.com"
MAIL_FROM_NAME="Python Check Service"
MAIL_STARTTLS=true
MAIL_SSL_TLS=false
USE_CREDENTIALS=true
VALIDATE_CERTS=true

# JWT secret key
# TOKEN_EXPIRES_MINUTES=
SECRET_KEY=secret_pass_for_jwc
ALGORITHM=HS256

Обратите внимание, что в данном случае в качестве почтовго сервера использовался публичный сервер gmail. Для реализации такого подхода вам потребуется использовать специально созданный логин/пароль для гостевого доступа. О том как это сделать смотрите справку gmail.

Промежуток времени для регулярной проверки файлов выбран в 60 секунд. В можете так-же сменить алгоритм шифрования для JWT.

Что-бы линтер IDE увидел зависимости проекта, вы можете установить необходимые пакеты из requirements-dev.txt d виртуальное окружение проекта. Устанавливать вручную зависимости для запуска не нужно - все поднимается докером в контейнерах.

Вам потребуется docker compose 3.8 и утилита make для запуска стека.

Старт и остановка dev стека

  • make serve to run dev mode services
  • make down shut down all services
  • rebuild and rerun single service docker compose up -d --no-deps --build <service-name>

Ссылки на локальные ресурсы, которые вы можете использовать для контроля работоспособности стека.

Общее затраченное время

33 часа