137 lines
7.3 KiB
Python
137 lines
7.3 KiB
Python
|
# -*- coding: utf-8 -*-
|
||
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
|
|
||
|
from odoo.addons.survey.tests.common import TestSurveyCommon
|
||
|
|
||
|
|
||
|
class TestCourseCertificationFailureFlow(TestSurveyCommon):
|
||
|
def test_course_certification_failure_flow(self):
|
||
|
# Step 1: create a simple certification
|
||
|
# --------------------------------------------------
|
||
|
with self.with_user('survey_user'):
|
||
|
certification = self.env['survey.survey'].create({
|
||
|
'title': 'Small course certification',
|
||
|
'access_mode': 'public',
|
||
|
'users_login_required': True,
|
||
|
'scoring_type': 'scoring_with_answers',
|
||
|
'certification': True,
|
||
|
'is_attempts_limited': True,
|
||
|
'scoring_success_min': 100.0,
|
||
|
'attempts_limit': 2,
|
||
|
})
|
||
|
|
||
|
self._add_question(
|
||
|
None, 'Question 1', 'simple_choice',
|
||
|
sequence=1,
|
||
|
survey_id=certification.id,
|
||
|
labels=[
|
||
|
{'value': 'Wrong answer'},
|
||
|
{'value': 'Correct answer', 'is_correct': True, 'answer_score': 1.0}
|
||
|
])
|
||
|
|
||
|
self._add_question(
|
||
|
None, 'Question 2', 'simple_choice',
|
||
|
sequence=2,
|
||
|
survey_id=certification.id,
|
||
|
labels=[
|
||
|
{'value': 'Wrong answer'},
|
||
|
{'value': 'Correct answer', 'is_correct': True, 'answer_score': 1.0}
|
||
|
])
|
||
|
|
||
|
# Step 1.1: create a simple channel
|
||
|
self.channel = self.env['slide.channel'].sudo().create({
|
||
|
'name': 'Test Channel',
|
||
|
'channel_type': 'training',
|
||
|
'enroll': 'public',
|
||
|
'visibility': 'public',
|
||
|
'is_published': True,
|
||
|
})
|
||
|
|
||
|
# Step 2: link the certification to a slide of category 'certification'
|
||
|
self.slide_certification = self.env['slide.slide'].sudo().create({
|
||
|
'name': 'Certification slide',
|
||
|
'channel_id': self.channel.id,
|
||
|
'slide_category': 'certification',
|
||
|
'survey_id': certification.id,
|
||
|
'is_published': True,
|
||
|
})
|
||
|
# Step 3: add portal user as member of the channel
|
||
|
self.channel._action_add_members(self.user_portal.partner_id)
|
||
|
# forces recompute of partner_ids as we create directly in relation
|
||
|
self.channel.invalidate_model()
|
||
|
slide_partner = self.slide_certification._action_set_viewed(self.user_portal.partner_id)
|
||
|
self.slide_certification.with_user(self.user_portal)._generate_certification_url()
|
||
|
|
||
|
self.assertEqual(1, len(slide_partner.user_input_ids), 'A user input should have been automatically created upon slide view')
|
||
|
|
||
|
first_attempt_in_first_pool = slide_partner.user_input_ids[0]
|
||
|
# Step 4: fill in the created user_input with wrong answers
|
||
|
self.fill_in_answer(slide_partner.user_input_ids[0], certification.question_ids)
|
||
|
|
||
|
self.assertFalse(slide_partner.survey_scoring_success, 'Quizz should not be marked as passed with wrong answers')
|
||
|
# forces recompute of partner_ids as we delete directly in relation
|
||
|
self.channel.invalidate_model()
|
||
|
self.assertIn(self.user_portal.partner_id, self.channel.partner_ids, 'Portal user should still be a member of the course because they still have attempts left')
|
||
|
certification_urls = self.slide_certification.with_user(self.user_portal)._generate_certification_url()
|
||
|
self.assertEqual(certification_urls[self.slide_certification.id],
|
||
|
slide_partner.user_input_ids[0].get_start_url(),
|
||
|
"Make sure that the url generated is the same even if we enter again the certification without doing retry.")
|
||
|
# Step 5: simulate a 'retry'
|
||
|
retry_user_input = self.slide_certification.survey_id.sudo()._create_answer(
|
||
|
partner=self.user_portal.partner_id,
|
||
|
**{
|
||
|
'slide_id': self.slide_certification.id,
|
||
|
'slide_partner_id': slide_partner.id
|
||
|
},
|
||
|
invite_token=slide_partner.user_input_ids[0].invite_token
|
||
|
)
|
||
|
second_attempt_in_first_pool = retry_user_input
|
||
|
# Step 6: fill in the new user_input with wrong answers again
|
||
|
self.fill_in_answer(retry_user_input, certification.question_ids)
|
||
|
# forces recompute of partner_ids as we delete directly in relation
|
||
|
self.channel.invalidate_model()
|
||
|
channel_partner = self.env['slide.channel.partner'].with_context(active_test=False).search([
|
||
|
('channel_id', 'in', self.channel.ids),
|
||
|
('partner_id', 'in', slide_partner.partner_id.ids),
|
||
|
])
|
||
|
self.assertFalse(channel_partner.active, 'Portal user membership should have been archived from the course attendee because he failed his last attempt')
|
||
|
|
||
|
# Step 7: add portal user as member of the channel once again
|
||
|
self.channel._action_add_members(self.user_portal.partner_id)
|
||
|
# forces recompute of partner_ids as we create directly in relation
|
||
|
self.channel.invalidate_model()
|
||
|
|
||
|
self.slide_certification.with_user(self.user_portal)._generate_certification_url()
|
||
|
self.assertTrue(channel_partner.active, 'Portal user membership should be a unarchived upon joining the course once again')
|
||
|
self.assertEqual(1, len(slide_partner.user_input_ids), 'A new user input should have been automatically created upon slide view')
|
||
|
first_attempt_in_second_pool = slide_partner.user_input_ids[0]
|
||
|
# Step 8: fill in the created user_input with correct answers this time
|
||
|
self.fill_in_answer(slide_partner.user_input_ids, certification.question_ids, good_answers=True)
|
||
|
self.assertTrue(slide_partner.survey_scoring_success, 'Quizz should be marked as passed with correct answers')
|
||
|
# forces recompute of partner_ids as we delete directly in relation
|
||
|
self.channel.invalidate_model()
|
||
|
self.assertIn(self.user_portal.partner_id, self.channel.partner_ids, 'Portal user should still be a member of the course')
|
||
|
# Checking the attempts numbers
|
||
|
self.assertEqual(1, first_attempt_in_first_pool.attempts_number,
|
||
|
'The first attempt of the first pool should be number 1')
|
||
|
self.assertEqual(2, second_attempt_in_first_pool.attempts_number,
|
||
|
'The second attempt of the first pool should be number 2')
|
||
|
self.assertEqual(1, first_attempt_in_second_pool.attempts_number,
|
||
|
'The first attempt of the second pool should be number 1')
|
||
|
|
||
|
def fill_in_answer(self, answer, questions, good_answers=False):
|
||
|
""" Fills in the user_input with answers for all given questions.
|
||
|
You can control whether the answer will be correct or not with the 'good_answers' param.
|
||
|
(It's assumed that wrong answers are at index 0 of question.suggested_answer_ids and good answers at index 1) """
|
||
|
answer.write({
|
||
|
'state': 'done',
|
||
|
'user_input_line_ids': [
|
||
|
(0, 0, {
|
||
|
'question_id': question.id,
|
||
|
'answer_type': 'suggestion',
|
||
|
'answer_score': 1 if good_answers else 0,
|
||
|
'suggested_answer_id': question.suggested_answer_ids[1 if good_answers else 0].id
|
||
|
}) for question in questions
|
||
|
]
|
||
|
})
|