from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range ) import random import itertools author = 'Thomas Graeber' #Revised and debugged by Tsahi Halyo doc = """ Subjective uncertainty in risk """ class Constants(BaseConstants): name_in_url = 'ambiguity' players_per_group = None decisions = { 'probabilities': [5, 10, 25, 35, 50, 65, 75, 90, 95], 'amounts': [15,16,17,18,19,20,21,22,23,24,25], } rocl_probability = 0 combinations = len(decisions['probabilities']) * len(decisions['amounts']) num_rounds_gains = 12 num_inconsis = 2 num_rounds_losses = 0 #Needs to be num_rounds_gains+1+num_rounds_losses +1 if we have repetitions for inconsistency analysis, otherwise, just num_rounds_gains+numrounds num_rounds = num_rounds_gains + num_inconsis class Subsession(BaseSubsession): def creating_session(self): if self.round_number == 1: for p in self.get_players(): p.participant.vars['failed_comprehension'] = False p.participant.vars['lists_paying_round'] = random.randint(1, Constants.num_rounds) p.participant.vars['gain_list_sequence'] = random.sample(range(0, Constants.combinations), Constants.num_rounds_gains) repeat = [p.participant.vars['gain_list_sequence'][i] for i in range(0, Constants.num_inconsis)] p.participant.vars['gain_list_sequence'].extend(repeat) p.participant.vars['loss_list_sequence'] = random.sample(range(0, Constants.combinations), Constants.num_rounds_losses) p.participant.vars['losses_first'] = random.choice([True, False]) if p.participant.vars['losses_first']: p.participant.vars['list_sequence'] = p.participant.vars['gain_list_sequence'] + p.participant.vars['loss_list_sequence'] else: p.participant.vars['list_sequence'] = p.participant.vars['loss_list_sequence'] + p.participant.vars['gain_list_sequence'] for p in self.get_players(): p.losses_first = p.participant.vars['losses_first'] p.set_treatment() p.set_current_task_number() if p.round_number == p.participant.vars['lists_paying_round']: p.on_paying_round = True else: p.on_paying_round = False class Group(BaseGroup): pass class Player(BasePlayer): ################################# #FOR BETA TESTING treatment = models.IntegerField(label="Choose which treatment option you prefer for the next round:", choices = [[0,'Baseline'], [1,'Ambiguity'], [2, 'Complex Amounts'], [3, 'Complex Probabilities']]) def set_treatment(self): self.is_rocl = True self.is_complex_amt = False self.is_complex_prob = False ################################## task_number = models.IntegerField() task_identifier = models.StringField() switching_point_1 = models.FloatField() # switching_point_2 = models.FloatField() on_paying_round = models.BooleanField(initial=False) lottery_amount = models.IntegerField() lottery_probability = models.IntegerField() lottery_prob_loss = models.IntegerField() is_rocl = models.BooleanField() is_complex_prob = models.BooleanField() is_complex_amt = models.BooleanField() complex_prob = models.StringField() complex_amt = models.StringField() type = models.StringField() endowment = models.FloatField() rocl_probability = models.IntegerField() # Field which is 0 if always Option B, 1 if switching point, 2 if never option B indicator_never_always_switcher = models.IntegerField() switching_point = models.FloatField() lottery_identifier = models.StringField() confidence = models.FloatField() #confidence_upper_bound = models.FloatField() #confidence_lower_bound = models.FloatField() losses_first = models.BooleanField() random_payoff = models.CurrencyField() random_right_side_amount = models.CurrencyField() attention_check = models.StringField() failed_comprehension = models.BooleanField(initial=False) qn_lottery_got_wrong = models.BooleanField(initial=False) qn_list_got_wrong = models.BooleanField(initial=False) qn_confidence_got_wrong = models.BooleanField(initial=False) qn_lottery = models.IntegerField( choices=[ [0, 'It is possible that I get paid both $15 and $5, i.e., I may receive a total amount of $20 from this lottery.'], [1, 'I receive EITHER $15 OR $5 from this lottery.'], [0, 'It is possible that I receive no money from this lottery.'], ], widget=widgets.RadioSelect, blank=False, label="" ) qn_list = models.IntegerField( choices=[ [0, 'This person indicated that the lottery is worth more to them than $9.'], [0, 'This person indicated that the lottery is worth between $3 and $7 to them. '], [1, 'This person indicated that the lottery is worth between $8 and $9 to them.'], ], widget=widgets.RadioSelect, blank=False, label="" ) qn_confidence = models.FloatField( blank=False, label="" ) def set_complex_prob(self): b = random.randint(1,10) a = random.randint(2,7) d = int(a*b - self.lottery_probability) if d>= 0: self.complex_prob = '(' + str(a) + ' x ' + str(b) + ') - ' + str(d) else: self.complex_prob = '(' + str(a) + ' x ' + str(b) + ') + ' + str(abs(d)) def set_complex_amount(self): b = random.randint(1,10) a = random.randint(2,7) d = int(a*b - self.lottery_amount) if d>= 0: self.complex_amt = '(' + str(a) + ' x ' + str(b) + ') - ' + str(d) else: self.complex_amt = '(' + str(a) + ' x ' + str(b) + ') + ' + str(abs(d)) def set_current_task_number(self): self.task_number = self.participant.vars['list_sequence'][self.round_number-1] task_list = [] #Cartesian product l = itertools.product( Constants.decisions['probabilities'], Constants.decisions['amounts'], ) task_list.extend(l) tup = task_list[self.task_number] self.lottery_probability = tup[0] self.lottery_prob_loss = 100 - self.lottery_probability # if self.losses_first and self.round_number <= Constants.num_rounds_losses or not self.losses_first and self.round_number > Constants.num_rounds_gains: # self.lottery_amount= -tup[1] # else: # self.lottery_amount = tup[1] self.lottery_amount = tup[1] if self.is_rocl: self.type = 'rocl' self.lottery_probability = random.choice([x for x in Constants.decisions['probabilities'] if x not in [1,99]]) elif self.is_complex_prob: self.type = 'complex_prob' elif self.is_complex_amt: self.type = 'complex_amt' else: self.type = 'list' self.task_identifier = 'lottery_prob{}_am{}_rocl{}_cp{}_ca{}'.format( self.lottery_probability, self.lottery_amount, self.is_rocl, self.is_complex_prob, self.is_complex_amt ) # setattr(self, 'lottery_amount', random.choice(Constants.decisions[self.task_number]['y_list'])) # setattr(self, 'task_identifier', Constants.decisions[self.task_number]['name']) if self.lottery_amount > 0: self.endowment = 0 else: self.endowment = abs(self.lottery_amount) #Why the generators? def frange(self, start, stop, step): i = start while i < stop: if isinstance(i, int): yield i else: yield round(i, 2) i += step def right_side_amounts1(self): if self.lottery_amount > 0: #start should be 0 instead of 1, speak to Ben and Toto lst = self.frange(1, self.lottery_amount + 1, 1) else: #end should be 1 instead of 0, speak to Ben and Toto lst = self.frange(self.lottery_amount, 0, 1) l = enumerate(lst, 1) count, amount = zip(*l) absamount = (abs(x) for x in amount) lst = zip(count, amount, absamount) return list(lst) def current_lottery(self): if self.is_rocl: lott = [('X', self.lottery_amount, abs(self.lottery_amount)), ('100% - X', 0, 0)] elif self.is_complex_prob: self.set_complex_prob() lott = [(self.complex_prob, self.lottery_amount, abs(self.lottery_amount)), ('100 - (' + self.complex_prob + ')', 0, 0)] elif self.is_complex_amt: self.set_complex_amount() lott = [(self.lottery_probability, self.complex_amt, abs(self.lottery_amount)), (100 - self.lottery_probability, 0, 0)] else: lott = [(self.lottery_probability, self.lottery_amount, abs(self.lottery_amount)), (100 - self.lottery_probability, 0, 0)] return lott def rocl_range(self): rr = [self.lottery_probability - 5, self.lottery_probability + 5] return rr def range_bounds(self): if self.lottery_amount > 0: bounds = [0, self.lottery_amount] elif self.lottery_amount < 0: bounds = [self.lottery_amount, 0] return bounds def table_length1(self): return abs(self.lottery_amount) + 1 def set_switching_point_and_indicator(self): if self.lottery_amount > 0: if self.switching_point == 9999: self.switching_point = self.lottery_amount + 1 self.indicator_never_always_switcher = 2 elif self.switching_point == 0: self.indicator_never_always_switcher = 0 else: self.indicator_never_always_switcher = 1 elif self.lottery_amount < 0: if self.switching_point == 9999: self.switching_point = 1 self.indicator_never_always_switcher = 2 elif self.switching_point == self.lottery_amount: self.indicator_never_always_switcher = 0 else: self.indicator_never_always_switcher = 1 def set_payoffs(self): random_right_side_amount = random.choice(self.right_side_amounts1())[1] possible_payoff = 0 if random_right_side_amount >= self.switching_point: possible_payoff = c(random_right_side_amount + self.endowment) else: if not self.is_rocl: prob = self.lottery_probability else: self.rocl_probability = random.randint(self.rocl_range()[0], self.rocl_range()[1]) prob = self.rocl_probability outcome = random.random() < (prob / 100) possible_payoff = c(outcome * self.lottery_amount) + self.endowment self.random_payoff = possible_payoff if self.on_paying_round: if 'possible_payoffs' in self.participant.vars: self.participant.vars['possible_payoffs'].append(round(possible_payoff, 2)) else: self.participant.vars['possible_payoffs'] = [round(possible_payoff, 2)]