from otree.api import * from decimal import Decimal, ROUND_HALF_UP doc = """ Your app description """ class C(BaseConstants): NAME_IN_URL = 'End_Page' PLAYERS_PER_GROUP = None NUM_ROUNDS = 1 def timer(player, seconds): """Return seconds if timers are enabled, else None.""" return seconds if player.session.config.get('enable_timers', False) else None class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): final_payoff = models.CurrencyField() final_payout = models.CurrencyField() feedback = models.LongStringField(blank=True, label="Do you have any additional comments or thoughts about the experiment?") # PAGES class DropOutEnd(Page): @staticmethod def is_displayed(player): return player.participant.vars.get('dropout', False) class Feedback(Page): form_model = 'player' form_fields = ['feedback'] timeout_seconds = 240 class End_Page(Page): @staticmethod def is_displayed(player: Player): # Show only if they didn’t drop out and it’s not a special end state return ( not getattr(player.participant, "dropout", False) and not player.participant.vars.get("early_end", False) and not player.participant.vars.get("timeout", False) and not player.participant.vars.get("dropout_manip", False) ) @staticmethod def vars_for_template(player: Player): pv = player.participant.vars bonus_round = pv.get("bonus_round") bonus_payoff = float(pv.get("bonus_payoff") or 0) # ensure numeric bonus_value = float(pv.get("bonus_value") or 0) # ensure numeric bonus_raw = (bonus_payoff or 0) * (bonus_value or 0) # Conversion factor from session config factor = player.session.config.get("points_to_gbp", 0.085) # Convert to GBP payout_gbp = bonus_raw # Set oTree payoff player.participant.payoff = payout_gbp # Persist in vars for later use pv["bonus_raw"] = bonus_raw pv["bonus_payout_gbp"] = payout_gbp pv["total_gain"] = payout_gbp # kept for backwards compatibility # Formatting helper def fmt2(x): if x is None: return None return str(Decimal(str(x)).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)) return dict( bonus_round=bonus_round, bonus_payoff=bonus_payoff, # points bonus_value=fmt2(bonus_value), # in-game value per point bonus_raw=fmt2(bonus_raw), conversion_factor=factor, total_gain=fmt2(payout_gbp), # £ shown to participant ) class GroupEnd(Page): @staticmethod def is_displayed(player): return player.participant.vars.get('early_end', False) class Timeout_End(Page): @staticmethod def is_displayed(player): return player.participant.vars.get('timeout', False) class DropOutManip(Page): @staticmethod def is_displayed(player): return player.participant.vars.get('dropout_manip', False) page_sequence = [DropOutEnd, DropOutManip, GroupEnd, Timeout_End, Feedback, End_Page]