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): name_in_url = 'Time_Adaptive_Lot_1' players_per_group = None # 两段各 18 轮,总 36 轮 num_rounds = 36 ################################################################## ############## Lottery 1 ######################################## ################################################################## lot1_early = {'high': 18, 'low': 6} multfact = 1.04 late_5 = {} for i in range(1, 17): late_5[f'high_{i}'] = lot1_early['high'] * multfact ** (i - 1) for i in range(1, 17): late_5[f'low_{i}'] = lot1_early['low'] * multfact ** (i - 1) late_4 = {} for i in range(1, 9): a, b = 2 * i, 2 * i - 1 late_4[f'high_{i}'] = (late_5[f'high_{a}'] + late_5[f'high_{b}']) / 2 late_4[f'low_{i}'] = (late_5[f'low_{a}'] + late_5[f'low_{b}']) / 2 late_3 = {} for i in range(1, 5): a, b = 2 * i, 2 * i - 1 late_3[f'high_{i}'] = (late_4[f'high_{a}'] + late_4[f'high_{b}']) / 2 late_3[f'low_{i}'] = (late_4[f'low_{a}'] + late_4[f'low_{b}']) / 2 late_2 = {} for i in range(1, 3): a, b = 2 * i, 2 * i - 1 late_2[f'high_{i}'] = (late_3[f'high_{a}'] + late_3[f'high_{b}']) / 2 late_2[f'low_{i}'] = (late_3[f'low_{a}'] + late_3[f'low_{b}']) / 2 late_1 = { 'high_1': (late_2['high_2'] + late_2['high_1']) / 2, 'low_1': (late_2['low_2'] + late_2['low_1']) / 2 } late_6 = {'high_6': lot1_early['high'] - 0.50, 'low_6': lot1_early['low'] - 0.50} lot1_late_1 = {k: round(v + 0.01, 1) for k, v in late_1.items()} lot1_late_2 = {k: round(v + 0.01, 1) for k, v in late_2.items()} lot1_late_3 = {k: round(v + 0.01, 1) for k, v in late_3.items()} lot1_late_4 = {k: round(v + 0.01, 1) for k, v in late_4.items()} lot1_late_5 = {k: round(v + 0.01, 1) for k, v in late_5.items()} lot1_late_6 = {k: round(v + 0.01, 1) for k, v in late_6.items()} ################################################################## ############## Lottery 2 ######################################## ################################################################## lot2_early = {'high': 12, 'low': 0} multfact = 1.05 late_5 = {} for i in range(1, 17): late_5[f'high_{i}'] = lot2_early['high'] * multfact ** (i - 1) for i in range(1, 17): late_5[f'low_{i}'] = lot2_early['low'] * multfact ** (i - 1) late_4 = {} for i in range(1, 9): a, b = 2 * i, 2 * i - 1 late_4[f'high_{i}'] = (late_5[f'high_{a}'] + late_5[f'high_{b}']) / 2 late_4[f'low_{i}'] = (late_5[f'low_{a}'] + late_5[f'low_{b}']) / 2 late_3 = {} for i in range(1, 5): a, b = 2 * i, 2 * i - 1 late_3[f'high_{i}'] = (late_4[f'high_{a}'] + late_4[f'high_{b}']) / 2 late_3[f'low_{i}'] = (late_4[f'low_{a}'] + late_4[f'low_{b}']) / 2 late_2 = {} for i in range(1, 3): a, b = 2 * i, 2 * i - 1 late_2[f'high_{i}'] = (late_3[f'high_{a}'] + late_3[f'high_{b}']) / 2 late_2[f'low_{i}'] = (late_3[f'low_{a}'] + late_3[f'low_{b}']) / 2 late_1 = { 'high_1': (late_2['high_2'] + late_2['high_1']) / 2, 'low_1': (late_2['low_2'] + late_2['low_1']) / 2 } lot2_late_1 = {k: round(v + 0.01, 1) for k, v in late_1.items()} lot2_late_2 = {k: round(v + 0.01, 1) for k, v in late_2.items()} lot2_late_3 = {k: round(v + 0.01, 1) for k, v in late_3.items()} lot2_late_4 = {k: round(v + 0.01, 1) for k, v in late_4.items()} lot2_late_5 = {k: round(v + 0.01, 1) for k, v in late_5.items()} ################################################################## ############## Lottery 3 ######################################## ################################################################## lot3_early = {'high': 10.5, 'low': 3.5} multfact = 1.06 late_5 = {} for i in range(1, 17): late_5[f'high_{i}'] = lot3_early['high'] * multfact ** (i - 1) for i in range(1, 17): late_5[f'low_{i}'] = lot3_early['low'] * multfact ** (i - 1) late_4 = {} for i in range(1, 9): a, b = 2 * i, 2 * i - 1 late_4[f'high_{i}'] = (late_5[f'high_{a}'] + late_5[f'high_{b}']) / 2 late_4[f'low_{i}'] = (late_5[f'low_{a}'] + late_5[f'low_{b}']) / 2 late_3 = {} for i in range(1, 5): a, b = 2 * i, 2 * i - 1 late_3[f'high_{i}'] = (late_4[f'high_{a}'] + late_4[f'high_{b}']) / 2 late_3[f'low_{i}'] = (late_4[f'low_{a}'] + late_4[f'low_{b}']) / 2 late_2 = {} for i in range(1, 3): a, b = 2 * i, 2 * i - 1 late_2[f'high_{i}'] = (late_3[f'high_{a}'] + late_3[f'high_{b}']) / 2 late_2[f'low_{i}'] = (late_3[f'low_{a}'] + late_3[f'low_{b}']) / 2 late_1 = { 'high_1': (late_2['high_2'] + late_2['high_1']) / 2, 'low_1': (late_2['low_2'] + late_2['low_1']) / 2 } lot3_late_1 = {k: round(v + 0.01, 1) for k, v in late_1.items()} lot3_late_2 = {k: round(v + 0.01, 1) for k, v in late_2.items()} lot3_late_3 = {k: round(v + 0.01, 1) for k, v in late_3.items()} lot3_late_4 = {k: round(v + 0.01, 1) for k, v in late_4.items()} lot3_late_5 = {k: round(v + 0.01, 1) for k, v in late_5.items()} class Subsession(BaseSubsession): def creating_session(self): for p in self.get_players(): pv = p.participant.vars # 1) discount_seq 兜底(单测 Lot1 时才会用到) seq = pv.get('discount_seq') if seq is None: seq = 1 pv['discount_seq'] = seq # 2) ✅ round 1 强制把 path/order 修正成“与 seq 一致” # 这样即使 participant.vars 里残留了错误的 order=84,也会被纠正 if p.round_number == 1: correct_path = 'Lot1→CE' if seq in (1, 3) else 'CE→Lot2' correct_order = '48' if seq in (1, 2) else '84' pv['path'] = correct_path pv['order'] = correct_order # ✅ 同时清理跨 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) # 3) 支付兜底 # pv.setdefault('task_to_pay', 0) # pv.setdefault('round_to_pay', random.randint(1, Constants.num_rounds)) # 4) block 开始 node=1 if p.round_number in (1, 7, 13, 19, 25, 31): pv['node'] = 1 # 5) round1 初始化 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): # 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 Lottery - Tomorrow 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 Lottery - 8 weeks change during this task?', ) incorrect_attempts = models.IntegerField(initial=0) # 记录展示的四个金额 lot_high_early = models.CurrencyField() lot_low_early = models.CurrencyField() lot_high_late = models.CurrencyField() lot_low_late = models.CurrencyField() # 导出辅助:当前是 4w/8w,所在段(1或2) current_week = models.IntegerField() segment_index = models.IntegerField() time_selected_task = models.StringField(blank=True) time_selected_round = models.IntegerField(blank=True, null=True) time_selected_choice = models.StringField(blank=True) # 'early' or 'late' time_selected_early_high = models.CurrencyField(blank=True, null=True) time_selected_early_low = models.CurrencyField(blank=True, null=True) time_selected_late_high = models.CurrencyField(blank=True, null=True) time_selected_late_low = models.CurrencyField(blank=True, null=True) time_lottery_realization = models.StringField(blank=True) # 'high', 'low', 'not_selected' time_random_draw_used = models.IntegerField(blank=True, null=True) def set_options(self): self.lot_high_early = self.participant.vars['early_high'] self.lot_low_early = self.participant.vars['early_low'] self.lot_high_late = self.participant.vars['late_high'] self.lot_low_late = self.participant.vars['late_low'] choice = models.StringField() def set_node(self): # 每个 block(6 轮)末把 node 重置 if self.round_number % 6 == 0: 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): # 每个 block 开始时切换 page(1,7,13,19,25,31) 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] # 支付 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 contains two logical tasks: # rounds 1-18 = lot_4w # rounds 19-36 = lot_8w if selected_task == 'lot_4w': target_round = selected_task_round delay_code = 4 elif selected_task == 'lot_8w': target_round = 18 + selected_task_round delay_code = 8 else: return if self.round_number != target_round: return self.round_to_pay = 'yes' # store selection details self.time_selected_task = selected_task self.time_selected_round = selected_task_round self.time_selected_choice = self.choice self.time_selected_early_high = self.participant.vars['early_high'] self.time_selected_early_low = self.participant.vars['early_low'] self.time_selected_late_high = self.participant.vars['late_high'] self.time_selected_late_low = self.participant.vars['late_low'] draw = pv.get('time_random_draw', 0) self.time_random_draw_used = draw if self.choice == 'early': if draw == 0: self.payment = pv['early_high'] self.time_lottery_realization = 'high' pv['payoff_time'] = self.payment pv['payoff_time_type'] = 1 pv['payoff_time_date'] = 0 else: self.payment = pv['early_low'] self.time_lottery_realization = 'low' pv['payoff_time'] = self.payment pv['payoff_time_type'] = 2 pv['payoff_time_date'] = 0 elif self.choice == 'late': if draw == 0: self.payment = pv['late_high'] self.time_lottery_realization = 'high' pv['payoff_time'] = self.payment pv['payoff_time_type'] = 1 pv['payoff_time_date'] = 1 else: self.payment = pv['late_low'] self.time_lottery_realization = 'low' pv['payoff_time'] = self.payment pv['payoff_time_type'] = 2 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'] = 'lottery' pv['time_selected_delay'] = delay_code pv['time_selected_early_high'] = pv['early_high'] pv['time_selected_early_low'] = pv['early_low'] pv['time_selected_late_high'] = pv['late_high'] pv['time_selected_late_low'] = pv['late_low'] pv['time_lottery_realization'] = self.time_lottery_realization pv['time_random_draw_used'] = draw pv['time_final_bonus'] = self.payment