Throughout this task, you will be presented with 15 balloons, one at a time.' +
+ '
You will be asked to inflate these balloons. Every time you choose to inflate the balloon,' +
+ ' it will grow slightly and you will receive one token. '
+ );
+ };
+
+ BART_tutorial.prototype.learntopump = function () {
+ resetBalloon();
+ tutorial.active = true;
+ tutorial.tokens = 0;
+ this.changeStatus('learntopump');
+ $('#back-instruction').show();
+ $('#continue-instruction').hide();
+ this.displayInstruction(
+ "To inflate the balloon click/tap anywhere in the white space of this container (or press the spacebar).
Go ahead and pump as many times as you'd like."
+ );
+ $('#pump-text').delay(1000).animate({opacity: '1'});
+ };
+
+ BART_tutorial.prototype.watchrangeIntro = function () {
+ this.changeStatus('onboardWatch');
+ this.displayInstruction(
+ "We'd like you to now watch several balloons grow to their maximum sizes, to get a feel for what sorts" +
+ " of balloons you might encounter.
Click or tap on this box to view these balloons inflate."
+ );
+ };
+
+ BART_tutorial.prototype.watchrange = function (required) {
+ var self = this;
+ var tokens = 0;
+ var popat = this.popList[required];
+ window.setTimeout(function () {
+ resetBalloon();
+ }, 1000);
+
+ if (this.autotrial == required) {
+ window.setTimeout(function () {
+ var interv = setInterval(function () {
+ tokens++;
+ $("#balloon-image").animate({height: String( 10 + (tokens /1.5)) + '%'}, 50);
+ $("#token-text").text(String(tokens) + ' Tokens');
+ if (tokens == popat) {
+ $('#progress').css({width: (1+required) * 6.25.toString() + '%'});
+ clearInterval(interv);
+ popAnimation();
+ self.incrementAuto();
+ $("#back-instruction").hide();
+ if (required < self.popList.length - 1) {
+ self.watchrange(required + 1);
+ } else {
+ self.displayInstruction(
+ "Now that you've seen what sorts of balloons you might encounter in this task," +
+ " we'd like to ask you what you think the largest any balloon could grow to is."
+ );
+ self.changeStatus('maxRating');
+ }
+ }
+ }, 120)
+ }, 1500)
+ }
+ };
+
+ // Trial helper
+ BART_tutorial.prototype.incrementAuto = function () {
+ this.autotrial++;
+ };
+
+ BART_tutorial.prototype.changeStatus = function (status) {
+ this.status = status;
+ };
+
+ BART_tutorial.prototype.maxRating = function () {
+ $('#continue-instruction').hide();
+ this.changeStatus('inputMax');
+ this.displayInstruction('Before we move on, please tell us what you think the largest a balloon can grow to is (in number of pumps).');
+ $('#instructions-box').delay(200).append(' ');
+ };
+
+ BART_tutorial.prototype.distribution = function () {
+ $('#balloon-image').hide();
+ $('#token-text').hide();
+ $('#cash-box').hide();
+ $('#result-text').hide();
+ $('#instructions-box').animate({opacity: '0'}).hide();
+ $("#coin_distribution").show();
+ Distribution_init(tutorial.maxSize);
+ tutorial.changeStatus('distributeGo');
+ };
+
+
+ BART_tutorial.prototype.submitMax = function () {
+ tutorial.changeStatus('Distribution');
+ tutorial.maxSize = parseInt($('#maxSize')[0].value);
+ $('input').hide().remove();
+ tutorial.distribution();
+ };
+
+ BART_tutorial.prototype.removeChart = function () {
+ $('#coin_distribution').animate({opacity: '0'}, function () {
+ $('#coin_distribution').remove();
+ addAndSubmitData();
+ });
+ resetBalloon();
+ $('#balloon-image').show();
+ $('#token-text').show();
+ $('#cash-box').show();
+ $('#result-text').show();
+ $('#pump-box').show();
+ $('#continue-instruction').hide();
+ $('#instructions-box').animate({opacity: '0'}).show();
+ tutorial.changeStatus('learntocash');
+ tutorial.displayInstruction(
+ "To save your tokens before a balloon pops, you may choose to 'cash in'.
To cash in, click/tap anywhere " +
+ "inside of the green 'cash in' box at the bottom of this container (or press the return key).
Go ahead and pump this balloon" +
+ " to 10 tokens and cash in."
+ );
+ tutorial.active = true;
+ tutorial.tokens = 0;
+ };
+
+ BART_tutorial.prototype.displayInstruction = function (message) {
+ var message = message;
+ $('#instructions-box').animate({opacity: '0'}, 750, function () {
+ $('#instructions-text').html(message);
+ }).animate({opacity: '1'}, {duration: 750});
+ };
+
+ BART_tutorial.prototype.checkTime = function () {
+ clearInterval(this.flashinterval);
+ this.lastClick = new Date().getTime();
+ var lastClick = this.lastClick;
+ this.flashinterval = window.setInterval(function () {
+ if (new Date().getTime() - lastClick > 7500) {
+ $('#pump-text')
+ .finish()
+ .animate({opacity: '1'})
+ .delay(500)
+ .animate({opacity: '0'});
+ }
+ }, 2000)
+ };
+ return BART_tutorial;
+ })();
+
+
+ BART_task = (function() {
+ function BART_task(){
+ datahandler.finishInstructions();
+ this.flashinterval = null;
+ this.proceed = false;
+ this.trial = 0;
+ this.tokens = 0;
+ this.active = false;
+
+ }
+
+
+
+ BART_task.prototype.newTrial = function() {
+ if (this.trial <= 14) {
+ this.trial++;
+ this.tokens = 0;
+ this.popPoint = Math.floor((Math.random() * 63) + 1);
+ resetBalloon();
+ this.active = true;
+ this.proceed = false;
+ }else {
+ console.log('end');
+ datahandler.saveData();
+ datahandler.completeTask();
+ window.setTimeout(function () {
+ datahandler.exitTask();
+ }, 1500);
+ }
+ };
+
+ BART_task.prototype.checkTime = function () {
+ clearInterval(this.flashinterval);
+ this.lastClick = new Date().getTime();
+ var lastClick = this.lastClick;
+ this.flashinterval = window.setInterval(function () {
+ if (new Date().getTime() - lastClick > 7500) {
+ $('#pump-text')
+ .finish()
+ .animate({opacity: '1'})
+ .delay(500)
+ .animate({opacity: '0'});
+ }
+ }, 2000)
+ };
+
+ return BART_task;
+ })();
+
+
+
+
+ tutorial = new Bart_tutorial();
+ tutorial.onboarding();
+ Task = new BART_task();
+
+
+
+ $('#continue-instruction').click(function() {
+ if (tutorial.status === 'onboarding') {
+ tutorial.learntopump();
+ } else if (tutorial.status === 'popped') {
+ tutorial.watchrangeIntro();
+ } else if (tutorial.status === 'onboardWatch') {
+ $('#progress-container').css({visibility:'visible'});
+ $('#instructions-box').animate({opacity:'0'});
+
+ tutorial.watchrange(0);
+ } else if (tutorial.status === 'maxRating'){
+ $('#progress-container').css({visibility:'hidden'});
+ tutorial.maxRating();
+ } else if (tutorial.status === 'preTask') {
+ tutorial.changeStatus('done');
+ $('#instructions-box').hide();
+ Task.newTrial();
+ } else if (tutorial.status === 'endLearnCash') {
+ tutorial.displayInstruction(
+ "At the end of this task we will compare your average tokens for cashed in balloons with other individuals who have also completed this task." +
+ "
' +
+ 'Every balloon that you inflate will have a different maximum size that it can grow to.' +
+ ' Once the balloon gets to its max size, it will pop and you will lose your current tokens.'
+ );
+ popAnimation();
+ }
+ }
+ } else if (tutorial.status === 'learntocash' && tutorial.tokens < 10){
+ console.log('hi');
+ if (tutorial.active){
+ tutorial.tokens++;
+ $("#balloon-image").animate({height: String( 10 + (tutorial.tokens /1.5)) + '%'}, 50);
+ $("#token-text").text(String(tutorial.tokens) + ' Tokens');
+ $('#pump-text').animate({opacity: '0'}, Task.checkTime());
+ }
+ } else if (Task.active){
+ datahandler.recordTrialData({
+ 'balloon_num': Task.trial,
+ 'action': 1,
+ 'pumps': Task.tokens,
+ 'pop_point': Task.popPoint,
+ 'dist': '_'
+ });
+ clearInterval(tutorial.flashinterval);
+ clearInterval(Task.flashinterval);
+ console.log(Task.tokens);
+ Task.tokens++;
+ $("#balloon-image").animate({height: String( 10 + (Task.tokens /1.5)) + '%'}, 50);
+ $("#token-text").text(String(Task.tokens) + ' Tokens');
+ $('#pump-text').animate({opacity: '0'}, Task.checkTime());
+ if (Task.tokens === Task.popPoint) {
+ datahandler.recordTrialData({
+ 'balloon_num': Task.trial,
+ 'action': 0,
+ 'pumps': Task.tokens,
+ 'pop_point': Task.popPoint,
+ 'dist': '_'
+ });
+ Task.active = false;
+ popAnimation();
+ $('#cash-text').html('Next Balloon');
+ Task.proceed = true;
+
+ } else if (Task.proceed) {
+ Task.newTrial();
+ }
+ }
+ }
+ else if(e.keyCode == 13){
+ if (tutorial.status === 'learntocash' && tutorial.tokens == 10) {
+ if (tutorial.active && tutorial.tokens != 0) {
+ clearInterval(tutorial.flashinterval);
+ clearInterval(Task.flashinterval);
+ tutorial.active = false;
+ cashDisplay();
+ $('#continue-instruction').show();
+ $('#back-instruction').show();
+ tutorial.displayInstruction(
+ "And that's all there is to it!
In this task you will be presented with 15 balloons." +
+ " For each balloon you can choose to pump the balloon to whatever size you like before cashing in" +
+ " your tokens. But remember, if you pump the balloon too much, it will explode and you will lose your tokens."
+ );
+ tutorial.changeStatus('endLearnCash');
+ }
+ } else if (Task.active && Task.tokens != 0){
+ datahandler.recordTrialData({
+ 'balloon_num': Task.trial,
+ 'action': 2,
+ 'pumps': Task.tokens,
+ 'pop_point': Task.popPoint,
+ 'dist': '_'
+ });
+
+ clearInterval(tutorial.flashinterval);
+ clearInterval(Task.flashinterval);
+ Task.active = false;
+ cashDisplay();
+ $('#cash-text').html('Next Balloon');
+ Task.proceed = true;
+ } else if (Task.proceed) {
+ Task.newTrial();
+ }
+ }
+ };
+
+ $("#pump-box").click(function() {
+ if (tutorial.status === 'learntopump') {
+ if (tutorial.active) {
+ tutorial.tokens++;
+ $("#balloon-image").animate({height: String( 10 + (tutorial.tokens /1.5)) + '%'}, 50);
+ $("#token-text").text(String(tutorial.tokens) + ' Tokens');
+ $('#pump-text').animate({opacity: '0'}, tutorial.checkTime());
+
+ if (tutorial.tokens === 7) {
+ tutorial.changeStatus('popped');
+ clearInterval(tutorial.flashinterval);
+ clearInterval(Task.flashinterval);
+ tutorial.active = false;
+ $('#continue-instruction').show();
+ tutorial.displayInstruction(
+ ' Oh no! It looks like the balloon popped.
' +
+ 'Every balloon that you inflate will have a different maximum size that it can grow to.' +
+ ' Once the balloon gets to its max size, it will pop and you will lose your current tokens.'
+ );
+ popAnimation();
+ }
+ }
+ } else if (tutorial.status === 'learntocash' && tutorial.tokens < 10){
+ console.log('hi');
+ if (tutorial.active){
+ tutorial.tokens++;
+ $("#balloon-image").animate({height: String( 10 + (tutorial.tokens /1.5)) + '%'}, 50);
+ $("#token-text").text(String(tutorial.tokens) + ' Tokens');
+ $('#pump-text').animate({opacity: '0'}, Task.checkTime());
+ }
+ } else if (Task.active){
+ datahandler.recordTrialData({
+ 'balloon_num': Task.trial,
+ 'action': 1,
+ 'pumps': Task.tokens,
+ 'pop_point': Task.popPoint,
+ 'dist': '_'
+ });
+ clearInterval(tutorial.flashinterval);
+ clearInterval(Task.flashinterval);
+ console.log(Task.tokens);
+ Task.tokens++;
+ $("#balloon-image").animate({height: String( 10 + (Task.tokens /1.5)) + '%'}, 50);
+ $("#token-text").text(String(Task.tokens) + ' Tokens');
+ $('#pump-text').animate({opacity: '0'}, Task.checkTime());
+ if (Task.tokens === Task.popPoint) {
+ datahandler.recordTrialData({
+ 'balloon_num': Task.trial,
+ 'action': 0,
+ 'pumps': Task.tokens,
+ 'pop_point': Task.popPoint,
+ 'dist': '_'
+ });
+ Task.active = false;
+ popAnimation();
+ $('#cash-text').html('Next Balloon');
+ Task.proceed = true;
+
+ } else if (Task.proceed) {
+ Task.newTrial();
+ }
+ }
+ });
+
+
+ $('#cash-box').click(function(){
+
+ if (tutorial.status === 'learntocash' && tutorial.tokens == 10) {
+ if (tutorial.active && tutorial.tokens != 0) {
+ clearInterval(tutorial.flashinterval);
+ clearInterval(Task.flashinterval);
+ tutorial.active = false;
+ cashDisplay();
+ $('#continue-instruction').show();
+ tutorial.displayInstruction(
+ "And that's all there is to it!
In this task you will be presented with 15 balloons." +
+ " For each balloon you can choose to pump the balloon to whatever size you like before cashing in" +
+ " your tokens. But remember, if you pump the balloon too much, it will explode and you will lose your tokens."
+ );
+ tutorial.changeStatus('endLearnCash');
+ }
+ } else if (Task.active && Task.tokens != 0){
+ datahandler.recordTrialData({
+ 'balloon_num': Task.trial,
+ 'action': 2,
+ 'pumps': Task.tokens,
+ 'pop_point': Task.popPoint,
+ 'dist': '_'
+ });
+ clearInterval(tutorial.flashinterval);
+ clearInterval(Task.flashinterval);
+ Task.active = false;
+ cashDisplay();
+ $('#cash-text').html('Next Balloon');
+ Task.proceed = true;
+ } else if (Task.proceed) {
+ Task.newTrial();
+ }
+ });
+}();
+
+
+
+
diff --git a/gfg_interactive/exp/static/BART/js/TestingFiled.html b/gfg_interactive/exp/static/BART/js/TestingFiled.html
new file mode 100644
index 00000000..710f97c9
--- /dev/null
+++ b/gfg_interactive/exp/static/BART/js/TestingFiled.html
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+ TestingFild
+
+
+
+
+
+ For this task we would like you to place some bets on where you think the balloon is going to pop most.
+ You can place a bet by clicking on any of the bins. You can place as many bets as you'd like, but will need
+ to place at least 100 bets before being allowed to move on.
+
+
click this container or the question mark to move on.
-
-
-
+ // These fields are coming from the "/task" start_exp() in experiments.py
+ var uniqueid = "{{ uniqueid }}";
+ var sessionid = "{{ sessionid }}";
+ var experimentname = "{{ experimentname }}";
+ var surveyid = "{{ surveyid }}";
+ var debug = "{{ debug }}";
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ For this task we would like you to place some bets on where you think the balloons in this task are likely to pop.
+ The scale below represents the size of a balloon from 0 pumps (leftmost column) to your estimated maximum balloon size (rightmost column). Each column represents a slightly larger balloon.
+ Out of 50 balloons, where do you think the balloons are likely to pop? Placing more bets in a column will indicate that you think more balloons will pop at that size.
You must place 50 bets to continue.
+
+
+
click this container or the question mark to move on.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
0 Tokens
+
+
+
+
+
Popped
+
+
+
Pump
-
-
CASH IN
+
+
+
Cash In
+
+
+
+
+
+
+
Welcome to the experiment!
+
+
+
+
-{% endblock %}
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gfg_interactive/exp/templates/BART/results.html b/gfg_interactive/exp/templates/BART/results.html
new file mode 100644
index 00000000..0dcde6b4
--- /dev/null
+++ b/gfg_interactive/exp/templates/BART/results.html
@@ -0,0 +1,202 @@
+{% extends "base.html" %}
+{% block content %}
+
+
+
+
+
+
Your results!
+
+ On Average you saved {{'%0.0f'| format(score|float)}} Tokens when you cashed in. Thanks for the hard work!
+
+ {% if previousSessions is not none %}
+
+
Result history
+
Here you will see the full history of all of your BART results.
+
+
+ {% endif %}
+
+ {% if percentile is not none %}
+
How you stack up.
+
+
+
On average, where you cashed in was greater than {{'%0.0f'| format(percentile|float)}}% of other Genes for Good participants who completed this task. This means that {{'%0.0f'| format(percentile|float)}}% of all participants, on average, cashed in below you, and that {{'%0.0f'|format( 100 - percentile|float)}}% of participants, on average, cashed in above you.
+
+ {% else %}
+
+ We cannot tell you how your score compared to others yet, as not enough people have completed the test. Please try again later.
+ {% endif %}
+
+
+
+
What do my results mean?
+
+
+ This task is formally called the Balloon Analogue Risk Task, or BART (link to original article). With the BART we are able to elicit individuals'
+ appetite for risk. We do this by measuring how many pumps an individual takes in the face of an uncertain explosion point.
+ Individuals vary greatly in their willingness to pump the balloons in the BART, and this variation allows us to label them
+ as risk averse, seeking or neutral. An individual who shies away from risk (pumps very few times) would be labeled risk averse, whereas an individual
+ who is willing to take greater risk (pump more) would be labeled risk seeking. Those individuals who fall in the middle of these
+ preferences would be labeled risk neutral.
+
+
+
+
Why do we study risk with this task?
+ Risky behavior, as it is measured in the BART, is frequently associated with a range of health related risk behaviors such as alcohol use, drug use,
+ unprotected sex, diving without a seatbelt and smoking. With the BART we can identify individuals who are at risk of engaging
+ in these behaviors in a harmful way. While individuals who are risk averse are less likely to engage in these behaviors, it is also known that
+ these individuals are also likely to overspend on insurance and miss out on investment opportunities due to them being overly cautious.
+ Ultimately, understanding how individuals engage in risky behaviors is critical for improving methods for helping populations of individuals
+ who make too many or too few risky decisions in their daily lives.
+
+
+
Throughout this task, you will be presented with 30 balloons, one at a time.' +
+ '
You will be asked to inflate these balloons. Every time you choose to inflate the balloon,' +
+ ' it will grow slightly and you will receive one token. '
+ );
+ };
+
+ BART_tutorial.prototype.learntopump = function () {
+ this.changeStatus('learntopump');
+ $('#continue-instruction').hide();
+ this.displayInstruction(
+ "To inflate the balloon click or tap anywhere in the upper white space of this container.
Go ahead and pump as many times as you'd like."
+ );
+ $('#pump-text').delay(1000).animate({opacity: '1'});
+ };
+
+ BART_tutorial.prototype.watchrangeIntro = function () {
+ this.changeStatus('onboardWatch');
+ this.displayInstruction(
+ "We'd like you to now watch several balloons grow to their maximum sizes, to get a feel for what sorts" +
+ " of balloons you might encounter.
Click or tap on this box to view these balloons inflate."
+ );
+ };
+
+ BART_tutorial.prototype.watchrange = function (required) {
+
+ var self = this;
+ var tokens = 0;
+ var popat = this.popList[required];
+ console.log(popat);
+ window.setTimeout(function () {
+ resetBalloon();
+ }, 1000);
+
+ if (this.autotrial == required) {
+ window.setTimeout(function () {
+ var interv = setInterval(function () {
+ tokens++;
+ $("#balloon-image").animate({height: '+=3.25px', width: '+=3px', top: '-=1px'}, 50);
+ $("#token-text").text(String(tokens) + ' Tokens');
+ if (tokens == popat) {
+ $('#progress').css({width: (1+required) * 6.25.toString() + '%'});
+ clearInterval(interv);
+ popAnimation();
+ self.incrementAuto();
+ if (required < self.popList.length - 1) {
+ self.watchrange(required + 1);
+ } else {
+ $('#continue-instruction').show();
+ self.displayInstruction(
+ "now that you've seen what sorts of balloons you might encounter in this task," +
+ " we'd like to ask you to tell us where you think the balloon is most likely to pop."
+ );
+ self.changeStatus('maxRating');
+ }
+ }
+ }, 120)
+ }, 1500)
+ }
+ };
+
+ // Trial helper
+ BART_tutorial.prototype.incrementAuto = function () {
+ this.autotrial++;
+ };
+
+ BART_tutorial.prototype.changeStatus = function (status) {
+ this.status = status;
+ };
+
+
+
+ BART_tutorial.prototype.maxRating = function () {
+ this.changeStatus('inputMax');
+ this.displayInstruction('Before we move on. Please tell us what you think the largest a balloon can grow to is.');
+ $('#instructions-box').delay(200).append(' ');
+ };
+
+
+ BART_tutorial.prototype.distribution = function () {
+ $('#balloon-image').hide();
+ $('#token-text').hide();
+ $('#cash-box').hide();
+ $('#result-text').hide();
+ $('#instructions-box').animate({opacity: '0'}).hide();
+ $('#chart').css({visibility: 'visible'}).delay(200).animate({opacity: '1'});
+ $('#chart-border').css({visibility: 'visible'}).delay(200).animate({opacity: '1'});
+
+ for (var i = 0; i < 20; i++) {
+ var barHTML = "";
+ var actionHTML = "";
+ $('#chart')
+ .append(barHTML)
+ .append(actionHTML);
+ $('#bar-' + i.toString()).css({left: (i * 5).toString() + '%'});
+ $('#action-' + i.toString()).css({left: (i * 5).toString() + '%'});
+ }
+ $('#chart').append(
+ "
" + this.maxSize + "
" +
+ "
0
" +
+ "
Size (in pumps)
" +
+ "
number of balloons
" +
+ "
Please distribute at least 500 balloons to where you believe the balloons in this task are most likely to pop.
You can click/tap and drag to draw your distribution. Once you have distributed 500 or more balloons you may continue.
" +
+ "
click this box to begin this task
" +
+ "
" +
+ "
0
" +
+ "
Continue
"
+ );
+
+ };
+
+ BART_tutorial.prototype.startDistribute = function () {
+ var self = tutorial;
+ $('#chart-instruct').animate({opacity: '0'}, function () {
+ $('#chart-instruct').hide();
+ self.changeStatus('distributeGo');
+ })
+ };
+
+ BART_tutorial.prototype.moveInChart = function (e) {
+ if (tutorial.status == 'distributeGo') {
+ if (tutorial.isDown) {
+ var selfID = e.id;
+ var chartBar = $('#bar-' + selfID.split('-')[1]);
+ var actionY = event.pageY - $('#chart').offset().top;
+ var barHeight = Math.floor(100 - ((actionY / parseInt($('#chart').css('height'), 10)) * 100));
+
+ chartBar.css({height: barHeight.toString() + '%'});
+ tutorial.barValues[parseInt(selfID.split('-')[1])] = barHeight;
+ tutorial.updateChartText();
+ }
+ }
+ };
+
+ BART_tutorial.prototype.clickInChart = function (e) {
+ if (tutorial.status == 'distributeGo') {
+ var selfID = e.id;
+ var chartBar = $('#bar-' + selfID.split('-')[1]);
+ var actionY = event.pageY - $('#chart').offset().top;
+ var barHeight = Math.floor(100 - ((actionY / parseInt($('#chart').css('height'), 10)) * 100));
+
+ chartBar.css({height: barHeight.toString() + '%'});
+ tutorial.barValues[parseInt(selfID.split('-')[1])] = barHeight;
+ tutorial.updateChartText();
+ }
+ };
+
+ BART_tutorial.prototype.updateChartText = function () {
+ var count = 0;
+ for (var i = 0; i < tutorial.barValues.length; i++) {
+ count += tutorial.barValues[i];
+ }
+ console.log(count);
+ $('#distribution-counter').text(count.toString());
+ if (count >= 500) {
+ $('#chart-next').css({visibility: 'visible'})
+ } else {
+ $('#chart-next').css({visibility: 'hidden'})
+ }
+ };
+
+ BART_tutorial.prototype.submitMax = function () {
+ tutorial.changeStatus('Distribution');
+ tutorial.displayInstruction('Now you will make a distribution');
+ tutorial.maxSize = parseInt($('#maxSize')[0].value);
+ $('input').hide().remove();
+ };
+
+ BART_tutorial.prototype.removeChart = function () {
+ $('#chart').animate({opacity: '0'}, function () {
+ $('#chart').remove();
+ });
+ $('#chart-border').animate({opacity: '0'}, function () {
+ $("#chart-border").remove();
+ });
+ resetBalloon();
+ $('#balloon-image').show();
+ $('#token-text').show();
+ $('#cash-box').show();
+ $('#result-text').show();
+ $('#pump-box').show();
+ $('#instructions-box').animate({opacity: '0'}).show();
+ tutorial.changeStatus('learntocash');
+ tutorial.displayInstruction(
+ "To save your tokens before a balloon pops, you may choose to 'cash in'.
To cash in, touch anywhere " +
+ "inside of the green 'cash in' box at the bottom of this container.
' +
+ 'Every balloon that you inflate will have a different maximum size that it can grow to.' +
+ ' Once the balloon gets to its max size, it will pop and you will lose your current tokens.'
+ );
+ popAnimation();
+ }
+ }
+ } else if (tutorial.status === 'learntocash' && tutorial.tokens < 10){
+ console.log('hi');
+ if (tutorial.active){
+ tutorial.tokens++;
+ $("#balloon-image").animate({height: '+=3.25px', width: '+=3px', top: '-=1px'}, 50);
+ $("#token-text").text(String(tutorial.tokens) + ' Tokens');
+ $('#pump-text').animate({opacity: '0'}, Task.checkTime());
+ }
+ } else if (Task.active){
+ clearInterval(tutorial.flashinterval);
+ clearInterval(Task.flashinterval);
+ console.log(Task.tokens);
+ Task.tokens++;
+ $("#balloon-image").animate({height: '+=3.25px', width: '+=3px', top: '-=1px'}, 50);
+ $("#token-text").text(String(Task.tokens) + ' Tokens');
+ $('#pump-text').animate({opacity: '0'}, Task.checkTime());
+ if (Task.tokens === Task.popPoint) {
+ Task.active = false;
+ popAnimation();
+ $('#cash-text').html('Next Balloon');
+ Task.proceed = true;
+ } else if (Task.proceed) {
+ Task.newTrial();
+
+ }
+ }
+ });
+
+ $('#cash-box').click(function(){
+
+ if (tutorial.status === 'learntocash' && tutorial.tokens == 10) {
+ if (tutorial.active) {
+ clearInterval(tutorial.flashinterval);
+ clearInterval(Task.flashinterval);
+ tutorial.active = false;
+ cashDisplay();
+ tutorial.displayInstruction(
+ "And that's all there is to it!
In this task you will be presented with 30 balloons." +
+ " For each balloon you can choose to pump the balloon to whatever size you like before cashing in" +
+ " your tokens. But remember, if you pump the balloon too much, it will explode and you will lose your tokens." +
+ "
If you are ready to start this task for real, click/tap this box and we will get started."
+ );
+ tutorial.changeStatus('preTask');
+
+ }
+ } else if (Task.active){
+ clearInterval(tutorial.flashinterval);
+ clearInterval(Task.flashinterval);
+ Task.active = false;
+ cashDisplay();
+ $('#cash-text').html('Next Balloon');
+ Task.proceed = true;
+ } else if (Task.proceed) {
+ Task.newTrial();
+ }
+ });
+
+}();
+
+
+
+
diff --git a/gfg_interactive/exp/templates/other/genesforgood.css b/gfg_interactive/exp/templates/other/genesforgood.css
new file mode 100755
index 00000000..e1dc7f11
--- /dev/null
+++ b/gfg_interactive/exp/templates/other/genesforgood.css
@@ -0,0 +1,706 @@
+/* Site Header Styles */
+
+header > nav > div.container-fluid {
+ padding-left: 0px;
+ padding-right: 0px;
+}
+
+header > nav > div.container-fluid > div.row {
+ background: url("../images/logo_bg.png") no-repeat scroll 0 #00294F;
+}
+
+header > nav > div.container-fluid > div.row .logo {
+ display: block;
+ margin: 12px 0px 12px 0px;
+}
+
+nav ul.nav-pills {
+ margin-top: 1em;
+}
+
+nav ul.nav-pills li a {
+ color: #FAFBFC;
+}
+
+nav ul.nav-pills li a:hover {
+ color: #00294F;
+}
+
+.nav-gfg-header {
+ margin-bottom: 0px;
+ background-color: #E7E7E7;
+ border: 0;
+ border-radius: 0;
+}
+
+li.dropdown a h3 {
+ margin: 0;
+ padding-right: 1.2em;
+ color: #00294F;
+}
+
+.navbar-gfg {
+ background-color: #00294F;
+ border: 0;
+}
+
+.navbar-gfg-xs {
+ margin: 0;
+}
+
+.navbar-gfg-xs > li > a {
+ line-height: 61px;
+ padding-top: 11px;
+ padding-bottom: 11px;
+ color: #FAFBFC;
+}
+
+.navbar-gfg-xs > li > a:hover {
+ line-height: 61px;
+ color: #00294F;
+}
+
+.navbar-gfg-xs > li a.navbar-logo:hover {
+ background-color: #00294F;
+}
+
+.navbar-gfg-xs > li {
+ float: left;
+}
+
+#site_notices div.alert {
+ margin-bottom: 0px;
+ text-align: center;
+ font-size: 110%;
+}
+
+#messages div.alert {
+ margin-bottom: 0px;
+ text-align: center;
+}
+
+/* Site Footer Styles */
+
+#footer {
+ padding-top: 1.5em;
+ padding-bottom: 1.5em;
+ background-color: #00294F;
+ color: #FFFFFF;
+ font-size: 110%;
+ position: absolute;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ padding: 1rem;
+ background-color: #efefef;
+ text-align: center;
+}
+
+#footer a {
+ color: #AEC4D9;
+}
+
+
+/* Section Headers */
+
+h2.section_title {
+ padding-top: 0.5em;
+ padding-bottom: 0.3em;
+}
+
+
+/* Main Container/Row Styles */
+
+#main_container {
+ background-color: #F2F2F2;
+}
+
+#main_row {
+ background-color: #FFFFFF;
+ margin-left: 2em;
+ margin-right: 2em;
+ margin: 0 auto;
+ max-width: 1100px;
+}
+
+/* Override default panel heading text color */
+.panel-default > .panel-heading {
+ color: #00294F;
+}
+
+ul.welcome li {
+ margin-bottom: 1em;
+}
+
+/* Checklist Styles */
+ul.checklist {
+ font-size: 1.2em;
+ padding-left: 1.2em;
+ list-style-type: none;
+}
+span.checklist-ok {
+ padding-right: 0.6em;
+ color: #528400;
+}
+span.checklist-not-ok {
+ padding-right: 0.6em;
+ color: #AF1500;
+}
+span.checklist-partial {
+ padding-right: 0.6em;
+ color: #E3B102;
+}
+ul.checklist div.extra-info {
+ padding-left: 2.2em;
+ font-size: 85%;
+}
+ul.checklist div.progress {
+ margin-top: 0.4em;
+}
+
+/* News Styles */
+div.panel-latest-news-clipped {
+ max-height: 940px;
+ overflow: hidden;
+}
+div.latest-news-clipped-read-more {
+ position: absolute;
+ bottom: 1.5em;
+ left: 16px;
+ width: -moz-calc(100% - 32px);
+ width: -webkit-calc(100% - 32px);
+ width: calc(100% - 32px);
+ padding-top: 4em;
+ padding-bottom: 0.8em;
+ text-align: center;
+ border-radius: 2px;
+ background: -moz-linear-gradient(top, rgba(250,251,252,0) 0%, rgba(250,251,252,1) 55%, rgba(250,251,252,1) 100%); /* FF3.6-15 */
+ background: -webkit-linear-gradient(top, rgba(250,251,252,0) 0%,rgba(250,251,252,1) 55%,rgba(250,251,252,1) 100%); /* Chrome10-25,Safari5.1-6 */
+ background: linear-gradient(to bottom, rgba(250,251,252,0) 0%,rgba(250,251,252,1) 55%,rgba(250,251,252,1) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00ffffff', endColorstr='#ffffff',GradientType=0 );
+}
+div.blog_post h2 {
+ font-weight: 800;
+ font-size: 2.3em;
+ text-transform: none;
+ font-variant: small-caps;
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+ padding-top: 0.1em;
+ padding-bottom: 0.1em;
+}
+div.blog_post h3 {
+ font-size: 1.4em;
+ text-transform: none;
+ font-variant: small-caps;
+ margin-top: 0.3em;
+ margin-bottom: 0.3em;
+ padding-top: 0.1em;
+ padding-bottom: 0.1em;
+ letter-spacing: 0px;
+ font-weight: 600;
+}
+div.blog_post img {
+ width: auto !important;
+ height: auto !important;
+ max-width: 650px;
+}
+div.blog_contents h5 {
+ font-size: 1.2em;
+}
+div.blog_contents h5 small {
+ vertical-align: bottom;
+}
+
+/* Consent Form Styles */
+
+#consent_text {
+ margin-left: 2em;
+ margin-right: 2em;
+ margin-bottom: 2em;
+}
+
+#consent_text ul li {
+ margin-bottom: 1em;
+ list-style: square outside none;
+}
+
+#consent_text h4 {
+ font-weight: 700;
+ color: #0B2972;
+}
+
+
+/* Left Nav Styles */
+
+div#left_nav {
+ padding-top: 16px;
+}
+
+ul.left_nav {
+ list-style: none;
+ padding-left: 0px;
+ margin-left: -3px;
+}
+
+ul.left_nav li {
+ line-height: 40px;
+}
+
+ul.left_nav li a {
+ display: inline-block;
+ background: url(../images/marker_1.gif) 0 2px no-repeat;
+ padding-left: 48px;
+ font-weight: normal;
+ text-decoration: none;
+}
+
+ul.left_nav li a:hover {
+ background-position: 0 -38px;
+ color: #0055a2;
+}
+
+ul.left_nav li a.locked{
+ background: url(../images/marker_locked.gif) 0 2px no-repeat;
+}
+
+ul.left_nav li a.unlocked {
+ background: url(../images/marker_unlocked.gif) 0 2px no-repeat;
+}
+
+
+/* Fancybox / Overlay Styles */
+
+.fancybox-nav span {
+ visibility: visible;
+}
+.fancybox-title {
+ font: normal 18px/24px "Helvetica Neue",Helvetica,Arial,sans-serif;
+}
+
+
+/* Download Slide Styles */
+
+div.download_slide {
+ padding: 20px 65px 20px 65px;
+ font-size: 120%;
+}
+
+div.download_slide p {
+ padding-bottom: 0.5em;
+}
+
+div.download_slide h1 {
+ margin-left: 0px;
+ padding-left: 0px;
+ padding-top: 5px;
+}
+
+div.download_slide ol {
+ list-style-type: decimal;
+}
+
+div.download_slide ul {
+ list-style-type: disc;
+}
+
+div.download_slide ul li {
+ margin-left: 20px;
+ padding-bottom: 0.5em;
+}
+
+div.download_slide ol li {
+ margin-left: 20px;
+ padding-bottom: 0.5em;
+}
+
+div.download_slide span.emphasis {
+ color: #1B7FC6;
+ font-weight: bold;
+}
+
+div.download_slide span.warning {
+ color: #FF0E0E;
+ font-weight: bold;
+}
+
+div.download_slide div.box {
+ border: 1px solid #1B7FC6;
+ padding: 10px;
+}
+
+
+/* LEGACY STYLES
+ Several of these may no longer be in use (and if so should be deleted)
+ Any others that override framework styles should at least be reevaluated.
+*/
+
+/* Left & Right alignment */
+.left {
+ float:left
+}
+.right {
+ float:right
+}
+.wrapper {
+ width:100%;
+ overflow:hidden
+}
+
+/* Global properties */
+
+p {
+ padding-bottom: 1em;
+}
+
+.pad1 {
+ padding:0 34px
+}
+.pad_left1 {
+ padding-left:5px
+}
+.pad_top2 {
+ padding-top: 7px;
+ padding-left: 5px;
+}
+.marg_right1 {
+ margin-right:21px;
+ margin-bottom: 15px;
+}
+
+.marg_right2 {
+ margin-right:21px;
+ margin-bottom: 300px;
+}
+.marg_left1 {
+ margin-left:-5px
+}
+
+.box2 {
+ background:#f2f2f2;
+ padding:17px 30px 20px;
+ border-radius:6px;
+ -moz-border-radius:6px;
+ -webkit-border-radius:6px;
+ position:relative;
+ margin-right:5px;
+ margin-bottom:25px
+}
+.button {
+ display:inline-block;
+ color:#fff;
+ background:url(../images/button1_bg.gif) 0 0 repeat-x;
+ line-height:40px;
+ text-decoration:none;
+ cursor:pointer
+}
+.button span {
+ display:block;
+ background:url(../images/button1_left.gif) 0 0 no-repeat
+}
+.button span span {
+ background:url(../images/button1_right.gif) top right no-repeat;
+ padding:0 25px;
+ height:42px
+}
+.button:hover {
+ background-position:bottom
+}
+.button:hover span {
+ background-position:bottom left
+}
+.button:hover span span {
+ background-position:bottom right
+}
+.list2 {
+ margin-top:-1px
+}
+.list2 li {
+ font:14px Tahoma, Geneva, sans-serif;
+ line-height:30px
+}
+.list3 li {
+ line-height:30px
+ border: 1px solid #F2F2F2;
+ padding: 5px,0;
+}
+.list3 li a {
+ color:#0055a2;
+ background:url(../images/marker_3.gif) 0 -17px no-repeat;
+ padding-left:10px;
+ text-decoration: none;
+}
+.list3 li a:hover {
+ color:#000;
+ background-position:0 6px
+}
+.date {
+ float:left;
+ width:72px;
+ height:50px;
+ background:url(../images/date.gif) 0 0 no-repeat;
+ margin-right:14px;
+ font-size:34px;
+ line-height:1.2em;
+ padding-top:22px;
+ text-align:center
+}
+
+.quot {
+ padding-left:42px;
+ background:url(../images/quot.gif) 0 4px no-repeat;
+ overflow:hidden
+}
+.quot span {
+ display:block
+}
+
+legend {
+ color: blue;
+}
+
+#container {
+ min-width: 400px;
+ height: 350px;
+ margin: 0 auto;
+ border:0px solid #DDD;
+ padding: 20px 10px 20px 0;
+}
+
+.loading {
+ display: none;
+ position: fixed;
+ z-index: 8060;
+ top: 0;
+ left: 0;
+ height: 100%;
+ width: 100%;
+ background: rgba( 255, 255, 255, .6) url('../images/ajax-loader1.gif') center 40% no-repeat;
+}
+
+.pad_left3 {
+ padding-left: 10px;
+
+}
+
+.member_status {
+ float: left;
+ padding: 5px 5px 5px 6px;
+}
+
+a.member_level {
+ text-decoration: none;
+ padding-left:-1px;
+ color: #363636;
+}
+
+a.member_level:hover{
+ color: #0055A2;
+}
+
+.formbox {
+ background: none repeat scroll 0 0 #F2F2F2;
+ border-radius: 8px 8px 8px 8px;
+ padding: 20px;
+ position: relative;
+}
+
+.back_link {
+ color: #444;
+ cursor: pointer;
+ display: inline-block;
+ line-height: 40px;
+ text-decoration: underline;
+ padding-right: 15px;
+ float: right;
+}
+
+.survey_list {
+ border-top: 1px solid #F2F2F2;
+ padding: 15px 0;
+}
+
+div.question_box {
+ padding:5px;
+ width: 98%;
+ font-size: 1.2em;
+}
+
+div.congrats_message {
+ height: 350px;
+}
+
+div.survey_list a{
+ font-size: 14px;
+ color: #0055A2;
+ font-weight: bold;
+}
+
+div span#survey_blurb a {
+ text-decoration:none;
+ font-size:0.8em;
+}
+
+div.row label {
+ display: inline-block;
+ padding-right: 8px;
+}
+
+div.row input[type=text] {
+ width: 250px;
+}
+
+div.pad_top2 label {
+ display: inline-block;
+ width: 180px;
+ text-indent: 0px;
+}
+
+div.pad_top2 input[type=text] {
+ width: 180px;
+}
+
+div.textblurb {
+ line-height: 20px;
+ display:block;
+ padding: 10px 0;
+
+}
+
+.trackerPanel {
+ width:48%;
+ height:200px;
+ border:solid 1px #DDD;
+}
+
+p.question_blurb {
+ padding: 0px 20px;
+ font-size: 1em;
+
+}
+
+p.question_blurb span {
+ font-size: 0.8em;
+}
+
+p.question_response {
+ padding-left: 20px;
+}
+
+span.view {
+ margin-top:0px;
+ font-size:0.6em;
+ padding-right:30px;
+}
+
+ul.inline, ul.links.inline {
+ display: inline;
+ padding-left: 0;
+}
+
+ul.inline li {
+ display: inline;
+ list-style-type: none;
+ padding: 0 0.5em;
+}
+
+ul.inline li a{
+ color: #ffffff;
+ text-decoration: none;
+}
+
+input.readonly {
+ color: #777;
+}
+
+input[type=text], select {
+ border: 1px solid #DEE2E7;
+ height: 30px;
+}
+
+#dialog.ui-dialog-titlebar-close {
+ display: none;
+}
+
+.tabs {
+ font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
+}
+
+ul.primary {
+ border-bottom: 1px solid #BBBBBB;
+ border-collapse: collapse;
+ height: auto;
+ line-height: normal;
+ list-style: none outside none;
+ margin: 5px;
+ padding: 0 0 0 1em;
+ white-space: nowrap;
+}
+
+.tabs ul.primary {
+ background: url("../images/tabs-border.png") repeat-x scroll left bottom transparent;
+ border: medium none;
+ margin: 0;
+ overflow: hidden;
+ padding: 0 0px;
+}
+
+.tabs ul.primary li {
+ display: block;
+ float: left;
+ margin: 0 5px 0 0;
+ vertical-align: bottom;
+}
+
+.tabs ul.primary li.active a {
+ background-color: #FFFFFF;
+ border-bottom: 1px solid #FFFFFF;
+}
+
+.tabs ul.primary li a {
+ background-color: #EDEDED;
+ border-top-left-radius: 6px;
+ border-top-right-radius: 6px;
+ color: #000000;
+ display: block;
+ float: left;
+ font-size: 0.929em;
+ height: 1.8em;
+ line-height: 1.9;
+ margin: 0;
+ padding: 0 10px 3px;
+ text-shadow: 0 1px 0 #FFFFFF;
+}
+
+ul.primary li.active a {
+ -moz-border-bottom-colors: none;
+ -moz-border-left-colors: none;
+ -moz-border-right-colors: none;
+ -moz-border-top-colors: none;
+ background-color: #FFFFFF;
+ border-color: #BBBBBB #BBBBBB #FFFFFF;
+ border-image: none;
+ border-style: solid;
+ border-width: 1px;
+}
+
+ul.primary li a {
+ background-color: #DDDDDD;
+ border-color: #BBBBBB;
+ border-style: solid solid none;
+ border-width: 1px;
+ height: auto;
+ margin-right: 0.5em;
+ padding: 0 1em;
+ text-decoration: none;
+}
+
+div.error {
+ font-size: 90%;
+ color: #990000;
+}
+
+div.success {
+ font-size: 90%;
+ color: #009900;
+}
\ No newline at end of file
diff --git a/gfg_interactive/exp/templates/other/header_logo.png b/gfg_interactive/exp/templates/other/header_logo.png
new file mode 100755
index 00000000..fd397b56
Binary files /dev/null and b/gfg_interactive/exp/templates/other/header_logo.png differ
diff --git a/gfg_interactive/experiments.py b/gfg_interactive/experiments.py
index 10930d10..d6dff770 100755
--- a/gfg_interactive/experiments.py
+++ b/gfg_interactive/experiments.py
@@ -1,7 +1,7 @@
from flask import Blueprint, render_template, request, jsonify, current_app, url_for, redirect
from errors import ExperimentError
from models import Session, Participant, CategorySwitch, EventData, KeepTrack, QuestionData, BART
-from sqlalchemy import func
+from sqlalchemy import func, asc, distinct
from sqlalchemy.exc import SQLAlchemyError
from database import db
import db_utils
@@ -34,14 +34,14 @@ def index():
@experiments.route('/task', methods=['GET'])
@utils.nocache
def start_exp():
- """ Serves up the experiment applet.
- If experiment is ongoing or completed, will not serve.
+ """ Serves up the experiment applet.
+ If experiment is ongoing or completed, will not serve.
Querystring args (required):
uniqueid: External gfg_id
surveyid: Which experiment to serve
"""
-
+ print 'make new'
browser, platform = utils.check_browser_platform(request.user_agent)
# Check query string
@@ -73,14 +73,15 @@ def start_exp():
if disqualifying_sessions and current_app.config['EXP_DEBUG'] == False:
raise ExperimentError('already_did_exp', session_id=disqualifying_sessions.session_id)
- # Otherwise, allow participant to re-enter
+
+
else:
session = Session(gfg_id=gfg_id, browser=browser, platform=platform,
status=1, exp_name=exp_name, begin_session=datetime.datetime.now())
db.session.add(session)
db.session.commit()
- return render_template(exp_name + "/exp.html", experimentname=exp_name, surveyid=survey_id,
+ return render_template(exp_name + "/exp.html", experimentname=exp_name, surveyid=survey_id,
sessionid=session.session_id, debug=current_app.config['EXP_DEBUG'],
uniqueid=urllib.quote(uniqueid))
@@ -106,14 +107,14 @@ def enterexp():
session_id = request.form['sessionid']
session = Session.query.filter_by(session_id=session_id).first()
-
+ print 'enterexp'
if session:
session.status = 2
session.begin_experiment = datetime.datetime.now()
db.session.commit()
current_app.logger.info(
- "User has finished the instructions in session id: %s, experiment name: %s",
+ "User has finished the instructions in session id: %s, experiment name: %s",
session_id, session.exp_name)
resp = {"status": "success"}
else:
@@ -170,7 +171,7 @@ def update(session_id=None):
valid_json['sessionid']))
## JAKE: This needs to be slightly customized to add your task
- ## However, most of the work will be in models.py
+ ## However, most of the work will be in models.py
# For each trial, pass to appropriate parser, if not in db
for json_trial in valid_json['data']:
if session.exp_name == "category_switch":
@@ -179,6 +180,7 @@ def update(session_id=None):
experiment_class = KeepTrack
elif session.exp_name == 'BART':
experiment_class = BART
+ print 'jake'
db_trial, new = db_utils.get_or_create(db.session,
experiment_class, gfg_id=session.gfg_id, session_id=session.session_id,
@@ -194,10 +196,11 @@ def update(session_id=None):
# For each event, pass to parser, if not in db
for json_event in valid_json['eventdata']:
db_event, new = db_utils.get_or_create(db.session, EventData,
- gfg_id=session.gfg_id, session_id=session.session_id, exp_name=session.exp_name,
+ gfg_id=session.gfg_id, session_id=session.session_id, exp_name=session.exp_name,
timestamp = utils.convert_timestamp(json_event['timestamp']))
if new:
+ print 'added';
db_event.add_json_data(json_event)
db.session.commit()
@@ -206,7 +209,7 @@ def update(session_id=None):
# For the QuestionData, pass to parser, if not in db
db_ques, new = db_utils.get_or_create(db.session, QuestionData,
gfg_id=session.gfg_id, session_id=session.session_id, exp_name=session.exp_name)
- db_ques.add_json_data(valid_json['questiondata'])
+ db_ques.add_json_data(valid_json['questiondata'])
db.session.commit()
if resp is None:
@@ -229,7 +232,7 @@ def quitter():
session = Session.query.filter(
Session.session_id == session_id).one()
session.status = 6
- db.session.commit()
+ db.session.commit()
resp = {"status": "marked as quitter"}
except SQLAlchemyError:
@@ -264,9 +267,7 @@ def worker_complete():
return jsonify(**resp)
-## JAKE: This part might take a bit more work since there is custom code to score
-## and percentile rank their results. dataHandler.js automatically redirects here
-## once exitTask() is called
+## As you know, this is where you get routed for results
@experiments.route('/results', methods=['GET'])
def results():
"""Return results at the end."""
@@ -279,23 +280,31 @@ def results():
current_app.logger.info("Results: uniqueid is %s, exp_name is %s and survey id is %s" %(uniqueid, exp_name, survey_id))
## Get last session with code 3 from user
+
gfg_id = utils.decrypt(str(current_app.config['SECRET_KEY']), str(uniqueid))
current_app.logger.info("GFG id after decrypt is -- %s" % (gfg_id))
+ # I would leave all the stuff above alone, as its just getting all the variable it needs
+
+ # Here we are "supposedly" querying the DB for the last session from that user. it is possible that this should be asc() not desc()
try:
session = Session.query.filter_by(gfg_id=gfg_id, status=3, exp_name=exp_name).order_by(Session.session_id.desc()).first()
except SQLAlchemyError:
raise ExperimentError('user_access_denied')
+ # As you can see, we start doing things different for each task here, but template is still just
+ # expecting two variables, score and percentile (last line). So if you want to do something
+ # totally different, then you need to stick an if statement that splits off the whole thing here
+ # if session.exp_name == "bart": {your stuff} else:
if session is None :
- current_app.logger.info("Session is null---%s" %(session))
- raise ExperimentError('user_access_denied')
+ current_app.logger.info("Session is null---%s" %(session))
+ raise ExperimentError('user_access_denied')
elif session.exp_name == "keep_track":
- target_trials = KeepTrack.query.filter(KeepTrack.session_id==session.session_id,
+ target_trials = KeepTrack.query.filter(KeepTrack.session_id==session.session_id,
KeepTrack.block.in_(["1", "2", "3", "4", "5", "6"])).all()
- all_scored = []
+ all_scored = []
for trial in target_trials:
score = trial.simple_score()
all_scored += score
@@ -306,10 +315,10 @@ def results():
elif session.exp_name == "category_switch":
single_trials_avg = db.session.query(func.avg(CategorySwitch.reaction_time).label('average')).filter(
- CategorySwitch.session_id==session.session_id, CategorySwitch.block.in_(["sizeReal", "livingReal"]),
+ CategorySwitch.session_id==session.session_id, CategorySwitch.block.in_(["sizeReal", "livingReal"]),
CategorySwitch.accuracy==1).all()
mixed_trials_avg = db.session.query(func.avg(CategorySwitch.reaction_time).label('average')).filter(
- CategorySwitch.session_id==session.session_id, CategorySwitch.block.in_(["mixedReal1", "mixedReal2"]),
+ CategorySwitch.session_id==session.session_id, CategorySwitch.block.in_(["mixedReal1", "mixedReal2"]),
CategorySwitch.accuracy==1).all()
## This value also needs to be stored
@@ -318,15 +327,84 @@ def results():
## JAKE: You'd need to add another elif for your task. This is probably not the best way to code this but oh well
## Probably would be good to have the scoring functions all in one file and just call the right function for the git
## task, but lets leave that for later
+ elif session.exp_name == 'BART':
+ def date_handler(date):
+ return [date.year,date.month,date.day,date.hour,date.minute]
+
+
+ score = round(db.session.query(func.avg(BART.pumps).label('average')).filter(BART.session_id == session.session_id,
+ BART.user_action == 2).all()[0][0])
+ session.results = score
+ db.session.commit()
+
+ # data from subjects with completed sessions
+ completed_others = [_[0] for _ in db.session.query(distinct(Session.gfg_id)).filter(
+ # Session.gfg_id != gfg_id,
+ Session.exp_name == session.exp_name,
+ Session.status == 3).all()]
+
+ # Individuals previous scores
+ previousSessions = [_[0] for _ in db.session.query(Session.results).filter(Session.gfg_id == gfg_id,
+ Session.exp_name == exp_name,
+ Session.status == 3).order_by(
+ asc(Session.session_id)).all()]
+ print previousSessions
+ dates = [date_handler(_[0]) for _ in db.session.query(Session.begin_session).filter(Session.gfg_id == gfg_id,
+ Session.exp_name == exp_name,
+ Session.status == 3).order_by(
+ asc(Session.session_id)).all()]
+ if len(previousSessions) == 0:
+ previousSessions = None
+
+ # current score
+ print dates
+
+ print 'Other participants'
+ print completed_others
+ if len(completed_others) > 0:
+
+ mean_score = db.session.query(func.avg(Session.results).label('average')).filter(
+ Session.gfg_id.in_(completed_others),
+ Session.status == 3,
+ Session.exp_name == exp_name).group_by(Session.gfg_id).all()
+
+ std_score = db.session.query(func.STD(Session.results).label('average')).filter(
+ Session.gfg_id.in_(completed_others),
+ Session.status == 3,
+ Session.exp_name == exp_name).group_by(Session.gfg_id).all()
+
+ percentile = stats.z2p((score - mean_score[0][0]) / (std_score[0][0] + 0.0000001))
+ otherResults = [_[0] for _ in db.session.query(Session.results).filter(Session.gfg_id.in_(completed_others),
+ Session.status == 3,
+ Session.exp_name == exp_name).all()]
+
+ print otherResults
+ else:
+ percentile = None
+ otherResults = None
+
+ return render_template(session.exp_name + "/results.html",
+ score=score,
+ others = otherResults,
+ previousSessions = previousSessions,
+ dates = dates,
+ percentile = int(percentile * 100)
+ )
+
+ # Do make sure to save the value that you computer for each subject here
session.results = score
db.session.commit()
+ ## I would also make sure to use the same method to get "comparion subjects"
+ ## to have consistency across GFG
+
## Find other people in same age range. If more than 20, calculate percentile and display
age_matched_ids = db_utils.get_age_matched_ids(gfg_id, current_app.config['RESEARCH_DB_HOST'], current_app.config['RESEARCH_DB_USER'],
current_app.config['RESEARCH_DB_PASSWORD'], current_app.config['RESEARCH_DB_NAME'])
-
+ # Importantly, I only show results if I have at least 20 control subjects, so when there is very little data
+ # I don't show anything because it can be misleading.
if len(age_matched_ids) > 20:
mean_score = db.session.query(func.avg(Session.results).label('average')).filter(
Session.gfg_id.in_(age_matched_ids), Session.exp_name == session.exp_name, Session.status==3).all()
@@ -334,13 +412,14 @@ def results():
std_score = db.session.query(func.STD(Session.results).label('average')).filter(
Session.gfg_id.in_(age_matched_ids), Session.exp_name == session.exp_name, Session.status==3).all()
- print mean_score
percentile = stats.z2p((score - mean_score[0][0]) / (std_score[0][0] + 0.0000001))
else:
percentile = None
- return render_template(session.exp_name + "/results.html",
+ # If you're going to do something different, then you'd want this call to be in your own
+ # "if" branch, because presumable you'd want to pass different variables.
+ return render_template(session.exp_name + "/results.html",
score=score,
percentile=percentile)
diff --git a/gfg_interactive/manage.py b/gfg_interactive/manage.py
index adb23ab7..45e41745 100755
--- a/gfg_interactive/manage.py
+++ b/gfg_interactive/manage.py
@@ -1,5 +1,5 @@
-from flask.ext.script import Manager
-from flask.ext.migrate import Migrate, MigrateCommand
+from flask_script import Manager
+from flask_migrate import Migrate, MigrateCommand
import ConfigParser
from app import app, db
import os
diff --git a/gfg_interactive/migrations/alembic.ini b/gfg_interactive/migrations/alembic.ini
old mode 100755
new mode 100644
diff --git a/gfg_interactive/migrations/script.py.mako b/gfg_interactive/migrations/script.py.mako
index 95702017..2c015630 100755
--- a/gfg_interactive/migrations/script.py.mako
+++ b/gfg_interactive/migrations/script.py.mako
@@ -1,18 +1,20 @@
"""${message}
Revision ID: ${up_revision}
-Revises: ${down_revision}
+Revises: ${down_revision | comma,n}
Create Date: ${create_date}
"""
+from alembic import op
+import sqlalchemy as sa
+${imports if imports else ""}
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
+branch_labels = ${repr(branch_labels)}
+depends_on = ${repr(depends_on)}
-from alembic import op
-import sqlalchemy as sa
-${imports if imports else ""}
def upgrade():
${upgrades if upgrades else "pass"}
diff --git a/gfg_interactive/migrations/versions/43c800fdf0c8_.py b/gfg_interactive/migrations/versions/23892fdee24d_.py
old mode 100755
new mode 100644
similarity index 69%
rename from gfg_interactive/migrations/versions/43c800fdf0c8_.py
rename to gfg_interactive/migrations/versions/23892fdee24d_.py
index a526c708..9dc19f07
--- a/gfg_interactive/migrations/versions/43c800fdf0c8_.py
+++ b/gfg_interactive/migrations/versions/23892fdee24d_.py
@@ -1,21 +1,23 @@
"""empty message
-Revision ID: 43c800fdf0c8
-Revises: None
-Create Date: 2016-07-28 13:25:36.097195
+Revision ID: 23892fdee24d
+Revises:
+Create Date: 2017-04-20 11:47:15.423963
"""
+from alembic import op
+import sqlalchemy as sa
+
# revision identifiers, used by Alembic.
-revision = '43c800fdf0c8'
+revision = '23892fdee24d'
down_revision = None
-
-from alembic import op
-import sqlalchemy as sa
+branch_labels = None
+depends_on = None
def upgrade():
- ### commands auto generated by Alembic - please adjust! ###
+ # ### commands auto generated by Alembic - please adjust! ###
op.create_table('participant',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('gfg_id', sa.String(length=32), nullable=False),
@@ -33,6 +35,20 @@ def upgrade():
sa.Column('results', sa.Float(precision=2), nullable=True),
sa.PrimaryKeyConstraint('session_id')
)
+ op.create_table('BART',
+ sa.Column('action_id', sa.Integer(), nullable=False),
+ sa.Column('gfg_id', sa.String(length=32), nullable=False),
+ sa.Column('session_id', sa.Integer(), nullable=True),
+ sa.Column('trial_num', sa.Integer(), nullable=True),
+ sa.Column('trial_action_num', sa.Integer(), nullable=True),
+ sa.Column('user_action', sa.Integer(), nullable=True),
+ sa.Column('pumps', sa.Integer(), nullable=True),
+ sa.Column('pop_point', sa.Integer(), nullable=True),
+ sa.Column('timestamp', sa.DateTime(), nullable=True),
+ sa.Column('distribution', sa.String(length=300), nullable=True),
+ sa.ForeignKeyConstraint(['session_id'], ['session.session_id'], ),
+ sa.PrimaryKeyConstraint('action_id')
+ )
op.create_table('category_switch',
sa.Column('cs_id', sa.Integer(), nullable=False),
sa.Column('gfg_id', sa.String(length=32), nullable=False),
@@ -41,10 +57,10 @@ def upgrade():
sa.Column('response', sa.String(length=2), nullable=True),
sa.Column('reaction_time', sa.Float(), nullable=True),
sa.Column('accuracy', sa.Integer(), nullable=True),
- sa.Column('block', sa.Unicode(length=32), nullable=True),
- sa.Column('question', sa.Unicode(length=32), nullable=True),
- sa.Column('answer', sa.Unicode(length=32), nullable=True),
- sa.Column('user_answer', sa.Unicode(length=32), nullable=True),
+ sa.Column('block', sa.String(length=200), nullable=True),
+ sa.Column('question', sa.String(length=100), nullable=True),
+ sa.Column('answer', sa.String(length=100), nullable=True),
+ sa.Column('user_answer', sa.String(length=100), nullable=True),
sa.Column('timestamp', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['session_id'], ['session.session_id'], ),
sa.PrimaryKeyConstraint('cs_id')
@@ -55,7 +71,7 @@ def upgrade():
sa.Column('session_id', sa.Integer(), nullable=True),
sa.Column('exp_name', sa.String(length=32), nullable=False),
sa.Column('event_type', sa.String(length=32), nullable=True),
- sa.Column('value', sa.Unicode(length=32), nullable=True),
+ sa.Column('value', sa.String(length=100), nullable=True),
sa.Column('interval', sa.Float(), nullable=True),
sa.Column('timestamp', sa.DateTime(), nullable=False),
sa.ForeignKeyConstraint(['session_id'], ['session.session_id'], ),
@@ -68,10 +84,10 @@ def upgrade():
sa.Column('trial_num', sa.Integer(), nullable=True),
sa.Column('reaction_time', sa.Float(), nullable=True),
sa.Column('accuracy', sa.String(length=32), nullable=True),
- sa.Column('block', sa.Unicode(length=32), nullable=True),
+ sa.Column('block', sa.String(length=200), nullable=True),
sa.Column('timestamp', sa.DateTime(), nullable=True),
- sa.Column('target_words', sa.Unicode(length=32), nullable=True),
- sa.Column('input_words', sa.Unicode(length=32), nullable=True),
+ sa.Column('target_words', sa.String(length=100), nullable=True),
+ sa.Column('input_words', sa.String(length=100), nullable=True),
sa.ForeignKeyConstraint(['session_id'], ['session.session_id'], ),
sa.PrimaryKeyConstraint('kt_id')
)
@@ -87,15 +103,16 @@ def upgrade():
sa.ForeignKeyConstraint(['session_id'], ['session.session_id'], ),
sa.PrimaryKeyConstraint('q_id')
)
- ### end Alembic commands ###
+ # ### end Alembic commands ###
def downgrade():
- ### commands auto generated by Alembic - please adjust! ###
+ # ### commands auto generated by Alembic - please adjust! ###
op.drop_table('question_data')
op.drop_table('keep_track')
op.drop_table('event_data')
op.drop_table('category_switch')
+ op.drop_table('BART')
op.drop_table('session')
op.drop_table('participant')
- ### end Alembic commands ###
+ # ### end Alembic commands ###
diff --git a/gfg_interactive/models.py b/gfg_interactive/models.py
index 151823a8..852c4f39 100755
--- a/gfg_interactive/models.py
+++ b/gfg_interactive/models.py
@@ -148,29 +148,31 @@ def simple_score(self):
class BART(db.Model):
- bart_id = db.Column(db.Integer, primary_key=True)
+ action_id = db.Column(db.Integer, primary_key=True)
gfg_id = db.Column(db.String(32), nullable=False)
session_id = db.Column(db.Integer, db.ForeignKey('session.session_id'))
trial_num = db.Column(db.Integer)
- balloon_num = db.Column(db.Integer)
+ trial_action_num = db.Column(db.Integer)
user_action = db.Column(db.Integer) # 1 = pump, 0 = pop, 2 = cash, 3 = reset
pumps = db.Column(db.Integer)
pop_point = db.Column(db.Integer)
timestamp = db.Column(db.DateTime)
-
+ distribution = db.Column(db.String(300))
# This is how the model prints itself
def __repr__(self):
- return "BART Values (%s, %s, %s, %s)" % (self.bart_id, self.gfg_id,
- self.session_id, self.trial_num)
+ return "BART Values (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)" % (self.action_id, self.gfg_id,
+ self.session_id, self.trial_num, self.trial_action_num,self.user_action,self.pumps,self.pop_point,self.timestamp, self.distribution)
def add_json_data(self, json_trial):
- self.trial_num = json_trial['current_trial']
trial_data = json_trial['trialdata']
+ self.trial_action_num = json_trial['current_trial']
+ self.trial_num = trial_data['balloon_num']
self.user_action = trial_data['action']
self.pumps = trial_data['pumps']
self.pop_point = trial_data['pop_point']
- self.balloon_num = trial_data['balloon']
self.timestamp = convert_timestamp(json_trial['dateTime'])
+ self.distribution = trial_data['dist']
+
class EventData(db.Model):