import itertools import json as json import math as math import random as random import pandas as pd from otree.api import * author = 'Your name here' doc = """Subs can only work on the math task""" def load_pwd_tasks(type='LinearDifficulty'): with open(f'AbilityCode/static/Code_{type}/counting_tasks.json', 'r') as read_file: task_list = json.load(read_file) return task_list def make_math_field(): return models.IntegerField( widget=widgets.RadioSelect, blank=True, label='', ) def make_pwd_ability_field(): return models.IntegerField( blank=True, label='', ) class Constants(BaseConstants): name_in_url = '0h254gdG2aBa' players_per_group = None num_rounds = 8 num_participants = 10 timeout = 40 # Payoff Constants payoff_incorrect = cu(0) pwd_rate = 10 pwd_piecerate = cu(pwd_rate) time_rate = 5 time_piecerate = cu(time_rate) example_time = 32 task = 'Codes' production_profile = ['Convex'] # ['Linear','Convex'] num_pwd_tasks = 10 # Load tasks # Load math tasks linear_task = load_pwd_tasks(type='LinearDifficulty') convex_task = load_pwd_tasks(type='DecreasingDifficulty') class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): # Selected Task task = models.StringField() # Production profile production_profile = models.StringField() ### Cumulated Time needed for one math task ################ pwd_1_time = models.FloatField(blank=True) pwd_2_time = models.FloatField(blank=True) pwd_3_time = models.FloatField(blank=True) pwd_4_time = models.FloatField(blank=True) pwd_5_time = models.FloatField(blank=True) pwd_6_time = models.FloatField(blank=True) pwd_7_time = models.FloatField(blank=True) pwd_8_time = models.FloatField(blank=True) pwd_9_time = models.FloatField(blank=True) pwd_10_time = models.FloatField(blank=True) pwd_1 = make_pwd_ability_field() pwd_2 = make_pwd_ability_field() pwd_3 = make_pwd_ability_field() pwd_4 = make_pwd_ability_field() pwd_5 = make_pwd_ability_field() pwd_6 = make_pwd_ability_field() pwd_7 = make_pwd_ability_field() pwd_8 = make_pwd_ability_field() pwd_9 = make_pwd_ability_field() pwd_10 = make_pwd_ability_field() # Check correct answers pwd_correct = models.IntegerField(initial=0) # Number of tries pwd_1_tries = models.IntegerField(blank=True) pwd_2_tries = models.IntegerField(blank=True) pwd_3_tries = models.IntegerField(blank=True) pwd_4_tries = models.IntegerField(blank=True) pwd_5_tries = models.IntegerField(blank=True) pwd_6_tries = models.IntegerField(blank=True) pwd_7_tries = models.IntegerField(blank=True) pwd_8_tries = models.IntegerField(blank=True) pwd_9_tries = models.IntegerField(blank=True) pwd_10_tries = models.IntegerField(blank=True) # Number of toggles pwd_1_toggles = models.IntegerField(blank=True) pwd_2_toggles = models.IntegerField(blank=True) pwd_3_toggles = models.IntegerField(blank=True) pwd_4_toggles = models.IntegerField(blank=True) pwd_5_toggles = models.IntegerField(blank=True) pwd_6_toggles = models.IntegerField(blank=True) pwd_7_toggles = models.IntegerField(blank=True) pwd_8_toggles = models.IntegerField(blank=True) pwd_9_toggles = models.IntegerField(blank=True) pwd_10_toggles = models.IntegerField(blank=True) # Timer timer_task1 = models.FloatField() new_timer_task1 = models.FloatField() pwd_time = models.FloatField() total_time = models.FloatField() # Time spent Code_Instructions_time_spent = models.FloatField(blank=True) CodeTask_time_spent = models.FloatField(blank=True) # Warnings Code_Instructions_warnings = models.IntegerField() CodeTask_warnings = models.IntegerField() StartRound_warnings = models.IntegerField() # Selected Task task_option_this_round = models.IntegerField() payoff_pwd = models.CurrencyField() payoff_time = models.CurrencyField() # FUNCTIONS def creating_session(subsession: Subsession): ##### This code is for obtaining a balanced session ############ task_profiles = Constants.production_profile.copy() # shuffle the list random.shuffle(task_profiles) # make a cycle out of the shuffled list profiles = itertools.cycle(task_profiles) for p in subsession.get_players(): if subsession.round_number == 1: p.participant.vars['pwd_piecerate'] = Constants.pwd_piecerate p.participant.vars['production_profile'] = next(profiles) # fixed on subject and session level p.task_option_this_round = subsession.round_number - 1 p.task = Constants.task p.production_profile = p.participant.vars['production_profile'] def set_payoff(player: Player): if player.production_profile == 'Linear': solutions = Constants.linear_task[player.task_option_this_round][0] elif player.production_profile == 'Convex': solutions = Constants.convex_task[player.task_option_this_round][0] pwd_1 = player.field_maybe_none('pwd_1') pwd_2 = player.field_maybe_none('pwd_2') pwd_3 = player.field_maybe_none('pwd_3') pwd_4 = player.field_maybe_none('pwd_4') pwd_5 = player.field_maybe_none('pwd_5') pwd_6 = player.field_maybe_none('pwd_6') pwd_7 = player.field_maybe_none('pwd_7') pwd_8 = player.field_maybe_none('pwd_8') pwd_9 = player.field_maybe_none('pwd_9') pwd_10 = player.field_maybe_none('pwd_10') answers = [ pwd_1, pwd_2, pwd_3, pwd_4, pwd_5, pwd_6, pwd_7, pwd_8, pwd_9, pwd_10, ] player.pwd_correct = sum([1 for x, y in zip(answers, solutions) if x == y]) player.payoff_pwd = player.pwd_correct * Constants.pwd_piecerate # Also give money for the time finished earlier # This code is robust to: # a) recorded time being not in the range (0,Timeout) # b) Participants proceeds by error/hacking and gets time bonus without completing all tasks pwd_10_time = player.field_maybe_none('pwd_10_time') if ( pwd_10_time != None and pwd_10_time < Constants.timeout and pwd_10_time > 0 and player.pwd_correct == Constants.num_pwd_tasks ): player.payoff_time = ( Constants.timeout - math.ceil(pwd_10_time) ) * Constants.time_piecerate else: player.payoff_time = 0 * Constants.time_piecerate player.payoff = player.payoff_pwd + player.payoff_time # Put it into the ether player.participant.vars[f'pwd_ability_round_{player.round_number}'] = player.pwd_correct player.participant.vars[f'pwd_ability_payoff_round_{player.round_number}'] = player.payoff def save_data(player: Player): p_vars = player.participant.vars if player.round_number == 1: p_vars[f'pwd_t_1'] = [] p_vars[f'pwd_t_2'] = [] p_vars[f'pwd_t_3'] = [] p_vars[f'pwd_t_4'] = [] p_vars[f'pwd_t_5'] = [] p_vars[f'pwd_t_6'] = [] p_vars[f'pwd_t_7'] = [] p_vars[f'pwd_t_8'] = [] p_vars[f'pwd_t_9'] = [] p_vars[f'pwd_t_10'] = [] elif player.round_number == Constants.num_rounds: p_vars['num_pwd_tasks_pwd_ability'] = Constants.num_pwd_tasks p_vars['num_rounds_pwd_ability'] = Constants.num_rounds else: pass # Put it into the ether p_vars[f'pwd_t_1'].append(player.field_maybe_none('pwd_1_time')) p_vars[f'pwd_t_2'].append(player.field_maybe_none('pwd_2_time')) p_vars[f'pwd_t_3'].append(player.field_maybe_none('pwd_3_time')) p_vars[f'pwd_t_4'].append(player.field_maybe_none('pwd_4_time')) p_vars[f'pwd_t_5'].append(player.field_maybe_none('pwd_5_time')) p_vars[f'pwd_t_6'].append(player.field_maybe_none('pwd_6_time')) p_vars[f'pwd_t_7'].append(player.field_maybe_none('pwd_7_time')) p_vars[f'pwd_t_8'].append(player.field_maybe_none('pwd_8_time')) p_vars[f'pwd_t_9'].append(player.field_maybe_none('pwd_9_time')) p_vars[f'pwd_t_10'].append(player.field_maybe_none('pwd_10_time')) # PAGES class Intro_Page(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 class StartRound(Page): # oTree Timer timer_text = 'Time left until the round starts:' timeout_seconds = 5 @staticmethod def vars_for_template(player: Player): return dict(round_num=player.round_number) @staticmethod def js_vars(player: Player): return dict( page_name='StartRound', ) form_model = 'player' form_fields = ['StartRound_warnings'] class Code_Instructions(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 form_model = 'player' form_fields = ['Code_Instructions_time_spent', 'Code_Instructions_warnings'] @staticmethod def js_vars(player: Player): return dict( page_name='Code_Instructions', ) @staticmethod def vars_for_template(player: Player): example_diff = Constants.timeout - Constants.example_time example_payoff = example_diff * Constants.time_rate full_payoff = Constants.pwd_rate * Constants.num_pwd_tasks return dict( example_diff=example_diff, example_payoff=example_payoff, full_payoff=full_payoff ) class MathMemory(Page): pass class CodeTask(Page): # oTree Timer timer_text = 'Time left in this round:' timeout_seconds = Constants.timeout @staticmethod def before_next_page(player: Player, timeout_happened): set_payoff(player) save_data(player) @staticmethod def vars_for_template(player: Player): p = player if p.production_profile == 'Linear': label = Constants.linear_task[player.round_number - 1][0] elif p.production_profile == 'Convex': label = Constants.convex_task[player.round_number - 1][0] return dict( pwd_1_label=label[0], pwd_2_label=label[1], pwd_3_label=label[2], pwd_4_label=label[3], pwd_5_label=label[4], pwd_6_label=label[5], pwd_7_label=label[6], pwd_8_label=label[7], pwd_9_label=label[8], pwd_10_label=label[9], ) @staticmethod def js_vars(player: Player): p = player if p.production_profile == 'Linear': sol = Constants.linear_task[player.round_number - 1][0] elif p.production_profile == 'Convex': sol = Constants.convex_task[player.round_number - 1][0] return dict( pwd_solutions_array=sol, page_name='CodeTask', num_pwd_tasks=Constants.num_pwd_tasks ) form_model = 'player' form_fields = [ 'timer_task1', 'new_timer_task1', 'pwd_time', 'total_time', 'pwd_1', 'pwd_2', 'pwd_3', 'pwd_4', 'pwd_5', 'pwd_6', 'pwd_7', 'pwd_8', 'pwd_9', 'pwd_10', 'pwd_time', 'pwd_1_time', 'pwd_2_time', 'pwd_3_time', 'pwd_4_time', 'pwd_5_time', 'pwd_6_time', 'pwd_7_time', 'pwd_8_time', 'pwd_9_time', 'pwd_10_time', 'pwd_1_tries', 'pwd_2_tries', 'pwd_3_tries', 'pwd_4_tries', 'pwd_5_tries', 'pwd_6_tries', 'pwd_7_tries', 'pwd_8_tries', 'pwd_9_tries', 'pwd_10_tries', 'pwd_1_toggles', 'pwd_2_toggles', 'pwd_3_toggles', 'pwd_4_toggles', 'pwd_5_toggles', 'pwd_6_toggles', 'pwd_7_toggles', 'pwd_8_toggles', 'pwd_9_toggles', 'pwd_10_toggles', 'CodeTask_time_spent', 'CodeTask_warnings', ] page_sequence = [Code_Instructions, StartRound, CodeTask]