14 Commits

10 changed files with 57 additions and 16 deletions

View File

@ -50,7 +50,7 @@
<div class="d-flex w-100 justify-content-between"> <div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">Start Time</h5> <h5 class="mb-1">Start Time</h5>
</div> </div>
{{ entry.start_time.strftime('%d %b %Y %H:%M:%S') }} {{ entry.start_time.strftime('%d %b %Y %H:%M:%S') if entry.start_time else None }}
</li> </li>
<li class="list-group-item list-group-item-action"> <li class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between"> <div class="d-flex w-100 justify-content-between">
@ -59,7 +59,7 @@
<span class="badge bg-danger">Late</span> <span class="badge bg-danger">Late</span>
{% endif %} {% endif %}
</div> </div>
{{ entry.end_time.strftime('%d %b %Y %H:%M:%S') }} {{ entry.end_time.strftime('%d %b %Y %H:%M:%S') if entry.end_time else None }}
</li> </li>
<li class="list-group-item list-group-item-action"> <li class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between"> <div class="d-flex w-100 justify-content-between">

View File

@ -28,7 +28,7 @@
<a href="{{ url_for('admin._view_test', id=test.id) }}">{{ test.get_code() }}</a> <a href="{{ url_for('admin._view_test', id=test.id) }}">{{ test.get_code() }}</a>
</td> </td>
<td> <td>
{{ test.end_date.strftime('%d %b %Y') }} {{ test.end_date.strftime('%d %b %Y') if test.end_date else None }}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
@ -72,10 +72,14 @@
<a href="{{ url_for('admin._view_entry', id=result.id) }}">{{ result.get_surname() }}, {{ result.get_first_name() }}</a> <a href="{{ url_for('admin._view_entry', id=result.id) }}">{{ result.get_surname() }}, {{ result.get_first_name() }}</a>
</td> </td>
<td> <td>
{{ result.end_time.strftime('%d %b %Y %H:%M') }} {{ result.end_time.strftime('%d %b %Y %H:%M') if result.end_time else None }}
</td> </td>
<td> <td>
{{ (100*result.result['score']/result.result['max'])|round|int }}&percnt; ({{ result.result.grade }}) {% if result.result %}
{{ (100*result.result['score']/result.result['max'])|round|int }}&percnt; ({{ result.result.grade }})
{% else %}
Incomplete
{% endif %}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
@ -117,7 +121,7 @@
<a href="{{ url_for('admin._view_test', id=test.id) }}">{{ test.get_code() }}</a> <a href="{{ url_for('admin._view_test', id=test.id) }}">{{ test.get_code() }}</a>
</td> </td>
<td> <td>
{{ test.end_date.strftime('%d %b %Y') }} {{ test.end_date.strftime('%d %b %Y') if test.end_date else None }}
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@ -49,7 +49,7 @@
<div class="d-flex w-100 justify-content-between"> <div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">Start Time</h5> <h5 class="mb-1">Start Time</h5>
</div> </div>
{{ entry.start_time.strftime('%d %b %Y %H:%M:%S') }} {{ entry.start_time.strftime('%d %b %Y %H:%M:%S') if entry.start_time else None }}
</li> </li>
{% endif %} {% endif %}
<li class="list-group-item list-group-item-action"> <li class="list-group-item list-group-item-action">

View File

@ -32,13 +32,13 @@
<div class="d-flex w-100 justify-content-between"> <div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">Start Date</h5> <h5 class="mb-1">Start Date</h5>
</div> </div>
{{ test.start_date.strftime('%d %b %Y %H:%M') }} {{ test.start_date.strftime('%d %b %Y %H:%M') if test.start_date else None }}
</li> </li>
<li class="list-group-item list-group-item-action"> <li class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between"> <div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">Expiry Date</h5> <h5 class="mb-1">Expiry Date</h5>
</div> </div>
{{ test.end_date.strftime('%d %b %Y %H:%M') }} {{ test.end_date.strftime('%d %b %Y %H:%M') if test.end_date else None }}
</li> </li>
<li class="list-group-item list-group-item-action"> <li class="list-group-item list-group-item-action">
<div class="d-flex w-100 justify-content-between"> <div class="d-flex w-100 justify-content-between">
@ -71,7 +71,7 @@
{% for entry in test.entries %} {% for entry in test.entries %}
<tr> <tr>
<td> <td>
<a href="{{ url_for('admin._view_entry', id=entry) }}" >Entry {{ loop.index }}</a> <a href="{{ url_for('admin._view_entry', id=entry.id) }}" >Entry {{ loop.index }}</a>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View File

