From 75fc33048fb0ab71865375fb7eaf979babd4069f Mon Sep 17 00:00:00 2001 From: evgfilim1 Date: Wed, 29 Aug 2018 12:13:54 +0500 Subject: [PATCH] Prevent sending answers when the test timed out for the user --- endpoints/admin.py | 9 +++++++-- endpoints/api.py | 12 ++++++++---- main.py | 20 ++++++-------------- static/js/test.js | 18 ++++++++++++++++-- utils.py | 30 +++++++++++++++++++++++++----- 5 files changed, 62 insertions(+), 27 deletions(-) diff --git a/endpoints/admin.py b/endpoints/admin.py index 5b25fcb..9500596 100644 --- a/endpoints/admin.py +++ b/endpoints/admin.py @@ -3,12 +3,17 @@ from secrets import randbelow from forms import AddQuestionForm, AddAdminForm, AddUsersForm, ImportQuestionsForm from models import db, User, UserAnswer, Question, Answer -from utils import admin_required, find_user, back, json_response +from utils import admin_required, find_user, back, json_response, remaining_time, finish_test @admin_required def admin_panel(): - user_list = sorted(User.query.all(), key=lambda x: x.points or 0, reverse=True) + users = User.query.all() + for user in users: + if remaining_time(user) <= 0: + finish_test(user) + db.session.commit() + user_list = sorted(users, key=lambda x: x.points or 0, reverse=True) return render_template('admin.html', users=user_list, title='Панель администратора') diff --git a/endpoints/api.py b/endpoints/api.py index 67e615d..f740b2e 100644 --- a/endpoints/api.py +++ b/endpoints/api.py @@ -6,11 +6,15 @@ def save_answer(): answer_id = request.form.get('a') user_id = request.form.get('u', None) - success = _save_answer(answer_id, user_id) - result = {'ok': success} + result = _save_answer(answer_id, user_id) + success = False + if result is True: + success = True + response = {'ok': success} if not success: - return json_response(result, 400) - return json_response(result) + response['error'], response['error_code'] = result + return json_response(response, 400) + return json_response(response) def change_question_data(): diff --git a/main.py b/main.py index df74406..c17253b 100644 --- a/main.py +++ b/main.py @@ -1,12 +1,13 @@ from flask import render_template, redirect, url_for, session, g, request, flash from markdown import markdown -from datetime import datetime, timedelta +from datetime import datetime from random import shuffle from app import app from endpoints import admin, api from forms import LoginForm from models import Question, db, UserAnswer -from utils import find_user, get_user, login_required, back, is_browser_supported +from utils import (find_user, get_user, login_required, back, is_browser_supported, remaining_time, + finish_test) @app.before_request @@ -61,10 +62,8 @@ def solve(): if g.user.start_time is None: g.user.start_time = datetime.utcnow() db.session.commit() - max_end_time = g.user.start_time + timedelta(seconds=app.config.get('TIME_TO_SOLVE', 3600)) - remaining = max_end_time - datetime.utcnow() - return render_template('test.html', - remaining=int(remaining.total_seconds()), answers=None, shuffle=shuffle, + remaining = remaining_time(g.user) + return render_template('test.html', remaining=int(remaining), answers=None, shuffle=shuffle, questions=Question.query.order_by(Question.number).all()) @@ -84,16 +83,9 @@ def result(user_id=None): return redirect(back('admin_panel')) if request.method == 'POST': flash('Тест завершён!', category='success') - if user.end_time is None: - user.end_time = datetime.utcnow() - user.points = 0 - for a in g.user.answers: - if a.is_correct: - g.user.points += a.question.points - db.session.commit() + finish_test(user) return redirect(url_for('result')) if user.end_time is None: - # testctf_flag2_commit_diff if user_id is not None: flash('Тест ещё не был завершён!') return redirect(back('admin_panel')) diff --git a/static/js/test.js b/static/js/test.js index 3bfaf6c..8b89b7c 100644 --- a/static/js/test.js +++ b/static/js/test.js @@ -14,8 +14,22 @@ function uploadAnswer(answer_id, user_id, question_id) { $(`#q${question_id}-a${answer_id}`).addClass('answered'); localStorage.removeItem(`u${user_id}-q${question_id}`); }, - error: function () { - console.warn(`Failed to upload answer (id=${answer_id})`); + error: function (jqxhr) { + if (jqxhr.status === 400 && jqxhr.responseJSON !== undefined) { + const r = jqxhr.responseJSON; + if (r.ok === false && 1 <= r.error_code && r.error_code <= 3) { + console.warn(`Failed to upload answer (id=${answer_id}): ${r.error}`); + if (r.error_code === 1) { + location.reload(true); + } else if (r.error_code === 3) { + $('#finish-test').submit(); + } + return; + } + } + console.warn( + `Failed to upload answer (id=${answer_id}): ${jqxhr.status} ${jqxhr.statusText}` + ); localStorage.setItem(`u${user_id}-q${question_id}`, answer_id); } }); diff --git a/utils.py b/utils.py index f819bcc..e95c2b0 100644 --- a/utils.py +++ b/utils.py @@ -1,7 +1,8 @@ from flask import g, Response, redirect, url_for, flash, request from json import dumps from functools import wraps -from datetime import datetime +from datetime import datetime, timedelta +from app import app from models import User, Answer, UserAnswer, db @@ -19,19 +20,38 @@ def is_browser_supported(): return ua.browser in ('chrome', 'firefox') and v >= (60, 0) +def remaining_time(user): + time_to_solve = app.config.get('TIME_TO_SOLVE', 3600) + if user.start_time is None: + return time_to_solve + max_end_time = user.start_time + timedelta(seconds=time_to_solve) + return (max_end_time - datetime.utcnow()).total_seconds() + + +def finish_test(user): + if user.end_time is None: + user.end_time = datetime.utcnow() + user.points = 0 + for a in user.answers: + if a.is_correct: + user.points += a.question.points + db.session.commit() + + def save_answer(answer_id, user_id=None): if user_id is None: user = g.user else: user = get_user(user_id) if answer_id is None or user is None: - return False + return 'Not found', 2 if user.end_time is not None: - return True # just ignore + return 'Test finished', 1 + if remaining_time(user) <= 0: + return 'Test timed out', 3 answer = Answer.query.get(answer_id) if answer is None: - # TODO: log this, maybe it was right - return False + return 'Not found', 2 user_answer = UserAnswer.query.filter_by(user_id=user.id, question_id=answer.question_id).first() if user_answer is not None: