from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range ) from otree.models import subsession from Multiple_Price_List.config import * import random from random import randrange # ---------------------------------------------------------------------------------------------------------------- # # *** CLASS CONSTANTS *** # # ---------------------------------------------------------------------------------------------------------------- # class Constants(BaseConstants): # ------------------------------------------------------------------------------------------------------------ # # --- oTree Settings (Don't Modify) --- # # ------------------------------------------------------------------------------------------------------------ # name_in_url = 'Time_Adaptive_CE' players_per_group = None num_rounds = 36 class Subsession(BaseSubsession): def creating_session(self): for p in self.get_players(): pv = p.participant.vars # ------------------------------------------------------------ # 1) discount_seq 兜底(只有单测 CE 时才会用到) # 正常完整流程:Risk/Welcome 已经写好,就不会走这个兜底 # ------------------------------------------------------------ seq = pv.get('discount_seq') if seq is None: seq = 2 pv['discount_seq'] = seq # ------------------------------------------------------------ # 2) ✅ round 1:强制把 path/order 修正成与 seq 一致 + 清残留 # 这一步是防止:seq=1/2 但 order 残留成 84 或 page 残留成 2 # ------------------------------------------------------------ if p.round_number == 1: pv['path'] = 'Lot1→CE' if seq in (1, 3) else 'CE→Lot2' pv['order'] = '48' if seq in (1, 2) else '84' # 清理跨 app/多次测试残留的强制切段标记 pv.pop('force_8w', None) pv.pop('force_4w', None) # 清理“一次性页面”标记(方便你反复测试,也避免跳页) pv.pop('ins8w_shown', None); pv.pop('quiz8w_shown', None); pv.pop('mid8w_shown', None) pv.pop('ins4w_shown', None); pv.pop('quiz4w_shown', None); pv.pop('mid4w_shown', None) # ✅ 关键:重置 randseq/page,避免沿用上一个 app 的 page=2/3 直接抢跑到 choice 页 pv.pop('randseq', None) pv['page'] = 0 # 先置 0,等下面生成 randseq 再设定 # ------------------------------------------------------------ # 3) 支付相关兜底 # ------------------------------------------------------------ #pv.setdefault('task_to_pay', 0) #pv.setdefault('round_to_pay', random.randint(1, Constants.num_rounds)) # ------------------------------------------------------------ # 4) ✅ 单测兜底:如果没从 Risk 来,就补齐 cert_equi_list2/9/12 # (你 CE 的 set_equivalent/set_choice_options 需要它们) # ------------------------------------------------------------ pv.setdefault('cert_equi_list2', [c(4.1)]) pv.setdefault('cert_equi_list9', [c(4.1)]) pv.setdefault('cert_equi_list12', [c(4.1)]) # ------------------------------------------------------------ # 5) block 开始 node=1 # ------------------------------------------------------------ if p.round_number in (1, 7, 13, 19, 25, 31): pv['node'] = 1 # ------------------------------------------------------------ # 6) round 1 生成 randseq,并设置 page # ------------------------------------------------------------ if p.round_number == 1: randseq = pv.get('randseq') if not randseq: randseq = [3, 2, 1] random.shuffle(randseq) pv['randseq'] = randseq pv['page'] = randseq[0] class Group(BaseGroup): pass class Player(BasePlayer): ### variables for quiz ### q1a = models.CurrencyField( label=' a) Please enter the amount you will earn. ' '(Please use "." to separate Dollar and Cent values)', min=0 ) q1b = models.IntegerField( choices=[[1, 'Today'], [2, 'Tomorrow'], [3, 'In 4 weeks'], ], label=' b) When will you receive your payments from this task?', ) q2 = models.IntegerField( choices=[[1, 'Never'], [2, '3 times - every time a new round starts'], [3, 'For each time (18 times)'], ], label='2. How often does the amount of the Alternative - 4 weeks change during this task?', ) q1a_8w = models.CurrencyField( label=' a) Please enter the amount you will earn. ' '(Please use "." to separate Dollar and Cent values)', min=0 ) q1b_8w = models.IntegerField( choices=[[1, 'Today'], [2, 'Tomorrow'], [3, 'In 8 weeks'], ], label=' b) When will you receive your payments from this task?', ) q2_8w = models.IntegerField( choices=[[1, 'Never'], [2, '3 times - every time a new round starts'], [3, 'For each time (18 times)'], ], label='2. How often does the amount of the Alternative - tomorrow change during this task?', ) incorrect_attempts = models.IntegerField(initial=0) ce = models.StringField() # indicates from which lottery the CE comes from equi = models.CurrencyField() # indicates the CE of the lottery from the risk task early_option = models.CurrencyField() late_option = models.CurrencyField() current_week = models.IntegerField() segment_index = models.IntegerField() def set_option(self): self.early_option = self.participant.vars['early'] self.late_option = self.participant.vars['late'] choice = models.StringField() #indecates the choice. def set_node(self): # ✅ CHANGED: 36轮下,每个block最后一轮都重置 node(6,12,18,24,30,36) if self.round_number in (6, 12, 18, 24, 30, 36): self.participant.vars['node'] = 1 else: last_round_node = self.participant.vars['node'] if self.choice == 'early': self.participant.vars['node'] = last_round_node * 2 else: self.participant.vars['node'] = last_round_node * 2 - 1 def set_page(self): # ✅ CHANGED: 36轮=两个18轮段(每段3个block),重复 randseq[0..2] if self.round_number in (1, 19): self.participant.vars['page'] = self.participant.vars['randseq'][0] elif self.round_number in (7, 25): self.participant.vars['page'] = self.participant.vars['randseq'][1] elif self.round_number in (13, 31): self.participant.vars['page'] = self.participant.vars['randseq'][2] def set_choice_options(self): if self.participant.vars['page'] == 1: ################################################################## ############## CE 1 ############################################# ################################################################## # define choices for early choices self.participant.vars['ce1_early'] = self.participant.vars['cert_equi_list2'] float(self.participant.vars['ce1_early'][0]) # define multiplication factor multfact = 1.05 # define choices for choice 5 late_5 = {} for i in range(1, 17): value = self.participant.vars['ce1_early'][0] * multfact ** (i - 1) late_5['high_%s' % i] = value # define choices for choice 4 late_4 = {} for i in range(1, 9): num1 = 2 * i num2 = num1 - 1 value = (late_5['high_' + str(num1)] + late_5['high_' + str(num2)]) / 2 late_4['high_%s' % i] = value # define choices for choice 3 late_3 = {} for i in range(1, 5): num1 = 2 * i num2 = num1 - 1 value = (late_4['high_' + str(num1)] + late_4['high_' + str(num2)]) / 2 late_3['high_%s' % i] = value # define choices for choice 2 late_2 = {} for i in range(1, 3): num1 = 2 * i num2 = num1 - 1 value = (late_3['high_' + str(num1)] + late_3['high_' + str(num2)]) / 2 late_2['high_%s' % i] = value # define choices for choice 1 late_1 = { 'high_1': (late_2['high_2'] + late_2['high_1']) / 2, } # create options for 6th choice for which later payment is lower than early payment late_6 = { 'high_6': self.participant.vars['ce1_early'][0] - 0.50, } # rounding new_1 = {} for i in late_1: new_1[i] = round(late_1[i] + 0.01, 1) # we have to add this very small number since otherwise the rounding inpython is strange self.participant.vars['ce1_late_1'] = new_1 new_2 = {} for i in late_2: new_2[i] = round(late_2[i] + 0.01, 1) self.participant.vars['ce1_late_2'] = new_2 new_3 = {} for i in late_3: new_3[i] = round(late_3[i] + 0.01, 1) self.participant.vars['ce1_late_3'] = new_3 new_4 = {} for i in late_4: new_4[i] = round(late_4[i] + 0.01, 1) self.participant.vars['ce1_late_4'] = new_4 new_5 = {} for i in late_5: new_5[i] = round(late_5[i] + 0.01, 1) self.participant.vars['ce1_late_5'] = new_5 new_6 = {} for i in late_6: new_6[i] = round(late_6[i] + 0.01, 1) self.participant.vars['ce1_late_6'] = new_6 elif self.participant.vars['page'] == 2: ################################################################## ############## CE 2 ######################################## ################################################################## # define choices for early choices self.participant.vars['ce2_early'] = self.participant.vars['cert_equi_list9'] float(self.participant.vars['ce2_early'][0]) # define multiplication factor multfact = 1.04 # define choices for choice 5 late_5 = {} for i in range(1, 17): value = self.participant.vars['ce2_early'][0] * multfact ** (i - 1) late_5['high_%s' % i] = value # define choices for choice 4 late_4 = {} for i in range(1, 9): num1 = 2 * i num2 = num1 - 1 value = (late_5['high_' + str(num1)] + late_5['high_' + str(num2)]) / 2 late_4['high_%s' % i] = value # define choices for choice 3 late_3 = {} for i in range(1, 5): num1 = 2 * i num2 = num1 - 1 value = (late_4['high_' + str(num1)] + late_4['high_' + str(num2)]) / 2 late_3['high_%s' % i] = value # define choices for choice 2 late_2 = {} for i in range(1, 3): num1 = 2 * i num2 = num1 - 1 value = (late_3['high_' + str(num1)] + late_3['high_' + str(num2)]) / 2 late_2['high_%s' % i] = value # define choices for choice 1 late_1 = { 'high_1': (late_2['high_2'] + late_2['high_1']) / 2, } # the 6th choice will be a replica of another but this will be defined in pages. # rounding new_1 = {} for i in late_1: new_1[i] = round(late_1[i] + 0.01, 1) self.participant.vars['ce2_late_1'] = new_1 new_2 = {} for i in late_2: new_2[i] = round(late_2[i] + 0.01, 1) self.participant.vars['ce2_late_2'] = new_2 new_3 = {} for i in late_3: new_3[i] = round(late_3[i] + 0.01, 1) self.participant.vars['ce2_late_3'] = new_3 new_4 = {} for i in late_4: new_4[i] = round(late_4[i] + 0.01, 1) self.participant.vars['ce2_late_4'] = new_4 new_5 = {} for i in late_5: new_5[i] = round(late_5[i] + 0.01, 1) self.participant.vars['ce2_late_5'] = new_5 else: ################################################################## ############## CE 3 ######################################## ################################################################## # define choices for early choices self.participant.vars['ce3_early'] = self.participant.vars['cert_equi_list12'] float(self.participant.vars['ce3_early'][0]) # define multiplication factor multfact = 1.06 # define choices for choice 5 late_5 = {} for i in range(1, 17): value = self.participant.vars['ce3_early'][0] * multfact ** (i - 1) late_5['high_%s' % i] = value # define choices for choice 4 late_4 = {} for i in range(1, 9): num1 = 2 * i num2 = num1 - 1 value = (late_5['high_' + str(num1)] + late_5['high_' + str(num2)]) / 2 late_4['high_%s' % i] = value # define choices for choice 3 late_3 = {} for i in range(1, 5): num1 = 2 * i num2 = num1 - 1 value = (late_4['high_' + str(num1)] + late_4['high_' + str(num2)]) / 2 late_3['high_%s' % i] = value # define choices for choice 2 late_2 = {} for i in range(1, 3): num1 = 2 * i num2 = num1 - 1 value = (late_3['high_' + str(num1)] + late_3['high_' + str(num2)]) / 2 late_2['high_%s' % i] = value # define choices for choice 1 late_1 = { 'high_1': (late_2['high_2'] + late_2['high_1']) / 2, } # the 6th choice will be a replica of another but this will be defined in pages. # rounding new_1 = {} for i in late_1: new_1[i] = round(late_1[i] + 0.01, 1) self.participant.vars['ce3_late_1'] = new_1 new_2 = {} for i in late_2: new_2[i] = round(late_2[i] + 0.01, 1) self.participant.vars['ce3_late_2'] = new_2 new_3 = {} for i in late_3: new_3[i] = round(late_3[i] + 0.01, 1) self.participant.vars['ce3_late_3'] = new_3 new_4 = {} for i in late_4: new_4[i] = round(late_4[i] + 0.01, 1) self.participant.vars['ce3_late_4'] = new_4 new_5 = {} for i in late_5: new_5[i] = round(late_5[i] + 0.01, 1) self.participant.vars['ce3_late_5'] = new_5 def set_equivalent(self): if self.participant.vars['page'] == 1: self.equi = self.participant.vars['cert_equi_list2'][0] elif self.participant.vars['page'] == 2: self.equi = self.participant.vars['cert_equi_list9'][0] else: self.equi = self.participant.vars['cert_equi_list12'][0] def set_ce(self): if self.participant.vars['page'] == 1: self.ce = 'list2' elif self.participant.vars['page'] == 2: self.ce = 'list9' else: self.ce = 'list12' round_to_pay = models.StringField() payment = models.CurrencyField() def set_payment_choice(self): pv = self.participant.vars self.round_to_pay = 'no' self.payment = None selected_task = pv.get('time_task_to_pay') selected_task_round = pv.get('time_round_to_pay') # This app: 4w first, then 8w # rounds 1–18 = ce_4w # rounds 19–36 = ce_8w if selected_task == 'ce_4w': target_round = selected_task_round delay_code = 4 elif selected_task == 'ce_8w': target_round = 18 + selected_task_round delay_code = 8 else: return if self.round_number != target_round: return self.round_to_pay = 'yes' # CE task: no lottery resolution, pay chosen amount directly if self.choice == 'early': self.payment = pv['early'] pv['payoff_time'] = self.payment pv['payoff_time_type'] = 0 pv['payoff_time_date'] = 0 else: self.payment = pv['late'] pv['payoff_time'] = self.payment pv['payoff_time_type'] = 0 pv['payoff_time_date'] = 1 # store full details for final results page pv['time_selected_task'] = selected_task pv['time_selected_round'] = selected_task_round pv['time_selected_choice'] = self.choice pv['time_selected_type'] = 'ce' pv['time_selected_delay'] = delay_code pv['time_selected_early'] = pv['early'] pv['time_selected_late'] = pv['late'] pv['time_lottery_realization'] = 'not_applicable' pv['time_random_draw_used'] = None pv['time_final_bonus'] = self.payment