@ -10,7 +10,7 @@ from flask import abort, Blueprint, jsonify, render_template, request, send_file
from flask.helpers import abort, flash, redirect, url_for from flask.helpers import abort, flash, redirect, url_for
from flask_login import current_user, login_required from flask_login import current_user, login_required
from datetime import date, datetime, timedelta from datetime import date, datetime, MINYEAR, timedelta
from json import loads from json import loads
from os import path from os import path
import secrets import secrets
@ -34,11 +34,11 @@ def _home():
write('system.log', f'Database error when processing request \'{request.url}\': {exception}') write('system.log', f'Database error when processing request \'{request.url}\': {exception}')
return abort(500) return abort(500)
current_tests = [ test for test in tests if test.end_date >= datetime.now() and test.start_date.date() <= date.today() ] current_tests = [ test for test in tests if test.end_date >= datetime.now() and test.start_date.date() <= date.today() ]
current_tests.sort(key= lambda x: x.end_date, reverse=True) current_tests.sort(key= lambda x: x.end_date or datetime(MINYEAR,1,1), reverse=True)
upcoming_tests = [ test for test in tests if test.start_date.date() > datetime.now().date()] upcoming_tests = [ test for test in tests if test.start_date.date() > datetime.now().date()]
upcoming_tests.sort(key= lambda x: x.start_date) upcoming_tests.sort(key= lambda x: x.start_date or datetime(MINYEAR,1,1))
recent_results = [result for result in results if not result.status == 'started' ] recent_results = [result for result in results if not result.status == 'started' ]
recent_results.sort(key= lambda x: x.end_time, reverse=True) recent_results.sort(key= lambda x: x.end_time or datetime(MINYEAR,1,1), reverse=True)
return render_template('/admin/index.html', current_tests = current_tests, upcomimg_tests = upcoming_tests, recent_results = recent_results) return render_template('/admin/index.html', current_tests = current_tests, upcomimg_tests = upcoming_tests, recent_results = recent_results)
@admin.route('/settings/') @admin.route('/settings/')
@ -309,7 +309,7 @@ def _tests(filter:str=None):
if filter in [None, '', 'active']: if filter in [None, '', 'active']:
tests = [ test for test in _tests if test.end_date >= now and test.start_date <= now ] tests = [ test for test in _tests if test.end_date >= now and test.start_date <= now ]
display_title = 'Active Exams' display_title = 'Active Exams'
error_none = 'There are no exams that are currently active. You can create one using the Creat Exam form.' error_none = 'There are no exams that are currently active. You can create one using the Create Exam form.'
if filter == 'expired': if filter == 'expired':
tests = [ test for test in _tests if test.end_date < now ] tests = [ test for test in _tests if test.end_date < now ]
display_title = 'Expired Exams' display_title = 'Expired Exams'

View File

@ -12,6 +12,7 @@ class Config(object):
SECRET_KEY = os.getenv('SECRET_KEY') SECRET_KEY = os.getenv('SECRET_KEY')
SERVER_NAME = os.getenv('SERVER_NAME') SERVER_NAME = os.getenv('SERVER_NAME')
SESSION_COOKIE_SECURE = True SESSION_COOKIE_SECURE = True
WTF_CSRF_TIME_LIMIT = None
"""Email Engine Configuration""" """Email Engine Configuration"""
MAIL_SERVER = os.getenv('MAIL_SERVER') MAIL_SERVER = os.getenv('MAIL_SERVER')

