/YandexBackendSchool

Тестовое задание для поступления в Яндекс школу бэкенд-разработки

Primary LanguagePython

YandexBackendSchool

Тестовое задание для поступления в Яндекс школу бэкенд-разработки

Алгоритм развёртки

Для развёрки на сервере необходимо:

  • установить Docker
  • установить Docker Compose
  • скопировать репозиторий
  • выполнить из корневой папки репозитория команду docker-compose up -d

Использованые инструменты

В данной реализации были использованы:

  • Docker и Docker Compose для сборки проекта
  • Flask и Flask_restful для обеспечения обработки запросов
  • Gunicorn для обеспечения одновременной обработки нескольких запросов
  • SQLAlchemy и Flask_SQLAlchemy для обеспечения взаимодействия с базой данных
  • MySQL bd (стандартный Docker-образ) в качестве базы данных
  • Cerberus для валидации данных

Структура базы данных

База данных состоит из трёх таблиц:

  • Import таблица с id импортов, единственный столбец:
    • import_id
  • Citizen таблица с информацией о жителях, столбцы:
    • import_id
    • citizen_id
    • town
    • street
    • building
    • apartment
    • name
    • birth_date
    • gender
  • Tie таблица, в которой хранятся родственные связи, столбцы:
    • import_id
    • first_citizen_id
    • second_citizen_id

Реализованные методы

Все ответы имеют вид

{
    "data": "
}

Добавление набора данных о жителях

Definition

Post /imports

Arguments

[
    {
        "citizen_id":int Уникальный идентификатор жителя, неотрицательное число, в разных выгрузках citizen_id не уникален и может
повторяться у разных жителей
        "town":string Название города. Непустая строка, содержащая хотя бы 1 букву или цифру, не более 256 символов.
        "street":string Название улицы. Непустая строка, содержащая хотя бы 1 букву или цифру, не более 256 символов.
        "building":string Номер дома, корпус и строение. Непустая строка, содержащая хотя бы 1 букву или цифру, не более 256 символов.
        "apartment":int Номер квартиры, неотрицательное число.
        "name":string Непустая строка, не более 256 символов.
        "birth_date":string Дата рождения в формате ДД.ММ.ГГГГ (UTC +0). Должна быть меньше текущей даты и должна быть существующей датой
        "gender":string Значения male, female.
        "relatives":list of ints Ближайшие родственники, уникальные значения существующих citizen_id жителей из этой же выгрузки.

    }
]

Response

  • 400 Bad Request в наборе есть неописанные поля / в наборе присутствуют не все поля / значения каких-то полей некорректны / какие-то значения полей null / в данных присутствуют неккоректные родственные связи

  • 201 Created в случае успеха

{
    "data": {
                "import_id":1
             }
}

Изменение информации о жителе в указанном наборе данных

Definition

PATCH /imports/$import_id/citizens/$citizen_id

Arguments

  • "import_id":int идентификатор импорта
  • "citizen_id" идентификатор жителя
{
        должно быть хотя бы одно из полей, значения не могут быть null
        
        "town":string Название города. Непустая строка, содержащая хотя бы 1 букву или цифру, не более 256 символов.
        "street":string Название улицы. Непустая строка, содержащая хотя бы 1 букву или цифру, не более 256 символов.
        "building":string Номер дома, корпус и строение. Непустая строка, содержащая хотя бы 1 букву или цифру, не более 256 символов.
        "apartment":int Номер квартиры, неотрицательное число.
        "name":string Непустая строка, не более 256 символов.
        "birth_date":string Дата рождения в формате ДД.ММ.ГГГГ (UTC +0). Должна быть меньше текущей даты и должна быть существующей датой
        "gender":string Значения male, female.
        "relatives":list of ints Ближайшие родственники, уникальные значения существующих citizen_id жителей из этой же выгрузки. Изменение должно быть двусторонним
}

Response

  • 400 Bad Request в наборе есть неописанные поля / в наборе присутствуют не все поля / значения каких-то полей некорректны / какие-то значения полей null / в данных присутствуют неккоректные родственные связи

  • 200 в случае успеха возращается актуальная информация об указанном жителе

