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 ########### ALL INSTRUCTION PAGES class A_Intro_Types(Page): def is_displayed(self): return self.round_number == 1 # only display in the first round def vars_for_template(self): loc = ("Part2_WTP_DOSE/data/B_Intro_Types.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, confirm = confirm, ) class B_Intro_Title_Part2(Page): form_model = 'player' form_fields = ['tab_intro_title_part2', 'focus_intro_title_part2', 'totaltime_intro_title_part2'] def is_displayed(self): return self.round_number == 1 # only display in the first round def vars_for_template(self): loc = ("Part2_WTP_DOSE/data/A_Intro_Title_Part2.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.format(currency=Constants.currency,FixedPointsType1=Constants.FixedPointsType1), confirm=confirm ) def before_next_page(self): # load lottery-data from part 1 # lottery A self.player.optionA_low = self.participant.vars['OptionA'][0] self.player.optionA_high = self.participant.vars['OptionA'][1] # lottery B self.player.optionB_high = self.participant.vars['OptionB'][0] self.player.optionB_low = self.participant.vars['OptionB'][1] self.player.Payoff_Participant_1_selected_round=self.participant.vars['Payoff_Participant_1_selected_round'][0] self.player.Payoff_Participant_2_selected_round=self.participant.vars['Payoff_Participant_2_selected_round'][0] class C_Intro_General(Page): form_model = 'player' form_fields = ['tab_general_part2', 'focus_general_part2', 'totaltime_general_part2'] def is_displayed(self): return self.round_number == 1 # only display in the first round def vars_for_template(self): loc = ("Part2_WTP_DOSE/data/C_Intro_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) 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, paragraph2 = paragraph2, paragraph3 = paragraph3, paragraph4=paragraph4, paragraph5=paragraph5, confirm = confirm, ) class D_Description_Lotteries(Page): form_model = 'player' form_fields = ['tab_desc_options_part2', 'focus_desc_options_part2', 'totaltime_desc_options_part2'] def is_displayed(self): return self.round_number == 1 # only display in the first round def vars_for_template(self): q = self.player.in_round(1) return dict( optionA_high=q.optionA_high, optionA_low=q.optionA_low, optionB_high=q.optionB_high, optionB_low=q.optionB_low, currency=Constants.currency, ) class E_Description_Choice(Page): form_model = 'player' form_fields = ['ControlQuestion1', 'ControlQuestion2', 'ControlQuestion3', 'ControlQuestion4', 'counterWrong', 'tab_desc_choice_part2', 'focus_desc_choice_part2', 'totaltime_desc_choice_part2'] def is_displayed(self): return self.round_number == 1 # only display in the first round def vars_for_template(self): loc = ("Part2_WTP_DOSE/data/E_Description_Choice.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) table_title = sheet.cell_value(3, Constants.language_code) table11 = sheet.cell_value(4, Constants.language_code) table21 = sheet.cell_value(5, Constants.language_code) table122 = sheet.cell_value(6, Constants.language_code) table13 = sheet.cell_value(7, Constants.language_code) table23 = sheet.cell_value(8, Constants.language_code) table14 = sheet.cell_value(9, Constants.language_code) table24 = sheet.cell_value(10, Constants.language_code) table15 = sheet.cell_value(11, Constants.language_code) table25 = sheet.cell_value(12, Constants.language_code) table16 = sheet.cell_value(13, Constants.language_code) table26 = sheet.cell_value(14, Constants.language_code) paragraph2 = sheet.cell_value(15, Constants.language_code) confirm = sheet.cell_value(16, Constants.language_code) return dict( title = title, paragraph1 = paragraph1.format(num_rounds = Constants.num_rounds), paragraph2 = paragraph2.format(num_rounds = Constants.num_rounds), table_title = table_title, table11 = table11, table21 = table21, table122 = table122, table13 = table13, table23 = table23, table14 = table14, table24 = table24, table15 = table15, table25 = table25, table16 = table16, table26 = table26, confirm = confirm, ) class F_Description_RLIM(Page): form_model = 'player' form_fields = ['tab_desc_rlim_part2', 'focus_desc_rlim_part2', 'totaltime_desc_rlim_part2'] def is_displayed(self): return self.round_number == 1 # only display in the first round class F_Description_PRE_RLIM(Page): form_model = 'player' def is_displayed(self): return self.round_number == 1 # only display in the first round class G_Xclusion(Page): def is_displayed(self): return self.player.counterWrong==3 class preQuestion(Page): form_model = 'player' form_fields = ['tab_pre_question_part2', 'focus_pre_question_part2', 'totaltime_pre_question_part2'] def is_displayed(self): return self.round_number == 1 # only display in the first round ############### END OF INSTRUCTION PAGES ################################################## # DOSE PROCEDURE class H_Question(Page): form_model = 'player' form_fields = ['pricechoice','tab_question_part2', 'focus_question_part2', 'totaltime_question_part2'] def before_next_page(self): self.player.in_round(1).WTP = 0 current_round = self.round_number if(self.round_number==Constants.num_rounds): # get pricechoice from last round ### set WTP 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.pricechoice==0): #if 'paying a price' was chosen move down by one cell #self.player.WTP = float(Constants.sheet.cell_value(self.player.in_round(current_round).tree_row+1, current_round+1)) *10 # multiply with 10 (linear transformation of payoffs) self.player.WTP = float(Constants.sheet.cell_value(self.player.in_round(current_round).tree_row+1, current_round+1)) * 200 # multiply with 200 (linear transformation in points) self.player.choice_consistency = float(Constants.sheet.cell_value(self.player.in_round(current_round).tree_row+1, current_round+2)) elif(self.player.pricechoice==1): #if 'not paying a price' was chosen stay on same line #self.player.WTP = float(Constants.sheet.cell_value(self.player.in_round(current_round).tree_row, current_round + 1)) *10 # multiply with 10 (linear transformation of payoffs) self.player.WTP = float(Constants.sheet.cell_value(self.player.in_round(current_round).tree_row, current_round + 1)) * 200 # multiply with 200 (linear transformation of payoffs in points!!!!) self.player.choice_consistency = float(Constants.sheet.cell_value(self.player.in_round(current_round).tree_row, current_round + 2)) ### select one chosen decision situation at random from the 10 choices # select payoff-relevant round self.player.payoffrelevant_round_part2 = random.choice(range(1,Constants.num_rounds+1)) payoffrelevant_round_part2 = self.player.payoffrelevant_round_part2 # determine if 'pay' or 'not pay' was chosen in the payoff-relevant round choice = self.player.in_round(payoffrelevant_round_part2).pricechoice def vars_for_template(self): # store current round current_round=self.round_number # The following is just a technical procedure: # We transfer the lottery information from one round to another. We do this assignment just that we don't have # to always remember that the lottery info is just saved in the lottery variables of the first round! if (current_round > 1): q = self.player q.optionA_high = q.in_round(current_round - 1).optionA_high q.optionA_low = q.in_round(current_round - 1).optionA_low q.optionB_high = q.in_round(current_round - 1).optionB_high q.optionB_low = q.in_round(current_round - 1).optionB_low ########### GET CURRENT OPTIMAL PRICE ############# ### PROCEDURE: ### 1. Get the row of the next optimal price by looking at the previous answer: ###### if 'pay & decide' was chosen in the previous round, move down the decision tree (i.e. row number will increase) ###### if 'not pay & delegate' was chosen in the previous round, move up the decision tree (i.e. row number will decrease) ###### Special Case in 1st round: No answer is necessary. Row is for everyone the same and given by our prior ###### 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 price by storing the value in the row of the decision tree ###### Read cell value (excel sheet:"decisiontree_WTP.xsls") of the optimal row to get optimal price ###### Note: the sheet is read in models.py in the Constants-class ###### Special Case in 1st round: optimal price is written on row number 512 in the 1st column of the excel sheet ###### VARIABLE NAME: player.optimal_price (corresponds to the index of the price matrix) ### 3. set current_price to the price corresponding to the optimal price ### NOTE: This procedure may seems a little bit cumbersome. This is because it is implemented the same way as in part 1 for the lotteries. ###### NOTE: because of the specific CSV-file of the prices, 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_price_row = self.player.tree_row # set player.optimal_price self.player.optimal_price_index=int(Constants.sheet.cell_value(optimal_price_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).pricechoice == 0): # if price 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).pricechoice == 1): # if price was not 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_price_row = self.player.tree_row self.player.optimal_price_index = int(Constants.sheet.cell_value(optimal_price_row, current_round)) optimal_price= self.player.optimal_price_index - 1 #subtract one, because we read the price from the CSV-file, which starts at row 0 #### Determine the price about to be asked self.player.current_price = float(Constants.questions[optimal_price]['price']) return dict( current_round=current_round, bonus = -self.player.current_price # inverse price (used for display purposes if price < 0) ) class I_preDecision(Page): form_model = 'player' form_fields = ['tab_pre_decision_part2', 'focus_pre_decision_part2', 'totaltime_pre_decision_part2'] def is_displayed(self): return self.round_number == Constants.num_rounds # only display in the last round class Results(Page): def is_displayed(self): return self.player.treatment == 3 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, ) class J_Decision_Self(Page): form_model = 'player' form_fields = ['optionchoice_auto'] def is_displayed(self): display = False if self.round_number == Constants.num_rounds: # only display in the last round and if autonomy was selected if(self.player.in_round(self.player.payoffrelevant_round_part2).pricechoice == 0): self.player.delegation = False display = True return display def vars_for_template(self): choice = self.player.in_round(self.player.payoffrelevant_round_part2).pricechoice price = self.player.in_round(self.player.payoffrelevant_round_part2).current_price return dict( choice = choice, price = price, bonus = -price, ) class K_Decision_Delegation(Page): form_model = 'player' form_fields = ['tab_delegation_part2', 'focus_delegation_part2', 'totaltime_delegation_part2'] def is_displayed(self): display = False if self.round_number == Constants.num_rounds: # only display in the last round and if delegation was selected if(self.player.in_round(self.player.payoffrelevant_round_part2).pricechoice == 1): display = True self.player.delegation = True return display def before_next_page(self): with open("Part2_WTP_DOSE\lotteries.csv", 'a', newline='') as file: # open CSV-File; change file name here writer = csv.writer(file) # Order of rows in delegation CSV-file # Ahigh, Alow, pAlow, Bhigh, Blow, pBlow, UserID, Decision userID = random.randint(1,100) writer.writerow([self.player.optionA_high, self.player.optionA_low, self.player.optionB_high, self.player.optionB_low, userID]) class L_Payoff_Screen(Page): def is_displayed(self): return self.round_number == Constants.num_rounds # only display in the last round ##################################################################################### def vars_for_template(self): externalDM = "none" self.player.Payoff_Participant_1_selected_round=self.participant.vars['Payoff_Participant_1_selected_round'][0] self.player.Payoff_Participant_2_selected_round=self.participant.vars['Payoff_Participant_2_selected_round'][0] self.player.payoffrelevant_round_part1=self.participant.vars['payoffrelevant_round_part1'] self.player.optionchoice_part1 = self.participant.vars['optionchoice_part1'] price = self.player.in_round(self.player.payoffrelevant_round_part2).current_price pricechoice = self.player.in_round(self.player.payoffrelevant_round_part2).pricechoice auto = self.player.in_round(self.player.payoffrelevant_round_part2).pricechoice price_round_selected=self.player.in_round(self.player.payoffrelevant_round_part2).current_price if auto == 0: # if lottery choice was not delegated in payoff-relevant round if self.player.optionchoice_auto == 0: # if lottery C was chosen self.player.Payoff_Participant_3_selected_round=self.player.in_round(self.player.payoffrelevant_round_part2).optionA_high self.player.Payoff_Participant_4_selected_round=self.player.in_round(self.player.payoffrelevant_round_part2).optionA_low self.player.payoff_part2 = Constants.FixedPointsType1 - self.player.in_round(self.player.payoffrelevant_round_part2).current_price self.player.payoff=Constants.FixedPointsType1 + self.player.payoff_part2 + 1.5 * Constants.XR + Constants.showup_fee * Constants.XR ##self.player.payoff_plus_participation_fee = Constants.FixedPointsType1 / Constants.XR + self.player.payoff_part2 / Constants.XR + 1.5 + Constants.showup_fee else: # if lottery D was chosen self.player.Payoff_Participant_3_selected_round=self.player.in_round(self.player.payoffrelevant_round_part2).optionB_high self.player.Payoff_Participant_4_selected_round=self.player.in_round(self.player.payoffrelevant_round_part2).optionB_low self.player.payoff_part2 = Constants.FixedPointsType1 - self.player.in_round(self.player.payoffrelevant_round_part2).current_price self.player.payoff = Constants.FixedPointsType1 + self.player.payoff_part2 + 1.5 * Constants.XR + Constants.showup_fee * Constants.XR ##self.player.payoff_plus_participation_fee = Constants.FixedPointsType1 / Constants.XR + self.player.payoff_part2 / Constants.XR + 1.5 + Constants.showup_fee ##### calculate final payoff in ECU and real currency (only in case of non-delegation) else: self.player.payoff_part2 = Constants.FixedPointsType1 self.player.payoff = Constants.FixedPointsType1 + self.player.payoff_part2 + 1.5 * Constants.XR + Constants.showup_fee * Constants.XR ##self.player.payoff_plus_participation_fee = Constants.FixedPointsType1 / Constants.XR + self.player.payoff_part2 / Constants.XR + 1.5 + Constants.showup_fee ######### NEW CASE: Type 2 (decision maker) makes decisions à la strategy method beforehand. Thus we know ################### the delegated decisions for each lottery. Decision file: decisions_type2.csv # read & store all SM decisions with open('Part2_WTP_DOSE/decisions_type2.csv') as decisions_file: decisions = list(csv.DictReader(decisions_file)) rowDecision = 20 # default to catch errors (instead of a try condition) for i in range(0, len(decisions)): if float(decisions[i]['Blow']) == round(self.player.optionB_low,0): rowDecision = i if decisions[rowDecision]['Decision'] == 'A': self.player.Payoff_Participant_3_selected_round = 2000 self.player.Payoff_Participant_4_selected_round = 2000 elif decisions[rowDecision]['Decision'] == 'B': self.player.Payoff_Participant_3_selected_round = 3000 self.player.Payoff_Participant_4_selected_round = self.player.optionB_low ############################################## return dict( price = price, bonus = -price_round_selected, pricechoice=pricechoice, price_round_selected=price_round_selected ) ################## PROLIFIC PILOT ONLY (REDUCED SURVEY + FEEDBACK ###################################################### class Z_prolificPart3(Page): form_model = 'player' def is_displayed(self): return self.round_number == Constants.num_rounds # only display in the last round class Z_Demographics(Page): form_model = 'player' def is_displayed(self): return self.round_number == Constants.num_rounds # only display in the last round form_fields = ['income','maritalstatus','employmentstatus','education','religious','age','gender'] class Z_prolificQ1(Page): form_model = 'player' form_fields = ['politics1','politics2','politics3','politics4','politics5','politics6','politics7','politics8','politics9','politics10','trust','luck'] def is_displayed(self): return self.round_number == Constants.num_rounds # only display in the last round class Z_prolificQ2(Page): form_model = 'player' form_fields = ['GSE1','GSE2','GSE3','GSE4','GSE5','GSE6','GSE7','GSE8','GSE9','GSE10', 'attentioncheck1'] def is_displayed(self): return self.round_number == Constants.num_rounds # only display in the last round class Z_prolificQ3(Page): form_model = 'player' form_fields = ['bigfive1','bigfive2','bigfive3','bigfive4','bigfive5'] def is_displayed(self): return self.round_number == Constants.num_rounds # only display in the last round class Z_prolificQ4(Page): form_model = 'player' form_fields = ['CultureOrientation1','CultureOrientation2','CultureOrientation3','CultureOrientation4','CultureOrientation5','CultureOrientation6','CultureOrientation7','CultureOrientation8'] def is_displayed(self): return self.round_number == Constants.num_rounds # only display in the last round class Z_prolificQ5(Page): form_model = 'player' form_fields = ['CultureOrientation9','CultureOrientation10','CultureOrientation11','CultureOrientation12','CultureOrientation13','CultureOrientation14','CultureOrientation15','CultureOrientation16'] def is_displayed(self): return self.round_number == Constants.num_rounds # only display in the last round$ class Z_prolificQ6(Page): form_model = 'player' form_fields = ['SocialDominance1','SocialDominance2','SocialDominance3','SocialDominance4','SocialDominance5','SocialDominance6','SocialDominance7','SocialDominance8'] def is_displayed(self): return self.round_number == Constants.num_rounds # only display in the last round class Z_prolificQ7(Page): form_model = 'player' form_fields = ['Control1','Control2','Control3','Control4','Control5','Control6','Control7','Control8'] def is_displayed(self): return self.round_number == Constants.num_rounds # only display in the last round class Z_prolificQ8(Page): form_model = 'player' form_fields = ['Control9','Control10','Control11','Control12','Control13','Control14','Control15','Control16','Control17','Control18'] def is_displayed(self): return self.round_number == Constants.num_rounds # only display in the last round class END(Page): def is_displayed(self): return self.round_number == Constants.num_rounds # only display in the last round ##################################################################################### def vars_for_template(self): externalDM = "none" self.player.Payoff_Participant_1_selected_round=self.participant.vars['Payoff_Participant_1_selected_round'][0] self.player.Payoff_Participant_2_selected_round=self.participant.vars['Payoff_Participant_2_selected_round'][0] auto = self.player.in_round(self.player.payoffrelevant_round_part2).pricechoice if auto == 0: # if lottery choice was not delegated in payoff-relevant round if self.player.optionchoice_auto == 0: # if lottery C was chosen self.player.Payoff_Participant_3_selected_round=self.player.in_round(self.player.payoffrelevant_round_part2).optionA_high self.player.Payoff_Participant_4_selected_round=self.player.in_round(self.player.payoffrelevant_round_part2).optionA_low self.player.payoff_part2 = Constants.FixedPointsType1 - self.player.in_round(self.player.payoffrelevant_round_part2).current_price self.player.payoff = Constants.FixedPointsType1 + self.player.payoff_part2 + 1.5 * Constants.XR + Constants.showup_fee * Constants.XR ##self.player.payoff_plus_participation_fee = Constants.FixedPointsType1 / Constants.XR + self.player.payoff_part2 / Constants.XR + 1.5 + Constants.showup_fee+1 else: # if lottery D was chosen self.player.Payoff_Participant_3_selected_round=self.player.in_round(self.player.payoffrelevant_round_part2).optionB_high self.player.Payoff_Participant_4_selected_round=self.player.in_round(self.player.payoffrelevant_round_part2).optionB_low self.player.payoff_part2 = Constants.FixedPointsType1 - self.player.in_round(self.player.payoffrelevant_round_part2).current_price self.player.payoff = Constants.FixedPointsType1 + self.player.payoff_part2 + 1.5 * Constants.XR + Constants.showup_fee * Constants.XR ##self.player.payoff_plus_participation_fee = Constants.FixedPointsType1 / Constants.XR + self.player.payoff_part2 / Constants.XR + 1.5 + Constants.showup_fee+1 ##### calculate final payoff in ECU and real currency (only in case of non-delegation) else: self.player.payoff_part2 = Constants.FixedPointsType1 self.player.payoff = Constants.FixedPointsType1 + self.player.payoff_part2 + 1.5 * Constants.XR + Constants.showup_fee * Constants.XR ##self.player.payoff_plus_participation_fee= Constants.FixedPointsType1 / Constants.XR + self.player.payoff_part2 / Constants.XR + 1.5 + Constants.showup_fee+1 ######### NEW CASE: Type 2 (decision maker) makes decisions à la strategy method beforehand. Thus we know ################### the delegated decisions for each lottery. Decision file: decisions_type2.csv # read & store all SM decisions with open('Part2_WTP_DOSE/decisions_type2.csv') as decisions_file: decisions = list(csv.DictReader(decisions_file)) rowDecision = 20 # default to catch errors (instead of a try condition) for i in range(0, len(decisions)): if float(decisions[i]['Blow']) == round(self.player.optionB_low,0): rowDecision = i if decisions[rowDecision]['Decision'] == 'A': self.player.Payoff_Participant_3_selected_round = 2000 self.player.Payoff_Participant_4_selected_round = 2000 elif decisions[rowDecision]['Decision'] == 'B': self.player.Payoff_Participant_3_selected_round = 3000 self.player.Payoff_Participant_4_selected_round = self.player.optionB_low ############################################## ######################################################################################################################## page_sequence = [ A_Intro_Types, B_Intro_Title_Part2, C_Intro_General, D_Description_Lotteries, F_Description_PRE_RLIM, E_Description_Choice, G_Xclusion, F_Description_RLIM, H_Question, I_preDecision, J_Decision_Self, K_Decision_Delegation, Z_prolificPart3, Z_Demographics, Z_prolificQ1, Z_prolificQ2, Z_prolificQ3, Z_prolificQ4, Z_prolificQ5, Z_prolificQ6, Z_prolificQ7, Z_prolificQ8, L_Payoff_Screen, ]