def current_drinks(adapter): # optional request paremeter, the name of the machine to get stock information machine_name = request.args.get('machine', None) # assemble an array of (id, name) tuples if machine_name is None: machines = adapter.get_machines() logger.debug('Fetching contents for machines {}'.format(', '.join( [m['name'] for m in machines]))) else: # We're given a machine name machine = adapter.get_machine(machine_name) if machine is None: err = f'The provided machine name \'{machine_name}\' is not a valid machine' logger.error(err) return bad_params(err) logger.debug('Fetching contents for machine {}'.format(machine_name)) machines = [] machines.append(machine) response = {} with Pool(5) as p: contents = p.map(query_machine, machines) response['machines'] = contents response[ 'message'] = 'Successfully retrieved machine contents for {}'.format( ', '.join([machine['name'] for machine in machines])) return jsonify(response), 200
def update_slot_status(): if request.headers.get('Content-Type') != 'application/json': return bad_headers_content_type() body = request.json logger.debug('Handling slot update') unprovided = [] if 'machine' not in body: unprovided.append('machine') if 'slot' not in body: unprovided.append('slot') if len(unprovided) > 0: return bad_params( 'The following required parameters were not provided: {}'.format( ', '.join(unprovided))) if 'active' not in body and 'item_id' not in body: return bad_params( 'Either the state or item within a slot must be provided for an update.' ) updates = {} if 'active' in body: if not isinstance(body['active'], bool): return bad_params('The active parameter must be a boolean value') updates['active'] = body['active'] if 'item_id' in body: try: item_id = int(body['item_id']) if item_id <= 0: raise ValueError() except ValueError: return bad_params('The item ID value must be a positive integer') updates['item'] = item_id item = db.session.query(Item).filter(Item.id == item_id).first() if item is None: return bad_params( 'No item with ID {} is present in the system'.format(item_id)) try: slot_num = int(body['slot']) if slot_num <= 0: raise ValueError() except ValueError: return bad_params('The slot number must be a positive integer') machine = db.session.query(Machine).filter( Machine.name == body['machine']).first() if machine is None: return bad_params('The machine \'{}\' is not a valid machine'.format( body['machine'])) slot = db.session.query(Slot).filter(Slot.number == slot_num, Slot.machine == machine.id).first() if slot is None: return bad_params( 'The machine \'{}\' does not have a slot number {}'.format( body['machine'], body['slot'], )) logger.debug('Slot update details validated') slot = db.session.query(Slot).filter(Slot.number == body['slot'], Slot.machine == machine.id).\ update(updates, synchronize_session=False) if slot < 1: return jsonify({ 'error': 'Could not update slot', 'errorCode': 500, 'message': 'Contact a drink admin' }), 500 db.session.commit() slot = db.session.query(Slot).filter(Slot.number == slot_num, Slot.machine == machine.id).first() success = { 'message': 'Successfully updated slot {} in {}'.format(slot.number, body['machine']), 'slot': { 'machine': body['machine'], 'number': slot.number, 'active': slot.active, 'item_id': slot.item, }, } return jsonify(success), 200
def current_drinks(adapter): # optional request paremeter, the name of the machine to get stock information machine_name = request.args.get('machine', None) # assemble an array of (id, name) tuples if machine_name is None: machines = adapter.get_machines() logger.debug('Fetching contents for machines {}'.format(', '.join( [m['name'] for m in machines]))) else: # We're given a machine name machine = adapter.get_machine(machine_name) if machine is None: return bad_params( 'The provided machine name \'{}\' is not a valid machine'. format(machine_name)) logger.debug('Fetching contents for machine {}'.format(machine_name)) machines = [] machines.append(machine) response = {"machines": []} for machine in machines: logger.debug('Querying machine details for {}'.format(machine['name'])) machine_slots = adapter.get_slots_in_machine(machine['name']) is_online = True try: slot_status = _get_machine_status(machine['name']) except requests.exceptions.ConnectionError: # We couldn't connect to the machine logger.debug( 'Machine {} is unreachable, reporting as offline'.format( machine['name'])) slot_status = [{'empty': True} for n in range(len(machine_slots))] is_online = False # seems a useful feature except requests.exceptions.Timeout: # We hit a timeout waiting for the machine to respond logger.debug( 'Machine {} was reachable, but did not respond within a reasonable amount of time' .format(machine['name'])) slot_status = [{'empty': True} for n in range(len(machine_slots))] is_online = False machine_contents = { 'id': machine['id'], 'name': machine['name'], 'display_name': machine['display_name'], 'is_online': is_online, 'slots': [] } for slot in machine_slots: slot_item = adapter.get_item(slot['item']) machine_contents['slots'].append({ "number": slot['number'], "active": slot['active'], "count": slot['count'], "empty": slot_status[slot['number'] - 1]['empty'], "item": { "name": slot_item['name'], "price": slot_item['price'], "id": slot_item['id'], }, }) response['machines'].append(machine_contents) logger.debug('Fetched all available details for {}'.format( machine['name'])) response[ 'message'] = 'Successfully retrieved machine contents for {}'.format( ', '.join([machine['name'] for machine in machines])) return jsonify(response), 200
def drop_drink(adapter, user=None): if request.headers.get('Content-Type') != 'application/json': return bad_headers_content_type() logger.debug('Handing request to drop drink') bal_before = _get_credits(user['preferred_username']) body = request.json unprovided = [] if 'machine' not in body: unprovided.append('machine') if 'slot' not in body: unprovided.append('slot') if len(unprovided) > 0: return bad_params( 'The following required parameters were not provided: {}'.format( ', '.join(unprovided))) machine = db.session.query(Machine).filter( Machine.name == body['machine']).first() if machine is None: return bad_params( 'The machine name \'{}\' is not a valid machine'.format( body['machine'])) slot = db.session.query(Slot).filter(Slot.number == body['slot'], Slot.machine == machine.id).first() if slot is None: return bad_params( 'The machine \'{}\' does not have a slot with id \'{}\''.format( body['machine'], body['slot'])) logger.debug('Drop request is valid') slot_status = _get_machine_status(body['machine']) if (body['machine'] == 'snack' and slot.count < 1 ) or slot_status[body['slot'] - 1]['empty']: # slots are 1 indexed return jsonify({ "error": "The requested slot is empty!", "errorCode": 400 }), 400 item = db.session.query(Item).filter(Item.id == slot.item).first() if bal_before < item.price: response = { "error": "The user \'{}\' does not have a sufficient drinkBalance", "errorCode": 402 } return jsonify(response), 402 logger.debug('User has sufficient balance') machine_hostname = '{}.csh.rit.edu'.format(machine.name) request_endpoint = 'https://{}/drop'.format(machine_hostname) body = {"slot": slot.number} headers = { 'X-Auth-Token': app.config['MACHINE_API_TOKEN'], 'Content-Type': 'application/json' } # Do the thing try: response = requests.post(request_endpoint, json=body, headers=headers, timeout=5) except requests.exceptions.ConnectionError: return jsonify({ "error": "Could not contact drink machine for drop!", "errorCode": 500 }), 500 except requests.exceptions.Timeout: return jsonify({ "error": "Connection to the drink machine timed out!", "errorCode": 500 }), 500 try: response.raise_for_status() except requests.exceptions.HTTPError: return jsonify({"error": "Could not access slot for drop!", "message": response.json()['error'], "errorCode": response.status_code}),\ response.status_code logger.debug('Dropped drink - adjusting user credits') new_balance = bal_before - item.price _manage_credits(user['preferred_username'], new_balance, adapter) logger.debug('Credits for {} updated'.format(user['preferred_username'])) if machine.name == 'snack': slot.count = Slot.count - 1 # Decrement stock count, set inactive if empty if slot.count == 0: slot.active = False db.session.commit() return jsonify({ "message": "Drop successful!", "drinkBalance": new_balance }), response.status_code