[IMP] survey: save questions on time limit reached

Purpose
=======

If the timer goes to 00:00, the form is automatically submitted but the answers
are not saved. The form should be submitted, validation should filter out
invalid answers to avoid showing validation errors on the form, and save
answers.
If the answer is not in the correct format : skip the question
If the answer is required but there is no answer : skip the question

This way, we can still compute score.

Specifications
==============

This commit removes the invalid answers from the question to save, only if the
timer is not over, to be able to compute score on not finished survey.

In order to avoid being able to submit answers when the survey is marked as
done, a check was added to return an error for that use case.

Also, to avoid frontend timer hacking, an extra buffer of 10 seconds has been
added. If the time limit is reached and the submit occurs 10 seconds after the
maximum stop time (start time + time limit), no answer can be submitted.

Task ID: 2152223
PR #41453
This commit is contained in:
David Beguin
2020-01-10 11:01:04 +00:00
committed by Aurélien Warnon
parent 94fd0624af
commit 26147efeab
+20 -25
View File
@@ -5,7 +5,7 @@ import json
import logging
import werkzeug
from datetime import datetime
from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from odoo import fields, http, _
@@ -302,42 +302,37 @@ class Survey(http.Controller):
# Survey Validation
access_data = self._get_access_data(survey_token, answer_token, ensure_token=True)
if access_data['validity_code'] is not True:
return {
'error': access_data['validity_code'],
}
return {'error': access_data['validity_code']}
survey_sudo, answer_sudo = access_data['survey_sudo'], access_data['answer_sudo']
if answer_sudo.state == 'done':
return {'error': 'unauthorized'}
questions, page_or_question_id = survey_sudo._get_survey_questions(answer=answer_sudo,
page_id=post.get('page_id'),
question_id=post.get('question_id'))
if not answer_sudo.test_entry and not survey_sudo._has_attempts_left(answer_sudo.partner_id, answer_sudo.email, answer_sudo.invite_token):
# prevent cheating with users creating multiple 'user_input' before their last attempt
return {}
return {'error': 'unauthorized'}
if not answer_sudo.is_time_limit_reached:
# Prepare answers and comment by question
prepared_questions = {}
for question in questions:
answer_full = post.get(str(question.id))
answer_without_comment, comment = self._extract_comment_from_answers(question, answer_full)
prepared_questions[question.id] = {'answer': answer_without_comment, 'comment': comment}
if answer_sudo.is_time_limit_reached:
time_limit = answer_sudo.start_datetime + timedelta(minutes=survey_sudo.time_limit)
if fields.Datetime.now() > (time_limit + timedelta(seconds=10)):
# prevent cheating with users blocking the JS timer and taking all their time to answer
return {'error': 'unauthorized'}
# Questions Validation
errors = {}
for question in questions:
answer = prepared_questions[question.id]['answer']
comment = prepared_questions[question.id]['comment']
errors.update(question.validate_question(answer, comment))
if errors:
return {'error': 'validation', 'fields': errors}
# Submitting questions
for question in questions:
answer = prepared_questions[question.id]['answer']
comment = prepared_questions[question.id]['comment']
errors = {}
# Prepare answers / comment by question, validate and save answers
for question in questions:
answer, comment = self._extract_comment_from_answers(question, post.get(str(question.id)))
errors.update(question.validate_question(answer, comment))
if not errors.get(question.id):
answer_sudo.save_lines(question, answer, comment)
if errors and not answer_sudo.is_time_limit_reached:
return {'error': 'validation', 'fields': errors}
if answer_sudo.is_time_limit_reached or survey_sudo.questions_layout == 'one_page':
answer_sudo._mark_done()
elif 'previous_page_id' in post: