]> jspc29.x-matter.uni-frankfurt.de Git - mvd_epics.git/commitdiff
DASH: first working version of sparklines
authorPhilipp Klaus <klaus@physik.uni-frankfurt.de>
Wed, 23 Aug 2017 11:04:28 +0000 (13:04 +0200)
committerPhilipp Klaus <klaus@physik.uni-frankfurt.de>
Wed, 23 Aug 2017 11:04:28 +0000 (13:04 +0200)
python_suite/dashboard/dashboard.py
python_suite/dashboard/static/js/sparkline.js [new file with mode: 0644]
python_suite/dashboard/views/pv_overview.jinja2

index 927a688427996a9ee38499c1aa3ba57f6bca8b66..1420280cd4466c05d8c0110f9ab788bf064c451f 100755 (executable)
@@ -114,6 +114,10 @@ def gview(name):
 def index():
     return CONFIG
 
+@route('/api/history/<name>.json')
+def api_history(name):
+    return {'history': HISTORY[name]}
+
 @route('/static/<path:path>')
 def static_content(path):
     return static_file(path, root='./static/')
diff --git a/python_suite/dashboard/static/js/sparkline.js b/python_suite/dashboard/static/js/sparkline.js
new file mode 100644 (file)
index 0000000..94cc089
--- /dev/null
@@ -0,0 +1,139 @@
+// https://github.com/DKirwan/reusable-d3-sparkline/blob/master/sparkline.js
+
+// Built to closely follow reusable charts best practices doc: http://bost.ocks.org/mike/chart/
+
+function sparkline() {
+  // defaults
+  var width = 200;
+  var height = 40;
+  var dataSource = '';
+  var dataSourceType = '';
+  var selector = 'body';
+  var gradientColors = ['green', 'orange', 'red'];
+
+  // setters and getters
+  chart.width = function(value) {
+    if (!arguments.length) return width;
+    width = value;
+    return chart;
+  };
+
+  chart.height = function(value) {
+    if (!arguments.length) return height;
+    height = value;
+    return chart;
+  };
+
+  chart.dataSource = function(value) {
+    if (!arguments.length) return dataSource;
+    dataSource = value;
+    return chart;
+  };
+
+  chart.dataSourceType = function(value) {
+    if (!arguments.length) return dataSourceType;
+    dataSourceType = value;
+    return chart;
+  };
+
+  chart.selector = function(value) {
+    if (!arguments.length) return selector;
+    selector = value;
+    return chart;
+  };
+
+  chart.gradientColors = function(value) {
+    if (!arguments.length) return gradientColors;
+    gradientColors = value;
+    return chart;
+  };
+
+  function chart() {
+    var margin = {
+      top: 5,
+      right: 5,
+      bottom: 5,
+      left: 5
+    };
+
+    var width = chart.width();
+    var height = chart.height();
+    var gradient;
+
+    var x = d3.time.scale().range([0, width]);
+    var y = d3.scale.linear().range([height, 0]);
+
+    // Define the line
+    var valueline = d3.svg.line()
+      .x(function (d) { return x(d[0]); })
+      .y(function (d) { return y(d[1]); });
+
+    // Adds the svg canvas to the selector - 'body' by default
+    var svg = d3.select(chart.selector())
+      .append('svg')
+        .attr('width', width + margin.left + margin.right)
+        .attr('height', height)
+      .append('g')
+        .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
+
+    if (chart.gradientColors() && chart.gradientColors().length) {
+      // this defines the gradient used
+      gradient = svg.append("defs")
+        .append("linearGradient")
+          .attr("id", "gradient")
+          .attr("x1", "0%")   // starting x point
+          .attr("y1", "0%")   // starting y point
+          .attr("x2", "0%")   // ending x point
+          .attr("y2", "100%") // ending y point
+          .attr("spreadMethod", "pad");
+
+      chart.gradientColors().forEach(function (color, index) {
+        gradient.append("stop")
+          .attr("offset", ((index * 100) / chart.gradientColors().length) + '%')
+          .attr("stop-color", color)
+          .attr("stop-opacity", 0.7);
+      })
+    }
+
+    if (chart.dataSourceType().toLowerCase() === 'csv') {
+      d3.csv(chart.dataSource(), drawChart);
+    } else if (chart.dataSourceType().toLowerCase() === 'tsv') {
+      d3.tsv(chart.dataSource(), drawChart);
+    } else {
+      d3.json(chart.dataSource(), function(error, json) {
+        if (error) return drawChart(error, json);
+        drawChart(error, json.history);
+      });
+    }
+
+    /*
+    * formats chart data and appends the sparkline
+    */
+    function drawChart(error, data) {
+      if (error) { console.log(error); return; }
+
+      data.forEach(function (d) {
+        d[0] = new Date(d[0]);
+        d[1] = +d[1];
+      });
+
+      // Scale the range of the data
+      x.domain(d3.extent(data, function (d) { return d[0]; }));
+      y.domain([0, d3.max(data, function (d) { return d[1]; })]);
+
+      // Add the valueline path.
+      svg.append('path')
+        .attr('class', 'line')
+        .attr('stroke', function () {
+          if (gradient) {
+            return 'url(#gradient)';
+          }
+          return '#444444';
+        })
+        .attr('d', valueline(data));
+
+    }
+  }
+
+  return chart;
+}
index ac40c002887bbd8ecd4ce9362403d9ca1625d9f2..d0ec60aa479cc3d86f7dace0405132b8935f67f3 100644 (file)
@@ -4,6 +4,20 @@
 
 {% block header %}
 <meta http-equiv="refresh" content="5">