{
    "data": {
                "citizen_id": 3,
                "town": "Москва",
                "street": "Льва Толстого",
                "building": "16к7стр5",
                "apartment": 7,
                "name": "Иванова Мария Леонидовна",
                "birth_date": "23.11.1986",
                "gender": "female",
                "relatives": []
            }    
}

Выдача списка всех жителей для указанного набора данных

Definition

GET /imports/$import_id/citizens

Arguments

  • "import_id":int идентификатор импорта

Response

  • 400 Bad Request не корректный идентификатор импорта

  • 200 в случае успеха возвращает список всех жителей для указанного набора данных

{
    "data": [
                {
                    "citizen_id": 1,
                    "town": "Москва",
                    "street": "Льва Толстого",
                    "building": "16к7стр5",
                    "apartment": 7,
                    "name": "Иванов Иван Иванович",
                    "birth_date": "26.12.1986",
                    "gender": "male",
                    "relatives": [2,3] // id родственников
                },
                {
                    "citizen_id": 2,
                    "town": "Москва",
                    "street": "Льва Толстого",
                    "building": "16к7стр5",
                    "apartment": 7,
                    "name": "Иванов Сергей Иванович",
                    "birth_date": "01.04.1997",
                    "gender": "male",
                    "relatives": [1] // id родственников
                },
                {
                    "citizen_id": 3,
                    "town": "Москва",
                    "street": "Льва Толстого",
                    "building": "16к7стр5",
                    "apartment": 7,
                    "name": "Иванова Мария Леонидовна",
                    "birth_date": "23.11.1986",
                    "gender": "female",
                    "relatives": [1]
                },
                ...
            ]
}

Выдача информации о количестве подарков, даримых жителями

Definition

GET /imports/$import_id/citizens/birthdays

Arguments

  • "import_id":int идентификатор импорта

Response

  • 400 Bad Request не корректный идентификатор импорта

  • 200 в случае успеха возвращает жителей и количество подарков, которые они будут покупать своим ближайшим родственникам (1-го порядка), сгруппированных по месяцам из указанного набора данных. Ключом должен быть месяц (нумерация должна начинаться с единицы, "1" - январь, "2" - февраль и т.п.). Если в импорте в каком-либо месяце нет ни одного жителя с днями рождения ближайших родственников, значением такого ключа должен быть пустой список.

{
    "data": {
                "1": [],
                "2": [],
                "3": [],
                "4": [
                        {
                            "citizen_id": 1,
                            "presents": 1,
                        }
                     ],
                "5": [],
                "6": [],
                "7": [],
                "8": [],
                "9": [],
                "10": [],
                "11": [
                        {
                            "citizen_id": 1,
                            "presents": 1
                        }
                      ],
                "12": [
                        {
                            "citizen_id": 2,
                            "presents": 1
                        },
                        {
                            "citizen_id": 3,
                            "presents": 1
                        }
                ]
            }
}

Выдача статистики о возрасте жителей

Definition

GET /imports/$import_id/towns/stat/percentile/age

Arguments

  • "import_id":int идентификатор импорта

Response

  • 400 Bad Request не корректный идентификатор импорта

  • 200 в случае успеха возвращает статистику по городам для указанного набора данных в разрезе возраста (полных лет) жителей: p50, p75, p99, где число - это значение перцентиля. Расчеты производятся используя текущую дату (UTC). Значения перцентилей округляются до 2х знаков после запятой.

{
    "data": [
                {
                    "town": "Москва",
                    "p50": 35.0,
                    "p75": 47.5,
                    "p99": 59.5
                },
                {
                    "town": "Санкт-Петербург",
                    "p50": 45.0,
                    "p75": 52.5,
                    "p99": 97.15
                }
            ]
}

Что означает:
"p50": 35.0, - 50% жителей меньше 35 лет
"p75": 47.5, - 75% жителей меньше 45 с половиной лет

Тестирование

Для тестирования написан скрипт test.py

Он генерирует валидные данные для post-запроса с помощью библиотеки Faker, а затем различными способами портит некоторую часть этих данных и проверяет, чтобы сервер правильно реагировал и на валидные, и на невалидные запросы.

Запросы можно посылать в том числе и одновременно.

Для запуска необходимо помимо стандартных библиотек иметь:

  • Faker
  • futures

Запуск тестов осуществляется с помощью команды python test.py (необходимые параметры в help)