refactored backend code
This commit is contained in:
parent
486562f12c
commit
94adb2a820
1 changed files with 144 additions and 170 deletions
314
flask/backend.py
314
flask/backend.py
|
|
@ -1,43 +1,39 @@
|
|||
from os import environ
|
||||
from io import BytesIO
|
||||
import filetype
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from flask import Flask, request, abort, make_response, send_file
|
||||
from flask_cors import CORS
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from deta import Deta
|
||||
import filetype
|
||||
|
||||
load_dotenv()
|
||||
app = Flask(__name__)
|
||||
CORS(app)
|
||||
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 fetchStore(key = None):
|
||||
if key is not None:
|
||||
data = storesDB.get(key)
|
||||
return data
|
||||
if key is not None: return storesDB.get(key)
|
||||
else:
|
||||
northing = float(request.args['northing'])
|
||||
easting = float(request.args['easting'])
|
||||
zone = int(request.args['zone'])
|
||||
# fetch store from deta Base
|
||||
|
||||
fetch = storesDB.fetch({
|
||||
'easting?r': [easting - 1, easting + 1],
|
||||
'northing?r': [northing - 1, northing + 1],
|
||||
'zone': zone,
|
||||
})
|
||||
data = fetch.items
|
||||
# if we didn't find the record on the first go around, try again
|
||||
if not data:
|
||||
store = fetch.items[0]
|
||||
|
||||
if not store:
|
||||
while (fetch.last is not None):
|
||||
fetch = storesDB.fetch({
|
||||
'easting?r': [easting - 1, easting + 1],
|
||||
|
|
@ -45,178 +41,154 @@ def fetchStore(key = None):
|
|||
'zone': zone,
|
||||
last: fetch.last
|
||||
})
|
||||
# check with each loop iteration if we've found it
|
||||
if fetch.items:
|
||||
data = fetch.items
|
||||
# if so, break loop
|
||||
break
|
||||
return data
|
||||
|
||||
# fetch requested item from item database based on request arguments and passed store
|
||||
if fetch.items:
|
||||
store = fetch.items[0]
|
||||
break
|
||||
|
||||
return store
|
||||
|
||||
def fetchItem(store):
|
||||
# fetch item from deta Base
|
||||
fetch = itemsDB.fetch({
|
||||
'lowername': request.args['itemName'].lower(),
|
||||
'store': store['key'],
|
||||
})
|
||||
data = fetch.items
|
||||
# if we didn't find the record on the first go around, try again
|
||||
if not data:
|
||||
item = fetch.items[0]
|
||||
|
||||
if not item:
|
||||
while (fetch.last is not None):
|
||||
fetch = itemsDB.fetch({
|
||||
'lowername': request.args['itemName'].lower(),
|
||||
'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
|
||||
if fetch.items:
|
||||
item = fetch.items[0]
|
||||
break
|
||||
|
||||
return item
|
||||
|
||||
def fetchItems(store):
|
||||
fetch = itemsDB.fetch({
|
||||
'store': store['key'],
|
||||
})
|
||||
data = fetch.items
|
||||
items = fetch.items
|
||||
|
||||
while (fetch.last is not None):
|
||||
fetch = itemsDB.fetch({
|
||||
'store': store['key'],
|
||||
last: fetch.last
|
||||
})
|
||||
data = data + fetch.items
|
||||
return data
|
||||
items = items + fetch.items
|
||||
|
||||
return items
|
||||
|
||||
# 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)
|
||||
storeItems = fetchItems(store)
|
||||
itemPerFloz = item['perFloz']
|
||||
|
||||
for storeItem in storeItems:
|
||||
storeItemPerFloz = storeItem['perFloz']
|
||||
if storeItemPerFloz < itemPerFloz: cheapest = False
|
||||
if storeItem['perFloz'] < itemPerFloz: cheapest = False
|
||||
|
||||
if cheapest:
|
||||
store['cheapestItem'] = item['name']
|
||||
store['cheapestFloz'] = itemPerFloz
|
||||
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
|
||||
while (fetch.last is not None):
|
||||
fetch = storesDB.fetch({
|
||||
last: fetch.last
|
||||
})
|
||||
data = data + fetch.items
|
||||
else:
|
||||
# get store location details from URL arguments
|
||||
if 'easting' in request.args and 'northing' in request.args and 'zone' in request.args:
|
||||
stores = fetchStore()
|
||||
if not stores: abort(404)
|
||||
store = stores[0]
|
||||
items = fetchAllItems(store)
|
||||
store['items'] = items
|
||||
# if an item GET request
|
||||
if 'itemName' in request.args:
|
||||
# passing through the store at position [0] in the fetched stores array
|
||||
items = fetchItem(store)
|
||||
if not items: abort(404)
|
||||
data = items[0]
|
||||
# otherwise is a store GET request
|
||||
else: data = store
|
||||
# else store key passed, get by key
|
||||
elif 'storeKey' in request.args: data = fetchStore(request.args['storeKey'])
|
||||
# otherwise is a malformed request
|
||||
else: abort(400)
|
||||
# otherwise return data
|
||||
return data
|
||||
if request.args:
|
||||
if 'storeKey' in request.args: return fetchStore(request.args['storeKey'])
|
||||
|
||||
if 'easting' not in request.args and 'northing' not in request.args and 'zone' not in request.args: abort(400)
|
||||
|
||||
store = fetchStore()
|
||||
|
||||
if not store: abort(404)
|
||||
|
||||
store['items'] = fetchItems(store)
|
||||
return store
|
||||
|
||||
fetch = storesDB.fetch({})
|
||||
stores = fetch.items
|
||||
while (fetch.last is not None):
|
||||
fetch = storesDB.fetch({
|
||||
last: fetch.last
|
||||
})
|
||||
stores = stores + fetch.items
|
||||
|
||||
for store in stores:
|
||||
store['items'] = fetchItems(store)
|
||||
|
||||
return stores
|
||||
|
||||
@app.route('/', methods=['POST'])
|
||||
def post():
|
||||
# checks for args
|
||||
# easier to work with args here because it allows for reuse of functions above
|
||||
if request.args:
|
||||
if 'easting' in request.args and 'northing' in request.args and 'zone' in request.args and 'zoneLetter' and 'name' in request.args:
|
||||
northing = float(request.args['northing'])
|
||||
easting = float(request.args['easting'])
|
||||
zone = int(request.args['zone'])
|
||||
# checks if this is posting a new store
|
||||
# test for presence of stores in close vicinity with similar name
|
||||
|
||||
# +/- 10 meters
|
||||
fetch = storesDB.fetch({
|
||||
'easting?r': [easting - 10, easting + 10],
|
||||
'northing?r': [northing - 10, northing + 10],
|
||||
'zone': zone,
|
||||
'lowername?contains': request.args['name'].lower(),
|
||||
})
|
||||
stores = fetch.items
|
||||
if not stores:
|
||||
while (fetch.last is not None):
|
||||
fetch = storesDB.fetch({
|
||||
'easting?r': [easting - 10, easting + 10],
|
||||
'northing?r': [northing - 10, northing + 10],
|
||||
'zone': zone,
|
||||
'lowername?contains': request.args['name'].lower(),
|
||||
last: fetch.last
|
||||
})
|
||||
# check with each loop iteration if we've found it
|
||||
if fetch.items:
|
||||
stores = stores + fetch.items
|
||||
# if so, break loop
|
||||
break
|
||||
# if found a store within +/-10 meters named similarly, 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
|
||||
price = float(request.args['itemPrice'])
|
||||
volume = int(request.args['itemVolume'])
|
||||
item = {
|
||||
'name': request.args['itemName'],
|
||||
'lowername': request.args['itemName'].lower(),
|
||||
'volumeFloz': volume,
|
||||
'price': price,
|
||||
'perFloz': round(price / float(volume), 2),
|
||||
'lastUpdated': datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
}
|
||||
# upload store to DB
|
||||
store = storesDB.put({
|
||||
'easting': easting,
|
||||
'northing': northing,
|
||||
if fetch.items: abort(409)
|
||||
|
||||
while (fetch.last is not None):
|
||||
fetch = storesDB.fetch({
|
||||
'easting?r': [easting - 10, easting + 10],
|
||||
'northing?r': [northing - 10, northing + 10],
|
||||
'zone': zone,
|
||||
'zoneLetter': request.args['zoneLetter'],
|
||||
'name': request.args['name'],
|
||||
'lowername': request.args['name'].lower(),
|
||||
'cheapestItem': item['name'],
|
||||
'cheapestFloz': item['perFloz'],
|
||||
'lastUpdated': datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'lowername?contains': request.args['name'].lower(),
|
||||
last: fetch.last
|
||||
})
|
||||
# upload item to DB
|
||||
item['store'] = store['key']
|
||||
itemsDB.put(item)
|
||||
# if there is an image upload
|
||||
if request.files:
|
||||
if 'file' in request.files:
|
||||
file = request.files['file']
|
||||
drive.put(store['key'], data=request.files['file'].read())
|
||||
# finish successfully
|
||||
return make_response("Success", 200)
|
||||
# or if it's posting a new item in the store
|
||||
elif 'storeKey' in request.args and 'itemName' in request.args and 'itemPrice' in request.args and 'itemVolume' in request.args:
|
||||
if fetch.items: abort(409)
|
||||
|
||||
price = float(request.args['itemPrice'])
|
||||
volume = int(request.args['itemVolume'])
|
||||
item = {
|
||||
'name': request.args['itemName'],
|
||||
'lowername': request.args['itemName'].lower(),
|
||||
'volumeFloz': volume,
|
||||
'price': price,
|
||||
'perFloz': round(price / float(volume), 2),
|
||||
'lastUpdated': datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
}
|
||||
|
||||
store = storesDB.put({
|
||||
'easting': easting,
|
||||
'northing': northing,
|
||||
'zone': zone,
|
||||
'zoneLetter': request.args['zoneLetter'],
|
||||
'name': request.args['name'],
|
||||
'lowername': request.args['name'].lower(),
|
||||
'cheapestItem': item['name'],
|
||||
'cheapestFloz': item['perFloz'],
|
||||
'lastUpdated': datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
})
|
||||
|
||||
item['store'] = store['key']
|
||||
itemsDB.put(item)
|
||||
|
||||
if request.files:
|
||||
if 'file' in request.files: drive.put(store['key'], data=request.files['file'].read())
|
||||
|
||||
return make_response("Success", 200)
|
||||
|
||||
if 'storeKey' in request.args and 'itemName' in request.args and 'itemPrice' in request.args and 'itemVolume' in request.args:
|
||||
store = fetchStore(request.args['storeKey'])
|
||||
if not store: abort(404)
|
||||
# check for existence of duplicate item
|
||||
|
||||
found = fetchItem(store)
|
||||
if (found): abort(409)
|
||||
# let's create the record
|
||||
|
||||
price = float(request.args['itemPrice'])
|
||||
volume = int(request.args['itemVolume'])
|
||||
item = {
|
||||
|
|
@ -228,93 +200,94 @@ def post():
|
|||
'lastUpdated': datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'store': store['key']
|
||||
}
|
||||
|
||||
updateCheapest(item, store)
|
||||
# beam it up
|
||||
|
||||
itemsDB.put(item)
|
||||
# finish successfully
|
||||
|
||||
return make_response("Success", 200)
|
||||
|
||||
abort(400)
|
||||
|
||||
@app.route('/', methods=['PUT'])
|
||||
def put():
|
||||
# checks for arguments
|
||||
if request.args:
|
||||
if 'storeKey' in request.args:
|
||||
# find store
|
||||
store = fetchStore(request.args['storeKey'])
|
||||
if not store: abort(404)
|
||||
# updating item price
|
||||
|
||||
if 'itemName' in request.args and 'itemPrice' in request.args:
|
||||
# find item
|
||||
items = fetchItem(store)
|
||||
if not items: abort (404)
|
||||
item = items[0]
|
||||
# change price
|
||||
item = fetchItem(store)
|
||||
if not item: abort (404)
|
||||
|
||||
price = float(request.args['itemPrice'])
|
||||
volume = int(item['volumeFloz'])
|
||||
|
||||
item['price'] = price
|
||||
item['perFloz'] = round(price / float(volume), 2)
|
||||
item['lastUpdated'] = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
updateCheapest(item, store)
|
||||
# beam it up
|
||||
|
||||
itemsDB.put(item)
|
||||
# finish successfully
|
||||
|
||||
return make_response("Success", 200)
|
||||
# updating store image
|
||||
# if there is an image upload
|
||||
|
||||
if request.files:
|
||||
if 'file' in request.files:
|
||||
store['lastUpdated'] = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
|
||||
storesDB.put(store)
|
||||
|
||||
file = request.files['file']
|
||||
drive.put(store['key'], data=request.files['file'].read())
|
||||
# finish successfully
|
||||
|
||||
return make_response("Success", 200)
|
||||
|
||||
abort(400)
|
||||
|
||||
@app.route('/img', methods=['GET'])
|
||||
def getImage():
|
||||
if not request.args: abort(400)
|
||||
else:
|
||||
if request.args:
|
||||
if 'imageKey' in request.args:
|
||||
mimetype = filetype.guess(drive.get(request.args['imageKey']).read()).mime
|
||||
return send_file(drive.get(request.args['imageKey']), download_name = request.args['imageKey'] + ".png", mimetype = mimetype)
|
||||
abort(400)
|
||||
|
||||
@app.route('/search', methods=['GET'])
|
||||
def search():
|
||||
if not request.args: abort(400)
|
||||
else:
|
||||
if request.args:
|
||||
if 'easting' in request.args and 'northing' and 'query' in request.args:
|
||||
# search all stores within 50 mile radius (80 km)
|
||||
northing = float(request.args['northing'])
|
||||
easting = float(request.args['easting'])
|
||||
# fetch store from deta Base
|
||||
|
||||
fetch = storesDB.fetch({
|
||||
'easting?r': [easting - 80000, easting + 80000],
|
||||
'northing?r': [northing - 80000, northing + 80000]
|
||||
})
|
||||
stores = fetch.items
|
||||
# if we didn't find records on the first go around, try again
|
||||
if not stores:
|
||||
while (fetch.last is not None):
|
||||
fetch = storesDB.fetch({
|
||||
'easting?r': [easting + 80000, easting + 80000],
|
||||
'northing?r': [northing - 80000, northing + 80000],
|
||||
last: fetch.last
|
||||
})
|
||||
stores = stores + fetch.items
|
||||
if not stores: abort(404)
|
||||
# for item matching query
|
||||
|
||||
while (fetch.last is not None):
|
||||
fetch = storesDB.fetch({
|
||||
'easting?r': [easting + 80000, easting + 80000],
|
||||
'northing?r': [northing - 80000, northing + 80000],
|
||||
last: fetch.last
|
||||
})
|
||||
stores = stores + fetch.items
|
||||
|
||||
if not stores: abort(404)
|
||||
|
||||
items = []
|
||||
|
||||
for store in stores:
|
||||
# fetch item from deta Base
|
||||
querySubstrings = request.args['query'].split()
|
||||
|
||||
for querySubstring in querySubstrings:
|
||||
fetch = itemsDB.fetch({
|
||||
'store': store['key'],
|
||||
'lowername?contains': querySubstring.lower(),
|
||||
})
|
||||
items = items + fetch.items
|
||||
|
||||
while (fetch.last is not None):
|
||||
fetch = itemsDB.fetch({
|
||||
'store': store['key'],
|
||||
|
|
@ -322,11 +295,12 @@ def search():
|
|||
last: fetch.last
|
||||
})
|
||||
items = items + fetch.items
|
||||
# append distance information
|
||||
|
||||
for item in items:
|
||||
if item['store'] == store['key']: item['distance'] = max(abs(store['easting'] - easting), abs(store['northing'] - northing))
|
||||
if not items: abort(404)
|
||||
return items
|
||||
abort(400)
|
||||
|
||||
# no deletion planned (for now)
|
||||
if not items: abort(404)
|
||||
|
||||
return items
|
||||
|
||||
abort(400)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue