from otree.api import * import random def read_csv(): import csv f = open(__name__ + '/catalog.csv', encoding='utf-8-sig') rows = [row for row in csv.DictReader(f)] for row in rows: # all values in CSV are string unless you convert them row['unit_price'] = cu(row['unit_price']) row['image_path'] = 'grocery/{}.png'.format(row['image_png']) return rows class C(BaseConstants): NAME_IN_URL = 'experiment_Contribution' PLAYERS_PER_GROUP = None NUM_ROUNDS = 1 ENDOWMENT = cu(1.00) PRODUCTS = read_csv() PRODUCTS_DICT = {row['sku']: row for row in PRODUCTS} MULTIPLIER = 40 class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): # For practice round total_price = models.CurrencyField(initial=0) player2_price = models.CurrencyField(initial=0) player3_price = models.CurrencyField(initial=0) player4_price = models.CurrencyField(initial=0) first_time = models.BooleanField(initial=True) # For Demographics confusing_text = models.TextField(label="Which parts of this study did you find confusing, if any?", blank=True) age = models.IntegerField(label="Please enter your age.")#, min=18, max=100) Effort_negative_question = models.StringField( label="I made the decision in this study randomly.", choices=["Strongly Disagree", "Disagree", "Neither Disagree nor Agree", "Agree", "Strongly Agree"], widget=widgets.RadioSelectHorizontal ) Effort_positive_question = models.StringField( label="I made the decision in this study carefully.", choices=["Strongly Disagree", "Disagree", "Neither Disagree nor Agree", "Agree", "Strongly Agree"], widget=widgets.RadioSelectHorizontal ) gender_choice = models.CharField( label='', widget=widgets.RadioSelect, choices=[('Man', 'Man'), ('Woman', 'Woman'), ('Other', 'Other')] ) gender_text = models.StringField(label='Please specify', blank=True) race_choice = models.CharField( label='', widget=widgets.RadioSelect, choices=[('American Indian/Alaska Native', 'American Indian/Alaska Native'), ('Asian', 'Asian'), ('Black or African American', 'Black or African American'), ('Native Hawaiian/Other Pacific Islander', 'Native Hawaiian/Other Pacific Islander'), ('White', 'White'), ('Other', 'Other')] ) race_text = models.StringField(label='Please specify', blank=True) ethnicity_choice = models.CharField( label='', widget=widgets.RadioSelect, choices=[('Hispanic or Latino or Spanish Origin', 'Hispanic or Latino or Spanish Origin'), ('Not Hispanic or Latino or Spanish Origin', 'Not Hispanic or Latino or Spanish Origin')] ) educ_choice = models.CharField( label='', widget=widgets.RadioSelect, choices=[('Regular high school diploma', 'Regular high school diploma'), ('GED or alternative credential', 'GED or alternative credential'), ('Associates degree (for example: AA, AS)', 'Associates degree (for example: AA, AS)'), ('Bachelor’s degree (for example: BA. BS)', 'Bachelor’s degree (for example: BA. BS)'), ('Master’s degree (for example: MA, MS, MEng, MEd, MSW, MBA)', 'Master’s degree (for example: MA, MS, MEng, MEd, MSW, MBA)'), ('Professional degree beyond bachelor’s degree (for example: MD, DDS, DVM, LLB, JD)', 'Professional degree beyond bachelor’s degree (for example: MD, DDS, DVM, LLB, JD)'), ('Doctorate degree (for example, PhD, EdD)', 'Doctorate degree (for example, PhD, EdD)'), ('Other', 'Other')] ) educ_text = models.StringField(label='Please specify', blank=True) politics_choice = models.CharField( label='', widget=widgets.RadioSelect, choices=[('Democrat', 'Democrat'), ('Republican', 'Republican'), ('Independent', 'Independent')] ) prolific_importance_choice = models.CharField( label='', widget=widgets.RadioSelect, choices=[('It is my main source of income', 'It is my main source of income'), ('It is not my main source of income, but I do need it to get by', 'It is not my main source of income, but I do need it to get by'), ('I do not need it to get by, but it is a welcome addition to my finances', 'I do not need it to get by, but it is a welcome addition to my finances'), ('I do it just for fun', 'I do it just for fun')] ) prolific_earnings_choice = models.CharField( label='', widget=widgets.RadioSelect, choices=[('$0 - $100 per month', '$0 - $100 per month'), ('$101 - $250 per month', '$101 - $250 per month'), ('$251 - $500 per month', '$251 - $500 per month'), ('$501 - $1,000 per month', '$501 - $1,000 per month'), ('$1,001 - $2,000 per month', '$1,001 - $2,000 per month'), ('$2,001 - $3,000 per month', '$2,001 - $3,000 per month'), ('$3,001 - $4,000 per month', '$3,001 - $4,000 per month'), ('$4,001+ per month', '$4,001+ per month')] ) # For final screen feedback_text = models.TextField(blank=True) # Contribution decisions contribution = models.BooleanField( # initial=False, label="Please indicate whether you want to contribute to the group project", choices=[ [True, "Yes, I want to contribute $1.00 to the group project"], [False, "No, I do not want to contribute $1.00 to the group project"] ] ) # contribution_two_choice_egoistic = models.BooleanField( # # initial=False, # label="Please select your choice if you are randomly selected to be the \"Decider\":", # choices=[ # [True, # "I decide to have the three other members of the group contribute while I do not. I earn $2.20 and they each earn $1.20."], # # " Propose: Your group plays the game from round 1 with fixed contribution decisions. You do not contribute and the other group members contribute; you would receive $5.50 and everyone else would receive $3.00."], # [False, # "I implement the version of the game from round 1 and each person earns whatever they earned in that version."] # # "Your group does not play the game. No one receives any bonus payoffs."] # # ] # ) contribution_two_choice_egoistic_1_0 = models.BooleanField( # initial=False, label="Please select your choice if you are randomly selected to be the \"Decider\":", choices=[ [True, "I decide to have the three other members of my group contribute while I do not. I earn $2.20 and they each earn $1.20."], # " Propose: Your group plays the game from round 1 with fixed contribution decisions. You do not contribute and the other group members contribute; you would receive $5.50 and everyone else would receive $3.00."], [False, "I implement the version of the game from round 1. I earn $1.00 and everyone else also earns $1.00."] # "Your group does not play the game. No one receives any bonus payoffs."] ] ) contribution_two_choice_egoistic_1_1 = models.BooleanField( # initial=False, label="Please select your choice if you are randomly selected to be the \"Decider\":", choices=[ [True, "I decide to have the three other members of my group contribute while I do not. I earn $2.20 and they each earn $1.20."], # " Propose: Your group plays the game from round 1 with fixed contribution decisions. You do not contribute and the other group members contribute; you would receive $5.50 and everyone else would receive $3.00."], [False, "I implement the version of the game from round 1. I earn $0.40 and everyone else earns $1.40."] # "Your group does not play the game. No one receives any bonus payoffs."] ] ) contribution_two_choice_egoistic_2_0 = models.BooleanField( # initial=False, label="Please select your choice if you are randomly selected to be the \"Decider\":", choices=[ [True, "I decide to have the three other members of my group contribute while I do not. I earn $2.20 and they each earn $1.20."], # " Propose: Your group plays the game from round 1 with fixed contribution decisions. You do not contribute and the other group members contribute; you would receive $5.50 and everyone else would receive $3.00."], [False, "I implement the version of the game from round 1. I earn $1.40 and the other members of the group earn $0.40, $1.40, and $1.40 respectively."] # "Your group does not play the game. No one receives any bonus payoffs."] ] ) contribution_two_choice_egoistic_2_1 = models.BooleanField( # initial=False, label="Please select your choice if you are randomly selected to be the \"Decider\":", choices=[ [True, "I decide to have the three other members of my group contribute while I do not. I earn $2.20 and they each earn $1.20."], # " Propose: Your group plays the game from round 1 with fixed contribution decisions. You do not contribute and the other group members contribute; you would receive $5.50 and everyone else would receive $3.00."], [False, "I implement the version of the game from round 1. I earn $0.80 and the other members of the group earn $0.80, $1.80, and $1.80 respectively."] # "Your group does not play the game. No one receives any bonus payoffs."] ] ) contribution_two_choice_egoistic_3_0 = models.BooleanField( # initial=False, label="Please select your choice if you are randomly selected to be the \"Decider\":", choices=[ [True, "I decide to have the three other members of my group contribute while I do not. I earn $2.20 and they each earn $1.20."], # " Propose: Your group plays the game from round 1 with fixed contribution decisions. You do not contribute and the other group members contribute; you would receive $5.50 and everyone else would receive $3.00."], [False, "I implement the version of the game from round 1. I earn $1.80 and the other members of the group earn $0.80, $0.80, and $1.80 respectively."] # "Your group does not play the game. No one receives any bonus payoffs."] ] ) contribution_two_choice_egoistic_3_1 = models.BooleanField( # initial=False, label="Please select your choice if you are randomly selected to be the \"Decider\":", choices=[ [True, "I decide to have the three other members of my group contribute while I do not. I earn $2.20 and they each earn $1.20."], # " Propose: Your group plays the game from round 1 with fixed contribution decisions. You do not contribute and the other group members contribute; you would receive $5.50 and everyone else would receive $3.00."], [False, "I implement the version of the game from round 1. I earn $1.20 and the other members of the group earn $1.20, $1.20, and $2.20 respectively."] # "Your group does not play the game. No one receives any bonus payoffs."] ] ) contribution_two_choice_egoistic_4_0 = models.BooleanField( # initial=False, label="Please select your choice if you are randomly selected to be the \"Decider\":", choices=[ [True, "I decide to have the three other members of my group contribute while I do not. I earn $2.20 and they each earn $1.20."], # " Propose: Your group plays the game from round 1 with fixed contribution decisions. You do not contribute and the other group members contribute; you would receive $5.50 and everyone else would receive $3.00."], [False, "I implement the version of the game from round 1. I earn $2.20 and everyone else earns $1.20."] # "Your group does not play the game. No one receives any bonus payoffs."] ] ) contribution_two_choice_egoistic_4_1 = models.BooleanField( # initial=False, label="Please select your choice if you are randomly selected to be the \"Decider\":", choices=[ [True, "I decide to have the three other members of my group contribute while I do not. I earn $2.20 and they each earn $1.20."], # " Propose: Your group plays the game from round 1 with fixed contribution decisions. You do not contribute and the other group members contribute; you would receive $5.50 and everyone else would receive $3.00."], [False, "I implement the version of the game from round 1. I earn $1.60 and everyone else also earns $1.60."] # "Your group does not play the game. No one receives any bonus payoffs."] ] ) # WTP MPL variables choice_0 = models.BooleanField() choice_1 = models.BooleanField() choice_2 = models.BooleanField() choice_3 = models.BooleanField() choice_4 = models.BooleanField() choice_5 = models.BooleanField() choice_6 = models.BooleanField() choice_7 = models.BooleanField() choice_8 = models.BooleanField() choice_9 = models.BooleanField() choice_10 = models.BooleanField() choice_11 = models.BooleanField() choice_12 = models.BooleanField() # Variables needed for some forms (non-altruistic dictator payoffs and the ROI) testpayoff_high = models.CurrencyField() testpayoff_low = models.CurrencyField() single_payout = models.CurrencyField() payment_scenario1 = models.CurrencyField() payment_scenario2 = models.CurrencyField() payment_scenario3 = models.CurrencyField() payment_scenario4 = models.CurrencyField() payment_scenario4 = models.CurrencyField() otherpayment_scenario1 = models.CurrencyField() otherpayment_scenario2_low = models.CurrencyField() otherpayment_scenario2_high = models.CurrencyField() otherpayment_scenario3_low = models.CurrencyField() otherpayment_scenario3_high = models.CurrencyField() otherpayment_scenario4 = models.CurrencyField() # For new MPL donation_amount = models.CurrencyField() is_next_button_disabled = models.BooleanField(initial=False) right_side_amounts = range(1,10) # left_side_amount = 10 switching_point_1 = models.IntegerField() switching_point_2 = models.IntegerField() switching_point_3 = models.IntegerField() switching_point_4 = models.IntegerField() first_understanding = models.BooleanField( # initial=False, label="If round 2 is randomly selected to determine your payoffs and your choice of the option on the left in the randomly selected row is implemented, what would happen?", choices=[ [True, "Everyone earns payoffs from round 1 and I receive some additional bonus payment"], [False, "The 'Decider' chooses contributions and I receive some additional bonus payment"] ] ) second_understanding = models.BooleanField( # initial=False, label="If round 2 is randomly selected to determine your payoffs and your choice of the option on the right in the randomly selected row is implemented, what would happen?", choices=[ [True, "Everyone earns payoffs from round 1 and I receive some additional bonus payment"], [False, "The 'Decider' chooses contributions and I receive some additional bonus payment"] ] ) def set_list(self): self.participant.vars['list'] += 1 def save_output(self): choice_now = self.choice_now if choice_now: prefix = self.prefix time = self.get_time() id_number = int(choice_now[:-4]) changed_now = self.get_changed_now(choice_now, id_number) self.participant.vars[prefix + "Time"].append(time) self.participant.vars[prefix + "Choices"].append(choice_now) self.participant.vars[prefix + "IdFinal"] = id_number self.participant.vars[prefix + "ChoiceFinal"] = choice_now self.participant.vars[prefix + "LB"], self.participant.vars[prefix + "UB"] = self.get_lb_ub(id_number, changed_now) self.participant.vars[prefix + "Changed"].append(changed_now) def get_time(self): time_start = self.participant.vars.get('time_start', 0) current_time = self.session.vars['time_elapsed'] + self.player.time_on_this_page(time_start) self.participant.vars['time_start'] = current_time return round(current_time, 2) def get_changed_now(self, choice_now, id_number): if choice_now == "21left": changed_now = 1 elif choice_now == "1right": changed_now = 2 elif id_number < 1: changed_now = 3 elif id_number > 100: changed_now = 4 elif id_number < 1: changed_now = 5 elif id_number > 100: changed_now = 6 else: changed_now = 0 return changed_now def get_lb_ub(self, id_number, changed_now): increment = 5 stake = int(self.session.config['stake']) if changed_now in [1, 4]: lb = (id_number - 1) * increment ub = id_number * increment elif changed_now in [2, 5]: lb = 0 ub = 0 else: lb = max((id_number - 2) * increment, 0) ub = min((id_number - 1) * increment, stake) return lb, ub def enable_next_button(self): self.player.is_next_button_disabled = False def disable_next_button(self): self.player.is_next_button_disabled = True # For Practice round class Item(ExtraModel): player = models.Link(Player) sku = models.StringField() name = models.StringField() quantity = models.IntegerField() unit_price = models.CurrencyField() contributioncall = models.StringField() def total_price(item: Item): return item.quantity * item.unit_price def variable_price(item: Item): return item.quantity * item.unit_price def to_dict(item: Item): return dict( sku=item.sku, name=item.name, quantity=item.quantity, contributioncall=item.contributioncall, total_price=total_price(item), variable_price=variable_price(item), ) def live_method(player: Player, data): if player.first_time: if len(data) < 4: Item.create( player=player, quantity=0, contributioncall="Does not contribute", sku="1", name="Player 1 (You)", unit_price=-0.6, ) Item.create( player=player, quantity=0, contributioncall="Does not contribute", sku="2", name="Player 2", unit_price=0.4, ) Item.create( player=player, quantity=0, contributioncall="Does not contribute", sku="3", name="Player 3", unit_price=0.4, ) Item.create( player=player, quantity=0, contributioncall="Does not contribute", sku="4", name="Player 4", unit_price=0.4, ) player.first_time = False if 'sku' in data: sku = data['sku'] delta = data['delta'] if (delta == 1): contribution_value = "Contributes" if (delta == 0): contribution_value = "Does not contribute" product = C.PRODUCTS_DICT[sku] matches = Item.filter(player=player, sku=sku) if matches: [item] = matches item.quantity = delta item.contributioncall = contribution_value # if item.quantity < 0: # item.delete() else: # if delta >= 0: Item.create( player=player, quantity=delta, contributioncall=contribution_value, sku=sku, name=product['name'], unit_price=product['unit_price'], ) # Item.sort() items = Item.filter(player=player) item_dicts = [to_dict(item) for item in items] player.total_price = sum([total_price(item) for item in items]) player1_item = items[0] player2_item = items[1] player3_item = items[2] player4_item = items[3] payoff2 = cu( 1.00 - player2_item.quantity * 0.6 + player1_item.quantity * 0.4 + player3_item.quantity * 0.4 + player4_item.quantity * 0.4) payoff3 = cu( 1.00 - player3_item.quantity * 0.6 + player1_item.quantity * 0.4 + player2_item.quantity * 0.4 + player4_item.quantity * 0.4) payoff4 = cu( 1.00 - player4_item.quantity * 0.6 + player1_item.quantity * 0.4 + player2_item.quantity * 0.4 + player3_item.quantity * 0.4) player.player2_price = payoff2 player.player3_price = payoff3 player.player4_price = payoff4 return {player.id_in_group: dict(items=item_dicts, total_price=player.total_price, player2_price=player.player2_price, player3_price=player.player3_price, player4_price=player.player4_price)} # PAGES class Page6_1(Page): form_model = 'player' form_fields = ['switching_point_1'] @staticmethod def vars_for_template(player:Player): right_side_amounts = range(1,10) left_side_amount = 10 right_side_amounts2 = list(range(5, 0)) + [0] * 4 return { 'right_side_amounts': right_side_amounts, 'left_side_amount': left_side_amount, 'right_side_amounts2':right_side_amounts2 } class Page6_2(Page): form_model = 'player' form_fields = ['switching_point_2'] class Page6_3(Page): form_model = 'player' form_fields = ['switching_point_3'] class Page6_4(Page): form_model = 'player' form_fields = ['switching_point_4'] class Page1(Page): @staticmethod def vars_for_template(player: Player): player.testpayoff_high = ((3 * C.MULTIPLIER / 100) + 1) * C.ENDOWMENT player.testpayoff_low = (3 * C.MULTIPLIER / 100) * C.ENDOWMENT player.single_payout = (1 * C.MULTIPLIER / 100) * C.ENDOWMENT class Page2(Page): @staticmethod def vars_for_template(player: Player): player.testpayoff_high = ((3 * C.MULTIPLIER / 100) + 1) * C.ENDOWMENT player.testpayoff_low = (3 * C.MULTIPLIER / 100) * C.ENDOWMENT player.single_payout = (1 * C.MULTIPLIER / 100) * C.ENDOWMENT class Page3(Page): live_method = live_method class Page4(Page): form_model = 'player' form_fields = ['contribution'] class Page4_5(Page): form_model = "player" @staticmethod def vars_for_template(player: Player): player.payment_scenario1 = (C.MULTIPLIER / 100) * C.ENDOWMENT * player.contribution + (1- player.contribution) player.payment_scenario2 = (C.MULTIPLIER / 100) * C.ENDOWMENT * (1+player.contribution) + (1- player.contribution) player.payment_scenario3 = (C.MULTIPLIER / 100) * C.ENDOWMENT * (2+player.contribution) + (1- player.contribution) player.payment_scenario4 = (C.MULTIPLIER / 100) * C.ENDOWMENT * (3+player.contribution) + (1- player.contribution) player.otherpayment_scenario1 = (C.MULTIPLIER / 100) * C.ENDOWMENT * (player.contribution) + 1 player.otherpayment_scenario2_low = (C.MULTIPLIER / 100) * C.ENDOWMENT * (1+player.contribution) player.otherpayment_scenario2_high = (C.MULTIPLIER / 100) * C.ENDOWMENT * (1+player.contribution) + 1 player.otherpayment_scenario3_low = (C.MULTIPLIER / 100) * C.ENDOWMENT * (2+player.contribution) player.otherpayment_scenario3_high = (C.MULTIPLIER / 100) * C.ENDOWMENT * (2+player.contribution) + 1 player.otherpayment_scenario4 = (C.MULTIPLIER / 100) * C.ENDOWMENT * (3+player.contribution) class Page4_6(Page): form_model = 'player' form_fields = ['first_understanding','second_understanding',] @staticmethod def error_message(self, values): if not values['first_understanding']: return 'You made a mistake answering the first question. Please make sure you understand how round 2 works and try again.' if values['second_understanding']: return 'You made a mistake answering the second question. Please make sure you understand how round 2 works and try again.' # if not(values['first_understanding']): # return 'You made a mistake answering the first question. Please try again.' # @staticmethod # def error_message(self, values): # if values['second_understanding']: # return 'You made a mistake answering the second question. Please try again.' class Page5(Page): pass class WTPpricelist_minimal(Page): form_model = "player" form_fields = ['choice_0', 'choice_1', 'choice_12'] class WTPpricelist(Page): form_model = "player" form_fields = ['choice_0', 'choice_1', 'choice_2', 'choice_3', 'choice_4', 'choice_5', 'choice_6', 'choice_7', 'choice_8', 'choice_9', 'choice_10', 'choice_11', 'choice_12'] class Page6(Page): form_model = "player" form_fields = ['choice_0', 'choice_1', 'choice_2', 'choice_3', 'choice_4', 'choice_5', 'choice_6', 'choice_7', 'choice_8', 'choice_9', 'choice_10', 'choice_11', 'choice_12'] # def error_message(player, values): # if values['choice_0'] < values['choice_1']: # return "You can only switch from the options on the left to the options on the right once." # if values['choice_1'] < values['choice_2']: # return "You can only switch from the options on the left to the options on the right once." # if values['choice_2'] < values['choice_3']: # return "You can only switch from the options on the left to the options on the right once." # if values['choice_3'] < values['choice_4']: # return "You can only switch from the options on the left to the options on the right once." # if values['choice_4'] < values['choice_5']: # return "You can only switch from the options on the left to the options on the right once." # if values['choice_5'] < values['choice_6']: # return "You can only switch from the options on the left to the options on the right once." # if values['choice_6'] < values['choice_7']: # return "You can only switch from the options on the left to the options on the right once." # if values['choice_7'] < values['choice_8']: # return "You can only switch from the options on the left to the options on the right once." # if values['choice_8'] < values['choice_9']: # return "You can only switch from the options on the left to the options on the right once." # if values['choice_9'] < values['choice_10']: # return "You can only switch from the options on the left to the options on the right once." # if values['choice_10'] < values['choice_11']: # return "You can only switch from the options on the left to the options on the right once." # if values['choice_11'] < values['choice_12']: # return "You can only switch from the options on the left to the options on the right once." class Page7(Page): form_model = 'player' form_fields = ['contribution_two_choice_egoistic'] class Page7_1(Page): form_model = 'player' def get_form_fields(self): if self.contribution: return ['contribution_two_choice_egoistic_1_1'] else: return [ 'contribution_two_choice_egoistic_1_0'] class Page7_2(Page): form_model = 'player' def get_form_fields(self): if self.contribution: return ['contribution_two_choice_egoistic_2_1'] else: return [ 'contribution_two_choice_egoistic_2_0'] class Page7_3(Page): form_model = 'player' def get_form_fields(self): if self.contribution: return ['contribution_two_choice_egoistic_3_1'] else: return [ 'contribution_two_choice_egoistic_3_0'] class Page7_4(Page): form_model = 'player' def get_form_fields(self): if self.contribution: return ['contribution_two_choice_egoistic_4_1'] else: return [ 'contribution_two_choice_egoistic_4_0'] class Page8(Page): form_model = "player" form_fields = ['confusing_text', 'age', 'Effort_negative_question', 'Effort_positive_question', 'gender_choice', 'gender_text', 'race_choice', 'race_text', 'ethnicity_choice', 'educ_choice', 'educ_text', 'politics_choice', 'prolific_importance_choice', 'prolific_earnings_choice'] @staticmethod def vars_for_template(player): player.participant.finished = True class Page9(Page): form_model = "player" form_fields = ['feedback_text'] class Page10(Page): pass page_sequence = [ Page1, Page2, Page3, Page4, Page5, Page4_5, Page4_6, Page6_1, Page6_2, Page6_3, Page6_4, Page7_1,Page7_2,Page7_3,Page7_4, Page8, Page9, Page10]