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)