Finished quiz and debugging
This commit is contained in:
		@@ -15,30 +15,30 @@
 | 
			
		||||
                            <h5 class="mb-1">Candidate</h5>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <h2>
 | 
			
		||||
                            {{ entry.name.surname}}, {{ entry.name.first_name }}
 | 
			
		||||
                            {{ entry.get_surname()}}, {{ entry.get_first_name() }}
 | 
			
		||||
                        </h2>
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li class="list-group-item list-group-item-action">
 | 
			
		||||
                        <div class="d-flex w-100 justify-content-between">
 | 
			
		||||
                            <h5 class="mb-1">Email Address</h5>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        {{ entry.email }}
 | 
			
		||||
                        {{ entry.get_email() }}
 | 
			
		||||
                    </li>
 | 
			
		||||
                    {% if entry['club'] %}
 | 
			
		||||
                    {% if entry.club %}
 | 
			
		||||
                        <li class="list-group-item list-group-item-action">
 | 
			
		||||
                            <div class="d-flex w-100 justify-content-between">
 | 
			
		||||
                                <h5 class="mb-1">Club</h5>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            {{ entry.club }}
 | 
			
		||||
                            {{ entry.get_club() }}
 | 
			
		||||
                        </li>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                    <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>
 | 
			
		||||
                        {{ '—'.join([entry.test_code[:4], entry.test_code[4:8], entry.test_code[8:]]) }}
 | 
			
		||||
                        {{ entry.test.get_code() }}
 | 
			
		||||
                    </li>
 | 
			
		||||
                    {% if entry['user_code'] %}
 | 
			
		||||
                    {% if entry.user_code %}
 | 
			
		||||
                        <li class="list-group-item list-group-item-action">
 | 
			
		||||
                            <div class="d-flex w-100 justify-content-between">
 | 
			
		||||
                                <h5 class="mb-1">User Code</h5>
 | 
			
		||||
@@ -59,19 +59,19 @@
 | 
			
		||||
                                <span class="badge bg-danger">Late</span>
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                        </div>
 | 
			
		||||
                        {{ entry.submission_time.strftime('%d %b %Y %H:%M:%S') }}
 | 
			
		||||
                        {{ entry.end_time.strftime('%d %b %Y %H:%M:%S') }}
 | 
			
		||||
                    </li>       
 | 
			
		||||
                    <li class="list-group-item list-group-item-action">
 | 
			
		||||
                        <div class="d-flex w-100 justify-content-between">
 | 
			
		||||
                            <h5 class="mb-1">Score</h5>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        {{ entry.results.score }}%
 | 
			
		||||
                        {{ entry.result.score }}%
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li class="list-group-item list-group-item-action {% if entry.results.grade == 'fail' %}list-group-item-danger {% elif entry.results.grade == 'merit' %} list-group-item-success {% endif %}">
 | 
			
		||||
                    <li class="list-group-item list-group-item-action {% if entry.result.grade == 'fail' %}list-group-item-danger {% elif entry.result.grade == 'merit' %} list-group-item-success {% endif %}">
 | 
			
		||||
                        <div class="d-flex w-100 justify-content-between">
 | 
			
		||||
                            <h5 class="mb-1">Grade</h5>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        {{ entry.results.grade[0]|upper }}{{ entry.results.grade[1:]}}
 | 
			
		||||
                        {{ entry.result.grade[0]|upper }}{{ entry.result.grade[1:]}}
 | 
			
		||||
                    </li>
 | 
			
		||||
                </ul>
 | 
			
		||||
                <div class="site-footer mt-5">
 | 
			
		||||
 
 | 
			
		||||
