🍎 Initial upload of the fridges api!

This commit is contained in:
rzmk 2022-03-08 01:10:30 -05:00
commit 89eb11b367
8 changed files with 494 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
__pycache__
venv

42
README.md Normal file
View file

@ -0,0 +1,42 @@
<!-- PROJECT LOGO -->
<br />
<div align="center">
<a href="https://github.com/rzmk/fridges-api">
<img src="fridge.svg" alt="Fridge" width="80" height="80">
</a>
<h3 align="center">fridges-api</h3>
<div align="center">
<a href="#"><strong>Try this server using Binder! »</strong></a>
<br />
</div>
</div>
# Fridges API
A REST API of fridges with various foods like fruits built with Flask using Python.
Built from the Backend (**Rest APIs and FLASK**) Workshop by [@lucentfong](https://github.com/lucentfong) for [@HackRU](https://github.com/HackRU).
![Preview](preview.gif)
## File Details
- `api.py` - Flask API server, can be ran with command `flask run`.
- `api_test.py` - Various HTTP requests using the `requests` module to test the local flask server endpoints. You can also instead use [Postman](https://www.postman.com/) or [Thunder Client on VSCode](https://marketplace.visualstudio.com/items?itemName=rangav.vscode-thunder-client).
- `requirements.txt` - can be used with command `pip install -r requirements.txt` to install all virtual environment dependencies (run this command _after activating your virtual environment_). You can instead solely install `flask` for the server.
## Dependencies to Install
For the Flask server:
- `flask`
For the API testing script:
- `requests`
## Acknowledgements/Credits
- [@lucentfong](https://github.com/lucentfong)
- [@HackRU](https://github.com/HackRU)

129
api_test.py Normal file
View file

@ -0,0 +1,129 @@
import requests
from colorama import init, Fore, Back, Style
from pprint import pprint
# Headers for all requests in JSON format
headers = {
"Accept": "application/json",
"Content-Type": "application/json"
}
# Terminal colored text setup using colorama
init()
def cprint(text):
print(Fore.CYAN + text + Fore.RESET)
def mprint(text):
print(Fore.MAGENTA + text + Fore.RESET)
# [=ENDPOINT REQUESTS=]
# GET /fridge
def get_fridges():
url = "http://localhost:5000/fridge"
r = requests.get(url, headers=headers)
return r.json()
# Print endpoint and response
cprint("GET /fridge")
pprint(get_fridges())
# GET /fridge/<name>
def get_fridge(name):
url = f"http://localhost:5000/fridge/{name}"
r = requests.get(url, headers=headers)
return r.json()
name = "Samsung Smart Fridge"
cprint("GET /fridge/<name>")
mprint(f"name: {name}")
pprint(get_fridge(name))
# GET /fridge/<name>/food
def get_fridge_food_list(name):
url = "http://localhost:5000/fridge/" + name + "/food"
r = requests.get(url, headers=headers)
return r.json()
name = "Samsung Smart Fridge"
cprint("GET /fridge/<name>/food")
mprint(f"name: {name}")
pprint(get_fridge_food_list(name))
# POST /fridge/<name>/add
def add_food_to_fridge(name, food):
url = "http://localhost:5000/fridge/" + name + "/add"
data = {
"name": food["name"],
"quantity": food["quantity"],
"color": food["color"],
"shape": food["shape"],
"group": food["group"]
}
r = requests.post(url, headers=headers, json=data)
return r.json()
name = "Samsung Smart Fridge"
data = {
"name": "orange",
"quantity": 1,
"color": "orange",
"shape": "round",
"group": "fruit"
}
cprint("POST /fridge/<name>/add")
mprint(f"name: {name}")
mprint(f"data: {data}")
pprint(add_food_to_fridge(name, data))
# PUT /fridge/<name>/eat
def eat_food_from_fridge(name, fname):
url = f"http://localhost:5000/fridge/{name}/eat"
r = requests.put(url, headers=headers, json={"fname": fname})
return r.json()
name = "Samsung Smart Fridge"
fname = "banana"
cprint("PUT /fridge/<name>/eat")
mprint(f"name: {name}")
mprint(f"fname: {fname}")
pprint(eat_food_from_fridge(name, fname))
# GET /fridge/<name>/food/<fname>/quantity
def get_food_quantity(name, fname):
url = f"http://localhost:5000/fridge/{name}/food/{fname}/quantity"
r = requests.get(url, headers=headers)
return r.json()
name = "Samsung Smart Fridge"
fname = "banana"
cprint("GET /fridge/<name>/food/<fname>/quantity")
mprint(f"name: {name}")
mprint(f"fname: {fname}")
pprint(get_food_quantity(name, fname))
# GET /fridge/<name>/food/<fname>/shape
def get_food_shape(name, fname):
url = f"http://localhost:5000/fridge/{name}/food/{fname}/shape"
r = requests.get(url, headers=headers)
return r.json()
name = "Samsung Smart Fridge"
fname = "banana"
cprint("GET /fridge/<name>/food/<fname>/shape")
mprint(f"name: {name}")
mprint(f"fname: {fname}")
pprint(get_food_shape(name, fname))

208
app.py Normal file
View file

@ -0,0 +1,208 @@
from flask import Flask, render_template, jsonify, request
"""
FRIDGE API
fridge is an object that stores food:
- List of foods
- Name
- Price
food is an object with certain properties:
- Quantity
- Color
- Shape
- Name
[=SAMPLES=]
Sample fridge:
{
"name": "Samsung Smart Fridge",
"price": "10000.0",
"food": []
}
Sample food:
{
"name": "banana",
"quantity": 3,
"color": "yellow",
"shape": "crescent",
"group": "fruit"
}
[=ENDPOINTS=]
GET /fridge
- Return a list of all fridges we own
GET /fridge/<name>
- Returns a fridge with the given name
GET /fridge/<name>/food
- Returns a list of food that fridge <name> stores
PUT /fridge/<name>/eat
- data:{fname:"fname", quantity:"q"}
- Remove "q" amount of food "fname" from fridge "name"
POST /fridge/<name>/add
- data:{fname:"fname", (details...), quantity:"q"}
- Add new type of food to fridge
GET /fridge/<name>/food/<fname>/quantity
- Returns the quantity of food "fname" in fridge "name"
GET /fridge/<name>/food/<fname>/shape
- Returns the shape of food "fname" in fridge "name"
"""
# Initial data
fridges = [
{
"name": "Samsung Smart Fridge",
"price": "10000.0",
"food": [
{
"name": "banana",
"quantity": 3,
"color": "yellow",
"shape": "crescent",
"group": "fruit"
},
{
"name": "apple",
"quantity": 2,
"color": "red",
"shape": "round",
"group": "fruit"
},
{
"name": "milk",
"quantity": 1,
"color": "white",
"shape": "round",
"group": "drink"
}
]
},
{
"name": "LG Smart Fridge",
"price": "5000.0",
"food": [
{
"name": "banana",
"quantity": 2,
"color": "yellow",
"shape": "crescent",
"group": "fruit"
},
{
"name": "watermelon",
"quantity": 1,
"color": "green",
"shape": "round",
"group": "fruit"
},
{
"name": "water",
"quantity": 1,
"color": "blue",
"shape": "round",
"group": "drink"
}
]
}
]
app = Flask(__name__)
@app.route("/")
def home():
return render_template("index.html")
# GET /fridge
# - Return a list of all fridges we own
@app.route("/fridge", methods=["GET"])
def get_fridges():
return jsonify({ "fridges": fridges })
# GET /fridge/<name>
# - Returns a fridge with the given name
@app.route("/fridge/<string:name>", methods=["GET"])
def get_fridge(name):
for fridge in fridges:
if fridge["name"] == name:
return jsonify(fridge)
return jsonify({ "message": "Fridge not found" })
# GET /fridge/<string:name>/food
# - Returns a list of food that fridge <name> stores
@app.route("/fridge/<string:name>/food", methods=["GET"])
def get_fridge_food_list(name):
for fridge in fridges:
if fridge["name"] == name:
return jsonify({ "food": fridge["food"] })
return jsonify({ "message": "Fridge not found" })
# PUT /fridge/<string:name>/eat
# - data:{fname:"fname", quantity:"q"}
# - Remove "q" amount of food "fname" from fridge "name"
@app.route("/fridge/<string:name>/eat", methods=["PUT"])
def eat_food_from_fridge(name):
request_data = request.get_json()
for fridge in fridges:
if fridge["name"] == name:
for food in fridge["food"]:
if food["name"] == request_data["fname"]:
food["quantity"] = food["quantity"] - 1
return jsonify({ "message": "Successfully removed food" })
return jsonify({ "message": "Fridge not found" })
# POST /fridge/<string:name>/add
# - data:{fname:"fname", (details...), quantity:"q"}
# - Add new type of food to fridge
@app.route("/fridge/<string:name>/add", methods=["POST"])
def add_item_to_fridge(name):
request_data = request.get_json()
for fridge in fridges:
if fridge["name"] == name:
if request_data["name"] in [food["name"] for food in fridge["food"]]:
return jsonify({ "message": "Food already in fridge" })
new_food = {
"name": request_data["name"],
"quantity": request_data["quantity"],
"color": request_data["color"],
"shape": request_data["shape"],
"group": request_data["group"]
}
fridge["food"].append(new_food)
return jsonify({ "message": "Food added to fridge" })
return jsonify({ "message": "Fridge not found" })
# GET /fridge/<string:name>/food/<string:fname>/quantity
# - Returns the quantity of food "fname" in fridge "name"
@app.route("/fridge/<string:name>/food/<string:fname>/quantity", methods=["GET"])
def get_food_quantity(name, fname):
for fridge in fridges:
if fridge["name"] == name:
for food in fridge["food"]:
if food["name"] == fname:
return jsonify({ "quantity": food["quantity"] })
return jsonify({ "message": "Fridge not found" })
# GET /fridge/<string:name>/food/<string:fname>/shape
# - Returns the shape of food "fname" in fridge "name"
@app.route("/fridge/<string:name>/food/<string:fname>/shape", methods=["GET"])
def get_food_shape(name, fname):
for fridge in fridges:
if fridge["name"] == name:
for food in fridge["food"]:
if food["name"] == fname:
return jsonify({ "shape": food["shape"] })
return jsonify({ "message": "Fridge not found" })
if __name__ == "__main__":
app.run(debug=True, port=5000)

84
fridge.svg Normal file
View file

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 512.001 512.001" style="enable-background:new 0 0 512.001 512.001;" xml:space="preserve">
<path style="fill:#7DE3EA;" d="M200.579,469.307h-53.842c-17.519,0-31.721-14.202-31.721-31.72V39.812
c0-17.519,14.2-31.72,31.721-31.72h218.527c17.519,0,31.721,14.199,31.721,31.72v106.264v291.51c0,17.516-14.2,31.72-31.721,31.72
H227.195"/>
<path style="fill:#49D0E2;" d="M396.983,39.812c0-17.519-14.2-31.72-31.721-31.72H146.735c-17.519,0-31.721,14.199-31.721,31.72
v156.556h281.968V65.693"/>
<g>
<path style="fill:#636D77;" d="M138.663,469.302v21.656c0,7.153,5.798,12.949,12.949,12.949h17.263
c7.151,0,12.949-5.796,12.949-12.949v-21.656H138.663z"/>
<path style="fill:#636D77;" d="M330.176,469.302v21.656c0,7.153,5.798,12.949,12.949,12.949h17.263
c7.151,0,12.949-5.796,12.949-12.949v-21.656H330.176z"/>
</g>
<g style="opacity:0.2;">
<path style="fill:#020202;" d="M164.398,490.959v-21.656h-25.735v21.656c0,7.153,5.798,12.949,12.949,12.949h17.263
c1.486,0,2.907-0.262,4.236-0.723C168.042,501.428,164.398,496.626,164.398,490.959z"/>
</g>
<g style="opacity:0.2;">
<path style="fill:#020202;" d="M355.911,490.959v-21.656h-25.735v21.656c0,7.153,5.798,12.949,12.949,12.949h17.263
c1.486,0,2.907-0.262,4.236-0.723C359.556,501.428,355.911,496.626,355.911,490.959z"/>
</g>
<path style="fill:#A8F2F4;" d="M115.016,196.368v26.976h12.856h43.163h21.58c7.151,0,12.949-5.798,12.949-12.949v-14.028H115.016z"
/>
<g>
<path style="fill:#020202;" d="M396.983,57.6c-4.47,0-8.093,3.622-8.093,8.093v80.383v42.198H205.563h-82.454V39.812
c0-13.027,10.598-23.627,23.627-23.627h218.528c13.028,0,23.627,10.598,23.627,23.627c0,4.47,3.622,8.093,8.093,8.093
s8.093-3.622,8.093-8.093C405.076,17.859,387.217,0,365.263,0H146.735c-21.953,0-39.812,17.859-39.812,39.812v156.556v26.976
v214.243c0,16.199,9.73,30.158,23.647,36.369v17.002c0,11.602,9.439,21.041,21.041,21.041h17.263
c11.602,0,21.041-9.439,21.041-21.041V477.4h10.663c4.47,0,8.093-3.622,8.093-8.093s-3.622-8.093-8.093-8.093h-18.672
c-0.028,0-0.055-0.004-0.083-0.004h-35.252c-12.952-0.09-23.462-10.65-23.462-23.622V231.437h69.506
c11.602,0,21.041-9.439,21.041-21.041v-5.935h175.235v233.126c0,12.971-10.509,23.532-23.462,23.622h-35.252
c-0.028,0-0.055,0.003-0.083,0.004H227.197c-4.47,0-8.093,3.622-8.093,8.093s3.622,8.093,8.093,8.093h94.889v13.559
c0,11.602,9.439,21.041,21.041,21.041h17.263c11.602,0,21.041-9.439,21.041-21.041v-17.003
c13.918-6.211,23.647-20.171,23.647-36.369V196.368v-50.291V65.693C405.076,61.223,401.454,57.6,396.983,57.6z M173.73,490.959
c0,2.677-2.179,4.856-4.856,4.856h-17.263c-2.677,0-4.856-2.179-4.856-4.856v-13.559h26.974V490.959z M197.47,210.395
c0,2.677-2.179,4.856-4.856,4.856h-69.506v-10.79h74.361V210.395z M360.387,495.814h-17.263c-2.677,0-4.856-2.179-4.856-4.856
v-13.559h26.974v13.559C365.243,493.637,363.065,495.814,360.387,495.814z"/>
<path style="fill:#020202;" d="M355.277,45.912v111.915c0,4.47,3.622,8.093,8.093,8.093s8.093-3.622,8.093-8.093V45.912
c0-4.47-3.622-8.093-8.093-8.093S355.277,41.442,355.277,45.912z"/>
<path style="fill:#020202;" d="M355.277,422.179c0,4.47,3.622,8.093,8.093,8.093s8.093-3.622,8.093-8.093v-5.434
c0-4.47-3.622-8.093-8.093-8.093s-8.093,3.622-8.093,8.093V422.179z"/>
<path style="fill:#020202;" d="M363.37,398.576c4.47,0,8.093-3.622,8.093-8.093v-156.1c0-4.47-3.622-8.093-8.093-8.093
s-8.093,3.622-8.093,8.093v156.1C355.277,394.953,358.899,398.576,363.37,398.576z"/>
<path style="fill:#020202;" d="M154.539,64.366c-4.47,0-8.093,3.622-8.093,8.093v66.609c0,4.47,3.622,8.093,8.093,8.093
s8.093-3.622,8.093-8.093V80.552h4.856c4.47,0,8.093-3.622,8.093-8.093c0-4.47-3.622-8.093-8.093-8.093H154.539z"/>
</g>
<g style="opacity:0.2;">
<path style="fill:#020202;" d="M140.389,437.587V39.812c0-17.519,14.2-31.72,31.72-31.72h-25.373
c-17.519,0-31.721,14.199-31.721,31.72v397.776c0,17.516,14.2,31.72,31.721,31.72h25.373
C154.589,469.307,140.389,455.104,140.389,437.587z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
preview.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 MiB

12
requirements.txt Normal file
View file

@ -0,0 +1,12 @@
certifi==2021.10.8
charset-normalizer==2.0.12
click==8.0.4
colorama==0.4.4
Flask==2.0.3
idna==3.3
itsdangerous==2.1.0
Jinja2==3.0.3
MarkupSafe==2.1.0
requests==2.27.1
urllib3==1.26.8
Werkzeug==2.0.3

17
templates/index.html Normal file
View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Main page!</title>
</head>
<body>
<h1>Welcome to the fridges repository!</h1>
<script type="text/javascript">
fetch("http://127.0.0.1:5000/", (response) => {
alert(response)
});
</script>
</body>
</html>