from otree.api import Currency as c, currency_range from ._builtin import Page, WaitPage from .models import Constants import csv import random import time import xlrd # for reading XLS-files import math # import xlsxwriter module import xlsxwriter from random import choice ########### ALL INSTRUCTION PAGES class A_Consent_Form(Page): form_model = 'player' form_fields = ['tab_consent','focus_consent','totaltime_consent'] def is_displayed(self): return self.round_number == 1 # only display in the first round def vars_for_template(self): loc = ("Prescreen/data/A_ConsentForm.xlsx") # To open Workbook wb = xlrd.open_workbook(loc) sheet = wb.sheet_by_index(0) # Get all titles & paragraphs title = sheet.cell_value(1, Constants.language_code) warning = sheet.cell_value(2, Constants.language_code) subtitle1 = sheet.cell_value(3, Constants.language_code) paragraph1 = sheet.cell_value(4, Constants.language_code) subtitle2 = sheet.cell_value(5, Constants.language_code) paragraph2 = sheet.cell_value(6, Constants.language_code) subtitle3 = sheet.cell_value(7, Constants.language_code) paragraph3 = sheet.cell_value(8, Constants.language_code) subtitle4 = sheet.cell_value(9, Constants.language_code) paragraph4 = sheet.cell_value(10, Constants.language_code) subtitle5 = sheet.cell_value(11, Constants.language_code) paragraph5 = sheet.cell_value(12, Constants.language_code) subtitle6 = sheet.cell_value(13, Constants.language_code) paragraph6 = sheet.cell_value(14, Constants.language_code) subtitle7 = sheet.cell_value(15, Constants.language_code) paragraph7 = sheet.cell_value(16, Constants.language_code) consent = sheet.cell_value(17, Constants.language_code) confirm = sheet.cell_value(18, Constants.language_code) return dict( title=title, warning=warning, subtitle1=subtitle1, subtitle2=subtitle2, subtitle3=subtitle3, subtitle4=subtitle4, subtitle5=subtitle5, subtitle6=subtitle6, subtitle7=subtitle7, paragraph1=paragraph1, paragraph2=paragraph2, paragraph3=paragraph3, paragraph4=paragraph4.format(showup_fee = "{:.0f}".format(Constants.showup_fee * Constants.XR), currency_real = Constants.currency_real), # showup_fee is only a placeholder in the excel-string: set value here paragraph5=paragraph5, paragraph6=paragraph6, paragraph7=paragraph7, consent=consent, confirm=confirm ) class GroupQuestions(Page): form_model = 'player' form_fields = ['affiliation','picture','age', 'allstates','gender'] def is_displayed(self): return self.round_number == 1 # only display in the first round def before_next_page(self): if self.player.affiliation == 1: self.subsession.democrat_count += 1 elif self.player.affiliation == 2: self.subsession.republican_count += 1 if self.player.gender == 1: self.subsession.male_count += 1 elif self.player.affiliation == 2: self.subsession.female_count += 1 class Independent(Page): form_model = 'player' form_fields = ['independent'] def is_displayed(self): return self.player.field_maybe_none('affiliation') == 3 and self.round_number == 1 def before_next_page(self): if self.player.independent == '1': self.subsession.democrat_count += 1 elif self.player.independent == '3': self.subsession.republican_count += 1 class FollowUpDemo(Page): form_model = 'player' form_fields = ['strength'] def is_displayed(self): return self.player.field_maybe_none('affiliation') == 1 and self.round_number == 1 class FollowUpRepu(Page): form_model = 'player' form_fields = ['strength'] def is_displayed(self): return self.player.field_maybe_none('affiliation') == 2 and self.round_number == 1 class XClusion3(Page): def is_displayed(self): if self.round_number == 1: male_reached = self.subsession.male_count >= 1 female_reached = self.subsession.female_count >= 8000 is_male = self.player.gender == 1 is_female = self.player.gender == 2 is_other = self.player.gender == 3 return (is_other) or ( male_reached and is_male) or (female_reached and is_female) else: return False class AffiliationCounterPage(Page): form_model = 'player' form_fields = ['democrat_counter','republican_counter'] def after_all_players_arrive(self): self.subsession.democrat_count = sum([p.affiliation == 1 for p in self.subsession.get_players()]) self.subsession.republican_count = sum([p.affiliation == 2 for p in self.subsession.get_players()]) class B_Instructions_General(Page): form_model = 'player' form_fields = ['tab_instructions_general_part1', 'focus_instructions_general_part1', 'totaltime_instructions_general_part1'] def is_displayed(self): return self.round_number == 1 # only display in the first round def vars_for_template(self): loc = ("Prescreen/data/B_Instructions_General.xlsx") # To open Workbook wb = xlrd.open_workbook(loc) sheet = wb.sheet_by_index(0) # Get all titles & paragraphs title = sheet.cell_value(1, Constants.language_code) paragraph1 = sheet.cell_value(2, Constants.language_code) confirm = sheet.cell_value(3, Constants.language_code) return dict( title=title, paragraph1=paragraph1.format(showup_fee="{:.0f}".format(Constants.showup_fee * Constants.XR),currency=Constants.currency_real), confirm=confirm ) class C_Intro_Title(Page): form_model = 'player' form_fields = ['tab_intro_title_part1', 'focus_intro_title_part1', 'totaltime_intro_title_part1'] def is_displayed(self): return self.round_number == 1 # only display in the first round def vars_for_template(self): loc = ("Prescreen/data/C_Intro_Title.xlsx") # To open Workbook wb = xlrd.open_workbook(loc) sheet = wb.sheet_by_index(0) # Get all titles & paragraphs title = sheet.cell_value(1, Constants.language_code) confirm = sheet.cell_value(2, Constants.language_code) return dict( title=title, confirm=confirm ) class D_Description_Lotteries(Page): form_model = 'player' form_fields = ['ControlQuestion1', 'ControlQuestion2', 'ControlQuestion3', 'ControlQuestion4','counterWrong', 'tab_desc_lotteries_part1', 'focus_desc_lotteries_part1', 'totaltime_desc_lotteries_part1'] def is_displayed(self): return self.round_number == 1 # only display in the first round def vars_for_template(self): loc = ("Prescreen/data/D_Description_Lotteries.xlsx") # To open Workbook wb = xlrd.open_workbook(loc) sheet = wb.sheet_by_index(0) # Get all titles & paragraphs title = sheet.cell_value(1, Constants.language_code) paragraph1 = sheet.cell_value(2, Constants.language_code) paragraph2 = sheet.cell_value(3, Constants.language_code) paragraph3 = sheet.cell_value(4, Constants.language_code) paragraph4 = sheet.cell_value(5, Constants.language_code) paragraph5 = sheet.cell_value(6, Constants.language_code) confirm = sheet.cell_value(7, Constants.language_code) return dict( title=title, paragraph1 = paragraph1,#.format(showup_fee=Constants.showup_fee, currency=Constants.currency), paragraph2 = paragraph2.format(lotteryA_high = Constants.questions[1]['Ahigh'], lotteryA_low = Constants.questions[1]['Alow'], currency = Constants.currency), paragraph3 = paragraph3, paragraph4 = paragraph4, paragraph5 = paragraph5, confirm=confirm, lotteryA_high = Constants.questions[1]['Ahigh'], lotteryA_low = Constants.questions[1]['Alow'], lotteryB_low = Constants.questions[1]['Blow'], ) class E_PracticeLottery(Page): form_model = 'player' form_fields = ['roundsA', 'draws_highA', 'roundsB', 'draws_highB', 'tab_practice_part1', 'focus_practice_part1', 'totaltime_practice_part1'] def is_displayed(self): return self.round_number == 1 # only display in the first round class F_ControlQuestions(Page): form_model = 'player' form_fields = ['ControlQuestion1', 'ControlQuestion2', 'ControlQuestion3', 'ControlQuestion4', 'ControlQuestion5', 'ControlQuestion6', 'counterWrong', 'tab_desc_lotteries_part1', 'focus_desc_lotteries_part1', 'totaltime_desc_lotteries_part1'] def is_displayed(self): return self.round_number == 1 # only display in the first round def vars_for_template(self): loc = ("Prescreen/data/D_Description_Lotteries.xlsx") # To open Workbook wb = xlrd.open_workbook(loc) sheet = wb.sheet_by_index(0) # Get all titles & paragraphs title = sheet.cell_value(1, Constants.language_code) paragraph1 = sheet.cell_value(2, Constants.language_code) paragraph2 = sheet.cell_value(3, Constants.language_code) paragraph3 = sheet.cell_value(4, Constants.language_code) paragraph4 = sheet.cell_value(5, Constants.language_code) paragraph5 = sheet.cell_value(6, Constants.language_code) confirm = sheet.cell_value(7, Constants.language_code) return dict( lotteryA_high=Constants.questions[1]['Ahigh'], lotteryA_low=Constants.questions[1]['Alow'], lotteryB_low=Constants.questions[1]['Blow'], paragraph1=paragraph1, # .format(showup_fee=Constants.showup_fee, currency=Constants.currency), paragraph2=paragraph2.format(lotteryA_high=Constants.questions[1]['Ahigh'], lotteryA_low=Constants.questions[1]['Alow'], currency=Constants.currency), paragraph3=paragraph3, paragraph4=paragraph4, paragraph5=paragraph5, confirm=confirm, ) class F_Description_RLIM(Page): form_model = 'player' form_fields = ['tab_desc_rlim_part1', 'focus_desc_rlim_part1', 'totaltime_desc_rlim_part1'] def is_displayed(self): return self.round_number == 1 # only display in the first round def vars_for_template(self): loc = ("Prescreen/data/D_Description_Lotteries.xlsx") # To open Workbook wb = xlrd.open_workbook(loc) sheet = wb.sheet_by_index(0) # Get all titles & paragraphs paragraph = sheet.cell_value(6, Constants.language_code) confirm = sheet.cell_value(7, Constants.language_code) return dict( paragraph = paragraph, confirm=confirm, lotteryA_high = Constants.questions[1]['Ahigh'], lotteryA_low = Constants.questions[1]['Alow'], lotteryB_low = Constants.questions[1]['Blow'], ) class prolificID(Page): form_model = 'player' form_fields = ['prolificID'] def is_displayed(self): return self.round_number == 1 # only display in the first round def before_next_page(self): self.participant.vars['prolificID'] = self.player.prolificID class Xclusion(Page): def is_displayed(self): return self.player.counterWrong==3 class Xclusion2(Page): def is_displayed(self): return self.player.exclusion == 2 class preQuestion(Page): form_model = 'player' form_fields = ['tab_pre_question_part1', 'focus_pre_question_part1', 'totaltime_pre_question_part1'] def is_displayed(self): return self.round_number == 1 # only display in the first round class AttentionCheck1(Page): form_model = 'player' form_fields = ['exclusion'] def is_displayed(self): return self.round_number == 5 # Display after the completion of the 10 questions ############### END OF INSTRUCTION PAGES ################################################## # DOSE PROCEDURE class Question(Page): form_model = 'player' form_fields = ['lotterychoice', 'tab_question_part1', 'focus_question_part1', 'totaltime_question_part1'] def before_next_page(self): current_round = self.round_number if (self.round_number==Constants.num_rounds): # get lotterychoice from last round # only for testing purposes # if (self.player.in_round(self.round_number).lotterychoice == 0): # if lottery A was chosen in previous round # print("your answer was A in last round", (self.round_number)) # elif (self.player.in_round( # self.round_number).lotterychoice == 1): # if lottery B was chosen in previous round # print("your answer was B in last round", (self.round_number)) #### end of testing ### set risk parameter and choice consistency according to the choices in the 10 previous rounds (i.e. look them up in the decision tree file) if(self.player.lotterychoice==0): #if A was chosen move down by one cell self.player.risk_parameter = float(Constants.sheet.cell_value(self.player.in_round(current_round).tree_row+1, current_round+1)) self.player.choice_consistency = float(Constants.sheet.cell_value(self.player.in_round(current_round).tree_row+1, current_round+2)) elif(self.player.lotterychoice==1): #if B was chosen stay on same line self.player.risk_parameter = float(Constants.sheet.cell_value(self.player.in_round(current_round).tree_row, current_round + 1)) self.player.choice_consistency = float(Constants.sheet.cell_value(self.player.in_round(current_round).tree_row, current_round + 2)) ### select one chosen lottery at random from the 10 choices # select payoff-relevant round self.player.payoffrelevant_round = random.choice(range(1,Constants.num_rounds+1)) payoffrelevant_round = self.player.payoffrelevant_round # determine if A or B was chosen in the payoff-relevant round choice = self.player.in_round(payoffrelevant_round).lotterychoice # determine outcome of payoff-relevant lottery if(choice==0): # A was chosen in payoff-relevant round high = self.player.in_round(payoffrelevant_round).lotteryA_high low = self.player.in_round(payoffrelevant_round).lotteryA_low # determine outcome #### ATTENTION: SLOPPY, INCLUDE PROBABILITIES!!!! self.player.payoff_part1 = random.choice([low,high,high,high]) elif(choice==1): # B was chosen high = self.player.in_round(payoffrelevant_round).lotteryB_high low = self.player.in_round(payoffrelevant_round).lotteryB_low # determine outcome #### ATTENTION: SLOPPY, INCLUDE PROBABILITIES!!!! self.player.payoff_part1 = random.choice([low,high]) ######################### PASSING VARIABLES (i.e. MAKING THEM GLOBALLY AVAILABLE IN 'participant.vars') ##### PASS RISK PREFERENCES ### We here directly calculate the indifference lottery based on the estimated risk-preferences ### We pass lottery A (same for everyone), lottery B (low payoff & probabilities are same for everyone) ### The only payoff that depends on the risk parameter is the high payoff of lottery B ### Find B_high: EU(A | r) = p_low * U(B_low | r) + p_high * U(B_high | r) r = self.player.risk_parameter EUA = self.player.probA_low * (self.player.lotteryA_low**(1-r))/(1-r) + self.player.probA_high * (self.player.lotteryA_high**(1-r))/(1-r) EUB_low = self.player.probB_low * (self.player.lotteryB_low**(1-r))/(1-r) numerator = (EUA - EUB_low)*(1-r) denominator = self.player.probB_high # calculate high payoff of indifference lottery for part 2 B_high_indifferent = (numerator/denominator)**(1/(1-r)) #print(B_high_indifferent) #print(r) # setup lottery arrays to be inter-app accessible (used in part 2) self.participant.vars['lotteryB'] = [self.player.lotteryB_low, B_high_indifferent, self.player.probB_low, self.player.probB_high] self.participant.vars['lotteryA'] = [self.player.lotteryA_low, self.player.lotteryA_high,self.player.probA_low,self.player.probA_high] # setup other information to be inter-app accessible (used in part 2) self.participant.vars['payoffrelevant_round_part1'] = self.player.payoffrelevant_round self.participant.vars['payoff_part1'] = self.player.payoff_part1 self.participant.vars['lotterychoice_part1'] = self.player.in_round(self.player.payoffrelevant_round).lotterychoice self.participant.vars['lotterychoice_part1_high'] = high self.participant.vars['lotterychoice_part1_low'] = low def vars_for_template(self): # store current round current_round=self.round_number ########### GET CURRENT OPTIMAL LOTTERY ############# ### PROCEDURE: ### 1. Get the row of the next optimal lottery by looking at the previous answer: ###### if A was chosen in the previous round, move down the decision tree (i.e. row number will increase) ###### if B was chosen in the previous round, move up the decision tree (i.e. row number will decrease) ###### Special Case in 1st round: No answer has been registered. Row is for everyone the same ###### VARIABLE NAME: player.tree_row ###### (Aug. 2020: We use 10 rounds, first row of binary tree is 512) ### 2. Get the number of the next optimal lottery by storing the value in the row of the decision tree ###### Read cell value (excel sheet:"decisiontree.xsls") of the optimal row to get optimal lottery ###### Note: the sheet is read in models.py in the Constants-class ###### Special Case in 1st round: optimal lottery is the on written on row number 512 in the 1st column of the excel sheet ###### VARIABLE NAME: player.optimal_lottery ### 3. set all lottery variables (Bhigh, Blow, pBlow,...) according to the next optimal lottery ###### NOTE: because of the specific CSV-file of the lotteries, we have to go one row up (i.e. subtract 1 from optimal_lottery-value) # intializing for 1st round. tree_row is set initially in models.py to 512, so we don't need make any changes in the 1st round if(current_round==1): # use the intial value of 512 set in models.py for the 1st round optimal_lottery_row = self.player.tree_row # set player.optimal_lottery self.player.optimal_lottery=int(Constants.sheet.cell_value(optimal_lottery_row,current_round)) elif(current_round>1): # move down (if A was chosen i.e. player.lotterychoice in previous round was 0) in the decision tree matrix # or down (if B was chosen i.e. player.lotterychoice in previous round was 1) in the decision tree matrix if(self.player.in_round(current_round-1).lotterychoice == 0): # if A was chosen in previous round self.player.tree_row = self.player.in_round(current_round-1).tree_row + Constants.shift[current_round - 2] # set row of optimal lottery for identification in the binary tree elif(self.player.in_round(current_round-1).lotterychoice == 1): # if B was chosen in previous round self.player.tree_row = self.player.in_round(current_round-1).tree_row - Constants.shift[current_round - 2] # set row of optimal lottery for identification in the binary tree optimal_lottery_row = self.player.tree_row self.player.optimal_lottery = int(Constants.sheet.cell_value(optimal_lottery_row, current_round)) optimal_lottery=self.player.optimal_lottery-1 #subtract one, because we read the lottery from the CSV-file, which starts at row 0 ##### only for testing purposes #print("lottery_row", optimal_lottery_row) #if(self.round_number>1): #optimal_lottery = int(Constants.sheet.cell_value(Constants.shift[self.round_number])) #if(self.player.in_round(self.round_number-1).lotterychoice == 0): #if lottery A was chosen in previous round #print("your answer was A in round",(self.round_number-1)) #elif(self.player.in_round(self.round_number-1).lotterychoice == 1): #if lottery B was chosen in previous round #print("your answer was B in round", (self.round_number - 1)) ################################# #### Determine the lottery about to be asked self.player.lotteryB_high = float(Constants.questions[optimal_lottery]['Bhigh']) self.player.lotteryB_low = float(Constants.questions[optimal_lottery]['Blow']) self.player.probB_low = float(Constants.questions[optimal_lottery]['pBlow']) self.player.probB_high = 1-self.player.probB_low self.player.lotteryA_high = float(Constants.questions[optimal_lottery]['Ahigh']) self.player.lotteryA_low = float(Constants.questions[optimal_lottery]['Alow']) self.player.probA_low = float(Constants.questions[optimal_lottery]['pAlow']) self.player.probA_high = 1 - self.player.probA_low return dict( current_round=current_round ) class H_End(Page): form_model = 'player' form_fields = ['tab_end_part1', 'focus_end_part1', 'totaltime_end_part1'] def is_displayed(self): return self.round_number == Constants.num_rounds #only display in the last round ##################################################################################### def vars_for_template(self): player_in_all_rounds = self.player.in_all_rounds() return dict( player_in_all_rounds=player_in_all_rounds, #B_high = self.participant.vars['B_high_indifferent'] B_high =0, ) class End(Page): form_model = 'player' page_sequence = [ # A_Consent_Form, prolificID, GroupQuestions, End, # XClusion3, # FollowUpDemo, # FollowUpRepu, # B_Instructions_General, # C_Intro_Title, # D_Description_Lotteries, # Xclusion, # E_PracticeLottery, # F_Description_RLIM, # preQuestion, # Question, # AttentionCheck1, # Xclusion2, H_End, ] #page_sequence = [preQuestion,Question,H_End]