summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMorgan Richomme <morgan.richomme@orange.com>2016-09-29 15:39:42 +0200
committerjose.lausuch <jose.lausuch@ericsson.com>2016-09-29 17:21:03 +0200
commit007411f6c79773a84e02db3da032a39f9314b705 (patch)
tree68e0de2f411ca42ec8efa80cf233e43bca3efa2c
parentaf41ba9498f820b127387ffa723119a0e2b4b33d (diff)
Add a trend line to Functest reporting
And use d3js lib rather than static images JIRA: FUNCTEST-483 Change-Id: I3c372a8e18fc366dfe510ab551596640fb6d9242 Signed-off-by: Morgan Richomme <morgan.richomme@orange.com>
-rw-r--r--reporting/functest/default.css77
-rwxr-xr-xreporting/functest/reporting-status.py7
-rw-r--r--reporting/functest/reportingConf.py1
-rw-r--r--reporting/functest/template/index-status-tmpl.html96
-rw-r--r--reporting/js/gauge.js165
-rw-r--r--reporting/js/trend.js68
6 files changed, 299 insertions, 115 deletions
diff --git a/reporting/functest/default.css b/reporting/functest/default.css
deleted file mode 100644
index 897c3b1..0000000
--- a/reporting/functest/default.css
+++ /dev/null
@@ -1,77 +0,0 @@
-.panel-header-item {
- position: relative;
- display: inline-block;
- padding-left: 17px;
- padding-right: 17px;
-}
-
-.panel-pod-name {
- margin-top: 10px;
- margin-right: 27px;
- float:right;
- padding: 6px;
-}
-
-.panel-default > .panel-heading .badge {
- background-color: #007e88;
- position: relative;
- display: inline-block;
-}
-
-.panel-default > .panel-heading .progress-bar {
- height: 100%;
- position: absolute;
- left: 0;
- top: 0;
- width: 100%;
- background-color: #0095a2
-}
-.panel-default > .panel-heading h4 {
- color: white;
-}
-
-.panel-default > .panel-heading {
- background-color: #00ADBB;
- overflow: hidden;
- position: relative;
- width: 100%;
-}
-
-th{
- text-align: center;
-}
-
-td{
- text-align: center;
-}
-
-.tr-danger {
- background-color: #177870;
- color: white;
-}
-
-.btn-more {
- color: white;
- background-color: #0095a2;
-}
-
-h1 {
- display: block;
- font-size: 2em;
- margin-top: 0.67em;
- margin-bottom: 0.67em;
- margin-left: 0;
- margin-right: 0;
- font-weight: bold;
-}
-
-h2 {
- display: block;
- font-size: 1.5em;
- margin-top: 0.83em;
- margin-bottom: 0.83em;
- margin-left: 0;
- margin-right: 0;
- font-weight: bold;
- color:rgb(128, 128, 128)
-}
diff --git a/reporting/functest/reporting-status.py b/reporting/functest/reporting-status.py
index 90699bd..9df6996 100755
--- a/reporting/functest/reporting-status.py
+++ b/reporting/functest/reporting-status.py
@@ -184,8 +184,13 @@ for version in conf.versions:
scenario_criteria = conf.MAX_SCENARIO_CRITERIA
s_score = str(scenario_score) + "/" + str(scenario_criteria)
- s_score_percent = float(
+ s_score_percent = 0.0
+ try:
+ s_score_percent = float(
scenario_score) / float(scenario_criteria) * 100
+ except:
+ logger.error("cannot calculate the score percent")
+
s_status = "KO"
if scenario_score < scenario_criteria:
logger.info(">>>> scenario not OK, score = %s/%s" %
diff --git a/reporting/functest/reportingConf.py b/reporting/functest/reportingConf.py
index e1c4b61..1c9a2ac 100644
--- a/reporting/functest/reportingConf.py
+++ b/reporting/functest/reportingConf.py
@@ -13,7 +13,6 @@ installers = ["apex", "compass", "fuel", "joid"]
# list of test cases declared in testcases.yaml but that must not be
# taken into account for the scoring
blacklist = ["ovno", "security_scan"]
-# versions = ["brahmaputra", "master"]
versions = ["master", "colorado"]
PERIOD = 10
MAX_SCENARIO_CRITERIA = 50
diff --git a/reporting/functest/template/index-status-tmpl.html b/reporting/functest/template/index-status-tmpl.html
index 67c2349..2beb912 100644
--- a/reporting/functest/template/index-status-tmpl.html
+++ b/reporting/functest/template/index-status-tmpl.html
@@ -3,17 +3,65 @@
<meta charset="utf-8">
<!-- Bootstrap core CSS -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet">
- <link href="default.css" rel="stylesheet">
+ <link href="../../../css/default.css" rel="stylesheet">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
- <script type="text/javascript">
- $(document).ready(function (){
- $(".btn-more").click(function() {
- $(this).hide();
- $(this).parent().find(".panel-default").show();
+ <script type="text/javascript" src="http://d3js.org/d3.v2.min.js"></script>
+ <script type="text/javascript" src="../../../js/gauge.js"></script>
+ <script type="text/javascript" src="../../../js/trend.js"></script>
+ <script>
+ function onDocumentReady() {
+ // Gauge management
+ {% for scenario in scenario_stats.iteritems() -%}
+ var gaugeScenario{{loop.index}} = gauge('#gaugeScenario{{loop.index}}');
+ {%- endfor %}
+
+ // assign success rate to the gauge
+ function updateReadings() {
+ {% for scenario,iteration in scenario_stats.iteritems() -%}
+ gaugeScenario{{loop.index}}.update({{scenario_results[scenario].getScorePercent()}});
+ {%- endfor %}
+ }
+ updateReadings();
+ }
+
+ // trend line management
+ d3.csv("./scenario_history.txt", function(data) {
+ // ***************************************
+ // Create the trend line
+ {% for scenario,iteration in scenario_stats.iteritems() -%}
+ // for scenario {{scenario}}
+ // Filter results
+ var trend{{loop.index}} = data.filter(function(row) {
+ return row["scenario"]=="{{scenario}}" && row["installer"]=="{{installer}}";
+ })
+ // Parse the date
+ trend{{loop.index}}.forEach(function(d) {
+ d.date = parseDate(d.date);
+ d.score = +d.score
});
- })
- </script>
+ // Draw the trend line
+ var mytrend = trend("#trend_svg{{loop.index}}",trend{{loop.index}})
+ // ****************************************
+ {%- endfor %}
+ });
+ if ( !window.isLoaded ) {
+ window.addEventListener("load", function() {
+ onDocumentReady();
+ }, false);
+ } else {
+ onDocumentReady();
+ }
+</script>
+<script type="text/javascript">
+$(document).ready(function (){
+ $(".btn-more").click(function() {
+ $(this).hide();
+ $(this).parent().find(".panel-default").show();
+ });
+})
+</script>
+
</head>
<body>
<div class="container">
@@ -40,41 +88,17 @@
<div class="panel-heading"><h4><b>List of last scenarios ({{version}}) run over the last {{period}} days </b></h4></div>
<table class="table">
<tr>
- <th width="60%">Scenario</th>
+ <th width="40%">Scenario</th>
<th width="20%">Status</th>
+ <th width="20%">Trend</th>
<th width="10%">Score</th>
<th width="10%">Iteration</th>
</tr>
{% for scenario,iteration in scenario_stats.iteritems() -%}
<tr class="tr-ok">
<td><a href={{scenario_results[scenario].getUrlLastRun()}}>{{scenario}}</a></td>
- <td>{%if scenario_results[scenario].getScorePercent() < 8.3 -%}
- <img src="../../img/gauge_0.png">
- {%elif scenario_results[scenario].getScorePercent() < 16.7 -%}
- <img src="../../img/gauge_8.3.png">
- {%elif scenario_results[scenario].getScorePercent() < 25 -%}
- <img src="../../img/gauge_16.7.png">
- {%elif scenario_results[scenario].getScorePercent() < 33.3 -%}
- <img src="../../img/gauge_25.png">
- {%elif scenario_results[scenario].getScorePercent() < 41.7 -%}
- <img src="../../img/gauge_33.3.png">
- {%elif scenario_results[scenario].getScorePercent() < 50 -%}
- <img src="../../img/gauge_41.7.png">
- {%elif scenario_results[scenario].getScorePercent() < 58.3 -%}
- <img src="../../img/gauge_50.png">
- {%elif scenario_results[scenario].getScorePercent() < 66.7 -%}
- <img src="../../img/gauge_58.3.png">
- {%elif scenario_results[scenario].getScorePercent() < 75 -%}
- <img src="../../img/gauge_66.7.png">
- {%elif scenario_results[scenario].getScorePercent() < 83.3 -%}
- <img src="../../img/gauge_75.png">
- {%elif scenario_results[scenario].getScorePercent() < 91.7 -%}
- <img src="../../img/gauge_83.3.png">
- {%elif scenario_results[scenario].getScorePercent() < 100 -%}
- <img src="../../img/gauge_91.7.png">
- {%- else -%}
- <img src="../../img/gauge_100.png">
- {%- endif %}</td>
+ <td><div id="gaugeScenario{{loop.index}}"></div></td>
+ <td><div id="trend_svg{{loop.index}}"></div></td>
<td>{{scenario_results[scenario].getScore()}}</td>
<td>{{iteration}}</td>
</tr>
diff --git a/reporting/js/gauge.js b/reporting/js/gauge.js
new file mode 100644
index 0000000..4cad16c
--- /dev/null
+++ b/reporting/js/gauge.js
@@ -0,0 +1,165 @@
+// ******************************************
+// Gauge for reporting
+// Each scenario has a score
+// We use a gauge to indicate the trust level
+// ******************************************
+var gauge = function(container) {
+ var that = {};
+ var config = {
+ size : 150,
+ clipWidth : 250,
+ clipHeight : 100,
+ ringInset : 20,
+ ringWidth : 40,
+
+ pointerWidth : 7,
+ pointerTailLength : 5,
+ pointerHeadLengthPercent : 0.8,
+
+ minValue : 0,
+ maxValue : 100,
+
+ minAngle : -90,
+ maxAngle : 90,
+
+ transitionMs : 4000,
+
+ majorTicks : 7,
+ labelFormat : d3.format(',g'),
+ labelInset : 10,
+
+ arcColorFn : d3.interpolateHsl(d3.rgb('#ff0000'), d3.rgb('#00ff00'))
+ };
+
+
+var range = undefined;
+var r = undefined;
+var pointerHeadLength = undefined;
+var value = 0;
+
+var svg = undefined;
+var arc = undefined;
+var scale = undefined;
+var ticks = undefined;
+var tickData = undefined;
+var pointer = undefined;
+
+var donut = d3.layout.pie();
+
+function deg2rad(deg) {
+ return deg * Math.PI / 180;
+}
+
+function newAngle(d) {
+ var ratio = scale(d);
+ var newAngle = config.minAngle + (ratio * range);
+ return newAngle;
+}
+
+function configure() {
+ range = config.maxAngle - config.minAngle;
+ r = config.size / 2;
+ pointerHeadLength = Math.round(r * config.pointerHeadLengthPercent);
+
+ // a linear scale that maps domain values to a percent from 0..1
+ scale = d3.scale.linear()
+ .range([0,1])
+ .domain([config.minValue, config.maxValue]);
+
+ ticks = scale.ticks(config.majorTicks);
+ tickData = d3.range(config.majorTicks).map(function() {return 1/config.majorTicks;});
+
+ arc = d3.svg.arc()
+ .innerRadius(r - config.ringWidth - config.ringInset)
+ .outerRadius(r - config.ringInset)
+ .startAngle(function(d, i) {
+ var ratio = d * i;
+ return deg2rad(config.minAngle + (ratio * range));
+ })
+ .endAngle(function(d, i) {
+ var ratio = d * (i+1);
+ return deg2rad(config.minAngle + (ratio * range));
+ });
+}
+that.configure = configure;
+
+function centerTranslation() {
+ return 'translate('+r +','+ r +')';
+}
+
+function isRendered() {
+ return (svg !== undefined);
+}
+that.isRendered = isRendered;
+
+function render(newValue) {
+ svg = d3.select(container)
+ .append('svg:svg')
+ .attr('class', 'gauge')
+ .attr('width', config.clipWidth)
+ .attr('height', config.clipHeight);
+
+ var centerTx = centerTranslation();
+
+ var arcs = svg.append('g')
+ .attr('class', 'arc')
+ .attr('transform', centerTx);
+
+ arcs.selectAll('path')
+ .data(tickData)
+ .enter().append('path')
+ .attr('fill', function(d, i) {
+ return config.arcColorFn(d * i);
+ })
+ .attr('d', arc);
+
+ var lg = svg.append('g')
+ .attr('class', 'label')
+ .attr('transform', centerTx);
+ lg.selectAll('text')
+ .data(ticks)
+ .enter().append('text')
+ .attr('transform', function(d) {
+ var ratio = scale(d);
+ var newAngle = config.minAngle + (ratio * range);
+ return 'rotate(' +newAngle +') translate(0,' +(config.labelInset - r) +')';
+ })
+ .text(config.labelFormat);
+
+ var lineData = [ [config.pointerWidth / 2, 0],
+ [0, -pointerHeadLength],
+ [-(config.pointerWidth / 2), 0],
+ [0, config.pointerTailLength],
+ [config.pointerWidth / 2, 0] ];
+ var pointerLine = d3.svg.line().interpolate('monotone');
+ var pg = svg.append('g').data([lineData])
+ .attr('class', 'pointer')
+ .attr('transform', centerTx);
+
+ pointer = pg.append('path')
+ .attr('d', pointerLine/*function(d) { return pointerLine(d) +'Z';}*/ )
+ .attr('transform', 'rotate(' +config.minAngle +')');
+
+ update(newValue === undefined ? 0 : newValue);
+}
+that.render = render;
+
+function update(newValue, newConfiguration) {
+ if ( newConfiguration !== undefined) {
+ configure(newConfiguration);
+ }
+ var ratio = scale(newValue);
+ var newAngle = config.minAngle + (ratio * range);
+ pointer.transition()
+ .duration(config.transitionMs)
+ .ease('elastic')
+ .attr('transform', 'rotate(' +newAngle +')');
+}
+that.update = update;
+
+configure();
+
+render();
+
+return that;
+};
diff --git a/reporting/js/trend.js b/reporting/js/trend.js
new file mode 100644
index 0000000..ec48e75
--- /dev/null
+++ b/reporting/js/trend.js
@@ -0,0 +1,68 @@
+// ******************************************
+// Trend line for reporting
+// based on scenario_history.txt
+// where data looks like
+// date,scenario,installer,detail,score
+// 2016-09-22 13:12,os-nosdn-fdio-noha,apex,4/12,33.0
+// 2016-09-22 13:13,os-odl_l2-fdio-noha,apex,12/15,80.0
+// 2016-09-22 13:13,os-odl_l2-sfc-noha,apex,18/24,75.0
+// .....
+// ******************************************
+// Set the dimensions of the canvas / graph
+var trend_margin = {top: 20, right: 30, bottom: 50, left: 40},
+ trend_width = 300 - trend_margin.left - trend_margin.right,
+ trend_height = 130 - trend_margin.top - trend_margin.bottom;
+
+// Parse the date / time
+var parseDate = d3.time.format("%Y-%m-%d %H:%M").parse;
+
+// Set the ranges
+var trend_x = d3.time.scale().range([0, trend_width]);
+var trend_y = d3.scale.linear().range([trend_height, 0]);
+
+// Define the axes
+var trend_xAxis = d3.svg.axis().scale(trend_x)
+ .orient("bottom").ticks(2).tickFormat(d3.time.format("%m-%d"));
+
+var trend_yAxis = d3.svg.axis().scale(trend_y)
+ .orient("left").ticks(2);
+
+// Define the line
+var valueline = d3.svg.line()
+ .x(function(d) { return trend_x(d.date); })
+ .y(function(d) { return trend_y(d.score); });
+
+var trend = function(container, trend_data) {
+
+ var trend_svg = d3.select(container)
+ .append("svg")
+ .attr("width", trend_width + trend_margin.left + trend_margin.right)
+ .attr("height", trend_height + trend_margin.top + trend_margin.bottom)
+ .append("g")
+ .attr("transform",
+ "translate(" + trend_margin.left + "," + trend_margin.top + ")");
+
+ // Scale the range of the data
+ trend_x.domain(d3.extent(trend_data, function(d) { return d.date; }));
+ trend_y.domain([0, d3.max(trend_data, function(d) { return d.score; })]);
+
+ // Add the X Axis
+ trend_svg.append("g")
+ .attr("class", "x axis")
+ .attr("transform", "translate(0," + trend_height + ")")
+ .call(trend_xAxis);
+
+ // Add the Y Axis
+ trend_svg.append("g")
+ .attr("class", "y axis")
+ .call(trend_yAxis);
+
+ // Add the valueline path.
+ trend_svg.append("path")
+ .attr("class", "line")
+ .attr("d", valueline(trend_data))
+ .attr("stroke", "steelblue")
+ .attr("fill", "none");
+
+ return trend;
+}