File: test_course_certification_failure.py

package info (click to toggle)
odoo 18.0.0%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 878,716 kB
  • sloc: javascript: 927,937; python: 685,670; xml: 388,524; sh: 1,033; sql: 415; makefile: 26
file content (136 lines) | stat: -rw-r--r-- 7,482 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# -*- 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
            ]
        })