beerbuddy/flask/backend.py
ak 9a9e6ac209 progress update 2023-11-29
backend code complete, to be uploaded to deta
2023-11-29 18:17:43 -08:00

253 lines
No EOL
9.8 KiB
Python

from os import environ
from dotenv import load_dotenv
from flask import Flask, request, abort
from datetime import datetime
from deta import Deta
load_dotenv()
app = Flask(__name__)
deta = Deta()
### connect to dbs and drive
itemsDB = deta.Base('items')
storesDB = deta.Base('stores')
drive = deta.Drive('beerbuddy')
### reusable functions
# fetch requested store from store database based on request arguments
# arguments are assumed checked beforehand
def fetchStores():
# fetch store from deta Base
fetch = storesDB.fetch({
'easting': request.args['easting'],
'northing': request.args['northing'],
'zone': request.args['zone'],
})
data = fetch.items
# if we didn't find the record on the first go around, try again
if not data:
### inb4 infinite loop
while (fetch.last is not None):
fetch = storesDB.fetch({
'easting': request.args['easting'],
'northing': request.args['northing'],
'zone': request.args['zone'],
last: fetch.last
})
# check with each loop iteration if we've found it
if fetch.items:
data.append(fetch.items)
# if so, break loop
break
return data
# fetch requested item from item database based on request arguments and passed store
def fetchItems(store):
# fetch item from deta Base
fetch = itemsDB.fetch({
'name': request.args['itemName'],
'store': store['key'],
})
data = fetch.items
# if we didn't find the record on the first go around, try again
if not data:
### inb4 infinite loop
while (fetch.last is not None):
fetch = itemsDB.fetch({
'name': request.args['itemName'],
'store': store['key'],
last: fetch.last
})
# check with each loop iteration if we've found it
if fetch.items:
data.append(fetch.items)
# if so, break loop
break
return data
# fetches all items from item database
def fetchAllItems(store):
# fetch item from deta Base
fetch = itemsDB.fetch({
'store': store['key'],
})
data = fetch.items
# if we didn't find the record on the first go around, try again
if not data:
### inb4 infinite loop
while (fetch.last is not None):
fetch = itemsDB.fetch({
'store': store['key'],
last: fetch.last
})
data.append(fetch.items)
return data
# compares this item to other items from its store to determine if it is the cheapest then updates store accordingly
def updateCheapest(item, store):
cheapest = true
storeItems = fetchAllItems(store)
for storeItem in storeItems:
if storeItem['perFloz'] < item['perFloz']: cheapest = false
if cheapest:
store['cheapestItem'] = item['name']
store['cheapestFloz'] = item['perFloz']
storesDB.put(store)
### routes
@app.route('/', methods=['GET'])
def get():
# if there are no arguments on the request
if not request.args:
# get all stores
fetch = storesDB.fetch({})
data = fetch.items
### inb4 infinite loop
while (fetch.last is not None):
fetch = storesDB.fetch({
last: fetch.last
})
data.append(fetch.items)
else:
# get store location details from URL arguments
if request.args['easting'] and request.args['northing'] and request.args['zone']:
stores = fetchStores()
if not stores: abort(404)
store = stores[0]
# if an item GET request
if request.args['itemName']:
# passing through the store at position [0] in the fetched stores array
items = fetchItems(store)
if not items: abort(404)
data = items[0]
# otherwise is a store GET request
else: data = store
# if wrong arguments, malformed request
else: abort(400)
# otherwise return data
return data
@app.route('/', methods=['POST'])
def post():
# checks for arguments
if request.args:
if request.args['easting'] and request.args['northing'] and request.args['zone'] and request.args['itemName'] and request.args['itemPrice'] and request.args['itemVolume']:
# checks if this is posting a new store
if request.args['name']:
# test for presence of stores in close vicinity with similar names
nameSubstrings = request.args['name'].split()
for nameSubstring in nameSubstrings:
fetch = storesDB.fetch({
'easting?r': [request.args['easting'] - 10, request.args['easting'] + 10],
'northing?r': [request.args['northing'] - 10, request.args['northing'] + 10],
'zone': request.args['zone'],
'name?contains': nameSubstring,
})
stores = fetch.items
# if we didn't find the record on the first go around, try again
if not stores:
### inb4 infinite loop
while (fetch.last is not None):
fetch = storesDB.fetch({
'easting?r': [request.args['easting'] - 10, request.args['easting'] + 10],
'northing?r': [request.args['northing'] - 10, request.args['northing'] + 10],
'zone': request.args['zone'],
'name?contains': 'nameSubstring',
last: fetch.last
})
# check with each loop iteration if we've found it
if fetch.items:
stores.append(fetch.items)
# if so, break loop
break
# if found a store within +/-10 meters named similarly to one of the substrings, abort
if stores: abort(409)
# by this point, app would have aborted if there were a problem
# let's create the records
# first the item dict
item = {
'name': request.args['itemName'],
'volumeFloz': request.args['itemVolume'],
'price': request.args['itemPrice'],
'perFloz': request.args['itemPrice'] / request.args['itemVolume'],
'lastUpdated': datetime.now().strftime('%m-%d-%Y %H:%M:%S'),
}
# upload store to DB
store = storesDB.put({
'easting': request.args['easting'],
'northing': request.args['northing'],
'zone': request.args['zone'],
'name': request.args['name'],
'cheapestItem': item['name'],
'cheapestFloz': item['perFloz'],
'lastUpdated': datetime.now().strftime('%m-%d-%Y %H:%M:%S'),
})
# upload item to DB
item['store'] = store['key']
itemsDB.put(item)
# if there is an image upload
if request.args['image']:
drive.put(store.key, data=request.args['image'])
# finish successfully
return {}
# or if it's posting a new item in the store
else:
stores = fetchStores()
if not stores: abort(404)
store = stores[0]
###
### HERE WE NEED TO CHECK IF ITEM ALREADY EXISTS
###
# let's create the record
item = {
'name': request.args['itemName'],
'volumeFloz': request.args['itemVolume'],
'price': request.args['itemPrice'],
'perFloz': request.args['itemPrice'] / request.args['itemVolume'],
'lastUpdated': datetime.now().strftime('%m-%d-%Y %H:%M:%S'),
'store': store['key']
}
updateCheapest(item, store)
# beam it up
itemsDB.put(item)
# finish successfully
return {}
# aborts if conditional chains not met
abort(400)
@app.route('/', methods=['PUT'])
def put():
# checks for arguments
if request.args:
if request.args['easting'] and request.args['northing'] and request.args['zone']:
# find store
stores = fetchStores()
if not stores: abort(404)
store = stores[0]
# updating item price
if request.args['itemName'] and request.args['price']:
# find item
items = fetchItems(store)
if not items: abort (404)
item = items[0]
# change price
item['price'] = request.args['itemPrice']
item['perFloz'] = item['price'] / item['volumeFloz']
item['lastUpdated'] = datetime.now().strftime('%m-%d-%Y %H:%M:%S')
updateCheapest(item, store)
# beam it up
itemsDB.put(item)
# finish successfully
return {}
# updating store image
if request.args['image']:
store['lastUpdated'] = datetime.now().strftime('%m-%d-%Y %H:%M:%S')
storesDB.put(store)
drive.put(store.key, data=request.args['image'])
return {}
# aborts if conditional chains not met
abort(400)
# no deletion planned (for now)