ska-referee-test/ref-test/quiz/views.py
viveksantayana 1f4848cc83 Finished client result API.
Need to work on adjustment user codes and server email notifications.
2021-11-30 18:06:24 +00:00

157 lines
5.7 KiB
Python

from flask import Blueprint, render_template, request, redirect, jsonify, session, abort, flash
from flask.helpers import url_for
from datetime import datetime, timedelta
from uuid import uuid4
import os
from json import loads
from pymongo.collection import ReturnDocument
from main import app, db
from common.security import encrypt
from common.data_tools import generate_questions, evaluate_answers
from common.security.database import decrypt_find_one
views = Blueprint(
'quiz_views',
__name__,
static_url_path='',
template_folder='templates',
static_folder='static'
)
@views.route('/')
@views.route('/home/')
def home():
_id = session.get('_id')
if _id and db.entries.find_one({'_id': _id}):
return redirect(url_for('quiz_views.start_quiz'))
return render_template('/quiz/index.html')
@views.route('/start/', methods = ['GET', 'POST'])
def start():
from .forms import StartQuiz
form = StartQuiz()
if request.method == 'GET':
_id = session.get('_id')
if _id and db.entries.find_one({'_id': _id}):
return redirect(url_for('quiz_views.start_quiz'))
return render_template('/quiz/start-quiz.html', form=form)
if request.method == 'POST':
if form.validate_on_submit():
name = {
'first_name': request.form.get('first_name'),
'surname': request.form.get('surname')
}
email = request.form.get('email')
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
if not db.tests.find_one({'test_code': test_code}):
return jsonify({'error': 'The exam code you entered is invalid.'}), 400
entry = {
'_id': uuid4().hex,
'name': encrypt(name),
'email': encrypt(email),
'club': encrypt(club),
'test_code': test_code,
'user_code': user_code
}
if db.entries.insert(entry):
session['_id'] = entry['_id']
return jsonify({
'success': 'Received and validated test and/or user code. Redirecting to test client.',
'_id': entry['_id']
}), 200
else:
errors = [*form.errors]
return jsonify({ 'error': errors}), 400
@views.route('/api/questions/', methods=['POST'])
def fetch_questions():
_id = request.get_json()['_id']
entry = db.entries.find_one({'_id': _id})
if not entry:
return abort(404)
test_code = entry['test_code']
# user_code = entry['user_code'] TODO Implement functionality for adjustments
test = db.tests.find_one({'test_code' : test_code})
time_limit = test['time_limit']
if time_limit:
_time_limit = int(time_limit)
end_delta = timedelta(minutes=_time_limit)
end_time = datetime.utcnow() + end_delta
else:
end_time = None
update = {
'start_time': datetime.utcnow(),
'status': 'started',
'end_time': end_time
}
entry = db.entries.find_one_and_update({'_id': _id}, {'$set': update}, upsert=False, return_document=ReturnDocument.AFTER)
dataset = test['dataset']
dataset_path = os.path.join(app.config['DATA_FILE_DIRECTORY'], dataset)
with open(dataset_path, 'r') as data_file:
data = loads(data_file.read())
questions = generate_questions(data)
return jsonify({
'time_limit': end_time,
'questions': questions,
'start_time': entry['start_time']
})
@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')
@views.route('/api/submit/', methods=['POST'])
def submit_quiz():
_id = request.get_json()['_id']
answers = request.get_json()['answers']
entry = db.entries.find_one({'_id': _id})
if not entry:
return jsonify('Unrecognised ID', 'error'), 400
status = 'submitted'
if entry['end_time']:
if datetime.utcnow() > entry['end_time'] + timedelta(minutes=2):
status = 'late'
test_code = entry['test_code']
test = db.tests.find_one({'test_code' : test_code})
dataset = test['dataset']
dataset_path = os.path.join(app.config['DATA_FILE_DIRECTORY'], dataset)
with open(dataset_path, 'r') as _dataset:
data = loads(_dataset.read())
results = evaluate_answers(data, answers)
entry = db.entries.find_one_and_update({'_id': _id}, {'$set': {
'status': status,
'submission_time': datetime.utcnow(),
'results': results,
'answers': answers
}})
return jsonify({
'success': 'Your submission has been processed. Redirecting you to receive your results.',
'_id': _id
}), 200
@views.route('/result/')
def result():
_id = session.get('_id')
entry = decrypt_find_one(db.entries, {'_id': _id})
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 = sorted(tags_low.items(), key=lambda x: x[1], reverse=True)
show_low_tags = sorted_low[0:3]
return render_template('/quiz/result.html', entry=entry, score=score, show_low_tags=show_low_tags)
@views.route('/privacy/')
def privacy():
return render_template('/quiz/privacy.html')