[MERGE][IMP] Reporting system for surveys

bzr revid: rim@openerp.com-20140408135908-0h3qiohc84kx4b2r
This commit is contained in:
Richard Mathot (OpenERP) 2014-04-08 15:59:08 +02:00
commit d197bab150
5 changed files with 383 additions and 271 deletions

View File

@ -22,9 +22,7 @@
import json import json
import logging import logging
import werkzeug import werkzeug
from collections import Counter
from datetime import datetime from datetime import datetime
from itertools import product
from math import ceil from math import ceil
from openerp import SUPERUSER_ID from openerp import SUPERUSER_ID
@ -287,118 +285,65 @@ class WebsiteSurvey(http.Controller):
type='http', auth='user', multilang=True, website=True) type='http', auth='user', multilang=True, website=True)
def survey_reporting(self, survey, token=None, **post): def survey_reporting(self, survey, token=None, **post):
'''Display survey Results & Statistics for given survey.''' '''Display survey Results & Statistics for given survey.'''
result_template, current_filters, filter_display_data = 'survey.result', [], [] result_template, current_filters, filter_display_data, filter_finish = 'survey.result', [], [], False
survey_obj = request.registry['survey.survey']
if not survey.user_input_ids or not [input_id.id for input_id in survey.user_input_ids if input_id.state != 'new']: if not survey.user_input_ids or not [input_id.id for input_id in survey.user_input_ids if input_id.state != 'new']:
result_template = 'survey.no_result' result_template = 'survey.no_result'
if post: if 'finished' in post:
current_filters, filter_display_data = self.filter_input_ids(post) post.pop('finished')
filter_finish = True
if post or filter_finish:
filter_data = self.get_filter_data(post)
current_filters = survey_obj.filter_input_ids(request.cr, request.uid, filter_data, filter_finish, context=request.context)
filter_display_data = survey_obj.get_filter_display_data(request.cr, request.uid, filter_data, context=request.context)
return request.website.render(result_template, return request.website.render(result_template,
{'survey': survey, {'survey_dict': self.prepare_result_dict(survey, current_filters),
'prepare_result': self.prepare_result,
'get_input_summary': self.get_input_summary,
'get_graph_data': self.get_graph_data,
'page_range': self.page_range, 'page_range': self.page_range,
'current_filters': current_filters, 'current_filters': current_filters,
'filter_display_data': filter_display_data 'filter_display_data': filter_display_data,
'filter_finish': filter_finish
}) })
def filter_input_ids(self, filters): def prepare_result_dict(self,survey, current_filters=[]):
'''If user applies any filters, then this function returns list of """Returns dictionary having values for rendering template"""
filtered user_input_id and label's strings for display data in web''' survey_obj = request.registry['survey.survey']
cr, uid, context = request.cr, request.uid, request.context result = {'survey':survey, 'page_ids': []}
question_obj = request.registry['survey.question'] for page in survey.page_ids:
input_obj = request.registry['survey.user_input_line'] page_dict = {'page': page, 'question_ids': []}
input_line_obj = request.registry['survey.user_input_line'] for question in page.question_ids:
label_obj = request.registry['survey.label'] question_dict = {'question':question, 'input_summary':survey_obj.get_input_summary(request.cr, request.uid, question, current_filters, context=request.context), 'prepare_result':survey_obj.prepare_result(request.cr, request.uid, question, current_filters, context=request.context), 'graph_data': self.get_graph_data(question, current_filters)}
domain_filter, choice, filter_display_data = [], [], [] page_dict['question_ids'].append(question_dict)
result['page_ids'].append(page_dict)
return result
#if user add some random data in query URI def get_filter_data(self, post):
try: """Returns data used for filtering the result"""
for ids in filters: filters = []
for ids in post:
#if user add some random data in query URI, ignore it
try:
row_id, answer_id = ids.split(',') row_id, answer_id = ids.split(',')
question_id = filters[ids] filters.append({'row_id': int(row_id), 'answer_id': int(answer_id)})
question = question_obj.browse(cr, uid, int(question_id), context=context) except:
if row_id == '0': return filters
choice.append(int(answer_id)) return filters
labels = label_obj.browse(cr, uid, [int(answer_id)], context=context)
else:
domain_filter.extend(['|', ('value_suggested_row.id', '=', int(row_id)), ('value_suggested.id', '=', int(answer_id))])
labels = label_obj.browse(cr, uid, [int(row_id), int(answer_id)], context=context)
filter_display_data.append({'question_text': question.question, 'labels': [label.value for label in labels]})
if choice:
domain_filter.insert(0, ('value_suggested.id', 'in', choice))
else:
domain_filter = domain_filter[1:]
except:
#if user add some random data in query URI
return([], [])
line_ids = input_line_obj.search(cr, uid, domain_filter, context=context)
filtered_input_ids = [input.user_input_id.id for input in input_obj.browse(cr, uid, line_ids, context=context)]
return (filtered_input_ids, filter_display_data)
def page_range(self, total_record, limit): def page_range(self, total_record, limit):
'''Returns number of pages required for pagination''' '''Returns number of pages required for pagination'''
total = ceil(total_record / float(limit)) total = ceil(total_record / float(limit))
return range(1, int(total + 1)) return range(1, int(total + 1))
def prepare_result(self, question, current_filters=[]): def get_graph_data(self, question, current_filters=[]):
'''Prepare statistical data for questions by counting number of vote per choice on basis of filter''' '''Returns formatted data required by graph library on basis of filter'''
survey_obj = request.registry['survey.survey']
#Calculate and return statistics for choice
if question.type in ['simple_choice', 'multiple_choice']:
result_summary = {}
[result_summary.update({label.id: {'text': label.value, 'count': 0, 'answer_id': label.id}}) for label in question.labels_ids]
for input_line in question.user_input_line_ids:
if result_summary.get(input_line.value_suggested.id) and (not(current_filters) or input_line.user_input_id.id in current_filters):
result_summary[input_line.value_suggested.id]['count'] += 1
result_summary = result_summary.values()
#Calculate and return statistics for matrix
if question.type == 'matrix':
rows, answers, res = {}, {}, {}
[rows.update({label.id: label.value}) for label in question.labels_ids_2]
[answers.update({label.id: label.value}) for label in question.labels_ids]
for cell in product(rows.keys(), answers.keys()):
res[cell] = 0
for input_line in question.user_input_line_ids:
if not(current_filters) or input_line.user_input_id.id in current_filters:
res[(input_line.value_suggested_row.id, input_line.value_suggested.id)] += 1
result_summary = {'answers': answers, 'rows': rows, 'result': res}
#Calculate and return statistics for free_text, textbox, datetime
if question.type in ['free_text', 'textbox', 'datetime']:
result_summary = []
for input_line in question.user_input_line_ids:
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.type == 'numerical_box':
result_summary = {'input_lines': []}
all_inputs = []
for input_line in question.user_input_line_ids:
if not(current_filters) or input_line.user_input_id.id in current_filters:
all_inputs.append(input_line.value_number)
result_summary['input_lines'].append(input_line)
result_summary.update({'average': round(sum(all_inputs) / len(all_inputs), 2),
'max': round(max(all_inputs), 2),
'min': round(min(all_inputs), 2),
'most_comman': Counter(all_inputs).most_common(5)})
return result_summary
@http.route(['/survey/results/graph/<model("survey.question"):question>'],
type='http', auth='user', multilang=True, website=True)
def get_graph_data(self, question, **post):
'''Returns appropriate formated data required by graph library on basis of filter'''
current_filters = safe_eval(post.get('current_filters', '[]'))
result = [] result = []
if question.type in ['simple_choice', 'multiple_choice']: if question.type == 'multiple_choice':
result.append({'key': str(question.question), result.append({'key': str(question.question),
'values': self.prepare_result(question, current_filters)}) 'values': survey_obj.prepare_result(request.cr, request.uid, question, current_filters, context=request.context)})
if question.type == 'simple_choice':
result = survey_obj.prepare_result(request.cr, request.uid, question, current_filters, context=request.context)
if question.type == 'matrix': if question.type == 'matrix':
data = self.prepare_result(question, current_filters) data = survey_obj.prepare_result(request.cr, request.uid, question, current_filters, context=request.context)
for answer in data['answers']: for answer in data['answers']:
values = [] values = []
for res in data['result']: for res in data['result']:
@ -407,21 +352,6 @@ class WebsiteSurvey(http.Controller):
result.append({'key': data['answers'].get(answer), 'values': values}) result.append({'key': data['answers'].get(answer), 'values': values})
return json.dumps(result) return json.dumps(result)
def get_input_summary(self, question, current_filters=[]):
'''Returns overall summary of question e.g. answered, skipped, total_inputs on basis of filter'''
result = {}
if question.survey_id.user_input_ids:
total_input_ids = current_filters or [input_id.id for input_id in question.survey_id.user_input_ids if input_id.state != 'new']
result['total_inputs'] = len(total_input_ids)
question_input_ids = []
for user_input in question.user_input_line_ids:
if not user_input.skipped:
question_input_ids.append(user_input.user_input_id.id)
result['answered'] = len(set(question_input_ids) & set(total_input_ids))
result['skipped'] = result['total_inputs'] - result['answered']
return result
def dict_soft_update(dictionary, key, value): def dict_soft_update(dictionary, key, value):
''' Insert the pair <key>: <value> into the <dictionary>. If <key> is ''' Insert the pair <key>: <value> into the <dictionary>. If <key> is
already present, this function will append <value> to the list of already present, this function will append <value> to the list of

