diff --git a/hardwarecheckout/controllers/request.py b/hardwarecheckout/controllers/request.py index e592e9f..0dfcd76 100644 --- a/hardwarecheckout/controllers/request.py +++ b/hardwarecheckout/controllers/request.py @@ -1,7 +1,6 @@ from hardwarecheckout import app from hardwarecheckout import socketio from hardwarecheckout.models import db - from hardwarecheckout.models.request import Request, RequestStatus from hardwarecheckout.models.inventory_entry import InventoryEntry from hardwarecheckout.models.inventory_entry import ItemType @@ -9,7 +8,6 @@ from hardwarecheckout.models.item import Item from hardwarecheckout.models.request_item import RequestItem from hardwarecheckout.models.socket import Socket - from hardwarecheckout.utils import requires_auth, requires_admin, verify_token from sqlalchemy import event @@ -32,7 +30,7 @@ def get_requests(): RequestStatus = RequestStatus, lottery_items = InventoryEntry.query.filter_by(item_type=ItemType.LOTTERY).all(), user=user) - + @app.route('/request/submit', methods=['POST']) @requires_auth() def request_submit(): @@ -40,14 +38,14 @@ def request_submit(): if not (user.location and user.phone): return jsonify( success=False, - message="""Please fill out your user info before + message="""Please fill out your user info before requesting items!""" ) proposal = request.form.get('proposal', '') requested_quantity = int(request.form.get('quantity', 1)) if app.config['LOTTERY_CHAR_LIMIT']: - if len(proposal) > app.config['LOTTERY_CHAR_LIMIT']: + if len(proposal) > app.config['LOTTERY_CHAR_LIMIT']: proposal = proposal[:app.config['LOTTERY_CHAR_LIMIT']] entry = InventoryEntry.query.get(request.form['item_id']) @@ -107,20 +105,20 @@ def request_submit(): db.session.commit() return jsonify( success=True, - ) + ) @app.route('/request//cancel', methods=['POST']) @requires_auth() def request_cancel(id): """Cancel request, returns status Non-admins can only cancel own request, returns 403 if attempted""" - r = Request.query.get(id) + r = Request.query.get(id) if user.is_admin or r.user_id == user.id: r.status = RequestStatus.CANCELLED db.session.commit() return jsonify( success=True, - ) + ) else: return jsonify( success=False, @@ -136,7 +134,7 @@ def request_update(id, status): r = Request.query.get(id) r.status = status db.session.commit() - return True + return True @app.route('/request//approve', methods=['POST']) @requires_admin() @@ -147,18 +145,49 @@ def request_approve(id): entry = request_item.entry quantity = request_item.quantity - # get items of proper type - for _ in xrange(quantity): - if entry.quantity < quantity: - return jsonify( - success=False, - message='Out of stock!' - ) + if entry.quantity < quantity: + return jsonify( + success=False, + message='Out of stock!' + ) request_update(id, RequestStatus.APPROVED) return jsonify( success=True, ) +@app.route('/request/approve', methods=['POST']) +@requires_admin() +def approve_requests(): + """Approve all requests""" + submitted_requests = Request.query.filter_by(requires_lottery = False, + status = RequestStatus.SUBMITTED).all() + + entries_dict = {} + for request in submitted_requests: + r = Request.query.get(request.id) + for request_item in r.items: + entry = request_item.entry + quantity = request_item.quantity + + if entry in entries_dict: + entries_dict[entry] += quantity + else: entries_dict[entry] = quantity + + for entry in entries_dict.keys(): + requested_quantity = entries_dict[entry] + if entry.quantity < requested_quantity: + return jsonify( + success=False, + message='Some items are out of stock!' + ) + + for request in submitted_requests: + request_update(request.id, RequestStatus.APPROVED) + + return jsonify( + success=True, + ) + @app.route('/request//fulfill', methods=['POST']) @requires_admin() def request_fulfill(id): @@ -182,8 +211,8 @@ def request_fulfill(id): message='Out of stock!' ) # give user item - r.user.items.append(item) - + r.user.items.append(item) + # update request status request_update(id, RequestStatus.FULFILLED) @@ -203,6 +232,18 @@ def request_deny(id): success=True, ) +@app.route('/request/deny', methods=['POST']) +@requires_admin() +def deny_requests(): + """Deny all requests""" + submitted_requests = Request.query.filter_by(requires_lottery = False, + status = RequestStatus.SUBMITTED).all() + for request in submitted_requests: + request_update(request.id, RequestStatus.DENIED) + return jsonify( + success=True, + ) + @socketio.on('connect', namespace='/admin') def authenticate_admin_conection(): """Callback when client connects to /admin namespace, returns True @@ -211,15 +252,15 @@ def authenticate_admin_conection(): if 'jwt' in request.cookies: quill_id = verify_token(request.cookies['jwt']) if not quill_id: - return False + return False user = User.query.filter_by(quill_id=quill_id).first() if user == None or not user.is_admin: - return False + return False return True - else: - return False + else: + return False @socketio.on('connect', namespace='/user') def authenticate_user_conection(): @@ -229,18 +270,18 @@ def authenticate_user_conection(): if 'jwt' in request.cookies: quill_id = verify_token(request.cookies['jwt']) if not quill_id: - return False + return False user = User.query.filter_by(quill_id=quill_id).first() if user == None: - return False + return False socket = Socket(request.sid, user) db.session.add(socket) db.session.commit() return True else: - return False + return False @socketio.on('disconnect', namespace='/user') def user_disconnect(): @@ -259,7 +300,7 @@ def on_request_update(mapper, connection, target): def request_change_handler(target): """Handler that sends updated HTML for rendering requests""" - user = target.user + user = target.user sockets = Socket.query.filter_by(user=user).all() requests = Request.query.filter(Request.user == user, Request.status.in_( @@ -277,14 +318,14 @@ def request_change_handler(target): }, namespace='/user', room=socket.sid) # TODO: add check if at least one admin is connected - approved_requests = render_template('includes/macros/display_requests.html', + approved_requests = render_template('includes/macros/display_requests.html', # display requests that are submitted and non lottery OR already approved requests = Request.query.filter_by(status = RequestStatus.APPROVED).all(), RequestStatus = RequestStatus, admin = True, time = True) - submitted_requests = render_template('includes/macros/display_requests.html', + submitted_requests = render_template('includes/macros/display_requests.html', # display requests that are submitted and non lottery OR already approved requests = Request.query.filter_by(requires_lottery = False, status = RequestStatus.SUBMITTED).all(), @@ -295,10 +336,10 @@ def request_change_handler(target): lottery_items = InventoryEntry.query.filter_by(item_type=ItemType.LOTTERY).all() lottery_quantities = [] for item in lottery_items: - lottery_quantities.append( + lottery_quantities.append( { "id": item.id, - "available": item.quantity, + "available": item.quantity, "submitted": item.submitted_request_quantity } ) @@ -306,7 +347,7 @@ def request_change_handler(target): socketio.emit('update', { 'approved_requests': approved_requests, 'submitted_requests': submitted_requests, - 'lottery_quantities': lottery_quantities + 'lottery_quantities': lottery_quantities }, namespace='/admin') # listeners for change to Request database diff --git a/hardwarecheckout/static/scripts/admin.js b/hardwarecheckout/static/scripts/admin.js index fd3e222..8fa8237 100644 --- a/hardwarecheckout/static/scripts/admin.js +++ b/hardwarecheckout/static/scripts/admin.js @@ -1,4 +1,4 @@ -var socket = io.connect(location.protocol + '//' + document.domain + ':' +var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + '/admin'); socket.on('connect', function() { console.log('Socket connected!') @@ -29,7 +29,7 @@ socket.on('update', function(data) { } }); -function init_request_actions() { +function init_request_actions() { $('.request-action').api({ method: 'POST', onSuccess: function(response) { @@ -43,6 +43,34 @@ function init_request_actions() { $(document).ready(function() { init_request_actions(); + $('.approve-all.button').api({ + method: 'POST', + onSuccess: function(response) { + }, + onFailure: function(err) { + console.log(err); + alert(err.message) + }, + onError: function(err) { + console.log("ERROR!"); + console.log(err); + } + }); + + $('.deny-all.button').api({ + method: 'POST', + onSuccess: function(response) { + }, + onFailure: function(err) { + console.log("ERROR!"); + console.log(err); + }, + onError: function(err) { + console.log("ERROR!"); + console.log(err); + } + }); + $('.run-lottery.button').api({ method: 'POST', onSuccess: function(response) { @@ -63,4 +91,4 @@ $(document).ready(function() { onSuccess: function(response) { }, }); -}); \ No newline at end of file +}); diff --git a/hardwarecheckout/static/scripts/api.js b/hardwarecheckout/static/scripts/api.js index 455970b..1713e41 100644 --- a/hardwarecheckout/static/scripts/api.js +++ b/hardwarecheckout/static/scripts/api.js @@ -2,7 +2,7 @@ $.fn.api.settings.api = { 'add item' : '/inventory/add', 'import items' : '/inventory/autoadd', 'update item' : '/inventory/update/{id}', - 'return item' : '/inventory/return/{id}', + 'return item' : '/inventory/return/{id}', 'delete item' : '/inventory/delete/{id}', 'add subitem' : '/inventory/subitem/add/{id}', 'update subitem' : '/inventory/subitem/update/{id}', @@ -14,7 +14,9 @@ $.fn.api.settings.api = { 'approve request' : '/request/{id}/approve', 'fulfill request' : '/request/{id}/fulfill', 'deny request' : '/request/{id}/deny', - 'update user' : '/user/{id}/update' + 'update user' : '/user/{id}/update', + 'approve all' : '/request/approve', + 'deny all' : '/request/deny' } $.fn.api.settings.successTest = function(response) { diff --git a/hardwarecheckout/templates/pages/admin.html b/hardwarecheckout/templates/pages/admin.html index 7457371..24b455c 100644 --- a/hardwarecheckout/templates/pages/admin.html +++ b/hardwarecheckout/templates/pages/admin.html @@ -10,7 +10,19 @@

Approved Requests

{{ display_requests(approved_requests, RequestStatus, user.is_admin) }} -

Pending Requests

+ + + + + + + +
+

Pending Requests

+
+
Approve All
+
Deny All
+
{{ display_requests(submitted_requests, RequestStatus, user.is_admin) }}
@@ -32,19 +44,19 @@

Lotteries

{{ item.name }}

- Quantity: + Quantity: {{ item.quantity }} - , - Proposals: + , + Proposals: - (view) + (view)
-
Run Lottery
@@ -57,4 +69,4 @@

{{ item.name }}

{% block script %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/setup.sh b/setup.sh new file mode 100644 index 0000000..b738cdd --- /dev/null +++ b/setup.sh @@ -0,0 +1,9 @@ +# Environment variables +export DATABASE_URL="postgresql://postgres:password@localhost/cog" +export QUILL="https://hackcog.herokuapp.com" +export SECRET="8561d0aebda375a4dc5d5622250b9dc4a2387aa433d144df49d5599e8f74965e" + +# Run +python initialize.py +gunicorn --worker-class eventlet -w 1 hardwarecheckout:app +