from otree.api import * doc = """ 1. Participants see figures with dots and enter their guess. 2. They see their guess displayed back together with the true number of dots. Payoff depends on accuracy over the two rounds. """ """ TODO: OPTIONAL TODOS: - What kind of feedback should no-feedback-treatment see? //Only if time allows - add update page where participants can use a slider to adjust between there guess and the algorithm recommendation //Only if time allows - host images externally to improve performance //Only if time allows - add feedback about all rounds at the very end of nofeedback treatment //Only if time allows - change payment function such that players can get no bonus pay // Only if time allows - Info about payment function can be hidden // Only if time allows DONE: - revise text explaining the algo (revise texts generally) - Check whether payoff function is too generous/strict - test extensively - On the final result page needs to be a link or instruction to go back to MTurk, how is that done? @Jan - On welcome page: after test runs on MTurk: enter average completion time and reward - @Jojo: Talk about the last page: Is rounding at the decimat level a problem? - - If you enter a value smaller than 0, the error message is in German (on my machine) | done - do-over for the pages layout | done - How do we ensure we don't overrun our budget on MTurk? Afaik we define how many people play it, but we don't know how much we have to pay them? - scale up to 16 rounds | done - Find a way to save overall payoff in the output | done - Add treatment No. 5 | done - Fix payments so they show up in the excel export file | done - add treatment vs control | done - add timeout after 60 seconds | done - Fix images: Image with and without red squares must have the IDENTICAL dot distribution, not just the same # of dots | done - Add pages with algorithmic recommendation (@Jojo and/or pair-coding) / done - Add text on transperancy | done - Make efficient choices: Where do we need different pages? | done - Once algorithmic recommendation is added: Implement all 4 treatments //IMPORTANT, but in case of issues, less treatments also ok | done - Add instructions, consent form, final page (@Jan) //IMPORTANT, but do it at the end | done """ class Constants(BaseConstants): name_in_url = 'dots' players_per_group = None # maximally possible num_rounds is 16 num_rounds = 16 # Algo predictions for triangular graphs machine_prediction1 = 617 # Need to scale up to 15 machine_prediction2 = 267 machine_prediction3 = 608 machine_prediction4 = 550 machine_prediction5 = 1308 machine_prediction6 = 650 machine_prediction7 = 1475 machine_prediction8 = 542 machine_prediction9 = 433 machine_prediction10 = 1100 machine_prediction11 = 792 machine_prediction12 = 908 machine_prediction13 = 842 machine_prediction14 = 667 machine_prediction15 = 1383 machine_prediction16 = 883 # True no of dots for triangular graphs true1 = 1637 # Need to scale this up to to 15 true2 = 1898 true3 = 1048 true4 = 1041 true5 = 2273 true6 = 1355 true7 = 2621 true8 = 1190 true9 = 942 true10 = 2640 true11 = 1831 true12 = 1696 true13 = 1855 true14 = 977 true15 = 2913 true16 = 3084 # True numbers and predictions for uniform graphs uni_true_32 = 3043 uni_pred_32 = 3217 uni_true_33 = 2016 uni_pred_33 = 2067 uni_true_34 = 1179 uni_pred_34 = 1183 uni_true_35 = 2191 uni_pred_35 = 2125 uni_true_36 = 2045 uni_pred_36 = 2083 uni_true_37 = 2522 uni_pred_37 = 2400 uni_true_38 = 3180 uni_pred_38 = 3000 uni_true_39 = 2650 uni_pred_39 = 2742 showup_fee = 0 # Not: They do get $6/hour in MTurk, but we don't track it here to simplify payouts bonus_correct_belief = 0.15 # Notes: This is per round bonus_belief_deviation = 0.0002 class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): consent = models.BooleanField(widget=widgets.CheckboxInput( ), label="I have read and understood this consent form and wish to participate in this study.") guess_i = models.IntegerField(min=0, label="Please enter your guess: ") # i =initial guess_r = models.IntegerField(min=0, label="Please enter your revised guess: ") # r=revised treatment = models.StringField() # i stands for "initial", i.e. before advice guessing_bonus_for_payoff_i = models.FloatField() # r stands for "revised", i.e. after advice guessing_bonus_for_payoff_r = models.FloatField() # s stands for "sum", i.e. the sum of initial and revised guessing_bonus_for_payoff_s = models.FloatField() feedback = models.LongStringField(blank=True) # FUNCTIONS def creating_session(subsession): import itertools treatments = itertools.cycle( ['blackbox_nofeed', 'trans_nofeed', 'blackbox_feed', 'trans_feed', 'dist']) for player in subsession.get_players(): player.treatment = next(treatments) # PAGES class aa_welcome(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 # Display only in first round form_model = 'player' form_fields = ['consent'] class bb_guess_page(Page): form_model = 'player' form_fields = ['guess_i'] timeout_seconds = 60 @staticmethod def vars_for_template(player): if player.treatment == 'dist': if player.round_number % 2 == 0: return dict( image_path='graph_{}.png'.format( player.round_number - 1) ) else: return dict( image_path='graph_{}.png'.format( player.round_number + 31) ) else: return dict( image_path='graph_{}.png'.format( player.round_number - 1) ) @staticmethod # Need to scale this up to to 15 def before_next_page(player: Player, timeout_happened): # if we're in the dist treatment, the true constants are "uni_true_i" and "true_i". # else: (we're in the other 4 treatments and the true constants are only "true_i") if player.treatment == 'dist': if player.round_number == 1: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.uni_true_32) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 2: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true1) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 3: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.uni_true_33) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 4: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true2) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 5: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.uni_true_34) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 6: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true3) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 7: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.uni_true_35) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 8: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true4) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 9: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.uni_true_36) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 10: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true5) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 11: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.uni_true_37) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 12: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true6) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 13: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.uni_true_38) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 14: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true7) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 15: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.uni_true_39) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 16: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true8) * Constants.bonus_belief_deviation, 0), 2) else: if player.round_number == 1: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true1) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 2: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true2) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 3: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true3) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 4: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true4) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 5: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true5) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 6: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true6) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 7: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true7) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 8: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true8) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 9: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true9) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 10: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true10) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 11: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true11) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 12: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true12) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 13: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true13) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 14: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true14) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 15: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true15) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 16: player.guessing_bonus_for_payoff_i = round(max(Constants.bonus_correct_belief - abs( player.guess_i - Constants.true16) * Constants.bonus_belief_deviation, 0), 2) class cc_revised_guess_page(Page): form_model = 'player' form_fields = ['guess_r'] timeout_seconds = 60 @staticmethod def vars_for_template(player): if player.treatment == 'blackbox_nofeed' or player.treatment == 'blackbox_feed': return dict( image_path_blackbox='graph_{}.png'.format( player.round_number - 1) ) elif player.treatment == 'trans_nofeed' or player.treatment == 'trans_feed': return dict( image_path_trans='graph_square_{}.png'.format( player.round_number - 1) ) elif player.treatment == 'dist': if player.round_number % 2 == 0: return dict( image_path='graph_square_{}.png'.format( player.round_number - 1) ) else: return dict( image_path='graph_square_{}.png'.format( player.round_number + 31) ) @staticmethod # Need to scale this up to to 15 def before_next_page(player: Player, timeout_happened): if player.treatment == 'dist': if player.round_number == 1: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.uni_true_32) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 2: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true1) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 3: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.uni_true_33) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 4: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true2) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 5: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.uni_true_34) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 6: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true3) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 7: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.uni_true_35) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 8: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true4) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 9: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.uni_true_36) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 10: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true5) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 11: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.uni_true_37) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 12: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true6) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 13: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.uni_true_38) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 14: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true7) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 15: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.uni_true_39) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 16: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true8) * Constants.bonus_belief_deviation, 0), 2) else: if player.round_number == 1: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true1) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 2: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true2) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 3: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true3) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 4: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true4) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 5: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true5) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 6: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true6) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 7: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true7) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 8: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true8) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 9: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true9) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 10: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true10) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 11: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true11) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 12: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true12) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 13: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true13) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 14: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true14) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 15: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true15) * Constants.bonus_belief_deviation, 0), 2) elif player.round_number == 16: player.guessing_bonus_for_payoff_r = round(max(Constants.bonus_correct_belief - abs( player.guess_r - Constants.true16) * Constants.bonus_belief_deviation, 0), 2) class dd_feedback_page(Page): @staticmethod # Need to scale this up to to 15 def vars_for_template(player): player.guessing_bonus_for_payoff_s = player.guessing_bonus_for_payoff_i + \ player.guessing_bonus_for_payoff_r player.payoff = player.guessing_bonus_for_payoff_i + \ player.guessing_bonus_for_payoff_r # Add up results from all rounds and show the result to the subjects class ee_final_results(Page): @staticmethod def is_displayed(player: Player): # Page is only displayed if this is true. player.round_number return player.round_number == Constants.num_rounds # is generated automatically. # feedback page class z_questions3(Page): form_model = 'player' form_fields = ['feedback'] @staticmethod def is_displayed(player: Player): # Page is only displayed if this is true. player.round_number return player.round_number == Constants.num_rounds # is generated automatically. page_sequence = [aa_welcome, bb_guess_page, cc_revised_guess_page, dd_feedback_page, z_questions3, ee_final_results ]