View File

@ -1,42 +1,53 @@
.only_right_radius{ .only_right_radius {
border-top-right-radius:2em; border-top-right-radius: 2em;
border-bottom-right-radius:2em; border-bottom-right-radius: 2em;
border-top-left-radius:0; border-top-left-radius: 0;
border-bottom-left-radius:0; border-bottom-left-radius: 0;
} }
.only_left_radius{ .only_left_radius {
border-top-right-radius:0; border-top-right-radius: 0;
border-bottom-right-radius:0; border-bottom-right-radius: 0;
border-top-left-radius:2em; border-top-left-radius: 2em;
border-bottom-left-radius:2em; border-bottom-left-radius: 2em;
} }
.no_radius{ .no_radius {
border-radius:0; border-radius: 0;
} }
.clear_survey_filter{ .clear_survey_filter, .filter-all, .filter-finished{
cursor:pointer; cursor: pointer;
} }
.nvtooltip h5 { .nvtooltip h5 {
margin: 0; margin: 0;
line-height: 18px; line-height: 18px;
font-weight: bold; font-weight: bold;
background-color: rgba(247,247,247,0.75); background-color: rgba(247,247,247,0.75);
text-align: center; text-align: center;
border-bottom: 1px solid #ebebeb; border-bottom: 1px solid #ebebeb;
-webkit-border-radius: 5px 5px 0 0; -webkit-border-radius: 5px 5px 0 0;
-moz-border-radius: 5px 5px 0 0; -moz-border-radius: 5px 5px 0 0;
border-radius: 5px 5px 0 0; border-radius: 5px 5px 0 0;
} }
.survey_answer i{ .survey_answer i {
padding:3px; padding:3px;
cursor:pointer; cursor:pointer;
} }
.survey_answer i.invisible{
visibility: hidden!important; .survey_answer i.invisible {
visibility: hidden!important;
}
@media print {
.tab-content > .tab-pane {
display: block;
}
.tab-content > .survey_graph > svg {
width: 1150px;
}
} }

