from otree.api import * import random import os doc = """ Condition 1 ---> promo perf all """ class C(BaseConstants): NAME_IN_URL = 'p1' PLAYERS_PER_GROUP = 6 treatment = 'mixed' # set to either 'performance', 'potential', OR 'mixed' NUM_ROUNDS = 1 p1_word_file = 'p1_words.txt' p1_time = 180 # length in seconds of tasks in part 1 # PART 1 MATH ANSWERS p1_m1_a = 6 p1_m2_a = 26 p1_m3_a = 96 p1_m4_a = 3 p1_m5_a = 48 p1_m6_a = 58 p1_m7_a = 1 p1_m8_a = 28 p1_m9_a = 15 p1_m10_a = 40 def load_word_set(filename): path = os.path.join(os.path.dirname(__file__), filename) with open(path, 'r', encoding='utf-8') as f: return {line.strip().lower() for line in f if line.strip()} p1_word_bank = load_word_set(C.p1_word_file) def score_word(word: str) -> int: """Return points for a *valid* word based on its length.""" n = len(word) points = n-1 return points def normalize_word(raw: str) -> str: return raw.strip().lower() class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): # COMPENSATION & PERFORMANCE VARS pennies_earned = models.IntegerField(initial=0) p1_quant_correct = models.IntegerField(initial=0) p1_accepted_words = models.LongStringField(initial="") p1_words_submitted = models.LongStringField(initial="") p1_v_score = models.IntegerField(initial=0) # LOGIC VARS part1_math_first = models.BooleanField() w_score = models.IntegerField(initial=0) g_id = models.IntegerField(initial=0) treatment = models.StringField(initial="") # PART 1 MATH QUESTIONS p1_m_1 = models.IntegerField(label='1.) If 4x-3=2x+9, what is the value of x?') p1_m_2 = models.IntegerField(label='2.) The average of three consecutive even integers is 24. What is the largest' ' integer?') p1_m_3 = models.IntegerField(label='3.) An item costs $1.00. The price is increased by 20% and then decreased by ' '20%. What is the current price for the item? (input your answer in cents as an' ' integer)') p1_m_4 = models.IntegerField(label='4.) If 5^x=125, what is the value of x?') p1_m_5 = models.IntegerField(label='5.) A car travels 120 miles at 60 mph and returns the same distance at 40 mph.' ' What is the average speed for the entire trip? (input your answer in mph as an' ' integer)') p1_m_6 = models.IntegerField(label='6.) If x+y=10 and xy=21, what is the value of x^2+y^2?') p1_m_7 = models.IntegerField(label='7.) What is the remainder when 73 is divided by 8?') p1_m_8 = models.IntegerField(label='8.) If the ratio of boys to girls is 4:7 and there are 44 students total, how ' 'many are girls?') p1_m_9 = models.IntegerField(label='9.) If (2/3)x-4=6, what is the value of x?') p1_m_10 = models.IntegerField(label='10.) A box contains only red and blue marbles. If 3/8 of the marbles are red ' 'and there are 15 red marbles, how many marbles are in the box?') # FUNCTIONS def creating_session(subsession): coin_flip = random.choice([True, False]) for player in subsession.get_players(): player.part1_math_first = coin_flip player.treatment = C.treatment # PAGES class Instruct(Page): pass class wp_start(WaitPage): body_text = "Please wait while the other study participants finish reading the instructions." class Gen_Info(Page): pass class p1_q_instruct(Page): # part 1 quantitative task first @staticmethod def is_displayed(player): return player.part1_math_first == True class p1_q(Page): timeout_seconds = C.p1_time # set in Constants timer_text = 'Time remaining:' form_model = 'player' form_fields = ['p1_m_1', 'p1_m_2', 'p1_m_3', 'p1_m_4', 'p1_m_5', 'p1_m_6', 'p1_m_7', 'p1_m_8', 'p1_m_9', 'p1_m_10'] @staticmethod def is_displayed(player): return player.part1_math_first == True @staticmethod def before_next_page(player: Player, timeout_happened): for i in range(1, 11): player_answer = getattr(player, f'p1_m_{i}') correct_answer = getattr(C, f'p1_m{i}_a') if player_answer == correct_answer: player.pennies_earned += 35 player.p1_quant_correct += 1 class p1_v_instruct(Page): # part 1 verbal task @staticmethod def vars_for_template(player: Player): if player.part1_math_first: task_number = '2' ordinal = 'second' else: task_number = '1' ordinal = 'first' return dict( tn=task_number, ordinal=ordinal ) class p1_v(Page): timeout_seconds = C.p1_time # set in Constants timer_text = 'Time remaining:' @staticmethod def vars_for_template(player: Player): if player.part1_math_first: task_number = '2' else: task_number = '1' return dict( tn=task_number, words_submitted=player.p1_words_submitted ) @staticmethod def live_method(player: Player, data): if data.get('type') == 'init': return { player.id_in_group: {"words_submitted": player.p1_words_submitted} } if data.get('type') == 'submit': raw_word = data["word"] word = normalize_word(raw_word) bank = p1_word_bank if player.p1_words_submitted == "": player.p1_words_submitted = word submission_list = player.p1_words_submitted.split(", ") if word not in submission_list: player.p1_words_submitted = f"{player.p1_words_submitted}, {word}" if word in bank: accepted_list = player.p1_accepted_words.split(", ") if word in accepted_list: return {player.id_in_group: {"words_submitted": player.p1_words_submitted,}} elif player.p1_accepted_words == "": player.p1_accepted_words = word player.w_score = score_word(word) player.p1_v_score = player.w_score + player.p1_v_score return {player.id_in_group: {"words_submitted": player.p1_words_submitted}} else: player.p1_accepted_words = f"{player.p1_accepted_words}, {word}" player.w_score = score_word(word) player.p1_v_score = player.w_score + player.p1_v_score return {player.id_in_group: {"words_submitted": player.p1_words_submitted}} else: return { player.id_in_group: {"words_submitted": player.p1_words_submitted}} @staticmethod def before_next_page(player: Player, timeout_happened): player.w_score = 0 player.pennies_earned += player.p1_v_score class p1_q2_instruct(Page): # part 1 quantitative task second @staticmethod def is_displayed(player): return player.part1_math_first == False class p1_q2(Page): timeout_seconds = C.p1_time # set in Constants timer_text = 'Time remaining:' form_model = 'player' form_fields = ['p1_m_1', 'p1_m_2', 'p1_m_3', 'p1_m_4', 'p1_m_5', 'p1_m_6', 'p1_m_7', 'p1_m_8', 'p1_m_9', 'p1_m_10'] @staticmethod def is_displayed(player): return player.part1_math_first == False @staticmethod def before_next_page(player: Player, timeout_happened): for i in range(1, 11): player_answer = getattr(player, f'p1_m_{i}') correct_answer = getattr(C, f'p1_m{i}_a') if player_answer == correct_answer: player.pennies_earned += 35 player.p1_quant_correct += 1 class AssignRolesWaitPage(WaitPage): @staticmethod def after_all_players_arrive(group: Group): players = group.get_players() assigned = set() # --- Rank by math score (p1_quant_correct), descending --- math_sorted = sorted(players, key=lambda p: p.p1_quant_correct, reverse=True) math_top = math_sorted[0] # best math performer math_top.g_id = 1 assigned.add(math_top) # Second-best math, excluding math_top math_second = None for p in math_sorted: if p not in assigned: math_second = p break math_second.g_id = 4 assigned.add(math_second) # --- Rank by verbal score (p1_v_score), descending --- verbal_sorted = sorted(players, key=lambda p: p.p1_v_score, reverse=True) # Top verbal performer, but NOT the same person as math_top verbal_top = None for p in verbal_sorted: if p not in assigned: verbal_top = p break verbal_top.g_id = 3 assigned.add(verbal_top) # Second-best verbal performer, excluding already-assigned players verbal_second = None for p in verbal_sorted: if p not in assigned: verbal_second = p break verbal_second.g_id = 6 assigned.add(verbal_second) # --- Remaining "average" players get g_id 2 and 5 --- remaining = [p for p in players if p not in assigned] random.shuffle(remaining) remaining[0].g_id = 2 remaining[1].g_id = 5 class p2(Page): @staticmethod def vars_for_template(player: Player): return dict( g_id=player.g_id ) @staticmethod def before_next_page(player: Player, timeout_happened): player.participant.pennies_earned = player.pennies_earned player.participant.p1_quant_correct = player.p1_quant_correct player.participant.p1_v_score = player.p1_v_score player.participant.g_id = player.g_id player.participant.treatment = player.treatment @staticmethod def app_after_this_page(player, upcoming_apps): if not player.part1_math_first: return upcoming_apps[0] else: return upcoming_apps[1] page_sequence = [Instruct, wp_start, Gen_Info, p1_q_instruct, p1_q, p1_v_instruct, p1_v, p1_q2_instruct, p1_q2, AssignRolesWaitPage, p2]