summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
Diffstat (limited to 'addons')
-rw-r--r--addons/survey/controllers/main.py212
-rw-r--r--addons/survey/models/survey_question.py152
-rw-r--r--addons/survey/models/survey_survey.py160
-rw-r--r--addons/survey/models/survey_user.py1
-rw-r--r--addons/survey/static/src/js/survey_result.js25
-rw-r--r--addons/survey/tests/test_survey.py50
-rw-r--r--addons/survey/views/survey_templates_management.xml7
-rw-r--r--addons/survey/views/survey_templates_statistics.xml487
8 files changed, 492 insertions, 602 deletions
diff --git a/addons/survey/controllers/main.py b/addons/survey/controllers/main.py
index b6147ca937f..af74e0f911c 100644
--- a/addons/survey/controllers/main.py
+++ b/addons/survey/controllers/main.py
@@ -7,13 +7,13 @@ import werkzeug
from datetime import datetime
from dateutil.relativedelta import relativedelta
-from math import ceil
from odoo import fields, http, _
from odoo.addons.base.models.ir_ui_view import keep_query
from odoo.exceptions import UserError
from odoo.http import request, content_disposition
-from odoo.tools import ustr, format_datetime, format_date
+from odoo.osv import expression
+from odoo.tools import format_datetime, format_date
_logger = logging.getLogger(__name__)
@@ -484,161 +484,65 @@ class Survey(http.Controller):
def survey_report(self, survey, answer_token=None, **post):
""" Display survey Results & Statistics for given survey.
- Quick retroengineering of what is injected into the template for now:
- (TODO: flatten and simplify this)
-
- survey: a browse record of the survey
- survey_dict: very messy dict containing all the info to display answers
- {'page_ids': [
-
- ...
-
- {'page': browse record of the page,
- 'question_ids': [
-
- ...
-
- {'graph_data': data to be displayed on the graph
- 'input_summary': number of answered, skipped...
- 'prepare_result': {
- answers displayed in the tables
- }
- 'question': browse record of the question_ids
- }
-
- ...
-
- ]
- }
-
- ...
-
- ]
- }
-
- page_range: pager helper function
- current_filters: a list of ids
- filter_display_data: [{'labels': ['a', 'b'], question_text} ... ]
- filter_finish: boolean => only finished surveys or not
+ New structure: {
+ 'survey': current survey browse record,
+ 'question_and_page_data': see ``SurveyQuestion._prepare_statistics()``,
+ 'survey_data'= see ``SurveySurvey._prepare_statistics()``
+ 'search_filters': [],
+ 'search_finished': either filter on finished inputs only or not,
+ }
"""
- current_filters = []
- filter_display_data = []
-
- answers = survey.user_input_ids.filtered(lambda answer: answer.state != 'new' and not answer.test_entry)
- filter_finish = post.get('finished') == 'true'
- if post or filter_finish:
- filter_data = self._get_filter_data(post)
- current_filters = survey.filter_input_ids(filter_data, filter_finish)
- filter_display_data = survey.get_filter_display_data(filter_data)
+ user_input_lines, search_filters = self._extract_filters_data(survey, post)
+ survey_data = survey._prepare_statistics(user_input_lines)
+ question_and_page_data = survey.question_and_page_ids._prepare_statistics(user_input_lines)
+
return request.render('survey.survey_page_statistics', {
+ # survey and its statistics
'survey': survey,
- 'answers': answers,
- 'survey_dict': self._prepare_result_dict(survey, current_filters),
- 'page_range': self.page_range,
- 'current_filters': current_filters,
- 'filter_display_data': filter_display_data,
- 'filter_finish': filter_finish
+ 'question_and_page_data': question_and_page_data,
+ 'survey_data': survey_data,
+ # search
+ 'search_filters': search_filters,
+ 'search_finished': post.get('finished') == 'true',
})
- def _prepare_result_dict(self, survey, current_filters=None):
- """Returns dictionary having values for rendering template"""
- current_filters = current_filters if current_filters else []
- result = {'page_ids': []}
-
- # First append questions without page
- questions_without_page = [self._prepare_question_values(question,current_filters) for question in survey.question_ids if not question.page_id]
- if questions_without_page:
- result['page_ids'].append({'page': request.env['survey.question'], 'question_ids': questions_without_page})
-
- # Then, questions in sections
- for page in survey.page_ids:
- page_dict = {'page': page, 'question_ids': [self._prepare_question_values(question,current_filters) for question in page.question_ids]}
- result['page_ids'].append(page_dict)
-
- if survey.scoring_type in ['scoring_with_answers', 'scoring_without_answers']:
- scoring_data = self._get_scoring_data(survey)
- result['success_rate'] = scoring_data['success_rate']
- result['scoring_graph_data'] = json.dumps(scoring_data['graph_data'])
-
- return result
-
- def _prepare_question_values(self, question, current_filters):
- Survey = request.env['survey.survey']
- return {
- 'question': question,
- 'input_summary': Survey.get_input_summary(question, current_filters),
- 'prepare_result': Survey.prepare_result(question, current_filters),
- 'graph_data': self._get_graph_data(question, current_filters),
- }
-
- def _get_filter_data(self, post):
- """Returns data used for filtering the result"""
- filters = []
- filters_data = post.get('filters')
- if filters_data:
- for data in filters_data.split('|'):
- try:
- row_id, answer_id = data.split(',')
- filters.append({'row_id': int(row_id), 'answer_id': int(answer_id)})
- except:
- return filters
- return filters
-
- def page_range(self, total_record, limit):
- '''Returns number of pages required for pagination'''
- total = ceil(total_record / float(limit))
- return range(1, int(total + 1))
-
- def _get_graph_data(self, question, current_filters=None):
- '''Returns formatted data required by graph library on basis of filter'''
- # TODO refactor this terrible method and merge it with _prepare_result_dict
- current_filters = current_filters if current_filters else []
- Survey = request.env['survey.survey']
- result = []
- if question.question_type == 'multiple_choice':
- result.append({'key': ustr(question.title),
- 'values': Survey.prepare_result(question, current_filters)['answers']
- })
- if question.question_type == 'simple_choice':
- result = Survey.prepare_result(question, current_filters)['answers']
- if question.question_type == 'matrix':
- data = Survey.prepare_result(question, current_filters)
- for answer in data['answers']:
- values = []
- for row in data['rows']:
- values.append({'text': data['rows'].get(row), 'count': data['result'].get((row, answer))})
- result.append({'key': data['answers'].get(answer), 'values': values})
- return json.dumps(result)
-
- def _get_scoring_data(self, survey):
- """Performs a read_group to fetch the count of failed/passed tests in a single query."""
-
- count_data = request.env['survey.user_input'].read_group(
- [('survey_id', '=', survey.id), ('state', '=', 'done'), ('test_entry', '=', False)],
- ['scoring_success', 'id:count_distinct'],
- ['scoring_success']
- )
-
- scoring_success_count = 0
- scoring_failed_count = 0
- for count_data_item in count_data:
- if count_data_item['scoring_success']:
- scoring_success_count = count_data_item['scoring_success_count']
+ def _extract_filters_data(self, survey, post):
+ search_filters = []
+ line_filter_domain, line_choices = [], []
+ for data in post.get('filters', '').split('|'):
+ try:
+ row_id, answer_id = (int(item) for item in data.split(','))
+ except:
+ pass
else:
- scoring_failed_count = count_data_item['scoring_success_count']
-
- graph_data = [{
- 'text': _('Passed'),
- 'count': scoring_success_count,
- 'color': '#2E7D32'
- }, {
- 'text': _('Missed'),
- 'count': scoring_failed_count,
- 'color': '#C62828'
- }]
-
- total_scoring_success = scoring_success_count + scoring_failed_count
- return {
- 'success_rate': round((scoring_success_count / total_scoring_success) * 100, 1) if total_scoring_success > 0 else 0,
- 'graph_data': graph_data
- }
+ if row_id and answer_id:
+ line_filter_domain = expression.AND([
+ ['&', ('matrix_row_id', '=', row_id), ('suggested_answer_id', '=', answer_id)],
+ line_filter_domain
+ ])
+ answers = request.env['survey.question.answer'].browse([row_id, answer_id])
+ elif answer_id:
+ line_choices.append(answer_id)
+ answers = request.env['survey.question.answer'].browse([answer_id])
+ if answer_id:
+ search_filters.append({
+ 'question': answers[0].question_id.title,
+ 'answers': '%s%s' % (answers[0].value, ': %s' % answers[1].value if len(answers) > 1 else '')
+ })
+ if line_choices:
+ line_filter_domain = expression.AND([[('suggested_answer_id', 'in', line_choices)], line_filter_domain])
+
+ user_input_domain = ['&', ('test_entry', '=', False), ('survey_id', '=', survey.id)]
+ if line_filter_domain:
+ matching_line_ids = request.env['survey.user_input.line'].sudo().search(line_filter_domain).ids
+ user_input_domain = expression.AND([
+ [('user_input_line_ids', 'in', matching_line_ids)],
+ user_input_domain
+ ])
+ if post.get('finished'):
+ user_input_domain = expression.AND([[('state', '=', 'done')], user_input_domain])
+ else:
+ user_input_domain = expression.AND([[('state', '!=', 'new')], user_input_domain])
+ user_input_lines = request.env['survey.user_input'].sudo().search(user_input_domain).mapped('user_input_line_ids')
+
+ return user_input_lines, search_filters
diff --git a/addons/survey/models/survey_question.py b/addons/survey/models/survey_question.py
index e5685564e8f..c0c9d6a4bef 100644
--- a/addons/survey/models/survey_question.py
+++ b/addons/survey/models/survey_question.py
@@ -1,6 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import collections
+import json
+import itertools
+import operator
+
from odoo import api, fields, models, tools, _
from odoo.exceptions import ValidationError
@@ -92,7 +97,7 @@ class SurveyQuestion(models.Model):
display_mode = fields.Selection(
[('columns', 'Radio Buttons'), ('dropdown', 'Selection Box')],
string='Display Mode', default='columns', help='Display mode of simple choice questions.')
- # -- comments
+ # -- comments (simple choice, multiple choice, matrix (without count as an answer))
comments_allowed = fields.Boolean('Show Comments Field')
comments_message = fields.Char('Comment Message', translate=True, default=lambda self: _("If other, please specify:"))
comment_count_as_answer = fields.Boolean('Comment Field is an Answer Choice')
@@ -273,10 +278,149 @@ class SurveyQuestion(models.Model):
self.ensure_one()
return list(self.survey_id.question_and_page_ids).index(self)
- def get_correct_answer_ids(self):
- self.ensure_one()
+ # ------------------------------------------------------------
+ # STATISTICS / REPORTING
+ # ------------------------------------------------------------
+
+ def _prepare_statistics(self, user_input_lines):
+ """ Compute statistical data for questions by counting number of vote per choice on basis of filter """
+ all_questions_data = []
+ for question in self:
+ question_data = {'question': question, 'is_page': question.is_page}
+
+ if question.is_page:
+ all_questions_data.append(question_data)
+ continue
+
+ # fetch answer lines, separate comments from real answers
+ all_lines = user_input_lines.filtered(lambda line: line.question_id == question)
+ if question.question_type in ['simple_choice', 'multiple_choice', 'matrix']:
+ answer_lines = all_lines.filtered(
+ lambda line: line.answer_type == 'suggestion' or (
+ line.answer_type == 'char_box' and question.comment_count_as_answer)
+ )
+ comment_line_ids = all_lines.filtered(lambda line: line.answer_type == 'char_box')
+ else:
+ answer_lines = all_lines
+ comment_line_ids = self.env['survey.user_input.line']
+ skipped_lines = answer_lines.filtered(lambda line: line.skipped)
+ done_lines = answer_lines - skipped_lines
+ question_data.update(
+ answer_line_ids=answer_lines,
+ answer_line_done_ids=done_lines,
+ answer_input_done_ids=done_lines.mapped('user_input_id'),
+ answer_input_skipped_ids=skipped_lines.mapped('user_input_id'),
+ comment_line_ids=comment_line_ids)
+ question_data.update(question._get_stats_summary_data(answer_lines))
+
+ # prepare table and graph data
+ table_data, graph_data = question._get_stats_data(answer_lines)
+ question_data['table_data'] = table_data
+ question_data['graph_data'] = json.dumps(graph_data)
+
+ all_questions_data.append(question_data)
+ return all_questions_data
+
+ def _get_stats_data(self, user_input_lines):
+ if self.question_type in ['simple_choice']:
+ return self._get_stats_data_answers(user_input_lines)
+ elif self.question_type in ['multiple_choice']:
+ table_data, graph_data = self._get_stats_data_answers(user_input_lines)
+ return table_data, [{'key': self.title, 'values': graph_data}]
+ elif self.question_type in ['matrix']:
+ return self._get_stats_graph_data_matrix(user_input_lines)
+ return [line for line in user_input_lines], []
+
+ def _get_stats_data_answers(self, user_input_lines):
+ """ Statistics for question.answer based questions (simple choice, multiple
+ choice.). A corner case with a void record survey.question.answer is added
+ to count comments that should be considered as valid answers. This small hack
+ allow to have everything available in the same standard structure. """
+ suggested_answers = [answer for answer in self.mapped('suggested_answer_ids')]
+ if self.comment_count_as_answer:
+ suggested_answers += [self.env['survey.question.answer']]
+
+ count_data = dict.fromkeys(suggested_answers, 0)
+ for line in user_input_lines:
+ if line.suggested_answer_id or (line.value_char_box and self.comment_count_as_answer):
+ count_data[line.suggested_answer_id] += 1
+
+ table_data = [{
+ 'value': _('Other (see comments)') if not sug_answer else sug_answer.value,
+ 'suggested_answer': sug_answer,
+ 'count': count_data[sug_answer]
+ }
+ for sug_answer in suggested_answers]
+ graph_data = [{
+ 'text': _('Other (see comments)') if not sug_answer else sug_answer.value,
+ 'count': count_data[sug_answer]
+ }
+ for sug_answer in suggested_answers]
+
+ return table_data, graph_data
+
+ def _get_stats_graph_data_matrix(self, user_input_lines):
+ suggested_answers = self.mapped('suggested_answer_ids')
+ matrix_rows = self.mapped('matrix_row_ids')
+
+ count_data = dict.fromkeys(itertools.product(matrix_rows, suggested_answers), 0)
+ for line in user_input_lines:
+ if line.matrix_row_id and line.suggested_answer_id:
+ count_data[(line.matrix_row_id, line.suggested_answer_id)] += 1
+
+ table_data = [{
+ 'row': row,
+ 'columns': [{
+ 'suggested_answer': sug_answer,
+ 'count': count_data[(row, sug_answer)]
+ } for sug_answer in suggested_answers],
+ } for row in matrix_rows]
+ graph_data = [{
+ 'key': sug_answer.value,
+ 'values': [{
+ 'text': row.value,
+ 'count': count_data[(row, sug_answer)]
+ }
+ for row in matrix_rows
+ ]
+ } for sug_answer in suggested_answers]
+
+ return table_data, graph_data
+
+ def _get_stats_summary_data(self, user_input_lines):
+ if self.question_type in ['simple_choice', 'multiple_choice']:
+ return self._get_stats_summary_data_choice(user_input_lines)
+ if self.question_type in ['numerical_box']:
+ return self._get_stats_summary_data_numerical(user_input_lines)
+ return {}
- return self.suggested_answer_ids.filtered(lambda label: label.is_correct)
+ def _get_stats_summary_data_choice(self, user_input_lines):
+ right_inputs, partial_inputs = self.env['survey.user_input'], self.env['survey.user_input']
+ right_answers = self.suggested_answer_ids.filtered(lambda label: label.is_correct)
+ if self.question_type == 'multiple_choice':
+ for user_input, lines in tools.groupby(user_input_lines, operator.itemgetter('user_input_id')):
+ user_input_answers = self.env['survey.user_input.line'].concat(*lines).filtered(lambda l: l.answer_is_correct).mapped('suggested_answer_id')
+ if user_input_answers and user_input_answers < right_answers:
+ partial_inputs += user_input
+ elif user_input_answers:
+ right_inputs += user_input
+ else:
+ right_inputs = user_input_lines.filtered(lambda line: line.answer_is_correct).mapped('user_input_id')
+ return {
+ 'right_answers': right_answers,
+ 'right_inputs': right_inputs,
+ 'partial_inputs': partial_inputs,
+ }
+
+ def _get_stats_summary_data_numerical(self, user_input_lines):
+ all_values = user_input_lines.filtered(lambda line: not line.skipped).mapped('value_numerical_box')
+ lines_sum = sum(all_values)
+ return {
+ 'numerical_max': max(all_values, default=0),
+ 'numerical_min': min(all_values, default=0),
+ 'numerical_average': round(lines_sum / len(all_values) or 1, 2),
+ 'numerical_common_lines': collections.Counter(all_values).most_common(5),
+ }
class SurveyQuestionAnswer(models.Model):
diff --git a/addons/survey/models/survey_survey.py b/addons/survey/models/survey_survey.py
index ac33d8a6d50..a321b1853ba 100644
--- a/addons/survey/models/survey_survey.py
+++ b/addons/survey/models/survey_survey.py
@@ -1,13 +1,11 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+import json
import random
import uuid
import werkzeug
-from collections import Counter, OrderedDict
-from itertools import product
-
from odoo import api, exceptions, fields, models, _
from odoo.osv import expression
@@ -554,129 +552,43 @@ class Survey(models.Model):
# GRAPH / RESULTS
# ------------------------------------------------------------
- def filter_input_ids(self, filters, finished=False):
- """If user applies any filters, then this function returns list of
- filtered user_input_id and label's strings for display data in web.
- :param filters: list of dictionary (having: row_id, ansewr_id)
- :param finished: True for completely filled survey,Falser otherwise.
- :returns list of filtered user_input_ids.
- """
- self.ensure_one()
- if filters:
- domain_filter, choice = [], []
- for current_filter in filters:
- row_id, answer_id = current_filter['row_id'], current_filter['answer_id']
- if row_id == 0:
- choice.append(answer_id)
- else:
- domain_filter.extend(['|', ('matrix_row_id.id', '=', row_id), ('suggested_answer_id.id', '=', answer_id)])
- if choice:
- domain_filter.insert(0, ('suggested_answer_id.id', 'in', choice))
- else:
- domain_filter = domain_filter[1:]
- input_lines = self.env['survey.user_input.line'].search(domain_filter)
- filtered_input_ids = [input_line.user_input_id.id for input_line in input_lines]
+ def _prepare_statistics(self, user_input_lines=None):
+ if user_input_lines:
+ user_input_domain = [
+ ('survey_id', 'in', self.ids),
+ ('id', 'in', user_input_lines.mapped('user_input_id').ids)
+ ]
else:
- filtered_input_ids = []
- if finished:
- UserInput = self.env['survey.user_input']
- if not filtered_input_ids:
- user_inputs = UserInput.search([('survey_id', '=', self.id)])
+ user_input_domain = [
+ ('survey_id', 'in', self.ids),
+ ('state', '=', 'done'),
+ ('test_entry', '=', False)
+ ]
+ count_data = self.env['survey.user_input'].sudo().read_group(user_input_domain, ['scoring_success', 'id:count_distinct'], ['scoring_success'])
+
+ scoring_success_count = 0
+ scoring_failed_count = 0
+ for count_data_item in count_data:
+ if count_data_item['scoring_success']:
+ scoring_success_count += count_data_item['scoring_success_count']
else:
- user_inputs = UserInput.browse(filtered_input_ids)
- return user_inputs.filtered(lambda input_item: input_item.state == 'done').ids
- return filtered_input_ids
-
- @api.model
- def get_filter_display_data(self, filters):
- """Returns data to display current filters
- :param filters: list of dictionary (having: row_id, answer_id)
- :returns list of dict having data to display filters.
- """
- filter_display_data = []
- if filters:
- Label = self.env['survey.question.answer']
- for current_filter in filters:
- row_id, answer_id = current_filter['row_id'], current_filter['answer_id']
- label = Label.browse(answer_id)
- question = label.question_id
- if row_id == 0:
- labels = label
- else:
- labels = Label.browse([row_id, answer_id])
- filter_display_data.append({'question_text': question.title,
- 'labels': labels.mapped('value')})
- return filter_display_data
-
- @api.model
- def prepare_result(self, question, current_filters=None):
- """ Compute statistical data for questions by counting number of vote per choice on basis of filter """
- current_filters = current_filters if current_filters else []
- result_summary = {}
- input_lines = question.user_input_line_ids.filtered(lambda line: not line.user_input_id.test_entry)
-
- # Calculate and return statistics for choice
- if question.question_type in ['simple_choice', 'multiple_choice']:
- comments = []
- answers = OrderedDict((label.id, {'text': label.value, 'count': 0, 'answer_id': label.id, 'answer_score': label.answer_score}) for label in question.suggested_answer_ids)
- for input_line in input_lines:
- if input_line.answer_type == 'suggestion' and answers.get(input_line.suggested_answer_id.id) and (not(current_filters) or input_line.user_input_id.id in current_filters):
- answers[input_line.suggested_answer_id.id]['count'] += 1
- if input_line.answer_type == 'char_box' and (not(current_filters) or input_line.user_input_id.id in current_filters):
- comments.append(input_line)
- result_summary = {'answers': list(answers.values()), 'comments': comments}
-
- # Calculate and return statistics for matrix
- if question.question_type == 'matrix':
- rows = OrderedDict()
- answers = OrderedDict()
- res = dict()
- comments = []
- [rows.update({label.id: label.value}) for label in question.matrix_row_ids]
- [answers.update({label.id: label.value}) for label in question.suggested_answer_ids]
- for cell in product(rows, answers):
- res[cell] = 0
- for input_line in input_lines:
- if input_line.answer_type == 'suggestion' and (not(current_filters) or input_line.user_input_id.id in current_filters) and input_line.matrix_row_id:
- res[(input_line.matrix_row_id.id, input_line.suggested_answer_id.id)] += 1
- if input_line.answer_type == 'char_box' and (not(current_filters) or input_line.user_input_id.id in current_filters):
- comments.append(input_line)
- result_summary = {'answers': answers, 'rows': rows, 'result': res, 'comments': comments}
-
- # Calculate and return statistics for text_box, char_box, date
- if question.question_type in ['text_box', 'char_box', 'date', 'datetime']:
- result_summary = []
- for input_line in input_lines:
- if not(current_filters) or input_line.user_input_id.id in current_filters:
- result_summary.append(input_line)
-
- # Calculate and return statistics for numerical_box
- if question.question_type == 'numerical_box':
- result_summary = {'input_lines': []}
- all_inputs = []
- for input_line in input_lines:
- if not(current_filters) or input_line.user_input_id.id in current_filters:
- all_inputs.append(input_line.value_numerical_box)
- result_summary['input_lines'].append(input_line)
- if all_inputs:
- result_summary.update({'average': round(sum(all_inputs) / len(all_inputs), 2),
- 'max': round(max(all_inputs), 2),
- 'min': round(min(all_inputs), 2),
- 'sum': sum(all_inputs),
- 'most_common': Counter(all_inputs).most_common(5)})
- return result_summary
-
- @api.model
- def get_input_summary(self, question, current_filters=None):
- """ Returns overall summary of question e.g. answered, skipped, total_inputs on basis of filter """
- current_filters = current_filters if current_filters else []
- result = {}
- search_line_ids = current_filters if current_filters else question.user_input_line_ids.ids
-
- result['answered'] = len([line for line in question.user_input_line_ids if line.user_input_id.state != 'new' and not line.user_input_id.test_entry and not line.skipped])
- result['skipped'] = len([line for line in question.user_input_line_ids if line.user_input_id.state != 'new' and not line.user_input_id.test_entry and line.skipped])
-
- return result
+ scoring_failed_count += count_data_item['scoring_success_count']
+
+ success_graph = json.dumps([{
+ 'text': _('Passed'),
+ 'count': scoring_success_count,
+ 'color': '#2E7D32'
+ }, {
+ 'text': _('Missed'),
+ 'count': scoring_failed_count,
+ 'color': '#C62828'
+ }])
+
+ total = scoring_success_count + scoring_failed_count
+ return {
+ 'global_success_rate': round((scoring_success_count / total) * 100, 1) if total > 0 else 0,
+ 'global_success_graph': success_graph
+ }
# ------------------------------------------------------------
# GAMIFICATION / BADGES
diff --git a/addons/survey/models/survey_user.py b/addons/survey/models/survey_user.py
index eb45c2a16e0..cea87343a89 100644
--- a/addons/survey/models/survey_user.py
+++ b/addons/survey/models/survey_user.py
@@ -342,6 +342,7 @@ class SurveyUserInputLine(models.Model):
value_text_box = fields.Text('Free Text answer')
suggested_answer_id = fields.Many2one('survey.question.answer', string="Suggested answer")
matrix_row_id = fields.Many2one('survey.question.answer', string="Row answer")
+ # scoring
answer_score = fields.Float('Score')
answer_is_correct = fields.Boolean('Correct', compute='_compute_answer_is_correct')
diff --git a/addons/survey/static/src/js/survey_result.js b/addons/survey/static/src/js/survey_result.js
index 3919015af00..dd7e1d5773d 100644
--- a/addons/survey/static/src/js/survey_result.js
+++ b/addons/survey/static/src/js/survey_result.js
@@ -14,7 +14,7 @@ var D3_COLORS = ["#1f77b4","#ff7f0e","#aec7e8","#ffbb78","#2ca02c","#98df8a","#d
// -> this is ugly / not efficient, needs to be refactored
publicWidget.registry.SurveyResultPagination = publicWidget.Widget.extend({
events: {
- 'click li a': '_onPageClick',
+ 'click li.o_survey_js_results_pagination a': '_onPageClick',
},
//--------------------------------------------------------------------------
@@ -38,7 +38,6 @@ publicWidget.registry.SurveyResultPagination = publicWidget.Widget.extend({
var self = this;
return this._super.apply(this, arguments).then(function () {
self.limit = self.$el.data("record_limit");
- self._changePage(1);
});
},
@@ -52,24 +51,9 @@ publicWidget.registry.SurveyResultPagination = publicWidget.Widget.extend({
*/
_onPageClick: function (ev) {
ev.preventDefault();
+ this.$('li.o_survey_js_results_pagination').removeClass('active');
- var pageNumber = $(ev.currentTarget).text();
- this._changePage(pageNumber);
- },
-
- // -------------------------------------------------------------------------
- // Private
- // -------------------------------------------------------------------------
-
- /**
- * Shows / hides the records based on the selected page.
- *
- * @private
- * @param {string} pageNumber
- */
- _changePage: function (pageNumber) {
- this.$('li').removeClass('active');
- var $target = this.$(_.str.sprintf('li:contains("%s")', pageNumber));
+ var $target = $(ev.currentTarget);
$target.closest('li').addClass('active');
this.$questionsEl.find('tbody tr').addClass('d-none');
@@ -82,6 +66,7 @@ publicWidget.registry.SurveyResultPagination = publicWidget.Widget.extend({
this.$questionsEl.find('tbody tr:lt('+ this.limit * num +'):gt(' + min + ')')
.removeClass('d-none');
}
+
},
});
@@ -343,7 +328,7 @@ publicWidget.registry.SurveyResultWidget = publicWidget.Widget.extend({
self.$('.pagination').each(function (){
var questionId = $(this).data("question_id");
new publicWidget.registry.SurveyResultPagination(self, {
- 'questionsEl': self.$('#table_question_'+ questionId)
+ 'questionsEl': self.$('#survey_table_question_'+ questionId)
}).attachTo($(this));
});
diff --git a/addons/survey/tests/test_survey.py b/addons/survey/tests/test_survey.py
index 23ccab5f83e..77ed53f6a69 100644
--- a/addons/survey/tests/test_survey.py
+++ b/addons/survey/tests/test_survey.py
@@ -120,53 +120,3 @@ class TestSurveyInternals(common.TestSurveyCommon):
question.validate_question('valid'),
{}
)
-
- @users('survey_manager')
- def test_result_data_simple_multiple_choice(self):
- question = self._add_question(
- self.page_0, 'Q0', 'simple_choice',
- labels=[{'value': 'Choice0'}, {'value': 'Choice1'}]
- )
- for i in range(3):
- answer = self._add_answer(self.survey, False, email='public@example.com')
- self._add_answer_line(
- question, answer, random.choice(question.suggested_answer_ids.ids),
- answer_type='suggestion', answer_fname='suggested_answer_id')
- lines = [line.suggested_answer_id.id for line in question.user_input_line_ids]
- answers = [{'text': label.value, 'count': lines.count(label.id), 'answer_id': label.id, 'answer_score': label.answer_score} for label in question.suggested_answer_ids]
- prp_result = self.env['survey.survey'].prepare_result(question)['answers']
- self.assertItemsEqual(prp_result, answers)
-
- @users('survey_manager')
- def test_result_data_matrix(self):
- question = self._add_question(
- self.page_0, 'Q0', 'matrix', matrix_subtype='simple',
- labels=[{'value': 'Column0'}, {'value': 'Column1'}],
- labels_2=[{'value': 'Row0'}, {'value': 'Row1'}]
- )
- for i in range(3):
- answer = self._add_answer(self.survey, False, email='public@example.com')
- self._add_answer_line(
- question, answer, random.choice(question.suggested_answer_ids.ids),
- answer_type='suggestion', answer_fname='suggested_answer_id', matrix_row_id=random.choice(question.matrix_row_ids.ids)
- )
- lines = [(line.matrix_row_id.id, line.suggested_answer_id.id) for line in question.user_input_line_ids]
- res = {}
- for i in product(question.matrix_row_ids.ids, question.suggested_answer_ids.ids):
- res[i] = lines.count((i))
- self.assertEqual(self.env['survey.survey'].prepare_result(question)['result'], res)
-
- @users('survey_manager')
- def test_result_data_numeric_box(self):
- question = self._add_question(self.page_0, 'Q0', 'numerical_box')
- num = [float(n) for n in random.sample(range(1, 100), 3)]
- nsum = sum(num)
- for i in range(3):
- answer = self._add_answer(self.survey, False, email='public@example.com')
- self._add_answer_line(question, answer, num[i])
- exresult = {
- 'average': round((nsum / len(num)), 2), 'max': round(max(num), 2),
- 'min': round(min(num), 2), 'sum': nsum, 'most_common': Counter(num).most_common(5)}
- result = self.env['survey.survey'].prepare_result(question)
- for key in exresult:
- self.assertEqual(result[key], exresult[key])
diff --git a/addons/survey/views/survey_templates_management.xml b/addons/survey/views/survey_templates_management.xml
index c2635e34cc4..c4684e76093 100644
--- a/addons/survey/views/survey_templates_management.xml
+++ b/addons/survey/views/survey_templates_management.xml
@@ -112,7 +112,12 @@
</div>
<div class="container o_survey_result p-4" t-if="graph_data">
<div class="tab-content">
- <div role="tabpanel" class="tab-pane active survey_graph" t-att-data-scoring_percentage="answer.scoring_percentage" t-att-id="'graph_question_%d' % answer.id" t-att-data-question_id="answer.id" data-graph-type="doughnut" t-att-data-graph-data="graph_data">
+ <div role="tabpanel" class="tab-pane active survey_graph"
+ t-att-data-scoring_percentage="answer.scoring_percentage"
+ t-att-id="'survey_graph_question_%d' % answer.id"
+ t-att-data-question_id="answer.id"
+ data-graph-type="doughnut"
+ t-att-data-graph-data="graph_data">
<canvas id="doughnut_chart"></canvas>
<span class="o_overall_performance"></span>
</div>
diff --git a/addons/survey/views/survey_templates_statistics.xml b/addons/survey/views/survey_templates_statistics.xml
index cc1f66fd782..63b0b4ecd10 100644
--- a/addons/survey/views/survey_templates_statistics.xml
+++ b/addons/survey/views/survey_templates_statistics.xml
@@ -3,111 +3,106 @@
<data>
<template id="survey_page_statistics" name="Survey: result statistics page">
<t t-call="survey.layout">
+ <t t-set="page_record_limit" t-value="10"/><!-- Change this record_limit to change number of record per page-->
<div class="oe_structure" id="oe_structure_survey_result_1"/>
<div class="container o_survey_result">
<t t-call="survey.survey_button_form_view" />
<div class="jumbotron mt32">
- <h1><span t-field="survey.title" />
- <span style="font-size:1.5em;" t-attf-class="fa fa-bar-chart-o #{'fa-bar-chart-o' if survey.scoring_type == 'no_scoring' else 'fa-trophy' if survey.certification else 'fa-question-circle-o'} float-right " role="img" aria-label="Chart" title="Chart"/></h1>
- <div t-field="survey.description" class="oe_no_empty" />
- <h2 t-if="not answers">
+ <h1>
+ <span t-field="survey.title"/>
+ <span style="font-size:1.5em;"
+ t-attf-class="fa fa-bar-chart-o #{'fa-bar-chart-o' if survey.scoring_type == 'no_scoring' else 'fa-trophy' if survey.certification else 'fa-question-circle-o'} float-right " role="img" aria-label="Chart" title="Chart"/>
+ </h1>
+ <div t-field="survey.description" class="oe_no_empty"/>
+ <h2 t-if="not question_and_page_data">
Sorry, no one answered this survey yet.
</h2>
</div>
- <div t-if="answers" class="card d-print-none">
- <div class="card-header"><span class="fa fa-filter"></span> Filters <span t-if="filter_display_data" class="float-right text-primary clear_survey_filter"><i class="fa fa-times"></i> Clear All Filters</span></div>
+ <div t-if="question_and_page_data" class="card d-print-none">
+ <div class="card-header"><span class="fa fa-filter"></span> Filters <span t-if="search_filters" class="float-right text-primary clear_survey_filter"><i class="fa fa-times"></i> Clear All Filters</span></div>
<div class="card-body">
- <span t-if="filter_finish == True">
- <span class="badge badge-secondary only_left_radius filter-all">All surveys</span><span class="badge badge-primary only_right_radius filter-finished o_active_filter">Finished surveys</span>
- </span>
- <span t-if="filter_finish == False">
- <span class="badge badge-primary only_left_radius filter-all o_active_filter">All surveys</span><span class="badge badge-secondary only_right_radius filter-finished">Finished surveys</span>
- </span>
- <span t-foreach="filter_display_data" t-as="filter_data">
+ <span t-att-class="'badge only_left_radius filter-all %s' % 'badge-secondary' if search_finished else 'badge-primary o_active_filter'">All surveys</span>
+ <span t-att-class="'badge only_right_radius filter-finished %s' % 'badge-secondary' if not search_finished else 'badge-primary o_active_filter'">Finished surveys</span>
+ <span t-foreach="search_filters" t-as="filter_data">
<span class="badge badge-primary only_left_radius"><i class="fa fa-filter" role="img" aria-label="Filter" title="Filter"></i></span><span class="badge badge-primary no_radius" t-esc="filter_data['question_text']"></span><span class="badge badge-success only_right_radius" t-esc="' > '.join(filter_data['labels'])"></span>
</span>
</div>
</div>
<div t-if="survey.scoring_type in ['scoring_with_answers', 'scoring_without_answers']">
<h1 class="mt16">Results Overview</h1>
- <div>Success rate: <mark class="font-weight-bold"><t t-esc="survey_dict['success_rate']"></t>%</mark></div>
- <div class="survey_graph" data-graph-type="pie" t-att-data-graph-data="survey_dict['scoring_graph_data']">
+ <div>Success rate: <mark class="font-weight-bold"><t t-esc="survey_data['global_success_rate']"></t>%</mark></div>
+ <div class="survey_graph" data-graph-type="pie" t-att-data-graph-data="survey_data['global_success_graph']">
<!-- canvas element for drawing pie chart -->
<canvas/>
</div>
<hr/>
</div>
- <div t-if="answers" t-foreach="survey_dict['page_ids']" t-as='page_ids'>
- <t t-set="page" t-value="page_ids['page']"/>
- <h1 class="mt16" t-field='page.title'></h1>
- <div t-field="page.description" class="oe_no_empty" />
- <hr/>
- <div t-foreach="page_ids['question_ids']" t-as='question_ids' class="mt16">
- <t t-set="input_summary" t-value="question_ids['input_summary']"/>
- <t t-set="question" t-value="question_ids['question']"/>
- <t t-set="graph_data" t-value="question_ids['graph_data']"/>
- <t t-set="prepare_result" t-value="question_ids['prepare_result']"/>
- <t t-set="question_with_correct_answers" t-value="survey.scoring_type in ['scoring_with_answers', 'scoring_without_answers'] and question.question_type in ['simple_choice', 'multiple_choice']" />
- <h4>
- <b>Question </b>
- <span t-field='question.title'></span>
- <t t-if="question.question_type == 'matrix'">
- <small><span class="badge badge-secondary">Matrix: <span t-field='question.matrix_subtype'/></span></small>
- </t>
- <t t-if="question.question_type in ['simple_choice', 'multiple_choice']">
- <small><span t-field='question.question_type' class="badge badge-secondary"></span></small>
- </t>
- <span class="float-right">
- <span class="badge badge-success"><span t-esc="input_summary['answered']"></span> Answered</span>
- <span class="badge badge-warning"><span t-esc="input_summary['skipped']"></span> Skipped</span>
- </span>
- </h4>
- <t t-if="question_with_correct_answers">
- <t t-set="correct_answers" t-value="question.get_correct_answer_ids()" />
- <div t-if="correct_answers">
- <t t-if="question.question_type == 'simple_choice'">
- <p><span>Correct answer</span>: <span class="font-weight-bold" t-esc="correct_answers[0].value"></span></p>
- </t>
- <t t-if="question.question_type == 'multiple_choice'">
- <span>Correct answers</span>:
- <ul>
- <li t-foreach="correct_answers" t-as="correct_answer">
- <t t-esc="correct_answer.value" />
- </li>
- </ul>
- </t>
+
+ <div t-foreach="question_and_page_data" t-as='question_data'>
+ <t t-set="question" t-value="question_data['question']"/>
+ <t t-if="question_data['is_page']">
+ <h2 class="mt16 text-uppercase text-muted" t-field="question.title"/>
+ <div t-field="question.description" class="oe_no_empty" />
+ <hr class="mt-2 pb-1 mb-1"/>
+ </t>
+ <div t-else="" class="ml-4 mt-4">
+ <t t-set="question_scoring" t-value="survey.scoring_type in ['scoring_with_answers', 'scoring_without_answers'] and question.question_type in ['simple_choice', 'multiple_choice']"/>
+ <t t-set="comment_lines" t-value="question_data['comment_line_ids']"/>
+ <t t-set="graph_data" t-value="question_data['graph_data']"/>
+ <t t-set="table_data" t-value="question_data['table_data']"/>
+
+ <div class="d-flex align-items-start mb-2">
+ <div class="mr-auto">
+ <h5 t-field="question.title" class="mb-1"/>
+ <div t-if="question_scoring">
+ <t t-if="len(question_data['right_answers']) >= 1">
+ <span t-if="len(question_data['right_answers']) == 1">Right answer: </span>
+ <span t-if="len(question_data['right_answers']) > 1">Right answers: </span>
+ <strong>
+ <t t-foreach="question_data['right_answers']" t-as="answer">
+ <span t-if="answer_index != 0">, </span><span t-esc="answer.value"/>
+ </t>
+ </strong>
+ </t>
+ </div>
+ <div class="ml-3 text-muted" t-field="question.description"/>
</div>
- </t>
- <t t-if="input_summary['answered'] != 0">
- <t t-if="question.description">
- <div class="text-muted oe_no_empty" t-field="question.description" />
- </t>
- <t t-if="question.question_type in ['char_box', 'text_box', 'date', 'datetime']">
- <t t-call="survey.question_result_text"></t>
- </t>
- <t t-if="question.question_type in ['simple_choice', 'multiple_choice']">
- <t t-call="survey.question_result_choice"></t>
- </t>
+ <!-- Question info -->
+ <span class="badge badge-info" t-field='question.question_type'/>
<t t-if="question.question_type == 'matrix'">
- <t t-call="survey.question_result_matrix"></t>
+ <span class="badge badge-info ml-2" t-field='question.matrix_subtype'/>
</t>
- <t t-if="question.question_type == 'numerical_box'">
- <t t-call="survey.question_result_number"></t>
+ <!-- Scoring info -->
+ <t t-if="question_scoring">
+ <span class="badge badge-success ml-3"><span t-esc="len(question_data['right_inputs'])"></span> Correct</span>
+ <span class="badge badge-warning ml-1" t-if="question.question_type == 'multiple_choice'">
+ <span t-esc="len(question_data['partial_inputs'])"></span> Partial
+ </span>
</t>
+ <!-- Inputs info -->
+ <span class="badge badge-info ml-3"><span t-esc="len(question_data['answer_input_done_ids'])"></span> Answered</span>
+ <span class="badge badge-info ml-1"><span t-esc="len(question_data['answer_input_skipped_ids'])"></span> Skipped</span>
+ </div>
+ <t t-if="question.question_type in ['text_box', 'char_box', 'date', 'datetime']">
+ <t t-call="survey.question_result_text"/>
+ </t>
+ <t t-elif="question.question_type in ['numerical_box']">
+ <t t-call="survey.question_result_number"></t>
</t>
- <t t-if="input_summary['answered'] == 0">
- <h2 style="padding-top:30px;padding-bottom:30px;text-align:center;" class="text-muted">Sorry, No one answered this question.</h2>
+ <t t-elif="question.question_type in ['simple_choice', 'multiple_choice']">
+ <t t-call="survey.question_result_choice"></t>
+ </t>
+ <t t-elif="question.question_type in ['matrix']">
+ <t t-call="survey.question_result_matrix"></t>
</t>
</div>
</div>
</div>
- <div class="oe_structure" id="oe_structure_survey_result_2"/>
</t>
</template>
- <!-- Result for text_box,char_box and date -->
- <template id="question_result_text" name="Question: text result">
- <table class="table table-hover table-sm" t-att-id="'table_question_%d' % question.id">
+ <template id="question_result_text" name="Question: text result (text_box, char_box, date, datetime)">
+ <table class="table table-hover table-sm" t-att-id="'survey_table_question_%d' % question.id">
<thead>
<tr>
<th>#</th>
@@ -115,245 +110,239 @@
</tr>
</thead>
<tbody>
- <t t-set="text_result" t-value="prepare_result"/>
- <tr t-foreach="text_result" t-as="user_input">
- <td><a t-att-href="'/survey/print/%s?answer_token=%s' % (user_input.survey_id.access_token, user_input.user_input_id.access_token)"><t t-esc="user_input_index + 1"></t></a></td>
- <t t-if="question.question_type == 'text_box'">
+ <t t-foreach="table_data" t-as="input_line">
+ <tr t-att-class="'d-none' if input_line_index >= page_record_limit else ''">
<td>
- <a t-att-href="'/survey/print/%s?answer_token=%s' % (user_input.survey_id.access_token, user_input.user_input_id.access_token)" t-field="user_input.value_text_box"></a><br/>
+ <a t-att-href="input_line.user_input_id.get_print_url()"><t t-esc="input_line_index + 1"></t></a>
</td>
- </t>
- <t t-if="question.question_type == 'char_box'">
<td>
- <a t-att-href="'/survey/print/%s?answer_token=%s' % (user_input.survey_id.access_token, user_input.user_input_id.access_token)" t-field="user_input.value_char_box"></a><br/>
- </td>
- </t>
- <t t-if="question.question_type == 'date'">
- <td>
- <a t-att-href="'/survey/print/%s?answer_token=%s' % (user_input.survey_id.access_token, user_input.user_input_id.access_token)" t-field="user_input.value_date"></a><br/>
- </td>
- </t>
- <t t-if="question.question_type == 'datetime'">
- <td>
- <a t-att-href="'/survey/print/%s?answer_token=%s' % (user_input.survey_id.access_token, user_input.user_input_id.access_token)" t-field="user_input.value_datetime"></a><br/>
+ <t t-if="question.question_type == 'text_box'">
+ <a t-att-href="input_line.user_input_id.get_print_url()" t-field="input_line.value_text_box"></a>
+ </t>
+ <t t-if="question.question_type == 'char_box'">
+ <a t-att-href="input_line.user_input_id.get_print_url()" t-field="input_line.value_char_box"></a>
+ </t>
+ <t t-if="question.question_type == 'date'">
+ <a t-att-href="input_line.user_input_id.get_print_url()" t-field="input_line.value_date"></a>
+ </t>
+ <t t-if="question.question_type == 'datetime'">
+ <a t-att-href="input_line.user_input_id.get_print_url()" t-field="input_line.value_datetime"></a>
+ </t>
</td>
- </t>
- </tr>
+ </tr>
+ </t>
</tbody>
</table>
- <t t-call="survey.question_pagination"/>
+ <t t-call="survey.question_table_pagination"/>
</template>
- <template id="question_result_comments" name="Question: text tesult">
- <!-- a 'comments' variable must be set an must contain a list of browse records of user input lines -->
- <table class="table table-hover table-sm" t-att-id="'table_question_%d' % question.id">
- <thead>
- <tr>
- <th>#</th>
- <th>Comment</th>
- </tr>
- </thead>
- <tbody>
- <tr t-foreach="comments" t-as="user_input">
- <td><a t-att-href="'/survey/print/%s?answer_token=%s' % (user_input.survey_id.access_token, user_input.user_input_id.access_token)"><t t-esc="user_input_index + 1"></t></a></td>
- <td>
- <span t-field="user_input.value_char_box"></span><br/>
- </td>
- </tr>
- </tbody>
- </table>
- </template>
+ <template id="question_result_number" name="Question: number result (numerical_box)">
+ <span class="float-right mt8">
+ <span class="badge badge-secondary only_left_radius">Maximum </span> <span class="badge badge-success only_right_radius" t-esc="question_data['numerical_max']"></span>
+ <span class="badge badge-secondary only_left_radius">Minimum </span> <span class="badge badge-danger only_right_radius" t-esc="question_data['numerical_min']"></span>
+ <span class="badge badge-secondary only_left_radius">Average </span> <span class="badge badge-warning only_right_radius" t-esc="question_data['numerical_average']"></span>
+ </span>
- <template id="question_result_choice" name="Question: choice result">
- <div>
- <!-- Tabs -->
- <ul class="nav nav-tabs d-print-none" role="tablist">
- <li class="nav-item" t-if="question.question_type != 'simple_choice'">
- <a t-att-href="'#graph_question_%d' % question.id" t-att-aria-controls="'graph_question_%d' % question.id" class="nav-link active" data-toggle="tab" role="tab">
- <i class="fa fa-bar-chart-o"></i> Graph
- </a>
- </li>
- <li class="nav-item" t-if="question.question_type == 'simple_choice'">
- <a t-att-href="'#graph_question_%d' % question.id" t-att-aria-controls="'graph_question_%d' % question.id" class="nav-link active" data-toggle="tab" role="tab">
- <i class="fa fa-bar-chart-o"></i> Pie Chart
- </a>
- </li>
- <li class="nav-item">
- <a t-att-href="'#data_question_%d' % question.id" t-att-aria-controls="'data_question_%d' % question.id" class="nav-link" data-toggle="tab" role="tab">
- <i class="fa fa-list-alt"></i> Data
- </a>
- </li>
- </ul>
- <div class="tab-content">
- <div role="tabpanel" class="tab-pane active survey_graph" t-if="question.question_type != 'simple_choice'" t-att-id="'graph_question_%d' % question.id" t-att-data-question_id="question.id" data-graph-type="bar" t-att-data-graph-data="graph_data">
- <!-- canvas element for drawing bar chart -->
- <canvas/>
- </div>
- <div role="tabpanel" class="tab-pane active survey_graph" t-if="question.question_type == 'simple_choice'" t-att-id="'graph_question_%d' % question.id" t-att-data-question_id="question.id" data-graph-type="pie" t-att-data-graph-data="graph_data">
- <!-- canvas element for drawing pie chart -->
- <canvas/>
- </div>
- <div role="tabpanel" class="tab-pane" t-att-id="'data_question_%d' % question.id">
- <table class="table table-hover table-sm">
- <thead>
- <tr>
- <th>Answer Choices</th>
- <th>User Responses</th>
- <th t-if="question_with_correct_answers">Answer Score</th>
- </tr>
- </thead>
- <tbody>
- <tr t-foreach="prepare_result['answers']" t-as="user_input">
+ <ul class="nav nav-tabs d-print-none" role="tablist">
+ <li class="nav-item">
+ <a t-att-href="'#survey_stats_question_%d' % question.id"
+ t-att-aria-controls="'survey_stats_question_%d' % question.id"
+ class="nav-link active" data-toggle="tab" role="tab">
+ <i class="fa fa-list-ol"></i> Most Common
+ </a>
+ </li>
+ <li class="nav-item">
+ <a t-att-href="'#survey_data_question_%d' % question.id"
+ t-att-aria-controls="'survey_data_question_%d' % question.id"
+ class="nav-link" data-toggle="tab" role="tab">
+ <i class="fa fa-list-alt"></i> All Data
+ </a>
+ </li>
+ </ul>
+ <div class="tab-content">
+ <div role="tabpanel" class="tab-pane active with-3d-shadow with-transitions" t-att-id="'survey_stats_question_%d' % question.id">
+ <table class="table table-hover table-sm">
+ <thead>
+ <tr>
+ <th>User Responses</th>
+ <th>Occurence</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr t-foreach="question_data['numerical_common_lines']" t-as="common_line">
+ <td><span t-esc="common_line[0]"></span></td>
+ <td><span t-esc="common_line[1]"></span></td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ <div role="tabpanel" class="tab-pane" t-att-id="'survey_data_question_%d' % question.id">
+ <table class="table table-hover table-sm" t-att-id="'survey_table_question_%d' % question.id">
+ <thead>
+ <tr>
+ <th>#</th>
+ <th>User Responses</th>
+ </tr>
+ </thead>
+ <tbody>
+ <t t-foreach="table_data" t-as="input_line">
+ <tr t-att-class="'d-none' if input_line_index >= page_record_limit else ''">
<td>
- <p t-esc="user_input['text']"></p>
- </td>
- <td class="survey_answer">
- <span t-esc="round(user_input['count']*100.0/(input_summary['answered'] or 1),2)"></span> %
- <span t-esc="user_input['count']" class="badge badge-primary">Vote</span>
- <i class="fa fa-filter text-primary survey_filter" t-att-data-question_id="question.id" t-att-data-answer_id="user_input['answer_id']" role="img" aria-label="Filter question" title="Filter question"/>
- </td>
- <td t-if="question_with_correct_answers" t-esc="user_input['answer_score']">
+ <a t-att-href="input_line.user_input_id.get_print_url()"><t t-esc="input_line_index + 1"></t></a>
</td>
+ <td><span t-field="input_line.value_numerical_box"></span><br/></td>
</tr>
- </tbody>
- </table>
- </div>
- </div>
- <!-- handle comments -->
- <div>
- <t t-set="comments" t-value="prepare_result['comments']" />
- <t t-if="comments">
- <t t-call="survey.question_result_comments" />
- </t>
+ </t>
+ </tbody>
+ </table>
+ <t t-call="survey.question_table_pagination"/>
</div>
-
</div>
</template>
- <template id="question_result_matrix" name="Question: matrix result">
- <t t-set="matrix_result" t-value="prepare_result"/>
- <!-- Tabs -->
+ <template id="question_result_choice" name="Question: choice result (simple_choice, multiple_choice)">
<ul class="nav nav-tabs d-print-none" role="tablist">
<li class="nav-item">
- <a t-att-href="'#graph_question_%d' % question.id" t-att-aria-controls="'graph_question_%d' % question.id" class="nav-link active" data-toggle="tab" role="tab">
- <i class="fa fa-bar-chart"></i>
- Graph
+ <a t-att-href="'#survey_graph_question_%d' % question.id"
+ t-att-aria-controls="'survey_graph_question_%d' % question.id"
+ class="nav-link active" data-toggle="tab" role="tab">
+ <i class="fa fa-bar-chart-o"></i>
+ <span t-if="question.question_type == 'simple_choice'">Pie Graph</span>
+ <span t-else="">Graph</span>
</a>
</li>
<li class="nav-item">
- <a t-att-href="'#data_question_%d' % question.id" t-att-aria-controls="'data_question_%d' % question.id" class="nav-link" data-toggle="tab" role="tab">
- <i class="fa fa-list-alt"></i>
- Data
+ <a t-att-href="'#survey_data_question_%d' % question.id"
+ t-att-aria-controls="'survey_data_question_%d' % question.id"
+ class="nav-link" data-toggle="tab" role="tab">
+ <i class="fa fa-list-alt"></i> Data
</a>
</li>
</ul>
<div class="tab-content">
- <div role="tabpanel" class="tab-pane active with-3d-shadow with-transitions survey_graph" t-att-id="'graph_question_%d' % question.id" t-att-data-question_id= "question.id" data-graph-type= "multi_bar" t-att-data-graph-data="graph_data">
- <!-- canvas element for drawing Multibar chart -->
+ <div role="tabpanel" class="tab-pane active survey_graph"
+ t-att-id="'survey_graph_question_%d' % question.id"
+ t-att-data-question_id="question.id"
+ t-att-data-graph-type="'pie' if question.question_type == 'simple_choice' else 'bar'"
+ t-att-data-graph-data="graph_data">
+ <!-- canvas element for drawing bar chart -->
<canvas/>
</div>
- <div role="tabpanel" class="tab-pane" t-att-id="'data_question_%d' % question.id">
- <table class="table table-hover table-sm text-right">
+ <div role="tabpanel" class="tab-pane" t-att-id="'survey_data_question_%d' % question.id">
+ <table class="table table-hover table-sm">
<thead>
<tr>
- <th></th>
- <th class="text-right" t-foreach="matrix_result['answers']" t-as="answer_id">
- <span t-esc="matrix_result['answers'][answer_id]"></span>
- </th>
+ <th>Answer</th>
+ <th>User Choice</th>
+ <th t-if="question_scoring">Score</th>
</tr>
</thead>
<tbody>
- <tr t-foreach="matrix_result['rows']" t-as="row_id">
+ <tr t-foreach="table_data" t-as="choice_data">
<td>
- <span t-esc="matrix_result['rows'][row_id]"></span>
+ <p t-esc="choice_data['value']"></p>
</td>
- <td class="survey_answer" t-foreach="matrix_result['answers']" t-as="answer_id">
- <span t-esc="round(matrix_result['result'][(row_id,answer_id)]*100.0/(input_summary['answered'] or 1),2)"></span> %
- <span class="badge badge-primary" t-esc="matrix_result['result'][(row_id,answer_id)]"></span><i class="fa fa-filter text-primary survey_filter" t-att-data-question_id="question.id" t-att-data-row_id="row_id" t-att-data-answer_id="answer_id" role="img" aria-label="Survey filter" title="Survey filter"></i>
+ <td class="survey_answer">
+ <span t-esc="round(choice_data['count'] * 100.0/ (len(question_data['answer_line_done_ids']) or 1), 2)"></span> %
+ <span t-esc="'%s Votes' % choice_data['count']" class="badge badge-primary"/>
+ <i class="fa fa-filter text-primary survey_filter"
+ t-att-data-question_id="question.id"
+ t-att-data-answer_id="choice_data['suggested_answer'].id"
+ role="img" aria-label="Filter question" title="Filter question"/>
</td>
+ <td t-if="question_scoring" t-esc="choice_data['suggested_answer'].answer_score"/>
</tr>
</tbody>
</table>
</div>
- <!-- handle comments to matrix -->
- <div>
- <t t-set="comments" t-value="matrix_result['comments']" />
- <t t-if="comments">
- <t t-call="survey.question_result_comments" />
- </t>
- </div>
</div>
+ <div t-if="comment_lines" t-call="survey.question_result_comments" />
</template>
- <template id="question_result_number" name="Question:; number result">
- <t t-set="number_result" t-value="prepare_result"/>
- <t t-set="text_result" t-value="number_result['input_lines']" />
- <span class="float-right mt8">
- <span class="badge badge-secondary only_left_radius">Sum </span> <span class="badge badge-info only_right_radius" t-esc="number_result['sum']"></span>
- <span class="badge badge-secondary only_left_radius">Maximum </span> <span class="badge badge-success only_right_radius" t-esc="number_result['max']"></span>
- <span class="badge badge-secondary only_left_radius">Minimum </span> <span class="badge badge-danger only_right_radius" t-esc="number_result['min']"></span>
- <span class="badge badge-secondary only_left_radius">Average </span> <span class="badge badge-warning only_right_radius" t-esc="number_result['average']"></span>
- </span>
+ <template id="question_result_matrix" name="Question: matrix result (matrix)">
+ <t t-set="graph_data" t-value="question_data['graph_data']"/>
+ <t t-set="table_data" t-value="question_data['table_data']"/>
<ul class="nav nav-tabs d-print-none" role="tablist">
<li class="nav-item">
- <a t-att-href="'#most_common_%d' % question.id" t-att-aria-controls="'most_common_%d' % question.id" class="nav-link active" data-toggle="tab" role="tab">
- <i class="fa fa-list-ol"></i>
- Most Common <span t-esc="len(number_result['most_common'])"></span>
+ <a t-att-href="'#survey_graph_question_%d' % question.id"
+ t-att-aria-controls="'survey_graph_question_%d' % question.id"
+ class="nav-link active" data-toggle="tab" role="tab">
+ <i class="fa fa-bar-chart"></i> Graph
</a>
</li>
<li class="nav-item">
- <a t-att-href="'#data_question_%d' % question.id" t-att-aria-controls="'data_question_%d' % question.id" class="nav-link" data-toggle="tab" role="tab">
- <i class="fa fa-list-alt"></i>
- All Data
+ <a t-att-href="'#survey_data_question_%d' % question.id"
+ t-att-aria-controls="'survey_data_question_%d' % question.id"
+ class="nav-link" data-toggle="tab" role="tab">
+ <i class="fa fa-list-alt"></i> Data
</a>
</li>
</ul>
<div class="tab-content">
- <div role="tabpanel" class="tab-pane active with-3d-shadow with-transitions" t-att-id="'most_common_%d' % question.id">
- <table class="table table-hover table-sm">
- <thead>
- <tr>
- <th>User Responses</th>
- <th>Occurence</th>
- </tr>
- </thead>
- <tbody>
- <tr t-foreach="number_result['most_common']" t-as="row">
- <td>
- <span t-esc="row[0]"></span>
- </td>
- <td>
- <span t-esc="row[1]"></span>
- </td>
- </tr>
- </tbody>
- </table>
+ <div role="tabpanel" class="tab-pane active with-3d-shadow with-transitions survey_graph"
+ t-att-id="'survey_graph_question_%d' % question.id"
+ t-att-data-question_id= "question.id"
+ data-graph-type= "multi_bar"
+ t-att-data-graph-data="graph_data">
+ <!-- canvas element for drawing Multibar chart -->
+ <canvas/>
</div>
- <div role="tabpanel" class="tab-pane" t-att-id="'data_question_%d' % question.id">
- <table class="table table-hover table-sm" t-att-id="'table_question_%d' % question.id">
+ <div role="tabpanel" class="tab-pane" t-att-id="'survey_data_question_%d' % question.id">
+ <table class="table table-hover table-sm text-right">
<thead>
<tr>
- <th>#</th>
- <th>User Responses</th>
+ <th></th>
+ <th class="text-right" t-foreach="table_data[0]['columns']" t-as="column_data">
+ <span t-esc="column_data['suggested_answer'].value"></span>
+ </th>
</tr>
</thead>
<tbody>
- <tr class="d-none" t-foreach="number_result['input_lines']" t-as="user_input">
- <td><a t-att-href="'/survey/print/%s?answer_token=%s' % (user_input.survey_id.access_token, user_input.user_input_id.access_token)"><t t-esc="user_input_index + 1"></t></a></td>
- <td><span t-field="user_input.value_numerical_box"></span><br/></td>
+ <tr t-foreach="table_data" t-as="choice_data">
+ <td>
+ <span t-esc="choice_data['row'].value"></span>
+ </td>
+ <td class="survey_answer" t-foreach="choice_data['columns']" t-as="column_data">
+ <span t-esc="round(column_data['count'] * 100.0/ (len(question_data['answer_line_done_ids']) or 1), 2)"></span> %
+ <span class="badge badge-primary" t-esc="column_data['count']"></span>
+ <i class="fa fa-filter text-primary survey_filter"
+ t-att-data-question_id="question.id"
+ t-att-data-row_id="choice_data['row'].id"
+ t-att-data-answer_id="column_data['suggested_answer'].id" role="img" aria-label="Survey filter" title="Survey filter"></i>
+ </td>
</tr>
</tbody>
</table>
- <t t-call="survey.question_pagination"/>
</div>
+ <div t-if="comment_lines" t-call="survey.question_result_comments" />
</div>
</template>
- <template id="question_pagination" name="Survey: statistics pages pagination">
- <t t-set="record_limit" t-value="10"/><!-- Change This record_limit to change number of record per page-->
- <ul t-att-id="'pagination_%d' % question.id" class="pagination" t-att-data-question_id="question.id" t-att-data-record_limit="record_limit">
- <t t-if="len(text_result) > record_limit">
- <li t-foreach="page_range(len(text_result), record_limit)" t-as="num" class="page-item">
+ <template id="question_result_comments" name="Question: comments">
+ <table class="table table-hover table-sm" t-att-id="'survey_table_question_%d' % question.id">
+ <thead>
+ <tr>
+ <th>#</th>
+ <th>Comment</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr t-foreach="comment_lines" t-as="input_line">
+ <td><a t-att-href="input_line.user_input_id.get_print_url()"><t t-esc="input_line_index + 1"></t></a></td>
+ <td>
+ <span t-field="input_line.value_char_box"></span><br/>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </template>
+
+ <template id="question_table_pagination" name="Survey: statistics table pagination">
+ <ul t-att-id="'pagination_%d' % question.id" class="pagination" t-att-data-question_id="question.id" t-att-data-record_limit="page_record_limit">
+ <t t-if="len(table_data) > page_record_limit">
+ <t t-set="total" t-value="ceil(len(table_data) / page_record_limit) + 1"/>
+ <li t-foreach="range(1, total)" t-as="num"
+ t-att-class="'page-item o_survey_js_results_pagination %s' % ('active' if num == 1 else '')">
<a href="#" class="page-link" t-esc="num"></a>
</li>
</t>