import random from otree.api import * class Constants(BaseConstants): name_in_url = 'Pre_Survey' players_per_group = None num_rounds = 1 showup_fee = Currency(1.00) # 9 tuples per row: (you, charity) — these mirror your HTML exactly SVO_PAYOFFS = { 1: [(85,85),(85,76),(85,68),(85,59),(85,50),(85,41),(85,33),(85,24),(85,15)], 2: [(85,15),(87,19),(89,24),(91,28),(93,33),(94,37),(96,41),(98,46),(100,50)], 3: [(50,100),(54,98),(59,96),(63,94),(68,93),(72,91),(76,89),(81,87),(85,85)], 4: [(50,100),(54,89),(59,79),(63,68),(68,58),(72,47),(76,36),(81,26),(85,15)], 5: [(100,50),(94,56),(88,63),(81,69),(75,75),(69,81),(63,88),(56,94),(50,100)], 6: [(100,50),(98,54),(96,59),(94,63),(93,68),(91,72),(89,76),(87,81),(85,85)], } CHARITIES = [ ('American Red Cross','American Red Cross'), ('Doctors Without Borders','Doctors Without Borders'), ('World Wildlife Fund','World Wildlife Fund'), ] class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): # SVO choices (column 1..9) svo_choice_1 = models.IntegerField(choices=range(1, 10), widget=widgets.RadioSelectHorizontal, label=" ") svo_choice_2 = models.IntegerField(choices=range(1, 10), widget=widgets.RadioSelectHorizontal, label=" ") svo_choice_3 = models.IntegerField(choices=range(1, 10), widget=widgets.RadioSelectHorizontal, label=" ") svo_choice_4 = models.IntegerField(choices=range(1, 10), widget=widgets.RadioSelectHorizontal, label=" ") svo_choice_5 = models.IntegerField(choices=range(1, 10), widget=widgets.RadioSelectHorizontal, label=" ") svo_choice_6 = models.IntegerField(choices=range(1, 10), widget=widgets.RadioSelectHorizontal, label=" ") # CAR car_you_financial = models.IntegerField(label="Obligation to financial success", min=1, max=7) car_you_environment = models.IntegerField(label="Obligation to protect the environment", min=1, max=7) car_firm_financial = models.IntegerField(label="Obligation to financial success", min=1, max=7) car_firm_environment = models.IntegerField(label="Obligation to protect the environment", min=1, max=7) # Prosocial motivation prosocial_1 = models.IntegerField(label="I get energized by working on tasks that have the potential to benefit others.", min=1, max=7) prosocial_2 = models.IntegerField(label="It is important to me to have the opportunity to use my abilities to benefit others.", min=1, max=7) prosocial_3 = models.IntegerField(label="I prefer to work on tasks that allow me to have a positive impact on others.", min=1, max=7) prosocial_4 = models.IntegerField(label="I do my best when I’m working on a task that contributes to the well-being of others.", min=1, max=7) prosocial_5 = models.IntegerField(label="I like to work on tasks that have the potential to benefit others.", min=1, max=7) # Payout tracking (no instance methods) selected_decision = models.IntegerField() # 1..6 selected_option = models.IntegerField() # 1..9 pay_to_participant = models.CurrencyField() # actual $ to participant (excl. showup unless added) donation_to_charity = models.CurrencyField() # actual $ to donation chosen_charity = models.StringField(choices=Constants.CHARITIES, widget=widgets.RadioSelect) # Bot/AI check questions meal_check = models.LongStringField( label="What did you eat for your most recent meal? Please describe in a few words." ) icecream_check = models.LongStringField( label="Imagine you’re naming a new ice cream flavor. What would you call it?" ) consent = models.BooleanField( label="Yes, I consent to participate.", widget=widgets.CheckboxInput ) def _get_player_choice(player: Player, row_idx: int) -> int: """Return the chosen column (1..9) for a given SVO row index (1..6).""" return getattr(player, f'svo_choice_{row_idx}') def realize_random_payment_for(player: Player): """ Randomly select one SVO row, read the chosen column, and set payoff + donation fields. Keeps everything on the player object. """ row = random.randint(1, 6) col = _get_player_choice(player, row) you_amt, charity_amt = Constants.SVO_PAYOFFS[row][col - 1] player.selected_decision = row player.selected_option = col player.pay_to_participant = cu(you_amt) player.donation_to_charity = cu(charity_amt) # Convenience copies for export/admin player.participant.vars['charity_name'] = player.chosen_charity player.participant.vars['charity_donation'] = float(player.donation_to_charity) player.participant.vars['selected_decision'] = row player.participant.vars['selected_option'] = col # ---------- Pages ---------- class Introduction(Page): pass class SVO(Page): form_model = 'player' form_fields = ['svo_choice_1','svo_choice_2','svo_choice_3','svo_choice_4','svo_choice_5','svo_choice_6'] class CAR(Page): form_model = 'player' form_fields = ['car_you_financial','car_you_environment','car_firm_financial','car_firm_environment'] class Prosocial(Page): form_model = 'player' form_fields = ['prosocial_1','prosocial_2','prosocial_3','prosocial_4','prosocial_5'] class HumanCheck(Page): form_model = 'player' form_fields = ['meal_check', 'icecream_check'] class CharitySelect(Page): """Participant picks the recipient charity, then we realize the random payment.""" form_model = 'player' form_fields = ['chosen_charity'] @staticmethod def before_next_page(player: Player, timeout_happened): realize_random_payment_for(player) class Results(Page): @staticmethod def vars_for_template(player: Player): row = player.selected_decision col = player.selected_option you_amt, charity_amt = Constants.SVO_PAYOFFS[row][col - 1] player.pay_to_participant = cu(you_amt / 100) player.donation_to_charity = cu(charity_amt / 100) player.payoff = Constants.showup_fee + player.pay_to_participant showup = Constants.showup_fee return dict( showup=showup, you_amt=player.pay_to_participant, charity_amt=player.donation_to_charity, chosen_charity=player.chosen_charity, total_payoff=player.payoff, ) class Consent(Page): form_model = 'player' form_fields = ['consent'] @staticmethod def before_next_page(player, timeout_happened): # Capture Prolific URL params passed in via the study link player.participant.prolific_pid = player.participant.vars.get('PROLIFIC_PID', '') player.participant.study_id = player.participant.vars.get('STUDY_ID', '') player.participant.prolific_session_id = player.participant.vars.get('SESSION_ID', '') @staticmethod def error_message(player, values): if not values['consent']: return 'You must consent to continue' class ProlificRedirect(Page): @staticmethod def is_displayed(player): return True @staticmethod def vars_for_template(player): return dict( redirect_url=player.session.config.get('prolific_redirect_url', '') ) page_sequence = [ Consent, Introduction, SVO, CAR, Prosocial, HumanCheck, CharitySelect, Results, ProlificRedirect ]