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,17 +207,14 @@ $('.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}),
 | 
			
		||||
        data: JSON.stringify({'user_code': user_code}),
 | 
			
		||||
        contentType: 'application/json',
 | 
			
		||||
        success: function(response) {
 | 
			
		||||
            window.location.reload();
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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