from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range ) import random as random import itertools import json as json import pandas as pd import numpy as np author = 'Your name here' doc = """ Main part of the experiment """ def load_math_options(): with open(f'AttentionAllocation/static/Addition_Increasing_Difficulty/task_list_5_rounds.json', 'r') as read_file: task_list = json.load(read_file) return task_list def load_pwd_tasks(type='LinearDifficulty'): with open(f'AttentionAllocation/static/Code_{type}/counting_tasks.json', 'r') as read_file: task_list = json.load(read_file) return task_list def make_math_ability_field(): return models.IntegerField( blank=True, label='', ) def make_pwd_ability_field(): return models.IntegerField( blank=True, label='', ) class Constants(BaseConstants): name_in_url = '9H32djdqaaBa' players_per_group = None num_rounds = 5 num_comp = int(num_rounds/2) num_participants = 10 #ordering = ['BA','AB'] # Load tasks from JSON addition_task = load_math_options() #linear_task = load_pwd_tasks(type='LinearDifficulty') convex_task = load_pwd_tasks(type='DecreasingDifficulty') # Payoff Constants payoff_incorrect = c(0) pwd_rate = 10 pwd_piecerate = c(pwd_rate) math_rate = 10 math_piecerate = c(math_rate) task = 'Baseline' production_profile = ['Convex']#['Linear','Convex'] timeout = 40 num_pwd_tasks = 10 num_math_tasks = 10 likert_scale_agree = [(1, 'I completely disagree'), (2, 'I somewhat disagree'), (3, 'Neither agree nor disagree'), (4, 'I somewhat agree'), (5, 'I completely agree')] class Subsession(BaseSubsession): def creating_session(self): ##### 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 self.get_players(): # Establishes task ordering & Incentives for Math Task if self.round_number == 1: try: p.participant.vars['production_profile'] except KeyError: #p.participant.vars['task_treatment'] = random.choice(Constants.task_treatment) p.participant.vars['production_profile'] = next(profiles) p.task = Constants.task p.task_option_this_round = (self.round_number - 1) p.production_profile = p.participant.vars['production_profile'] class Group(BaseGroup): pass class Player(BasePlayer): # Task task = models.StringField() # Production function type production_profile = models.StringField() # Check correct answers math_correct = models.IntegerField(initial=0) # Check correct answers pwd_correct = models.IntegerField(initial=0) # Optimal Score #optimal_score = models.FloatField() # Optimal Switch Point #optimal_switch_point = models.IntegerField() # Most important timer math_time = models.FloatField() pwd_time = models.FloatField() ###### sequence task ############################ math_1 = make_math_ability_field() math_2 = make_math_ability_field() math_3 = make_math_ability_field() math_4 = make_math_ability_field() math_5 = make_math_ability_field() math_6 = make_math_ability_field() math_7 = make_math_ability_field() math_8 = make_math_ability_field() math_9 = make_math_ability_field() math_10 = make_math_ability_field() ### Cumulated Time needed for one sequence ################ math_1_time = models.FloatField(blank=True) math_2_time = models.FloatField(blank=True) math_3_time = models.FloatField(blank=True) math_4_time = models.FloatField(blank=True) math_5_time = models.FloatField(blank=True) math_6_time = models.FloatField(blank=True) math_7_time = models.FloatField(blank=True) math_8_time = models.FloatField(blank=True) math_9_time = models.FloatField(blank=True) math_10_time = models.FloatField(blank=True) ### Number tries needed to find the correct input math_1_tries = models.IntegerField(blank=True) math_2_tries = models.IntegerField(blank=True) math_3_tries = models.IntegerField(blank=True) math_4_tries = models.IntegerField(blank=True) math_5_tries = models.IntegerField(blank=True) math_6_tries = models.IntegerField(blank=True) math_7_tries = models.IntegerField(blank=True) math_8_tries = models.IntegerField(blank=True) math_9_tries = models.IntegerField(blank=True) math_10_tries = models.IntegerField(blank=True) # Timer timer_task1 = models.FloatField() new_timer_task1 = models.FloatField() timer_task2 = models.FloatField() new_timer_task2 = models.FloatField() total_time = models.FloatField() # Prompt Counter prompt_counter = models.IntegerField() # Switch Counter switch_counter = models.IntegerField() # Switch Timer switch_time_1 = models.FloatField(blank=True) switch_time_2 = models.FloatField(blank=True) switch_time_3 = models.FloatField(blank=True) switch_time_4 = models.FloatField(blank=True) switch_time_5 = models.FloatField(blank=True) switch_time_6 = models.FloatField(blank=True) switch_time_7 = models.FloatField(blank=True) switch_time_8 = models.FloatField(blank=True) switch_time_9 = models.FloatField(blank=True) switch_time_10 = models.FloatField(blank=True) # Switch Timer switch_1_math_perf = models.IntegerField(blank=True) switch_2_math_perf = models.IntegerField(blank=True) switch_3_math_perf = models.IntegerField(blank=True) switch_4_math_perf = models.IntegerField(blank=True) switch_5_math_perf = models.IntegerField(blank=True) switch_6_math_perf = models.IntegerField(blank=True) switch_7_math_perf = models.IntegerField(blank=True) switch_8_math_perf = models.IntegerField(blank=True) switch_9_math_perf = models.IntegerField(blank=True) switch_10_math_perf = models.IntegerField(blank=True) switch_1_pwd_perf = models.IntegerField(blank=True) switch_2_pwd_perf = models.IntegerField(blank=True) switch_3_pwd_perf = models.IntegerField(blank=True) switch_4_pwd_perf = models.IntegerField(blank=True) switch_5_pwd_perf = models.IntegerField(blank=True) switch_6_pwd_perf = models.IntegerField(blank=True) switch_7_pwd_perf = models.IntegerField(blank=True) switch_8_pwd_perf = models.IntegerField(blank=True) switch_9_pwd_perf = models.IntegerField(blank=True) switch_10_pwd_perf = models.IntegerField(blank=True) # Time Spent Main_Instructions_time_spent = models.FloatField(blank=True) Task_time_spent = models.FloatField(blank=True) Exit_Q1_time_spent = models.FloatField(blank=True) Exit_Q2_time_spent = models.FloatField(blank=True) # Warnings Main_Instructions_warnings = models.IntegerField() StartRound_warnings = models.IntegerField() Task_warnings = models.IntegerField() Exit_Q1_warnings = models.IntegerField() Exit_Q2_warnings = models.IntegerField() task_option_this_round = models.IntegerField() ## Control questions control_q1 = models.PositiveIntegerField(choices=[i * 10 for i in range(11)],initial=None) control_q2 = models.PositiveIntegerField(choices=[i * 10 for i in range(11)],initial=None) control_q3 = models.PositiveIntegerField(choices=[i * 10 for i in range(11)],initial=None) def control_q1_error_message(self,value): """Customized error message for control question 1.""" cond = (value != 30) if cond: return 'Your input is incorrect. Please try again!' def control_q2_error_message(self,value): """Customized error message for control question 2.""" cond = (value != 60) if cond: return 'Your input is incorrect. Please try again!' def control_q3_error_message(self,value): """Customized error message for control question 3.""" cond = (value != 90) if cond: return 'Your input is incorrect. Please try again!' def q_subjective_optimality_1_error_message(self,value): """Customized error message for subjective optimality.""" if value == None: return 'Your input is incorrect. Please try again!' # Questions q_subjective_sophistication_1 = models.PositiveIntegerField(choices=Constants.likert_scale_agree, label='I chose a good allocation of my time between the two tasks.', widget=widgets.RadioSelectHorizontal()) q_subjective_optimality_1 = models.IntegerField(choices=range(1,Constants.timeout+1), widget=widgets.RadioSelect) # Payoffs and Piecerates payoff_pwd = models.CurrencyField() payoff_math = models.CurrencyField() ### 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() # 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) def set_payoff(self): if self.production_profile == 'Linear': pwd_solutions = Constants.linear_task[self.task_option_this_round][0] elif self.production_profile == 'Convex': pwd_solutions = Constants.convex_task[self.task_option_this_round][0] pwd_answers = [ self.pwd_1, self.pwd_2, self.pwd_3, self.pwd_4, self.pwd_5, self.pwd_6, self.pwd_7, self.pwd_8, self.pwd_9, self.pwd_10, ] self.pwd_correct = sum([1 for x,y in zip(pwd_answers,pwd_solutions) if x == y]) self.payoff_pwd = self.pwd_correct * Constants.pwd_piecerate math_solutions = Constants.addition_task[self.task_option_this_round][1] math_answers = [ self.math_1, self.math_2, self.math_3, self.math_4, self.math_5, self.math_6, self.math_7, self.math_8, self.math_9, self.math_10, ] self.math_correct = sum([1 for x,y in zip(math_answers,math_solutions) if x == y]) self.payoff_math = self.math_correct * Constants.math_piecerate ### Compute total payoff self.payoff = self.payoff_math + self.payoff_pwd # Put it into the ether # Pwd self.participant.vars[f'base_pwd_round_{self.round_number}'] = self.pwd_correct self.participant.vars[f'base_pwd_payoff_round_{self.round_number}'] = self.payoff_pwd # Math self.participant.vars[f'base_math_round_{self.round_number}'] = self.math_correct self.participant.vars[f'base_math_payoff_round_{self.round_number}'] = self.payoff_math def comp_opt_alloc(self): p_vars = self.participant.vars # Simplifies code later math_cols = ['math_t_1', 'math_t_2', 'math_t_3', 'math_t_4', 'math_t_5', 'math_t_6', 'math_t_7', 'math_t_8','math_t_9','math_t_10'] mem_cols = ['pwd_t_1' ,'pwd_t_2', 'pwd_t_3', 'pwd_t_4', 'pwd_t_5', 'pwd_t_6', 'pwd_t_7', 'pwd_t_8', 'pwd_t_9', 'pwd_t_10'] # Create df of only pwd data pwddict={col:p_vars[col] for col in pwd_cols} df_pwd = pd.DataFrame(pwddict) # Create df of only math data mathdict={col:p_vars[col] for col in math_cols} df_math = pd.DataFrame(mathdict) # Create respective production function rslt_pwd = get_production_func(df_pwd,'pwd') rslt_math = get_production_func(df_math,'math') # Find optimal allocation df_both = pd.DataFrame({'pwd': rslt_mem, 'math': rslt_math}) get_optimal_allocation(df_both,p_vars) def write_math_time(self): p_vars = self.participant.vars p_vars['last_round_math_time'] = self.math_time p_vars['gen_num_pwd_tasks'] = Constants.num_pwd_tasks p_vars['gen_num_math_tasks'] = Constants.num_math_tasks p_vars['gen_num_rounds'] = Constants.num_rounds