253 lines
No EOL
9.8 KiB
Python
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) |