=== modified file 'dashboard_app/static/dashboard_app/css/image-report.css'
@@ -9,7 +9,7 @@
text-align: left;
}
.inner-table td, .inner-table th {
- min-width: 25ex;
+ min-width: 90px;
padding: 3px 4px;
border: thin solid black;
}
@@ -43,3 +43,47 @@
#go-to-bug-dialog {
text-align: center;
}
+#inner-container {
+ width: 80%;
+ height:250px;
+ margin:0 auto;
+ margin-left: 10px;
+ float: left;
+}
+#legend-container {
+ float: left;
+ margin-left: 20px;
+}
+#build_numbers_filter {
+ margin: 5px 0 0 20px;
+}
+#outer-container {
+ overflow: auto;
+}
+#filters {
+ border: 1px solid #000000;
+ clear: both;
+ margin: 20px 2px 20px 0px;
+}
+#tests_filter, #graph_type_filter, #target_goal_filter {
+ margin: 5px 0 10px 20px;
+}
+#filter_headline {
+ font-weight: bold;
+ font-size: 16px;
+ margin: 5px 0 0 10px;
+}
+#test_headline, #build_number_headline, #graph_type_headline, #target_goal_headline {
+ width: 120px;
+ float: left;
+}
+#tooltip {
+ position: absolute;
+ z-index: 3000;
+ border: 1px solid #111;
+ background-color: #eee;
+ color: #000;
+ padding: 5px;
+ opacity: 0.85;
+}
+#tooltip h3, #tooltip div { margin: 0; }
=== modified file 'dashboard_app/static/dashboard_app/js/image-report.js'
@@ -22,6 +22,260 @@
resultRow.css('height', Math.max(nameRowHeight, resultRowHeight));
}
}
+
+function update_filters(column_data, test_run_names) {
+ for (iter in column_data) {
+ build_number = column_data[iter]["number"].split('.')[0];
+ $("#build_number_start").append($('<option>', {
+ value: build_number,
+ text: build_number
+ }));
+ $("#build_number_end").append($('<option>', {
+ value: build_number,
+ text: build_number
+ }));
+ }
+ $("#build_number_end option:last").attr("selected", true);
+
+ for (iter in test_run_names) {
+ selected = false;
+ if (column_data[column_data.length-1]["test_runs"][test_run_names[iter]]) {
+ selected = true;
+ }
+ $("#test_select").append($('<option>', {
+ value: test_run_names[iter],
+ text: test_run_names[iter],
+ selected: selected
+ }));
+ }
+
+ // Use jStorage to load the filter values from browser.
+ load_filters();
+}
+
+function update_table(column_data, table_data, test_run_names) {
+
+ if ($("#test_select").val() == null) {
+ alert("Please select at least one test.");
+ return false;
+ }
+
+ if ($("#build_number_start").val() > $("#build_number_end").val()) {
+ alert("End build number must be greater then the start build number.");
+ return false;
+ }
+
+ // Create row headlines.
+ test_name_rows = "<tr><td>Date</td></tr>";
+ for (iter in test_run_names) {
+ if ($("#test_select").val().indexOf(test_run_names[iter]) >= 0) {
+ test_name = test_run_names[iter];
+ if (test_name.length > 20) {
+ test_name = test_name.substring(0,20) + "...";
+ }
+ test_name_rows += "<tr><td tooltip='" + test_run_names[iter] + "'>" + test_name + "</td></tr>";
+ }
+ }
+ $("#test-run-names tbody").html(test_name_rows);
+
+ // Create column headlines.
+ result_table_head = "<tr>";
+ for (iter in column_data) {
+ build_number = column_data[iter]["number"].split('.')[0];
+
+ if (build_number <= $("#build_number_end").val() && build_number >= $("#build_number_start").val()) {
+ link = '<a href="' + column_data[iter]["link"] + '">' + build_number.split(' ')[0] + '</a>';
+ result_table_head += "<th>" + link + "</th>";
+ }
+ }
+ result_table_head += "</tr>";
+ $("#results-table thead").html(result_table_head);
+
+ // Create table body
+ result_table_body = "<tr>";
+ for (iter in column_data) {
+ build_number = column_data[iter]["number"].split('.')[0];
+ build_date = column_data[iter]["date"].split('.')[0];
+
+ if (build_number <= $("#build_number_end").val() && build_number >= $("#build_number_start").val()) {
+ result_table_body += "<td>" + build_date.split(' ')[0] + "</td>";
+ }
+
+ }
+ result_table_body += "</tr>";
+
+ for (cnt in test_run_names) {
+ test = test_run_names[cnt];
+ if ($("#test_select").val().indexOf(test) >= 0) {
+ result_table_body += "<tr>";
+ row = table_data[test];
+
+ for (iter in row) {
+ build_number = column_data[iter]["number"].split('.')[0];
+ if (build_number <= $("#build_number_end").val() && build_number >= $("#build_number_start").val()) {
+ result_table_body += '<td class="' + row[iter]["cls"] + '" data-uuid="' + row[iter]["uuid"] + '">';
+ if (row[iter]["uuid"]) {
+ result_table_body += '<a href="' + row[iter]["link"] + '">' + row[iter]["passes"] + '/' + row[iter]["total"] + '</a>';
+ result_table_body += '<span class="bug-links">';
+ for (bug_id in row[iter]["bug_ids"]) {
+ bug = row[iter]["bug_ids"];
+ result_table_body += '<a class="bug-link" href="https://bugs.launchpad.net/bugs/' + bug[bug_id] + '" data-bug-id="' + bug[bug_id] + '">[' + bug[bug_id] + ']</a>';
+ }
+ result_table_body += '<a href="#" class="add-bug-link">[+]</a>';
+ result_table_body += '</span>';
+
+ } else {
+ result_table_body += "—";
+ }
+ result_table_body += "</td>";
+ }
+ }
+ result_table_body += "</tr>";
+ }
+ }
+
+ $("#results-table tbody").html(result_table_body);
+ $("#scroller").scrollLeft($("#scroller")[0].scrollWidth);
+
+ // Use jStorage to save filter values to the browser.
+ store_filters();
+ update_plot(column_data, table_data, test_run_names);
+ update_tooltips();
+}
+
+function update_tooltips() {
+ // Update tooltips on the remaining td's for the test names.
+ $("td", "#test-run-names").each(function () {
+ if ($(this).attr('tooltip')) {
+ $(this).tooltip({
+ bodyHandler: function() {
+ return $(this).attr('tooltip');
+ }
+ });
+ }
+ });
+}
+
+function store_filters() {
+ // Use jStorage to save filter values to the browser.
+
+ $.jStorage.set("target_goal", $("#target_goal").val());
+ $.jStorage.set("build_number_start", $("#build_number_start").val());
+ $.jStorage.set("build_number_end", $("#build_number_end").val());
+ $.jStorage.set("test_select", $("#test_select").val());
+ $.jStorage.set("graph_type", $('input:radio[name=graph_type]:checked').val());
+}
+
+function load_filters() {
+ // Use jStorage to load the filter values from browser.
+
+ if ($.jStorage.get("target_goal")) {
+ $("#target_goal").val($.jStorage.get("target_goal"));
+ }
+ if ($.jStorage.get("build_number_start")) {
+ $("#build_number_start").val($.jStorage.get("build_number_start"));
+ }
+ if ($.jStorage.get("build_number_end")) {
+ $("#build_number_end").val($.jStorage.get("build_number_end"));
+ }
+ if ($.jStorage.get("test_select")) {
+ $("#test_select").val($.jStorage.get("test_select"));
+ }
+ if ($.jStorage.get("graph_type")) {
+ if ($.jStorage.get("graph_type") == "number") {
+ $('input:radio[name=graph_type][value="number"]').attr("checked", true);
+ } else {
+ $('input:radio[name=graph_type][value="percentage"]').attr("checked", true);
+ }
+ }
+}
+
+function update_plot(column_data, table_data, test_run_names) {
+
+ // Get the plot data.
+
+ data = [];
+ for (test in table_data) {
+
+ if ($("#test_select").val().indexOf(test) >= 0) {
+ row_data = [];
+
+ row = table_data[test];
+ for (iter in row) {
+ build_number = column_data[iter]["number"].split('.')[0];
+ if (build_number <= $("#build_number_end").val() && build_number >= $("#build_number_start").val()) {
+ if (row[iter]["cls"]) {
+ if ($('input:radio[name=graph_type]:checked').val() == "number") {
+ row_data.push([iter, row[iter]["passes"]]);
+ } else {
+ if (isNaN(row[iter]["passes"]/row[iter]["total"])) {
+ row_data.push([iter, 0]);
+ } else {
+ row_data.push([iter, 100*row[iter]["passes"]/row[iter]["total"]]);
+ }
+ }
+ }
+ }
+ }
+ data.push({label: test, data: row_data});
+ }
+ }
+
+ // Add target goal dashed line to the plot.
+ if ($("#target_goal").val()) {
+ row_data = [];
+ row = table_data[test_run_names[0]];
+ for (iter in row) {
+ build_number = column_data[iter]["number"].split('.')[0];
+ if (build_number <= $("#build_number_end").val() && build_number >= $("#build_number_start").val()) {
+ row_data.push([iter, $("#target_goal").val()]);
+ }
+ }
+ data.push({data: row_data, dashes: {show: true}, lines: {show: false}, color: "#000000"});
+ }
+
+ // Get all build numbers to be used as tick labels.
+ build_numbers = [];
+ for (test in table_data) {
+ row = table_data[test];
+ for (iter in row) {
+ build_numbers.push(column_data[iter]["number"].split(' ')[0]);
+ }
+ // Each test has the same number of build numbers.
+ break;
+ }
+
+ var options = {
+ series: {
+ lines: { show: true },
+ points: { show: false }
+ },
+ legend: {
+ show: true,
+ position: "ne",
+ margin: 3,
+ container: "#legend-container",
+ },
+ xaxis: {
+ tickDecimals: 0,
+ tickFormatter: function (val, axis) {
+ return build_numbers[val];
+ },
+ },
+ yaxis: {
+ tickDecimals: 0,
+ },
+ };
+
+ if ($('input:radio[name=graph_type]:checked').val() == "percentage") {
+ options["yaxis"]["max"] = 100;
+ options["yaxis"]["min"] = 0;
+ }
+
+
+ $.plot($("#outer-container #inner-container"), data, options);
+}
+
$(window).ready(
function () {
// Hook up the event and run resize ASAP (looks jumpy in FF if you
@@ -139,3 +393,5 @@
// chromium if you don't do this).
$(window).load(_resize);
$(window).load(_fixRowHeights);
+$(window).load(function() {update_filters(columns, test_names);});
+$(window).load(function() {update_table(columns, chart_data, test_names);});
=== added file 'dashboard_app/static/dashboard_app/js/jquery.flot.dashes.min.js'
@@ -0,0 +1,29 @@
+/*
+ * jQuery.flot.dashes
+ *
+ * options = {
+ * series: {
+ * dashes: {
+ *
+ * // show
+ * // default: false
+ * // Whether to show dashes for the series.
+ * show: <boolean>,
+ *
+ * // lineWidth
+ * // default: 2
+ * // The width of the dashed line in pixels.
+ * lineWidth: <number>,
+ *
+ * // dashLength
+ * // default: 10
+ * // Controls the length of the individual dashes and the amount of
+ * // space between them.
+ * // If this is a number, the dashes and spaces will have that length.
+ * // If this is an array, it is read as [ dashLength, spaceLength ]
+ * dashLength: <number> or <array[2]>
+ * }
+ * }
+ * }
+ */
+(function($){function init(plot){plot.hooks.processDatapoints.push(function(plot,series,datapoints){if(!series.dashes.show)return;plot.hooks.draw.push(function(plot,ctx){var plotOffset=plot.getPlotOffset(),axisx=series.xaxis,axisy=series.yaxis;function plotDashes(xoffset,yoffset){var points=datapoints.points,ps=datapoints.pointsize,prevx=null,prevy=null,dashRemainder=0,dashOn=true,dashOnLength,dashOffLength;if(series.dashes.dashLength[0]){dashOnLength=series.dashes.dashLength[0];if(series.dashes.dashLength[1]){dashOffLength=series.dashes.dashLength[1]}else{dashOffLength=dashOnLength}}else{dashOffLength=dashOnLength=series.dashes.dashLength}ctx.beginPath();for(var i=ps;i<points.length;i+=ps){var x1=points[i-ps],y1=points[i-ps+1],x2=points[i],y2=points[i+1];if(x1==null||x2==null)continue;if(y1<=y2&&y1<axisy.min){if(y2<axisy.min)continue;x1=(axisy.min-y1)/(y2-y1)*(x2-x1)+x1;y1=axisy.min}else if(y2<=y1&&y2<axisy.min){if(y1<axisy.min)continue;x2=(axisy.min-y1)/(y2-y1)*(x2-x1)+x1;y2=axisy.min}if(y1>=y2&&y1>axisy.max){if(y2>axisy.max)continue;x1=(axisy.max-y1)/(y2-y1)*(x2-x1)+x1;y1=axisy.max}else if(y2>=y1&&y2>axisy.max){if(y1>axisy.max)continue;x2=(axisy.max-y1)/(y2-y1)*(x2-x1)+x1;y2=axisy.max}if(x1<=x2&&x1<axisx.min){if(x2<axisx.min)continue;y1=(axisx.min-x1)/(x2-x1)*(y2-y1)+y1;x1=axisx.min}else if(x2<=x1&&x2<axisx.min){if(x1<axisx.min)continue;y2=(axisx.min-x1)/(x2-x1)*(y2-y1)+y1;x2=axisx.min}if(x1>=x2&&x1>axisx.max){if(x2>axisx.max)continue;y1=(axisx.max-x1)/(x2-x1)*(y2-y1)+y1;x1=axisx.max}else if(x2>=x1&&x2>axisx.max){if(x1>axisx.max)continue;y2=(axisx.max-x1)/(x2-x1)*(y2-y1)+y1;x2=axisx.max}if(x1!=prevx||y1!=prevy){ctx.moveTo(axisx.p2c(x1)+xoffset,axisy.p2c(y1)+yoffset)}var ax1=axisx.p2c(x1)+xoffset,ay1=axisy.p2c(y1)+yoffset,ax2=axisx.p2c(x2)+xoffset,ay2=axisy.p2c(y2)+yoffset,dashOffset;function lineSegmentOffset(segmentLength){var c=Math.sqrt(Math.pow(ax2-ax1,2)+Math.pow(ay2-ay1,2));if(c<=segmentLength){return{deltaX:ax2-ax1,deltaY:ay2-ay1,distance:c,remainder:segmentLength-c}}else{var xsign=ax2>ax1?1:-1,ysign=ay2>ay1?1:-1;return{deltaX:xsign*Math.sqrt(Math.pow(segmentLength,2)/(1+Math.pow((ay2-ay1)/(ax2-ax1),2))),deltaY:ysign*Math.sqrt(Math.pow(segmentLength,2)-Math.pow(segmentLength,2)/(1+Math.pow((ay2-ay1)/(ax2-ax1),2))),distance:segmentLength,remainder:0}}}do{dashOffset=lineSegmentOffset(dashRemainder>0?dashRemainder:dashOn?dashOnLength:dashOffLength);if(dashOffset.deltaX!=0||dashOffset.deltaY!=0){if(dashOn){ctx.lineTo(ax1+dashOffset.deltaX,ay1+dashOffset.deltaY)}else{ctx.moveTo(ax1+dashOffset.deltaX,ay1+dashOffset.deltaY)}}dashOn=!dashOn;dashRemainder=dashOffset.remainder;ax1+=dashOffset.deltaX;ay1+=dashOffset.deltaY}while(dashOffset.distance>0);prevx=x2;prevy=y2}ctx.stroke()}ctx.save();ctx.translate(plotOffset.left,plotOffset.top);ctx.lineJoin='round';var lw=series.dashes.lineWidth,sw=series.shadowSize;if(lw>0&&sw>0){ctx.lineWidth=sw;ctx.strokeStyle="rgba(0,0,0,0.1)";var angle=Math.PI/18;plotDashes(Math.sin(angle)*(lw/2+sw/2),Math.cos(angle)*(lw/2+sw/2));ctx.lineWidth=sw/2;plotDashes(Math.sin(angle)*(lw/2+sw/4),Math.cos(angle)*(lw/2+sw/4))}ctx.lineWidth=lw;ctx.strokeStyle=series.color;if(lw>0){plotDashes(0,0)}ctx.restore()})})}$.plot.plugins.push({init:init,options:{series:{dashes:{show:false,lineWidth:2,dashLength:10}}},name:'dashes',version:'0.1'})})(jQuery)
=== added file 'dashboard_app/static/dashboard_app/js/jquery.tooltip.min.js'
@@ -0,0 +1,19 @@
+/*
+ * jQuery Tooltip plugin 1.3
+ *
+ * http://bassistance.de/jquery-plugins/jquery-plugin-tooltip/
+ * http://docs.jquery.com/Plugins/Tooltip
+ *
+ * Copyright (c) 2006 - 2008 Jörn Zaefferer
+ *
+ * $Id: jquery.tooltip.js 5741 2008-06-21 15:22:16Z joern.zaefferer $
+ *
+ * Dual licensed under the MIT and GPL licenses:
+ * http://www.opensource.org/licenses/mit-license.php
+ * http://www.gnu.org/licenses/gpl.html
+ */;(function($){var helper={},current,title,tID,IE=$.browser.msie&&/MSIE\s(5\.5|6\.)/.test(navigator.userAgent),track=false;$.tooltip={blocked:false,defaults:{delay:200,fade:false,showURL:true,extraClass:"",top:15,left:15,id:"tooltip"},block:function(){$.tooltip.blocked=!$.tooltip.blocked;}};$.fn.extend({tooltip:function(settings){settings=$.extend({},$.tooltip.defaults,settings);createHelper(settings);return this.each(function(){$.data(this,"tooltip",settings);this.tOpacity=helper.parent.css("opacity");this.tooltipText=this.title;$(this).removeAttr("title");this.alt="";}).mouseover(save).mouseout(hide).click(hide);},fixPNG:IE?function(){return this.each(function(){var image=$(this).css('backgroundImage');if(image.match(/^url\(["']?(.*\.png)["']?\)$/i)){image=RegExp.$1;$(this).css({'backgroundImage':'none','filter':"progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=crop, src='"+image+"')"}).each(function(){var position=$(this).css('position');if(position!='absolute'&&position!='relative')$(this).css('position','relative');});}});}:function(){return this;},unfixPNG:IE?function(){return this.each(function(){$(this).css({'filter':'',backgroundImage:''});});}:function(){return this;},hideWhenEmpty:function(){return this.each(function(){$(this)[$(this).html()?"show":"hide"]();});},url:function(){return this.attr('href')||this.attr('src');}});function createHelper(settings){if(helper.parent)return;helper.parent=$('<div id="'+settings.id+'"><h3></h3><div class="body"></div><div class="url"></div></div>').appendTo(document.body).hide();if($.fn.bgiframe)helper.parent.bgiframe();helper.title=$('h3',helper.parent);helper.body=$('div.body',helper.parent);helper.url=$('div.url',helper.parent);}function settings(element){return $.data(element,"tooltip");}function handle(event){if(settings(this).delay)tID=setTimeout(show,settings(this).delay);else
+show();track=!!settings(this).track;$(document.body).bind('mousemove',update);update(event);}function save(){if($.tooltip.blocked||this==current||(!this.tooltipText&&!settings(this).bodyHandler))return;current=this;title=this.tooltipText;if(settings(this).bodyHandler){helper.title.hide();var bodyContent=settings(this).bodyHandler.call(this);if(bodyContent.nodeType||bodyContent.jquery){helper.body.empty().append(bodyContent)}else{helper.body.html(bodyContent);}helper.body.show();}else if(settings(this).showBody){var parts=title.split(settings(this).showBody);helper.title.html(parts.shift()).show();helper.body.empty();for(var i=0,part;(part=parts[i]);i++){if(i>0)helper.body.append("<br/>");helper.body.append(part);}helper.body.hideWhenEmpty();}else{helper.title.html(title).show();helper.body.hide();}if(settings(this).showURL&&$(this).url())helper.url.html($(this).url().replace('http://','')).show();else
+helper.url.hide();helper.parent.addClass(settings(this).extraClass);if(settings(this).fixPNG)helper.parent.fixPNG();handle.apply(this,arguments);}function show(){tID=null;if((!IE||!$.fn.bgiframe)&&settings(current).fade){if(helper.parent.is(":animated"))helper.parent.stop().show().fadeTo(settings(current).fade,current.tOpacity);else
+helper.parent.is(':visible')?helper.parent.fadeTo(settings(current).fade,current.tOpacity):helper.parent.fadeIn(settings(current).fade);}else{helper.parent.show();}update();}function update(event){if($.tooltip.blocked)return;if(event&&event.target.tagName=="OPTION"){return;}if(!track&&helper.parent.is(":visible")){$(document.body).unbind('mousemove',update)}if(current==null){$(document.body).unbind('mousemove',update);return;}helper.parent.removeClass("viewport-right").removeClass("viewport-bottom");var left=helper.parent[0].offsetLeft;var top=helper.parent[0].offsetTop;if(event){left=event.pageX+settings(current).left;top=event.pageY+settings(current).top;var right='auto';if(settings(current).positionLeft){right=$(window).width()-left;left='auto';}helper.parent.css({left:left,right:right,top:top});}var v=viewport(),h=helper.parent[0];if(v.x+v.cx<h.offsetLeft+h.offsetWidth){left-=h.offsetWidth+20+settings(current).left;helper.parent.css({left:left+'px'}).addClass("viewport-right");}if(v.y+v.cy<h.offsetTop+h.offsetHeight){top-=h.offsetHeight+20+settings(current).top;helper.parent.css({top:top+'px'}).addClass("viewport-bottom");}}function viewport(){return{x:$(window).scrollLeft(),y:$(window).scrollTop(),cx:$(window).width(),cy:$(window).height()};}function hide(event){if($.tooltip.blocked)return;if(tID)clearTimeout(tID);current=null;var tsettings=settings(this);function complete(){helper.parent.removeClass(tsettings.extraClass).hide().css("opacity","");}if((!IE||!$.fn.bgiframe)&&tsettings.fade){if(helper.parent.is(':animated'))helper.parent.stop().fadeTo(tsettings.fade,0,complete);else
+helper.parent.stop().fadeOut(tsettings.fade,complete);}else
+complete();if(settings(this).fixPNG)helper.parent.unfixPNG();}})(jQuery);
\ No newline at end of file
=== added file 'dashboard_app/static/dashboard_app/js/jstorage.min.js'
@@ -0,0 +1,27 @@
+/*
+ * ----------------------------- JSTORAGE -------------------------------------
+ * Simple local storage wrapper to save data on the browser side, supporting
+ * all major browsers - IE6+, Firefox2+, Safari4+, Chrome4+ and Opera 10.5+
+ *
+ * Copyright (c) 2010 - 2012 Andris Reinman, andris.reinman@gmail.com
+ * Project homepage: www.jstorage.info
+ *
+ * Licensed under MIT-style license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+(function(){var JSTORAGE_VERSION="0.4.3",$=window.jQuery||window.$||(window.$={}),JSON={parse:window.JSON&&(window.JSON.parse||window.JSON.decode)||String.prototype.evalJSON&&function(str){return String(str).evalJSON()}||$.parseJSON||$.evalJSON,stringify:Object.toJSON||window.JSON&&(window.JSON.stringify||window.JSON.encode)||$.toJSON};if(!JSON.parse||!JSON.stringify){throw new Error("No JSON support found, include //cdnjs.cloudflare.com/ajax/libs/json2/20110223/json2.js to page")}var _storage={__jstorage_meta:{CRC32:{}}},_storage_service={jStorage:"{}"},_storage_elm=null,_storage_size=0,_backend=false,_observers={},_observer_timeout=false,_observer_update=0,_pubsub_observers={},_pubsub_last=+new Date(),_ttl_timeout,_XMLService={isXML:function(elm){var documentElement=(elm?elm.ownerDocument||elm:0).documentElement;return documentElement?documentElement.nodeName!=="HTML":false},encode:function(xmlNode){if(!this.isXML(xmlNode)){return false}try{return new XMLSerializer().serializeToString(xmlNode)}catch(E1){try{return xmlNode.xml}catch(E2){}}return false},decode:function(xmlString){var dom_parser=("DOMParser"in window&&(new DOMParser()).parseFromString)||(window.ActiveXObject&&function(_xmlString){var xml_doc=new ActiveXObject('Microsoft.XMLDOM');xml_doc.async='false';xml_doc.loadXML(_xmlString);return xml_doc}),resultXML;if(!dom_parser){return false}resultXML=dom_parser.call("DOMParser"in window&&(new DOMParser())||window,xmlString,'text/xml');return this.isXML(resultXML)?resultXML:false}};function _init(){var localStorageReallyWorks=false;if("localStorage"in window){try{window.localStorage.setItem('_tmptest','tmpval');localStorageReallyWorks=true;window.localStorage.removeItem('_tmptest')}catch(BogusQuotaExceededErrorOnIos5){}}if(localStorageReallyWorks){try{if(window.localStorage){_storage_service=window.localStorage;_backend="localStorage";_observer_update=_storage_service.jStorage_update}}catch(E3){}}else if("globalStorage"in window){try{if(window.globalStorage){if(window.location.hostname=='localhost'){_storage_service=window.globalStorage['localhost.localdomain']}else{_storage_service=window.globalStorage[window.location.hostname]}_backend="globalStorage";_observer_update=_storage_service.jStorage_update}}catch(E4){}}else{_storage_elm=document.createElement('link');if(_storage_elm.addBehavior){_storage_elm.style.behavior='url(#default#userData)';document.getElementsByTagName('head')[0].appendChild(_storage_elm);try{_storage_elm.load("jStorage")}catch(E){_storage_elm.setAttribute("jStorage","{}");_storage_elm.save("jStorage");_storage_elm.load("jStorage")}var data="{}";try{data=_storage_elm.getAttribute("jStorage")}catch(E5){}try{_observer_update=_storage_elm.getAttribute("jStorage_update")}catch(E6){}_storage_service.jStorage=data;_backend="userDataBehavior"}else{_storage_elm=null;return}}_load_storage();_handleTTL();_setupObserver();_handlePubSub();if("addEventListener"in window){window.addEventListener("pageshow",function(event){if(event.persisted){_storageObserver()}},false)}}function _reloadData(){var data="{}";if(_backend=="userDataBehavior"){_storage_elm.load("jStorage");try{data=_storage_elm.getAttribute("jStorage")}catch(E5){}try{_observer_update=_storage_elm.getAttribute("jStorage_update")}catch(E6){}_storage_service.jStorage=data}_load_storage();_handleTTL();_handlePubSub()}function _setupObserver(){if(_backend=="localStorage"||_backend=="globalStorage"){if("addEventListener"in window){window.addEventListener("storage",_storageObserver,false)}else{document.attachEvent("onstorage",_storageObserver)}}else if(_backend=="userDataBehavior"){setInterval(_storageObserver,1000)}}function _storageObserver(){var updateTime;clearTimeout(_observer_timeout);_observer_timeout=setTimeout(function(){if(_backend=="localStorage"||_backend=="globalStorage"){updateTime=_storage_service.jStorage_update}else if(_backend=="userDataBehavior"){_storage_elm.load("jStorage");try{updateTime=_storage_elm.getAttribute("jStorage_update")}catch(E5){}}if(updateTime&&updateTime!=_observer_update){_observer_update=updateTime;_checkUpdatedKeys()}},25)}function _checkUpdatedKeys(){var oldCrc32List=JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32)),newCrc32List;_reloadData();newCrc32List=JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32));var key,updated=[],removed=[];for(key in oldCrc32List){if(oldCrc32List.hasOwnProperty(key)){if(!newCrc32List[key]){removed.push(key);continue}if(oldCrc32List[key]!=newCrc32List[key]&&String(oldCrc32List[key]).substr(0,2)=="2."){updated.push(key)}}}for(key in newCrc32List){if(newCrc32List.hasOwnProperty(key)){if(!oldCrc32List[key]){updated.push(key)}}}_fireObservers(updated,"updated");_fireObservers(removed,"deleted")}function _fireObservers(keys,action){keys=[].concat(keys||[]);if(action=="flushed"){keys=[];for(var key in _observers){if(_observers.hasOwnProperty(key)){keys.push(key)}}action="deleted"}for(var i=0,len=keys.length;i<len;i++){if(_observers[keys[i]]){for(var j=0,jlen=_observers[keys[i]].length;j<jlen;j++){_observers[keys[i]][j](keys[i],action)}}if(_observers["*"]){for(var j=0,jlen=_observers["*"].length;j<jlen;j++){_observers["*"][j](keys[i],action)}}}}function _publishChange(){var updateTime=(+new Date()).toString();if(_backend=="localStorage"||_backend=="globalStorage"){_storage_service.jStorage_update=updateTime}else if(_backend=="userDataBehavior"){_storage_elm.setAttribute("jStorage_update",updateTime);_storage_elm.save("jStorage")}_storageObserver()}function _load_storage(){if(_storage_service.jStorage){try{_storage=JSON.parse(String(_storage_service.jStorage))}catch(E6){_storage_service.jStorage="{}"}}else{_storage_service.jStorage="{}"}_storage_size=_storage_service.jStorage?String(_storage_service.jStorage).length:0;if(!_storage.__jstorage_meta){_storage.__jstorage_meta={}}if(!_storage.__jstorage_meta.CRC32){_storage.__jstorage_meta.CRC32={}}}function _save(){_dropOldEvents();try{_storage_service.jStorage=JSON.stringify(_storage);if(_storage_elm){_storage_elm.setAttribute("jStorage",_storage_service.jStorage);_storage_elm.save("jStorage")}_storage_size=_storage_service.jStorage?String(_storage_service.jStorage).length:0}catch(E7){}}function _checkKey(key){if(!key||(typeof key!="string"&&typeof key!="number")){throw new TypeError('Key name must be string or numeric')}if(key=="__jstorage_meta"){throw new TypeError('Reserved key name')}return true}function _handleTTL(){var curtime,i,TTL,CRC32,nextExpire=Infinity,changed=false,deleted=[];clearTimeout(_ttl_timeout);if(!_storage.__jstorage_meta||typeof _storage.__jstorage_meta.TTL!="object"){return}curtime=+new Date();TTL=_storage.__jstorage_meta.TTL;CRC32=_storage.__jstorage_meta.CRC32;for(i in TTL){if(TTL.hasOwnProperty(i)){if(TTL[i]<=curtime){delete TTL[i];delete CRC32[i];delete _storage[i];changed=true;deleted.push(i)}else if(TTL[i]<nextExpire){nextExpire=TTL[i]}}}if(nextExpire!=Infinity){_ttl_timeout=setTimeout(_handleTTL,nextExpire-curtime)}if(changed){_save();_publishChange();_fireObservers(deleted,"deleted")}}function _handlePubSub(){var i,len;if(!_storage.__jstorage_meta.PubSub){return}var pubelm,_pubsubCurrent=_pubsub_last;for(i=len=_storage.__jstorage_meta.PubSub.length-1;i>=0;i--){pubelm=_storage.__jstorage_meta.PubSub[i];if(pubelm[0]>_pubsub_last){_pubsubCurrent=pubelm[0];_fireSubscribers(pubelm[1],pubelm[2])}}_pubsub_last=_pubsubCurrent}function _fireSubscribers(channel,payload){if(_pubsub_observers[channel]){for(var i=0,len=_pubsub_observers[channel].length;i<len;i++){_pubsub_observers[channel][i](channel,JSON.parse(JSON.stringify(payload)))}}}function _dropOldEvents(){if(!_storage.__jstorage_meta.PubSub){return}var retire=+new Date()-2000;for(var i=0,len=_storage.__jstorage_meta.PubSub.length;i<len;i++){if(_storage.__jstorage_meta.PubSub[i][0]<=retire){_storage.__jstorage_meta.PubSub.splice(i,_storage.__jstorage_meta.PubSub.length-i);break}}if(!_storage.__jstorage_meta.PubSub.length){delete _storage.__jstorage_meta.PubSub}}function _publish(channel,payload){if(!_storage.__jstorage_meta){_storage.__jstorage_meta={}}if(!_storage.__jstorage_meta.PubSub){_storage.__jstorage_meta.PubSub=[]}_storage.__jstorage_meta.PubSub.unshift([+new Date,channel,payload]);_save();_publishChange()}function murmurhash2_32_gc(str,seed){var l=str.length,h=seed^l,i=0,k;while(l>=4){k=((str.charCodeAt(i)&0xff))|((str.charCodeAt(++i)&0xff)<<8)|((str.charCodeAt(++i)&0xff)<<16)|((str.charCodeAt(++i)&0xff)<<24);k=(((k&0xffff)*0x5bd1e995)+((((k>>>16)*0x5bd1e995)&0xffff)<<16));k^=k>>>24;k=(((k&0xffff)*0x5bd1e995)+((((k>>>16)*0x5bd1e995)&0xffff)<<16));h=(((h&0xffff)*0x5bd1e995)+((((h>>>16)*0x5bd1e995)&0xffff)<<16))^k;l-=4;++i}switch(l){case 3:h^=(str.charCodeAt(i+2)&0xff)<<16;case 2:h^=(str.charCodeAt(i+1)&0xff)<<8;case 1:h^=(str.charCodeAt(i)&0xff);h=(((h&0xffff)*0x5bd1e995)+((((h>>>16)*0x5bd1e995)&0xffff)<<16))}h^=h>>>13;h=(((h&0xffff)*0x5bd1e995)+((((h>>>16)*0x5bd1e995)&0xffff)<<16));h^=h>>>15;return h>>>0}$.jStorage={version:JSTORAGE_VERSION,set:function(key,value,options){_checkKey(key);options=options||{};if(typeof value=="undefined"){this.deleteKey(key);return value}if(_XMLService.isXML(value)){value={_is_xml:true,xml:_XMLService.encode(value)}}else if(typeof value=="function"){return undefined}else if(value&&typeof value=="object"){value=JSON.parse(JSON.stringify(value))}_storage[key]=value;_storage.__jstorage_meta.CRC32[key]="2."+murmurhash2_32_gc(JSON.stringify(value),0x9747b28c);this.setTTL(key,options.TTL||0);_fireObservers(key,"updated");return value},get:function(key,def){_checkKey(key);if(key in _storage){if(_storage[key]&&typeof _storage[key]=="object"&&_storage[key]._is_xml){return _XMLService.decode(_storage[key].xml)}else{return _storage[key]}}return typeof(def)=='undefined'?null:def},deleteKey:function(key){_checkKey(key);if(key in _storage){delete _storage[key];if(typeof _storage.__jstorage_meta.TTL=="object"&&key in _storage.__jstorage_meta.TTL){delete _storage.__jstorage_meta.TTL[key]}delete _storage.__jstorage_meta.CRC32[key];_save();_publishChange();_fireObservers(key,"deleted");return true}return false},setTTL:function(key,ttl){var curtime=+new Date();_checkKey(key);ttl=Number(ttl)||0;if(key in _storage){if(!_storage.__jstorage_meta.TTL){_storage.__jstorage_meta.TTL={}}if(ttl>0){_storage.__jstorage_meta.TTL[key]=curtime+ttl}else{delete _storage.__jstorage_meta.TTL[key]}_save();_handleTTL();_publishChange();return true}return false},getTTL:function(key){var curtime=+new Date(),ttl;_checkKey(key);if(key in _storage&&_storage.__jstorage_meta.TTL&&_storage.__jstorage_meta.TTL[key]){ttl=_storage.__jstorage_meta.TTL[key]-curtime;return ttl||0}return 0},flush:function(){_storage={__jstorage_meta:{CRC32:{}}};_save();_publishChange();_fireObservers(null,"flushed");return true},storageObj:function(){function F(){}F.prototype=_storage;return new F()},index:function(){var index=[],i;for(i in _storage){if(_storage.hasOwnProperty(i)&&i!="__jstorage_meta"){index.push(i)}}return index},storageSize:function(){return _storage_size},currentBackend:function(){return _backend},storageAvailable:function(){return!!_backend},listenKeyChange:function(key,callback){_checkKey(key);if(!_observers[key]){_observers[key]=[]}_observers[key].push(callback)},stopListening:function(key,callback){_checkKey(key);if(!_observers[key]){return}if(!callback){delete _observers[key];return}for(var i=_observers[key].length-1;i>=0;i--){if(_observers[key][i]==callback){_observers[key].splice(i,1)}}},subscribe:function(channel,callback){channel=(channel||"").toString();if(!channel){throw new TypeError('Channel not defined')}if(!_pubsub_observers[channel]){_pubsub_observers[channel]=[]}_pubsub_observers[channel].push(callback)},publish:function(channel,payload){channel=(channel||"").toString();if(!channel){throw new TypeError('Channel not defined')}_publish(channel,payload)},reInit:function(){_reloadData()}};_init()})();
=== modified file 'dashboard_app/templates/dashboard_app/image-report.html'
@@ -4,79 +4,102 @@
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}dashboard_app/css/image-report.css"/>
<script type="text/javascript" src="{{ STATIC_URL }}dashboard_app/js/image-report.js"></script>
+<script src="{{ STATIC_URL }}dashboard_app/js/excanvas.min.js"></script>
+<script src="{{ STATIC_URL }}dashboard_app/js/jquery.flot.min.js"></script>
+<script src="{{ STATIC_URL }}dashboard_app/js/jquery.flot.dashes.min.js"></script>
+<script src="{{ STATIC_URL }}dashboard_app/js/jstorage.min.js"></script>
+<script src="{{ STATIC_URL }}dashboard_app/js/jquery.tooltip.min.js"></script>
{% endblock %}
{% block content %}
<h1>Image Report: {{ image.name }}</h1>
+
+<div id="outer-container">
+<div id="inner-container">
+</div>
+<div id="legend-container">
+</div>
+</div>
+
+
+
+<script language="javascript">
+ chart_data = $.parseJSON($('<div/>').html("{{chart_data}}").text());
+ test_names = $.parseJSON($('<div/>').html("{{test_names}}").text());
+ columns = $.parseJSON($('<div/>').html("{{columns}}").text());
+</script>
+
+<div id="filters">
+ <div id="filter_headline">Filters</div>
+ <div id="build_numbers_filter">
+ <div id="build_number_headline">
+ Start build number:
+ </div>
+ <span id="build_number_start_container">
+ <select id="build_number_start" onchange='update_table(columns, chart_data, test_names)'>
+ </select>
+ </span>
+
+ End build number:
+ <span id="build_number_end_container">
+ <select id="build_number_end" onchange='update_table(columns, chart_data, test_names)'>
+ </select>
+ </span>
+ </div>
+
+ <div id="tests_filter">
+ <div id="test_headline">
+ Tests:
+ </div>
+ <select id="test_select" onchange='update_table(columns, chart_data, test_names)' multiple>
+ </select>
+ </div>
+
+ <div id="target_goal_filter">
+ <div id="target_goal_headline">
+ Target Goal:
+ </div>
+ <input type="text" id="target_goal" onblur='update_table(columns, chart_data, test_names)' />
+ </div>
+
+ <div id="graph_type_filter">
+ <div id="graph_type_headline">
+ Graph type:
+ </div>
+ <input type="radio" name="graph_type" onclick='update_table(columns, chart_data, test_names)' checked value="percentage">
+ By percentage
+ </input>
+ <input type="radio" name="graph_type" onclick='update_table(columns, chart_data, test_names)' value="number">
+ By pass/fail test numbers
+ </input>
+ </div>
+</div>
+
+
<table id="outer-table">
<tr>
<td>
<table id="test-run-names" class="inner-table">
<thead>
<tr>
- <th>
+ <th style='width: 170px;'>
Build Number
</th>
</tr>
</thead>
<tbody>
- <tr>
- <td>
- Date
- </td>
- </tr>
- {% for test_run_name in test_run_names %}
- <tr>
- <td>
- {{ test_run_name }}
- </td>
- </tr>
- {% endfor %}
- </tbody>
+ </tbody>
</table>
</td>
<td>
<div id="scroller">
<table id="results-table" class="inner-table">
<thead>
- <tr>
- {% for col in cols %}
- <th>
- <a href="{{ col.link }}">{{ col.number }}</a>
- </th>
- {% endfor %}
- </tr>
- </thead>
- <tbody>
- <tr>
- {% for col in cols %}
- <td>
- {{ col.date|date }}
- </td>
- {% endfor %}
- </tr>
- {% for row_data in table_data %}
- <tr>
- {% for result in row_data %}
- <td class="{{ result.cls }}" data-uuid="{{ result.uuid }}">
- {% if result.present %}
- <a href="{{ result.link }}"> {{ result.passes }}/{{ result.total }} </a>
- <span class="bug-links">
- {% for bug_id in result.bug_ids %}
- <a class="bug-link" href="https://bugs.launchpad.net/bugs/{{ bug_id }}" data-bug-id="{{ bug_id }}">[{{ bug_id }}]</a>
- {% endfor %}
- <a href="#" class="add-bug-link">[+]</a>
- </span>
- {% else %}
- —
- {% endif %}
- </td>
- {% endfor %}
- </tr>
- {% endfor %}
- </tbody>
- </table>
+ </thead>
+ <tbody>
+ </tbody>
+ </table>
</div>
</td>
</tr>
=== modified file 'dashboard_app/views/images.py'
@@ -35,7 +35,7 @@
TestRun,
)
from dashboard_app.views import index
-
+import json
@BreadCrumb("Image Reports", parent=index)
def image_report_list(request):
@@ -100,8 +100,8 @@
if (match.tag, test_run.bundle.uploaded_on) not in build_number_to_cols:
build_number_to_cols[(match.tag, test_run.bundle.uploaded_on)] = {
'test_runs': {},
- 'number': match.tag,
- 'date': test_run.bundle.uploaded_on,
+ 'number': str(match.tag),
+ 'date': str(test_run.bundle.uploaded_on),
'link': test_run.bundle.get_absolute_url(),
}
build_number_to_cols[(match.tag, test_run.bundle.uploaded_on)]['test_runs'][name] = test_run_data
@@ -114,7 +114,7 @@
cols = [c for n, c in sorted(build_number_to_cols.items())]
- table_data = []
+ table_data = {}
for test_run_name in test_run_names:
row_data = []
@@ -126,16 +126,16 @@
cls='missing',
)
row_data.append(test_run_data)
- table_data.append(row_data)
+ table_data[test_run_name] = row_data
return render_to_response(
"dashboard_app/image-report.html", {
'bread_crumb_trail': BreadCrumbTrail.leading_to(
image_report_detail, name=image.name),
'image': image,
- 'cols': cols,
- 'table_data': table_data,
- 'test_run_names': test_run_names,
+ 'chart_data': json.dumps(table_data),
+ 'test_names': json.dumps(test_run_names),
+ 'columns': json.dumps(cols),
}, RequestContext(request))