[MERGE] Merge all work from branch trunk-survey_reporting-pga due to new survey parent branch
bzr revid: pga@tinyerp.com-20140123112107-s1nvv160v3zoevlq
This commit is contained in:
parent
6b00de93c1
commit
918ee43f9a
|
@ -42,6 +42,7 @@ sent mails with personal token for the invitation of the survey.
|
|||
'security/ir.model.access.csv',
|
||||
'views/survey_views.xml',
|
||||
'views/survey_templates.xml',
|
||||
'views/survey_result.xml',
|
||||
'wizard/survey_email_compose_message.xml',
|
||||
'data/survey_cron.xml'
|
||||
],
|
||||
|
|
|
@ -29,6 +29,9 @@ from openerp.addons.web import http
|
|||
from openerp.addons.web.http import request
|
||||
#from openerp.addons.website.models import website
|
||||
from openerp.addons.website.controllers.main import Website as controllers
|
||||
import itertools
|
||||
import math
|
||||
from collections import Counter
|
||||
controllers = controllers()
|
||||
|
||||
|
||||
|
@ -286,6 +289,78 @@ class WebsiteSurvey(http.Controller):
|
|||
'token': token,
|
||||
'page_nr': 0})
|
||||
|
||||
@http.route(['/survey/results/<model("survey.survey"):survey>'],type='http', auth='user', multilang=True, website=True)
|
||||
def survey_reporting(self, survey, token=None, **post):
|
||||
return request.website.render('survey.result',
|
||||
{'survey': survey,
|
||||
'prepare_result':self.prepare_result,
|
||||
'get_input_summary':self.get_input_summary,
|
||||
'get_graph_data':self.get_graph_data,
|
||||
'page_range':self.page_range
|
||||
})
|
||||
|
||||
def page_range(self, total_record):
|
||||
total = math.ceil( total_record/5.0 )
|
||||
return range(1, int( total+1 ))
|
||||
|
||||
def prepare_result(self, question):
|
||||
if question.type in ['simple_choice', 'multiple_choice'] :
|
||||
result_summary = {}
|
||||
[ result_summary.update({ label.id : {'text':label.value, 'count':0} }) 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 input_line.skipped:
|
||||
result_summary[input_line.value_suggested.id]['count'] += 1
|
||||
result_summary = result_summary.values()
|
||||
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 itertools.product(rows.keys(), answers.keys()):
|
||||
res[cell] = 0
|
||||
for input_line in question.user_input_line_ids:
|
||||
if not input_line.skipped:
|
||||
res[(input_line.value_suggested_row.id,input_line.value_suggested.id)] += 1
|
||||
result_summary= {'answers':answers,'rows':rows,'result':res}
|
||||
if question.type == 'numerical_box':
|
||||
result_summary = {}
|
||||
all_inputs = []
|
||||
for input_line in question.user_input_line_ids:
|
||||
if not input_line.skipped:
|
||||
all_inputs.append(input_line.value_number)
|
||||
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_graph_data(self, question):
|
||||
result = []
|
||||
if question.type in ['simple_choice', 'multiple_choice']:
|
||||
result.append({'key':str(question.question),
|
||||
'values':self.prepare_result(question)})
|
||||
if question.type == 'matrix':
|
||||
data = self.prepare_result(question)
|
||||
for answer in data['answers']:
|
||||
values = []
|
||||
for res in data['result']:
|
||||
if res[1] == answer:
|
||||
values.append({'text': data['rows'][res[0]], 'count': data['result'][res]})
|
||||
result.append({'key':data['answers'].get(answer),'values':values})
|
||||
return json.dumps(result)
|
||||
|
||||
def get_input_summary(self, question):
|
||||
result = {}
|
||||
if question.page_id.survey_id.user_input_ids:
|
||||
result['total_inputs'] = len(question.page_id.survey_id.user_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))
|
||||
result['skipped'] = result['total_inputs'] - result['answered']
|
||||
return result
|
||||
|
||||
def dict_soft_update(dictionary, key, value):
|
||||
''' Insert the pair <key>: <value> into the <dictionary>. If <key> is
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,768 @@
|
|||
|
||||
/********************
|
||||
* HTML CSS
|
||||
*/
|
||||
|
||||
|
||||
.chartWrap {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/********************
|
||||
Box shadow and border radius styling
|
||||
*/
|
||||
.nvtooltip.with-3d-shadow, .with-3d-shadow .nvtooltip {
|
||||
-moz-box-shadow: 0 5px 10px rgba(0,0,0,.2);
|
||||
-webkit-box-shadow: 0 5px 10px rgba(0,0,0,.2);
|
||||
box-shadow: 0 5px 10px rgba(0,0,0,.2);
|
||||
|
||||
-webkit-border-radius: 6px;
|
||||
-moz-border-radius: 6px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
/********************
|
||||
* TOOLTIP CSS
|
||||
*/
|
||||
|
||||
.nvtooltip {
|
||||
position: absolute;
|
||||
background-color: rgba(255,255,255,1.0);
|
||||
padding: 1px;
|
||||
border: 1px solid rgba(0,0,0,.2);
|
||||
z-index: 10000;
|
||||
|
||||
font-family: Arial;
|
||||
font-size: 13px;
|
||||
text-align: left;
|
||||
pointer-events: none;
|
||||
|
||||
white-space: nowrap;
|
||||
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
/*Give tooltips that old fade in transition by
|
||||
putting a "with-transitions" class on the container div.
|
||||
*/
|
||||
.nvtooltip.with-transitions, .with-transitions .nvtooltip {
|
||||
transition: opacity 250ms linear;
|
||||
-moz-transition: opacity 250ms linear;
|
||||
-webkit-transition: opacity 250ms linear;
|
||||
|
||||
transition-delay: 250ms;
|
||||
-moz-transition-delay: 250ms;
|
||||
-webkit-transition-delay: 250ms;
|
||||
}
|
||||
|
||||
.nvtooltip.x-nvtooltip,
|
||||
.nvtooltip.y-nvtooltip {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.nvtooltip h3,.nvtooltip h5 {
|
||||
margin: 0;
|
||||
line-height: 18px;
|
||||
font-weight: bold;
|
||||
background-color: rgba(247,247,247,0.75);
|
||||
text-align: center;
|
||||
|
||||
border-bottom: 1px solid #ebebeb;
|
||||
|
||||
-webkit-border-radius: 5px 5px 0 0;
|
||||
-moz-border-radius: 5px 5px 0 0;
|
||||
border-radius: 5px 5px 0 0;
|
||||
}
|
||||
|
||||
.nvtooltip p {
|
||||
margin: 0;
|
||||
padding: 5px 14px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nvtooltip span {
|
||||
display: inline-block;
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
.nvtooltip table {
|
||||
margin: 6px;
|
||||
border-spacing:0;
|
||||
}
|
||||
|
||||
|
||||
.nvtooltip table td {
|
||||
padding: 2px 9px 2px 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.nvtooltip table td.key {
|
||||
font-weight:normal;
|
||||
}
|
||||
.nvtooltip table td.value {
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.nvtooltip table tr.highlight td {
|
||||
padding: 1px 9px 1px 0;
|
||||
border-bottom-style: solid;
|
||||
border-bottom-width: 1px;
|
||||
border-top-style: solid;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
.nvtooltip table td.legend-color-guide div {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.nvtooltip .footer {
|
||||
padding: 3px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
.nvtooltip-pending-removal {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
/********************
|
||||
* SVG CSS
|
||||
*/
|
||||
|
||||
|
||||
svg {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
/* Trying to get SVG to act like a greedy block in all browsers */
|
||||
display: block;
|
||||
width:100%;
|
||||
height:100%;
|
||||
}
|
||||
|
||||
|
||||
svg text {
|
||||
font: normal 12px Arial;
|
||||
}
|
||||
|
||||
svg .title {
|
||||
font: bold 14px Arial;
|
||||
}
|
||||
|
||||
.nvd3 .nv-background {
|
||||
fill: white;
|
||||
fill-opacity: 0;
|
||||
/*
|
||||
pointer-events: none;
|
||||
*/
|
||||
}
|
||||
|
||||
.nvd3.nv-noData {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
/**********
|
||||
* Brush
|
||||
*/
|
||||
|
||||
.nv-brush .extent {
|
||||
fill-opacity: .125;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Legend
|
||||
*/
|
||||
|
||||
.nvd3 .nv-legend .nv-series {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nvd3 .nv-legend .disabled circle {
|
||||
fill-opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Axes
|
||||
*/
|
||||
.nvd3 .nv-axis {
|
||||
pointer-events:none;
|
||||
}
|
||||
|
||||
.nvd3 .nv-axis path {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
stroke-opacity: .75;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.nvd3 .nv-axis path.domain {
|
||||
stroke-opacity: .75;
|
||||
}
|
||||
|
||||
.nvd3 .nv-axis.nv-x path.domain {
|
||||
stroke-opacity: 0;
|
||||
}
|
||||
|
||||
.nvd3 .nv-axis line {
|
||||
fill: none;
|
||||
stroke: #e5e5e5;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.nvd3 .nv-axis .zero line,
|
||||
/*this selector may not be necessary*/ .nvd3 .nv-axis line.zero {
|
||||
stroke-opacity: .75;
|
||||
}
|
||||
|
||||
.nvd3 .nv-axis .nv-axisMaxMin text {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.nvd3 .x .nv-axis .nv-axisMaxMin text,
|
||||
.nvd3 .x2 .nv-axis .nv-axisMaxMin text,
|
||||
.nvd3 .x3 .nv-axis .nv-axisMaxMin text {
|
||||
text-anchor: middle
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Brush
|
||||
*/
|
||||
|
||||
.nv-brush .resize path {
|
||||
fill: #eee;
|
||||
stroke: #666;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Bars
|
||||
*/
|
||||
|
||||
.nvd3 .nv-bars .negative rect {
|
||||
zfill: brown;
|
||||
}
|
||||
|
||||
.nvd3 .nv-bars rect {
|
||||
zfill: steelblue;
|
||||
fill-opacity: .75;
|
||||
|
||||
transition: fill-opacity 250ms linear;
|
||||
-moz-transition: fill-opacity 250ms linear;
|
||||
-webkit-transition: fill-opacity 250ms linear;
|
||||
}
|
||||
|
||||
.nvd3 .nv-bars rect.hover {
|
||||
fill-opacity: 1;
|
||||
}
|
||||
|
||||
.nvd3 .nv-bars .hover rect {
|
||||
fill: lightblue;
|
||||
}
|
||||
|
||||
.nvd3 .nv-bars text {
|
||||
fill: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
.nvd3 .nv-bars .hover text {
|
||||
fill: rgba(0,0,0,1);
|
||||
}
|
||||
|
||||
|
||||
/**********
|
||||
* Bars
|
||||
*/
|
||||
|
||||
.nvd3 .nv-multibar .nv-groups rect,
|
||||
.nvd3 .nv-multibarHorizontal .nv-groups rect,
|
||||
.nvd3 .nv-discretebar .nv-groups rect {
|
||||
stroke-opacity: 0;
|
||||
|
||||
transition: fill-opacity 250ms linear;
|
||||
-moz-transition: fill-opacity 250ms linear;
|
||||
-webkit-transition: fill-opacity 250ms linear;
|
||||
}
|
||||
|
||||
.nvd3 .nv-multibar .nv-groups rect:hover,
|
||||
.nvd3 .nv-multibarHorizontal .nv-groups rect:hover,
|
||||
.nvd3 .nv-discretebar .nv-groups rect:hover {
|
||||
fill-opacity: 1;
|
||||
}
|
||||
|
||||
.nvd3 .nv-discretebar .nv-groups text,
|
||||
.nvd3 .nv-multibarHorizontal .nv-groups text {
|
||||
font-weight: bold;
|
||||
fill: rgba(0,0,0,1);
|
||||
stroke: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
/***********
|
||||
* Pie Chart
|
||||
*/
|
||||
|
||||
.nvd3.nv-pie path {
|
||||
stroke-opacity: 0;
|
||||
transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear;
|
||||
-moz-transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear;
|
||||
-webkit-transition: fill-opacity 250ms linear, stroke-width 250ms linear, stroke-opacity 250ms linear;
|
||||
|
||||
}
|
||||
|
||||
.nvd3.nv-pie .nv-slice text {
|
||||
stroke: #000;
|
||||
stroke-width: 0;
|
||||
}
|
||||
|
||||
.nvd3.nv-pie path {
|
||||
stroke: #fff;
|
||||
stroke-width: 1px;
|
||||
stroke-opacity: 1;
|
||||
}
|
||||
|
||||
.nvd3.nv-pie .hover path {
|
||||
fill-opacity: .7;
|
||||
}
|
||||
.nvd3.nv-pie .nv-label {
|
||||
pointer-events: none;
|
||||
}
|
||||
.nvd3.nv-pie .nv-label rect {
|
||||
fill-opacity: 0;
|
||||
stroke-opacity: 0;
|
||||
}
|
||||
|
||||
/**********
|
||||
* Lines
|
||||
*/
|
||||
|
||||
.nvd3 .nv-groups path.nv-line {
|
||||
fill: none;
|
||||
stroke-width: 1.5px;
|
||||
/*
|
||||
stroke-linecap: round;
|
||||
shape-rendering: geometricPrecision;
|
||||
|
||||
transition: stroke-width 250ms linear;
|
||||
-moz-transition: stroke-width 250ms linear;
|
||||
-webkit-transition: stroke-width 250ms linear;
|
||||
|
||||
transition-delay: 250ms
|
||||
-moz-transition-delay: 250ms;
|
||||
-webkit-transition-delay: 250ms;
|
||||
*/
|
||||
}
|
||||
|
||||
.nvd3 .nv-groups path.nv-line.nv-thin-line {
|
||||
stroke-width: 1px;
|
||||
}
|
||||
|
||||
|
||||
.nvd3 .nv-groups path.nv-area {
|
||||
stroke: none;
|
||||
/*
|
||||
stroke-linecap: round;
|
||||
shape-rendering: geometricPrecision;
|
||||
|
||||
stroke-width: 2.5px;
|
||||
transition: stroke-width 250ms linear;
|
||||
-moz-transition: stroke-width 250ms linear;
|
||||
-webkit-transition: stroke-width 250ms linear;
|
||||
|
||||
transition-delay: 250ms
|
||||
-moz-transition-delay: 250ms;
|
||||
-webkit-transition-delay: 250ms;
|
||||
*/
|
||||
}
|
||||
|
||||
.nvd3 .nv-line.hover path {
|
||||
stroke-width: 6px;
|
||||
}
|
||||
|
||||
/*
|
||||
.nvd3.scatter .groups .point {
|
||||
fill-opacity: 0.1;
|
||||
stroke-opacity: 0.1;
|
||||
}
|
||||
*/
|
||||
|
||||
.nvd3.nv-line .nvd3.nv-scatter .nv-groups .nv-point {
|
||||
fill-opacity: 0;
|
||||
stroke-opacity: 0;
|
||||
}
|
||||
|
||||
.nvd3.nv-scatter.nv-single-point .nv-groups .nv-point {
|
||||
fill-opacity: .5 !important;
|
||||
stroke-opacity: .5 !important;
|
||||
}
|
||||
|
||||
|
||||
.with-transitions .nvd3 .nv-groups .nv-point {
|
||||
transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
|
||||
-moz-transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
|
||||
-webkit-transition: stroke-width 250ms linear, stroke-opacity 250ms linear;
|
||||
|
||||
}
|
||||
|
||||
.nvd3.nv-scatter .nv-groups .nv-point.hover,
|
||||
.nvd3 .nv-groups .nv-point.hover {
|
||||
stroke-width: 7px;
|
||||
fill-opacity: .95 !important;
|
||||
stroke-opacity: .95 !important;
|
||||
}
|
||||
|
||||
|
||||
.nvd3 .nv-point-paths path {
|
||||
stroke: #aaa;
|
||||
stroke-opacity: 0;
|
||||
fill: #eee;
|
||||
fill-opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.nvd3 .nv-indexLine {
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
|
||||
/**********
|
||||
* Distribution
|
||||
*/
|
||||
|
||||
.nvd3 .nv-distribution {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Scatter
|
||||
*/
|
||||
|
||||
/* **Attempting to remove this for useVoronoi(false), need to see if it's required anywhere
|
||||
.nvd3 .nv-groups .nv-point {
|
||||
pointer-events: none;
|
||||
}
|
||||
*/
|
||||
|
||||
.nvd3 .nv-groups .nv-point.hover {
|
||||
stroke-width: 20px;
|
||||
stroke-opacity: .5;
|
||||
}
|
||||
|
||||
.nvd3 .nv-scatter .nv-point.hover {
|
||||
fill-opacity: 1;
|
||||
}
|
||||
|
||||
/*
|
||||
.nv-group.hover .nv-point {
|
||||
fill-opacity: 1;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/**********
|
||||
* Stacked Area
|
||||
*/
|
||||
|
||||
.nvd3.nv-stackedarea path.nv-area {
|
||||
fill-opacity: .7;
|
||||
/*
|
||||
stroke-opacity: .65;
|
||||
fill-opacity: 1;
|
||||
*/
|
||||
stroke-opacity: 0;
|
||||
|
||||
transition: fill-opacity 250ms linear, stroke-opacity 250ms linear;
|
||||
-moz-transition: fill-opacity 250ms linear, stroke-opacity 250ms linear;
|
||||
-webkit-transition: fill-opacity 250ms linear, stroke-opacity 250ms linear;
|
||||
|
||||
/*
|
||||
transition-delay: 500ms;
|
||||
-moz-transition-delay: 500ms;
|
||||
-webkit-transition-delay: 500ms;
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
.nvd3.nv-stackedarea path.nv-area.hover {
|
||||
fill-opacity: .9;
|
||||
/*
|
||||
stroke-opacity: .85;
|
||||
*/
|
||||
}
|
||||
/*
|
||||
.d3stackedarea .groups path {
|
||||
stroke-opacity: 0;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
.nvd3.nv-stackedarea .nv-groups .nv-point {
|
||||
stroke-opacity: 0;
|
||||
fill-opacity: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
.nvd3.nv-stackedarea .nv-groups .nv-point.hover {
|
||||
stroke-width: 20px;
|
||||
stroke-opacity: .75;
|
||||
fill-opacity: 1;
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Line Plus Bar
|
||||
*/
|
||||
|
||||
.nvd3.nv-linePlusBar .nv-bar rect {
|
||||
fill-opacity: .75;
|
||||
}
|
||||
|
||||
.nvd3.nv-linePlusBar .nv-bar rect:hover {
|
||||
fill-opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
/**********
|
||||
* Bullet
|
||||
*/
|
||||
|
||||
.nvd3.nv-bullet { font: 10px sans-serif; }
|
||||
.nvd3.nv-bullet .nv-measure { fill-opacity: .8; }
|
||||
.nvd3.nv-bullet .nv-measure:hover { fill-opacity: 1; }
|
||||
.nvd3.nv-bullet .nv-marker { stroke: #000; stroke-width: 2px; }
|
||||
.nvd3.nv-bullet .nv-markerTriangle { stroke: #000; fill: #fff; stroke-width: 1.5px; }
|
||||
.nvd3.nv-bullet .nv-tick line { stroke: #666; stroke-width: .5px; }
|
||||
.nvd3.nv-bullet .nv-range.nv-s0 { fill: #eee; }
|
||||
.nvd3.nv-bullet .nv-range.nv-s1 { fill: #ddd; }
|
||||
.nvd3.nv-bullet .nv-range.nv-s2 { fill: #ccc; }
|
||||
.nvd3.nv-bullet .nv-title { font-size: 14px; font-weight: bold; }
|
||||
.nvd3.nv-bullet .nv-subtitle { fill: #999; }
|
||||
|
||||
|
||||
.nvd3.nv-bullet .nv-range {
|
||||
fill: #bababa;
|
||||
fill-opacity: .4;
|
||||
}
|
||||
.nvd3.nv-bullet .nv-range:hover {
|
||||
fill-opacity: .7;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Sparkline
|
||||
*/
|
||||
|
||||
.nvd3.nv-sparkline path {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.nvd3.nv-sparklineplus g.nv-hoverValue {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nvd3.nv-sparklineplus .nv-hoverValue line {
|
||||
stroke: #333;
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
|
||||
.nvd3.nv-sparklineplus,
|
||||
.nvd3.nv-sparklineplus g {
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.nvd3 .nv-hoverArea {
|
||||
fill-opacity: 0;
|
||||
stroke-opacity: 0;
|
||||
}
|
||||
|
||||
.nvd3.nv-sparklineplus .nv-xValue,
|
||||
.nvd3.nv-sparklineplus .nv-yValue {
|
||||
/*
|
||||
stroke: #666;
|
||||
*/
|
||||
stroke-width: 0;
|
||||
font-size: .9em;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.nvd3.nv-sparklineplus .nv-yValue {
|
||||
stroke: #f66;
|
||||
}
|
||||
|
||||
.nvd3.nv-sparklineplus .nv-maxValue {
|
||||
stroke: #2ca02c;
|
||||
fill: #2ca02c;
|
||||
}
|
||||
|
||||
.nvd3.nv-sparklineplus .nv-minValue {
|
||||
stroke: #d62728;
|
||||
fill: #d62728;
|
||||
}
|
||||
|
||||
.nvd3.nv-sparklineplus .nv-currentValue {
|
||||
/*
|
||||
stroke: #444;
|
||||
fill: #000;
|
||||
*/
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
/**********
|
||||
* historical stock
|
||||
*/
|
||||
|
||||
.nvd3.nv-ohlcBar .nv-ticks .nv-tick {
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
.nvd3.nv-ohlcBar .nv-ticks .nv-tick.hover {
|
||||
stroke-width: 4px;
|
||||
}
|
||||
|
||||
.nvd3.nv-ohlcBar .nv-ticks .nv-tick.positive {
|
||||
stroke: #2ca02c;
|
||||
}
|
||||
|
||||
.nvd3.nv-ohlcBar .nv-ticks .nv-tick.negative {
|
||||
stroke: #d62728;
|
||||
}
|
||||
|
||||
.nvd3.nv-historicalStockChart .nv-axis .nv-axislabel {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.nvd3.nv-historicalStockChart .nv-dragTarget {
|
||||
fill-opacity: 0;
|
||||
stroke: none;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.nvd3 .nv-brush .extent {
|
||||
/*
|
||||
cursor: ew-resize !important;
|
||||
*/
|
||||
fill-opacity: 0 !important;
|
||||
}
|
||||
|
||||
.nvd3 .nv-brushBackground rect {
|
||||
stroke: #000;
|
||||
stroke-width: .4;
|
||||
fill: #fff;
|
||||
fill-opacity: .7;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**********
|
||||
* Indented Tree
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* TODO: the following 3 selectors are based on classes used in the example. I should either make them standard and leave them here, or move to a CSS file not included in the library
|
||||
*/
|
||||
.nvd3.nv-indentedtree .name {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.nvd3.nv-indentedtree .clickable {
|
||||
color: #08C;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nvd3.nv-indentedtree span.clickable:hover {
|
||||
color: #005580;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
||||
.nvd3.nv-indentedtree .nv-childrenCount {
|
||||
display: inline-block;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.nvd3.nv-indentedtree .nv-treeicon {
|
||||
cursor: pointer;
|
||||
/*
|
||||
cursor: n-resize;
|
||||
*/
|
||||
}
|
||||
|
||||
.nvd3.nv-indentedtree .nv-treeicon.nv-folded {
|
||||
cursor: pointer;
|
||||
/*
|
||||
cursor: s-resize;
|
||||
*/
|
||||
}
|
||||
|
||||
/**********
|
||||
* Parallel Coordinates
|
||||
*/
|
||||
|
||||
.nvd3 .background path {
|
||||
fill: none;
|
||||
stroke: #ccc;
|
||||
stroke-opacity: .4;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.nvd3 .foreground path {
|
||||
fill: none;
|
||||
stroke: steelblue;
|
||||
stroke-opacity: .7;
|
||||
}
|
||||
|
||||
.nvd3 .brush .extent {
|
||||
fill-opacity: .3;
|
||||
stroke: #fff;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.nvd3 .axis line, .axis path {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.nvd3 .axis text {
|
||||
text-shadow: 0 1px 0 #fff;
|
||||
}
|
||||
|
||||
/****
|
||||
Interactive Layer
|
||||
*/
|
||||
.nvd3 .nv-interactiveGuideLine {
|
||||
pointer-events:none;
|
||||
}
|
||||
.nvd3 line.nv-guideline {
|
||||
stroke: #ccc;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -98,6 +98,15 @@ class survey_survey(osv.Model):
|
|||
% survey_browse.id)
|
||||
return res
|
||||
|
||||
def _get_result_url(self, cr, uid, ids, name, arg, context=None):
|
||||
""" Computes a result URL for the survey """
|
||||
res = dict((id, 0) for id in ids)
|
||||
base_url = self.pool.get('ir.config_parameter').get_param(cr, uid,
|
||||
'web.base.url')
|
||||
for survey_browse in self.browse(cr, uid, ids, context=context):
|
||||
res[survey_browse.id] = urljoin(base_url, "survey/results/%s/"
|
||||
% survey_browse.id)
|
||||
return res
|
||||
# Model fields #
|
||||
|
||||
_columns = {
|
||||
|
@ -132,6 +141,8 @@ class survey_survey(osv.Model):
|
|||
'User responses', readonly=1),
|
||||
'public_url': fields.function(_get_public_url,
|
||||
string="Public link", type="char"),
|
||||
'result_url': fields.function(_get_result_url,
|
||||
string="Result link", type="char"),
|
||||
'email_template_id': fields.many2one('email.template',
|
||||
'Email Template', ondelete='set null'),
|
||||
'thank_you_message': fields.html('Thank you message', translate=True,
|
||||
|
@ -426,6 +437,8 @@ class survey_question(osv.Model):
|
|||
oldname='minimum_req_ans'),
|
||||
'constr_error_msg': fields.char("Error message",
|
||||
oldname='req_error_msg'),
|
||||
'user_input_line_ids': fields.one2many('survey.user_input_line',
|
||||
'question_id', 'Answers',domain=[('skipped','=',False)]),
|
||||
}
|
||||
_defaults = {
|
||||
'page_id': lambda s, cr, uid, c: c.get('page_id'),
|
||||
|
|
|
@ -0,0 +1,316 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<template id="result" name="Survey Result">
|
||||
<t t-call="website.layout">
|
||||
<div class="container">
|
||||
<link href="/survey/static/src/lib/nvd3/nv.d3.css" rel="stylesheet" type="text/css"></link>
|
||||
<script src="/survey/static/src/lib/nvd3/lib/d3.v3.js"></script>
|
||||
<script src="/survey/static/src/lib/nvd3/nv.d3.js"></script>
|
||||
<style>
|
||||
.only_left_radius{
|
||||
border-top-right-radius:2em;
|
||||
border-bottom-right-radius:2em;
|
||||
}
|
||||
|
||||
.only_right_radius{
|
||||
border-top-left-radius:2em;
|
||||
border-bottom-left-radius:2em;
|
||||
}
|
||||
</style>
|
||||
<h1>Survey Result</h1>
|
||||
<h2><span t-field='survey.title'></span></h2>
|
||||
<div t-foreach='survey.page_ids' t-as='page' class="panel panel-primary">
|
||||
<div class="panel-heading"><h3 class="panel-title" t-field='page.title'></h3></div>
|
||||
<div class="row">
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
<div t-foreach='page.question_ids' t-as='question' class="panel panel-default mt16">
|
||||
<t t-set="input_summary" t-value="get_input_summary(question)"/>
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">
|
||||
<b>Question </b>
|
||||
<span t-field='question.question'></span>
|
||||
<span class="pull-right">
|
||||
<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>
|
||||
</h3>
|
||||
</div>
|
||||
<t t-if="question.type in ['textbox', 'free_text']">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- Result for free_text and textbox -->
|
||||
<template id="result_text" name="Text Result">
|
||||
<table class="table table-hover" t-att-id="'table_question_'+str(question.id)">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>User Responses</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="hidden" t-foreach="question.user_input_line_ids" t-as="user_input">
|
||||
<td><t t-esc="user_input_index+1"></t></td>
|
||||
<t t-if="question.type == 'free_text'">
|
||||
<td>
|
||||
<span t-field="user_input.value_free_text"></span><br/>
|
||||
<small><p t-field="user_input.date_create" class="fa fa-calendar oe_date text-muted"></p></small>
|
||||
</td>
|
||||
</t>
|
||||
<t t-if="question.type == 'textbox'">
|
||||
<td>
|
||||
<span t-field="user_input.value_text"></span><br/>
|
||||
<small><p t-field="user_input.date_create" class="fa fa-calendar oe_date text-muted"></p></small>
|
||||
</td>
|
||||
</t>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- Pagination Element -->
|
||||
<ul t-att-id="'pagination_'+str(question.id)" class="pagination" >
|
||||
<t t-if="len(question.user_input_line_ids) > 5">
|
||||
<li t-foreach="page_range(len(question.user_input_line_ids))" t-as="num">
|
||||
<a href="#" t-esc="num"></a>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
<!-- Dynamic Script for Pagination Element -->
|
||||
<script>
|
||||
$( document ).ready(function() {
|
||||
$('#table_question_<t t-esc="question.id"></t> tbody tr:lt(5)').removeClass('hidden');
|
||||
$('#<t t-esc="'pagination_'+str(question.id)"></t> li:first').addClass('active');
|
||||
$('#<t t-esc="'pagination_'+str(question.id)"></t> li a').click(function(event){
|
||||
event.preventDefault();
|
||||
$('#<t t-esc="'pagination_'+str(question.id)"></t> li').removeClass('active');
|
||||
$(this).parent('li').addClass('active');
|
||||
$('#table_question_<t t-esc="question.id"></t> tbody tr').addClass('hidden');
|
||||
var num = $(this).text();
|
||||
min = (5 * (num-1))-1;
|
||||
if (min == -1){
|
||||
$('#table_question_<t t-esc="question.id"></t> tbody tr:lt('+5 * num+')').removeClass('hidden');
|
||||
}
|
||||
else{
|
||||
$('#table_question_<t t-esc="question.id"></t> tbody tr:lt('+5 * num+'):gt('+min+')').removeClass('hidden');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</template>
|
||||
|
||||
<!-- Result for simple_choice and multiple_choice -->
|
||||
<template id="result_choice" name="Choice Result">
|
||||
<div>
|
||||
<!-- Tabs -->
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active">
|
||||
<a t-att-href="'#graph_question_'+str(question.id)" data-toggle="tab">
|
||||
<i class="fa fa-bar-chart-o"></i> Graph
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a t-att-href="'#data_question_'+str(question.id)" data-toggle="tab">
|
||||
<i class="fa fa-list-alt"></i> Data
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" t-att-id="'graph_question_'+str(question.id)">
|
||||
<!-- Dynamic script for single bar chart -->
|
||||
<script>
|
||||
var question_<t t-esc="question.id"></t> = <t t-esc="get_graph_data(question)"></t>;
|
||||
nv.addGraph(function() {
|
||||
var chart = nv.models.discreteBarChart()
|
||||
.x(function(d) { return d.text })
|
||||
.y(function(d) { return d.count })
|
||||
.staggerLabels(true)
|
||||
.tooltips(false)
|
||||
.showValues(true)
|
||||
|
||||
// Custom Tick fuction for replacing long text with '...'
|
||||
var customtick_function = function(d){
|
||||
if(this.hasOwnProperty('window') || d.length < 35){
|
||||
return d;
|
||||
}
|
||||
else{
|
||||
return d.slice(0,35)+'...';
|
||||
}
|
||||
}
|
||||
|
||||
chart.xAxis
|
||||
.tickFormat(customtick_function);
|
||||
|
||||
chart.yAxis
|
||||
.tickFormat(d3.format('d'));
|
||||
|
||||
d3.select('#graph_question_<t t-esc="question.id"></t> svg')
|
||||
.datum(question_<t t-esc="question.id"></t>)
|
||||
.transition().duration(500)
|
||||
.call(chart);
|
||||
|
||||
nv.utils.windowResize(chart.update);
|
||||
return chart;
|
||||
});
|
||||
</script>
|
||||
<!-- svg element for drawing bar chart -->
|
||||
<svg style="height:20em"></svg>
|
||||
</div>
|
||||
<div class="tab-pane" t-att-id="'data_question_'+str(question.id)">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Answer Choices</th>
|
||||
<th>User Responses</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="prepare_result(question)" t-as="user_input">
|
||||
<td>
|
||||
<p t-esc="user_input['text']"></p>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="round(user_input['count']*100.0/input_summary['answered'],2)"></span> %
|
||||
<span t-esc="user_input['count']" class="label label-primary">Vote</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Result for matrix -->
|
||||
<template id="result_matrix" name="Matrix Result">
|
||||
<t t-set="matrix_result" t-value="prepare_result(question)" />
|
||||
<!-- Tabs -->
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active">
|
||||
<a t-att-href="'#graph_question_'+str(question.id)" data-toggle="tab">
|
||||
<i class="fa fa-bar-chart-o"></i>
|
||||
Graph
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a t-att-href="'#data_question_'+str(question.id)" data-toggle="tab">
|
||||
<i class="fa fa-list-alt"></i>
|
||||
Data
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active with-3d-shadow with-transitions" t-att-id="'graph_question_'+str(question.id)">
|
||||
<!-- Dynamic script for Multi bar chart -->
|
||||
<script>
|
||||
var question_<t t-esc="question.id"></t> = <t t-esc="get_graph_data(question)"></t>;
|
||||
nv.addGraph(function(){
|
||||
var chart = nv.models.multiBarChart()
|
||||
.x(function(d) { return d.text })
|
||||
.y(function(d) { return d.count })
|
||||
.staggerLabels(true);
|
||||
|
||||
// Replacing Library's Default Tooltip with our Custom One
|
||||
chart.tooltip(function(key, x, y, e, graph) {
|
||||
return '<h5 class="panel-primary"><div class="panel-heading">' + x + '</div></h5>' +
|
||||
'<p>'+ '<b>Responses : </b>' + key + '</p>' +
|
||||
'<p>' + "<b>Total Vote : </b>" + y + '</p>'
|
||||
})
|
||||
|
||||
// Custom Tick fuction for replacing long text with '...'
|
||||
var customtick_function = function(d){
|
||||
if(this.hasOwnProperty('window') || d.length < 25){
|
||||
return d;
|
||||
}
|
||||
else{
|
||||
return d.slice(0,25)+'...';
|
||||
}
|
||||
}
|
||||
|
||||
chart.xAxis
|
||||
.tickFormat(customtick_function);
|
||||
chart.yAxis
|
||||
.tickFormat(d3.format('d'));
|
||||
|
||||
d3.select('#graph_question_<t t-esc="question.id"></t> svg')
|
||||
.datum(question_<t t-esc="question.id"></t>)
|
||||
.transition().duration(500).call(chart);
|
||||
|
||||
nv.utils.windowResize(chart.update);
|
||||
return chart;
|
||||
});
|
||||
</script>
|
||||
<!-- svg element for drawing Multibar chart -->
|
||||
<svg style="height:20em"></svg>
|
||||
</div>
|
||||
<div class="tab-pane" t-att-id="'data_question_'+str(question.id)">
|
||||
<table class="table table-hover text-right">
|
||||
<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>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="matrix_result['rows']" t-as="row_id">
|
||||
<td>
|
||||
<span t-esc="matrix_result['rows'][row_id]"></span>
|
||||
</td>
|
||||
<td 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'],2)"></span> %
|
||||
<span class="label label-primary" t-esc="matrix_result['result'][(row_id,answer_id)]"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template id="result_number" name="Number Result">
|
||||
<t t-set="number_result" t-value="prepare_result(question)" />
|
||||
<span class="pull-right">
|
||||
<span class="label label-default only_right_radius">Maximum </span> <span class="label label-success only_left_radius" t-esc="number_result['max']"></span>
|
||||
<span class="label label-default only_right_radius">Minimum </span> <span class="label label-danger only_left_radius" t-esc="number_result['min']"></span>
|
||||
<span class="label label-default only_right_radius">Average </span> <span class="label label-warning only_left_radius" t-esc="number_result['average']"></span>
|
||||
</span>
|
||||
<h3>Most Common <span t-esc="len(number_result['most_comman'])"></span></h3>
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>User Responce</th>
|
||||
<th>Occurence</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="number_result['most_comman']" t-as="row">
|
||||
<td>
|
||||
<span t-esc="row[0]"></span>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="row[1]"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
</data>
|
||||
</openerp>
|
|
@ -130,6 +130,7 @@
|
|||
<field name="visible_to_user" />
|
||||
<field name="auth_required" />
|
||||
<field name="public_url" widget="url" attrs="{'invisible': [('visible_to_user', '=', False)]}"/>
|
||||
<field name="result_url" widget="url"/>
|
||||
</group>
|
||||
<field name="description" />
|
||||
<notebook>
|
||||
|
|
Loading…
Reference in New Issue