Finished Quiz Console
This commit is contained in:
parent
2ea778143e
commit
b9d45f94fe
@ -49,8 +49,8 @@ def _submit_quiz():
|
|||||||
id = request.get_json()['id']
|
id = request.get_json()['id']
|
||||||
answers = request.get_json()['answers']
|
answers = request.get_json()['answers']
|
||||||
entry = Entry.query.filter_by(id=id).first()
|
entry = Entry.query.filter_by(id=id).first()
|
||||||
if not entry: return jsonify({'error': 'Unrecognised ID.'}), 400
|
if not entry: return jsonify({'error': 'Unrecognised Entry.'}), 400
|
||||||
test = entry['test']
|
test = entry.test
|
||||||
dataset = test.dataset
|
dataset = test.dataset
|
||||||
success, message = dataset.check_file()
|
success, message = dataset.check_file()
|
||||||
if not success: return jsonify({'error': message}), 500
|
if not success: return jsonify({'error': message}), 500
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import StringField
|
||||||
|
from wtforms.validators import InputRequired, Length, Email, Optional
|
||||||
|
|
||||||
|
class StartQuiz(FlaskForm):
|
||||||
|
first_name = StringField('First Name(s)', validators=[InputRequired(), Length(max=30)])
|
||||||
|
surname = StringField('Surname', validators=[InputRequired(), Length(max=30)])
|
||||||
|
email = StringField('Email Address', validators=[InputRequired(), Email(message='You must enter a valid email address.'), Length(max=50)])
|
||||||
|
club = StringField('Affiliated Club (Optional)', validators=[Optional(), Length(max=50)])
|
||||||
|
test_code = StringField('Exam Code', validators=[InputRequired(), Length(min=14, max=14)])
|
||||||
|
user_code = StringField('User Code (Optional)', validators=[Optional(), Length(min=6, max=6)])
|
@ -67,10 +67,12 @@ class Entry(db.Model):
|
|||||||
def get_club(self): return decrypt(self.club)
|
def get_club(self): return decrypt(self.club)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
|
self.generate_id()
|
||||||
self.start_time = datetime.now()
|
self.start_time = datetime.now()
|
||||||
self.status = 'started'
|
self.status = 'started'
|
||||||
write('tests.log', f'New test started by {self.get_first_name()} {self.get_surname()}.')
|
write('tests.log', f'New test started by {self.get_first_name()} {self.get_surname()}.')
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
return True, f'New test started with id {self.id}.'
|
||||||
|
|
||||||
def complete(self, answers:dict=None, result:dict=None):
|
def complete(self, answers:dict=None, result:dict=None):
|
||||||
self.end_time = datetime.now()
|
self.end_time = datetime.now()
|
||||||
@ -84,6 +86,7 @@ class Entry(db.Model):
|
|||||||
self.status = 'late'
|
self.status = 'late'
|
||||||
self.valid = False
|
self.valid = False
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
return True, f'Test entry completed for id {self.id}.'
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if self.valid: return False, f'The entry is already valid.'
|
if self.valid: return False, f'The entry is already valid.'
|
||||||
|
@ -25,7 +25,7 @@ $('form[name=form-quiz-start]').submit(function(event) {
|
|||||||
success: function(response) {
|
success: function(response) {
|
||||||
var id = response.id
|
var id = response.id
|
||||||
window.localStorage.setItem('id', id);
|
window.localStorage.setItem('id', id);
|
||||||
window.location.href = `/test/`;
|
window.location.href = `/quiz/`;
|
||||||
},
|
},
|
||||||
error: function(response) {
|
error: function(response) {
|
||||||
error_response(response);
|
error_response(response);
|
||||||
|
@ -6,13 +6,13 @@
|
|||||||
<h2>Candidate Results</h2>
|
<h2>Candidate Results</h2>
|
||||||
|
|
||||||
<h3 class="results-name">
|
<h3 class="results-name">
|
||||||
<span class="surname">{{ entry.name.surname }}</span>, {{ entry.name.first_name }}
|
<span class="surname">{{ entry.get_surname() }}</span>, {{ entry.get_first_name() }}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
<strong class="results-details">Email Address</strong>: {{ entry.email }} <br />
|
<strong class="results-details">Email Address</strong>: {{ entry.get_email() }} <br />
|
||||||
|
|
||||||
{% if entry.club %}
|
{% if entry.club %}
|
||||||
<strong class="results-details">Club</strong>: {{ entry.club }} <br />
|
<strong class="results-details">Club</strong>: {{ entry.get_club() }} <br />
|
||||||
{% endif%}
|
{% endif%}
|
||||||
|
|
||||||
{% if entry.status == 'late' %}
|
{% if entry.status == 'late' %}
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
from flask import Blueprint
|
from ..forms.quiz import StartQuiz
|
||||||
|
from ..models import Entry, Test
|
||||||
|
from ..tools.test import redirect_if_started
|
||||||
|
|
||||||
|
from flask import abort, Blueprint, jsonify, redirect, render_template, request, session
|
||||||
|
from flask.helpers import flash, url_for
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
quiz = Blueprint(
|
quiz = Blueprint(
|
||||||
name='quiz',
|
name='quiz',
|
||||||
@ -9,21 +16,82 @@ quiz = Blueprint(
|
|||||||
|
|
||||||
@quiz.route('/')
|
@quiz.route('/')
|
||||||
@quiz.route('/home/')
|
@quiz.route('/home/')
|
||||||
|
@redirect_if_started
|
||||||
def _home():
|
def _home():
|
||||||
return 'Quiz Home Page'
|
return render_template('/quiz/index.html')
|
||||||
|
|
||||||
@quiz.route('/instructions/')
|
@quiz.route('/instructions/')
|
||||||
def _instructions():
|
def _instructions():
|
||||||
return 'Instructions'
|
return render_template('/quiz/instructions.html')
|
||||||
|
|
||||||
@quiz.route('/start/')
|
@quiz.route('/start/')
|
||||||
def _start():
|
def _start():
|
||||||
return 'Start Quiz'
|
form = StartQuiz()
|
||||||
|
if request.method == 'POST':
|
||||||
|
if form.validate_on_submit():
|
||||||
|
entry = Entry()
|
||||||
|
entry.set_first_name(request.form.get('first_name'))
|
||||||
|
entry.set_surname(request.form.get('surname'))
|
||||||
|
entry.set_club(request.form.get('club'))
|
||||||
|
code = request.form.get('test_code').replace('—', '').lower()
|
||||||
|
test = Test.query.filter_by(code=code)
|
||||||
|
entry.test = test
|
||||||
|
entry.user_code = request.form.get('user_code')
|
||||||
|
entry.user_code = None if entry.user_code == '' else entry.user_code.lower()
|
||||||
|
if not test: return jsonify({'error': 'The exam code you entered is invalid.'}), 400
|
||||||
|
if entry.user_code and entry.user_code not in test.adjustments: return jsonify({'error': f'The user code you entered is not valid.'}), 400
|
||||||
|
if test.end_date < datetime.now(): return jsonify({'error': f'The exam code you entered expired on {test["expiry_date"].strftime("%d %b %Y %H:%M")}.'}), 400
|
||||||
|
if test.start_date > datetime.now(): return jsonify({'error': f'The exam has not yet opened. Your exam code will be valid from {test["start_date"].strftime("%d %b %Y %H:%M")}.'}), 400
|
||||||
|
success, message = entry.start()
|
||||||
|
if success:
|
||||||
|
session['id'] = entry.id
|
||||||
|
return jsonify({
|
||||||
|
'success': 'Received and validated test and/or user code. Redirecting to test client.',
|
||||||
|
'id': entry.id
|
||||||
|
}), 200
|
||||||
|
return jsonify({'error': 'There was an error processing the user test and/or user codes.'}), 400
|
||||||
|
errors = [*form.test_code.errors, *form.user_code.errors, *form.first_name.errors, *form.surname.errors, *form.email.errors, *form.club.errors]
|
||||||
|
return jsonify({ 'error': errors}), 400
|
||||||
|
render_template('/quiz/start_quiz.html', form = form)
|
||||||
|
|
||||||
@quiz.route('/quiz/')
|
@quiz.route('/quiz/')
|
||||||
def _quiz():
|
def _quiz():
|
||||||
return 'Quiz Console'
|
id = session.get('id')
|
||||||
|
if not id or not Entry.query.filter_by(id=id).first():
|
||||||
|
flash('Your session was not recognised. Please sign in to the quiz again.', 'error')
|
||||||
|
session.pop('id')
|
||||||
|
return redirect(url_for('quiz_views.start'))
|
||||||
|
return render_template('/quiz/client.html')
|
||||||
|
|
||||||
@quiz.route('/result/')
|
@quiz.route('/result/')
|
||||||
def _result():
|
def _result():
|
||||||
return 'Quiz Result'
|
id = session.get('id')
|
||||||
|
entry = Entry.query.filter_by('id').first()
|
||||||
|
if not entry:
|
||||||
|
return abort(404)
|
||||||
|
score = round(100*entry.results['score']/entry.results['max'])
|
||||||
|
tags_low = { tag: tag_result['max'] - tag_result['scored'] for tag, tag_result in entry.results['tags'].items() }
|
||||||
|
sorted_low_tags = sorted(tags_low.items(), key=lambda x: x[1], reverse=True)
|
||||||
|
tag_output = [ tag[0] for tag in sorted_low_tags[0:3] if tag[1] > 3]
|
||||||
|
revision_plain = ''
|
||||||
|
revision_html = ''
|
||||||
|
if entry.results['grade'] == 'pass':
|
||||||
|
flavour_text_plain = """Well done on successfully completing the refereeing theory exam. We really appreciate members of our community taking the time to get qualified to referee.
|
||||||
|
"""
|
||||||
|
elif entry.results['grade'] == 'merit':
|
||||||
|
flavour_text_plain = """Congratulations on achieving a merit in the refereeing exam. We are delighted that members of our community work so hard with refereeing.
|
||||||
|
"""
|
||||||
|
elif entry.results['grade'] == 'fail':
|
||||||
|
flavour_text_plain = """Unfortunately, you were not successful in passing the theory exam in this attempt. We hope that this does not dissuade you, and that you try again in the future.
|
||||||
|
"""
|
||||||
|
revision_plain = f"""Based on your answers, we would also suggest you brush up on the following topics for your next attempt:\n\n
|
||||||
|
{','.join(tag_output)}\n\n
|
||||||
|
"""
|
||||||
|
revision_html = f"""<p>Based on your answers, we would also suggest you brush up on the following topics for your next attempt:</p>
|
||||||
|
<ul>
|
||||||
|
<li>{'</li><li>'.join(tag_output)}</li>
|
||||||
|
</ul>
|
||||||
|
"""
|
||||||
|
if not entry['status'] == 'late':
|
||||||
|
pass
|
||||||
|
return render_template('/quiz/result.html', entry=entry, score=score, tag_output=tag_output)
|
@ -1,4 +1,10 @@
|
|||||||
from .data import randomise_list
|
from .data import randomise_list
|
||||||
|
from ..models import Entry
|
||||||
|
|
||||||
|
from flask import redirect, request, session
|
||||||
|
from flask.helpers import url_for
|
||||||
|
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
def parse_test_code(code):
|
def parse_test_code(code):
|
||||||
return code.replace('—', '').lower()
|
return code.replace('—', '').lower()
|
||||||
@ -101,4 +107,13 @@ def get_correct_answers(dataset:list):
|
|||||||
if block['type'] == 'block':
|
if block['type'] == 'block':
|
||||||
for question in block['questions']:
|
for question in block['questions']:
|
||||||
output[str(question['q_no'])] = question['options'][question['correct']]
|
output[str(question['q_no'])] = question['options'][question['correct']]
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
def redirect_if_started(function):
|
||||||
|
@wraps(function)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
id = session.get('id')
|
||||||
|
if request.method == 'GET' and id and Entry.query.filter_by(id=id).first():
|
||||||
|
return redirect(url_for('quiz._quiz'))
|
||||||
|
return function(*args, **kwargs)
|
||||||
|
return wrapper
|
||||||
|
Loading…
Reference in New Issue
Block a user