from otree.api import * import pandas import random import timeit from datetime import datetime as dt doc = """""" class C(BaseConstants): NAME_IN_URL = 'predict-numbers' PLAYERS_PER_GROUP = None NUM_BLOCKS = 3 BLOCK_ROUNDS = 60 NUM_ROUNDS = NUM_BLOCKS*BLOCK_ROUNDS bonus_exchange = 1000 show_up_fee = 2 incentives = {"0vs10" : [0 , 10], "10vs20" : [10, 20], "0vs20" : [0, 20] } class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): show_up_fee = models.CurrencyField() is_mobile = models.BooleanField() is_dropout = models.BooleanField(initial=False, blank=True) time_ended_utc = models.StringField() instruction_time_taken_min = models.StringField() total_time_taken_min = models.StringField() consent = models.BooleanField(label="", choices=[[True, 'CONSENT - I consent to participate in the study and agree to the collection, storage, and use of my data as described above.'] ]) instruction_comment = models.StringField(label='Comments:', blank=True) attention_check = models.BooleanField() starting_question1 = models.BooleanField() starting_question2 = models.BooleanField() starting_question3 = models.BooleanField() block_order = models.StringField() block_type = models.StringField() block_num = models.IntegerField() low_points = models.IntegerField() high_points = models.IntegerField() yes_side = models.StringField() die_outcome = models.IntegerField() chose_yes = models.BooleanField() rt_think_ms = models.FloatField() rt_choice_ms = models.FloatField() trial_points = models.IntegerField() block_yes_ratio = models.FloatField() block_points = models.IntegerField() total_yes_ratio = models.FloatField() total_points = models.CurrencyField() bonus = models.CurrencyField() finished = models.BooleanField() def creating_session(subsession: Subsession): if subsession.round_number == 1: start = timeit.default_timer() for p in subsession.get_players(): print(f'plyaer num: {p.id_in_group}') incentives = list(C.incentives.keys()) random.shuffle(incentives) yes_side = random.choice(["right", "left"]) for block in range(1, C.NUM_BLOCKS + 1): # print(f'player: {p.id_in_group}, block: {block}') curr_incentive = incentives[block - 1] rounds_start = ( (block-1)*C.BLOCK_ROUNDS ) + 1 rounds_end = rounds_start + C.BLOCK_ROUNDS for round_num in range(rounds_start , rounds_end): p.in_round(round_num).show_up_fee = cu(C.show_up_fee) p.in_round(round_num).block_order = str(incentives)[1:-2] p.in_round(round_num).yes_side = yes_side p.in_round(round_num).block_num = block p.in_round(round_num).block_type = curr_incentive p.in_round(round_num).low_points = C.incentives[curr_incentive][0] p.in_round(round_num).high_points = C.incentives[curr_incentive][1] stop = timeit.default_timer() print('Time to create session: ', (stop - start)/60) def get_time_taken(curr_timestamp, time_started): if "." in time_started: diff_timestamp = curr_timestamp - dt.strptime(time_started[:time_started.rfind(".")], '%Y-%m-%d %H:%M:%S').timestamp() else: diff_timestamp = curr_timestamp - dt.strptime(ptime_started, '%Y-%m-%d %H:%M:%S').timestamp() minutes, seconds = divmod(int(diff_timestamp), 60) while minutes >= 60 or minutes < 0: if minutes >= 60: minutes -= 60 else: minutes += 60 time_difference = f"{minutes}:{seconds:02d}" return time_difference # PAGES class MobileCheck(Page): form_model = 'player' form_fields = ['is_mobile'] timeout_seconds = 60 def is_displayed(player: Player): return player.round_number == 1 def before_next_page(player: Player, timeout_happened): for p_r in player.in_rounds(1, C.NUM_ROUNDS): # p_r.participant.time_started_utc = dt.now().strftime('%Y-%m-%d %H:%M:%S') p_r.is_mobile = player.is_mobile if p_r.is_mobile == 1 or timeout_happened: p_r.is_dropout = 1 class Consent(Page): form_model = 'player' form_fields = ['consent', 'is_dropout'] timeout_seconds = 60*7 def is_displayed(player: Player): return player.round_number == 1 def before_next_page(player: Player, timeout_happened): if timeout_happened: for p in player.in_rounds(1, C.NUM_ROUNDS): p.is_dropout = True for p_r in player.in_rounds(1, C.NUM_ROUNDS): p_r.consent = player.consent class Welcome(Page): form_model = 'player' form_fields = ['is_dropout'] timeout_seconds = 600 def is_displayed(player: Player): return player.round_number == 1 def vars_for_template(player: Player): if player.yes_side == 'right': yes_side = 'right' no_side = 'left' else: yes_side = 'left' no_side = 'right' return{ 'yes_side': yes_side, 'no_side': no_side, 'img_choice': 'mind_game/Choice/' + player.block_type + player.yes_side + '.jpeg', 'img_feedback': 'mind_game/Feedback/' + player.block_type + player.yes_side + random.choice(['yes', 'no']) + '.jpeg', } def before_next_page(player: Player, timeout_happened): if timeout_happened: for p in player.in_rounds(1, C.NUM_ROUNDS): p.is_dropout = True class EarningPoints(Page): form_model = 'player' form_fields = ['instruction_comment', 'is_dropout'] timeout_seconds = 60*5 def is_displayed(player: Player): return player.round_number == 1 def vars_for_template(player: Player): if player.yes_side == 'right': yes_side = 'right' no_side = 'left' img_choice = "mind_game/Choice/ChoicePage_yesRight.jpeg" else: yes_side = 'left' no_side = 'right' img_choice = "mind_game/Choice/ChoicePage_yesLeft.jpeg" return{ 'yes_side': yes_side, 'no_side': no_side, 'img_choice': img_choice, 'img_feedback': 'mind_game/Feedback/FeedbackPage_' + random.choice(['yes', 'no']) + 'Chosen.jpeg' } def before_next_page(player: Player, timeout_happened): if timeout_happened: for p in player.in_rounds(1, C.NUM_ROUNDS): p.is_dropout = True for p in player.in_rounds(1, C.NUM_ROUNDS): p.attention_check = 'PREDICT' in player.instruction_comment p.instruction_comment = player.instruction_comment class DoYourBest(Page): form_model = 'player' form_fields = ['is_dropout'] timeout_seconds = 60*5 def is_displayed(player: Player): return player.round_number == 1 def before_next_page(player: Player, timeout_happened): if timeout_happened: for p in player.in_rounds(1, C.NUM_ROUNDS): p.is_dropout = True class StartingQuestions(Page): form_model = 'player' form_fields = ['starting_question1', 'starting_question2', 'starting_question3', 'is_dropout' ] timeout_seconds = 60*7 def is_displayed(player: Player): return player.round_number == 1 def before_next_page(player: Player, timeout_happened): if timeout_happened: for p in player.in_rounds(1, C.NUM_ROUNDS): p.is_dropout = True ans1 = player.starting_question1 ans2 = not player.starting_question2 ans3 = player.starting_question3 curr_timestamp = dt.now().timestamp() time_difference = get_time_taken(curr_timestamp, player.participant.time_started_utc) for p in player.in_rounds(1, C.NUM_ROUNDS): p.starting_question1 = ans1 p.starting_question2 = ans2 p.starting_question3 = ans3 p.instruction_time_taken_min = time_difference class ThinkNumber(Page): form_model = 'player' form_fields = ['rt_think_ms'] timeout_seconds = 60 def before_next_page(player: Player, timeout_happened): if timeout_happened: for p in player.in_rounds(1, C.NUM_ROUNDS): p.is_dropout = True class Choice(Page): form_model = 'player' form_fields = ['chose_yes', 'rt_choice_ms', 'die_outcome'] timeout_seconds = 60 def vars_for_template(player: Player): if player.yes_side == "right": on_right = "Yes" on_left = "No" else: on_right = "No" on_left = "Yes" return { 'right': on_right, 'left': on_left, # 'generated_number': player.die_outcome, 'high_points': player.high_points, 'low_points': player.low_points } def before_next_page(player: Player, timeout_happened): if timeout_happened: for p in player.in_rounds(1, C.NUM_ROUNDS): p.is_dropout = True class DropOut(Page): form_model = 'player' form_fields = ['total_time_taken_min'] def is_displayed(player: Player): if player.is_dropout: end_time = dt.now().strftime('%Y-%m-%d %H:%M:%S') curr_timestamp = dt.now().timestamp() time_difference = get_time_taken(curr_timestamp, player.participant.time_started_utc) for p_r in player.in_rounds(1, C.NUM_ROUNDS): p_r.total_time_taken_min = time_difference p_r.time_ended_utc = end_time return player.is_dropout class Feedback(Page): form_model = 'player' timeout_seconds = 0.75 def vars_for_template(player: Player): if player.yes_side == "right": on_right = player.high_points on_left = player.low_points text_right = "Yes" text_left = "No" else: on_right = player.low_points on_left = player.high_points text_right = "No" text_left = "Yes" if player.chose_yes: chosen_ans = "Yes" chosen_points = player.high_points player.trial_points = chosen_points else: chosen_ans = "No" chosen_points = player.low_points player.trial_points = chosen_points return { 'right': on_right, 'left': on_left, 'text_right': text_right, 'text_left': text_left, 'chosen_ans': chosen_ans, 'chosen_points': chosen_points } class EndBlock(Page): timeout_seconds = 60 def js_vars(player: Player): return { 'start_utc': player.participant.time_started_utc } def vars_for_template(player: Player): curr_block = player.block_num curr_round = player.round_number block_points = 0 block_yes = 0 for p_r in player.in_rounds(1, curr_round): if p_r.block_num == curr_block: block_points += p_r.trial_points block_yes += p_r.chose_yes for p_r in player.in_rounds(1, curr_round): if p_r.block_num == curr_block: p_r.block_points = block_points p_r.block_yes_ratio = round(block_yes/C.BLOCK_ROUNDS, 2) if curr_block == 1: game = 'first' elif curr_block == 2: game = 'second' return {'game': game, 'high_points': player.in_round(player.round_number+1).high_points, 'low_points': player.in_round(player.round_number+1).low_points } def is_displayed(player: Player): if player.round_number == C.NUM_ROUNDS: curr_block = player.block_num curr_round = player.round_number block_points = 0 block_yes = 0 for p_r in player.in_rounds(1, curr_round): if p_r.block_num == curr_block: block_points += p_r.trial_points block_yes += p_r.chose_yes for p_r in player.in_rounds(1, curr_round): if p_r.block_num == curr_block: p_r.block_points = block_points p_r.block_yes_ratio = round(block_yes/C.BLOCK_ROUNDS, 2) return player.round_number < C.NUM_ROUNDS and (player.round_number % C.BLOCK_ROUNDS) + 1 == 1 def before_next_page(player: Player, timeout_happened): if timeout_happened: for p in player.in_rounds(1, C.NUM_ROUNDS): p.is_dropout = True class EndPage(Page): timeout_seconds = 60 def is_displayed(player: Player): return player.round_number == C.NUM_ROUNDS def js_vars(player: Player): return { 'start_utc': player.participant.time_started_utc, 'completion': player.session.prolific_completion_url } def vars_for_template(player: Player): total_points = 0 total_yes = 0 for p_r in player.in_rounds(1, C.NUM_ROUNDS): total_points += p_r.trial_points total_yes += p_r.chose_yes bonus = (total_points - C.bonus_exchange) / C.bonus_exchange if bonus < 0.2: bonus = 0.2 elif bonus > 2: bonus = 2 for p_r in player.in_rounds(1, C.NUM_ROUNDS): p_r.total_points = total_points p_r.total_yes_ratio = round(total_yes/C.NUM_ROUNDS, 2) p_r.bonus = cu(bonus) # p_r.payoff = C.show_up_fee + p_r.bonus # p_r.total_time_taken_min = player.total_time_taken_min player.payoff = player.bonus return {'bonus': player.bonus} def before_next_page(player: Player, timeout_happened): end_time = dt.now().strftime('%Y-%m-%d %H:%M:%S') curr_timestamp = dt.now().timestamp() time_difference = get_time_taken(curr_timestamp, player.participant.time_started_utc) player.participant.finished = True for p_r in player.in_rounds(1, C.NUM_ROUNDS): p_r.total_time_taken_min = time_difference p_r.time_ended_utc = end_time p_r.finished = True class CloseWindow(Page): def is_displayed(player: Player): return player.round_number == C.NUM_ROUNDS page_sequence = [MobileCheck, DropOut, Consent, DropOut, Welcome, DropOut, EarningPoints, DropOut, DoYourBest, DropOut, StartingQuestions, DropOut, ThinkNumber, DropOut, Choice, DropOut, Feedback, EndBlock, DropOut, EndPage, CloseWindow]