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_2_84'
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 兜底(单测 Lot2 时才会用到)
seq = pv.get('discount_seq')
if seq is None:
seq = 4
pv['discount_seq'] = seq
# 2) ✅ round 1 强制把 path/order 修正成“与 seq 一致”
# 避免 participant.vars 里残留错误的 order/path 影响 weeks_now / is_displayed
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()
# 导出辅助
current_week = models.IntegerField()
segment_index = models.IntegerField()
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: 8w first, then 4w
# rounds 1–18 = lot_8w
# rounds 19–36 = lot_4w
if selected_task == 'lot_8w':
target_round = selected_task_round
delay_code = 8
elif selected_task == 'lot_4w':
target_round = 18 + selected_task_round
delay_code = 4
else:
return
if self.round_number != target_round:
return
self.round_to_pay = 'yes'
draw = pv.get('time_random_draw', 0)
# store for results
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_random_draw_used'] = draw
# resolve lottery
if self.choice == 'early':
if draw == 0:
self.payment = pv['early_high']
pv['payoff_time'] = self.payment
pv['payoff_time_type'] = 1
pv['payoff_time_date'] = 0
pv['time_lottery_realization'] = 'high'
else:
self.payment = pv['early_low']
pv['payoff_time'] = self.payment
pv['payoff_time_type'] = 2
pv['payoff_time_date'] = 0
pv['time_lottery_realization'] = 'low'
elif self.choice == 'late':
if draw == 0:
self.payment = pv['late_high']
pv['payoff_time'] = self.payment
pv['payoff_time_type'] = 1
pv['payoff_time_date'] = 1
pv['time_lottery_realization'] = 'high'
else:
self.payment = pv['late_low']
pv['payoff_time'] = self.payment
pv['payoff_time_type'] = 2
pv['payoff_time_date'] = 1
pv['time_lottery_realization'] = 'low'
pv['time_final_bonus'] = self.payment