+<style>
+    path {
+    stroke-width: 1;
+    fill: none;
+    }
+    label {
+    float: left;
+    margin-top: 5px;
+    margin-right: 5px;
+    font-family: sans-serif;
+    }
+</style>
+<script src="http://d3js.org/d3.v3.min.js"></script>
+<script src="/static/js/sparkline.js"></script>
 {% endblock %}
 
 {% block content %}
                                <h2>{{ this_group.name }}</h2>
                                         <table style="width:100%">
                                                <colgroup>
-                                                       <col span="1" style="width: 60%;">
-                                                       <col span="1" style="width: 25%;">
+                                                       <col span="1" style="width: 55%;">
                                                        <col span="1" style="width: 15%;">
+                                                       <col span="1" style="width: 10%;">
+                                                       <col span="1" style="width: 20%;">
                                                </colgroup>
                                                <tr>
                                                        <th>Process Variable</th>
                                                        <th>Value</th>
                                                        <th>Unit</th>
+                                                       <th>Sparkline</th>
                                                </tr>
                                                {% for pv_name in this_group.PVs %}
                                                {% set PV = config.PVs[config.PV_lookup[pv_name]] %}
@@ -43,6 +59,7 @@
                                                                {% endif %}
                                                        </td>
                                                        <td>{{ PV.unit }}</td>
+                                                       <td><div id="sparkline-{{ PV.name|replace(':','-') }}" class="sparkline"></div></td>
                                                </tr>
                                                {% endfor %}
                                        </table>
                        {% endfor %}
                </div>
 {% endblock %}
+
+
+{% block js_end_of_page %}
+
+function updateSparklines() {
+  var sparklineItems = document.getElementsByClassName("sparkline");
+  [].forEach.call(sparklineItems, function (el) {
+    var el_id = el.id;
+    var pvName = el_id.substring(el_id.indexOf("-")+1, el_id.length);
+    var pvName = pvName.replace(/-/g, ':');
+
+    var sparklineChart = sparkline()
+                         .width(155)
+                         .height(50)
+                         .gradientColors(['green', 'orange', 'red']) // top -> bottom
+                         .dataSource('/api/history/' + pvName + '.json')
+                         .dataSourceType('JSON')
+                         .selector('#'+el.id);
+    sparklineChart();  // render the chart
+  });
+};
+
+$(function() {
+  // on page load
+  updateSparklines();
+});
+{% endblock %}