View File

@ -14,6 +14,9 @@
<div class="container quiz-start-text"> <div class="container quiz-start-text">
You can use this panel to adjust the display settings for the exam. Please use the menu below to select the font face and font size. Below is a sample question so you can see how the exam will render with your chosen settings. You can use this panel to adjust the display settings for the exam. Please use the menu below to select the font face and font size. Below is a sample question so you can see how the exam will render with your chosen settings.
</div> </div>
<div class="container quiz-start-text">
These settings will be stored locally on your browser window. No information about your preferences below will be collected by the app.
</div>
<div class="alert alert-primary quiz-start-text" role="alert"> <div class="alert alert-primary quiz-start-text" role="alert">
<strong>Note</strong>: Some fonts may not be available depending on your device and/or operating system. <strong>Note</strong>: Some fonts may not be available depending on your device and/or operating system.
</div> </div>

View File

@ -56,6 +56,8 @@
integrity="sha384-QJHtvGhmr9XOIpI6YVutG+2QOK9T+ZnN4kzFN1RtK3zEFEIsxhlmWl5/YESvpZ13" integrity="sha384-QJHtvGhmr9XOIpI6YVutG+2QOK9T+ZnN4kzFN1RtK3zEFEIsxhlmWl5/YESvpZ13"
crossorigin="anonymous" crossorigin="anonymous"
></script> ></script>
<!-- jQuery UI -->
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
<!-- Custom js --> <!-- Custom js -->
<script type="text/javascript"> <script type="text/javascript">
var csrf_token = "{{ csrf_token() }}"; var csrf_token = "{{ csrf_token() }}";

View File

@ -1,6 +1,10 @@
{% extends "quiz/components/base.html" %} {% extends "quiz/components/base.html" %}
{% import "bootstrap/wtf.html" as wtf %} {% import "bootstrap/wtf.html" as wtf %}
{% block style %}
<link rel="stylesheet" href="https://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css">
{% endblock %}
{% block content %} {% block content %}
<div class="form-container"> <div class="form-container">
<form name="form-quiz-start" class="form-quiz-start"> <form name="form-quiz-start" class="form-quiz-start">
@ -43,4 +47,14 @@
</div> </div>
</form> </form>
</div> </div>
{% endblock %}
{% block script %}
<script>
$( function() {
const clubs = {{ clubs|tojson }}
$('#club').autocomplete({
source: clubs
})
} )
</script>
{% endblock %} {% endblock %}

View File

@ -29,6 +29,23 @@ def _instructions():
@quiz.route('/start/', methods=['GET', 'POST']) @quiz.route('/start/', methods=['GET', 'POST'])
def _start(): def _start():
clubs = [
'Dundee Korfball Club',
'Edinburgh City Korfball Club',
'Edinburgh Mavericks Korfball Club',
'Edinburgh University Korfball Club',
'Glasgow Korfball Club',
'Saint Andrews University Korfball Club',
'Strathclyde University Korfball Club'
]
try: entries = Entry.query.all()
except Exception as exception:
write('system.log', f'Database error when processing request \'{request.url}\': {exception}')
return abort(500)
for entry in entries: clubs.append(entry.get_club())
clubs = list(set(clubs))
try: clubs.remove('')
except: pass
form = StartQuiz() form = StartQuiz()
if request.method == 'POST': if request.method == 'POST':
if form.validate_on_submit(): if form.validate_on_submit():
@ -58,7 +75,7 @@ def _start():
}), 200 }), 200
return jsonify({'error': 'There was an error processing the user test and/or user codes.'}), 400 return jsonify({'error': 'There was an error processing the user test and/or user codes.'}), 400
return send_errors_to_client(form=form) return send_errors_to_client(form=form)
return render_template('/quiz/start_quiz.html', form = form) return render_template('/quiz/start_quiz.html', form = form, clubs = clubs)
@quiz.route('/quiz/') @quiz.route('/quiz/')
def _quiz(): def _quiz():