from otree.api import * c = cu doc = 'Participants are told their earnings (cumulative for entire experiment) and debriefed. Shown amount to be paid and then dismissed.' class C(BaseConstants): # built-in constants NAME_IN_URL = 'Outro' PLAYERS_PER_GROUP = 2 NUM_ROUNDS = 1 # user-defined constants BASE_PAY = 5 UW_TETON_BG_TEMPLATE = 'Outro/uw_teton_bg.html' class Subsession(BaseSubsession): pass def after_all_players_arrive(subsession: Subsession): session = subsession.session # pull the matrix you stored in the code‐entry app matrix = subsession.session.vars.get('couple_matrix') if not matrix: # safety check in case someone forgot to run grouping first raise RuntimeError("No couple_matrix in session.vars") # re‐apply it in this app's subsession subsession.set_group_matrix(matrix) class Group(BaseGroup): selected_round_price = models.IntegerField() spender_WTP = models.IntegerField() winner = models.IntegerField() def payoff_calc_func(group: Group): session = group.session import random players = group.get_players() for p in players: payoff_round = p.participant.selected_round p.participant.random_code = random.randint(10000, 99999) # code used for verifying item payout # Assign payoff depending on selected round and add correct belief bonus if payoff_round == 1: for p in players: p.participant.final_earnings_without_fee = p.participant.earnings_1 if p.participant.correct_belief == 1: p.participant.final_earnings_without_fee += session.config['belief_payout'] p.payoff = p.participant.final_earnings_without_fee else: for p in players: p.participant.final_earnings_without_fee = p.participant.earnings_2 if p.participant.correct_belief == 1: p.participant.final_earnings_without_fee += session.config['belief_payout'] p.payoff = p.participant.final_earnings_without_fee # Add in the participation fee, adjusted for rounding for p in players: p.participant.final_earnings_with_fee = int(p.participant.final_earnings_without_fee + session.config["fee"] + 0.5) # the "int(x + 0.5)" is used to round. Basically, there's some weird behavior with the round function due to "banker's rounding": if using round(), if the leading number is even, the number will round up, and if leading number is odd, the number will round down. This way will work no matter what, as int() always rounds down (so 2.3 -> 2.3 + 0.5 = 2.8, int(2.8) = 2, and 2.7 -> 2.7 + 0.5 = 3.2, int(3.3) = 3) def select_payoff_round(group: Group): import random # Select the round for payoff for the group payoff_round = random.randint(1,2) players = group.get_players() for p in players: # Save the selected round to the participant field p.participant.selected_round = payoff_round # Conditional depending on which round was selected for payoff if payoff_round == 1: # Save the spender's WTP for the selected round in a group variable if p.participant.spender == 1: group.spender_WTP = int(p.participant.WTP_1) # Save the computer's price for the selected round in a group variable group.selected_round_price = p.participant.computer_price_1 # If the participant is the spender AND won in the selected round if p.participant.spender == 1 and p.participant.winner_1 == 1: group.winner = 1 else: group.winner = 0 else: # i.e. if the selected round is round 2 # Save the spender's WTP for the selected round in a group variable if p.participant.spender == 1: group.spender_WTP = int(p.participant.WTP_2) # Save the computer's price and spender's WTP for the selected round in a group variable group.selected_round_price = p.participant.computer_price_2 # If the participant is the spender AND won in the selected round if p.participant.spender == 1 and p.participant.winner_2 == 1: group.winner = 1 else: group.winner = 0 class Player(BasePlayer): exp_payment = models.FloatField() def custom_export(players): # Header yield [ 'session_code', 'participant_id_in_session', 'participant_code', 'id_in_group', 'group_number', 'spender', 'payoff', 'show_up_fee', 'earnings', 'selected_round', 'winner_round_1', 'winner_round_2', 'group_winner', 'selected_item', 'auction_win_code' ] # Rows for p in players: pp = p.participant pg = p.group ps = p.session # Calculating the earnings payoff = p.participant.payoff fee = p.session.config['fee'] # if fee is stored as a plain number, wrap it in Currency: fee_currency = c(fee) earnings = payoff + fee_currency # Pull participant fields spender = p.participant.vars.get('spender', '') selected_round = p.participant.vars.get('selected_round', '') winner_1 = p.participant.vars.get('winner_1', '') winner_2 = p.participant.vars.get('winner_2', '') spender_selected_item = p.participant.vars.get('spender_selected_item', '') random_code = p.participant.vars.get('random_code', '') yield [ ps.code, pp.id_in_session, pp.code, p.id_in_group, pg.id_in_subsession, spender, payoff, fee_currency, earnings, selected_round, winner_1, winner_2, pg.winner, spender_selected_item, random_code ] class GroupingWaitPage(WaitPage): wait_for_all_groups = True after_all_players_arrive = after_all_players_arrive class SelectRoundWaitPage(WaitPage): after_all_players_arrive = select_payoff_round class PayoffCalcWaitPage(WaitPage): after_all_players_arrive = payoff_calc_func class Dismissal(Page): form_model = 'player' # vars_for_template is deprecated in favor of python-based renderers @staticmethod def vars_for_template(player: Player): group = player.group participant = player.participant # Define final variables for dismissal winner_1 = int(participant.winner_1) winner_2 = int(participant.winner_2) selected_round = int(participant.selected_round) payoff = int(participant.final_earnings_with_fee) computer_price = int(group.selected_round_price) spender_WTP = group.spender_WTP # Passing Python variables to the HTML template of the page return dict(selected_round = selected_round, payoff = payoff, computer_price = computer_price, spender_WTP = spender_WTP, winner_1 = winner_1, winner_2 = winner_2) page_sequence = [GroupingWaitPage, SelectRoundWaitPage, PayoffCalcWaitPage, Dismissal]