annotate analysis/analysis.js @ 3141:335bc77627e0 tip

fixing discrete interface to allow labels to display
author Dave Moffat <me@davemoffat.com>
date Mon, 26 Jul 2021 12:15:24 +0100
parents 464c6c6692d6
children
rev   line source
nickjillings@2131 1 /*
nicholas@2538 2 * Analysis script for WAET
nicholas@2538 3 */
nickjillings@2131 4
nickjillings@2192 5 // Firefox does not have an XMLDocument.prototype.getElementsByName
nickjillings@2192 6 // and there is no searchAll style command, this custom function will
nickjillings@2192 7 // search all children recusrively for the name. Used for XSD where all
nickjillings@2192 8 // element nodes must have a name and therefore can pull the schema node
nicholas@2538 9 XMLDocument.prototype.getAllElementsByName = function (name) {
nickjillings@2192 10 name = String(name);
nickjillings@2192 11 var selected = this.documentElement.getAllElementsByName(name);
nickjillings@2192 12 return selected;
nickjillings@2192 13 }
nickjillings@2192 14
nicholas@2538 15 Element.prototype.getAllElementsByName = function (name) {
nickjillings@2192 16 name = String(name);
nickjillings@2192 17 var selected = [];
nickjillings@2192 18 var node = this.firstElementChild;
nicholas@2538 19 while (node != null) {
nicholas@2538 20 if (node.getAttribute('name') == name) {
nickjillings@2192 21 selected.push(node);
nickjillings@2192 22 }
nicholas@2538 23 if (node.childElementCount > 0) {
nickjillings@2192 24 selected = selected.concat(node.getAllElementsByName(name));
nickjillings@2192 25 }
nickjillings@2192 26 node = node.nextElementSibling;
nickjillings@2192 27 }
nickjillings@2192 28 return selected;
nickjillings@2192 29 }
nickjillings@2192 30
nicholas@2538 31 XMLDocument.prototype.getAllElementsByTagName = function (name) {
nickjillings@2192 32 name = String(name);
nickjillings@2192 33 var selected = this.documentElement.getAllElementsByTagName(name);
nickjillings@2192 34 return selected;
nickjillings@2192 35 }
nickjillings@2192 36
nicholas@2538 37 Element.prototype.getAllElementsByTagName = function (name) {
nickjillings@2192 38 name = String(name);
nickjillings@2192 39 var selected = [];
nickjillings@2192 40 var node = this.firstElementChild;
nicholas@2538 41 while (node != null) {
nicholas@2538 42 if (node.nodeName == name) {
nickjillings@2192 43 selected.push(node);
nickjillings@2192 44 }
nicholas@2538 45 if (node.childElementCount > 0) {
nickjillings@2192 46 selected = selected.concat(node.getAllElementsByTagName(name));
nickjillings@2192 47 }
nickjillings@2192 48 node = node.nextElementSibling;
nickjillings@2192 49 }
nickjillings@2192 50 return selected;
nickjillings@2192 51 }
nickjillings@2192 52
nickjillings@2192 53 // Firefox does not have an XMLDocument.prototype.getElementsByName
nickjillings@2192 54 if (typeof XMLDocument.prototype.getElementsByName != "function") {
nicholas@2538 55 XMLDocument.prototype.getElementsByName = function (name) {
nickjillings@2192 56 name = String(name);
nickjillings@2192 57 var node = this.documentElement.firstElementChild;
nickjillings@2192 58 var selected = [];
nicholas@2538 59 while (node != null) {
nicholas@2538 60 if (node.getAttribute('name') == name) {
nickjillings@2192 61 selected.push(node);
nickjillings@2192 62 }
nickjillings@2192 63 node = node.nextElementSibling;
nickjillings@2192 64 }
nickjillings@2192 65 return selected;
nickjillings@2192 66 }
nickjillings@2192 67 }
nickjillings@2192 68
nickjillings@2192 69 var chartContext, testData;
nicholas@2538 70 window.onload = function () {
nickjillings@2131 71 // Load the Visualization API and the corechart package.
nicholas@2538 72 google.charts.load('current', {
nicholas@2538 73 'packages': ['corechart']
nicholas@2538 74 });
nickjillings@2131 75 chartContext = new Chart();
nickjillings@2192 76 testData = new Data();
nickjillings@2192 77 }
nickjillings@2192 78
nickjillings@2192 79 function get(url) {
nicholas@2538 80 // Return a new promise.
nicholas@2538 81 return new Promise(function (resolve, reject) {
nicholas@2538 82 // Do the usual XHR stuff
nicholas@2538 83 var req = new XMLHttpRequest();
nicholas@2538 84 req.open('GET', url);
nicholas@2538 85 req.onload = function () {
nicholas@2538 86 // This is called even on 404 etc
nicholas@2538 87 // so check the status
nicholas@2538 88 if (req.status == 200) {
nicholas@2538 89 // Resolve the promise with the response text
nicholas@2538 90 resolve(req.response);
nicholas@2538 91 } else {
nicholas@2538 92 // Otherwise reject with the status text
nicholas@2538 93 // which will hopefully be a meaningful error
nicholas@2538 94 reject(Error(req.statusText));
nicholas@2538 95 }
nicholas@2538 96 };
nickjillings@2192 97
nicholas@2538 98 // Handle network errors
nicholas@2538 99 req.onerror = function () {
nicholas@2538 100 reject(Error("Network Error"));
nicholas@2538 101 };
nickjillings@2192 102
nicholas@2538 103 // Make the request
nicholas@2538 104 req.send();
nicholas@2538 105 });
nickjillings@2131 106 }
nickjillings@2131 107
nickjillings@2135 108 function arrayMean(values) {
nickjillings@2135 109 var mean = 0;
nickjillings@2135 110 for (var value of values) {
nickjillings@2135 111 mean += value;
nickjillings@2135 112 }
nickjillings@2135 113 mean /= values.length;
nickjillings@2135 114 return mean;
nickjillings@2135 115 }
nickjillings@2135 116
nickjillings@2183 117 function percentile(values, p) {
nickjillings@2183 118 //http://web.stanford.edu/class/archive/anthsci/anthsci192/anthsci192.1064/handouts/calculating%20percentiles.pdf
nicholas@2538 119 values.sort(function (a, b) {
nicholas@2538 120 return a - b;
nicholas@2538 121 });
nickjillings@2135 122 // get ordinal rank
nicholas@2538 123 var index = values.length * p / 100;
nickjillings@2183 124 var k = Math.floor(index);
nickjillings@2183 125 if (k == index) {
nickjillings@2183 126 return values[k];
nickjillings@2183 127 } else {
nicholas@2538 128 var f = index - k;
nicholas@2538 129 var x_int = (1 - f) * values[k] + f * values[k + 1];
nickjillings@2183 130 return x_int;
nickjillings@2183 131 }
nickjillings@2135 132 }
nickjillings@2135 133
nickjillings@2135 134 function arrayMin(array) {
nickjillings@2135 135 // Return the minimum value of an array
nickjillings@2135 136 var min = array[0];
nickjillings@2135 137 for (var value of array) {
nickjillings@2135 138 if (value < min) {
nickjillings@2135 139 min = value;
nickjillings@2135 140 }
nickjillings@2135 141 }
nickjillings@2135 142 return min;
nickjillings@2135 143 }
nickjillings@2135 144
nickjillings@2135 145 function arrayMax(array) {
nickjillings@2135 146 // Return the minimum value of an array
nickjillings@2135 147 var max = array[0];
nickjillings@2135 148 for (var value of array) {
nickjillings@2135 149 if (value > max) {
nickjillings@2135 150 max = value;
nickjillings@2135 151 }
nickjillings@2135 152 }
nickjillings@2135 153 return max;
nickjillings@2135 154 }
nickjillings@2135 155
nickjillings@2181 156 function boxplotRow(array) {
nickjillings@2181 157 // Take an array of element values and return array of computed intervals
nickjillings@2181 158 var result = {
nicholas@2538 159 median: percentile(array, 50),
nicholas@2538 160 pct25: percentile(array, 25),
nicholas@2538 161 pct75: percentile(array, 75),
nicholas@2538 162 IQR: null,
nickjillings@2181 163 min: null,
nickjillings@2181 164 max: null,
nickjillings@2181 165 outliers: new Array()
nickjillings@2181 166 }
nicholas@2538 167 result.IQR = result.pct75 - result.pct25;
nickjillings@2181 168 var rest = [];
nicholas@2538 169 var pct75_IQR = result.pct75 + 1.5 * result.IQR;
nicholas@2538 170 var pct25_IQR = result.pct25 - 1.5 * result.IQR;
nicholas@2538 171 for (var i = 0; i < array.length; i++) {
nickjillings@2181 172 //outliers, ranger above pct75+1.5*IQR or below pct25-1.5*IQR
nickjillings@2181 173 var point = array[i];
nickjillings@2181 174 if (point > pct75_IQR || point < pct25_IQR) {
nickjillings@2181 175 result.outliers.push(point);
nickjillings@2181 176 } else {
nickjillings@2181 177 rest.push(point);
nickjillings@2181 178 }
nickjillings@2181 179 }
nickjillings@2181 180 result.max = arrayMax(rest);
nickjillings@2181 181 result.min = arrayMin(rest);
nickjillings@2181 182 return result;
nicholas@2538 183
nickjillings@2181 184 }
nickjillings@2181 185
nicholas@2538 186 function arrayHistogram(values, steps, min, max) {
nickjillings@2135 187 if (steps == undefined) {
nickjillings@2135 188 steps = 0.25;
nickjillings@2135 189 console.log("Warning: arrayHistogram called without steps size set, default to 0.25");
nickjillings@2135 190 }
nicholas@2538 191 if (min == undefined) {
nicholas@2538 192 min = arrayMin(values);
nicholas@2538 193 }
nicholas@2538 194 if (max == undefined) {
nicholas@2538 195 max = arrayMax(values);
nicholas@2538 196 }
nickjillings@2135 197 var histogram = [];
nickjillings@2135 198 var index = min;
nicholas@2538 199 while (index < max) {
nickjillings@2135 200 histogram.push({
nickjillings@2135 201 marker: index,
nickjillings@2135 202 lt: index,
nicholas@2538 203 rt: index + steps,
nickjillings@2135 204 count: 0
nickjillings@2135 205 });
nickjillings@2135 206 index += steps;
nickjillings@2135 207 }
nickjillings@2135 208 for (var value of values) {
nickjillings@2135 209 for (var entry of histogram) {
nickjillings@2135 210 if (value >= entry.lt && value <= entry.rt) {
nickjillings@2135 211 entry.count++;
nickjillings@2135 212 break;
nickjillings@2135 213 }
nickjillings@2135 214 }
nickjillings@2135 215 }
nickjillings@2135 216 return histogram;
nickjillings@2135 217 }
nickjillings@2135 218
nickjillings@2131 219 function Chart() {
nickjillings@2192 220 this.valueData;
nickjillings@2135 221 this.charts = [];
nicholas@2538 222
nicholas@2538 223 this.chartObject = function (name) {
nickjillings@2135 224 // Create the charting object
nickjillings@2135 225 this.name = name;
nickjillings@2135 226 this.root = document.createElement("div");
nickjillings@2135 227 this.root.className = "chart-holder";
nicholas@2538 228 this.root.setAttribute("name", name);
nickjillings@2135 229 this.chartDOM = document.createElement("div");
nickjillings@2135 230 this.tableDOM = document.createElement("div");
nickjillings@2135 231 this.latexDOM = document.createElement("div");
nickjillings@2135 232 this.downloadDOM = document.createElement("div");
nickjillings@2135 233 this.chart = undefined;
nickjillings@2135 234 this.data = new google.visualization.DataTable();
nickjillings@2135 235 this.options = {};
nickjillings@2135 236 this.print = document.createElement("button");
nickjillings@2136 237 this.sortDataButton = document.createElement("button");
nickjillings@2136 238 this.sortDataButton.textContent = "Sort by Data";
nicholas@2538 239 this.sortDataButton.addEventListener("click", this);
nicholas@2538 240 this.sortDataButton.setAttribute("name", "sort-data");
nickjillings@2136 241 this.sortNameButton = document.createElement("button");
nickjillings@2136 242 this.sortNameButton.textContent = "Sort by Name";
nicholas@2538 243 this.sortNameButton.addEventListener("click", this);
nicholas@2538 244 this.sortNameButton.setAttribute("name", "sort-name");
nicholas@2538 245 this.draw = function () {
nicholas@2538 246 if (this.chart == undefined) {
nicholas@2538 247 return;
nicholas@2538 248 }
nickjillings@2136 249 this.tableDOM.innerHTML = null;
nickjillings@2136 250 this.latexDOM.innerHTML = null;
nickjillings@2136 251 this.buildTable();
nickjillings@2136 252 this.writeLatex();
nicholas@2538 253 this.chart.draw(this.data, this.options);
nickjillings@2136 254 }
nicholas@2538 255 this.sortData = function () {
nickjillings@2173 256 this.data.sort(1);
nickjillings@2136 257 }
nicholas@2538 258 this.sortName = function () {
nickjillings@2173 259 this.data.sort(0);
nickjillings@2136 260 }
nicholas@2538 261 this.handleEvent = function () {
nickjillings@2135 262 // Only used to handle the chart.event.addListener(this,'ready') callback
nicholas@2538 263 switch (event.currentTarget.getAttribute("name")) {
nickjillings@2136 264 case "download":
nickjillings@2136 265 window.open(this.chart.getImageURI());
nickjillings@2136 266 break;
nickjillings@2136 267 case "sort-data":
nickjillings@2136 268 this.sortData();
nickjillings@2136 269 this.draw();
nickjillings@2136 270 break;
nickjillings@2136 271 case "sort-name":
nickjillings@2136 272 this.sortName();
nickjillings@2136 273 this.draw();
nickjillings@2136 274 break;
nickjillings@2136 275 }
nickjillings@2135 276 }
nicholas@2538 277
nickjillings@2135 278 this.root.appendChild(this.chartDOM);
nickjillings@2135 279 this.root.appendChild(this.tableDOM);
nickjillings@2135 280 this.root.appendChild(this.latexDOM);
nickjillings@2136 281 this.root.appendChild(this.sortDataButton);
nickjillings@2136 282 this.root.appendChild(this.sortNameButton);
nickjillings@2135 283 this.root.appendChild(this.print);
nickjillings@2135 284 this.print.textContent = "Download";
nicholas@2538 285 this.print.setAttribute("name", "download");
nicholas@2538 286 this.print.addEventListener("click", this);
nickjillings@2135 287 this.root.appendChild(this.downloadDOM);
nicholas@2538 288 this.buildTable = function () {
nickjillings@2135 289 var table = document.createElement("table");
nickjillings@2135 290 table.border = "1";
nickjillings@2172 291 var numRows = this.data.getNumberOfRows();
nickjillings@2172 292 var numColumns = this.data.getNumberOfColumns();
nicholas@2538 293 for (var columnIndex = 0; columnIndex < numColumns; columnIndex++) {
nickjillings@2181 294 var tableTitle = this.data.getColumnLabel(columnIndex);
nickjillings@2181 295 if (tableTitle != "") {
nickjillings@2181 296 var table_row = document.createElement('tr');
nickjillings@2181 297 table.appendChild(table_row);
nickjillings@2181 298 var row_title = document.createElement('td');
nickjillings@2181 299 table_row.appendChild(row_title);
nickjillings@2181 300 row_title.textContent = tableTitle;
nicholas@2538 301 for (var rowIndex = 0; rowIndex < numRows; rowIndex++) {
nickjillings@2181 302 var row_entry = document.createElement('td');
nickjillings@2181 303 table_row.appendChild(row_entry);
nicholas@2538 304 var entry = this.data.getValue(rowIndex, columnIndex);
nicholas@2538 305 if (isFinite(Number(entry))) {
nickjillings@2181 306 entry = String(Number(entry).toFixed(4));
nickjillings@2181 307 }
nickjillings@2181 308 row_entry.textContent = entry;
nickjillings@2172 309 }
nickjillings@2135 310 }
nickjillings@2135 311 }
nickjillings@2135 312 this.tableDOM.appendChild(table);
nickjillings@2135 313 };
nicholas@2538 314 this.writeLatex = function () {
nickjillings@2172 315 var numRows = this.data.getNumberOfRows();
nickjillings@2172 316 var numColumns = this.data.getNumberOfColumns();
nickjillings@2135 317 var root = document.createElement("div");
nickjillings@2135 318 root.className = "code";
nickjillings@2135 319 var holder = document.createElement("pre");
nickjillings@2135 320 // Table start
nickjillings@2135 321 var start = document.createElement("p");
nickjillings@2135 322 start.textContent = "\\" + "begin{tabular}{|l|";
nickjillings@2135 323 holder.appendChild(start);
nicholas@2538 324 for (var i = 0; i < numRows; i++) {
nicholas@2538 325 start.textContent = start.textContent + "c|";
nickjillings@2135 326 }
nickjillings@2136 327 start.textContent = start.textContent.concat("}");
nickjillings@2135 328 // Now write the rows:
nicholas@2538 329 for (var rIndex = 0; rIndex < numColumns; rIndex++) {
nickjillings@2181 330 var tableTitle = this.data.getColumnLabel(rIndex);
nicholas@2538 331 if (tableTitle != "") {
nickjillings@2181 332 var row = document.createElement("p");
nickjillings@2181 333 row.textContent = tableTitle.concat(" & ");
nicholas@2538 334 for (var cIndex = 0; cIndex < numRows; cIndex++) {
nicholas@2538 335 var entry = this.data.getValue(cIndex, rIndex);
nicholas@2538 336 if (isFinite(Number(entry))) {
nickjillings@2181 337 entry = String(Number(entry).toFixed(4));
nickjillings@2181 338 }
nickjillings@2181 339 row.textContent = row.textContent.concat(entry);
nicholas@2538 340 if (cIndex < numRows - 1) {
nickjillings@2181 341 row.textContent = row.textContent.concat(" & ");
nickjillings@2181 342 } else {
nickjillings@2181 343 row.textContent = row.textContent.concat(" \\\\ \\hline");
nickjillings@2181 344 }
nickjillings@2172 345 }
nickjillings@2181 346 holder.appendChild(row);
nickjillings@2135 347 }
nickjillings@2135 348 }
nickjillings@2135 349 // Table end
nickjillings@2135 350 var end = document.createElement("p");
nickjillings@2135 351 end.textContent = "\\" + "end{tabular}";
nickjillings@2135 352 holder.appendChild(end);
nickjillings@2135 353 root.appendChild(holder);
nickjillings@2135 354 this.latexDOM.appendChild(root);
nickjillings@2135 355 }
nickjillings@2135 356 }
nicholas@2538 357
nicholas@2538 358 this.clear = function () {
nickjillings@2135 359 var inject = document.getElementById("test-pages");
nickjillings@2135 360 for (var chart of this.charts) {
nickjillings@2135 361 inject.removeChild(chart.root);
nickjillings@2135 362 }
nickjillings@2135 363 this.charts = [];
nickjillings@2135 364 }
nicholas@2538 365
nicholas@2538 366 this.drawTestMean = function () {
nickjillings@2135 367 // This draws one bargraph per axis with every test element on
nickjillings@2135 368 if (this.valueData == null) {
nickjillings@2135 369 console.log("Error - Data not loaded");
nickjillings@2135 370 return;
nickjillings@2135 371 }
nickjillings@2135 372 var chartList = [];
nicholas@2538 373
nickjillings@2135 374 // Create the data table
nickjillings@2135 375 for (var page of this.valueData.pages) {
nickjillings@2135 376 for (var element of page.elements) {
nickjillings@2135 377 for (var axis of element.axis) {
nickjillings@2135 378 // Find the axis
nicholas@2538 379 var axisChart = chartList.find(function (element, index, array) {
nicholas@2538 380 if (element.name == this) {
nicholas@2538 381 return true;
nicholas@2538 382 } else {
nicholas@2538 383 return false;
nicholas@2538 384 }
nicholas@2538 385 }, "mean-test-" + axis.name);
nickjillings@2135 386 if (axisChart == null) {
nicholas@2538 387 axisChart = new this.chartObject("mean-test-" + axis.name);
nickjillings@2135 388 axisChart.options = {
nicholas@2538 389 'title': 'Mean of axis: ' + axis.name,
nicholas@2538 390 'width': window.innerWidth * 0.9,
nicholas@2538 391 'height': (window.innerWidth * 0.9) / 1.77
nickjillings@2135 392 }
nicholas@2538 393 axisChart.data.addColumn('string', 'id');
nicholas@2538 394 axisChart.data.addColumn('number', axis.name);
nickjillings@2135 395 chartList.push(axisChart);
nickjillings@2135 396 document.getElementById("test-pages").appendChild(axisChart.root);
nickjillings@2135 397 }
nickjillings@2135 398 var mean = arrayMean(axis.values);
nicholas@2538 399 axisChart.data.addRow([element.id, mean]);
nickjillings@2135 400 }
nickjillings@2135 401 }
nickjillings@2135 402 }
nicholas@2538 403
nickjillings@2135 404 // Build and push charts
nickjillings@2135 405 for (var chart of chartList) {
nickjillings@2135 406 chart.chart = new google.visualization.ColumnChart(chart.chartDOM);
nicholas@2538 407 chart.chart.draw(chart.data, chart.options);
nickjillings@2135 408 chart.buildTable();
nickjillings@2135 409 chart.writeLatex();
nickjillings@2135 410 this.charts.push(chart);
nickjillings@2135 411 }
nickjillings@2135 412 }
nicholas@2538 413
nicholas@2538 414 this.drawTestBoxplot = function () {
nickjillings@2181 415 if (this.valueData == null) {
nickjillings@2181 416 console.log("Error - Data not loaded");
nickjillings@2181 417 return;
nickjillings@2181 418 }
nickjillings@2181 419 var chartList = [];
nicholas@2538 420
nickjillings@2181 421 // Creates one chart per axis
nicholas@2538 422
nickjillings@2181 423 // Create the data table
nickjillings@2181 424 for (var page of this.valueData.pages) {
nickjillings@2181 425 for (var element of page.elements) {
nickjillings@2181 426 for (var axis of element.axis) {
nickjillings@2181 427 // Find the axis
nicholas@2538 428 var axisChart = chartList.find(function (element, index, array) {
nicholas@2538 429 if (element.name == this) {
nicholas@2538 430 return true;
nicholas@2538 431 } else {
nicholas@2538 432 return false;
nicholas@2538 433 }
nicholas@2538 434 }, "boxplot-test-" + axis.name);
nickjillings@2181 435 if (axisChart == null) {
nickjillings@2181 436 // Axis chart doesn't exist
nicholas@2538 437 axisChart = new this.chartObject("boxplot-test-" + axis.name);
nickjillings@2181 438 axisChart.options = {
nicholas@2538 439 'title': 'Boxplot of axis ' + axis.name,
nicholas@2538 440 'width': window.innerWidth * 0.9,
nicholas@2538 441 'height': (window.innerWidth * 0.9) / 1.77,
nicholas@2538 442 legend: {
nicholas@2538 443 position: 'none'
nicholas@2538 444 },
nickjillings@2181 445 lineWidth: 0,
nicholas@2538 446 series: [{
nicholas@2538 447 'color': '#D3362D'
nicholas@2538 448 }],
nickjillings@2181 449 intervals: {
nickjillings@2181 450 barWidth: 1,
nickjillings@2181 451 boxWidth: 1,
nickjillings@2181 452 lineWidth: 2,
nickjillings@2181 453 style: 'boxes'
nickjillings@2181 454 },
nickjillings@2181 455 interval: {
nickjillings@2181 456 max: {
nickjillings@2181 457 style: 'bars',
nickjillings@2181 458 fillOpacity: 1,
nickjillings@2181 459 color: '#777'
nickjillings@2181 460 },
nickjillings@2181 461 min: {
nickjillings@2181 462 style: 'bars',
nickjillings@2181 463 fillOpacity: 1,
nickjillings@2181 464 color: '#777'
nickjillings@2181 465 }
nickjillings@2181 466 }
nickjillings@2181 467 };
nicholas@2538 468 axisChart.data.addColumn('string', 'id');
nicholas@2538 469 axisChart.data.addColumn('number', 'median');
nicholas@2538 470 axisChart.data.addColumn({
nicholas@2538 471 id: 'max',
nicholas@2538 472 type: 'number',
nicholas@2538 473 role: 'interval'
nicholas@2538 474 });
nicholas@2538 475 axisChart.data.addColumn({
nicholas@2538 476 id: 'min',
nicholas@2538 477 type: 'number',
nicholas@2538 478 role: 'interval'
nicholas@2538 479 });
nicholas@2538 480 axisChart.data.addColumn({
nicholas@2538 481 id: 'firstQuartile',
nicholas@2538 482 type: 'number',
nicholas@2538 483 role: 'interval'
nicholas@2538 484 });
nicholas@2538 485 axisChart.data.addColumn({
nicholas@2538 486 id: 'median',
nicholas@2538 487 type: 'number',
nicholas@2538 488 role: 'interval'
nicholas@2538 489 });
nicholas@2538 490 axisChart.data.addColumn({
nicholas@2538 491 id: 'thirdQuartile',
nicholas@2538 492 type: 'number',
nicholas@2538 493 role: 'interval'
nicholas@2538 494 });
nickjillings@2181 495 chartList.push(axisChart);
nickjillings@2181 496 document.getElementById("test-pages").appendChild(axisChart.root);
nickjillings@2181 497 }
nickjillings@2181 498 var result = boxplotRow(axis.values);
nicholas@2538 499 axisChart.data.addRow([element.id, result.median, result.max, result.min, result.pct25, result.median, result.pct75]);
nickjillings@2181 500 }
nickjillings@2181 501 }
nickjillings@2181 502 }
nickjillings@2181 503 // Build and push charts
nickjillings@2181 504 for (var chart of chartList) {
nickjillings@2181 505 chart.chart = new google.visualization.LineChart(chart.chartDOM);
nicholas@2538 506 chart.chart.draw(chart.data, chart.options);
nickjillings@2181 507 chart.buildTable();
nickjillings@2181 508 chart.writeLatex();
nickjillings@2181 509 this.charts.push(chart);
nickjillings@2181 510 }
nickjillings@2181 511 }
nicholas@2538 512
nicholas@2538 513 this.drawPageMean = function () {
nickjillings@2131 514 // First we must get the value data
nickjillings@2134 515 if (this.valueData == null) {
nickjillings@2134 516 console.log("Error - Data not loaded");
nickjillings@2134 517 return;
nickjillings@2134 518 }
nickjillings@2134 519 // We create one plot per page
nickjillings@2134 520 for (var page of this.valueData.pages) {
nicholas@2538 521
nickjillings@2135 522 // Create the chart resulting point
nicholas@2538 523 var chart = new this.chartObject("mean-page-" + page.id);
nickjillings@2135 524 document.getElementById("test-pages").appendChild(chart.root);
nicholas@2538 525
nickjillings@2134 526 // Create the data table
nicholas@2538 527 chart.data.addColumn('string', 'id');
nickjillings@2134 528 // Get axis labels
nickjillings@2134 529 for (var axis of page.elements[0].axis) {
nicholas@2538 530 chart.data.addColumn('number', axis.name);
nickjillings@2134 531 }
nickjillings@2134 532 var rows = []; // Rows is an array of tuples [col1, col2, col3 ... colN];
nickjillings@2134 533 for (var element of page.elements) {
nickjillings@2134 534 var entry = [element.id];
nicholas@2538 535 for (var i = 0; i < page.elements[0].axis.length; i++) {
nicholas@2538 536 var mean = 0;
nickjillings@2134 537 if (i < element.axis.length) {
nickjillings@2134 538 var axis = element.axis[i];
nickjillings@2135 539 mean = arrayMean(axis.values);
nickjillings@2134 540 }
nickjillings@2134 541 entry.push(mean);
nickjillings@2134 542 }
nickjillings@2134 543 rows.push(entry);
nickjillings@2134 544 }
nickjillings@2135 545 chart.data.addRows(rows);
nickjillings@2135 546 chart.options = {
nicholas@2538 547 'title': 'Mean of page: ' + page.id,
nicholas@2538 548 'width': 800,
nicholas@2538 549 'height': 700
nicholas@2538 550 }
nicholas@2538 551 // Draw the chart
nickjillings@2135 552 chart.chart = new google.visualization.ColumnChart(chart.chartDOM);
nicholas@2538 553 chart.chart.draw(chart.data, chart.options);
nickjillings@2135 554 chart.buildTable();
nickjillings@2135 555 chart.writeLatex();
nickjillings@2135 556 this.charts.push(chart);
nickjillings@2135 557 }
nickjillings@2135 558 }
nicholas@2538 559
nicholas@2538 560 this.drawElementHistogram = function () {
nickjillings@2135 561 // First we must get the value data
nickjillings@2135 562 if (this.valueData == null) {
nickjillings@2135 563 console.log("Error - Data not loaded");
nickjillings@2135 564 return;
nickjillings@2135 565 }
nickjillings@2135 566 // We create one plot per element, enjoy...
nickjillings@2135 567 for (var page of this.valueData.pages) {
nickjillings@2135 568 for (var element of page.elements) {
nickjillings@2135 569 // Build the chart object
nicholas@2538 570 var chart = new this.chartObject("histogram-element-" + element.id);
nickjillings@2135 571 document.getElementById("test-pages").appendChild(chart.root);
nicholas@2538 572 chart.data.addColumn('string', 'index');
nickjillings@2135 573 var histograms = [];
nickjillings@2135 574 for (var axis of element.axis) {
nicholas@2538 575 chart.data.addColumn('number', axis.name);
nicholas@2538 576 histograms.push(arrayHistogram(axis.values, 0.125, 0.0, 1.0));
nickjillings@2135 577 }
nickjillings@2135 578 for (var axis of element.axis) {
nicholas@2538 579 for (var i = 0; i < histograms[0].length; i++) {
nicholas@2538 580 var entry = ["" + histograms[0][i].lt.toPrecision(2) + "-" + histograms[0][i].rt.toPrecision(3)]
nickjillings@2135 581 for (var histogram of histograms) {
nickjillings@2135 582 entry.push(histogram[i].count);
nickjillings@2135 583 }
nickjillings@2135 584 chart.data.addRow(entry);
nickjillings@2135 585 }
nickjillings@2135 586 }
nickjillings@2135 587 chart.options = {
nicholas@2538 588 'title': 'Histogram of element: ' + element.id,
nicholas@2538 589 'width': 800,
nicholas@2538 590 'height': 700,
nicholas@2538 591 'bar': {
nicholas@2538 592 'groupWidth': '100%'
nicholas@2538 593 }
nicholas@2538 594 }
nicholas@2538 595 // Draw the chart
nickjillings@2135 596 chart.chart = new google.visualization.ColumnChart(chart.chartDOM);
nicholas@2538 597 chart.chart.draw(chart.data, chart.options);
nickjillings@2135 598 chart.buildTable();
nickjillings@2135 599 chart.writeLatex();
nickjillings@2135 600 this.charts.push(chart);
nickjillings@2135 601 }
nickjillings@2134 602 }
nickjillings@2131 603 }
nickjillings@2192 604 }
nickjillings@2192 605
nickjillings@2192 606 function Data() {
nickjillings@2192 607 // This holds the link between the server side calculations and the client side visualisation of the data
nicholas@2538 608
nickjillings@2192 609 // Dynamically generate the test filtering / page filterting tools
nickjillings@2192 610 var self = this;
nickjillings@2192 611 // Collect the test types and counts
nickjillings@2192 612 this.testSavedDiv = document.getElementById("test-saved");
nickjillings@2192 613 this.testSaves = null;
nickjillings@2192 614 this.selectURL = null;
nicholas@2538 615
nickjillings@2192 616 this.specification = new Specification();
nicholas@2538 617 get("../xml/test-schema.xsd").then(function (response) {
nickjillings@2192 618 var parse = new DOMParser();
nicholas@2538 619 self.specification.schema = parse.parseFromString(response, 'text/xml');
nicholas@2538 620 }, function (error) {
nickjillings@2192 621 console.log("ERROR: Could not get Test Schema");
nickjillings@2192 622 });
nicholas@2538 623 this.update = function (url) {
nickjillings@2192 624 var self = this;
nickjillings@2192 625 }
nicholas@2538 626
nicholas@2538 627 this.updateData = function (req_str) {
nickjillings@2193 628 // Now go get that data
nicholas@2538 629 get(req_str).then(function (response) {
nickjillings@2193 630 // Returns the data
nickjillings@2193 631 chartContext.valueData = JSON.parse(response);
nicholas@2538 632 }, function (error) {
nicholas@2538 633 console.error(error);
nicholas@2538 634 });
nickjillings@2193 635 }
nickjillings@2192 636 }
nickjillings@2192 637
nicholas@2538 638 var interfaceContext = new function () {
nickjillings@2192 639 // This creates the interface for the user to connect with the dynamic back-end to retrieve data
nickjillings@2192 640 this.rootDOM = document.createElement("div");
nickjillings@2193 641 this.getDataButton = {
nickjillings@2193 642 button: document.createElement("button"),
nickjillings@2193 643 parent: this,
nicholas@2538 644 handleEvent: function (event) {
nickjillings@2193 645 // Get the list of files:
nicholas@2538 646 var req_str = "../php/get_filtered_score.php" + this.parent.getFilterString();
nickjillings@2193 647 testData.updateData(req_str);
nickjillings@2193 648 }
nickjillings@2193 649 }
nickjillings@2193 650 this.getDataButton.button.textContent = "Get Filtered Data";
nicholas@2538 651 this.getDataButton.button.addEventListener("click", this.getDataButton);
nicholas@2538 652
nicholas@2287 653 this.getRawScoreData = {
nicholas@2287 654 root: document.createElement("div"),
nicholas@2287 655 csvDOM: document.createElement("button"),
nicholas@2287 656 jsonDOM: document.createElement("button"),
nicholas@2287 657 xmlDOM: document.createElement("button"),
nicholas@2287 658 presentDOM: document.createElement("div"),
nicholas@2287 659 parent: this,
nicholas@2287 660 XHR: new XMLHttpRequest(),
nicholas@2538 661 handleEvent: function (event) {
nicholas@2287 662 this.presentDOM.innerHTML = null;
nicholas@2538 663 var url = "../php/get_filtered_score.php" + this.parent.getFilterString();
nicholas@2538 664 this.XHR.open("GET", url + "&format=" + event.currentTarget.textContent, true);
nicholas@2538 665 switch (event.currentTarget.textContent) {
nicholas@2287 666 case "CSV":
nicholas@2538 667 this.XHR.onload = function () {
nicholas@2287 668 var file = [this.response];
nicholas@2538 669 var bb = new Blob(file, {
nicholas@2538 670 type: 'text/csv'
nicholas@2538 671 });
nicholas@2538 672 this.parent.presentDOM.appendChild(this.parent.generateLink(bb, "scores.csv"));
nicholas@2287 673 }
nicholas@2287 674 break;
nicholas@2287 675 case "JSON":
nicholas@2538 676 this.XHR.onload = function () {
nicholas@2287 677 var file = [this.response];
nicholas@2538 678 var bb = new Blob(file, {
nicholas@2538 679 type: 'application/json'
nicholas@2538 680 });
nicholas@2538 681 this.parent.presentDOM.appendChild(this.parent.generateLink(bb, "scores.json"));
nicholas@2287 682 }
nicholas@2287 683 break;
nicholas@2287 684 case "XML":
nicholas@2538 685 this.XHR.onload = function () {
nicholas@2287 686 var file = [this.response];
nicholas@2538 687 var bb = new Blob(file, {
nicholas@2538 688 type: 'text/xml'
nicholas@2538 689 });
nicholas@2538 690 this.parent.presentDOM.appendChild(this.parent.generateLink(bb, "scores.xml"));
nicholas@2287 691 }
nicholas@2287 692 break;
nicholas@2287 693 }
nicholas@2287 694 this.XHR.send();
nicholas@2287 695 },
nicholas@2538 696 generateLink: function (blob, filename) {
nicholas@2287 697 var dnlk = window.URL.createObjectURL(blob);
nicholas@2287 698 var a = document.createElement("a");
nicholas@2287 699 a.hidden = '';
nicholas@2287 700 a.href = dnlk;
nicholas@2287 701 a.download = filename;
nicholas@2287 702 a.textContent = "Save File";
nicholas@2287 703 return a;
nicholas@2287 704 }
nicholas@2287 705 }
nicholas@2538 706
nicholas@2287 707 this.getRawScoreData.root.appendChild(this.getRawScoreData.csvDOM);
nicholas@2287 708 this.getRawScoreData.root.appendChild(this.getRawScoreData.jsonDOM);
nicholas@2287 709 this.getRawScoreData.root.appendChild(this.getRawScoreData.xmlDOM);
nicholas@2287 710 this.getRawScoreData.root.appendChild(this.getRawScoreData.presentDOM);
nicholas@2287 711 this.getRawScoreData.XHR.parent = this.getRawScoreData;
nicholas@2287 712 this.getRawScoreData.csvDOM.textContent = 'CSV';
nicholas@2538 713 this.getRawScoreData.csvDOM.addEventListener('click', this.getRawScoreData);
nicholas@2287 714 this.getRawScoreData.jsonDOM.textContent = 'JSON';
nicholas@2538 715 this.getRawScoreData.jsonDOM.addEventListener('click', this.getRawScoreData);
nicholas@2287 716 this.getRawScoreData.xmlDOM.textContent = 'XML';
nicholas@2538 717 this.getRawScoreData.xmlDOM.addEventListener('click', this.getRawScoreData);
nicholas@2538 718
nickjillings@2195 719 this.testSaves = {
nickjillings@2195 720 json: null,
nickjillings@2195 721 selectedURL: null,
nickjillings@2195 722 inputs: [],
nickjillings@2195 723 parent: this
nickjillings@2195 724 };
nicholas@2538 725 this.init = function () {
nickjillings@2195 726 var self = this;
nicholas@2538 727 get('../php/get_tests.php?format=JSON').then(function (response) {
nickjillings@2195 728 document.getElementById("test-saved").innerHTML = null;
nickjillings@2195 729 var table = document.createElement("table");
nickjillings@2195 730 table.innerHTML = "<tr><td>Test Filename</td><td>Count</td><td>Include</td></tr>";
nickjillings@2195 731 self.testSaves.json = JSON.parse(response);
nickjillings@2195 732 for (var test of self.testSaves.json.tests) {
nickjillings@2195 733 var tableRow = document.createElement("tr");
nickjillings@2195 734 var tableRowFilename = document.createElement("td");
nickjillings@2195 735 tableRowFilename.textContent = test.testName;
nickjillings@2195 736 var tableRowCount = document.createElement("td");
nickjillings@2195 737 tableRowCount.textContent = test.files.length;
nickjillings@2195 738 tableRow.appendChild(tableRowFilename);
nickjillings@2195 739 tableRow.appendChild(tableRowCount);
nickjillings@2195 740 var tableRowInclude = document.createElement("td");
nickjillings@2195 741 var obj = {
nickjillings@2195 742 root: document.createElement("input"),
nickjillings@2195 743 parent: self.testSaves,
nicholas@2538 744 handleEvent: function (event) {
nickjillings@2195 745 this.parent.selectedURL = event.currentTarget.getAttribute("source");
nickjillings@2195 746 var self = this;
nicholas@2538 747 get(this.parent.selectedURL).then(function (response) {
nickjillings@2195 748 var parse = new DOMParser();
nicholas@2538 749 testData.specification.decode(parse.parseFromString(response, 'text/xml'));
nickjillings@2195 750 self.parent.parent.generateFilters(testData.specification);
nickjillings@2195 751 self.parent.parent.getFileCount();
nickjillings@2195 752 return true;
nicholas@2538 753 }, function (error) {
nicholas@2538 754 console.log("ERROR: Could not get" + url);
nickjillings@2195 755 return false;
nickjillings@2195 756 });
nickjillings@2195 757 }
nickjillings@2195 758 }
nickjillings@2195 759 obj.root.type = "radio";
nickjillings@2195 760 obj.root.name = "test-include";
nicholas@2538 761 obj.root.setAttribute("source", test.testName);
nicholas@2538 762 obj.root.addEventListener("change", obj);
nickjillings@2195 763 tableRowInclude.appendChild(obj.root);
nickjillings@2195 764 tableRow.appendChild(tableRowInclude);
nickjillings@2195 765 table.appendChild(tableRow);
nickjillings@2195 766 self.testSaves.inputs.push(obj);
nickjillings@2195 767 }
nickjillings@2195 768 document.getElementById("test-saved").appendChild(table);
nicholas@2538 769 }, function (error) {
nicholas@2538 770 console.error(error);
nicholas@2538 771 });
nickjillings@2195 772 }
nicholas@2538 773
nickjillings@2192 774 this.filterDOM = document.createElement("div");
nickjillings@2192 775 this.filterDOM.innerHTML = "<p>PreTest Filters</p><div id='filter-count'></div>";
nickjillings@2192 776 this.filterObjects = [];
nicholas@2538 777 this.generateFilters = function (specification) {
nickjillings@2192 778 // Filters are based on the pre and post global surverys
nicholas@2538 779 var FilterObject = function (parent, specification) {
nickjillings@2192 780 this.parent = parent;
nickjillings@2192 781 this.specification = specification;
nickjillings@2192 782 this.rootDOM = document.createElement("div");
nicholas@2538 783 this.rootDOM.innerHTML = "<span>ID: " + specification.id + ", Type: " + specification.type + "</span>";
nickjillings@2192 784 this.rootDOM.className = "filter-entry";
nicholas@2538 785 this.handleEvent = function (event) {
nicholas@2538 786 switch (this.specification.type) {
nickjillings@2192 787 case "number":
nickjillings@2192 788 var name = event.currentTarget.name;
nicholas@2538 789 eval("this." + name + " = event.currentTarget.value");
nickjillings@2192 790 break;
nickjillings@2192 791 case "checkbox":
nickjillings@2192 792 break;
nickjillings@2192 793 case "radio":
nickjillings@2192 794 break;
nickjillings@2192 795 }
nickjillings@2192 796 this.parent.getFileCount();
nickjillings@2192 797 }
nicholas@2538 798 this.getFilterPairs = function () {
nickjillings@2192 799 var pairs = [];
nicholas@2538 800 switch (this.specification.type) {
nickjillings@2192 801 case "number":
nickjillings@2192 802 if (this.min != "") {
nicholas@2538 803 pairs.push([specification.id + "-min", this.min]);
nickjillings@2192 804 }
nickjillings@2192 805 if (this.max != "") {
nicholas@2538 806 pairs.push([specification.id + "-max", this.max]);
nickjillings@2192 807 }
nickjillings@2192 808 break;
nickjillings@2192 809 case "radio":
nickjillings@2192 810 case "checkbox":
nicholas@2538 811 for (var i = 0; i < this.options.length; i++) {
nickjillings@2192 812 if (!this.options[i].checked) {
nicholas@2538 813 pairs.push([specification.id + "-exclude-" + i, specification.options[i].name]);
nickjillings@2192 814 }
nickjillings@2192 815 }
nickjillings@2192 816 break;
nickjillings@2192 817 }
nickjillings@2192 818 return pairs;
nickjillings@2192 819 }
nicholas@2538 820 switch (specification.type) {
nickjillings@2192 821 case "number":
nickjillings@2192 822 // Number can be ranged by min/max levels
nickjillings@2192 823 this.min = "";
nickjillings@2192 824 this.max = "";
nickjillings@2192 825 this.minDOM = document.createElement("input");
nickjillings@2192 826 this.minDOM.type = "number";
nickjillings@2192 827 this.minDOM.name = "min";
nicholas@2538 828 this.minDOM.addEventListener("change", this);
nickjillings@2192 829 this.minDOMText = document.createElement("span");
nickjillings@2192 830 this.minDOMText.textContent = "Minimum: ";
nickjillings@2192 831 var pairHolder = document.createElement("div");
nickjillings@2192 832 pairHolder.appendChild(this.minDOMText);
nickjillings@2192 833 pairHolder.appendChild(this.minDOM);
nickjillings@2192 834 this.rootDOM.appendChild(pairHolder);
nicholas@2538 835
nickjillings@2192 836 this.maxDOM = document.createElement("input");
nickjillings@2192 837 this.maxDOM.type = "number";
nickjillings@2192 838 this.maxDOM.name = "max";
nicholas@2538 839 this.maxDOM.addEventListener("change", this);
nickjillings@2192 840 this.maxDOMText = document.createElement("span");
nickjillings@2192 841 this.maxDOMText.textContent = "Maximum: ";
nickjillings@2192 842 var pairHolder = document.createElement("div");
nickjillings@2192 843 pairHolder.appendChild(this.maxDOMText);
nickjillings@2192 844 pairHolder.appendChild(this.maxDOM);
nickjillings@2192 845 this.rootDOM.appendChild(pairHolder);
nickjillings@2192 846 break;
nickjillings@2192 847 case "radio":
nickjillings@2192 848 case "checkbox":
nickjillings@2192 849 this.options = [];
nicholas@2538 850 for (var i = 0; i < specification.options.length; i++) {
nickjillings@2192 851 var option = specification.options[i];
nickjillings@2192 852 var pairHolder = document.createElement("div");
nickjillings@2192 853 var text = document.createElement("span");
nickjillings@2192 854 text.textContent = option.text;
nickjillings@2192 855 var check = document.createElement("input");
nickjillings@2192 856 check.type = "checkbox";
nicholas@2538 857 check.setAttribute("option-index", i);
nickjillings@2192 858 check.checked = true;
nicholas@2538 859 check.addEventListener("click", this);
nickjillings@2192 860 this.options.push(check);
nickjillings@2192 861 pairHolder.appendChild(text);
nickjillings@2192 862 pairHolder.appendChild(check);
nickjillings@2192 863 this.rootDOM.appendChild(pairHolder);
nickjillings@2192 864 }
nickjillings@2192 865 break;
nickjillings@2192 866 default:
nickjillings@2192 867 break;
nickjillings@2192 868 }
nickjillings@2192 869 }
nicholas@2251 870 var options = [];
nicholas@2538 871 if (specification.preTest) {
nicholas@2251 872 options = options.concat(specification.preTest.options);
nicholas@2251 873 }
nicholas@2251 874 if (specification.postTest) {
nicholas@2251 875 options = options.concat(specification.postTest.options);
nicholas@2251 876 }
nicholas@2251 877 for (var survey_entry of options) {
nicholas@2538 878 switch (survey_entry.type) {
nickjillings@2192 879 case "number":
nickjillings@2192 880 case "radio":
nickjillings@2192 881 case "checkbox":
nicholas@2538 882 var node = new FilterObject(this, survey_entry);
nickjillings@2192 883 this.filterObjects.push(node);
nickjillings@2192 884 this.filterDOM.appendChild(node.rootDOM);
nickjillings@2192 885 break;
nickjillings@2192 886 default:
nickjillings@2192 887 break;
nickjillings@2192 888 }
nickjillings@2192 889 }
nickjillings@2192 890 document.getElementById("test-saved").appendChild(this.filterDOM);
nickjillings@2193 891 document.getElementById("test-saved").appendChild(this.getDataButton.button);
nicholas@2287 892 document.getElementById("test-saved").appendChild(this.getRawScoreData.root);
nickjillings@2192 893 }
nicholas@2538 894 this.getFilterString = function () {
nickjillings@2192 895 var pairs = [];
nickjillings@2192 896 for (var obj of this.filterObjects) {
nickjillings@2192 897 pairs = pairs.concat(obj.getFilterPairs());
nickjillings@2192 898 }
nicholas@2538 899 var req_str = "?url=" + this.testSaves.selectedURL;
nickjillings@2192 900 var index = 0;
nicholas@2538 901 while (pairs[index] != undefined) {
nickjillings@2192 902 req_str += '&';
nicholas@2538 903 req_str += pairs[index][0] + "=" + pairs[index][1];
nickjillings@2192 904 index++;
nickjillings@2192 905 }
nickjillings@2193 906 return req_str;
nickjillings@2193 907 }
nicholas@2538 908 this.getFilteredUrlArray = function () {
nicholas@2538 909 var req_str = "../php/get_filtered_count.php" + this.getFilterString();
nicholas@2538 910 return get(req_str).then(function (response) {
nickjillings@2192 911 var urls = JSON.parse(response);
nickjillings@2193 912 return urls.urls;
nicholas@2538 913 }, function (error) {
nickjillings@2193 914 console.error(error);
nickjillings@2193 915 });
nickjillings@2193 916 }
nicholas@2538 917 this.getFileCount = function () {
nickjillings@2193 918 // First we must get the filter pairs
nicholas@2538 919 this.getFilteredUrlArray().then(function (response) {
nicholas@2538 920 var str = "Filtered to " + response.length + " file";
nickjillings@2193 921 if (response.length != 1) {
nickjillings@2192 922 str += "s.";
nickjillings@2192 923 } else {
nickjillings@2192 924 str += ".";
nickjillings@2192 925 }
nickjillings@2192 926 document.getElementById("filter-count").textContent = str;
nicholas@2538 927 }, function (error) {});
nickjillings@2192 928 }
nicholas@2538 929
nickjillings@2195 930 this.init();
nicholas@2538 931 }