View File

@ -62,29 +62,38 @@ $(document).ready(function () {
//initialize discreteBar Chart //initialize discreteBar Chart
function init_bar_chart(){ function init_bar_chart(){
return nv.models.discreteBarChart() return nv.models.discreteBarChart()
.x(function(d) { return d.text; }) .x(function(d) { return d.text; })
.y(function(d) { return d.count; }) .y(function(d) { return d.count; })
.staggerLabels(true) .staggerLabels(true)
.tooltips(false) .tooltips(false)
.showValues(true); .showValues(true);
}
//initialize Pie Chart
function init_pie_chart(){
return nv.models.pieChart()
.x(function(d) { return d.text; })
.y(function(d) { return d.count; })
.showLabels(false);
} }
//load chart to svg element chart:initialized chart, response:AJAX response, quistion_id:if of survey question, tick_limit:text length limit //load chart to svg element chart:initialized chart, response:AJAX response, quistion_id:if of survey question, tick_limit:text length limit
function load_chart(chart, response, question_id, tick_limit){ function load_chart(chart, response, question_id, tick_limit, graph_type){
// Custom Tick fuction for replacing long text with '...' // Custom Tick fuction for replacing long text with '...'
var customtick_function = function(d){ var customtick_function = function(d){
if(! this || d.length <= tick_limit){ if(! this || d.length <= tick_limit){
return d; return d;
} }
else{ else{
return d.slice(0,tick_limit)+'...'; return d.slice(0,tick_limit) + '...';
} }
}; };
chart.xAxis if (graph_type != 'pie'){
.tickFormat(customtick_function); chart.xAxis
chart.yAxis .tickFormat(customtick_function);
.tickFormat(d3.format('d')); chart.yAxis
.tickFormat(d3.format('d'));
}
d3.select('#graph_question_' + question_id + ' svg') d3.select('#graph_question_' + question_id + ' svg')
.datum(response) .datum(response)
.transition().duration(500).call(chart); .transition().duration(500).call(chart);
@ -96,42 +105,42 @@ $(document).ready(function () {
$.each(survey_graphs, function(index, graph){ $.each(survey_graphs, function(index, graph){
var question_id = $(graph).attr("data-question_id"); var question_id = $(graph).attr("data-question_id");
var graph_type = $(graph).attr("data-graph_type"); var graph_type = $(graph).attr("data-graph_type");
var current_filters = $(graph).attr("data-current_filters"); var graph_data = JSON.parse($(graph).attr("graph-data"));
console.log(current_filters); if(graph_type == 'multi_bar'){
$.ajax({ nv.addGraph(function(){
url: '/survey/results/graph/'+question_id, var chart = init_multibar_chart();
type: 'POST', return load_chart(chart, graph_data, question_id, 25);
dataType: 'json', });
data:{'current_filters': current_filters}, }
success: function(response, status, xhr, wfe){ else if(graph_type == 'bar'){
if(graph_type == 'multi_bar'){ nv.addGraph(function() {
nv.addGraph(function(){ var chart = init_bar_chart();
var chart = init_multibar_chart(); return load_chart(chart, graph_data, question_id, 35);
return load_chart(chart,response,question_id,25); });
}); }
} else if(graph_type == 'pie'){
else if(graph_type == 'bar'){ nv.addGraph(function() {
nv.addGraph(function() { var chart = init_pie_chart();
var chart = init_bar_chart(); return load_chart(chart, graph_data, question_id, 25, 'pie');
return load_chart(chart,response,question_id,35); });
}); }
}
}
});
}); });
// Script for filter // Script for filter
$('td.survey_answer').hover(function(){$(this).find('i.fa-filter').removeClass('invisible');},function(){$(this).find('i.fa-filter').addClass('invisible');}); $('td.survey_answer').hover(function(){
$(this).find('i.fa-filter').removeClass('invisible');
}, function(){
$(this).find('i.fa-filter').addClass('invisible');
});
$('td.survey_answer i.fa-filter').click(function(){ $('td.survey_answer i.fa-filter').click(function(){
var cell=$(this); var cell = $(this);
var question_id = cell.attr('data-question_id');
var row_id = cell.attr('data-row_id') | 0; var row_id = cell.attr('data-row_id') | 0;
var answer_id = cell.attr('data-answer_id'); var answer_id = cell.attr('data-answer_id');
if(document.URL.indexOf("?") == -1){ if(document.URL.indexOf("?") == -1){
window.location.href = document.URL+'?' + encodeURI(row_id + ','+answer_id + '=' + question_id); window.location.href = document.URL + '?' + encodeURI(row_id + ',' + answer_id);
} }
else{ else {
window.location.href = document.URL+'&' + encodeURI(row_id + ','+answer_id + '=' + question_id); window.location.href = document.URL + '&' + encodeURI(row_id + ',' + answer_id);
} }
}); });
@ -139,6 +148,30 @@ $(document).ready(function () {
$('.clear_survey_filter').click(function(){ $('.clear_survey_filter').click(function(){
window.location.href = document.URL.substring(0,document.URL.indexOf("?")); window.location.href = document.URL.substring(0,document.URL.indexOf("?"));
}); });
$('span.filter-all').click(function(){
event.preventDefault();
if(document.URL.indexOf("finished") != -1){
window.location.href = document.URL.replace('?finished&','?').replace('&finished&','&').replace('?finished','').replace('&finished','');
}
}).hover(function(){
if(document.URL.indexOf("finished") == -1){
$(this)[0].style.cursor = 'default';
}
});
// toggle finished/all surveys filter
$('span.filter-finished').click(function(){
event.preventDefault();
if(document.URL.indexOf("?") == -1){
window.location.href = document.URL + '?' + encodeURI('finished');
}
else if(document.URL.indexOf("finished") == -1){
window.location.href = document.URL + '&' + encodeURI('finished');
}
}).hover(function(){
if(document.URL.indexOf("finished") != -1){
$(this)[0].style.cursor = 'default';
}
});
console.debug("[survey] Survey Result JS loaded!"); console.debug("[survey] Survey Result JS loaded!");
}); });

View File

@ -24,6 +24,8 @@ from openerp.tools.translate import _
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as DF from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as DF
from openerp.addons.website.models.website import slug from openerp.addons.website.models.website import slug
from urlparse import urljoin from urlparse import urljoin
from itertools import product
from collections import Counter
import datetime import datetime
import logging import logging
@ -133,6 +135,126 @@ class survey_survey(osv.Model):
res[survey.id] = urljoin(base_url, "survey/results/%s" % slug(survey)) res[survey.id] = urljoin(base_url, "survey/results/%s" % slug(survey))
return res return res
def filter_input_ids(self, cr, uid, filters, finished=False, context=None):
'''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.
'''
if context is None:
context = {}
if filters:
input_line_obj = self.pool.get('survey.user_input_line')
domain_filter, choice, filter_display_data = [], [], []
for filter in filters:
row_id, answer_id = filter['row_id'], filter['answer_id']
if row_id == 0:
choice.append(answer_id)
else:
domain_filter.extend(['|', ('value_suggested_row.id', '=', row_id), ('value_suggested.id', '=', answer_id)])
if choice:
domain_filter.insert(0, ('value_suggested.id', 'in', choice))
else:
domain_filter = domain_filter[1:]
line_ids = input_line_obj.search(cr, uid, domain_filter, context=context)
filtered_input_ids = [input.user_input_id.id for input in input_line_obj.browse(cr, uid, line_ids, context=context)]
else:
filtered_input_ids, filter_display_data = [], []
if finished:
user_input = self.pool.get('survey.user_input')
if not filtered_input_ids:
current_filters = user_input.search(cr, uid, [], context=context)
user_input_objs = user_input.browse(cr, uid, current_filters, context=context)
else:
user_input_objs = user_input.browse(cr, uid, filtered_input_ids, context=context)
return [input.id for input in user_input_objs if input.state == 'done']
return filtered_input_ids
def get_filter_display_data(self, cr, uid, filters, context):
'''Returns data to display current filters
:param filters: list of dictionary (having: row_id, answer_id)
:param finished: True for completely filled survey, False otherwise.
:returns list of dict having data to display filters.
'''
filter_display_data = []
if filters:
question_obj = self.pool.get('survey.question')
label_obj = self.pool.get('survey.label')
for filter in filters:
row_id, answer_id = filter['row_id'], filter['answer_id']
question_id = label_obj.browse(cr, uid, answer_id, context=context).question_id.id
question = question_obj.browse(cr, uid, question_id, context=context)
if row_id == 0:
labels = label_obj.browse(cr, uid, [answer_id], context=context)
else:
labels = label_obj.browse(cr, uid, [row_id, answer_id], context=context)
filter_display_data.append({'question_text': question.question, 'labels': [label.value for label in labels]})
return filter_display_data
def prepare_result(self, cr, uid, question, current_filters=[], context=None):
''' Compute statistical data for questions by counting number of vote per choice on basis of filter '''
if context is None:
context = {}
#Calculate and return statistics for choice
if question.type in ['simple_choice', 'multiple_choice']:
result_summary = {}
[result_summary.update({label.id: {'text': label.value, 'count': 0, 'answer_id': label.id}}) for label in question.labels_ids]
for input_line in question.user_input_line_ids:
if result_summary.get(input_line.value_suggested.id) and (not(current_filters) or input_line.user_input_id.id in current_filters):
result_summary[input_line.value_suggested.id]['count'] += 1
result_summary = result_summary.values()
#Calculate and return statistics for matrix
if question.type == 'matrix':
rows, answers, res = {}, {}, {}
[rows.update({label.id: label.value}) for label in question.labels_ids_2]
[answers.update({label.id: label.value}) for label in question.labels_ids]
for cell in product(rows.keys(), answers.keys()):
res[cell] = 0
for input_line in question.user_input_line_ids:
if not(current_filters) or input_line.user_input_id.id in current_filters:
res[(input_line.value_suggested_row.id, input_line.value_suggested.id)] += 1
result_summary = {'answers': answers, 'rows': rows, 'result': res}
#Calculate and return statistics for free_text, textbox, datetime
if question.type in ['free_text', 'textbox', 'datetime']:
result_summary = []
for input_line in question.user_input_line_ids:
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.type == 'numerical_box':
result_summary = {'input_lines': []}
all_inputs = []
for input_line in question.user_input_line_ids:
if not(current_filters) or input_line.user_input_id.id in current_filters:
all_inputs.append(input_line.value_number)
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),
'most_comman': Counter(all_inputs).most_common(5)})
return result_summary
def get_input_summary(self, cr, uid, question, current_filters=[], context=None):
''' Returns overall summary of question e.g. answered, skipped, total_inputs on basis of filter '''
if context is None:
context = {}
result = {}
if question.survey_id.user_input_ids:
total_input_ids = current_filters or [input_id.id for input_id in question.survey_id.user_input_ids if input_id.state != 'new']
result['total_inputs'] = len(total_input_ids)
question_input_ids = []
for user_input in question.user_input_line_ids:
if not user_input.skipped:
question_input_ids.append(user_input.user_input_id.id)
result['answered'] = len(set(question_input_ids) & set(total_input_ids))
result['skipped'] = result['total_inputs'] - result['answered']
return result
# Model fields # # Model fields #
_columns = { _columns = {

View File

@ -13,42 +13,66 @@
<div class="oe_structure" /> <div class="oe_structure" />
<div class="container"> <div class="container">
<div class="jumbotron mt32"> <div class="jumbotron mt32">
<t t-set="survey" t-value="survey_dict['survey']"/>
<h1><span t-field="survey.title"></span> <span style="font-size:1.5em;" class="fa fa-bar-chart-o pull-right "/></h1> <h1><span t-field="survey.title"></span> <span style="font-size:1.5em;" class="fa fa-bar-chart-o pull-right "/></h1>
<h2><span t-field="survey.description"></span></h2> <h2><span t-field="survey.description"></span></h2>
</div> </div>
<div t-if="filter_display_data" class="filteres"> <div class="panel panel-default hidden-print">
<span><h3><span class="fa fa-filter"></span> Filters <small class="pull-right clear_survey_filter"> <i class="fa fa-times"></i> Clear All Filters</small></h3></span> <div class="panel-heading"><span class="fa fa-filter"></span> Filters <span t-if="filter_display_data" class="pull-right text-primary clear_survey_filter"><i class="fa fa-times"></i> Clear All Filters</span></div>
<span t-foreach="filter_display_data" t-as="filter_data"> <div class="panel-body">
<span class="label label-primary only_left_radius"><i class="fa fa-filter"></i></span><span class="label label-primary no_radius" t-esc="filter_data['question_text']"></span><span class="label label-success only_right_radius" t-esc="' > '.join(filter_data['labels'])"></span> <span t-if="filter_finish == True">
</span> <span class="label label-default only_left_radius filter-all">All surveys</span><span class="label label-primary only_right_radius filter-finished">Finished surveys</span>
</span>
<span t-if="filter_finish == False">
<span class="label label-primary only_left_radius filter-all">All surveys</span><span class="label label-default only_right_radius filter-finished">Finished surveys</span>
</span>
<span t-foreach="filter_display_data" t-as="filter_data">
<span class="label label-primary only_left_radius"><i class="fa fa-filter"></i></span><span class="label label-primary no_radius" t-esc="filter_data['question_text']"></span><span class="label label-success only_right_radius" t-esc="' > '.join(filter_data['labels'])"></span>
</span>
</div>
</div> </div>
<div t-foreach='survey.page_ids' t-as='page'> <div 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> <h1 class="mt16" t-field='page.title'></h1>
<hr/> <hr/>
<div t-foreach='page.question_ids' t-as='question' class="mt16"> <div t-foreach="page_ids['question_ids']" t-as='question_ids' class="mt16">
<t t-set="input_summary" t-value="get_input_summary(question, current_filters)"/> <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']"/>
<h4> <h4>
<b>Question </b> <b>Question </b>
<span t-field='question.question'></span> <span t-field='question.question'></span>
<t t-if="question.type == 'matrix'">
<small><span class="label label-default">Matrix: <span t-field='question.matrix_subtype'/></span></small>
</t>
<t t-if="question.type in ['simple_choice', 'multiple_choice']">
<small><span t-field='question.type' class="label label-default"></span></small>
</t>
<span class="pull-right"> <span class="pull-right">
<span class="label label-success"><span t-esc="input_summary['answered']"></span> Answered</span> <span class="label label-success"><span t-esc="input_summary['answered']"></span> Answered</span>
<span class="label label-danger"><span t-esc="input_summary['skipped']"></span> Skipped</span> <span class="label label-danger"><span t-esc="input_summary['skipped']"></span> Skipped</span>
</span> </span>
</h4> </h4>
<t t-if="question.description"> <t t-if="input_summary['answered'] != 0">
<h5><span class="text-muted" t-field="question.description"></span></h5> <t t-if="question.description">
<h5><span class="text-muted" t-field="question.description"></span></h5>
</t>
<t t-if="question.type in ['textbox', 'free_text', 'datetime']">
<t t-call="survey.result_text"></t>
</t>
<t t-if="question.type in ['simple_choice', 'multiple_choice']">
<t t-call="survey.result_choice"></t>
</t>
<t t-if="question.type == 'matrix'">
<t t-call="survey.result_matrix"></t>
</t>
<t t-if="question.type == 'numerical_box'">
<t t-call="survey.result_number"></t>
</t>
</t> </t>
<t t-if="question.type in ['textbox', 'free_text', 'datetime']"> <t t-if="input_summary['answered'] == 0">
<t t-call="survey.result_text"></t> <h2 style="padding-top:30px;padding-bottom:30px;text-align:center;" class="text-muted">Sorry, No one answered this question.</h2>
</t>
<t t-if="question.type in ['simple_choice', 'multiple_choice']">
<t t-call="survey.result_choice"></t>
</t>
<t t-if="question.type == 'matrix'">
<t t-call="survey.result_matrix"></t>
</t>
<t t-if="question.type == 'numerical_box'">
<t t-call="survey.result_number"></t>
</t> </t>
</div> </div>
</div> </div>
@ -59,78 +83,70 @@
<!-- Result for free_text,textbox and datetime --> <!-- Result for free_text,textbox and datetime -->
<template id="result_text" name="Text Result"> <template id="result_text" name="Text Result">
<t t-if="input_summary['answered'] != 0"> <table class="table table-hover table-condensed" t-att-id="'table_question_%d' % question.id">
<table class="table table-hover" t-att-id="'table_question_'+str(question.id)"> <thead>
<thead> <tr>
<tr> <th>#</th>
<th>#</th> <th>User Responses</th>
<th>User Responses</th> </tr>
</tr> </thead>
</thead> <tbody>
<tbody> <t t-set="text_result" t-value="prepare_result"/>
<t t-set="text_result" t-value="prepare_result(question, current_filters)"></t> <tr t-foreach="text_result" t-as="user_input">
<tr class="hidden" t-foreach="text_result" t-as="user_input"> <td><a t-att-href="'%s/%s' % (user_input.user_input_id.print_url, user_input.user_input_id.token)"><t t-esc="user_input_index + 1"></t></a></td>
<td><t t-esc="user_input_index+1"></t></td> <t t-if="question.type == 'free_text'">
<t t-if="question.type == 'free_text'"> <td>
<td> <span t-field="user_input.value_free_text"></span><br/>
<span t-field="user_input.value_free_text"></span><br/> </td>
<small><p t-field="user_input.date_create" class="fa fa-calendar oe_date text-muted"></p></small> </t>
<span class="pull-right"> <t t-if="question.type == 'textbox'">
<a t-att-href="user_input.user_input_id.print_url+'/'+user_input.user_input_id.token"> <i class="fa fa-print"></i> Print respondent's answers</a><br/> <td>
</span> <span t-field="user_input.value_text"></span><br/>
</td> </td>
</t> </t>
<t t-if="question.type == 'textbox'"> <t t-if="question.type == 'datetime'">
<td> <td>
<span t-field="user_input.value_text"></span><br/> <span class="oe_date" t-field="user_input.value_date"></span><br/>
<small><p t-field="user_input.date_create" class="fa fa-calendar oe_date text-muted"></p> </td>
<span class="pull-right"> </t>
<a t-att-href="user_input.user_input_id.print_url+'/'+user_input.user_input_id.token"> <i class="fa fa-print"></i> Print respondent's answers</a><br/> </tr>
</span> </tbody>
</small> </table>
</td> <t t-call="survey.pagination" />
</t>
<t t-if="question.type == 'datetime'">
<td>
<span class="oe_date" t-field="user_input.value_date"></span><br/>
<span class="pull-right">
<a t-att-href="user_input.user_input_id.print_url+'/'+user_input.user_input_id.token"> <i class="fa fa-print"></i> Print respondent's answers</a><br/>
</span>
</td>
</t>
</tr>
</tbody>
</table>
<t t-call="survey.pagination" />
</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>
</template> </template>
<!-- Result for simple_choice and multiple_choice --> <!-- Result for simple_choice and multiple_choice -->
<template id="result_choice" name="Choice Result"> <template id="result_choice" name="Choice Result">
<div> <div>
<!-- Tabs --> <!-- Tabs -->
<ul class="nav nav-tabs"> <ul class="nav nav-tabs hidden-print">
<li class="active"> <li class="active" t-if="question.type != 'simple_choice'">
<a t-att-href="'#graph_question_'+str(question.id)" data-toggle="tab"> <a t-att-href="'#graph_question_%d' % question.id" data-toggle="tab">
<i class="fa fa-bar-chart-o"></i> Graph <i class="fa fa-bar-chart-o"></i> Graph
</a> </a>
</li> </li>
<li class="active" t-if="question.type == 'simple_choice'">
<a t-att-href="'#graph_question_%d' % question.id" data-toggle="tab">
<i class="fa fa-bar-chart-o"></i> Pie Chart
</a>
</li>
<li> <li>
<a t-att-href="'#data_question_'+str(question.id)" data-toggle="tab"> <a t-att-href="'#data_question_%d' % question.id" data-toggle="tab">
<i class="fa fa-list-alt"></i> Data <i class="fa fa-list-alt"></i> Data
</a> </a>
</li> </li>
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
<div class="tab-pane active survey_graph" t-att-id="'graph_question_'+str(question.id)" t-att-data-question_id="question.id" data-graph_type="bar" t-att-data-current_filters="current_filters"> <div class="tab-pane active survey_graph" t-if="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-graph-data="graph_data">
<!-- svg element for drawing bar chart --> <!-- svg element for drawing bar chart -->
<svg style="height:20em"></svg> <svg style="height:20em"></svg>
</div> </div>
<div class="tab-pane" t-att-id="'data_question_'+str(question.id)"> <div class="tab-pane active survey_graph" t-if="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-graph-data="graph_data">
<table class="table table-hover"> <!-- svg element for drawing pie chart -->
<svg style="height:20em"></svg>
</div>
<div class="tab-pane" t-att-id="'data_question_%d' % question.id">
<table class="table table-hover table-condensed">
<thead> <thead>
<tr> <tr>
<th>Answer Choices</th> <th>Answer Choices</th>
@ -138,7 +154,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr t-foreach="prepare_result(question, current_filters)" t-as="user_input"> <tr t-foreach="prepare_result" t-as="user_input">
<td> <td>
<p t-esc="user_input['text']"></p> <p t-esc="user_input['text']"></p>
</td> </td>
@ -157,29 +173,29 @@
<!-- Result for matrix --> <!-- Result for matrix -->
<template id="result_matrix" name="Matrix Result"> <template id="result_matrix" name="Matrix Result">
<t t-set="matrix_result" t-value="prepare_result(question, current_filters)" /> <t t-set="matrix_result" t-value="prepare_result"/>
<!-- Tabs --> <!-- Tabs -->
<ul class="nav nav-tabs"> <ul class="nav nav-tabs hidden-print">
<li class="active"> <li class="active">
<a t-att-href="'#graph_question_'+str(question.id)" data-toggle="tab"> <a t-att-href="'#graph_question_%d' % question.id" data-toggle="tab">
<i class="fa fa-bar-chart-o"></i> <i class="fa fa-bar-chart-o"></i>
Graph Graph
</a> </a>
</li> </li>
<li> <li>
<a t-att-href="'#data_question_'+str(question.id)" data-toggle="tab"> <a t-att-href="'#data_question_%d' % question.id" data-toggle="tab">
<i class="fa fa-list-alt"></i> <i class="fa fa-list-alt"></i>
Data Data
</a> </a>
</li> </li>
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
<div class="tab-pane active with-3d-shadow with-transitions survey_graph" t-att-id="'graph_question_'+str(question.id)" t-att-data-question_id= "question.id" data-graph_type= "multi_bar" t-att-data-current_filters="current_filters"> <div 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-graph-data="graph_data">
<!-- svg element for drawing Multibar chart --> <!-- svg element for drawing Multibar chart -->
<svg style="height:20em"></svg> <svg style="height:20em"></svg>
</div> </div>
<div class="tab-pane" t-att-id="'data_question_'+str(question.id)"> <div class="tab-pane" t-att-id="'data_question_%d' % question.id">
<table class="table table-hover text-right"> <table class="table table-hover table-condensed text-right">
<thead> <thead>
<tr> <tr>
<th></th> <th></th>
@ -206,30 +222,30 @@
<!-- Result for Numeric Data --> <!-- Result for Numeric Data -->
<template id="result_number" name="Number Result"> <template id="result_number" name="Number Result">
<t t-set="number_result" t-value="prepare_result(question, current_filters)" /> <t t-set="number_result" t-value="prepare_result"/>
<t t-set="text_result" t-value="number_result['input_lines']" /> <t t-set="text_result" t-value="number_result['input_lines']" />
<span class="pull-right mt8"> <span class="pull-right mt8">
<span class="label label-default only_left_radius">Maximum </span> <span class="label label-success only_right_radius" t-esc="number_result['max']"></span> <span class="label label-default only_left_radius">Maximum </span> <span class="label label-success only_right_radius" t-esc="number_result['max']"></span>
<span class="label label-default only_left_radius">Minimum </span> <span class="label label-danger only_right_radius" t-esc="number_result['min']"></span> <span class="label label-default only_left_radius">Minimum </span> <span class="label label-danger only_right_radius" t-esc="number_result['min']"></span>
<span class="label label-default only_left_radius">Average </span> <span class="label label-warning only_right_radius" t-esc="number_result['average']"></span> <span class="label label-default only_left_radius">Average </span> <span class="label label-warning only_right_radius" t-esc="number_result['average']"></span>
</span> </span>
<ul class="nav nav-tabs"> <ul class="nav nav-tabs hidden-print">
<li class="active"> <li class="active">
<a t-att-href="'#most_common_'+str(question.id)" data-toggle="tab"> <a t-att-href="'#most_common_%d' % question.id" data-toggle="tab">
<i class="fa fa-list-ol"></i> <i class="fa fa-list-ol"></i>
Most Common <span t-esc="len(number_result['most_comman'])"></span> Most Common <span t-esc="len(number_result['most_comman'])"></span>
</a> </a>
</li> </li>
<li> <li>
<a t-att-href="'#data_question_'+str(question.id)" data-toggle="tab"> <a t-att-href="'#data_question_%d' % question.id" data-toggle="tab">
<i class="fa fa-list-alt"></i> <i class="fa fa-list-alt"></i>
All Data All Data
</a> </a>
</li> </li>
</ul> </ul>
<div class="tab-content"> <div class="tab-content">
<div class="tab-pane active with-3d-shadow with-transitions" t-att-id="'most_common_'+str(question.id)"> <div class="tab-pane active with-3d-shadow with-transitions" t-att-id="'most_common_%d' % question.id">
<table class="table table-hover"> <table class="table table-hover table-condensed">
<thead> <thead>
<tr> <tr>
<th>User Responce</th> <th>User Responce</th>
@ -248,8 +264,8 @@
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="tab-pane" t-att-id="'data_question_'+str(question.id)"> <div class="tab-pane" t-att-id="'data_question_%d' % question.id">
<table class="table table-hover" t-att-id="'table_question_'+str(question.id)"> <table class="table table-hover table-condensed" t-att-id="'table_question_%d' % question.id">
<thead> <thead>
<tr> <tr>
<th>#</th> <th>#</th>
@ -287,7 +303,7 @@
</template> </template>
<!-- Pagination Element --> <!-- Pagination Element -->
<template id="pagination" name="Survey Result"> <template id="pagination" name="Survey Result">
<t t-set="record_limit" t-value="5"/><!-- Change This record_limit to change number of record per page--> <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"> <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"> <t t-if="len(text_result) > record_limit">
<li t-foreach="page_range(len(text_result), record_limit)" t-as="num"> <li t-foreach="page_range(len(text_result), record_limit)" t-as="num">