| OLD | NEW |
| (Empty) |
| 1 <html> | |
| 2 <head> | |
| 3 <title>Skia Buildstep Dashboard</title> | |
| 4 <link rel="icon" href="favicon.ico"> | |
| 5 <script language="JavaScript"> | |
| 6 "use strict"; | |
| 7 | |
| 8 var DEFAULT_MASTER_FILTER = "*"; | |
| 9 var DEFAULT_BUILDER_FILTER = "*"; | |
| 10 var DEFAULT_BUILDSTEP_FILTER = "*"; | |
| 11 var DEFAULT_TIME_PERIOD = 24*60*60; // 24 hours in seconds. | |
| 12 | |
| 13 // Column indices for grouping Graphite data. | |
| 14 var GROUP_NODE_BUILDER = 2; | |
| 15 var GROUP_NODE_BUILDSTEP = 3; | |
| 16 | |
| 17 // We hard-code this giant interval to be passed to Graphite's summarize | |
| 18 // function to ensure that the data ends up grouped into one big bucket, no | |
| 19 // matter how we set the time period. | |
| 20 var SUMMARIZE_INTERVAL = "10year"; | |
| 21 | |
| 22 // Timestamp of when we started loading data. Used to compute load time. | |
| 23 var loadStart = null; | |
| 24 | |
| 25 // Used to store information about what data is currently being loaded. | |
| 26 var currentlyLoadingData = null; | |
| 27 | |
| 28 // Metrics to load from Graphite. | |
| 29 // Takes the form: [[metric_name, merge_function], ...] | |
| 30 var metrics = [["success", "sum"], | |
| 31 ["failure", "sum"], | |
| 32 ["duration", "avg"]]; | |
| 33 | |
| 34 // Metrics to derive from the loaded metrics above. We display these in the | |
| 35 // table. Takes the form: [[metric_name, derivation_fn], ...], where | |
| 36 // derivation_fn is a function which takes as a parameter a dictionary | |
| 37 // containing values for all of the above metrics for a given buildStep and | |
| 38 // returns a value, which may be null. | |
| 39 var derivedMetrics = [ | |
| 40 ["Duration", function(stepData) { | |
| 41 if (stepData["duration"] == undefined) { | |
| 42 return null; | |
| 43 } | |
| 44 return stepData["duration"]; | |
| 45 }], | |
| 46 ["Failure Rate", function(stepData) { | |
| 47 if (stepData["success"] == undefined || | |
| 48 stepData["failure"] == undefined) { | |
| 49 return null; | |
| 50 } | |
| 51 var success = parseFloat(stepData["success"]); | |
| 52 var failure = parseFloat(stepData["failure"]); | |
| 53 var totalRuns = success + failure; | |
| 54 var failureRate = failure / totalRuns; | |
| 55 if (totalRuns <= 0) { | |
| 56 return null; | |
| 57 } | |
| 58 return failureRate; | |
| 59 }], | |
| 60 ["Total Runs", function(stepData) { | |
| 61 if (stepData["success"] == undefined || | |
| 62 stepData["failure"] == undefined) { | |
| 63 return null; | |
| 64 } | |
| 65 var success = parseFloat(stepData["success"]); | |
| 66 var failure = parseFloat(stepData["failure"]); | |
| 67 return success + failure; | |
| 68 }], | |
| 69 ]; | |
| 70 | |
| 71 // Use the second data column as the default sort index, since that typically | |
| 72 // indicates the most important column which isn't a label. | |
| 73 var defaultSortIndex = 1; | |
| 74 | |
| 75 /** | |
| 76 * Display text or HTML in the logging div. | |
| 77 * | |
| 78 * @param {string} msg The HTML or text to display. | |
| 79 */ | |
| 80 function setMessage(msg) { | |
| 81 console.log(msg); | |
| 82 document.getElementById("logging_div").innerHTML = msg; | |
| 83 } | |
| 84 | |
| 85 /** | |
| 86 * Function to call when starting to load data. Prints the given URL and | |
| 87 * stores some data for retrieval on loadingDone(). | |
| 88 * | |
| 89 * @param {string} url The URL that we're loading. | |
| 90 * @param {Object} data Arbitrary data to store.s | |
| 91 */ | |
| 92 function loadingStart(url, data) { | |
| 93 document.body.style.cursor = "wait"; | |
| 94 document.getElementById("buildstep_div").style.display = "none"; | |
| 95 document.getElementById("builder_div").style.display = "none"; | |
| 96 document.getElementById("load_data_button").disabled = true; | |
| 97 loadStart = new Date().getTime(); | |
| 98 setMessage("Loading data from " + url); | |
| 99 currentlyLoadingData = data; | |
| 100 } | |
| 101 | |
| 102 /** | |
| 103 * Function to call when done loading data. Returns any data stored on | |
| 104 * loadingStart(). | |
| 105 */ | |
| 106 function loadingDone() { | |
| 107 document.body.style.cursor = "auto"; | |
| 108 document.getElementById("buildstep_div").style.display = "block"; | |
| 109 document.getElementById("builder_div").style.display = "block"; | |
| 110 document.getElementById("load_data_button").disabled = false; | |
| 111 var message = ""; | |
| 112 if (loadStart) { | |
| 113 var elapsedSeconds = (new Date().getTime() - loadStart) / 1000; | |
| 114 message = "Loaded data in " + elapsedSeconds + " seconds."; | |
| 115 } | |
| 116 setMessage(message); | |
| 117 var retData = currentlyLoadingData; | |
| 118 currentlyLoadingData = null; | |
| 119 return retData; | |
| 120 } | |
| 121 | |
| 122 /** | |
| 123 * Load data from the given URL using JSONP. | |
| 124 * | |
| 125 * @param {string} url The URL from which to load data. | |
| 126 * @param {string} callbackName The name of the function to use as a callback. | |
| 127 */ | |
| 128 function loadJSONP(url, callbackName) { | |
| 129 var script = document.createElement("script"); | |
| 130 var join = "&"; | |
| 131 if (url.indexOf("?") < 0) { | |
| 132 join = "?"; | |
| 133 } | |
| 134 script.src = url + join + "jsonp=" + callbackName; | |
| 135 document.head.appendChild(script); | |
| 136 } | |
| 137 | |
| 138 /** | |
| 139 * Re-organize Graphite data into a dictionary. | |
| 140 * | |
| 141 * @param {Array<Object>} data Data to organize. | |
| 142 */ | |
| 143 function graphiteDataDict(data) { | |
| 144 var dataDict = {}; | |
| 145 for (var i = 0; i < data.length; ++i) { | |
| 146 var splitName = data[i]["target"].split("."); | |
| 147 var name = splitName.slice(0, splitName.length - 1).join("."); | |
| 148 var resultType = splitName[1]; | |
| 149 var datapoints = data[i]["datapoints"]; | |
| 150 var result = 0; | |
| 151 if (datapoints.length > 0) { | |
| 152 result = datapoints[datapoints.length - 1][0]; | |
| 153 } | |
| 154 if (result == null) { | |
| 155 result = 0; | |
| 156 } | |
| 157 if (!dataDict[name]) { | |
| 158 dataDict[name] = {}; | |
| 159 } | |
| 160 dataDict[name][resultType] = result; | |
| 161 dataDict[name]["name"] = name; | |
| 162 } | |
| 163 return dataDict; | |
| 164 } | |
| 165 | |
| 166 /** | |
| 167 * Create lists of column names and rows given a data dictionary. | |
| 168 * | |
| 169 * @param {Object} dataDict data to organize into columns and rows. | |
| 170 */ | |
| 171 function getColsAndRows(dataDict) { | |
| 172 // Create the set of columns | |
| 173 var cols = ["Name"]; | |
| 174 for (var i = 0; i < derivedMetrics.length; ++i) { | |
| 175 cols.push(derivedMetrics[i][0]); | |
| 176 } | |
| 177 | |
| 178 // Create a row of data for each buildStep. | |
| 179 var rows = []; | |
| 180 for (var name in dataDict) { | |
| 181 var row = [name]; | |
| 182 // Fill in the data row. | |
| 183 for (var i = 0; i < derivedMetrics.length; ++i) { | |
| 184 var value = derivedMetrics[i][1](dataDict[name]); | |
| 185 row.push(value); | |
| 186 } | |
| 187 rows.push(row); | |
| 188 } | |
| 189 return [cols, rows] | |
| 190 } | |
| 191 | |
| 192 /** | |
| 193 * Build the data table given a set of data. | |
| 194 * | |
| 195 * @param {Array<Object>} data The data to put into the table. | |
| 196 * @param {string} title The title of the table. | |
| 197 * @param {string} containerId ID of the container to hold the table. | |
| 198 */ | |
| 199 function makeTableFromData(data, title, containerId) { | |
| 200 var buildStepData = graphiteDataDict(data); | |
| 201 var colsAndRows = getColsAndRows(buildStepData); | |
| 202 rebuildTable(title, colsAndRows[0], colsAndRows[1], containerId, | |
| 203 null, true); | |
| 204 } | |
| 205 | |
| 206 /** | |
| 207 * Sort the rows on the given column index. | |
| 208 * | |
| 209 * @param {Array<Array>} rows The rows to sort. | |
| 210 * @param {number} sortIndex The index of the column by which to sort. | |
| 211 * @param {number} sortOrder 1 or -1; determines whether to sort ascending or | |
| 212 * descending. | |
| 213 */ | |
| 214 function sortRows(rows, sortIndex, sortOrder) { | |
| 215 // Sort the table rows by sortIndex. | |
| 216 if (null == sortIndex || undefined == sortIndex) { | |
| 217 sortIndex = defaultSortIndex; | |
| 218 } | |
| 219 rows.sort(function(a, b) { | |
| 220 if (null == a) { return -sortOrder; } | |
| 221 if (null == b) { return sortOrder; } | |
| 222 if (null == a[sortIndex]) { return -sortOrder; } | |
| 223 if (null == b[sortIndex]) { return sortOrder; } | |
| 224 if (a[sortIndex] > b[sortIndex]) { return sortOrder; } | |
| 225 if (a[sortIndex] < b[sortIndex]) { return -sortOrder; } | |
| 226 return 0; | |
| 227 }); | |
| 228 } | |
| 229 | |
| 230 /** | |
| 231 * Rebuild the data table. | |
| 232 * | |
| 233 * @param {string} title The title of the table. | |
| 234 * @param {Array<string>} cols Array of column names. | |
| 235 * @param {Array<Array>} rows Array of row data. | |
| 236 * @param {string} containerId ID of the container where the table goes. | |
| 237 * @param {number} sortIndex The index of the column by which to sort. | |
| 238 * @param {boolean} reload Whether or not we reloaded data. This affects the | |
| 239 * sorting of rows. | |
| 240 */ | |
| 241 function rebuildTable(title, cols, rows, containerId, sortIndex, reload) { | |
| 242 var tableId = containerId + "_table"; | |
| 243 var oldTable = document.getElementById(tableId); | |
| 244 var lastSortIndex = null; | |
| 245 var sortOrder = -1; | |
| 246 if (oldTable) { | |
| 247 lastSortIndex = oldTable.sortIndex; | |
| 248 if (reload) { | |
| 249 sortIndex = lastSortIndex; | |
| 250 } else if (sortIndex == lastSortIndex) { | |
| 251 sortOrder = -oldTable.sortOrder; | |
| 252 } | |
| 253 } | |
| 254 sortRows(rows, sortIndex, sortOrder); | |
| 255 | |
| 256 var table = document.createElement("table"); | |
| 257 table.sortIndex = sortIndex; | |
| 258 table.sortOrder = sortOrder; | |
| 259 var thead = document.createElement("thead"); | |
| 260 | |
| 261 for (var i = 0; i < cols.length; ++i) { | |
| 262 var th = document.createElement("th"); | |
| 263 th.style.padding = "5px"; | |
| 264 th.style.textAlign = "right"; | |
| 265 var sortLink = document.createElement("a"); | |
| 266 sortLink.href = "#"; | |
| 267 sortLink.innerHTML = cols[i]; | |
| 268 sortLink.tableTitle = title; | |
| 269 sortLink.containerId = containerId; | |
| 270 sortLink.sortIndex = i; | |
| 271 sortLink.rowsObj = rows; | |
| 272 sortLink.colsObj = cols; | |
| 273 sortLink.addEventListener("click", function(event) { | |
| 274 var cols = event.target.colsObj; | |
| 275 var rows = event.target.rowsObj; | |
| 276 var containerId = event.target.containerId; | |
| 277 var sortIndex = event.target.sortIndex; | |
| 278 var tableTitle = event.target.tableTitle; | |
| 279 rebuildTable(tableTitle, cols, rows, containerId, sortIndex, false); | |
| 280 }); | |
| 281 th.appendChild(sortLink); | |
| 282 thead.appendChild(th); | |
| 283 } | |
| 284 | |
| 285 table.appendChild(thead); | |
| 286 | |
| 287 for (var i = 0; i < rows.length; ++i) { | |
| 288 var tr = document.createElement("tr"); | |
| 289 for (var j = 0; j < rows[i].length; ++j) { | |
| 290 var td = document.createElement("td"); | |
| 291 td.style.padding = "5px"; | |
| 292 td.style.textAlign = "right"; | |
| 293 var value = rows[i][j]; | |
| 294 if (typeof value == "number") { | |
| 295 value = parseFloat(value).toFixed(2); | |
| 296 } | |
| 297 // Add links for builder breakdowns to the first column. | |
| 298 if (tableId == "buildstep_div_table" && j == 0) { | |
| 299 var a = document.createElement("a"); | |
| 300 a.href = "#"; | |
| 301 a.buildStepName = value; | |
| 302 a.addEventListener("click", function(event) { | |
| 303 var name = event.target.buildStepName; | |
| 304 loadGraphiteData(null, null, null, null, name, | |
| 305 name + " on ...", "builder_div", | |
| 306 GROUP_NODE_BUILDER); | |
| 307 }); | |
| 308 a.innerHTML = value; | |
| 309 td.appendChild(a); | |
| 310 } else { | |
| 311 td.innerHTML = value; | |
| 312 } | |
| 313 tr.appendChild(td); | |
| 314 } | |
| 315 table.appendChild(tr); | |
| 316 } | |
| 317 var container = document.getElementById(containerId); | |
| 318 container.innerHTML = ""; | |
| 319 table.id = tableId; | |
| 320 var h2 = document.createElement("h2"); | |
| 321 h2.style.textAlign = "center"; | |
| 322 h2.innerHTML = title; | |
| 323 container.appendChild(h2); | |
| 324 container.appendChild(table); | |
| 325 } | |
| 326 | |
| 327 /** | |
| 328 * Callback function for receiving data from Graphite. Organizes the data and | |
| 329 * rebuilds the data table. | |
| 330 * | |
| 331 * @param {Array<Object>} data Data obtained from Graphite. | |
| 332 */ | |
| 333 function gotGraphiteData(data) { | |
| 334 var loadingData = loadingDone(); | |
| 335 makeTableFromData(data, loadingData["tableTitle"], | |
| 336 loadingData["containerId"]); | |
| 337 } | |
| 338 | |
| 339 /** | |
| 340 * Helper function for building Graphite data URLs. If any of the parameters | |
| 341 * are not provided, it obtains defaults from the text boxes on the page. | |
| 342 * | |
| 343 * @param {string} timeStart Unix timestamp; time period beginning. | |
| 344 * @param {string} timeEnd Unix timestamp; time period end. | |
| 345 * @param {string} masterFilter Filter the data by build master. | |
| 346 * @param {string} builderFilter Filter the data by builder. | |
| 347 * @param {string} buildStepFilter Filter the data by build step. | |
| 348 * @param {number} groupNode Column index for grouping Graphite data. | |
| 349 */ | |
| 350 function makeGraphiteURL(timeStart, timeEnd, masterFilter, builderFilter, | |
| 351 buildStepFilter, groupNode) { | |
| 352 if (!timeStart) { | |
| 353 timeStart = | |
| 354 dateTimeLocalToUnixUTC(document.getElementById("time_start").value); | |
| 355 } | |
| 356 if (!timeEnd) { | |
| 357 timeEnd = | |
| 358 dateTimeLocalToUnixUTC(document.getElementById("time_end").value); | |
| 359 } | |
| 360 if (!masterFilter) { | |
| 361 masterFilter = document.getElementById("master_filter").value; | |
| 362 } | |
| 363 if (!builderFilter) { | |
| 364 builderFilter = document.getElementById("builder_filter").value; | |
| 365 } | |
| 366 if (!buildStepFilter) { | |
| 367 buildStepFilter = document.getElementById("buildstep_filter").value; | |
| 368 } | |
| 369 var url = "http://skiamonitor.com/render?format=json&from=" + | |
| 370 timeStart + "&until=" + timeEnd; | |
| 371 var metricPrefixParts = ["buildbot", masterFilter, builderFilter, | |
| 372 buildStepFilter] | |
| 373 var metricPrefix = metricPrefixParts.join("."); | |
| 374 | |
| 375 for (var i = 0; i < metrics.length; ++i) { | |
| 376 var metric = metrics[i][0]; | |
| 377 var summaryMode = metrics[i][1]; | |
| 378 var fullMetricName = metricPrefix + "." + metric; | |
| 379 var groupByNode = "groupByNode(" + fullMetricName + "," + groupNode + | |
| 380 ",%22" + summaryMode + "%22)"; | |
| 381 var summarize = "summarize(" + groupByNode + ",%22" + SUMMARIZE_INTERVAL + | |
| 382 "%22,%22" + summaryMode + "%22,true)"; | |
| 383 var aliasByNode = "aliasByNode(" + summarize + ",0)"; | |
| 384 var aliasSub = "aliasSub(" + aliasByNode + ",%22$%22,%22." + metric + | |
| 385 "%22)"; | |
| 386 var fullTarget = "&target=" + aliasSub; | |
| 387 url += fullTarget; | |
| 388 } | |
| 389 return url; | |
| 390 } | |
| 391 | |
| 392 /** | |
| 393 * Load data from Graphite. | |
| 394 * | |
| 395 * @param {string} timeStart Unix timestamp; time period beginning. | |
| 396 * @param {string} timeEnd Unix timestamp; time period end. | |
| 397 * @param {string} masterFilter Filter the data by build master. | |
| 398 * @param {string} builderFilter Filter the data by builder. | |
| 399 * @param {string} buildStepFilter Filter the data by build step. | |
| 400 * @param {string} tableTitle Title for the table. | |
| 401 * @param {string} containerId ID of the element which will hold the table. | |
| 402 * @param {number} groupNode Column index for grouping Graphite data. | |
| 403 */ | |
| 404 function loadGraphiteData(timeStart, timeEnd, masterFilter, builderFilter, | |
| 405 buildStepFilter, tableTitle, containerId, groupNode)
{ | |
| 406 var url = makeGraphiteURL(timeStart, timeEnd, masterFilter, builderFilter, | |
| 407 buildStepFilter, groupNode); | |
| 408 var data = {"tableTitle": tableTitle, "containerId": containerId}; | |
| 409 loadingStart(url, data); | |
| 410 loadJSONP(url, "gotGraphiteData"); | |
| 411 } | |
| 412 | |
| 413 /** | |
| 414 * Function called when the "Load data" button is clicked. | |
| 415 */ | |
| 416 function reloadWithParams() { | |
| 417 var newQuery = | |
| 418 "from=" + dateTimeLocalToUnixUTC(document.getElementById("time_start").v
alue) + | |
| 419 "&until=" + dateTimeLocalToUnixUTC(document.getElementById("time_end").v
alue) + | |
| 420 "&master=" + document.getElementById("master_filter").value + | |
| 421 "&builder=" + document.getElementById("builder_filter").value + | |
| 422 "&buildstep=" + document.getElementById("buildstep_filter").value; | |
| 423 window.location.search = newQuery; | |
| 424 } | |
| 425 | |
| 426 /** | |
| 427 * Get the local timezone offset in seconds. | |
| 428 */ | |
| 429 function getTimezoneOffset() { | |
| 430 return new Date().getTimezoneOffset() * 60; | |
| 431 } | |
| 432 | |
| 433 /** | |
| 434 * Convert UTC to Local time. | |
| 435 */ | |
| 436 function UTCToLocal(unixDate) { | |
| 437 return unixDate - getTimezoneOffset(); | |
| 438 } | |
| 439 | |
| 440 /** | |
| 441 * Convert Local to UTC time. | |
| 442 */ | |
| 443 function localToUTC(unixDate) { | |
| 444 return unixDate + getTimezoneOffset(); | |
| 445 } | |
| 446 | |
| 447 /** | |
| 448 * Get the date in Unix format. | |
| 449 */ | |
| 450 function unixDate(date) { | |
| 451 var dateObj = null; | |
| 452 if (!date) { | |
| 453 dateObj = new Date() | |
| 454 } else { | |
| 455 dateObj = new Date(date); | |
| 456 } | |
| 457 return dateObj.getTime() / 1000; | |
| 458 } | |
| 459 | |
| 460 /** | |
| 461 * Convert from RFC 3339 to Unix format. | |
| 462 */ | |
| 463 function RFC3339ToUnix(date) { | |
| 464 return unixDate(date); | |
| 465 } | |
| 466 | |
| 467 /** | |
| 468 * Convert from Unix to RFC 3339 format, with no time zone information. | |
| 469 */ | |
| 470 function unixToRFC3339(timestamp) { | |
| 471 var date = new Date(parseInt(timestamp) * 1000); | |
| 472 function zfill(num) { | |
| 473 if (num < 10) { | |
| 474 return "0" + num; | |
| 475 } else { | |
| 476 return num; | |
| 477 } | |
| 478 } | |
| 479 return (date.getUTCFullYear() + "-" + | |
| 480 zfill(date.getUTCMonth() + 1) + "-" + | |
| 481 zfill(date.getUTCDate()) + "T" + | |
| 482 zfill(date.getUTCHours()) + ":" + | |
| 483 zfill(date.getUTCMinutes()) + ":" + | |
| 484 zfill(date.getUTCSeconds())); | |
| 485 } | |
| 486 | |
| 487 /** | |
| 488 * Convert from Unix timestamp to datetime-local RFC 3339 format. | |
| 489 */ | |
| 490 function unixUTCToDateTimeLocal(unix) { | |
| 491 return unixToRFC3339(UTCToLocal(unix)); | |
| 492 } | |
| 493 | |
| 494 /** | |
| 495 * Convert from datetime-local RFC 3339 format to Unix timestamp. | |
| 496 */ | |
| 497 function dateTimeLocalToUnixUTC(rfcLocal) { | |
| 498 return localToUTC(RFC3339ToUnix(rfcLocal)); | |
| 499 } | |
| 500 | |
| 501 /** | |
| 502 * Function called on page load. | |
| 503 */ | |
| 504 function init() { | |
| 505 // Load default query values. | |
| 506 var currentTime = unixDate(); | |
| 507 var timeEnd = currentTime; | |
| 508 var timeStart = currentTime - DEFAULT_TIME_PERIOD; | |
| 509 var masterFilter = DEFAULT_MASTER_FILTER; | |
| 510 var builderFilter = DEFAULT_BUILDER_FILTER; | |
| 511 var buildstepFilter = DEFAULT_BUILDSTEP_FILTER; | |
| 512 | |
| 513 // Retrieve any parameters from the URL. | |
| 514 var params = window.location.search.substring(1).split("&"); | |
| 515 // Remove empty string from parameters. | |
| 516 while (params.indexOf("") != -1) { | |
| 517 params.splice(params.indexOf("")); | |
| 518 } | |
| 519 for (var i = 0; i < params.length; ++i) { | |
| 520 var splitParam = params[i].split("="); | |
| 521 if (splitParam[0] == "from") { | |
| 522 timeStart = splitParam[1]; | |
| 523 } else if (splitParam[0] == "until") { | |
| 524 timeEnd = splitParam[1]; | |
| 525 } else if (splitParam[0] == "master") { | |
| 526 masterFilter = splitParam[1]; | |
| 527 } else if (splitParam[0] == "builder") { | |
| 528 builderFilter = splitParam[1]; | |
| 529 } else if (splitParam[0] == "buildstep") { | |
| 530 buildstepFilter = splitParam[1]; | |
| 531 } else { | |
| 532 console.error("Unknown parameter: " + splitParam[0]); | |
| 533 } | |
| 534 } | |
| 535 | |
| 536 // Fill the form with the appropriate values. | |
| 537 document.getElementById("time_start").value = | |
| 538 unixUTCToDateTimeLocal(timeStart); | |
| 539 document.getElementById("time_end").value = | |
| 540 unixUTCToDateTimeLocal(timeEnd); | |
| 541 document.getElementById("master_filter").value = masterFilter; | |
| 542 document.getElementById("builder_filter").value = builderFilter; | |
| 543 document.getElementById("buildstep_filter").value = buildstepFilter; | |
| 544 | |
| 545 // Decide what to do, based on the number of provided parameters. | |
| 546 if (params.length == 0) { | |
| 547 // Don't load data; we assume that the user might want to tweak the | |
| 548 // filters first. | |
| 549 } else if (params.length != 5) { | |
| 550 // Reload the page, filling in the rest of the parameters with their | |
| 551 // defaults. | |
| 552 reloadWithParams(); | |
| 553 } else { | |
| 554 // All parameters were provided. Load data. | |
| 555 loadGraphiteData(timeStart, timeEnd, masterFilter, builderFilter, | |
| 556 buildstepFilter, "Build Steps", "buildstep_div", | |
| 557 GROUP_NODE_BUILDSTEP); | |
| 558 } | |
| 559 } | |
| 560 | |
| 561 </script> | |
| 562 </head> | |
| 563 <body> | |
| 564 <div id="heading" style="font-size:2.5em; text-align:center; height:7%;"> | |
| 565 Skia Buildstep Dashboard | |
| 566 </div> | |
| 567 <div style="text-align:center;"> | |
| 568 Click a column header to sort that column. Click a buildstep to see its | |
| 569 results broken down into builders. | |
| 570 </div> | |
| 571 <div id="main_content_area" style="width:100%; height:90%; padding:0px; | |
| 572 margin:0px;"> | |
| 573 <div id="menu_div" | |
| 574 style="float:left; width:18%; height:100%; padding:0px; margin:0px;"> | |
| 575 <div style="width:100%;"> | |
| 576 <nobr>Time period:</nobr><br/> | |
| 577 From:<br/> | |
| 578 <input type="datetime-local" id="time_start" /><br/> | |
| 579 To:<br/> | |
| 580 <input type="datetime-local" id="time_end" /><br/> | |
| 581 <nobr>Build master filter:</nobr><br/> | |
| 582 <input type="text" id="master_filter" /><br/> | |
| 583 <nobr>Builder filter:</nobr><br/> | |
| 584 <input type="text" id="builder_filter" /><br/> | |
| 585 <nobr>Build step filter:</nobr><br/> | |
| 586 <input type="text" id="buildstep_filter" /><br/> | |
| 587 <input type="button" id="load_data_button" onClick="reloadWithParams()
;" value="Load Data"/> | |
| 588 </div> | |
| 589 <div id="logging_div" style="width:100%; padding:0px; margin:0px"></div> | |
| 590 </div> | |
| 591 <div id="buildstep_div" | |
| 592 style="float:left; width:41%; padding:0px; margin:0px"> | |
| 593 </div> | |
| 594 <div id="builder_div" | |
| 595 style="float:left; width:41%; padding:0px; margin:0px"> | |
| 596 </div> | |
| 597 </div> | |
| 598 <script type="text/javascript">init();</script> | |
| 599 </body> | |
| 600 </html> | |
| 601 | |
| OLD | NEW |