Completed client side time adjustment handling
Corrected error display bug Removed redundant auth and models in quiz client app
This commit is contained in:
		| @@ -41,18 +41,17 @@ class Test: | ||||
|         return jsonify({'error': f'Could not create exam. An error occurred.'}), 400 | ||||
|  | ||||
|     def add_time_adjustment(self, time_adjustment): | ||||
|         user_code = secrets.token_hex(3).upper() | ||||
|         adjustment = { | ||||
|             '_id': uuid4().hex, | ||||
|             'user_code': secrets.token_hex(3).upper(), | ||||
|             'time_adjustment': time_adjustment | ||||
|             user_code: time_adjustment | ||||
|         } | ||||
|         if db.tests.find_one_and_update({'_id': self._id}, {'$push': {'time_adjustments': adjustment}},upsert=False): | ||||
|             flash(f'Time adjustment for {adjustment["time_adjustment"]} minutes has been added. This can be enabled using the user code {adjustment["user_code"]}.') | ||||
|         if db.tests.find_one_and_update({'_id': self._id}, {'$set': {'time_adjustments': adjustment}},upsert=False): | ||||
|             flash(f'Time adjustment for {time_adjustment} minutes has been added. This can be enabled using the user code {user_code}.') | ||||
|             return jsonify({'success': adjustment}) | ||||
|         return jsonify({'error': 'Failed to add the time adjustment. An error occurred.'}), 400 | ||||
|  | ||||
|     def remove_time_adjustment(self, _id): | ||||
|         if db.tests.find_one_and_update({'_id': self._id}, {'$pull': {'time_adjustments': {'_id': _id} }}): | ||||
|     def remove_time_adjustment(self, user_code): | ||||
|         if db.tests.find_one_and_update({'_id': self._id}, {'$unset': {f'time_adjustments.{user_code}': {}}}): | ||||
|             message = 'Time adjustment has been deleted.' | ||||
|             flash(message, 'success') | ||||
|             return jsonify({'success': message}) | ||||
|   | ||||
| @@ -126,14 +126,16 @@ function error_response(response) { | ||||
|         </div> | ||||
|         `); | ||||
|     } else if (response.responseJSON.error instanceof Array) { | ||||
|         var output = '' | ||||
|         for (var i = 0; i < response.responseJSON.error.length; i ++) { | ||||
|             $alert.html(` | ||||
|             output += ` | ||||
|             <div class="alert alert-danger alert-dismissible fade show" role="alert"> | ||||
|                 <i class="bi bi-exclamation-triangle-fill" title="Danger"></i> | ||||
|                 ${response.responseJSON.error[i]} | ||||
|                 <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> | ||||
|             </div> | ||||
|             `); | ||||
|             `; | ||||
|             $alert.html(output); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -205,25 +207,22 @@ $('.result-action-buttons').click(function(event){ | ||||
| // Script for Deleting Time Adjustment | ||||
| $('.adjustment-delete').click(function(event){ | ||||
|  | ||||
|     var _id = $(this).data('_id'); | ||||
|     var user_code = $(this).data('user_code'); | ||||
|     var location = window.location.href; | ||||
|     location.replace('#', '') | ||||
|     location = location.replace('#', '') | ||||
|  | ||||
|     console.log(location) | ||||
|  | ||||
|     console.log(_id) | ||||
|         $.ajax({ | ||||
|             url: location + 'delete-adjustment/', | ||||
|             type: 'POST', | ||||
|             data: JSON.stringify({'_id': _id}), | ||||
|             contentType: 'application/json', | ||||
|             success: function(response) { | ||||
|                 window.location.reload(); | ||||
|             }, | ||||
|             error: function(response){ | ||||
|                 error_response(response); | ||||
|             }, | ||||
|         }); | ||||
|     $.ajax({ | ||||
|         url: location + 'delete-adjustment/', | ||||
|         type: 'POST', | ||||
|         data: JSON.stringify({'user_code': user_code}), | ||||
|         contentType: 'application/json', | ||||
|         success: function(response) { | ||||
|             window.location.reload(); | ||||
|         }, | ||||
|         error: function(response){ | ||||
|             error_response(response); | ||||
|         }, | ||||
|     }); | ||||
|      | ||||
|     event.preventDefault(); | ||||
| }); | ||||
| @@ -110,16 +110,16 @@ | ||||
|                                                 </tr> | ||||
|                                             </thead> | ||||
|                                             <tbody> | ||||
|                                                 {% for entry in test.time_adjustments %} | ||||
|                                                 {% for key, value in test.time_adjustments.items() %} | ||||
|                                                     <tr> | ||||
|                                                         <td> | ||||
|                                                             {{ entry.user_code }} | ||||
|                                                             {{ key }} | ||||
|                                                         </td> | ||||
|                                                         <td> | ||||
|                                                             {{ entry.time_adjustment }} | ||||
|                                                             {{ value }} | ||||
|                                                         </td> | ||||
|                                                         <td> | ||||
|                                                             <a href="javascript::void(0);" class="btn btn-danger adjustment-delete" title="Delete Adjustment" data-_id="{{ entry._id }}" data-action="delete"> | ||||
|                                                             <a href="javascript::void(0);" class="btn btn-danger adjustment-delete" title="Delete Adjustment" data-user_code="{{ key }}"> | ||||
|                                                                 <i class="bi bi-slash-circle-fill button-icon"></i> | ||||
|                                                             </a> | ||||
|                                                         </td> | ||||
|   | ||||
| @@ -417,7 +417,7 @@ def view_test(_id): | ||||
|         return render_template('/admin/test.html', test = test, form = form) | ||||
|     if request.method == 'POST': | ||||
|         if form.validate_on_submit(): | ||||
|             time = request.form.get('time') | ||||
|             time = int(request.form.get('time')) | ||||
|             return Test(_id=_id).add_time_adjustment(time) | ||||
|         return jsonify({'error': form.time.errors }), 400 | ||||
|  | ||||
| @@ -425,8 +425,8 @@ def view_test(_id): | ||||
| @admin_account_required | ||||
| @login_required | ||||
| def delete_adjustment(_id): | ||||
|     adjustment_id = request.get_json()['_id'] | ||||
|     return Test(_id=_id).remove_time_adjustment(adjustment_id) | ||||
|     user_code = request.get_json()['user_code'] | ||||
|     return Test(_id=_id).remove_time_adjustment(user_code) | ||||
|  | ||||
| @views.route('/results/') | ||||
| @admin_account_required | ||||
|   | ||||
| @@ -43,10 +43,8 @@ if __name__ == '__main__': | ||||
|     from admin.auth import auth as admin_auth | ||||
|     from admin.results import results | ||||
|     from quiz.views import views as quiz_views | ||||
|     from quiz.auth import auth as quiz_auth | ||||
|  | ||||
|     app.register_blueprint(quiz_views, url_prefix = '/') | ||||
|     app.register_blueprint(quiz_auth, url_prefix = '/') | ||||
|     app.register_blueprint(admin_views, url_prefix = '/admin/') | ||||
|     app.register_blueprint(admin_auth, url_prefix = '/admin/') | ||||
|     app.register_blueprint(results, url_prefix = '/admin/results/') | ||||
|   | ||||
| @@ -1,6 +0,0 @@ | ||||
| from flask import Blueprint | ||||
|  | ||||
| auth = Blueprint( | ||||
|     'quiz_auth', | ||||
|     __name__, | ||||
| ) | ||||
| @@ -163,6 +163,18 @@ $("#btn-start-quiz").click(function(event){ | ||||
|                 time_remaining = get_time_remaining(); | ||||
|                 clock = setInterval(timer, 1000); | ||||
|             } | ||||
|             if (response.time_adjustment > 0) { | ||||
|                 const $alert = $("#alert-box"); | ||||
|                 $alert.html( | ||||
|                     `<div class="alert alert-primary alert-dismissible fade show" role="alert"> | ||||
|                     <i class="bi bi-exclamation-triangle-fill" title="Alert"></i> | ||||
|                     User code validated. Extra time of ${response.time_adjustment} minutes added to the exam time limit. | ||||
|                     <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> | ||||
|                 </div> | ||||
|                 ` | ||||
|                 ); | ||||
|                 $alert.focus(); | ||||
|             } | ||||
|         }, | ||||
|         error: function(response) { | ||||
|             error_response(response); | ||||
| @@ -507,7 +519,7 @@ function timer() { | ||||
|     if (time_remaining > 0) { | ||||
|         var timer_display = ''; | ||||
|         if (hours > 0) { | ||||
|             timer_display = `${hours.toString()}: `; | ||||
|             timer_display = `${hours.toString()}:`; | ||||
|         } | ||||
|         if (minutes > 0 || hours > 0) { | ||||
|             if (minutes < 10) { | ||||
|   | ||||
| @@ -37,11 +37,11 @@ $('form[name=form-quiz-start]').submit(function(event) { | ||||
|  | ||||
| function error_response(response) { | ||||
|  | ||||
|     var alert = $("#alert-box"); | ||||
|     alert.html(''); | ||||
|     const $alert = $("#alert-box"); | ||||
|     $alert.html(''); | ||||
|  | ||||
|     if (typeof response.responseJSON.error === 'string' || response.responseJSON.error instanceof String) { | ||||
|         alert.html(` | ||||
|         $alert.html(` | ||||
|         <div class="alert alert-danger alert-dismissible fade show" role="alert"> | ||||
|             <i class="bi bi-exclamation-triangle-fill" title="Danger"></i> | ||||
|             ${response.responseJSON.error} | ||||
| @@ -49,14 +49,16 @@ function error_response(response) { | ||||
|         </div> | ||||
|         `); | ||||
|     } else if (response.responseJSON.error instanceof Array) { | ||||
|         var output = '' | ||||
|         for (var i = 0; i < response.responseJSON.error.length; i ++) { | ||||
|             alert.html(` | ||||
|             output += ` | ||||
|             <div class="alert alert-danger alert-dismissible fade show" role="alert"> | ||||
|                 <i class="bi bi-exclamation-triangle-fill" title="Danger"></i> | ||||
|                 ${response.responseJSON.error[i]} | ||||
|                 <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> | ||||
|             </div> | ||||
|             `); | ||||
|             `; | ||||
|             $alert.html(output); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| {% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
|     <div id="alert-box" tabindex="-1"></div> | ||||
|     <div class="container quiz-panel" id="quiz-settings" tabindex="-1"> | ||||
|         <h1>Adjust Display Settings</h1> | ||||
|         <div class="container quiz-start-text"> | ||||
| @@ -255,7 +256,6 @@ | ||||
|             <a href="#" class="btn btn-success quiz-button-submit" title="Submit Exam">Submit Exam</a> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div id="alert-box"></div> | ||||
| {% endblock %} | ||||
| {% block script %} | ||||
| <script | ||||
|   | ||||
| @@ -36,7 +36,7 @@ | ||||
|                     <div class="col text-center"> | ||||
|                         <button class="btn btn-md btn-success btn-block" type="submit"> | ||||
|                             <i class="bi bi-pencil-fill button-icon"></i> | ||||
|                             Start Exam | ||||
|                             Get Ready | ||||
|                         </button> | ||||
|                     </div> | ||||
|                 </div> | ||||
|   | ||||
| @@ -55,10 +55,12 @@ def start(): | ||||
|             club = request.form.get('club') | ||||
|             test_code = request.form.get('test_code').replace('—', '') | ||||
|             user_code = request.form.get('user_code') | ||||
|             user_code = None if user_code == '' else user_code | ||||
|             user_code = None if user_code == '' else user_code.upper() | ||||
|             test = db.tests.find_one({'test_code': test_code}) | ||||
|             if not test: | ||||
|                 return jsonify({'error': 'The exam code you entered is invalid.'}), 400 | ||||
|             if user_code and user_code not in test['time_adjustments']: | ||||
|                 return jsonify({'error': f'The user code you entered is not valid.'}), 400 | ||||
|             if test['expiry_date'] + timedelta(days=1) < datetime.utcnow(): | ||||
|                 return jsonify({'error': f'The exam code you entered expired on {test["expiry_date"].strftime("%d %b %Y")} UTC.'}), 400 | ||||
|             if test['start_date'] > datetime.utcnow(): | ||||
| @@ -88,12 +90,16 @@ def fetch_questions(): | ||||
|     if not entry: | ||||
|         return jsonify({'error': 'The data that the client sent to the server is invalid. This is possibly because you have already submitted your exam and have tried to access the page again.'}), 400 | ||||
|     test_code = entry['test_code'] | ||||
|     # user_code = entry['user_code'] TODO Implement functionality for adjustments | ||||
|  | ||||
|     user_code = entry['user_code'] | ||||
|     test = db.tests.find_one({'test_code' : test_code}) | ||||
|     time_limit = test['time_limit'] | ||||
|     time_adjustment = 0 | ||||
|     if time_limit: | ||||
|         _time_limit = int(time_limit) | ||||
|         if user_code: | ||||
|             time_adjustment = test['time_adjustments'][user_code] | ||||
|             _time_limit += time_adjustment | ||||
|             adjustment = True | ||||
|         end_delta = timedelta(minutes=_time_limit) | ||||
|         end_time = datetime.utcnow() + end_delta | ||||
|     else: | ||||
| @@ -113,14 +119,14 @@ def fetch_questions(): | ||||
|     return jsonify({ | ||||
|         'time_limit': end_time, | ||||
|         'questions': questions, | ||||
|         'start_time': entry['start_time'] | ||||
|         'start_time': entry['start_time'], | ||||
|         'time_adjustment': time_adjustment | ||||
|     }) | ||||
|  | ||||
| @views.route('/test/') | ||||
| def start_quiz(): | ||||
|     _id = session.get('_id') | ||||
|     if not _id or not db.entries.find_one({'_id': _id}): | ||||
|         print('Foo') | ||||
|         flash('Your log in was not recognised. Please sign in to the quiz again.', 'error') | ||||
|         return redirect(url_for('quiz_views.start')) | ||||
|     return render_template('quiz/client.html') | ||||
|   | ||||
		Reference in New Issue
	
	Block a user