1<%--
2  ~ Copyright (c) 2016 Google Inc. All Rights Reserved.
3  ~
4  ~ Licensed under the Apache License, Version 2.0 (the "License"); you
5  ~ may not use this file except in compliance with the License. You may
6  ~ obtain a copy of the License at
7  ~
8  ~     http://www.apache.org/licenses/LICENSE-2.0
9  ~
10  ~ Unless required by applicable law or agreed to in writing, software
11  ~ distributed under the License is distributed on an "AS IS" BASIS,
12  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13  ~ implied. See the License for the specific language governing
14  ~ permissions and limitations under the License.
15  --%>
16<%@ page contentType='text/html;charset=UTF-8' language='java' %>
17<%@ taglib prefix='fn' uri='http://java.sun.com/jsp/jstl/functions' %>
18<%@ taglib prefix='c' uri='http://java.sun.com/jsp/jstl/core'%>
19
20<html>
21  <%@ include file="header.jsp" %>
22  <link type='text/css' href='/css/datepicker.css' rel='stylesheet'>
23  <link type='text/css' href='/css/show_graph.css' rel='stylesheet'>
24  <link rel='stylesheet' href='https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.0/jquery-ui.css'>
25  <script type='text/javascript' src='https://www.gstatic.com/charts/loader.js'></script>
26  <script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js'></script>
27  <body>
28    <script type='text/javascript'>
29        google.charts.load('current', {packages:['corechart', 'table', 'line']});
30        google.charts.setOnLoadCallback(drawAllGraphs);
31
32        ONE_DAY = 86400000000;
33        MICRO_PER_MILLI = 1000;
34        N_BUCKETS = 200;
35
36        var graphs = ${graphs};
37
38        $(function() {
39            $('select').material_select();
40            var date = $('#date').datepicker({
41                showAnim: 'slideDown',
42                maxDate: new Date()
43            });
44            date.datepicker('setDate', new Date(${endTime} / MICRO_PER_MILLI));
45            $('#load').click(load);
46            $('#outlier-select').change(drawAllGraphs);
47        });
48
49        // Draw all graphs.
50        function drawAllGraphs() {
51            $('#profiling-container').empty();
52            var percentileIndex = Number($('#outlier-select').val());
53
54            // Get histogram extrema
55            var histMin = null;
56            var histMax = null;
57            graphs.forEach(function(g) {
58                if (g.type != 'HISTOGRAM') return;
59                var minVal;
60                var maxVal;
61                if (percentileIndex == -1) {
62                    minVal = g.min;
63                    maxVal = g.max;
64                } else {
65                    minVal = g.percentile_values[percentileIndex];
66                    var endIndex = g.percentiles.length - percentileIndex - 1
67                    maxVal = g.percentile_values[endIndex];
68                }
69                if (!histMin || minVal < histMin) histMin = minVal;
70                if (!histMax || maxVal > histMax) histMax = maxVal;
71            });
72
73            graphs.forEach(function(graph) {
74                if (graph.type == 'LINE_GRAPH') drawLineGraph(graph);
75                else if (graph.type == 'HISTOGRAM')
76                    drawHistogram(graph, histMin, histMax);
77            });
78        }
79
80       /**
81        * Draw a line graph.
82        *
83        * Args:
84        *     lineGraph: a JSON object containing the following fields:
85        *                - name: the name of the graph
86        *                - values: an array of numbers
87        *                - ticks: an array of strings to use as x-axis labels
88        *                - ids: an array of string labels for each point (e.g. the
89        *                       build info for the run that produced the point)
90        *                - x_label: the string label for the x axis
91        *                - y_label: the string label for the y axis
92        */
93        function drawLineGraph(lineGraph) {
94            if (!lineGraph.ticks || lineGraph.ticks.length < 1) {
95                return;
96            }
97            var title = 'Performance';
98            if (lineGraph.name) title += ' (' + lineGraph.name + ')';
99            lineGraph.ticks.forEach(function (label, i) {
100                lineGraph.values[i].unshift(label);
101            });
102            var data = new google.visualization.DataTable();
103            data.addColumn('string', lineGraph.x_label);
104            lineGraph.ids.forEach(function(id) {
105                data.addColumn('number', id);
106            });
107            data.addRows(lineGraph.values);
108            var options = {
109              chart: {
110                  title: title,
111                  subtitle: lineGraph.y_label
112              },
113              legend: { position: 'none' }
114            };
115            var container = $('<div class="row card center-align col s12 graph-wrapper"></div>');
116            container.appendTo('#profiling-container');
117            var chartDiv = $('<div class="col s12 graph"></div>');
118            chartDiv.appendTo(container);
119            var chart = new google.charts.Line(chartDiv[0]);
120            chart.draw(data, options);
121        }
122
123       /**
124        * Draw a histogram.
125        *
126        * Args:
127        *     hist: a JSON object containing the following fields:
128        *           - name: the name of the graph
129        *           - values: an array of numbers
130        *           - ids: an array of string labels for each point (e.g. the
131        *                  build info for the run that produced the point)
132        *           - x_label: the string label for the x axis
133        *           - y_label: the string label for the y axis
134        *     min: the minimum value to display
135        *     max: the maximum value to display
136        */
137        function drawHistogram(hist, min, max) {
138            if (!hist.values || hist.values.length == 0) return;
139            var title = 'Performance';
140            if (hist.name) title += ' (' + hist.name + ')';
141            var values = hist.values;
142            var histogramData = values.reduce(function(result, d, i) {
143                if (d <= max && d >= min) result.push([hist.ids[i], d]);
144                return result;
145            }, []);
146
147            var data = google.visualization.arrayToDataTable(histogramData, true);
148            var bucketSize = (max - min) / N_BUCKETS;
149
150            var options = {
151                title: title,
152                titleTextStyle: {
153                    color: '#757575',
154                    fontSize: 16,
155                    bold: false
156                },
157                legend: { position: 'none' },
158                colors: ['#4285F4'],
159                fontName: 'Roboto',
160                vAxis:{
161                    title: hist.y_label,
162                    titleTextStyle: {
163                        color: '#424242',
164                        fontSize: 12,
165                        italic: false
166                    },
167                    textStyle: {
168                        fontSize: 12,
169                        color: '#757575'
170                    },
171                },
172                hAxis: {
173                    title: hist.x_label,
174                    textStyle: {
175                        fontSize: 12,
176                        color: '#757575'
177                    },
178                    titleTextStyle: {
179                        color: '#424242',
180                        fontSize: 12,
181                        italic: false
182                    }
183                },
184                bar: { gap: 0 },
185                histogram: {
186                    minValue: min,
187                    maxValue: max,
188                    maxNumBuckets: N_BUCKETS,
189                    bucketSize: bucketSize
190                },
191                chartArea: {
192                    width: '100%',
193                    top: 40,
194                    left: 60,
195                    height: 375
196                }
197            };
198            var container = $('<div class="row card col s12 graph-wrapper"></div>');
199            container.appendTo('#profiling-container');
200
201            var chartDiv = $('<div class="col s12 graph"></div>');
202            chartDiv.appendTo(container);
203            var chart = new google.visualization.Histogram(chartDiv[0]);
204            chart.draw(data, options);
205
206            var tableDiv = $('<div class="col s12"></div>');
207            tableDiv.appendTo(container);
208
209            var tableHtml = '<table class="percentile-table"><thead><tr>';
210            hist.percentiles.forEach(function(p) {
211                tableHtml += '<th data-field="id">' + p + '%</th>';
212            });
213            tableHtml += '</tr></thead><tbody><tr>';
214            hist.percentile_values.forEach(function(v) {
215                tableHtml += '<td>' + v + '</td>';
216            });
217            tableHtml += '</tbody></table>';
218            $(tableHtml).appendTo(tableDiv);
219        }
220
221        // Reload the page.
222        function load() {
223            var endTime = $('#date').datepicker('getDate').getTime();
224            endTime = endTime + (ONE_DAY / MICRO_PER_MILLI) - 1;
225            var filterVal = $('#outlier-select').val();
226            var ctx = '${pageContext.request.contextPath}';
227            var link = ctx + '/show_graph?profilingPoint=${profilingPointName}' +
228                '&testName=${testName}' +
229                '&endTime=' + (endTime * MICRO_PER_MILLI) +
230                '&filterVal=' + filterVal;
231            if ($('#device-select').prop('selectedIndex') > 1) {
232                link += '&device=' + $('#device-select').val();
233            }
234            window.open(link,'_self');
235        }
236    </script>
237    <div id='download' class='fixed-action-btn'>
238      <a id='b' class='btn-floating btn-large red waves-effect waves-light'>
239        <i class='large material-icons'>file_download</i>
240      </a>
241    </div>
242    <div class='container wide'>
243      <div class='row card'>
244        <div id='header-container' class='valign-wrapper col s12'>
245          <div class='col s3 valign'>
246            <h5>Profiling Point:</h5>
247          </div>
248          <div class='col s9 right-align valign'>
249            <h5 class='profiling-name truncate'>${profilingPointName}</h5>
250          </div>
251        </div>
252        <div id='date-container' class='col s12'>
253          <c:set var='offset' value='${showFilterDropdown ? 0 : 2}' />
254          <c:if test='${showFilterDropdown}'>
255            <div id='outlier-select-wrapper' class='col s2'>
256              <select id='outlier-select'>
257                <option value='-1' ${filterVal eq -1 ? 'selected' : ''}>Show outliers</option>
258                <option value='0' ${filterVal eq 0 ? 'selected' : ''}>Filter outliers (1%)</option>
259                <option value='1' ${filterVal eq 1 ? 'selected' : ''}>Filter outliers (2%)</option>
260                <option value='2' ${filterVal eq 2 ? 'selected' : ''}>Filter outliers (5%)</option>
261              </select>
262            </div>
263          </c:if>
264          <div id='device-select-wrapper' class='input-field col s5 m3 offset-m${offset + 4} offset-s${offset}'>
265            <select id='device-select'>
266              <option value='' disabled>Select device</option>
267              <option value='0' ${empty selectedDevice ? 'selected' : ''}>All Devices</option>
268              <c:forEach items='${devices}' var='device' varStatus='loop'>
269                <option value=${device} ${selectedDevice eq device ? 'selected' : ''}>${device}</option>
270              </c:forEach>
271            </select>
272          </div>
273          <input type='text' id='date' name='date' class='col s4 m2'>
274          <a id='load' class='btn-floating btn-medium red right waves-effect waves-light'>
275            <i class='medium material-icons'>cached</i>
276          </a>
277        </div>
278      </div>
279      <div id='profiling-container'>
280      </div>
281      <c:if test='${not empty error}'>
282        <div id='error-container' class='row card'>
283          <div class='col s10 offset-s1 center-align'>
284            <!-- Error in case of profiling data is missing -->
285            <h5>${error}</h5>
286          </div>
287        </div>
288      </c:if>
289    </div>
290    <%@ include file="footer.jsp" %>
291  </body>
292</html>
293