Skip to content
This repository was archived by the owner on Jan 4, 2023. It is now read-only.

technothecow/candy_delivery_app

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Candy Delivery App

Простая реализация простого задания второго испытательного этапа в школу бекэнд-разработки Яндекса.

Обработчики и краткое описание их реализации

1: POST /couriers

В качестве СУБД в целом по проекту использовался SQLite из-за простоты работы с ним и отсутствия необходимости в большем функционале.

Так вот, переходя к курьерам, для них создана таблица со следующими колонками:

  • courier_id - уникальный идентификатор курьера, являющийся целочисленным первичным ключом
  • courier_type - тип курьера, строка
  • regions - регионы, JSON
  • working_hours - рабочий график, JSON
  • assign_time - время последнего назначения заказов, строка
  • courier_type_when_formed - тип курьера во время формирования заказа (требуется для расчета прибыли), строка
  • earnings - заработанные деньги курьера, целое число, начальное значение
  • delivery_time_for_regions - информация о времени доставки по районам (требуется для расчета рейтинга), mutable nested JSON
  • last_action_time - время последнего действия, строка
  • orders - внешняя зависимость

Названия у части колонок аналогичны данным в запросе полям, остальные заполняются по ходу исполнения курьером своей работы и обработки запросов

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

2: PATCH /couriers/$courier_id

Изначально обработчик просто проводил валидацию поступаемых значений и вносил изменения в базу данных. Потом были добавлены всяческие проверки на то, все ли заказы курьера все еще подходят ему.

Если при проверке временных интервалов, или, скажем, регионов, вопросов не возникает, то при превышении лимита на вес, сначала "выкидываются" самые тяжелые заказы до тех пор, вес заказов не дойдет до нормы.

Бывают случаи, когда у курьера пропадают все оставшиеся заказы, тогда его развоз считается закрытым. И да, если у курьера до изменений не было заказов, развоз засчитан не будет.

3: POST /orders

Возвращаемся к описанию колонок. Для заказов создана следующая структура:

  • order_id - уникальный идентификатор заказа, целочисленный первичный ключ
  • weight - вес заказа, число с плавающей точкой
  • region - номер региона заказа, целое число
  • delivery_hours - время для доставки, JSON
  • complete_time - время завершения заказа, строка
  • courier_id - идентификатор назначенного курьера, целое число, внешний ключ
  • courier - назначенный курьер, внешняя зависимость

Тут все намного проще: два курьерских поля созданы для того, чтобы заказы было удобнее "цеплять", время завершения заказа берется из обработчика №5, а все остальные поля поступают в запросе.

Сам обработчик состоит из детальной валидации входных данных с обработкой всевозможных ошибок и, в случае их отсутствия, сохранения новой информации в базу данных.

4: POST /orders/assign

Мы перешли к одному из самых интересных обработчиков.

Мое решение многим проблемам можно охарактеризовать наречием "колхозно", соглашусь, но в данном случае цель была сделать проект, а не написать идеальный алгоритм.

Итак, сначала производится валидация данных, затем идет проверка на прикрепленные к курьеру заказы. Если среди таковых имеются невыполненные: без времени выполнения, они возвращаются курьеру. Иначе запрашиваются все "свободные" заказы: те, что не имеют за собой курьера, и происходит проверка на соответствие региона, времени и веса курьера.

Регион проверяется очевидным способом: проверяется наличие региона заказа среди регионов доставки курьера.

По весу немного интереснее: заказы отсортированы по возрастанию веса, так, чтобы курьер мог набрать как можно больше.

Проверку времени я не зря оставил напоследок. Если вкратце, то в примере двух времен: 12:00-18:00 и 14:00-20:00 идет проверка, если 12:00 < 14:00 < 18:00 или 12:00 < 20:00 < 18:00 или 14:00 < 12:00 < 20:00 или 14:00 < 18:00 < 20:00. Если хоть одно из этих условий верно, то времена пересекаются и подходят. Естественно, описание очень сильно упрощено: как устроено на самом деле можно посмотреть в api/logic.py.

Далее, если заказов нет, то возвращается пустой список. В противном случае, курьеру записывается время назначения заказов, время последнего действия и его тип при формировании заказа. Ну и, соответственно, возвращается список заказов.

5: POST /orders/complete

Тут все снова сначала было несложно: валидация данных, запись в заказ времени исполнения.

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

На словах все не так сложно, да? Кто бы мог подумать, что используемый модуль sqlalchemy не умеет нормально работать с JSON'ами. В предыдущих обработчиках мы либо полностью их переписывали, либо читали. Здесь же их нужно модифицировать: добавлять время районам, на что изменения, после коммита, просто исчезали, аки мой отец в пять лет. Пришлось искать решение. К счастью, эта библиотека прямиком из палеозоя (первый релиз на гитхабе был в начале 2006 года), и трудами предшественников были созданы костыли, которыми я не побрезговал воспользоваться. Если бы на этом проблемы закончились... Потом оказалось, что сюрприз-сюрприз, ключи для словарей из чисел превращаются в строки. Еще, значит, время было потрачено на поиск и решение этой проблемы. И так практически с каждым из обработчиков, просто если бы я вдавался в еще большие детали "краткого описания реализации", можно было бы книгу печатать.

