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