from otree.api import * class C(BaseConstants): NAME_IN_URL = 'pilot2_2' PLAYERS_PER_GROUP = None NUM_ROUNDS = 100 # Total rounds NUM_CLS = 2 # Number of class used NUM_CLS_ROUNDS = int(NUM_ROUNDS / NUM_CLS) TIMEOUT_S = 60 # Maximum time used for each image NUM_ROUNDS_PAY = 1 # Number of rounds paid BREAK_ROUND = 10 # Number of rounds before going to break NUM_TREAT_GP = 1 # Number of control and treatment groups TIMEOUT = 'TRUE' #TRUE if timeout, else no timeout BONUS_LIST = [0, 1, 5, 10, 20] INSTRUCTIONS_TEMPLATE1 = 'pilot2/instructions1.html' INSTRUCTIONS_TEMPLATE2 = 'pilot2/instructions2.html' INSTRUCTIONS_TEMPLATE3 = 'pilot2/instructions3.html' BSR = 'pilot2/BSR.html' #PRIOR = 0.5 PRIOR_LIST = [0.5] # prob of category1 between 0 and 1 [0.5, 0.8] #PRIOR_DISPLAY = PRIOR*100 PAYOFF = 5 PARTICIPATION_FEE = 5 class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): Authorization = models.CharField( choices=['Yes, I would like to participate in this study, I am a resident of the United States, and I am 18 or older', 'No, I cannot participate'], widget=widgets.RadioSelect) math = models.CharField(#label = '100/2=', #choices=['20', # '30', # '50'], #widget=widgets.RadioSelect ) atten = models.CharField(#label = '2+5+3=', #choices=['10', # '12', # '14'], #widget=widgets.RadioSelect ) quiz = models.FloatField( min=0, max=100, label = "" ) prob = models.FloatField( min=0, max=100, label = "" #label="What is the probability that this picture is a DOG (rather than a cat)? Please pick a number from 0 to 100:", ) check_slider_main = models.IntegerField( min=0, max=100, label = "" ) prob_expost = models.FloatField( min=0, max=100, label = "" #label="What is the probability that this picture is a DOG (rather than a cat)? Please pick a number from 0 to 100:", ) check_slider_expost = models.IntegerField( min=0, max=100, label = "" ) prac1 = models.FloatField( min=0, max=100, label = "" ) check_slider_one = models.IntegerField( min=0, max=100, label = "" ) prac2 = models.FloatField( min=0, max=100, label = "" ) check_slider_two = models.IntegerField( min=0, max=100, label = "" ) prac3 = models.FloatField( min=0, max=100, label = "" ) check_slider_three = models.IntegerField( min=0, max=100, label = "" ) prac4 = models.FloatField( min=0, max=100, label = "" ) check_slider_four = models.IntegerField( min=0, max=100, label = "" ) fatigue = models.StringField(blank=True, label = "When did you start to feel fatigue? (e.g. Session 2 Round 24)") instruction = models.StringField(blank=True, label = "Is the instruction clear enough to you? If not, why do you think it is not clear?") ever_work_pict = models.CharField(blank=True) #ever_work_pict = models.StringField(blank=True, label = "Do you have any other comments about the experiment? Any feedback is welcome.") #random_pict = [models.IntegerField(min=0,max=9,label = str(i)) for i in range(C.NUM_ROUNDS)] other_comment = models.StringField(blank=True, label = "Do you have any other comments about the experiment? Any feedback is welcome.") button_clicks = models.IntegerField(initial=0) timer = models.IntegerField(initial=0) class Machine(ExtraModel): number = models.StringField() filename = models.StringField() machine_prob = models.FloatField() def prob_error_message(player, value): if value > 100: return 'Number must be between 0 and 100.' # FUNCTIONS def creating_session(subsession): def createList(r1, r2): return [item for item in range(r1, r2+1)] if subsession.round_number == 1: import random for player in subsession.get_players(): participant = player.participant #participant.AI_inst1 = random.choice([0,1]) participant.prior = random.choice(C.PRIOR_LIST) # 'treat_gp' determines which treatment groups subjects are assigned # treat_gp = 1 implies no AI first # treat_gp = 2 implies AI first participant.treat_gp = random.choice(createList(1, C.NUM_TREAT_GP)) # 'paying_rounds' is a list of numbers determining which rounds to be paid, from 0 to 99 participant.paying_rounds = random.sample(range(C.NUM_ROUNDS), C.NUM_ROUNDS_PAY) # 'random_pic_num' represents which pictures to show to subjects, this is subject specific participant.random_pic_num = random.sample(range(100), C.NUM_ROUNDS) # 'random_class' represents a list of number drawn from a uniform distribution determine cat or dog, this is subject specific # participant.random_class = [random.uniform(0, 1) for _ in range(C.NUM_ROUNDS)] # participant.bonus_list1 = random.sample(C.BONUS_LIST, k=(int(C.NUM_ROUNDS/C.BREAK_ROUND/C.NUM_CLS))) # participant.bonus_list2 = random.sample(C.BONUS_LIST, k=(int(C.NUM_ROUNDS/C.BREAK_ROUND/C.NUM_CLS))) # dummy = [25] #participant.bonus_list = participant.bonus_list.extend(dummy) # for i in range(C.NUM_ROUNDS): # if participant.random_class[i] < participant.prior: # participant.random_class[i] = 0 #category1 # else: # participant.random_class[i] = 1 #category2 def set_payoffs(player: Player): import random subsession = player.subsession session = player.session participant = player.participant participant.payoff_list = [None] * C.NUM_ROUNDS_PAY participant.bsq_prob_list = [None] * C.NUM_ROUNDS_PAY prob_list = [random.uniform(0, 1) for _ in range(C.NUM_ROUNDS_PAY)] round_paid = participant.paying_rounds[0]+1 image = participant.random_pic_num[round_paid-1] for i in range(C.NUM_ROUNDS_PAY): if image <= 50: participant.bsq_prob_list[i] = 1 - (1 - player.in_round(participant.paying_rounds[i]+1).prob / 100)**2 else: participant.bsq_prob_list[i] = 1 - (player.in_round(participant.paying_rounds[i]+1).prob / 100) **2 for i in range(C.NUM_ROUNDS_PAY): from random import choices potential_payment = [0, C.PAYOFF] weights = [1 - participant.bsq_prob_list[i], participant.bsq_prob_list[i]] participant.payoff_list[i] = random.choices(potential_payment, weights)[0] if player.in_round(participant.paying_rounds[i]+1).prob < 0: # if the person either put something does not make sense, or not enough time, their payoff for the round must be equal to 0. participant.payoff_list[i] = 0 #player.payoff = sum(participant.payoff_list) # participant.payoff_list[i] = player.in_round(participant.paying_rounds[i]+1).prob # for player in subsession.get_players(): # import random # participant = player.participant # participant.payoff_list = [None] * C.NUM_ROUNDS_PAY # for i in range(C.NUM_ROUNDS_PAY): # prev_player = player.in_round(participant.paying_rounds[i]+1) # participant.payoff_list[i] = player.in_round(1).prob # for i in range(C.NUM_ROUNDS_PAY): # participant.payoff_list[i] = player.prob # participant.total_payoff = sum(participant.payoff_list) # PAGES class Consent(Page): form_model = 'player' form_fields = ['Authorization'] @staticmethod def is_displayed(player: Player): return player.round_number == 1 class Consentout(Page): form_model = 'player' @staticmethod def is_displayed(player: Player): return (player.round_number == 1) & (player.in_round(1).Authorization == 'No, I cannot participate') def js_vars(player): return dict( consentlink= player.subsession.session.config['consentlink'] ) class Consentfinal(Page): form_model = 'player' @staticmethod def is_displayed(player: Player): return (player.round_number == 1) & (player.in_round(1).Authorization == 'No, I cannot participate') def js_vars(player): return dict( consentlink= player.subsession.session.config['consentlink'] ) class Attention(Page): form_model = 'player' form_fields = ['math', 'atten'] @staticmethod def is_displayed(player: Player): return player.round_number == 1 class Attentionout(Page): form_model = 'player' @staticmethod def is_displayed(player: Player): return (player.round_number == 1)&((player.in_round(1).math != '50') | (player.in_round(1).atten != '14')) def js_vars(player): return dict( attentionlink= player.subsession.session.config['attentionlink'] ) class Attentionfinal(Page): form_model = 'player' @staticmethod def is_displayed(player: Player): return (player.round_number == 1)&((player.in_round(1).math != '50') | (player.in_round(1).atten != '14')) def js_vars(player): return dict( attentionlink= player.subsession.session.config['attentionlink'] ) class Welcome(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 class Introduction1(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 def vars_for_template(player): participant = player.participant treatment = participant.treat_gp return dict( treatment=treatment ) class Introduction2(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 class Introduction3(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 class Treatment1(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 class Quiz(Page): form_model = 'player' form_fields = ['quiz'] @staticmethod def is_displayed(player: Player): return player.round_number == 1 def error_message(player, value): if value['quiz'] != 79: return 'The answer is not correct. Please think about it again.' class Main(Page): form_model = 'player' form_fields = ['prob', 'check_slider_main'] if C.TIMEOUT == 'TRUE': timeout_seconds = C.TIMEOUT_S @staticmethod def prob_error_message(player, value): if values['prob'] < 0 | values['prob'] > 100: return 'Number must be between 0 and 100.' @staticmethod def vars_for_template(player): import pathlib import math participant = player.participant block_paid = math.ceil(player.round_number /C.BREAK_ROUND) - 1 # -1 because to match python numbering #if player.round_number <= C.NUM_CLS_ROUNDS: treatment = participant.treat_gp image = player.participant.random_pic_num[player.round_number - 1] image_path='pilot_2/figure{}.png'.format(image) rows = read_csv('pilot2/machine_results/truck_car.csv', Machine) round_number = player.round_number a = player.round_number dict1 = dict( # image = player.random_pict[player.round_number - 1] #image_path='../sample/figure/figure{}.png'.format(image) image_path=image_path, #image_path = pathlib.Path().resolve() round_number=round_number, a = a, treatment=treatment ) dict2 = rows[image] dict2 = dict(machine_prob = int(dict2["machine_prob"])) def Merge(dict1, dict2): #res = dict1 | dict2 #only for Python 3.9 or more res = {**dict1,**dict2} return res dict3 = Merge(dict1, dict2) return dict3 @staticmethod def before_next_page(player: Player, timeout_happened): if timeout_happened: player.prob = -0.0000000001 if player.round_number == C.NUM_ROUNDS: set_payoffs(player) class Start(Page): @staticmethod def is_displayed(player: Player): return player.round_number % C.NUM_ROUNDS == 1 def vars_for_template(player): participant = player.participant treatment = participant.treat_gp prior = int(participant.prior*100) prior_other = 100 - prior round_number = player.round_number if player.round_number <= C.NUM_CLS_ROUNDS: part = 1 else: part = 2 return dict( part=part, prior=prior, prior_other=prior_other, treatment = treatment, round_number = round_number) class Prior(Page): @staticmethod def is_displayed(player: Player): return player.round_number % C.NUM_CLS_ROUNDS == 1 def vars_for_template(player): participant = player.participant prior = int(participant.prior*100) prior_other = 100 - prior if player.round_number <= C.NUM_CLS_ROUNDS: part = 1 else : part = 2 return dict( part=part, prior=prior, prior_other = prior_other) class Practice1(Page): form_model = 'player' form_fields = ['prac1', 'check_slider_one'] if C.TIMEOUT == 'TRUE': timeout_seconds = C.TIMEOUT_S @staticmethod def is_displayed(player: Player): return player.round_number % C.NUM_ROUNDS == 1 def vars_for_template(player): participant = player.participant treatment = participant.treat_gp round_number = player.round_number import pathlib part = 1 image_path='pilot/truck/truck73.png' rows = read_csv('pilot/machine_results/truck.csv', Machine) dict1 = dict( part=part, image_path=image_path, treatment=treatment, round_number = round_number) dict2 = rows[73] dict2 = dict(machine_prob = int(dict2["machine_prob"])) def Merge(dict1, dict2): res = {**dict1,**dict2} #res = dict1 | dict2 return res dict3 = Merge(dict1, dict2) return dict3 class Practice2(Page): form_model = 'player' form_fields = ['prac2', 'check_slider_two'] if C.TIMEOUT == 'TRUE': timeout_seconds = C.TIMEOUT_S @staticmethod def is_displayed(player: Player): return player.round_number % C.NUM_ROUNDS == 1 def vars_for_template(player): participant = player.participant treatment = participant.treat_gp round_number = player.round_number import pathlib part = 1 image_path='pilot/car/car73.png' rows = read_csv('pilot/machine_results/car.csv', Machine) dict1 = dict( part=part, image_path=image_path, treatment=treatment, round_number=round_number) dict2 = rows[73] dict2 = dict(machine_prob = int(dict2["machine_prob"])) def Merge(dict1, dict2): #res = dict1 | dict2 res = {**dict1,**dict2} return res dict3 = Merge(dict1, dict2) return dict3 class Practice3(Page): form_model = 'player' form_fields = ['prac3', 'check_slider_three'] if C.TIMEOUT == 'TRUE': timeout_seconds = C.TIMEOUT_S @staticmethod def is_displayed(player: Player): return player.round_number % C.NUM_ROUNDS == 1 def vars_for_template(player): participant = player.participant treatment = participant.treat_gp round_number = player.round_number import pathlib part = 1 image_path='pilot/truck/truck74.png' rows = read_csv('pilot/machine_results/truck.csv', Machine) dict1 = dict( part=part, image_path=image_path, treatment=treatment, round_number=round_number) dict2 = rows[74] dict2 = dict(machine_prob = int(dict2["machine_prob"])) def Merge(dict1, dict2): #res = dict1 | dict2 res = {**dict1,**dict2} return res dict3 = Merge(dict1, dict2) return dict3 class Practice4(Page): form_model = 'player' form_fields = ['prac4', 'check_slider_four'] if C.TIMEOUT == 'TRUE': timeout_seconds = C.TIMEOUT_S @staticmethod def is_displayed(player: Player): return player.round_number % C.NUM_ROUNDS == 1 def vars_for_template(player): participant = player.participant treatment = participant.treat_gp round_number = player.round_number import pathlib part = 1 image_path='pilot/car/car74.png' rows = read_csv('pilot/machine_results/car.csv', Machine) dict1 = dict( part=part, image_path=image_path, treatment=treatment, round_number=round_number) dict2 = rows[74] dict2 = dict(machine_prob = int(dict2["machine_prob"])) def Merge(dict1, dict2): #res = dict1 | dict2 res = {**dict1,**dict2} return res dict3 = Merge(dict1, dict2) return dict3 class Middle(Page): @staticmethod def is_displayed(player: Player): return player.round_number % C.NUM_ROUNDS == 1 def vars_for_template(player): participant = player.participant if player.round_number <= C.NUM_CLS_ROUNDS: part = 1 # bonus = participant.bonus_list1[0] else : part = 2 # bonus = participant.bonus_list2[0] return dict( part=part) class Break(Page): @staticmethod def is_displayed(player: Player): return (player.round_number % C.NUM_CLS_ROUNDS != 1) & (player.round_number %C.BREAK_ROUND == 0) def vars_for_template(player): a = player.round_number return dict( a=a) class End(Page): @staticmethod def is_displayed(player: Player): return player.round_number % C.NUM_ROUNDS == 0 class Survey(Page): form_model = 'player' form_fields = ['fatigue', 'instruction', 'ever_work_pict', 'other_comment'] def is_displayed(player: Player): return player.round_number == C.NUM_ROUNDS class Result(Page): def is_displayed(player: Player): return player.round_number == C.NUM_ROUNDS @staticmethod def live_method(player: Player, data): if data == 'clicked-button': player.button_clicks += 1 def vars_for_template(player: Player): import math participant = player.participant player_input = player.in_round(participant.paying_rounds[0]+1).prob player_input_other = 100 - player_input round_paid = participant.paying_rounds[0]+1 round_num = participant.paying_rounds[0]+1 part_num = math.ceil((participant.paying_rounds[0]+1)/C.NUM_CLS_ROUNDS) image = participant.random_pic_num[round_paid-1] image_path='pilot_2/figure{}.png'.format(image) if image <= 50: answer = "a truck" else: answer = "a car" return dict(set_payoff = sum(participant.payoff_list), prob = round(participant.bsq_prob_list[0]*100,2), player_input=player_input, player_input_other=player_input_other, total=sum(participant.payoff_list) + C.PARTICIPATION_FEE, round_num=round_num, part_num = part_num, image_path=image_path, answer=answer ) class Timerprac(Page): def is_displayed(player: Player): if player.round_number == C.NUM_ROUNDS: participant = player.participant player_input = player.in_round(participant.paying_rounds[0]+1).prob return (player.round_number == C.NUM_ROUNDS) & (player_input >= 0) else: return player.round_number == C.NUM_ROUNDS class Timer(Page): form_model = 'player' form_fields = ['timer'] def is_displayed(player: Player): if player.round_number == C.NUM_ROUNDS: participant = player.participant player_input = player.in_round(participant.paying_rounds[0]+1).prob return (player.round_number == C.NUM_ROUNDS) & (player_input >= 0) else: return player.round_number == C.NUM_ROUNDS @staticmethod def before_next_page(player: Player, timeout_happened): import math last2digit = player.timer%100 participant = player.participant prob = round(participant.bsq_prob_list[0]*100,2) round_paid = participant.paying_rounds[0]+1 block_paid = math.ceil(round_paid/C.BREAK_ROUND) - 1 #amount_paid = participant.bonus_list[block_paid] if last2digit < prob: player.payoff = 5 else: player.payoff = 0 #def before_next_page(player: Player, timeout_happened): # last2digit = player.timer%100 # participant = player.participant # prob = round(participant.bsq_prob_list[0]*100,2) # if last2digit < prob: # player.payoff == C.PAYOFF + C.PARTICIPATION_FEE # else: # player.payoff == C.PARTICIPATION_FEE class Final(Page): def is_displayed(player: Player): if player.round_number == C.NUM_ROUNDS: participant = player.participant player_input = player.in_round(participant.paying_rounds[0]+1).prob return (player.round_number == C.NUM_ROUNDS) & (player_input >= 0) else: return player.round_number == C.NUM_ROUNDS def vars_for_template(player: Player): last2digit = player.timer%100 participant = player.participant prob = round(participant.bsq_prob_list[0]*100,2) total = participant.payoff return dict( last2digit=last2digit, prob = prob, total = total) class Thankyou(Page): def is_displayed(player: Player): return player.round_number == C.NUM_ROUNDS @staticmethod def js_vars(player): return dict( completionlink= player.subsession.session.config['completionlink'] ) page_sequence = [Consent, Consentout, Consentfinal, Attention, Attentionout, Attentionfinal, Welcome, Introduction1, Introduction3, Start, Treatment1, Quiz, Practice1, Practice2, Practice3, Practice4, Middle, Main, Break, End, Survey, Result, Timerprac, Timer, Final, Thankyou]