Added server and admin-side time limit adjustments
This commit is contained in:
		@@ -41,13 +41,14 @@ class Test:
 | 
			
		||||
        return jsonify({'error': f'Could not create exam. An error occurred.'}), 400
 | 
			
		||||
 | 
			
		||||
    def add_time_adjustment(self, time_adjustment):
 | 
			
		||||
        code = {
 | 
			
		||||
        adjustment = {
 | 
			
		||||
            '_id': uuid4().hex,
 | 
			
		||||
            'user_code': secrets.token_hex(2).upper(),
 | 
			
		||||
            'user_code': secrets.token_hex(3).upper(),
 | 
			
		||||
            'time_adjustment': time_adjustment
 | 
			
		||||
        }
 | 
			
		||||
        if db.tests.find_one_and_update({'_id': self._id}, {'$push': {'time_adjustments': code}},upsert=False):
 | 
			
		||||
            return jsonify({'success': code})
 | 
			
		||||
        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"]}.')
 | 
			
		||||
            return jsonify({'success': adjustment})
 | 
			
		||||
        return jsonify({'error': 'Failed to add the time adjustment. An error occurred.'}), 400
 | 
			
		||||
 | 
			
		||||
    def remove_time_adjustment(self, _id):
 | 
			
		||||
 
 | 
			
		||||
@@ -214,11 +214,15 @@ table.dataTable {
 | 
			
		||||
    font-size: 14pt;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.result-action-buttons {
 | 
			
		||||
.result-action-buttons, .test-action {
 | 
			
		||||
    margin: 5px auto;
 | 
			
		||||
    width: fit-content;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.accordion-item {
 | 
			
		||||
    background-color: unset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Fallback for Edge
 | 
			
		||||
-------------------------------------------------- */
 | 
			
		||||
@supports (-ms-ime-align: auto) {
 | 
			
		||||
 
 | 
			
		||||
@@ -63,10 +63,12 @@ $('form[name=form-upload-questions]').submit(function(event) {
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Edit and Delete Test Button Handlers
 | 
			
		||||
$('.delete-test').click(function(event) {
 | 
			
		||||
$('.test-action').click(function(event) {
 | 
			
		||||
    
 | 
			
		||||
    let _id = $(this).data('_id')
 | 
			
		||||
    let _id = $(this).data('_id');
 | 
			
		||||
    let action = $(this).data('action');
 | 
			
		||||
 | 
			
		||||
    if (action == 'delete') {
 | 
			
		||||
        $.ajax({
 | 
			
		||||
            url: `/admin/tests/delete/`,
 | 
			
		||||
            type: 'POST',
 | 
			
		||||
@@ -79,6 +81,9 @@ $('.delete-test').click(function(event) {
 | 
			
		||||
                error_response(response);
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    } else if (action == 'edit') {
 | 
			
		||||
        window.location.href = `/admin/test/${_id}/`
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    event.preventDefault();
 | 
			
		||||
});
 | 
			
		||||
@@ -109,11 +114,11 @@ $('.edit-question-dataset').click(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}
 | 
			
		||||
@@ -122,7 +127,7 @@ function error_response(response) {
 | 
			
		||||
        `);
 | 
			
		||||
    } else if (response.responseJSON.error instanceof Array) {
 | 
			
		||||
        for (var i = 0; i < response.responseJSON.error.length; i ++) {
 | 
			
		||||
            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[i]}
 | 
			
		||||
@@ -131,6 +136,8 @@ function error_response(response) {
 | 
			
		||||
            `);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    $alert.focus()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Dismiss Cookie Alert
 | 
			
		||||
@@ -152,13 +159,12 @@ $('#dismiss-cookie-alert').click(function(event){
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    event.preventDefault();
 | 
			
		||||
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
// Script for Resutlt Actions
 | 
			
		||||
// Script for Result Actions
 | 
			
		||||
$('.result-action-buttons').click(function(event){
 | 
			
		||||
 | 
			
		||||
    var _id = $(this).data('_id')
 | 
			
		||||
    var _id = $(this).data('_id');
 | 
			
		||||
 | 
			
		||||
    if ($(this).data('result-action') == 'generate') {
 | 
			
		||||
        $.ajax({
 | 
			
		||||
@@ -192,4 +198,32 @@ $('.result-action-buttons').click(function(event){
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    event.preventDefault();
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Script for Deleting Time Adjustment
 | 
			
		||||
$('.adjustment-delete').click(function(event){
 | 
			
		||||
 | 
			
		||||
    var _id = $(this).data('_id');
 | 
			
		||||
    var location = window.location.href;
 | 
			
		||||
    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);
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
    
 | 
			
		||||
    event.preventDefault();
 | 
			
		||||
});
 | 
			
		||||
@@ -1 +1 @@
 | 
			
		||||
<div id="alert-box"></div>
 | 
			
		||||
<div id="alert-box" tabindex="-1"></div>
 | 
			
		||||
							
								
								
									
										180
									
								
								ref-test/admin/templates/admin/test.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								ref-test/admin/templates/admin/test.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,180 @@
 | 
			
		||||
{% extends "admin/components/base.html" %}
 | 
			
		||||
{% block title %} SKA Referee Test | Edit Exam {% endblock %}
 | 
			
		||||
{% block content %}
 | 
			
		||||
    <h1>Edit Exam</h1>
 | 
			
		||||
 | 
			
		||||
    <div class="container">
 | 
			
		||||
        <div class="row justify-content-center">
 | 
			
		||||
            <div class="col-lg-6">          
 | 
			
		||||
                <ul class="list-group">
 | 
			
		||||
                    <li class="list-group-item list-group-item-action">
 | 
			
		||||
                        <div class="d-flex w-100 justify-content-between">
 | 
			
		||||
                            <h5 class="mb-1">Exam Code</h5>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <h2>
 | 
			
		||||
                            {{ '—'.join([test.test_code[:4], test.test_code[4:8], test.test_code[8:]]) }}
 | 
			
		||||
                        </h2>
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li class="list-group-item list-group-item-action">
 | 
			
		||||
                        <div class="d-flex w-100 justify-content-between">
 | 
			
		||||
                            <h5 class="mb-1">Dataset</h5>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        {{ test.dataset }}
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li class="list-group-item list-group-item-action">
 | 
			
		||||
                        <div class="d-flex w-100 justify-content-between">
 | 
			
		||||
                            <h5 class="mb-1">Created By</h5>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        {{ test.creator }}
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li class="list-group-item list-group-item-action">
 | 
			
		||||
                        <div class="d-flex w-100 justify-content-between">
 | 
			
		||||
                            <h5 class="mb-1">Date Created</h5>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        {{ test.date_created.strftime('%d %b %Y') }}
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li class="list-group-item list-group-item-action">
 | 
			
		||||
                        <div class="d-flex w-100 justify-content-between">
 | 
			
		||||
                            <h5 class="mb-1">Expiry Date</h5>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        {{ test.start_date.strftime('%d %b %Y') }}
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li class="list-group-item list-group-item-action">
 | 
			
		||||
                        <div class="d-flex w-100 justify-content-between">
 | 
			
		||||
                            <h5 class="mb-1">Expiry Date</h5>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        {{ test.expiry_date.strftime('%d %b %Y') }}
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li class="list-group-item list-group-item-action">
 | 
			
		||||
                        <div class="d-flex w-100 justify-content-between">
 | 
			
		||||
                            <h5 class="mb-1">Time Limit</h5>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        {% if test.time_limit == None -%}
 | 
			
		||||
                            None
 | 
			
		||||
                        {% elif test.time_limit == 60 -%}
 | 
			
		||||
                            1 hour
 | 
			
		||||
                        {% elif test.time_limit == 90 -%}
 | 
			
		||||
                            1 hour 30 min
 | 
			
		||||
                        {% elif test.time_limit == 120 -%}
 | 
			
		||||
                            2 hours
 | 
			
		||||
                        {% else -%}
 | 
			
		||||
                            {{ test.time_limit }}
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <div class="accordion" id="test-info-detail">
 | 
			
		||||
                        {% if 'entries' in test and test.entries|length > 0 %}
 | 
			
		||||
                            <div class="accordion-item">
 | 
			
		||||
                                <h2 class="accordion-header" id="test-entries">
 | 
			
		||||
                                    <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#test-entries-list" aria-expanded="false" aria-controls="test-entries-list">
 | 
			
		||||
                                        List Test Entries
 | 
			
		||||
                                    </button>
 | 
			
		||||
                                </h2>
 | 
			
		||||
                                <div id="test-entries-list" class="accordion-collapse collapse" aria-labelledby="test-entries" data-bs-parent="#test-info-detail">
 | 
			
		||||
                                    <div class="accordion-body">
 | 
			
		||||
                                        <table class="table table-striped">
 | 
			
		||||
                                            <tbody>
 | 
			
		||||
                                                {% for entry in test.entries %}
 | 
			
		||||
                                                    <tr>
 | 
			
		||||
                                                        <td>
 | 
			
		||||
                                                            <a href="{{ url_for('admin_views.view_entry', _id=entry) }}" >Entry {{ loop.index }}</a>
 | 
			
		||||
                                                        </td>
 | 
			
		||||
                                                    </tr>
 | 
			
		||||
                                                {% endfor %}
 | 
			
		||||
                                            </tbody>
 | 
			
		||||
                                        </table>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                        {% if 'time_adjustments' in test and test.time_adjustments|length > 0 %}
 | 
			
		||||
                            <div class="accordion-item">
 | 
			
		||||
                                <h2 class="accordion-header" id="test-adjustments">
 | 
			
		||||
                                    <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#test-adjustments-list" aria-expanded="false" aria-controls="test-adjustments-list">
 | 
			
		||||
                                        List Time Adjustments
 | 
			
		||||
                                    </button>
 | 
			
		||||
                                </h2>
 | 
			
		||||
                                <div id="test-adjustments-list" class="accordion-collapse collapse" aria-labelledby="test-adjustments" data-bs-parent="#test-info-detail">
 | 
			
		||||
                                    <div class="accordion-body">
 | 
			
		||||
                                        <table class="table table-striped">
 | 
			
		||||
                                            <thead>
 | 
			
		||||
                                                <tr>
 | 
			
		||||
                                                    <th>
 | 
			
		||||
                                                        User Code
 | 
			
		||||
                                                    </th>
 | 
			
		||||
                                                    <th>
 | 
			
		||||
                                                        Adjustment (Minutes)
 | 
			
		||||
                                                    </th>
 | 
			
		||||
                                                    <th>
 | 
			
		||||
                                                        Delete
 | 
			
		||||
                                                    </th>
 | 
			
		||||
                                                </tr>
 | 
			
		||||
                                            </thead>
 | 
			
		||||
                                            <tbody>
 | 
			
		||||
                                                {% for entry in test.time_adjustments %}
 | 
			
		||||
                                                    <tr>
 | 
			
		||||
                                                        <td>
 | 
			
		||||
                                                            {{ entry.user_code }}
 | 
			
		||||
                                                        </td>
 | 
			
		||||
                                                        <td>
 | 
			
		||||
                                                            {{ entry.time_adjustment }}
 | 
			
		||||
                                                        </td>
 | 
			
		||||
                                                        <td>
 | 
			
		||||
                                                            <a href="javascript::void(0);" class="btn btn-danger adjustment-delete" title="Delete Adjustment" data-_id="{{ entry._id }}" data-action="delete">
 | 
			
		||||
                                                                <i class="bi bi-slash-circle-fill button-icon"></i>
 | 
			
		||||
                                                            </a>
 | 
			
		||||
                                                        </td>
 | 
			
		||||
                                                    </tr>
 | 
			
		||||
                                                {% endfor %}
 | 
			
		||||
                                            </tbody>
 | 
			
		||||
                                        </table>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                        {% if not test.time_limit == None %}
 | 
			
		||||
                            <div class="accordion-item">
 | 
			
		||||
                                <h2 class="accordion-header" id="test-add-adjustments">
 | 
			
		||||
                                    <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#test-adjustments-add" aria-expanded="false" aria-controls="test-adjustments-add">
 | 
			
		||||
                                        Add Time Adjustments
 | 
			
		||||
                                    </button>
 | 
			
		||||
                                </h2>
 | 
			
		||||
                                <div id="test-adjustments-add" class="accordion-collapse collapse" aria-labelledby="test-add-adjustments" data-bs-parent="#test-info-detail">
 | 
			
		||||
                                    <div class="accordion-body">
 | 
			
		||||
                                        <form name="form-add-adjustment" class="form-display form-post" action="{{ url_for(request.endpoint, **request.view_args) }}" data-rel-success="">
 | 
			
		||||
                                            {{ form.hidden_tag() }}
 | 
			
		||||
                                            <div class="form-label-group">
 | 
			
		||||
                                                {{ form.time(class_="form-control", placeholder="Enter Username") }}
 | 
			
		||||
                                                {{ form.time.label }}
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                            <div class="container form-submission-button">
 | 
			
		||||
                                                <div class="row">
 | 
			
		||||
                                                    <div class="col text-center">
 | 
			
		||||
                                                        <button title="Add Time Adjustment" class="btn btn-md btn-success btn-block" type="submit">
 | 
			
		||||
                                                            <i class="bi bi-clock-history button-icon"></i>
 | 
			
		||||
                                                            Add Time Adjustment
 | 
			
		||||
                                                        </button>
 | 
			
		||||
                                                    </div>
 | 
			
		||||
                                                </div>
 | 
			
		||||
                                            </div>
 | 
			
		||||
                                        </form>
 | 
			
		||||
                                    </div>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    </div>
 | 
			
		||||
                </ul>
 | 
			
		||||
                <div class="row">
 | 
			
		||||
                    {% include "admin/components/client-alerts.html" %}
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="container justify-content-center">
 | 
			
		||||
                    <div class="row">                            
 | 
			
		||||
                            <a href="#" class="btn btn-danger test-action" data-action="delete" data-_id="{{ test._id }}">
 | 
			
		||||
                                <i class="bi bi-file-earmark-excel-fill button-icon"></i>
 | 
			
		||||
                                Delete Exam
 | 
			
		||||
                            </a>
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
@@ -60,17 +60,19 @@
 | 
			
		||||
                        <td class="row-actions">
 | 
			
		||||
                            <a
 | 
			
		||||
                                href="#"
 | 
			
		||||
                                class="btn btn-primary edit-test"
 | 
			
		||||
                                class="btn btn-primary test-action"
 | 
			
		||||
                                data-_id="{{test._id}}"
 | 
			
		||||
                                title="Edit Exam"
 | 
			
		||||
                                data-action="edit"
 | 
			
		||||
                            >
 | 
			
		||||
                                <i class="bi bi-file-earmark-text-fill button-icon"></i>
 | 
			
		||||
                            </a>
 | 
			
		||||
                            <a
 | 
			
		||||
                                href="#"
 | 
			
		||||
                                class="btn btn-danger delete-test"
 | 
			
		||||
                                class="btn btn-danger test-action"
 | 
			
		||||
                                data-_id="{{test._id}}"
 | 
			
		||||
                                title="Delete Exam"
 | 
			
		||||
                                data-action="delete"
 | 
			
		||||
                            >
 | 
			
		||||
                                <i class="bi bi-file-earmark-excel-fill button-icon"></i>
 | 
			
		||||
                            </button>
 | 
			
		||||
 
 | 
			
		||||
@@ -407,10 +407,26 @@ def delete_test():
 | 
			
		||||
@views.route('/test/<_id>/', methods=['GET','POST'])
 | 
			
		||||
@admin_account_required
 | 
			
		||||
@login_required
 | 
			
		||||
def view_test(_id, filter=''):
 | 
			
		||||
    test = db.tests.find_one({'_id':_id})
 | 
			
		||||
def view_test(_id):
 | 
			
		||||
    from .models.forms import AddTimeAdjustment
 | 
			
		||||
    form = AddTimeAdjustment()
 | 
			
		||||
    test = decrypt_find_one(db.tests, {'_id': _id})
 | 
			
		||||
    if request.method == 'GET':
 | 
			
		||||
        if not test:
 | 
			
		||||
            return abort(404)
 | 
			
		||||
        return render_template('/admin/test.html', test = test, form = form)
 | 
			
		||||
    if request.method == 'POST':
 | 
			
		||||
        if form.validate_on_submit():
 | 
			
		||||
            time = request.form.get('time')
 | 
			
		||||
            return Test(_id=_id).add_time_adjustment(time)
 | 
			
		||||
        return jsonify({'error': form.time.errors }), 400
 | 
			
		||||
 | 
			
		||||
@views.route('/test/<_id>/delete-adjustment/', methods = ['POST'])
 | 
			
		||||
@admin_account_required
 | 
			
		||||
@login_required
 | 
			
		||||
def delete_adjustment(_id):
 | 
			
		||||
    adjustment_id = request.get_json()['_id']
 | 
			
		||||
    return Test(_id=_id).remove_time_adjustment(adjustment_id)
 | 
			
		||||
 | 
			
		||||
@views.route('/results/')
 | 
			
		||||
@admin_account_required
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user