@@ -23,8 +23,37 @@
 | 
			
		||||
                    <li class="nav-item" id="nav-results">
 | 
			
		||||
                        <a href="{{ url_for('admin._view_entries') }}" id="link-results" class="nav-link">View Results</a>
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li class="nav-item" id="nav-tests">
 | 
			
		||||
                        <a href="{{ url_for('admin._tests') }}" id="link-tests" class="nav-link">Manage Exams</a>
 | 
			
		||||
                    <li class="nav-item dropdown" id="nav-tests">
 | 
			
		||||
                        <a
 | 
			
		||||
                            class="nav-link dropdown-toggle"
 | 
			
		||||
                            id="dropdown-tests"
 | 
			
		||||
                            role="button"
 | 
			
		||||
                            href="{{ url_for('admin._tests') }}"
 | 
			
		||||
                            data-bs-toggle="dropdown"
 | 
			
		||||
                            aria-expanded="false"
 | 
			
		||||
                        >
 | 
			
		||||
                            Exams
 | 
			
		||||
                        </a>
 | 
			
		||||
                        <ul
 | 
			
		||||
                            class="dropdown-menu"
 | 
			
		||||
                            aria-labelledby="dropdown-settings"
 | 
			
		||||
                        >
 | 
			
		||||
                            <li>
 | 
			
		||||
                                <a href="{{ url_for('admin._tests', filter='active') }}" id="link-active" class="dropdown-item">Active</a>
 | 
			
		||||
                            </li>
 | 
			
		||||
                            <li>
 | 
			
		||||
                                <a href="{{ url_for('admin._tests', filter='scheduled') }}" id="link-scheduled" class="dropdown-item">Scheduled</a>
 | 
			
		||||
                            </li>
 | 
			
		||||
                            <li>
 | 
			
		||||
                                <a href="{{ url_for('admin._tests', filter='expired') }}" id="link-expired" class="dropdown-item">Expired</a>
 | 
			
		||||
                            </li>
 | 
			
		||||
                            <li>
 | 
			
		||||
                                <a href="{{ url_for('admin._tests', filter='all') }}" id="link-all" class="dropdown-item">All</a>
 | 
			
		||||
                            </li>
 | 
			
		||||
                            <li>
 | 
			
		||||
                                <a href="{{ url_for('admin._tests', filter='create') }}" id="link-create" class="dropdown-item">Create</a>
 | 
			
		||||
                            </li>
 | 
			
		||||
                        </ul>
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li class="nav-item dropdown" id="nav-settings">
 | 
			
		||||
                        <a
 | 
			
		||||
@@ -42,7 +71,7 @@
 | 
			
		||||
                            aria-labelledby="dropdown-settings"
 | 
			
		||||
                        >
 | 
			
		||||
                            <li>
 | 
			
		||||
                                <a href="{{ url_for('admin._settings') }}" id="link-settings" class="dropdown-item">Settings</a>
 | 
			
		||||
                                <a href="{{ url_for('admin._settings') }}" id="link-settings" class="dropdown-item">View Settings</a>
 | 
			
		||||
                            </li>
 | 
			
		||||
                            <li>
 | 
			
		||||
                                <a href="{{ url_for('admin._users') }}" id="link-users" class="dropdown-item">Users</a>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<div class="navbar navbar-expand-sm navbar-light bg-light">
 | 
			
		||||
    <div class="container-fluid">
 | 
			
		||||
    <div class="expand navbar-expand justify-content-center" id="navbar_secondary">
 | 
			
		||||
        <ul class="nav">
 | 
			
		||||
        <ul class="nav nav-pills">
 | 
			
		||||
            <li class="nav-item">
 | 
			
		||||
                <a class="nav-link" href="{{ url_for('admin._tests', filter='active') }}">Active</a>
 | 
			
		||||
            </li>
 | 
			
		||||
 
 | 
			
		||||
