/okkam-test

okkam-test

Primary LanguagePython

Okkam-test

Задача

Имеется дамп базы data.csv со следующими полями:

  • Date – дата в формате yyyymmdd
  • respondent – уникальный номер респондента
  • Sex – пол респондента (1=М, 2=Ж)
  • Age – возраст респондента
  • Weight – Некая статистика респондента для этого дня

Необходимо:

  1. Загрузить данные в таблицу СУБД SQL на ваш выбор (но не SQLlite)
  2. Разработать API с одним GET эндпоинтом /getPercent который будет выполнять следующий алгоритм:
  • На вход эндпоинта приходят 2 параметра: audience1 и audience2:

    • Аудитории будут приходить в формате SQL синтаксиса, например Age BETWEEN 18 AND 35 или Sex = 2 AND Age >= 18

    • Для каждой из аудиторий отобрать из таблицы всех респондентов, подходящих под параметры

    • Для каждой из аудиторий взять средний Weight респондента этой аудиторий, сгруппировав по их уникальному номеру

    • Далее вычислить процент вхождения второй аудитории в первую, основываясь на среднем Weight. Пример:

      • Имеем первую аудиторию, в которую входят resp1 с avg(Weight)=1, resp2 с avg(Weight)=2 и resp3 с avg(Weight)=3
      • Имеем вторую аудиторию, в которую входят resp2, resp3 и resp4 с avg(Weight) = 2,3,4 соответственно
      • Видим, что аудитории пересекаются респондентами resp2 и resp3, у которых avg(Weight) равен 2 и 3 (Средний Weight не может быть разным у одного и того же респондента). Значит процент вхождения второй аудитории в первую будет равен (2+3) / (1+2+3) = 0.8(3)
  • Предусмотреть варианты:

    • Аудитории могут быть идентичными
    • Аудитории могут не пересекаться вообще
    • Вторая аудитория может быть подмножеством первой
    • Первая аудитория может быть подмножеством второй
  • Отдать респонс в формате {‘percent’: результат}

  1. Обернуть API в Docker контейнер

  2. Подготовить docker-compose конфиг для связки API+СУБД:

    • База данных должна быть создана и наполнена
    • API слушает 80 порт
  3. Опционально:

    • Позаботиться об отказоустойчивости
    • Позаботиться о быстродействии (учитывая, что данные не будут изменены ретроспективно)
    • Порядок в коде – pep8, докстринги, тайпинг, структура проекта

Результат работы и креативы

Всего на задачу ушло 12 часов.

Сделано:

  • развернуты докер-сворм стек, postgres dev, postgres test, api
  • реализован dev stage
  • реализован апи с версионированием на базе fastapi. Работает swaggher и redoc документация
  • приложение полностью синхронное... но, естественно, для меня не проблема и асинхронные вызовы
  • сделана интеграция с бд на базе alemic и SQLAlchemy (автомиграции, модели) - в данном случае я расширил задачу, чтобы показать, что я умею работать с данными инстурментами
  • модель бд расширена мной до двух таблиц (чтобы показать концепцию crud и работу с джойнами в рав запросе)
  • реализована автозагрузка данных в dev базу не через дамп, а через функционал апи sqlalchemy
  • сделан базовый crud
  • аналитический запрос к бд для эндпоинта реализован на чистом sql
  • реализованы проверки входных данных (возраста)
  • сам эдпоинт реализован в виде post-запроса с request body. Это тоже не совпадает с заданным в задаче, но тут я предлагаю просто не слать сырой sql в виде строк по апи, а реализовать нормальный защищенный запрос. В конечном счете в реальных условиях это так и было бы.
  • flake8 соблюден, код аннотирован и добавлены комментарии (на нерусском)

Что не сделано:

  • вообще не успел написать тесты. Задача большая, к сожалению никак не получалось по времени. Тесты могу делать любые юнит на базе pytest, с моками и любым цирком. Если надо, могу функциональное тестирование
  • не выполнен mypy - обычно с mypy много возни, там сейчас выпадает небольшое количество ошибок, но если раскопать, может быть всякое. Не успел.
  • быстродействие соответственно тоже не успел. Надо проверять запрос, остальное все ок
  • "отказоустойчивость" без тестов естественно нет

Разработка локально

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

# postgres
POSTGRES_USER=postgres
POSTGRES_PASSWORD=mybrilliantpassword
POSTGRES_DB=okkam
POSTGRES_SERVER=okkam-postgres-dev
DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_SERVER}:5432/${POSTGRES_DB}

TEST_POSTGRES_USER=postgres
TEST_POSTGRES_PASSWORD=mybrilliantpassword
TEST_POSTGRES_DB=okkam-test
TEST_DATABASE_URL=postgresql://${TEST-POSTGRES_USER}:${TEST-POSTGRES_PASSWORD}@okkam-postgres-test:5432/${TEST-POSTGRES_DB}

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

Запуск и остановка стека

  1. Для vscode создайnt проект code .

  2. Установите poetry окружение и подготовьте линтер. Для этого используйте poetry config virtualenvs.in-project true и команду poetry install --with dev. pyproject.toml находится в папке api/app Не забудьте перезапустить IDE.

  3. Старт и шотдаун:

    • make serve
    • make down
  4. Внутри контейнера можно выполнить:

    • pytest -v -s -x для тестирования
    • используйте python -m IPython для проверок кода
    • mypy --install-types
    • mypy app и flake8 app
  • пересобрать отдельный сервис можно так docker compose up -d --no-deps --build <service-name>

TO-DO

  • docker stack
  • db models
  • init dev db (dump)
  • init and mock test db
  • alembic migration
  • pgadmin for vscode dev
  • crud
  • api models
  • logic
  • endpoint
  • tests
  • mypy
  • docs