from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range, ) import random from random import randint author = 'Nanyin Yang' doc = """ In this app, subjects play 20 rounds of observable condition, continuously. And the doctor's altruism level is induced. """ class Constants(BaseConstants): name_in_url = 'reputation_altruism' players_per_group = 2 num_rounds = 20 starting_round = 1 expert_q1a = 3 expert_q2a = 1 expert_q3a_100 = 100 expert_q3a_60 = 60 expert_q3a_20 = 20 expert_q3a_0 = 0 expert_q4a = 1 expert_q5a = 2 client_q1a = 3 client_q2a_active = 2 client_q2a_passive = 1 #exchange_rate = 0.005 exchange_rate = 0.1 initial_earning = 10 ## round when the player have been selected to get paid round_selected = randint(1, num_rounds) # # Expert's induced preferences # expert_altruism = random.choice([0, 0.3, 0.7, 1]) class Subsession(BaseSubsession): #pass def creating_session(self): self.group_randomly(fixed_id_in_group=True) if self.round_number == 1: for player in self.get_players(): if player.id_in_group == 1: player.expert_altruism = random.choice([0, 0.2, 0.6, 1]) player.participant.vars['altruism'] = player.expert_altruism player.altruism_contract = int(player.expert_altruism*100) player.participant.vars['altruism_contract'] = player.altruism_contract elif player.id_in_group == 2: player.activeness = random.choices([0, 1], weights = (30, 70))[0] # this function returns a list with length of 1, so I have to take [0] player.participant.vars['activeness'] = player.activeness else: for player in self.get_players(): if player.id_in_group == 1: player.expert_altruism = player.participant.vars['altruism'] player.altruism_contract = player.participant.vars['altruism_contract'] elif player.id_in_group == 2: player.activeness = player.participant.vars['activeness'] def set_ratings_subsession(self): for g in self.get_groups(): g.set_ratings_for_client() class Group(BaseGroup): #pass def set_payoffs(self): player1 = self.get_player_by_role('player1') player2 = self.get_player_by_role('player2') player2.set_final_treatment() player1.treatment_final = player2.treatment_final player1.treatment_final_letter = player2.treatment_final_letter player2.display_activeness() player2.display_active_cost() for p in self.get_players(): p.set_payoff() def set_problems_and_signals(self): player1 = self.get_player_by_role('player1') player2 = self.get_player_by_role('player2') for p in self.get_players(): player2.set_problem() player1.problem = player2.problem player1.problem_letter = player2.problem_letter player1.activeness = player2.activeness player2.get_signal() player2.set_letter_signal() def set_ratings(self): player1 = self.get_player_by_role('player1') player2 = self.get_player_by_role('player2') player1.give_rating = player2.give_rating if self.round_number == Constants.starting_round: player1.average_rating = player1.give_rating player2.average_rating = player1.average_rating #else: elif self.round_number > Constants.starting_round: prev_player = player1.in_round(player1.round_number - 1) player1.average_rating = (player1.give_rating+prev_player.average_rating*(player1.round_number-1))/(player1.round_number) player2.average_rating = player1.average_rating def set_ratings_for_client(self): player1 = self.get_player_by_role('player1') player2 = self.get_player_by_role('player2') if self.round_number > Constants.starting_round: prev_player = player2.in_round(player2.round_number - 1) player2.view_rating = prev_player.average_rating # For displaying in the payoff page # def set_final_treatments(self): # player1 = self.get_player_by_role('player1') # player2 = self.get_player_by_role('player2') # for p in self.get_players(): # player2.set_final_treatment() # player1.treatment_final = player2.treatment_final # player1.treatment_final_letter = player2.treatment_final_letter # def set_private_signals(self): # for p in self.get_players(): # if p.id_in_group == 2: # p.set_private_signal() class Player(BasePlayer): final_payment = models.FloatField(initial=0.0, min=0) problem = models.IntegerField(choices = [[0, "A"],[1, "B"]]) problem_letter = models.StringField() private_signal = models.IntegerField() private_signal_letter = models.StringField(blank = True) diagnosis_letter = models.StringField(blank = True) recommend_soln_letter = models.StringField(blank = True) treatment_choice = models.IntegerField(widget=widgets.RadioSelect, choices=[ [0, 'Solution A*'], [1, 'Solution B*'], ], blank=True) treatment_final = models.IntegerField() treatment_final_letter = models.StringField() # belief elicitation for clients belief_precision = models.FloatField( choices=[ [0.5, "50%"], [0.6, "60%"], [0.7, "70%"], [0.8, "80%"], [0.9, "90%"], [1, "100%"], ], blank=True ) # expert's choice of precision precision_choice = models.FloatField( choices = [ [0.5, "50%"], [0.6, "60%"], [0.7, "70%"], [0.8, "80%"], [0.9, "90%"], [1, "100%"], ], widget = widgets.RadioSelectHorizontal, blank = True ) diagnostic_result = models.IntegerField() diagnostic_cost = models.IntegerField() # Expert's altruism level expert_altruism = models.FloatField() altruism_contract = models.IntegerField() altruism_payoff = models.IntegerField() # actual income from gamma*U^p payment = models.IntegerField() payment_temp = models.IntegerField() # New version: only 2 WTP: active1 = models.IntegerField( choices = [ [1, 'Make own decision & Pay 10 tokens'], [2, 'Follow the expert & Pay 0 tokens'], ], widget=widgets.RadioSelectHorizontal, label='Decision 1' ) active2 = models.IntegerField( choices = [ [1, 'Make own decision & Pay 0 tokens'], [2, 'Follow the expert & Pay 10 tokens'], ], widget=widgets.RadioSelectHorizontal, label='Decision 2' ) # Whether active or passive activeness = models.IntegerField() # which row is chosen activeness_chosen = models.IntegerField() active_result_display = models.StringField(blank = True) active_cost_display = models.IntegerField(blank = True) # Passive client's guess on whether the doctor can successfully solve the problem passive_guess = models.IntegerField(widget=widgets.RadioSelect, choices=[ [1, 'Yes'], [0, 'No'], ], blank=True) def display_activeness(self): # This is for displaying the randomization result in the result page for the client if self.id_in_group == 2: if self.activeness_chosen == 1: if self.activeness == 1: self.active_result_display = "Make own decision & Pay 10 tokens" else: self.active_result_display = "Follow the expert & Pay 0 tokens" elif self.activeness_chosen == 2: if self.activeness == 1: self.active_result_display = "Make own decision & Pay 0 tokens" else: self.active_result_display = "Follow the expert & Pay 10 tokens" def display_active_cost(self): # This is for displaying the cost of activeness in the result page for the client if self.id_in_group == 2: if self.activeness_chosen == 1: if self.activeness == 1: self.active_cost_display = 10 else: self.active_cost_display = 0 elif self.activeness_chosen == 2: if self.activeness == 1: self.active_cost_display = 0 else: self.active_cost_display = 10 def role(self): if self.id_in_group == 1: return 'player1' else: return 'player2' def set_payoff(self): if self.id_in_group == 1: # expert self.altruism_payoff = int(self.expert_altruism*100*(self.treatment_final == self.problem)) # income from gamma*U^p self.payment = 80 - self.diagnostic_cost + self.altruism_payoff else: # client self.payment = 20 + 100*((self.activeness == 1)*(self.treatment_choice == self.problem) + (self.activeness == 0)*(self.diagnostic_result == self.problem)) + 10*(self.activeness == 0)*(self.passive_guess == int(self.treatment_final == self.problem)) + 10*(self.belief_precision == self.precision_choice) self.payment_temp = 20 + 100* ((self.activeness == 1)*(self.treatment_choice == self.problem) + (self.activeness == 0)*(self.diagnostic_result == self.problem)) def set_problem(self): if self.id_in_group == 2: random_num = randint(1, 100) if random_num <=50: self.problem = 0 self.problem_letter = "A" else: self.problem = 1 self.problem_letter = "B" def get_signal(self): if self.id_in_group == 2: random_num = randint(1, 100) if random_num <=60: self.private_signal = self.problem else: self.private_signal = abs(self.problem - 1) def set_letter_signal(self): if self.private_signal == 0: self.private_signal_letter = "A" elif self.private_signal == 1: self.private_signal_letter = "B" def set_final_treatment(self): if self.activeness == 1: self.treatment_final = self.treatment_choice if self.treatment_final == 0: self.treatment_final_letter = "A*" else: self.treatment_final_letter = "B*" else: self.treatment_final = self.diagnostic_result self.treatment_final_letter = self.recommend_soln_letter give_rating = models.IntegerField( label = '', choices = [1, 2, 3, 4, 5], widget=widgets.RadioSelectHorizontal, blank = True ) average_rating = models.FloatField(blank = True) view_rating = models.FloatField(blank = True) # Control questions # Expert expert_q1 = models.IntegerField(label='Suppose you choose 80% as your diagnostic accuracy, and your diagnosis is ' '“Problem A”. Which of the following is true?', choices = [ [1, 'There is a 0% of chance that the client’s problem is “Problem A”.'], [2, 'There is a 50% of chance that the client’s problem is “Problem A”.'], [3, 'There is an 80% of chance that the client’s problem is “Problem A”.'], [4, 'There is a 100% of chance that the client’s problem is “Problem A”.'] ],widget=widgets.RadioSelect,blank = True) def expert_q1_error_message(self, value): if self.id_in_group ==1: if value != Constants.expert_q1a: return 'Your answer to this question is wrong. Please check the hint.' expert_q2 = models.IntegerField(label='According to the cost table below, suppose that you choose 60% as your diagnostic accuracy. ' 'What is your cost of diagnosis?') def expert_q2_error_message(self, value): if self.id_in_group ==1: if value != Constants.expert_q2a: return 'Your answer to this question is wrong. Please check the hint.' expert_q3 = models.IntegerField(label = 'Suppose that your client\'s problem is solved. What is your extra gain from it?', blank = True) def expert_q3_error_message(self, value): if self.id_in_group ==1: if self.expert_altruism == 0: if value != Constants.expert_q3a_0: return 'Your answer to this question is wrong. Please check the hint.' elif self.expert_altruism == 0.2: if value != Constants.expert_q3a_20: return 'Your answer to this question is wrong. Please check the hint.' elif self.expert_altruism == 0.6: if value != Constants.expert_q3a_60: return 'Your answer to this question is wrong. Please check the hint.' elif self.expert_altruism == 1: if value != Constants.expert_q3a_100: return 'Your answer to this question is wrong. Please check the hint.' expert_q4 = models.IntegerField(label = '', choices=[ [1, 'True.'], [2, 'False.'] ], widget=widgets.RadioSelect, blank = True) def expert_q4_error_message(self, value): if self.id_in_group ==1: if value != Constants.expert_q4a: return 'Your answer to this question is wrong. Please check the hint.' expert_q5 = models.IntegerField( label='Suppose you are matched with an Active client, and you recommend solution B* to the client. Which of the following statement is true?', choices=[ [1, 'The client will always follow my recommendation and choose receive solution B*.'], [2, 'The client might not necessarily receive solution B. He/she can instead choose solution A* if he/she likes.'] ], widget=widgets.RadioSelect, blank=True) def expert_q5_error_message(self, value): if self.id_in_group == 1: if value != Constants.expert_q5a: return 'Your answer to this question is wrong. Please check the hint.' client_q1 = models.IntegerField(#label='Suppose you see the following page. Which statements are true?', label='', choices = [ [1, '(i) & (iii)'], [2, '(ii) & (iii)'], [3, '(i) & (iv)'], [4, '(ii) & (iv)'] ], widget=widgets.RadioSelect, blank = True) def client_q1_error_message(self, value): if self.id_in_group ==2: if value != Constants.client_q1a: return 'Your answer to this question is wrong. Please check the hint.' client_q2 = models.IntegerField( label = '', choices=[ [1, 'The expert will choose the solution for me.'], [2, 'I will choose the solution by myself, after seeing the private signal and the expert’s recommendation.'], ], widget=widgets.RadioSelect, blank=True ) def client_q2_error_message(self, value): if self.id_in_group ==2 and self.activeness == 1: if value != Constants.client_q2a_active: return 'Your answer to this question is wrong. Please check the hint.' elif self.id_in_group ==2 and self.activeness == 0: if value != Constants.client_q2a_passive: return 'Your answer to this question is wrong. Please check the hint.'