@@ -69,13 +69,13 @@
 | 
			
		||||
                                        {% for result in recent_results %}
 | 
			
		||||
                                            <tr>
 | 
			
		||||
                                                <td>
 | 
			
		||||
                                                    <a href="{{ url_for('admin._view_entry', id=result.id) }}">{{ result.name.surname }}, {{ result.name.first_name }}</a>
 | 
			
		||||
                                                    <a href="{{ url_for('admin._view_entry', id=result.id) }}">{{ result.get_surname() }}, {{ result.get_first_name() }}</a>
 | 
			
		||||
                                                </td>
 | 
			
		||||
                                                <td>
 | 
			
		||||
                                                    {{ result.submission_time.strftime('%d %b %Y %H:%M') }}
 | 
			
		||||
                                                    {{ result.end_time.strftime('%d %b %Y %H:%M') }}
 | 
			
		||||
                                                </td>
 | 
			
		||||
                                                <td>
 | 
			
		||||
                                                    {{ result.percent }}% ({{ result.results.grade }})
 | 
			
		||||
                                                    {{ (100*result.result['score']/result.result['max'])|round|int }}% ({{ result.result.grade }})
 | 
			
		||||
                                                </td>
 | 
			
		||||
                                            </tr>
 | 
			
		||||
                                        {% endfor %}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,30 +13,30 @@
 | 
			
		||||
                            <h5 class="mb-1">Candidate</h5>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        <h2>
 | 
			
		||||
                            {{ entry.name.surname }}, {{ entry.name.first_name }}
 | 
			
		||||
                            {{ entry.get_surname() }}, {{ entry.get_first_name() }}
 | 
			
		||||
                        </h2>
 | 
			
		||||
                    </li>
 | 
			
		||||
                    <li class="list-group-item list-group-item-action">
 | 
			
		||||
                        <div class="d-flex w-100 justify-content-between">
 | 
			
		||||
                            <h5 class="mb-1">Email Address</h5>
 | 
			
		||||
                        </div>
 | 
			
		||||
                        {{ entry.email }}
 | 
			
		||||
                        {{ entry.get_email() }}
 | 
			
		||||
                    </li>
 | 
			
		||||
                    {% if entry['club'] %}
 | 
			
		||||
                    {% if entry.club %}
 | 
			
		||||
                        <li class="list-group-item list-group-item-action">
 | 
			
		||||
                            <div class="d-flex w-100 justify-content-between">
 | 
			
		||||
                                <h5 class="mb-1">Club</h5>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            {{ entry.club }}
 | 
			
		||||
                            {{ entry.get_club() }}
 | 
			
		||||
                        </li>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                    <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>
 | 
			
		||||
                        {{ '—'.join([entry.test_code[:4], entry.test_code[4:8], entry.test_code[8:]]) }}
 | 
			
		||||
                        {{ entry.test.get_code() }}
 | 
			
		||||
                    </li>
 | 
			
		||||
                    {% if entry['user_code'] %}
 | 
			
		||||
                    {% if entry.user_code %}
 | 
			
		||||
                        <li class="list-group-item list-group-item-action">
 | 
			
		||||
                            <div class="d-flex w-100 justify-content-between">
 | 
			
		||||
                                <h5 class="mb-1">User Code</h5>
 | 
			
		||||
@@ -44,7 +44,7 @@
 | 
			
		||||
                            {{ entry.user_code }}
 | 
			
		||||
                        </li>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                    {% if 'start_time' in entry %}
 | 
			
		||||
                    {% if entry.start_time %}
 | 
			
		||||
                        <li class="list-group-item list-group-item-action">
 | 
			
		||||
                            <div class="d-flex w-100 justify-content-between">
 | 
			
		||||
                                <h5 class="mb-1">Start Time</h5>
 | 
			
		||||
@@ -59,28 +59,28 @@
 | 
			
		||||
                                <span class="badge bg-danger">Late</span>
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                        </div>
 | 
			
		||||
                        {% if 'submission_time' in entry %}
 | 
			
		||||
                            {{ entry.submission_time.strftime('%d %b %Y %H:%M:%S') }}
 | 
			
		||||
                        {% if entry.end_time %}
 | 
			
		||||
                            {{ entry.end_time.strftime('%d %b %Y %H:%M:%S') }}
 | 
			
		||||
                        {% else %}
 | 
			
		||||
                            Incomplete
 | 
			
		||||
                        {% endif %}
 | 
			
		||||
                    </li>
 | 
			
		||||
                    {% if 'results' in entry %}
 | 
			
		||||
                    {% if entry.result %}
 | 
			
		||||
                        <li class="list-group-item list-group-item-action">
 | 
			
		||||
                            <div class="d-flex w-100 justify-content-between">
 | 
			
		||||
                                <h5 class="mb-1">Score</h5>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            {{ entry.results.score }}%
 | 
			
		||||
                            {{ entry.result.score }}%
 | 
			
		||||
                        </li>
 | 
			
		||||
                        <li class="list-group-item list-group-item-action {% if entry.results.grade == 'fail' %}list-group-item-danger {% elif entry.results.grade == 'merit' %} list-group-item-success {% endif %}">
 | 
			
		||||
                        <li class="list-group-item list-group-item-action {% if entry.result.grade == 'fail' %}list-group-item-danger {% elif entry.result.grade == 'merit' %} list-group-item-success {% endif %}">
 | 
			
		||||
                            <div class="d-flex w-100 justify-content-between">
 | 
			
		||||
                                <h5 class="mb-1">Grade</h5>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            {{ entry.results.grade[0]|upper }}{{ entry.results.grade[1:]}}
 | 
			
		||||
                            {{ entry.result.grade[0]|upper }}{{ entry.result.grade[1:]}}
 | 
			
		||||
                        </li>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                </ul>
 | 
			
		||||
                {% if 'results' in entry %}
 | 
			
		||||
                {% if entry.result %}
 | 
			
		||||
                    <div class="accordion" id="results-breakdown">
 | 
			
		||||
                        <div class="accordion-item">
 | 
			
		||||
                            <h2 class="accordion-header" id="by-category">
 | 
			
		||||
