Mercurial > hg > webaudioevaluationtool
changeset 3013:5acdaf4c14c3
Merge branch 'vnext' into Dev_main
author | Nicholas Jillings <n.g.r.jillings@se14.qmul.ac.uk> |
---|---|
date | Wed, 06 Sep 2017 14:50:13 +0100 |
parents | 03b9e845f730 (diff) 3342bc1f3256 (current diff) |
children | 7eb029906f81 |
files | js/core.js |
diffstat | 13 files changed, 841 insertions(+), 543 deletions(-) [+] |
line wrap: on
line diff
--- a/.gitignore Wed Sep 06 14:50:00 2017 +0100 +++ b/.gitignore Wed Sep 06 14:50:13 2017 +0100 @@ -11,3 +11,6 @@ *.DS_STORE *.swp *.swo +saves/ratings/* +saves/timelines/* +saves/timelines_movement/*
--- a/interfaces/ape.js Wed Sep 06 14:50:00 2017 +0100 +++ b/interfaces/ape.js Wed Sep 06 14:50:13 2017 +0100 @@ -21,136 +21,15 @@ testContent.id = 'testContent'; // Bindings for interfaceContext - interfaceContext.checkAllPlayed = function () { - var hasBeenPlayed = audioEngineContext.checkAllPlayed(); - if (hasBeenPlayed.length > 0) // if a fragment has not been played yet - { - var str = ""; - if (hasBeenPlayed.length > 1) { - for (var i = 0; i < hasBeenPlayed.length; i++) { - var ao_id = audioEngineContext.audioObjects[hasBeenPlayed[i]].interfaceDOM.getPresentedId(); - str = str + ao_id; // start from 1 - if (i < hasBeenPlayed.length - 2) { - str += ", "; - } else if (i == hasBeenPlayed.length - 2) { - str += " or "; - } - } - str = 'You have not played fragments ' + str + ' yet. Please listen, rate and comment all samples before submitting.'; - } else { - str = 'You have not played fragment ' + (audioEngineContext.audioObjects[hasBeenPlayed[0]].interfaceDOM.getPresentedId()) + ' yet. Please listen, rate and comment all samples before submitting.'; - } - this.storeErrorNode(str); - interfaceContext.lightbox.post("Message", str); - return false; - } - return true; - }; interfaceContext.checkAllMoved = function () { - var state = true; - var str = 'You have not moved the following sliders. '; - for (var i = 0; i < this.interfaceSliders.length; i++) { - var interfaceTID = []; - for (var j = 0; j < this.interfaceSliders[i].metrics.length; j++) { - var ao_id = this.interfaceSliders[i].sliders[j].getAttribute("trackIndex"); - if (this.interfaceSliders[i].metrics[j].wasMoved === false && audioEngineContext.audioObjects[ao_id].interfaceDOM.canMove()) { - state = false; - interfaceTID.push(j); - } - } - if (interfaceTID.length !== 0) { - var interfaceName = this.interfaceSliders[i].interfaceObject.title; - if (interfaceName === undefined) { - str += 'On axis ' + String(i + 1) + ' you must move '; - } else { - str += 'On axis "' + interfaceName + '" you must move '; - } - if (interfaceTID.length == 1) { - str += 'slider ' + (audioEngineContext.audioObjects[interfaceTID[0]].interfaceDOM.getPresentedId()) + '. '; // start from 1 - } else { - str += 'sliders '; - for (var k = 0; k < interfaceTID.length - 1; k++) { - str += (audioEngineContext.audioObjects[interfaceTID[k]].interfaceDOM.getPresentedId()) + ', '; // start from 1 - } - str += (audioEngineContext.audioObjects[interfaceTID[interfaceTID.length - 1]].interfaceDOM.getPresentedId()) + '. '; - } - } - } - if (state !== true) { - this.storeErrorNode(str); - interfaceContext.lightbox.post("Message", str); - console.log(str); - } - return state; + return module.checkAllMoved(); }; interfaceContext.checkScaleRange = function () { - var audioObjs = audioEngineContext.audioObjects; - var audioHolder = testState.stateMap[testState.stateIndex]; - var interfaceObject = this.interfaceSliders[0].interfaceObject; - var state = true; - var str = ''; - this.interfaceSliders.forEach(function (sliderHolder, i) { - var scales = (function () { - var scaleRange = interfaceObject.options.find(function (a) { - return a.name == "scalerange"; - }); - return { - min: scaleRange.min, - max: scaleRange.max - }; - })(); - var range = sliderHolder.sliders.reduce(function (a, b) { - var v = convSliderPosToRate(b) * 100.0; - return { - min: Math.min(a.min, v), - max: Math.max(a.max, v) - }; - }, { - min: 100, - max: 0 - }); - if (range.min >= scales.min || range.max <= scales.max) { - state = false; - str += 'On axis "' + sliderHolder.interfaceObject.title + '" you have not used the full width of the scale. '; - } - }); - if (state !== true) { - this.storeErrorNode(str); - interfaceContext.lightbox.post("Message", str); - console.log(str); - } - return state; + return module.checkScaleRange(); }; - Interface.prototype.objectSelected = null; - Interface.prototype.objectMoved = false; - Interface.prototype.selectObject = function (object) { - if (this.objectSelected === null) { - this.objectSelected = object; - this.objectMoved = false; - } - }; - Interface.prototype.moveObject = function () { - if (this.objectMoved === false) { - this.objectMoved = true; - } - }; - Interface.prototype.releaseObject = function () { - this.objectSelected = null; - this.objectMoved = false; - }; - Interface.prototype.getSelectedObject = function () { - return this.objectSelected; - }; - Interface.prototype.hasSelectedObjectMoved = function () { - return this.objectMoved; - }; - - // Bindings for slider interfaces - Interface.prototype.interfaceSliders = []; - // Bindings for audioObjects // Create the top div for the Title element @@ -220,12 +99,13 @@ interfaceContext.insertPoint.appendChild(testContent); // Load the full interface + window.module = new ape(); testState.initialise(); testState.advanceState(); - } function loadTest(audioHolderObject) { + module.clear(); var width = window.innerWidth; var height = window.innerHeight; var id = audioHolderObject.id; @@ -246,15 +126,10 @@ document.getElementById("test-title").textContent = audioHolderObject.title; } - // Delete outside reference document.getElementById("outside-reference-holder").innerHTML = ""; var interfaceObj = interfaceContext.getCombinedInterfaces(audioHolderObject); - interfaceObj.forEach(function (interfaceObjectInstance) { - // Create the div box to center align - interfaceContext.interfaceSliders.push(new interfaceSliderHolder(interfaceObjectInstance, audioHolderObject)); - }); interfaceObj.forEach(function (interface) { interface.options.forEach(function (option) { if (option.type == "show") { @@ -298,113 +173,7 @@ var loopPlayback = audioHolderObject.loop; - var currentTestHolder = document.createElement('audioHolder'); - currentTestHolder.id = audioHolderObject.id; - currentTestHolder.repeatCount = audioHolderObject.repeatCount; - - // Find all the audioElements from the audioHolder - $(audioHolderObject.audioElements).each(function (index, element) { - // Find URL of track - // In this jQuery loop, variable 'this' holds the current audioElement. - var audioObject = audioEngineContext.newTrack(element); - // Check if an outside reference - if (element.type == 'outside-reference') { - // Construct outside reference; - var orNode = new outsideReferenceDOM(audioObject, index, document.getElementById("outside-reference-holder")); - audioObject.bindInterface(orNode); - } else { - // Create a slider per track - var sliderNode = new sliderObject(audioObject, interfaceObj, index); - audioObject.bindInterface(sliderNode); - interfaceContext.commentBoxes.createCommentBox(audioObject); - } - }); - - // Initialse the interfaceSlider object metrics - - $('.track-slider').mousedown(function (event) { - interfaceContext.selectObject($(this)[0]); - }); - $('.track-slider').on('touchstart', null, function (event) { - interfaceContext.selectObject($(this)[0]); - }); - - $('.track-slider').mousemove(function (event) { - event.preventDefault(); - }); - - $('.slider').mousemove(function (event) { - event.preventDefault(); - var obj = interfaceContext.getSelectedObject(); - if (obj === null) { - return; - } - var move = event.clientX - 6; - var w = $(event.currentTarget).width(); - move = Math.max(50, move); - move = Math.min(w + 50, move); - $(obj).css("left", move + "px"); - interfaceContext.moveObject(); - }); - - $('.slider').on('touchmove', null, function (event) { - event.preventDefault(); - var obj = interfaceContext.getSelectedObject(); - if (obj === null) { - return; - } - var move = event.originalEvent.targetTouches[0].clientX - 6; - var w = $(event.currentTarget).width(); - move = Math.max(50, move); - move = Math.min(w + 50, move); - $(obj).css("left", move + "px"); - interfaceContext.moveObject(); - }); - - $(document).mouseup(function (event) { - event.preventDefault(); - var obj = interfaceContext.getSelectedObject(); - if (obj === null) { - return; - } - var interfaceID = obj.parentElement.getAttribute("interfaceid"); - var trackID = obj.getAttribute("trackindex"); - var id; - if (interfaceContext.hasSelectedObjectMoved() === true) { - var l = $(obj).css("left"); - id = obj.getAttribute('trackIndex'); - var time = audioEngineContext.timer.getTestTime(); - var rate = convSliderPosToRate(obj); - audioEngineContext.audioObjects[id].metric.moved(time, rate); - interfaceContext.interfaceSliders[interfaceID].metrics[trackID].moved(time, rate); - console.log("slider " + id + " moved to " + rate + ' (' + time + ')'); - obj.setAttribute("slider-value", convSliderPosToRate(obj)); - } else { - id = Number(obj.attributes.trackIndex.value); - //audioEngineContext.metric.sliderPlayed(id); - audioEngineContext.play(id); - } - interfaceContext.releaseObject(); - }); - - $('.slider').on('touchend', null, function (event) { - var obj = interfaceContext.getSelectedObject(); - if (obj === null) { - return; - } - var interfaceID = obj.parentElement.getAttribute("interfaceid"); - var trackID = obj.getAttribute("trackindex"); - if (interfaceContext.hasSelectedObjectMoved() === true) { - var l = $(obj).css("left"); - var id = obj.getAttribute('trackIndex'); - var time = audioEngineContext.timer.getTestTime(); - var rate = convSliderPosToRate(obj); - audioEngineContext.audioObjects[id].metric.moved(time, rate); - interfaceContext.interfaceSliders[interfaceID].metrics[trackID].moved(time, rate); - console.log("slider " + id + " moved to " + rate + ' (' + time + ')'); - } - interfaceContext.releaseObject(); - }); + module.initialisePage(audioHolderObject); var interfaceList = audioHolderObject.interfaces.concat(specification.interfaces); for (var k = 0; k < interfaceList.length; k++) { @@ -447,237 +216,465 @@ //testWaitIndicator(); } -function interfaceSliderHolder(interfaceObject, page) { - this.sliders = []; - this.metrics = []; - this.id = document.getElementsByClassName("sliderCanvasDiv").length; - this.name = interfaceObject.name; - this.interfaceObject = interfaceObject; - this.sliderDOM = document.createElement('div'); - this.sliderDOM.className = 'sliderCanvasDiv'; - this.sliderDOM.id = 'sliderCanvasHolder-' + this.id; - this.imageHolder = (function () { - var imageController = {}; - imageController.root = document.createElement("div"); - imageController.root.className = "imageController"; - imageController.img = document.createElement("img"); - imageController.root.appendChild(imageController.img); - imageController.setImage = function (src) { - imageController.img.src = ""; - if (typeof src !== "string" || src.length === undefined) { +function ape() { + var axis = [] + var DOMRoot = document.getElementById("slider-holder"); + var AOIs = []; + var page = undefined; + + function audioObjectInterface(audioObject, parent) { + // The audioObject communicates with this object + var playing = false; + var sliders = []; + this.enable = function () { + sliders.forEach(function (s) { + s.enable(); + }) + } + + this.updateLoading = function (p) { + sliders.forEach(function (s) { + s.updateLoading(p); + }) + } + + this.startPlayback = function () { + playing = true; + sliders.forEach(function (s) { + s.playing(); + }); + } + + this.stopPlayback = function () { + playing = false; + sliders.forEach(function (s) { + s.stopped(); + }); + } + + this.getValue = function () { + return sliders[0].value(); + } + + this.getPresentedId = function () { + return sliders[0].label; + } + + this.canMove = function () { + return true; + } + + this.exportXMLDOM = function (audioObject) { + var elements = []; + sliders.forEach(function (s) { + elements.push(s.exportXMLDOM()); + }); + return elements; + } + + this.error = function () { + sliders.forEach(function (s) { + s.error(); + }); + } + + this.addSlider = function (s) { + sliders.push(s); + } + + this.clicked = function (event) { + if (!playing) { + audioEngineContext.play(audioObject.id); + } else { + audioEngineContext.stop(); + } + playing = !playing; + } + + this.pageXMLSave = function (store) { + var inject = audioObject.storeDOM.getElementsByTagName("metric")[0]; + sliders.forEach(function (s) { + s.pageXMLSave(inject); + }); + } + + } + + function axisObject(interfaceObject, parent) { + + function sliderInterface(AOI, axisInterface) { + var trackObj = document.createElement('div'); + var labelHolder = document.createElement("span"); + var label = ""; + var metric = new metricTracker(this); + trackObj.align = "center"; + trackObj.className = 'track-slider track-slider-disabled'; + trackObj.appendChild(labelHolder); + trackObj.style.left = (Math.random() * $(sliderRail).width()) + 50 + "px"; + axisInterface.sliderRail.appendChild(trackObj); + metric.initialise(this.value); + this.setLabel = function (s) { + label = s; + } + this.resize = function (event) { + var width = $(axisInterface.sliderRail).width(); + var w = Number(value * width + 50); + trackObj.style.left = String(w) + "px"; + } + this.playing = function () { + trackObj.classList.add("track-slider-playing"); + } + this.stopped = function () { + trackObj.classList.remove("track-slider-playing"); + } + this.enable = function () { + trackObj.addEventListener("mousedown", this); + trackObj.addEventListener("mouseup", this); + trackObj.addEventListener("touchstart", this); + trackObj.classList.remove("track-slider-disabled"); + labelHolder.textContent = label; + } + this.updateLoading = function (progress) { + labelHolder.textContent = progress + "%"; + } + this.exportXMLDOM = function () { + var node = storage.document.createElement('value'); + node.setAttribute("interface-name", axisInterface.name) + node.textContent = this.value(); + return node; + } + this.error = function () { + trackObj.classList.add("error-colour"); + trackObj.removeEventListener("mousedown"); + trackObj.removeEventListener("mouseup"); + trackObj.removeEventListener("touchstart"); + } + var timing = undefined; + this.handleEvent = function (e) { + // This is only for the mousedown / touchdown + if (e.preventDefault) { + e.preventDefault(); + } + if (e.type == "mousedown" || e.type == "touchstart") { + axisInterface.mousedown(this); + } else if (e.type == "mouseup") { + axisInterface.mouseup(this); + } + } + this.clicked = function (e) { + AOI.clicked(); + } + this.pageXMLSave = function (inject) { + var nodes = metric.exportXMLDOM(inject); + nodes.forEach(function (elem) { + var name = elem.getAttribute("name"); + if (name == "elementTracker" || name == "elementTrackerFull" || name == "elementInitialPosition" || name == "elementFlagMoved") { + elem.setAttribute("interface-name", axisInterface.name); + } else { + inject.removeChild(elem); + } + }); + } + this.hasMoved = function () { + return metric.wasMoved; + } + Object.defineProperties(this, { + "DOM": { + "value": trackObj + }, + "value": { + "value": function () { + var maxPix = $(axisInterface.sliderRail).width(); + var pix = trackObj.style.left.substr(0, trackObj.style.left.length - 2); + return (pix - 50) / maxPix; + } + }, + "moveToPixel": { + "value": function (pix) { + var t = audioEngineContext.timer.getTestTime(); + trackObj.style.left = String(pix) + "px"; + metric.moved(t, this.value); + } + }, + "label": { + "get": function () { + return label; + }, + "set": function () {} + } + }); + } + + function createScaleMarkers(interfaceObject, root, w) { + interfaceObject.scales.forEach(function (scaleObj) { + var position = Number(scaleObj.position) * 0.01; + var pixelPosition = (position * w) + 50; + var scaleDOM = document.createElement('span'); + scaleDOM.className = "ape-marker-text"; + scaleDOM.textContent = scaleObj.text; + scaleDOM.setAttribute('value', position); + root.appendChild(scaleDOM); + scaleDOM.style.left = Math.floor((pixelPosition - ($(scaleDOM).width() / 2))) + 'px'; + }, this); + } + var sliders = []; + var UI = { + selected: undefined, + startTime: undefined + } + this.name = interfaceObject.name; + var DOMRoot = document.createElement("div"); + parent.getDOMRoot().appendChild(DOMRoot); + DOMRoot.className = "sliderCanvasDiv"; + DOMRoot.id = "sliderCanvasHolder-" + this.name; + var sliders = []; + + var axisTitle = document.createElement("div"); + axisTitle.className = "pageTitle"; + axisTitle.align = "center"; + var titleSpan = document.createElement('span'); + titleSpan.id = "pageTitle-" + this.name; + if (interfaceObject.title !== undefined && typeof interfaceObject.title == "string") { + titleSpan.textContent = interfaceObject.title; + } else { + titleSpan.textContent = "Axis " + String(this.id + 1); + } + axisTitle.appendChild(titleSpan); + DOMRoot.appendChild(axisTitle); + + var imageHolder = (function () { + var imageController = {}; + imageController.root = document.createElement("div"); + imageController.root.className = "imageController"; + imageController.img = document.createElement("img"); + imageController.root.appendChild(imageController.img); + imageController.setImage = function (src) { + imageController.img.src = ""; + if (typeof src !== "string" || src.length === undefined) { + return; + } + imageController.img.src = src; + }; + return imageController; + })(); + if (interfaceObject.image !== undefined || page.audioElements.some(function (a) { + return a.image !== undefined; + })) { + DOMRoot.appendChild(imageHolder.root); + imageHolder.setImage(interfaceObject.image); + } + + // Now create the slider box to hold the fragment sliders + var sliderRail = document.createElement("div"); + sliderRail.id = "sliderrail-" + this.name; + sliderRail.className = "slider"; + sliderRail.align = "left"; + DOMRoot.appendChild(sliderRail); + + // Create the div to hold any scale objects + var scale = document.createElement("div"); + scale.className = "sliderScale"; + scale.id = "slider-scale-holder-" + this.name; + scale.slign = "left"; + DOMRoot.appendChild(scale); + createScaleMarkers(interfaceObject, scale, $(sliderRail).width()); + + this.resize = function (event) { + var w = $(sliderRail).width(); + var marginsize = 50; + sliders.forEach(function (s) { + s.resize(); + }); + scale.innerHTML = ""; + createScaleMarkers(interfaceObject, scale, $(sliderRail).width()); + } + this.playing = function (id) { + var node = audioEngineContext.audioObjects.find(function (a) { + return a.id == id; + }); + if (node === undefined) { + this.imageHolder.setImage(interfaceObject.image || ""); return; } - imageController.img.src = src; - }; - return imageController; - })(); + var imgurl = node.specification.image || interfaceObject.image || ""; + this.imageHolder.setImage(imgurl); + } + this.stopped = function () { + var imgurl = interfaceObject.image || ""; + this.imageHolder.setImage(imgurl); + } + this.addSlider = function (aoi) { + var node = new sliderInterface(aoi, this); + sliders.push(node); + return node; + } + this.mousedown = function (sliderUI) { + UI.selected = sliderUI; + UI.startTime = new Date(); + } + this.mouseup = function (sliderUI) { + var delta = new Date() - UI.startTime; + if (delta < 200) { + UI.selected.clicked(); + } + UI.selected = undefined; + UI.startTime = undefined; + } + this.handleEvent = function (event) { + if (event.preventDefault) { + event.preventDefault(); + } + if (UI.selected === undefined) { + return; + } + if (event.type == "mousemove") { + var move = event.clientX - 6; + var w = $(sliderRail).width(); + move = Math.max(50, move); + move = Math.min(w + 50, move); + UI.selected.moveToPixel(move); + } else if (event.type == "touchmove") { + var move = event.originalEvent.targetTouches[0].clientX - 6; + var w = $(event.currentTarget).width(); + move = Math.max(50, move); + move = Math.min(w + 50, move); + UI.selected.moveToPixel(move); + } + } + this.checkAllMoved = function () { + var notMoved = sliders.filter(function (s) { + return !s.hasMoved(); + }); + if (notMoved.length !== 0) { + var ls = []; + notMoved.forEach(function (s) { + ls.push(s.label); + }) + var str = "On axis \"" + interfaceObject.title + "\", "; + if (ls.length == 1) { + str += "slider " + ls[0]; + } else { + str += "sliders " + [ls.slice(0, ls.length - 1).join(", ")].concat(ls[ls.length - 1]).join(" and "); + } + str += "."; + return str; + } else { + return ""; + } + } + this.checkScaleRange = function () { + var scaleRange = interfaceObject.options.find(function (a) { + return a.name == "scalerange"; + }); + if (scaleRange === undefined) { + return ""; + } + var scales = { + min: scaleRange.min, + max: scaleRange.max + }; + var maxSlider = sliders.reduce(function (a, b) { + return Math.max(a, b.value); + }, 0); + var minSlider = sliders.reduce(function (a, b) { + return Math.min(a, b.value); + }, 100); + if (minSlider >= scales.min || maxSlider <= scales.max) { + return "On axis \"" + interfaceObject.title + "\", you have not used the required width of the scales"; + } + return ""; + } + sliderRail.addEventListener("mousemove", this); + sliderRail.addEventListener("touchmove", this); + Object.defineProperties(this, { + "sliderRail": { + "value": sliderRail + } + }); + } + this.getDOMRoot = function () { + return DOMRoot; + } + this.getPage = function () { + return page; + } + this.clear = function () { + page = undefined; + axis = []; + AOIs = []; + DOMRoot.innerHTML = ""; + } + this.initialisePage = function (page_init) { + this.clear(); + page = page_init; + var interfaceObj = interfaceContext.getCombinedInterfaces(page); + // Create each of the interface axis + interfaceObj.forEach(function (i) { + var node = new axisObject(i, this); + axis.push(node); + }, this); - var pagetitle = document.createElement('div'); - pagetitle.className = "pageTitle"; - pagetitle.align = "center"; - var titleSpan = document.createElement('span'); - titleSpan.id = "pageTitle-" + this.id; - if (interfaceObject.title !== undefined && typeof interfaceObject.title == "string") { - titleSpan.textContent = interfaceObject.title; - } else { - titleSpan.textContent = "Axis " + String(this.id + 1); + // Create the audioObject interface objects for each aO. + page.audioElements.forEach(function (element, index) { + var audioObject = audioEngineContext.newTrack(element); + if (element.type == 'outside-reference') { + // Construct outside reference; + var orNode = new outsideReferenceDOM(audioObject, index, document.getElementById("outside-reference-holder")); + audioObject.bindInterface(orNode); + } else { + var aoi = new audioObjectInterface(audioObject, this); + AOIs.push(aoi); + var label = interfaceContext.getLabel(page.label, index, page.labelStart); + axis.forEach(function (a) { + var node = a.addSlider(aoi); + node.setLabel(label); + aoi.addSlider(node); + audioObject.bindInterface(aoi); + }); + } + }); } - pagetitle.appendChild(titleSpan); - this.sliderDOM.appendChild(pagetitle); - - if (interfaceObject.image !== undefined || page.audioElements.some(function (a) { - return a.image !== undefined; - })) { - this.sliderDOM.appendChild(this.imageHolder.root); - this.imageHolder.setImage(interfaceObject.image); + this.checkAllMoved = function () { + var str = "You have not moved the following sliders. " + var cont = true; + axis.forEach(function (a) { + var msg = a.checkAllMoved(); + if (msg.length > 0) { + cont = false; + str += msg; + } + }); + if (!cont) { + interfaceContext.lightbox.post("Error", str); + interfaceContext.storeErrorNode(str); + console.log(str); + } + return cont; } - // Create the slider box to hold the slider elements - this.canvas = document.createElement('div'); - if (this.name !== undefined) - this.canvas.id = 'slider-' + this.name; - else - this.canvas.id = 'slider-' + this.id; - this.canvas.setAttribute("interfaceid", this.id); - this.canvas.className = 'slider'; - this.canvas.align = "left"; - this.canvas.addEventListener('dragover', function (event) { - event.preventDefault(); - event.dataTransfer.effectAllowed = 'none'; - event.dataTransfer.dropEffect = 'copy'; - return false; - }, false); - this.sliderDOM.appendChild(this.canvas); - - // Create the div to hold any scale objects - this.scale = document.createElement('div'); - this.scale.className = 'sliderScale'; - this.scale.id = 'sliderScaleHolder-' + this.id; - this.scale.align = 'left'; - this.sliderDOM.appendChild(this.scale); - var positionScale = this.canvas.style.width.substr(0, this.canvas.style.width.length - 2); - var offset = 50; - var dest = document.getElementById("slider-holder").appendChild(this.sliderDOM); - interfaceObject.scales.forEach(function (scaleObj) { - var position = Number(scaleObj.position) * 0.01; - var pixelPosition = (position * $(this.canvas).width()) + offset; - var scaleDOM = document.createElement('span'); - scaleDOM.className = "ape-marker-text"; - scaleDOM.textContent = scaleObj.text; - scaleDOM.setAttribute('value', position); - this.scale.appendChild(scaleDOM); - scaleDOM.style.left = Math.floor((pixelPosition - ($(scaleDOM).width() / 2))) + 'px'; - }, this); - - this.createSliderObject = function (audioObject, label) { - var trackObj = document.createElement('div'); - trackObj.align = "center"; - trackObj.className = 'track-slider track-slider-disabled track-slider-' + audioObject.id; - trackObj.id = 'track-slider-' + this.id + '-' + audioObject.id; - trackObj.setAttribute('trackIndex', audioObject.id); - if (this.name !== undefined) { - trackObj.setAttribute('interface-name', this.name); - } else { - trackObj.setAttribute('interface-name', this.id); + this.checkScaleRange = function () { + var str = ""; + var cont = true; + axis.forEach(function (a) { + var msg = a.checkScaleRange(); + if (msg.length > 0) { + cont = false; + str += msg; + } + }); + if (!cont) { + interfaceContext.lightbox.post("Error", str); + interfaceContext.storeErrorNode(str); + console.log(str); } - var offset = 50; - // Distribute it randomnly - var w = window.innerWidth - (offset + 8) * 2; - w = Math.random() * w; - w = Math.floor(w + (offset + 8)); - trackObj.style.left = w + 'px'; - this.canvas.appendChild(trackObj); - this.sliders.push(trackObj); - this.metrics.push(new metricTracker(this)); - var labelHolder = document.createElement("span"); - labelHolder.textContent = label; - trackObj.appendChild(labelHolder); - var rate = convSliderPosToRate(trackObj); - this.metrics[this.metrics.length - 1].initialise(rate); - trackObj.setAttribute("slider-value", rate); - return trackObj; - }; - - this.resize = function (event) { - var sliderDiv = this.canvas; - var sliderScaleDiv = this.scale; - var width = $(sliderDiv).width(); - var marginsize = 50; - // Move sliders into new position - this.sliders.forEach(function (slider, index) { - var pix = Number(slider.getAttribute("slider-value")) * width; - slider.style.left = (pix + marginsize) + 'px'; - }); - - // Move scale labels - for (var index = 0; index < this.scale.children.length; index++) { - var scaleObj = this.scale.children[index]; - var position = Number(scaleObj.attributes.value.value); - var pixelPosition = (position * width) + marginsize; - scaleObj.style.left = Math.floor((pixelPosition - ($(scaleObj).width() / 2))) + 'px'; - } - }; - - this.playing = function (id) { - var node = audioEngineContext.audioObjects.find(function (a) { - return a.id == id; - }); - if (node === undefined) { - this.imageHolder.setImage(interfaceObject.image || ""); - return; - } - var imgurl = node.specification.image || interfaceObject.image || ""; - this.imageHolder.setImage(imgurl); + return cont; } -} - -function sliderObject(audioObject, interfaceObjects, index) { - // Create a new slider object; - this.parent = audioObject; - this.trackSliderObjects = []; - this.label = interfaceContext.getLabel(audioObject.specification.parent.label, index, audioObject.specification.parent.labelStart); - this.playing = false; - for (var i = 0; i < interfaceContext.interfaceSliders.length; i++) { - var trackObj = interfaceContext.interfaceSliders[i].createSliderObject(audioObject, this.label); - this.trackSliderObjects.push(trackObj); - } - - // Onclick, switch playback to that track - - this.enable = function () { - if (this.parent.state == 1) { - $(this.trackSliderObjects).each(function (i, trackObj) { - $(trackObj).removeClass('track-slider-disabled'); + this.pageXMLSave = function (store, pageSpecification) { + if (axis.length > 1) { + AOIs.forEach(function (ao) { + ao.pageXMLSave(store); }); } - }; - this.updateLoading = function (progress) { - if (progress != 100) { - progress = String(progress); - progress = progress.split('.')[0]; - this.trackSliderObjects[0].children[0].textContent = progress + '%'; - } else { - this.trackSliderObjects[0].children[0].textContent = this.label; - } - }; - this.startPlayback = function () { - $('.track-slider').removeClass('track-slider-playing'); - var name = ".track-slider-" + this.parent.id; - $(name).addClass('track-slider-playing'); - interfaceContext.commentBoxes.highlightById(audioObject.id); - $('.outside-reference').removeClass('track-slider-playing'); - this.playing = true; - - if (this.parent.specification.parent.playOne || specification.playOne) { - $('.track-slider').addClass('track-slider-disabled'); - $('.outside-reference').addClass('track-slider-disabled'); - } - interfaceContext.interfaceSliders.forEach(function (ts) { - ts.playing(this.parent.id); - }, this); - }; - this.stopPlayback = function () { - if (this.playing) { - this.playing = false; - var name = ".track-slider-" + this.parent.id; - $(name).removeClass('track-slider-playing'); - $('.track-slider').removeClass('track-slider-disabled'); - $('.outside-reference').removeClass('track-slider-disabled'); - var box = interfaceContext.commentBoxes.boxes.find(function (a) { - return a.id === audioObject.id; - }); - if (box) { - box.highlight(false); - } - } - }; - this.exportXMLDOM = function (audioObject) { - // Called by the audioObject holding this element. Must be present - var obj = []; - $(this.trackSliderObjects).each(function (i, trackObj) { - var node = storage.document.createElement('value'); - if (trackObj.getAttribute("interface-name") !== "null") { - node.setAttribute("interface-name", trackObj.getAttribute("interface-name")); - } - node.textContent = convSliderPosToRate(trackObj); - obj.push(node); - }); - - return obj; - }; - this.getValue = function () { - return convSliderPosToRate(this.trackSliderObjects[0]); - }; - this.getPresentedId = function () { - return this.label; - }; - this.canMove = function () { - return true; - }; - this.error = function () { - // audioObject has an error!! - this.playback.textContent = "Error"; - $(this.playback).addClass("error-colour"); - }; + } } function outsideReferenceDOM(audioObject, index, inject) { @@ -833,32 +830,5 @@ // pageSpecification is the current page node configuration // To create new XML nodes, use storage.document.createElement(); - if (interfaceContext.interfaceSliders.length == 1) { - // If there is only one axis, there only needs to be one metric return - return; - } - var audioelements = store.getElementsByTagName("audioelement"); - for (var i = 0; i < audioelements.length; i++) { - // Have to append the metric specific nodes - if (pageSpecification.outsideReference === undefined || pageSpecification.outsideReference.id != audioelements[i].id) { - var inject = audioelements[i].getElementsByTagName("metric"); - if (inject.length === 0) { - inject = storage.document.createElement("metric"); - } else { - inject = inject[0]; - } - for (var k = 0; k < interfaceContext.interfaceSliders.length; k++) { - var mrnodes = interfaceContext.interfaceSliders[k].metrics[i].exportXMLDOM(inject); - for (var j = 0; j < mrnodes.length; j++) { - var name = mrnodes[j].getAttribute("name"); - if (name == "elementTracker" || name == "elementTrackerFull" || name == "elementInitialPosition" || name == "elementFlagMoved") { - if (interfaceContext.interfaceSliders[k].name !== null) { - mrnodes[j].setAttribute("interface-name", interfaceContext.interfaceSliders[k].name); - } - mrnodes[j].setAttribute("interface-id", k); - } - } - } - } - } + module.pageXMLSave(store, pageSpecification); }
--- a/interfaces/discrete.js Wed Sep 06 14:50:00 2017 +0100 +++ b/interfaces/discrete.js Wed Sep 06 14:50:13 2017 +0100 @@ -67,9 +67,19 @@ submit.onclick = buttonSubmitClick; submit.id = 'submit-button'; submit.style.float = 'left'; + + // Create the sort button + var sort = document.createElement("button"); + sort.id = "sort-fragments"; + sort.textContent = "Sort"; + sort.style.display = "inline-block"; + sort.style.visibility = "hidden"; + sort.onclick = buttonSortFragmentClick; + // Append the interface buttons into the interfaceButtons object. interfaceButtons.appendChild(playback); interfaceButtons.appendChild(submit); + interfaceButtons.appendChild(sort); // Create a slider box var sliderBox = document.createElement('div'); @@ -187,6 +197,10 @@ case "comments": interfaceContext.commentBoxes.showCommentBoxes(feedbackHolder, true); break; + case "fragmentSort": + var button = document.getElementById('sort-fragments'); + button.style.visibility = "visible"; + break; } } }); @@ -539,6 +553,22 @@ } } +function buttonSortFragmentClick() { + var sortIndex = interfaceContext.sortFragmentsByScore(); + var sliderBox = document.getElementById("slider-holder"); + var nodes = audioEngineContext.audioObjects.filter(function (ao) { + return ao.specification.type !== "outside-reference"; + }); + var i; + nodes.forEach(function (ao) { + sliderBox.removeChild(ao.interfaceDOM.holder); + }); + for (i = 0; i < nodes.length; i++) { + var j = sortIndex[i]; + sliderBox.appendChild(nodes[j].interfaceDOM.holder); + } +} + function pageXMLSave(store, pageSpecification) { // MANDATORY // Saves a specific test page
--- a/interfaces/horizontal-sliders.js Wed Sep 06 14:50:00 2017 +0100 +++ b/interfaces/horizontal-sliders.js Wed Sep 06 14:50:13 2017 +0100 @@ -62,9 +62,20 @@ submit.onclick = buttonSubmitClick; submit.id = 'submit-button'; submit.style.float = 'left'; + + // Create the sort button + var sort = document.createElement("button"); + sort.id = "sort-fragments"; + sort.textContent = "Sort"; + sort.style.display = "inline-block"; + sort.style.visibility = "hidden"; + sort.onclick = buttonSortFragmentClick; + // Append the interface buttons into the interfaceButtons object. interfaceButtons.appendChild(playback); interfaceButtons.appendChild(submit); + interfaceButtons.appendChild(sort); + // Create outside reference holder var outsideRef = document.createElement("div"); @@ -226,6 +237,10 @@ case "comments": interfaceContext.commentBoxes.showCommentBoxes(feedbackHolder, true); break; + case "fragmentSort": + var button = document.getElementById('sort-fragments'); + button.style.visibility = "visible"; + break; } } }); @@ -419,6 +434,22 @@ }); } +function buttonSortFragmentClick() { + var sortIndex = interfaceContext.sortFragmentsByScore(); + var sliderBox = document.getElementById("slider-holder"); + var nodes = audioEngineContext.audioObjects.filter(function (ao) { + return ao.specification.type !== "outside-reference"; + }); + var i; + nodes.forEach(function (ao) { + sliderBox.removeChild(ao.interfaceDOM.holder); + }); + for (i = 0; i < nodes.length; i++) { + var j = sortIndex[i]; + sliderBox.appendChild(nodes[j].interfaceDOM.holder); + } +} + function buttonSubmitClick() // TODO: Only when all songs have been played! { var checks = testState.currentStateMap.interfaces[0].options,
--- a/interfaces/mushra.js Wed Sep 06 14:50:00 2017 +0100 +++ b/interfaces/mushra.js Wed Sep 06 14:50:13 2017 +0100 @@ -67,9 +67,20 @@ submit.onclick = buttonSubmitClick; submit.id = 'submit-button'; submit.style.display = 'inline-block'; + + // Create the sort button + var sort = document.createElement("button"); + sort.id = "sort-fragments"; + sort.textContent = "Sort"; + sort.style.display = "inline-block"; + sort.style.visibility = "hidden"; + sort.onclick = buttonSortFragmentClick; + // Append the interface buttons into the interfaceButtons object. interfaceButtons.appendChild(playback); interfaceButtons.appendChild(submit); + interfaceButtons.appendChild(sort); + // Create outside reference holder var outsideRef = document.createElement("div"); @@ -206,6 +217,8 @@ var interfaceOptions = interfaceObj.options; + var sortButton = document.getElementById("sort-fragments"); + sortButton.style.visibility = "hidden"; interfaceOptions.forEach(function (option) { if (option.type == "show") { switch (option.name) { @@ -239,31 +252,8 @@ interfaceContext.commentBoxes.showCommentBoxes(feedbackHolder, true); break; case "fragmentSort": - var button = document.getElementById('sort'); - if (button === null) { - button = document.createElement("button"); - button.id = 'sort'; - button.textContent = "Sort"; - button.style.display = 'inline-block'; - var container = document.getElementById("interface-buttons"); - var neighbour = container.lastElementChild; - container.appendChild(button); - button.onclick = function () { - var sortIndex = interfaceContext.sortFragmentsByScore(); - var sliderBox = document.getElementById("slider-holder"); - var nodes = audioEngineContext.audioObjects.filter(function (ao) { - return ao.specification.type !== "outside-reference"; - }); - var i; - nodes.forEach(function (ao) { - sliderBox.removeChild(ao.interfaceDOM.holder); - }); - for (i = 0; i < nodes.length; i++) { - var j = sortIndex[i]; - sliderBox.appendChild(nodes[j].interfaceDOM.holder); - } - }; - } + var button = document.getElementById('sort-fragments'); + button.style.visibility = "visible"; break; } } @@ -513,6 +503,22 @@ }); } +function buttonSortFragmentClick() { + var sortIndex = interfaceContext.sortFragmentsByScore(); + var sliderBox = document.getElementById("slider-holder"); + var nodes = audioEngineContext.audioObjects.filter(function (ao) { + return ao.specification.type !== "outside-reference"; + }); + var i; + nodes.forEach(function (ao) { + sliderBox.removeChild(ao.interfaceDOM.holder); + }); + for (i = 0; i < nodes.length; i++) { + var j = sortIndex[i]; + sliderBox.appendChild(nodes[j].interfaceDOM.holder); + } +} + function buttonSubmitClick() // TODO: Only when all songs have been played! { var checks = testState.currentStateMap.interfaces[0].options,
--- a/interfaces/ordinal.js Wed Sep 06 14:50:00 2017 +0100 +++ b/interfaces/ordinal.js Wed Sep 06 14:50:13 2017 +0100 @@ -222,6 +222,7 @@ root.addEventListener('dragleave', this, true); root.addEventListener('drop', this, true); root.addEventListener('dragend', this, true); + this.dragging = false; this.handleEvent = function (event) { if (event.type == "click") { if (playing === false) { @@ -252,6 +253,7 @@ e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/plain', audioObject.id); + this.dragging = "true"; } function dragEnter(e) { @@ -267,13 +269,10 @@ if (e.preventDefault) { e.preventDefault(); // Necessary. Allows us to drop. } - e.dataTransfer.dropEffect = 'move'; // See the section on the DataTransfer object. - - var srcid = Number(e.dataTransfer.getData("text/plain")); var elements = container.childNodes; var srcObject = audioEngineContext.audioObjects.find(function (ao) { - return ao.id === srcid; + return ao.interfaceDOM.dragging; }); var src = srcObject.interfaceDOM.root; if (src !== root) { @@ -293,8 +292,6 @@ } } - - return false; } function drop(e) { @@ -318,6 +315,7 @@ // this/e.target is the source node. $(".ordinal-element").removeClass("dragging"); $(".ordinal-element").removeClass("over"); + this.dragging = false; } this.getElementPosition = function () {
--- a/js/core.js Wed Sep 06 14:50:00 2017 +0100 +++ b/js/core.js Wed Sep 06 14:50:13 2017 +0100 @@ -3600,7 +3600,7 @@ returnURL = specification.projectReturn; } } - xmlhttp.open("POST", returnURL + "php/save.php?key=" + this.key + "&saveFilenamePrefix=" + this.parent.filenamePrefix); + xmlhttp.open("POST", returnURL + "php/save.php?key=" + this.key + "&saveFilenamePrefix=" + this.parent.filenamePrefix + "&state=update"); xmlhttp.setRequestHeader('Content-Type', 'text/xml'); xmlhttp.onerror = function () { console.log('Error updating file to server!'); @@ -3638,6 +3638,7 @@ } else { saveURL += this.parent.filenamePrefix; } + saveURL += "&state=finish"; return new Promise(function (resolve, reject) { var xmlhttp = new XMLHttpRequest(); xmlhttp.open("POST", saveURL);
--- a/php/save.php Wed Sep 06 14:50:00 2017 +0100 +++ b/php/save.php Wed Sep 06 14:50:13 2017 +0100 @@ -33,7 +33,17 @@ } $postText = file_get_contents('php://input'); $file_key = $_GET['key']; -$filename = '../saves/'.$saveFilenamePrefix.$file_key.".xml"; + +$update = false; +if (isset($_GET["update"])) { + $update = $_GET["update"] == "update"; +} + +if ($update) { + $filename = '../saves/update-'.$saveFilenamePrefix.$file_key.".xml"; +} else { + $filename = '../saves/'.$saveFilenamePrefix.$file_key.".xml"; +} if (!file_exists($filename)) { die('<response state="error"><message>Could not find save</message></response>'); @@ -132,4 +142,8 @@ // Return XML confirmation data $xml = '<response state="OK"><message>OK</message><file bytes="'.$wbytes.'">"'.$filename.'"</file></response>'; echo $xml; + +if (!$update) { + unlink('../saves/update-'.$saveFilenamePrefix.$file_key.".xml"); +} ?>
--- a/python/generate_report.py Wed Sep 06 14:50:00 2017 +0100 +++ b/python/generate_report.py Wed Sep 06 14:50:13 2017 +0100 @@ -227,17 +227,18 @@ # number of comments (interesting if comments not mandatory) for audioelement in audioelements: - response = audioelement.find("./comment/response") - was_played = audioelement.find("./metric/metricresult/[@name='elementFlagListenedTo']") - was_moved = audioelement.find("./metric/metricresult/[@name='elementFlagMoved']") - if response is not None and response.text is not None and len(response.text) > 1: - number_of_comments += 1 - else: - number_of_missing_comments += 1 - if was_played is not None and was_played.text == 'false': - not_played.append(audioelement.get('name')) - if was_moved is not None and was_moved.text == 'false': - not_moved.append(audioelement.get('name')) + if audioelement.get("type") != "outside-reference": + response = audioelement.find("./comment/response") + was_played = audioelement.find("./metric/metricresult/[@name='elementFlagListenedTo']") + was_moved = audioelement.find("./metric/metricresult/[@name='elementFlagMoved']") + if response is not None and response.text is not None and len(response.text) > 1: + number_of_comments += 1 + else: + number_of_missing_comments += 1 + if was_played is not None and was_played.text == 'false': + not_played.append(audioelement.get('name')) + if was_moved is not None and was_moved.text == 'false': + not_moved.append(audioelement.get('name')) # update global counters total_empty_comments += number_of_missing_comments
--- a/python/pythonServer.py Wed Sep 06 14:50:00 2017 +0100 +++ b/python/pythonServer.py Wed Sep 06 14:50:13 2017 +0100 @@ -138,12 +138,15 @@ global curSaveIndex options = self.path.rsplit('?') options = options[1].rsplit('&') + update = False for option in options: optionPair = option.rsplit('=') if optionPair[0] == "key": key = optionPair[1] elif optionPair[0] == "saveFilenamePrefix": prefix = optionPair[1] + elif optionPair[0] == "state": + update = optionPair[1] == "update" if key == None: self.send_response(404) return @@ -153,6 +156,8 @@ postVars = self.rfile.read(varLen) print("Saving file key "+key) filename = prefix+'-'+key+'.xml' + if update: + filename = "update-"+filename file = open('../saves/'+filename,'wb') file.write(postVars) file.close() @@ -173,6 +178,8 @@ self.wfile.write(bytes(reply, "utf-8")) curSaveIndex += 1 curFileName = 'test-'+str(curSaveIndex)+'.xml' + if update == False: + os.remove("../saves/update-"+filename) def poolXML(s): pool = ET.parse('../tests/pool.xml')
--- a/python/score_parser.py Wed Sep 06 14:50:00 2017 +0100 +++ b/python/score_parser.py Wed Sep 06 14:50:13 2017 +0100 @@ -58,17 +58,16 @@ # Check if page in the store if storage.get(page_name) == None: - storage[page_name] = {'header':[], 'axis':{}} # add to the store + storage[page_name] = {'header':[], 'axis':{"default": {}}} # add to the store # Get the axis names pageConfig = root.find('./waet/page/[@id="'+page_name+'"]') for interface in pageConfig.findall('./interface'): # Get the <interface> noeds interfaceName = interface.get("name"); # Get the axis name - if interfaceName == None: + if interfaceName == None or interfaceName == "null": interfaceName = "default" # If name not set, make name 'default' - if storage[page_name]['axis'].get(interfaceName) == None: + if interfaceName not in storage[page_name]['axis'].keys(): storage[page_name]['axis'][interfaceName] = {} # If not in store for page, add empty dict - storage[page_name]['axis'][interfaceName][subject_id] = [] # Add the store for the session # header: fragment IDs in 'alphabetical' order # go to fragment column, or create new column if it doesn't exist yet @@ -76,7 +75,8 @@ # get alphabetical array of fragment IDs from this subject's XML fragmentnamelist = [] # make empty list for audioelement in page.findall("./audioelement"): # iterate over all audioelements - fragmentnamelist.append(audioelement.get('ref')) # add to list + if audioelement.get("type") != "outside-reference": + fragmentnamelist.append(audioelement.get('ref')) # add to list fragmentnamelist = sorted(fragmentnamelist); # Sort the list storage[page_name]['header'] = fragmentnamelist; @@ -87,11 +87,17 @@ axisName = value.get('interface-name') if axisName == None or axisName == "null": axisName = 'default' + print(storage[page_name]['axis']) axisStore = storage[page_name]['axis'][axisName] + try: + subjectStore = axisStore[subject_id] + except KeyError: + axisStore[subject_id] = [] + subjectStore = axisStore[subject_id] if hasattr(value, 'text'): - axisStore[subject_id].append(value.text) + subjectStore.append(value.text) else: - axisStore[subject_id].append('') + subjectStore.append('') # Now create the individual files for page_name in storage:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/python/survey_parser.py Wed Sep 06 14:50:13 2017 +0100 @@ -0,0 +1,211 @@ +#!/usr/bin/python +import xml.etree.ElementTree as ET +import os +import sys +import csv +import matplotlib.pyplot as plt +import numpy as np +import scipy as sp +import scipy.stats + + +# COMMAND LINE ARGUMENTS + +assert len(sys.argv)<3, "score_parser takes at most 1 command line argument\n"+\ + "Use: python score_parser.py [rating_folder_location]" + +# XML results files location +if len(sys.argv) == 1: + folder_name = "../saves" # Looks in 'saves/' folder from 'scripts/' folder + print("Use: python score_parser.py [rating_folder_location]") + print("Using default path: " + folder_name) +elif len(sys.argv) == 2: + folder_name = sys.argv[1] # First command line argument is folder + +# check if folder_name exists +if not os.path.exists(folder_name): + #the file is not there + print("Folder '"+folder_name+"' does not exist.") + sys.exit() # terminate script execution +elif not os.access(os.path.dirname(folder_name), os.W_OK): + #the file does exist but write privileges are not given + print("No write privileges in folder '"+folder_name+"'.") + +# CODE + +storage = {"globals":{}, "pages": {}} + +def decodeSurveyTree(session_id, surveyroot, store): + # Get all the childs + for survey_entry in list(surveyroot): + survey_id = survey_entry.get("ref") + if survey_id not in store.keys(): + store[survey_id] = {"responses": []} + survey_type = survey_entry.get("type") + store[survey_id]["type"] = survey_type + if survey_type == "statement" or survey_type == "video": + if "header" not in store[survey_id]: + store[survey_id]["header"] = ("ids", "duration") + store[survey_id] = decodeSurveyStatement(session_id, survey_entry, store[survey_id]) + elif survey_type == "question" or survey_type == "number" or survey_type == "slider": + if "header" not in store[survey_id]: + store[survey_id]["header"] = ("ids", "durations", "response") + store[survey_id] = decodeSurveyQuestion(session_id, survey_entry, store[survey_id]) + elif survey_type == "checkbox": + if "header" not in store[survey_id]: + head = ["ids", "duration"] + for option in survey_entry.findall("./response"): + head.append(option.get("name")) + store[survey_id]["header"] = tuple(head) + store[survey_id] = decodeSurveyCheckbox(session_id, survey_entry, store[survey_id]) + elif survey_type == "radio": + if "header" not in store[survey_id]: + store[survey_id]["header"] = ("ids", "duration", "response") + store[survey_id] = decodeSurveyRadio(session_id, survey_entry, store[survey_id]) + return store + +def decodeSurveyStatement(session_id, survey_entry, store): + resp = (session_id, survey_entry.get("duration")) + store["responses"].append(resp) + return store + +def decodeSurveyQuestion(session_id, survey_entry, store): + resp = (session_id, survey_entry.get("duration"), survey_entry.find("./response").text) + store["responses"].append(resp) + return store + +def decodeSurveyCheckbox(session_id, survey_entry, store): + response = [session_id, survey_entry.get("duration")] + for node in survey_entry.findall("./response"): + response.append(node.get("checked")) + store["responses"].append(tuple(response)) + return store + +def decodeSurveyRadio(session_id, survey_entry, store): + response = (session_id, survey_entry.get("duration"), survey_entry.find("./response").get("name")) + store["responses"].append(response) + return store + +if folder_name.endswith("/") is False: + folder_name += "/" + +# Create the folder 'surveys' if not yet created +if not os.path.exists(folder_name + 'surveys'): + os.makedirs(folder_name + 'surveys') + +#Iterate through every XML file in folder_name +for file_name in os.listdir(folder_name): + if file_name.endswith(".xml"): + tree = ET.parse(folder_name +file_name) + root = tree.getroot() + subject_id = root.get('key') + pre_survey = root.find("./survey[@location='pre']") + if len(pre_survey) is not 0: + if "pre" not in storage["globals"].keys(): + storage["globals"]["pre"] = {} + storage["globals"]["pre"] = decodeSurveyTree(subject_id, pre_survey, storage["globals"]["pre"]) + post_survey = root.find("./survey[@location='post']") + if len(post_survey) is not 0: + if "post" not in storage["globals"].keys(): + storage["globals"]["post"] = {} + storage["globals"]["post"] = decodeSurveyTree(subject_id, post_survey, storage["globals"]["post"]) + + # Now iterate through the page specifics + for page in root.findall("./page[@state='complete']"): + page_name = page.get("ref") + pre_survey = page.find("./survey[@location='pre']") + try: + page_store = storage["pages"][page_name] + except KeyError: + storage["pages"][page_name] = {} + page_store = storage["pages"][page_name] + if len(pre_survey) is not 0: + if "pre" not in page_store.keys(): + page_store["pre"] = {} + page_store["pre"] = decodeSurveyTree(subject_id, pre_survey, page_store["pre"]) + post_survey = page.find("./survey[@location='post']") + if len(post_survey) is not 0: + if "post" not in page_store.keys(): + page_store["post"] = {} + page_store["post"] = decodeSurveyTree(subject_id, post_survey, page_store["post"]) + +#Storage now holds entire survey structure +# Time to start exporting to files + +# Store globals +file_store_root = folder_name + 'surveys/' +for position in storage["globals"].keys(): + for ref in storage["globals"][position].keys(): + with open(file_store_root+ref+".csv", "w") as f: + filewriter = csv.writer(f, delimiter=",") + filewriter.writerow(storage["globals"][position][ref]["header"]) + for row in storage["globals"][position][ref]["responses"]: + filewriter.writerow(row) +for page_name in storage["pages"].keys(): + for position in storage["pages"][page_name].keys(): + if not os.path.exists(file_store_root + page_name): + os.makedirs(file_store_root + page_name) + for ref in storage["pages"][page_name][position].keys(): + with open(file_store_root+page_name+"/"+ref+".csv", "w") as f: + filewriter = csv.writer(f, delimiter=",") + filewriter.writerow(storage["pages"][page_name][position][ref]["header"]) + for row in storage["pages"][page_name][position][ref]["responses"]: + filewriter.writerow(row) + +#Time to plot + +def plotDurationHistogram(store, plot_id, saveloc): + x = [] + for row in store["responses"]: + x.append(float(row[1])) + x = np.asarray(x) + plt.figure() + n, bins, patches = plt.hist(x, 10, facecolor='green', alpha=0.75) + plt.xlabel("Duration") + plt.ylabel("Count") + plt.grid(True) + plt.title("Histogram of durations for "+plot_id) + plt.savefig(saveloc+plot_id+"-duration.pdf", bbox_inches='tight') + +def plotRadio(store, plot_id, saveloc): + plt.figure() + data = {} + for row in store["responses"]: + try: + data[row[2]] += 1 + except KeyError: + data[row[2]] = 1 + labels = data.keys() + sizes = data.values() + plt.pie(sizes, labels=labels, autopct='%1.1f%%', startangle=90) + plt.title("Selections of "+plot_id) + plt.savefig(saveloc+plot_id+".pdf", bbox_inches='tight') + +def plotCheckbox(store, plot_id, saveloc): + data = [] + labels = [] + for h in store["header"][2::1]: + labels.append(h) + data.append(0) + for row in store["responses"]: + for i in range(2, len(labels)+2): + if row[i] == "true": + data[i-2] += 1 + x = scipy.arange(4) + plt.figure() + plt.bar(x, data, width=0.8) + plt.xticks(x+0.4, labels) + plt.xlabel("Option") + plt.ylabel("Count") + plt.title("Selection counts of "+plot_id) + plt.savefig(saveloc+plot_id+".pdf", bbox_inches='tight') + +for page_name in storage["pages"].keys(): + for position in storage["pages"][page_name].keys(): + saveloc = file_store_root+page_name+"/" + for ref in storage["pages"][page_name][position].keys(): + plotDurationHistogram(storage["pages"][page_name][position][ref],ref, saveloc) + if storage["pages"][page_name][position][ref]["type"] == "radio": + plotRadio(storage["pages"][page_name][position][ref],ref, saveloc) + if storage["pages"][page_name][position][ref]["type"] == "checkbox": + plotCheckbox(storage["pages"][page_name][position][ref],ref, saveloc) \ No newline at end of file
--- a/python/timeline_view_movement.py Wed Sep 06 14:50:00 2017 +0100 +++ b/python/timeline_view_movement.py Wed Sep 06 14:50:13 2017 +0100 @@ -74,6 +74,10 @@ if page_name is None: # ignore 'empty' audio_holders print("Skipping empty page name from "+subject_id+".") break + + if page.get("state") != "complete": + print("Skipping non-completed page "+page_name+" from "+subject_id+".") + break # subtract total page length from subsequent page event times page_time_temp = page.find("./metric/metricresult/[@id='testTime']") @@ -108,11 +112,20 @@ if audioelement is not None: # Check it exists audio_id = str(audioelement.get('ref')) - # break if no initial position or move events registered + # break if outside-reference + if audioelement.get("type") == "outside-reference": + break; + + # break if no initial position.... initial_position_temp = audioelement.find("./metric/metricresult/[@name='elementInitialPosition']") if initial_position_temp is None: print("Skipping "+page_name+" from "+subject_id+": does not have initial positions specified.") break + # ... or move events registered + movements = audioelement.find("./metric/metricresult[@name='elementTrackerFull']") + if movements is None: + print("Skipping "+page_name+" from "+subject_id+": does not have trackers.") + break # get move events, initial and eventual position initial_position = float(initial_position_temp.text) @@ -299,13 +312,20 @@ interfaces = page_setup.findall("./interface") interface_title = interfaces[0].find("./title") scales = interfaces[0].findall("./scales") # get first interface by default - scalelabels = scales[0].findall("./scalelabel") # get first scale by default - + labelpos = [] # array of scalelabel positions labelstr = [] # array of strings at labels - for scalelabel in scalelabels: - labelpos.append(float(scalelabel.get('position'))/100.0) - labelstr.append(scalelabel.text) + + # No scales given. Use normal floats + if len(scales) is 0: + labelpos = [0.0, 1.0] + labelstr = ["0", "100"] + else: + scalelabels = scales[0].findall("./scalelabel") # get first scale by default + + for scalelabel in scalelabels: + labelpos.append(float(scalelabel.get('position'))/100.0) + labelstr.append(scalelabel.text) # use interface name as Y axis label if interface_title is not None: