from otree.api import Currency as cu
from ._builtin import Page
from .models import Constants
from experience_no_choice.models import mark_slot_taken_for_participant, touch_reservation, reservation_active
class AttentionCheck(Page):
def is_displayed(self):
if not reservation_active(self.session, self.participant):
self.participant.vars['expired'] = True
return False
return self.round_number == 40
def vars_for_template(self):
# just read, don't modify; we want history up to Round 39
tg = self.participant.vars.get('tg') or getattr(self.player, 'tg', None)
try:
tg = int(tg) if tg is not None else None
except (TypeError, ValueError):
tg = None
history = self.participant.vars.get('history', [])
return dict(tg=tg, history=history)
def error_message(self, values):
if not reservation_active(self.session, self.participant):
self.participant.vars['expired'] = True
return "Your session has expired due to inactivity"
def before_next_page(self):
if not reservation_active(self.session, self.participant):
self.participant.vars['expired'] = True
return
touch_reservation(self.session, self.participant)
class Page1(Page):
def is_displayed(self):
if not reservation_active(self.session, self.participant):
self.participant.vars['expired'] = True
return False
return True
def _get_tg_int(self):
tg = self.participant.vars.get('tg') or getattr(self.player, 'tg', None)
try:
return int(tg) if tg is not None else None
except (TypeError, ValueError):
return None
def _get_info_set_int(self):
info_set = self.participant.vars.get('info_set') or getattr(self.player, 'info_set', None)
try:
return int(info_set) if info_set is not None else None
except (TypeError, ValueError):
return None
def _get_outcome_for_round(self, info_set: int) -> int:
seq = Constants.SEQUENCES_BY_INFOSET.get(info_set)
if not seq:
raise RuntimeError(f"No sequence for info_set {info_set}.")
idx = self.subsession.round_number - 1
try:
val = int(seq[idx])
except IndexError:
raise RuntimeError(f"Sequence for info_set {info_set} is shorter than {self.subsession.round_number} rounds.")
if val not in (0, -3, -4):
raise RuntimeError(f"Invalid outcome {val} (must be 0, 3, or 5).")
return val
def vars_for_template(self):
tg = self._get_tg_int()
info_set = self._get_info_set_int()
if info_set is None:
raise RuntimeError("info_set not assigned. Set it in the first app.")
outcome = self._get_outcome_for_round(info_set)
label = {-4: "L1: -4", -3: "L2: -3", 0: "L1: 0"}[outcome]
# Preview total if tg==3 (only then do we add the outcome to payoff)
added = cu(outcome) / 100 if tg == 3 else cu(0)
current_total = self.participant.payoff + added
# History
line = f"Round {self.subsession.round_number}: {label}"
hist = self.participant.vars.setdefault('history', [])
history = hist if (hist and hist[-1] == line) else hist + [line]
return dict(
tg=tg,
info_set=info_set,
outcome=outcome,
label=label,
history=history,
current_total=current_total,
)
def error_message(self, values):
if not reservation_active(self.session, self.participant):
self.participant.vars['expired'] = True
return "Your session has expired due to inactivity"
def before_next_page(self, timeout_happened=False):
if not reservation_active(self.session, self.participant):
self.participant.vars['expired'] = True
return
tg = self._get_tg_int()
info_set = self._get_info_set_int()
if info_set is None:
raise RuntimeError("info_set not assigned. Set it in the first app.")
outcome = self._get_outcome_for_round(info_set)
# Add payoff only for tg==3
if tg == 3:
self.participant.payoff += cu(outcome) / 100
# Persist history (one line per round)
label = {-4: "L1: -4", -3: "L2: -3", 0: "L1: 0"}[outcome]
line = f"Round {self.subsession.round_number}: {label}"
hist = self.participant.vars.setdefault('history', [])
if not hist or hist[-1] != line:
hist.append(line)
touch_reservation(self.session, self.participant)
class Expired(Page):
# Show if we’ve already marked them expired, or if the reservation is no longer active.
def is_displayed(self):
if self.participant.vars.get('expired'):
return True
if not reservation_active(self.session, self.participant):
self.participant.vars['expired'] = True
return True
return False
page_sequence = [
AttentionCheck,
Page1,
Expired,
]