Here building a backend service & a database for a food delivery platform, with the following 2 raw datasets:
- Restaurants data
Location: static_files/restaurant_with_menu.json
This dataset contains a list of restaurants with their menus and prices, as well as their cash balances. This cash balance is the amount of money the restaurants hold in their merchant accounts on this platform. It increases by the respective dish price whenever a user purchases a dish from them.
- Users data
Location: static_files/users_with_purchase_history.json
This dataset contains a list of users with their transaction history and cash balances. This cash balance is the amount of money the users hold in their wallets on this platform. It decreases by the dish price whenever they purchase a dish.
- List all restaurants that are open at a certain datetime
- List top y restaurants that have more or less than x number of dishes within a price range, ranked alphabetically. More or less (than x) is a parameter that the API allows the consumer to enter.
- Search for restaurants or dishes by name, ranked by relevance to search term
- Process a user purchasing a dish from a restaurant, handling all relevant data changes in an atomic transaction. Do watch out for potential race conditions that can arise from concurrent transactions.
Set deploy_as_docker = True
in config.py
to run in Docker. Set deploy_as_docker = False
in config.py
to run locally.
- Install Postgres db:
sudo apt update
sudo apt install postgresql postgresql-contrib
- Create db credentials:
sudo -iu postgres psql
CREATE DATABASE foodapp_db;
CREATE USER foodapp WITH PASSWORD 'foodapp';
GRANT ALL PRIVILEGES ON DATABASE foodapp_db TO foodapp;
- Check if db is created typing:
\l
- Exit PostgreSQL prompt typing:
\q
(Extra):
- DROP db if need.
DROP DATABASE foodapp_db;
- install python3, Create & activate virtual env(say, v3), then install project requirements:
sudo apt update
sudo apt install python3-venv
sudo apt install python3-pip
python3 -m venv v3
source v3/bin/activate
pip3 install -r requirements.txt
Ref: create-python-virtual-environments, install-pip-on-ubuntu, install python3
- Preload Data, ETL: run
./etl_run.sh
on bash. - For App deployment: run
gunicorn --workers 3 --worker-class=gevent --worker-connections=100 -b 0.0.0.0:9341 wsgi:app --timeout 0 -k gevent
on bash.
pytest test_file.py --html=pytest_report.html
- It will generate
pytest_report.html
as unittest report in current dir.
- Run docker-compose
sudo ./run.sh docker_compose
- Hit
http://127.0.0.1:9341/runetl/
with method POST to preload data. Equivalent CURL:
curl --location --request POST 'http://127.0.0.1:9341/runetl/' --data-raw ''
- List all restaurants that are open at a certain datetime.
methods=['GET']
http://127.0.0.1:9341/checkslot/
Example JSON payload:
{
"datetime": "21/03/2022 01:09 PM"
}
Equivalent CURL:
curl --location --request GET 'http://127.0.0.1:9341/checkslot/' --header 'Content-Type: application/json' --data-raw '{ "datetime": "21/03/2022 01:09 PM"}'
- Output:
{
"available_restaurants": [
"024 Grille",
"100% de Agave",
"12 Baltimore",
"15Fifty - Sheraton - Starwood",
"1808 American Bistro",
"2 Cents",
"24 Plates",
"247 Craven", ...]
}
- List top y restaurants that have more or less than x number of dishes within a price range
methods=['GET']
http://127.0.0.1:9341/price_range_filter/
Example JSON payload:
{
"x": "4",
"y": 3,
"price_range_low": 10,
"price_range_high": 50
}
Equivalent CURL:
curl --location --request GET 'http://127.0.0.1:9341/price_range_filter/' --header 'Content-Type: application/json' --data-raw '{ "x": "4", "y": 3, "price_range_low": 10, "price_range_high": 50 }'
- Output:
{
"less_than_x_dishes_restaurants": [
"Mezzodi's",
"Sweet Basil",
"The Purple Pig"
],
"more_than_x_dishes_restaurants": [
"'Ulu Ocean Grill and Sushi Lounge",
"13 Coins",
"2G Japanese Brasserie"
]
}
- Search for restaurants or dishes by name, ranked by relevance to search term.
methods=['GET']
http://127.0.0.1:9341/search/
Example JSON payload:
{
"name": "Salad",
"type": "dish"
}
or
{
"name": "Salad",
"type": "restaurant"
}
Example Equivalent CURL:
curl --location --request GET 'http://127.0.0.1:9341/search/' --header 'Content-Type: application/json' --data-raw '{ "name": "Salad", "type": "dish" }'
- Output:
{
"relevant_dishes": [
"Salad",
"Salade",
"Salad of tortellini",
"cold corned beer and potato salad",
"Salad -- Lettuce"
]
}
- Process a user purchasing a dish from a restaurant, handling all relevant data changes in an atomic transaction.
methods=['POST']
http://127.0.0.1:9341/buy/
Example JSON payload:
{
"user_name":"Alma Meadows",
"dish_name": "INDV. GULF TROUT BROILED",
"dish_price": "13.85",
"restaurant_name": "Lefty's",
"userid": "12",
"buyingdate": "03/04/2018 06:41 AM"
}
Equivalent CURL:
curl --location --request POST 'http://127.0.0.1:9341/buy/' --header 'Content-Type: application/json' --data-raw '{ "user_name":"Alma Meadows", "dish_name": "INDV. GULF TROUT BROILED", "dish_price": "13.85", "restaurant_name": "Lefty'\''s", "userid": "12", "buyingdate": "03/04/2018 06:41 AM" }'
- Output:
{
"response": "Successful Transaction"
}
- Load Data (ETL), init db again if need.
methods=['POST']
http://127.0.0.1:9341/runetl/
Equivalent CURL:
curl --location --request POST 'http://127.0.0.1:9341/runetl/' --data-raw ''
- Output:
{
"response": "Data loading... Done"
}
- Run unittest: It will generate a file
pytest_report.html
as unittest results.
methods=['POST']
http://127.0.0.1:9341/unittest/
Equivalent CURL:
curl --location --request POST 'http://127.0.0.1:9341/unittest/' --data-raw ''
- Output:
pytest_report.html
Report generated on 04-Mar-2022 at 09:15:37 by pytest-html v3.1.1
Environment
Packages {"pluggy": "1.0.0", "py": "1.11.0", "pytest": "7.0.1"}
Platform Linux-4.15.0-88-generic-x86_64-with-glibc2.29
Plugins {"html": "3.1.1", "metadata": "1.11.0"}
Python 3.8.10
Summary
6 tests ran in 0.98 seconds.
6 passed, 0 skipped, 0 failed, 0 errors, 0 expected failures, 0 unexpected passes
Results
Result Test Duration Links
Passed test_file.py::Test::test_0_checkslot_api_0 0.22
No log output captured.
Passed test_file.py::Test::test_1_price_range_filter_api_1 0.13
No log output captured.
Passed test_file.py::Test::test_2_search_api_2 0.36
No log output captured.
Passed test_file.py::Test::test_3_buy_api_3 0.01
No log output captured.
Passed test_file.py::Test::test_4_etl_users_data_4 0.04
No log output captured.
Passed test_file.py::Test::test_5_etl_users_data_5 0.04
No log output captured.