from ..forms.quiz import StartQuiz from ..models import Entry, Test from ..tools.forms import send_errors_to_client from ..tools.logs import write from ..tools.test import redirect_if_started from flask import Blueprint, jsonify, render_template, request, session from flask.helpers import abort, flash, redirect, url_for from sqlalchemy.exc import SQLAlchemyError from datetime import datetime quiz = Blueprint( name='quiz', import_name=__name__, template_folder='templates', static_folder='static', static_url_path='/quiz/static' ) @quiz.route('/') @quiz.route('/home/') @redirect_if_started def _home(): return render_template('/quiz/index.html') @quiz.route('/instructions/') def _instructions(): return render_template('/quiz/instructions.html') @quiz.route('/start/', methods=['GET', 'POST']) def _start(): 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')) entry.set_email(request.form.get('email')) code = request.form.get('test_code').replace('—', '').lower() try: test = Test.query.filter_by(code=code).first() except (SQLAlchemyError, ConnectionError) as exception: write('system.log', f'Database error when processing request \'{request.url}\': {exception}') return abort(500) 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.ready() 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 return send_errors_to_client(form=form) return render_template('/quiz/start_quiz.html', form = form) @quiz.route('/quiz/') def _quiz(): id = session.get('id') try: 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', None) return redirect(url_for('quiz._start')) except (SQLAlchemyError, ConnectionError) as exception: write('system.log', f'Database error when processing request \'{request.url}\': {exception}') return abort(500) return render_template('/quiz/client.html') @quiz.route('/result/') def _result(): id = session.get('id') try: entry = Entry.query.filter_by(id=id).first() except (SQLAlchemyError, ConnectionError) as exception: write('system.log', f'Database error when processing request \'{request.url}\': {exception}') return abort(500) if not entry: return abort(404) session.pop('id',None) score = round(100*entry.result['score']/entry.result['max']) tags_low = { tag: tag_result['max'] - tag_result['scored'] for tag, tag_result in entry.result['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] if not entry.status == 'late': entry.notify_result() return render_template('/quiz/result.html', entry=entry, score=score, tag_output=tag_output)