@@ -105,7 +105,7 @@
 | 
			
		||||
                                            </tr>
 | 
			
		||||
                                        </thead>
 | 
			
		||||
                                        <tbody>
 | 
			
		||||
                                            {% for tag, scores in entry.results.tags.items() %}
 | 
			
		||||
                                            {% for tag, scores in entry.result.tags.items() %}
 | 
			
		||||
                                                <tr>
 | 
			
		||||
                                                    <td>
 | 
			
		||||
                                                        {{ tag }}
 | 
			
		||||
@@ -149,8 +149,8 @@
 | 
			
		||||
                                                        {{ question }}
 | 
			
		||||
                                                    </td>
 | 
			
		||||
                                                    <td>
 | 
			
		||||
                                                        {{ answer }}
 | 
			
		||||
                                                        {% if not correct[question] == answer %}
 | 
			
		||||
                                                        {{ answers[question|int][answer|int] }}
 | 
			
		||||
                                                        {% if not correct[question] == answer|int %}
 | 
			
		||||
                                                            <span class="badge badge-pill bg-danger badge-danger">Incorrect</span>
 | 
			
		||||
                                                        {% endif %}
 | 
			
		||||
                                                    </td>
 | 
			
		||||
 
 | 
			
		||||
@@ -37,34 +37,34 @@
 | 
			
		||||
                {% for entry in entries %}
 | 
			
		||||
                    <tr class="table-row">
 | 
			
		||||
                        <td>
 | 
			
		||||
                            {{ entry.name.surname }}, {{ entry.name.first_name }}
 | 
			
		||||
                            {{ entry.get_surname() }}, {{ entry.get_first_name() }}
 | 
			
		||||
                        </td>
 | 
			
		||||
                        <td>
 | 
			
		||||
                            {% if 'club' in entry %}
 | 
			
		||||
                                {{ entry.club }}
 | 
			
		||||
                            {% if entry.club %}
 | 
			
		||||
                                {{ entry.get_club() }}
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                        </td>
 | 
			
		||||
                        <td>
 | 
			
		||||
                            {{ '—'.join([entry.test_code[:4], entry.test_code[4:8], entry.test_code[8:]]) }}
 | 
			
		||||
                            {{ entry.test.get_code() }}
 | 
			
		||||
                        </td>
 | 
			
		||||
                        <td>
 | 
			
		||||
                            {% if 'status' in entry %}
 | 
			
		||||
                            {% if entry.status %}
 | 
			
		||||
                                {{ entry.status }}
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                        </td>
 | 
			
		||||
                        <td>
 | 
			
		||||
                            {% if 'submission_time' in entry %}
 | 
			
		||||
                                {{ entry.submission_time.strftime('%d %b %Y') }}
 | 
			
		||||
                            {% if entry.end_time %}
 | 
			
		||||
                                {{ entry.end_time.strftime('%d %b %Y') }}
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                        </td>
 | 
			
		||||
                        <td>
 | 
			
		||||
                            {% if 'results' in entry %}
 | 
			
		||||
                                {{ entry.results.score }}%
 | 
			
		||||
                            {% if entry.result %}
 | 
			
		||||
                                {{ entry.result.score }}%
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                        </td>
 | 
			
		||||
                        <td>
 | 
			
		||||
                            {% if 'results' in entry %}
 | 
			
		||||
                                {{ entry.results.grade }}
 | 
			
		||||
                            {% if entry.result %}
 | 
			
		||||
                                {{ entry.result.grade }}
 | 
			
		||||
                            {% endif %}
 | 
			
		||||
                        </td>
 | 
			
		||||
                        <td class="row-actions">
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@ from ..models import Dataset, Entry, Test, User
 | 
			
		||||
from ..tools.auth import disable_if_logged_in, require_account_creation
 | 
			
		||||
from ..tools.forms import get_dataset_choices, get_time_options
 | 
			
		||||
