from otree.api import * author = "Yuanqing and Anna" import random import json doc = "An experiment about confidence and uncertainty" class Constants(BaseConstants): # REQUIRED lowercase names name_in_url = 'experiment' players_per_group = None num_rounds = 1 # Your conditions (also lowercase) condition_high = 'high_confidence' condition_low = 'low_confidence' condition_control = 'control' conditions = [condition_high, condition_low, condition_control] # Word lists (unchanged) word_pairs_hard = [ {'cue': 'Absence', 'target': 'Kids'}, {'cue': 'Attitude', 'target': 'Feeling'}, {'cue': 'Bravado', 'target': 'Music'}, {'cue': 'Cleaning', 'target': 'Bill'}, {'cue': 'Closet', 'target': 'Case'}, {'cue': 'Design', 'target': 'Pattern'}, {'cue': 'Elbow', 'target': 'Joint'}, {'cue': 'Fiber', 'target': 'Rope'}, {'cue': 'Grand', 'target': 'Theft'}, {'cue': 'Hazard', 'target': 'Waste'}, {'cue': 'Imagine', 'target': 'Song'}, {'cue': 'Jet', 'target': 'Ski'}, {'cue': 'Kangaroo', 'target': 'Baby'}, {'cue': 'Lace', 'target': 'Silk'}, {'cue': 'Mail', 'target': 'Air'}, {'cue': 'Nylon', 'target': 'Run'}, {'cue': 'Oak', 'target': 'Maple'}, {'cue': 'Palace', 'target': 'Dance'}, {'cue': 'Quarter', 'target': 'Game'}, {'cue': 'Racoon', 'target': 'Cap'}, {'cue': 'Sentence', 'target': 'English'}, {'cue': 'Thumb', 'target': 'Print'}, {'cue': 'Uranus', 'target': 'Joke'}, {'cue': 'Vanish', 'target': 'Appear'}, {'cue': 'Weed', 'target': 'Garden'}, {'cue': 'Yearn', 'target': 'Strive'}, {'cue': 'Zero', 'target': 'Capacity'} ] word_pairs_easy = [ {'cue': 'Add', 'target': 'Substract'}, {'cue': 'Aunt', 'target': 'Uncle'}, {'cue': 'Bad', 'target': 'Good'}, {'cue': 'Boy', 'target': 'Girl'}, {'cue': 'Dad', 'target': 'Mom'}, {'cue': 'Empty', 'target': 'Full'}, {'cue': 'Grandma', 'target': 'Grandpa'}, {'cue': 'Happy', 'target': 'Sad'}, {'cue': 'He', 'target': 'She'}, {'cue': 'Hurt', 'target': 'Pain'}, {'cue': 'King', 'target': 'Queen'}, {'cue': 'Less', 'target': 'More'}, {'cue': 'Negative', 'target': 'Positive'}, {'cue': 'Noun', 'target': 'Verb'}, {'cue': 'Off', 'target': 'On'}, {'cue': 'Pepper', 'target': 'Salt'}, {'cue': 'Scream', 'target': 'Yell'}, {'cue': 'Arms', 'target': 'Legs'}, {'cue': 'Army', 'target': 'Navy'}, {'cue': 'Begin', 'target': 'End'}, {'cue': 'Best', 'target': 'Worst'}, {'cue': 'Closing', 'target': 'Opening'}, {'cue': 'Dinner', 'target': 'Supper'}, {'cue': 'Far', 'target': 'Near'}, {'cue': 'Forget', 'target': 'Remember'}, {'cue': 'Early', 'target': 'Late'}, {'cue': 'Today', 'target': 'Tomorrow'} ] # Map condition to word list word_list_map = { condition_high: word_pairs_easy, condition_low: word_pairs_hard, condition_control: word_pairs_easy, } # Risk task: 18 gambles (Y = win amount, X = sure amount) risk_gambles = [ {'Y': 200, 'X': 40}, {'Y': 200, 'X': 60}, {'Y': 200, 'X': 80}, {'Y': 200, 'X': 100}, {'Y': 200, 'X': 120}, {'Y': 200, 'X': 140}, {'Y': 300, 'X': 60}, {'Y': 300, 'X': 90}, {'Y': 300, 'X': 120}, {'Y': 300, 'X': 150}, {'Y': 300, 'X': 180}, {'Y': 300, 'X': 210}, {'Y': 400, 'X': 80}, {'Y': 400, 'X': 120}, {'Y': 400, 'X': 160}, {'Y': 400, 'X': 200}, {'Y': 400, 'X': 240}, {'Y': 400, 'X': 280}, ] class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): condition = models.StringField(initial='') word_order = models.LongStringField(initial='[]') recall_answers = models.LongStringField(initial='{}') risk_order = models.LongStringField(initial='[]') risk_choices = models.LongStringField(initial='{}') rank_estimate = models.IntegerField( min=1, max=10, label='Your rank estimate (1 = top 10%, 10 = bottom 10%)' ) belief_list_1 = models.LongStringField(initial='') belief_list_2 = models.LongStringField(initial='') belief_list_3 = models.LongStringField(initial='') class WelcomePage(Page): """Page 1: General introduction, identical for all participants.""" @staticmethod def before_next_page(player: Player, timeout_happened): # Assign condition (balanced, per-session random order) n = player.session.num_participants rng = random.Random(player.session.code) treatments = (Constants.conditions * (n // len(Constants.conditions) + 1))[:n] rng.shuffle(treatments) player.condition = treatments[player.participant.id_in_session - 1] # Generate per-participant random word order and store it word_pairs = Constants.word_list_map.get(player.condition, Constants.word_pairs_easy) indices = list(range(len(word_pairs))) word_rng = random.Random(player.session.code + str(player.participant.id_in_session)) word_rng.shuffle(indices) player.word_order = json.dumps(indices) # Generate per-participant random risk question order risk_indices = list(range(len(Constants.risk_gambles))) risk_rng = random.Random(player.session.code + 'risk' + str(player.participant.id_in_session)) risk_rng.shuffle(risk_indices) player.risk_order = json.dumps(risk_indices) class TaskInstructions(Page): """Page 2: Condition-specific task description derived from player.condition (DB field).""" @staticmethod def vars_for_template(player: Player): condition = player.condition show_full_instructions = condition in (Constants.condition_high, Constants.condition_low) if not show_full_instructions: task_description = ( "In this task you will study a set of word pairs. " "Pay close attention to each pair, as you will be asked to recall them later." ) else: task_description = '' word_pairs = Constants.word_list_map.get(condition, Constants.word_pairs_easy) return { 'task_description': task_description, 'num_pairs': len(word_pairs), 'condition': condition, 'show_full_instructions': show_full_instructions, } class LearningPhase(Page): @staticmethod def is_displayed(player: Player): return player.condition != Constants.condition_control @staticmethod def vars_for_template(player: Player): word_pairs = Constants.word_list_map.get(player.condition, Constants.word_pairs_easy) order = json.loads(player.word_order) if player.word_order else list(range(len(word_pairs))) shuffled_pairs = [word_pairs[i] for i in order] return {'shuffled_pairs': shuffled_pairs} class RecallIntro(Page): timeout_seconds = 20 @staticmethod def vars_for_template(player: Player): return {'is_control': player.condition == Constants.condition_control} class RecallPhase(Page): form_model = 'player' form_fields = ['recall_answers'] @staticmethod def vars_for_template(player: Player): if player.condition == Constants.condition_control: shuffled_cues = [ {'cue': chr(65 + i) + ' or ' + chr(97 + i) + '?', 'idx': i} for i in range(26) ] cue_label = 'Question' placeholder = 'Type your guess...' else: word_pairs = Constants.word_list_map.get(player.condition, Constants.word_pairs_easy) order = json.loads(player.word_order) if player.word_order else list(range(len(word_pairs))) shuffled_cues = [{'cue': word_pairs[i]['cue'], 'idx': i} for i in order] cue_label = 'Hint word' placeholder = 'Type the matching word...' return {'shuffled_cues': shuffled_cues, 'cue_label': cue_label, 'placeholder': placeholder, 'is_control': player.condition == Constants.condition_control} class RiskIntro(Page): pass class RiskTask(Page): form_model = 'player' form_fields = ['risk_choices'] @staticmethod def vars_for_template(player: Player): order = json.loads(player.risk_order) if player.risk_order else list(range(len(Constants.risk_gambles))) shuffled_gambles = [Constants.risk_gambles[i] for i in order] return {'gambles': shuffled_gambles} class EstimateIntro(Page): @staticmethod def vars_for_template(player: Player): return {'is_control': player.condition == Constants.condition_control} class EstimateChoice(Page): form_model = 'player' form_fields = ['rank_estimate'] @staticmethod def vars_for_template(player): # Each tuple: (rank_number, lower_bound_pct, upper_bound_pct) ranks = [(i, (i - 1) * 10, i * 10) for i in range(1, 11)] return {'ranks': ranks, 'is_control': player.condition == Constants.condition_control} class BeliefIntro(Page): pass def _belief_vars(option1_text, field_name): p_values = list(range(0, 101, 5)) # 0, 5, 10, ..., 100 return {'option1_text': option1_text, 'field_name': field_name, 'p_values': p_values} class BeliefList1(Page): template_name = 'experiment/BeliefList.html' form_model = 'player' form_fields = ['belief_list_1'] @staticmethod def vars_for_template(player): return _belief_vars( 'You win 200 tokens if your estimate is correct.', 'belief_list_1' ) class BeliefList2(Page): template_name = 'experiment/BeliefList.html' form_model = 'player' form_fields = ['belief_list_2'] @staticmethod def vars_for_template(player): return _belief_vars( 'You win 200 tokens if your true ranking is below your estimate.', 'belief_list_2' ) class BeliefList3(Page): template_name = 'experiment/BeliefList.html' form_model = 'player' form_fields = ['belief_list_3'] @staticmethod def vars_for_template(player): return _belief_vars( 'You win 200 tokens if your true ranking is above your estimate.', 'belief_list_3' ) page_sequence = [ WelcomePage, TaskInstructions, LearningPhase, RecallIntro, RecallPhase, RiskIntro, RiskTask, EstimateIntro, EstimateChoice, BeliefIntro, BeliefList1, BeliefList2, BeliefList3, ]