[MERGE][IMP] survey: apply small tweaks on survey views (certification)

Purpose
======

This merge implies several small tweaks on the survey views, mainly to add support for the new certification fields.

Show the quizz_passed field in the answers views
-------------------------------------------------------------------

The quizz_passed field is now more important because it determines whether
a certification has been successfully taken or not.

The quizz_passed field is added in:
- The search view of user_input
- The list view of user_input
- The form view of user_input (along with the score)

Fix survey statistics computation
-------------------------------------------

Fix the shared dict in the survey statistics computation that
causes surveys to incorrectly share the same stats when computed in batch.

Add a stat button on survey to show the certified count
------------------------------------------------------------------------

Add a stat button on the survey form view showing the amount
of answers with the quizz_passed field set to True.
It allows to quickly see people that have been granted the certification.

On click, the stat button redirects to the answers list view with the "Quizz passed"
filter defaulted to True.

Task-1936539

closes odoo/odoo#31467

Signed-off-by: Jérome Maes (jem) <jem@openerp.com>
This commit is contained in:
Robot Odoo
2019-03-11 13:07:38 +01:00
committed by GitHub
8 changed files with 65 additions and 13 deletions
+10 -2
View File
@@ -9,11 +9,19 @@
<div style="margin: 0px; padding: 0px; font-size: 13px;">
<p style="margin: 0px; padding: 0px; font-size: 13px;">
Dear ${object.partner_id.name or 'participant'}<br/><br/>
We are conducting a survey and your response would be appreciated.
% if object.survey_id.certificate:
You have been invited to take a new certification.
% else:
We are conducting a survey and your response would be appreciated.
% endif
<div style="margin: 16px 0px 16px 0px;">
<a href="${('%s?answer_token=%s' % (object.survey_id.public_url, object.token)) | safe}"
style="background-color: #875A7B; padding: 8px 16px 8px 16px; text-decoration: none; color: #fff; border-radius: 5px; font-size:13px;">
Start Survey
% if object.survey_id.certificate:
Start Certification
% else:
Start Survey
% endif
</a>
</div>
% if object.deadline:
+29 -2
View File
@@ -70,6 +70,7 @@ class Survey(models.Model):
invite_count = fields.Integer("Invite", compute="_compute_survey_statistic")
answer_count = fields.Integer("Started", compute="_compute_survey_statistic")
answer_done_count = fields.Integer("Completed", compute="_compute_survey_statistic")
certified_count = fields.Integer("Certified", compute="_compute_certified_count")
# scoring and certification fields
scoring_type = fields.Selection([
@@ -104,7 +105,7 @@ class Survey(models.Model):
@api.depends('user_input_ids.state', 'user_input_ids.test_entry')
def _compute_survey_statistic(self):
stat = dict.fromkeys(self.ids, {'invite_count': 0, 'answer_count': 0, 'answer_done_count': 0})
stat = dict((cid, dict(invite_count=0, answer_count=0, answer_done_count=0)) for cid in self.ids)
UserInput = self.env['survey.user_input']
base_domain = ['&', ('survey_id', 'in', self.ids), ('test_entry', '!=', True)]
@@ -119,6 +120,22 @@ class Survey(models.Model):
for survey in self:
survey.update(stat[survey.id])
@api.depends('certificate', 'user_input_ids.quizz_passed', 'user_input_ids.test_entry')
def _compute_certified_count(self):
stat = dict((cid, 0) for cid in self.ids)
certificate_surveys = self.filtered(lambda survey: survey.certificate)
if certificate_surveys:
read_group_res = self.env['survey.user_input'].read_group(
[('survey_id', 'in', certificate_surveys.ids), ('test_entry', '!=', True), ('quizz_passed', '=', True)],
[],
['survey_id']
)
for item in read_group_res:
stat[item['survey_id'][0]] += item['survey_id_count']
for survey in self:
survey.certified_count = stat.get(survey.id, 0)
def _compute_survey_url(self):
""" Computes a public URL for the survey """
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
@@ -434,7 +451,7 @@ class Survey(models.Model):
def action_send_survey(self):
""" Open a window to compose an email, pre-filled with the survey message """
# Ensure that this survey has at least one page with at least one question.
if not self.page_ids or not [page.question_ids for page in self.page_ids if page.question_ids]:
if (not self.page_ids and self.questions_layout != 'page_per_question') or not self.question_ids:
raise UserError(_('You cannot send an invitation for a survey that has no questions.'))
if self.stage_id.closed:
@@ -503,6 +520,16 @@ class Survey(models.Model):
action['context'] = ctx
return action
@api.multi
def action_survey_user_input_certified(self):
action_rec = self.env.ref('survey.action_survey_user_input_notest')
action = action_rec.read()[0]
ctx = dict(self.env.context)
ctx.update({'search_default_survey_id': self.ids[0],
'search_default_quizz_passed': 1})
action['context'] = ctx
return action
@api.multi
def action_survey_user_input_invite(self):
action_rec = self.env.ref('survey.action_survey_user_input_notest')
+2 -1
View File
@@ -32,6 +32,7 @@ class SurveyUserInput(models.Model):
# description
survey_id = fields.Many2one('survey.survey', string='Survey', required=True, readonly=True, ondelete='cascade')
scoring_type = fields.Selection(string="Scoring", related="survey_id.scoring_type")
start_datetime = fields.Datetime('Start date and time', readonly=True)
is_time_limit_reached = fields.Boolean("Is time limit reached?", compute='_compute_is_time_limit_reached')
input_type = fields.Selection([
@@ -58,7 +59,7 @@ class SurveyUserInput(models.Model):
question_ids = fields.Many2many('survey.question', string='Predefined Questions', readonly=True)
deadline = fields.Datetime('Deadline', help="Datetime until customer can open the survey and submit answers")
quizz_score = fields.Float("Score for the quiz (%)", compute="_compute_quizz_score", default=0.0)
quizz_score = fields.Float("Score (%)", compute="_compute_quizz_score")
# Stored for performance reasons while displaying results page
quizz_passed = fields.Boolean('Quizz Passed', compute='_compute_quizz_passed', store=True, compute_sudo=True)
@@ -9,8 +9,8 @@ odoo.define('survey.tour_test_certification_failure', function (require) {
var tour = require('web_tour.tour');
var failSteps = [{ // Page-1
content: "Clicking on Start Survey",
trigger: 'a.btn.btn-primary.btn-lg:contains("Start Survey")',
content: "Clicking on Start Certification",
trigger: 'a.btn.btn-primary.btn-lg:contains("Start Certification")',
}, { // Question: Do we sell Acoustic Bloc Screens?
content: "Selecting answer 'No'",
trigger: 'div.js_question-wrapper:contains("Do we sell Acoustic Bloc Screens") select',
@@ -8,8 +8,8 @@ tour.register('test_certification_success', {
url: '/survey/start/4ead4bc8-b8f2-4760-a682-1fde8ddb95ac'
},
[{ // Page-1
content: "Clicking on Start Survey",
trigger: 'a.btn.btn-primary.btn-lg:contains("Start Survey")',
content: "Clicking on Start Certification",
trigger: 'a.btn.btn-primary.btn-lg:contains("Start Certification")',
}, { // Question: Do we sell Acoustic Bloc Screens?
content: "Selecting answer 'Yes'",
trigger: 'div.js_question-wrapper:contains("Do we sell Acoustic Bloc Screens") select',
@@ -24,6 +24,13 @@
icon="fa-envelope-o">
<field string="Invitations" name="invite_count" widget="statinfo"/>
</button>
<button name="action_survey_user_input_certified"
type="object"
class="oe_stat_button"
attrs="{'invisible': [('certificate', '=', False)]}"
icon="fa-trophy">
<field string="Certified" name="certified_count" widget="statinfo"/>
</button>
<button name="action_survey_user_input_completed"
type="object"
class="oe_stat_button"
+6 -1
View File
@@ -178,7 +178,12 @@
</p>
</div>
<a role="button" class="btn btn-primary btn-lg" t-att-href="'/survey/fill/%s/%s' % (survey.access_token, answer.token)">
Start Survey
<t t-if="survey.certificate">
Start Certification
</t>
<t t-else="">
Start Survey
</t>
</a>
</div>
</div>
+7 -3
View File
@@ -16,6 +16,7 @@
<separator/>
<filter string="Invite" name="invite" domain="[('input_type', '=', 'link')]"/>
<separator/>
<filter string="Quizz passed" name="quizz_passed" domain="[('quizz_passed','=', True)]"/>
<filter string="Test Entries" name="test" domain="[('test_entry','=', True)]"/>
<group expand="0" string="Group By">
<filter name="group_by_survey" string="Survey" domain="[]" context="{'group_by': 'survey_id'}"/>
@@ -50,7 +51,9 @@
<field name="partner_id"/>
<field name="email" widget="email"/>
<field name="test_entry" groups="base.group_no_one"/>
<field name="quizz_score" groups="base.group_no_one"/>
<field name="scoring_type" invisible="1"/>
<field name="quizz_score" attrs="{'invisible': [('scoring_type', '=', 'no_scoring')]}"/>
<field name="quizz_passed" attrs="{'invisible': [('scoring_type', '=', 'no_scoring')]}"/>
</group>
</group>
<field name="user_input_line_ids" mode="tree" attrs="{'readonly': False}">
@@ -76,10 +79,11 @@
<field name="deadline"/>
<field name="partner_id"/>
<field name="email"/>
<field name="input_type"/>
<field name="input_type" groups="base.group_no_one"/>
<field name="state"/>
<field name="test_entry" invisible="True"/>
<field name="quizz_score" groups="base.group_no_one"/>
<field name="quizz_passed"/>
<field name="quizz_score"/>
</tree>
</field>
</record>