Add a trend line to Functest reporting 57/22557/4
authorMorgan Richomme <morgan.richomme@orange.com>
Thu, 29 Sep 2016 13:39:42 +0000 (15:39 +0200)
committerjose.lausuch <jose.lausuch@ericsson.com>
Thu, 29 Sep 2016 15:21:03 +0000 (17:21 +0200)
And use d3js lib rather than static images

JIRA: FUNCTEST-483

Change-Id: I3c372a8e18fc366dfe510ab551596640fb6d9242
Signed-off-by: Morgan Richomme <morgan.richomme@orange.com>
utils/test/reporting/functest/default.css [deleted file]
utils/test/reporting/functest/reporting-status.py
utils/test/reporting/functest/reportingConf.py
utils/test/reporting/functest/template/index-status-tmpl.html
utils/test/reporting/js/gauge.js [new file with mode: 0644]
utils/test/reporting/js/trend.js [new file with mode: 0644]

diff --git a/utils/test/reporting/functest/default.css b/utils/test/reporting/functest/default.css
deleted file mode 100644 (file)
index 897c3b1..0000000
+++ /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)
-}
index 90699bd..9df6996 100755 (executable)
@@ -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" %
index e1c4b61..1c9a2ac 100644 (file)
@@ -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
index 67c2349..2beb912 100644 (file)
@@ -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">
             <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/utils/test/reporting/js/gauge.js b/utils/test/reporting/js/gauge.js
new file mode 100644 (file)
index 0000000..4cad16c
--- /dev/null
@@ -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/utils/test/reporting/js/trend.js b/utils/test/reporting/js/trend.js
new file mode 100644 (file)
index 0000000..ec48e75
--- /dev/null
@@ -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;
+}