from ..tools.data import check_is_json, validate_json
 | 
			
		||||
from ..tools.test import get_correct_answers
 | 
			
		||||
from ..tools.test import  answer_options, get_correct_answers
 | 
			
		||||
 | 
			
		||||
from flask import Blueprint, jsonify, render_template, redirect, request, session
 | 
			
		||||
from flask.helpers import flash, url_for
 | 
			
		||||
@@ -33,8 +33,6 @@ def _home():
 | 
			
		||||
    upcoming_tests.sort(key= lambda x: x.start_date)
 | 
			
		||||
    recent_results = [result for result in results if not result.status == 'started' ]
 | 
			
		||||
    recent_results.sort(key= lambda x: x.end_time, reverse=True)
 | 
			
		||||
    for result in recent_results:
 | 
			
		||||
        result['percent'] = round(100*result['result']['score']/result['result']['max'])
 | 
			
		||||
    return render_template('/admin/index.html', current_tests = current_tests, upcomimg_tests = upcoming_tests, recent_results = recent_results)
 | 
			
		||||
 | 
			
		||||
@admin.route('/settings/')
 | 
			
		||||
@@ -255,7 +253,7 @@ def _tests(filter:str=None):
 | 
			
		||||
    if not datasets:
 | 
			
		||||
        flash('There are no available question datasets. Please upload a question dataset in order to set up an exam.', 'error')
 | 
			
		||||
        return redirect(url_for('admin._questions'))
 | 
			
		||||
    if filter not in [None, '', 'create','active','scheduled','expired','all']: return redirect(url_for('admin._tests'))
 | 
			
		||||
    if filter not in ['create','active','scheduled','expired','all']: return redirect(url_for('admin._tests', filter='active'))
 | 
			
		||||
    if filter == 'create':
 | 
			
		||||
        form = CreateTest()
 | 
			
		||||
        form.time_limit.choices = get_time_options()
 | 
			
		||||
@@ -337,7 +335,7 @@ def _view_test(id:str=None):
 | 
			
		||||
        return jsonify({'error': form.time.errors }), 400
 | 
			
		||||
    if not test:
 | 
			
		||||
        flash('Invalid test ID.', 'error')
 | 
			
		||||
        return redirect(url_for('admin._tests'))
 | 
			
		||||
        return redirect(url_for('admin._tests', filter='active'))
 | 
			
		||||
    return render_template('/admin/test.html', test = test, form = form)
 | 
			
		||||
 | 
			
		||||
@admin.route('/test/<string:id>/delete-adjustment/', methods=['POST'])
 | 
			
		||||
@@ -359,7 +357,7 @@ def _view_entries():
 | 
			
		||||
@admin.route('/results/<string:id>/', methods = ['GET', 'POST'])
 | 
			
		||||
@login_required
 | 
			
		||||
def _view_entry(id:str=None):
 | 
			
		||||
    entry = Entry.query.filter_by(id=id)
 | 
			
		||||
    entry = Entry.query.filter_by(id=id).first()
 | 
			
		||||
    if request.method == 'POST':
 | 
			
		||||
        if not entry: return jsonify({'error': 'Invalid entry ID.'}), 404
 | 
			
		||||
        action = request.get_json()['action']
 | 
			
		||||
@@ -375,13 +373,14 @@ def _view_entry(id:str=None):
 | 
			
		||||
    if not entry:
 | 
			
		||||
        flash('Invalid entry ID.', 'error')
 | 
			
		||||
        return redirect(url_for('admin._view_entries'))
 | 
			
		||||
    test = entry['test']
 | 
			
		||||
    test = entry.test
 | 
			
		||||
    dataset = test.dataset
 | 
			
		||||
    dataset_path = dataset.get_file()
 | 
			
		||||
    with open(dataset_path, 'r') as _dataset:
 | 
			
		||||
        data = loads(_dataset.read())
 | 
			
		||||
    correct = get_correct_answers(dataset=data)
 | 
			
		||||
    return render_template('/admin/result-detail.html', entry = entry, correct = correct)
 | 
			
		||||
    answers = answer_options(dataset=data)
 | 
			
		||||
    return render_template('/admin/result-detail.html', entry = entry, correct = correct, answers = answers)
 | 
			
		||||
 | 
			
		||||
@admin.route('/certificate/',methods=['POST'])
 | 
			
		||||
@login_required
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user