6: GET /couriers/$courier_id

Главная сложность этого обработчика только в том, чтобы придумать максимально менее стремное решение для расчета среднего времени доставки по районам.

Не буду судить, как я с этой задачей справился, но работает это так: после того, как курьер сделает действие (доставка или запрос заказов), будет записано его время так, чтобы в следующий раз записать, сколько времени было потрачено на его выполнение. Я очень долго ленился садиться за создание проекта, до того момента, как мне в голову не стукнуло описанное решение.

Использованные python-библиотеки

Здесь будут описаны только главные, на которых стоит все приложение. Полный список доступен в requirements.txt.

  • Flask - простейший фреймворк, самое то для подобной задачи
  • Flask RESTful - куда без него, у нас же REST API, верно?..
  • SQLAlchemy - ORM для упрощения взаимодействия с базой данных
  • sqlalchemy-json - забудьте про слово "упрощение" из прошлого пункта - это тот самый "костыль", без которого от листка бумаги в качестве ORM было бы больше пользы
  • pytest - я не настолько плохой человек, чтобы сделать 40 штук неавтоматизированных тестов
  • requests - тесты должны отправлять запросы
  • waitress - простенькая библиотека, чтобы запустить проект в продакшн
  • datetime - вы правда думали, что я стану делать собственный алгоритм для счета времени?

Установка, развертывание и запуск сервиса

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

Тут все несложно: сначала нужно загрузить всю папку проекта в удобную директорию (лично я сделал это используя подключение sftp), установить интерпретатор Python одной из последних версий (я использовал 3.8, другие - на ваш риск) и еще несколько пакетов. Команда для терминала ниже.

sudo apt-get update
sudo apt-get install python3 python3-pip python3-dev gcc git nginx

Далее нужно открыть терминал в каталоге проекта и прописать следующую команду для установки библиотек:

pip3 install -r requirements.txt

На данном этапе индикатором, что все идет по плану, является заспамленный белым (иногда желтым) текстом терминал. Если текст уместился всего в пару строчек или окрашен в красные тона, вы что-то сделали не так. Попробуйте убрать тройку после команды pip, проверить, установлен ли python корректно и прописан ли он в PATH. Если ничего из перечисленного не помогло, то пожалуйста, свяжитесь со мной или используйте уже развернутый сервис на выданной вами машине.

Вот, в принципе, и все: осталось только прописать заветную команду для запуска файла app.py, находясь в директории проекта:

python3 app.py

P.S. По умолчанию сервер запускается на порту 8080, как и сказано в задании. При необходимости его изменить, запишите нужный вам в файле app.py, в последней строчке, после port=, вместо 8080

Если требуется, чтобы при перезагрузке машины, сервис автоматически запускался, то необходимо создать файл-скрипт и добавить строчку в crontab. Делается это следующим образом (необходимо заполнить пропуски):

cat > <название скрипта>
#!/bin/bash
cd <путь до проекта>/
python3 app.py
^C
chmod a+x <название скрипта>
crontab -e

Далее в открывшемся редакторе nano в конце добавить строчку: @reboot <путь до скрипта>/<название скрипта>, зажать ctrl+x, затем отпустить и нажать enter.

Все, на этом развертка проекта завершена.

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

Для запуска тестов необходимо установить вышеупомянутые библиотеки pytest и requests, изменить адрес в переменной ADDRESS в файле tests/test.py на адрес сервера и прописать в терминал следующую команду, находясь в папке проекта:

pytest tests/test.py -vv

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

Примечание: тесты созданы только для пустой базы данных и последовательного запуска.

Послесловие

Сомневаюсь, что кто-то прочитает весь текст целиком, ведь хоть на проверку заданий и больше месяца, но, судя по тому, что было сказано на прямой трансляции, придется проверять по нескольку десятков работ в день (даже в праздники!), и тем не менее, хочу поблагодарить организаторов за создание подобных мероприятий. Сначала я был очень зол на всю команду за откровенно грубый ответ на мое замечание о признанной впоследствии опечатке в задачи, а потом и на ожидание в две недели (вместо максимальных десяти дней, написанных в письме), но потом, посмотрев стрим и получив кучу оперативных ответов на мои, местами, возможно, не очень уместные вопросы, мне становится приятно, что подобные вещи еще делаются. К компании Яндекс у меня такое же отношение, как и у любого другого нормального человека после новостей о яндекс.телефоне и отношении к курьерам и таксистам, но проекты вроде этой школы, вселяют в меня надежду, что еще не все потеряно. Не думаю, что пройду испытание: проект действительно получился сыроват, но я правда получил удовольствие от работы над этим заданием. Спасибо.

About

No description, website, or topics provided.

Resources

License

Stars

0 stars

Watchers

1 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages