import random from otree.api import ( Page, WaitPage, models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range, ) doc = """ This is a two-period public goods game with 4 players per group in the context of environmental economics. """ class Constants(BaseConstants): name_in_url = 'clamp_environmentalexp' players_per_group = 4 num_rounds = 2 instructions_template = 'clamp_environmentalexp/instructions.html' endowment = 80 flood = 52 initial_risk = 0.6 pub_risk = 0.5 priv_risk = 0.3 both_risk = 0.85 threshold = 60 class Subsession(BaseSubsession): pass class Group(BaseGroup): total_contribution = models.CurrencyField() individual_share = models.CurrencyField() # The fields that each player will have to enter to contribute to. class Player(BasePlayer): currency_payoff = models.CurrencyField() total_payoff = models.CurrencyField() #selected_round = models.IntegerField() contribution = models.IntegerField( min=0, max=60, #max= endowment, doc="""The amount contributed by the player towards the public """, #Unsure if wording of 'projects' is what I want to use in this question. label="How much will you contribute to the public risk mitigation measure?", ) # Including private currency field so subjects have to actively choose how much to contribute to each. private_measure = models.BooleanField( choices = [[True, 'Yes'], [False, 'No']], label = "Would you like to purchase a private risk mitigation measure?", ) private_insurance = models.IntegerField( choices=[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20], doc="""The highest amount the player is willing to pay for the private risk mitigation measure.""", label="If you are interested in purchasing the private risk mitigation measure, what is the highest price you would be willing to pay for it?", ) #Demographic Questions age = models.IntegerField(label='Please enter your age?', min=13, max=125) gender = models.StringField( choices=[['Male', 'Male'], ['Female', 'Female'], ['Non-binary', 'Non-binary'], ['Prefer not to say', 'Prefer not to say']], label='What is your gender?', widget=widgets.RadioSelect, ) race = models.StringField( choices=[['White', 'White'], ['Black or African American', 'Black or African American'], ['Asian', 'Asian'], ['American Indian or Alaska Native', 'American Indian or Alaska Native'], ['Native Hawaiian or Other Pacific Islander', 'Native Hawaiian or Other Pacific Islander'], ['Race not listed', 'Race not listed'], ['Prefer not to say', 'Prefer not to say']], label='What racial or ethnic groups do you identify with? Please select all that apply.', widget=widgets.RadioSelect, ) education = models.StringField( choices=[['Grades 1-8', 'Grades 1-8'], ['Grades 9-11', 'Grades 9-11'], ['High school diploma', 'High school diploma'], ['Some college but no degree', 'Some college but no degree'], ['Community college degree', 'Community college degree'], ['Vocational or technical school degree', 'Vocational or technical school degree'], ['BA or BS degree', 'BA or BS degree'], ['Graduate or professional school', 'Graduate or professional school']], label='What is the highest level of schooling you have completed? ', widget=widgets.RadioSelect, ) hometown = models.StringField(label='What is your hometown? Please include city and state.') current_city = models.StringField(label='What city are you currently living in?') major = models.StringField(label='What is your major?') politics = models.StringField( choices=[['Republican', 'Republican'], ['Democrat', 'Democrat'], ['Green Party', 'Green Party'], ['Independent', 'Independent'], ['Libertarian', 'Libertarian'], ['Other', 'Other'], ['Unsure', 'Unsure'], ['Prefer not to answer', 'Prefer not to answer']], label='What political party do you align the most with on climate related policy?', widget=widgets.RadioSelect, ) #Experimentally relevant questions from the survey #In the next 30 years, how certain are you that changes in the climate will have a negative impact on... Q1_1 = models.StringField( choices=[ [1, 'Not at all'], [2, 'A little'], [3, 'Somewhat'], [4, 'Very'], [5, 'Extremely'] ], label='In the next 30 years, how certain are you that changes in the climate will have a negative impact on you and your family?', widget= widgets.RadioSelectHorizontal, ) Q1_2 = models.StringField( choices=[ [1, 'Not at all'], [2, 'A little'], [3, 'Somewhat'], [4, 'Very'], [5, 'Extremely'] ], label='In the next 30 years, how certain are you that changes in the climate will have a negative impact on your town and your city?', widget=widgets.RadioSelectHorizontal, ) Q1_3 = models.StringField( choices=[ [1, 'Not at all'], [2, 'A little'], [3, 'Somewhat'], [4, 'Very'], [5, 'Extremely'] ], label='In the next 30 years, how certain are you that changes in the climate will have a negative impact on your state?', widget=widgets.RadioSelectHorizontal, ) Q1_4 = models.StringField( choices=[ [1, 'Not at all'], [2, 'A little'], [3, 'Somewhat'], [4, 'Very'], [5, 'Extremely'] ], label='In the next 30 years, how certain are you that changes in the climate will have a negative impact on people across the world', widget=widgets.RadioSelectHorizontal, ) #Thinking of life in your town or city, to the best of your knowledge, how much of an impact will a changing climate have on… Q2_1 = models.StringField( choices=[ [1, 'No impact'], [2, 'A small impact'], [3, 'A moderate impact'], [4, 'A large impact'], [5, 'An extremely large impact'], ], label='Thinking of life in your town or city, to the best of your knowledge, how much of an impact will a changing climate have on drought or water shortages?', widget= widgets.RadioSelectHorizontal, ) Q2_2 = models.StringField( choices=[ [1, 'No impact'], [2, 'A small impact'], [3, 'A moderate impact'], [4, 'A large impact'], [5, 'An extremely large impact'], ], label='Thinking of life in your town or city, to the best of your knowledge, how much of an impact will a changing climate have on higher likelihood of storms and floods.', widget=widgets.RadioSelectHorizontal, ) Q2_3 = models.StringField( choices=[ [1, 'No impact'], [2, 'A small impact'], [3, 'A moderate impact'], [4, 'A large impact'], [5, 'An extremely large impact'], ], label='Thinking of life in your town or city, to the best of your knowledge, how much of an impact will a changing climate have on higher severity of storms and floods', widget=widgets.RadioSelectHorizontal, ) Q2_4 = models.StringField( choices=[ [1, 'No impact'], [2, 'A small impact'], [3, 'A moderate impact'], [4, 'A large impact'], [5, 'An extremely large impact'], ], label='Thinking of life in your town or city, to the best of your knowledge, how much of an impact will a changing climate have on increased likelihood of heatwaves.', widget=widgets.RadioSelectHorizontal, ) #For the following statement regarding the environment and society. Please indicate how much you agree or disagree. Q3_1 = models.StringField( choices=[ [1, 'Strongly disagree'], [2, 'Mildly disagree'], [3, 'Neutral'], [4, 'Mildly agree'], [5, 'Strongly agree'], ], label='For the following statement regarding the environment and society. Please indicate how much you agree or disagree. We are approaching the limit of the number of people the earth can support.', widget=widgets.RadioSelectHorizontal, ) Q3_2 = models.StringField( choices=[ [1, 'Strongly disagree'], [2, 'Mildly disagree'], [3, 'Neutral'], [4, 'Mildly agree'], [5, 'Strongly agree'], ], label='For the following statement regarding the environment and society. Please indicate how much you agree or disagree. The balance of nature is very delicate and easily upset.', widget=widgets.RadioSelectHorizontal, ) Q3_3 = models.StringField( choices=[ [1, 'Strongly disagree'], [2, 'Mildly disagree'], [3, 'Neutral'], [4, 'Mildly agree'], [5, 'Strongly agree'], ], label='For the following statement regarding the environment and society. Please indicate how much you agree or disagree. Despite our special abilities to adapt humans are still subject to the laws of nature.', widget=widgets.RadioSelectHorizontal, ) Q3_4 = models.StringField( choices=[ [1, 'Strongly disagree'], [2, 'Mildly disagree'], [3, 'Neutral'], [4, 'Mildly agree'], [5, 'Strongly agree'], ], label='For the following statement regarding the environment and society. Please indicate how much you agree or disagree. If things continue with their present course, we will soon experience a major ecological catastrophe.', widget=widgets.RadioSelectHorizontal, ) #How much would you say you know about how to.. Q4_1 = models.StringField( choices=[ [1, 'Strongly disagree'], [2, 'Mildly disagree'], [3, 'Neutral'], [4, 'Mildly agree'], [5, 'Strongly agree'], ], label='How much would you say you know about how to prepare for a climate related natural disaster like a hurricane in your own home.', widget=widgets.RadioSelectHorizontal, ) Q4_2 = models.StringField( choices=[ [1, 'Strongly disagree'], [2, 'Mildly disagree'], [3, 'Neutral'], [4, 'Mildly agree'], [5, 'Strongly agree'], ], label='How much would you say you know about how to find out what your community is doing for hurricane preparedness plans.', widget=widgets.RadioSelectHorizontal, ) Q4_3 = models.StringField( choices=[ [1, 'Strongly disagree'], [2, 'Mildly disagree'], [3, 'Neutral'], [4, 'Mildly agree'], [5, 'Strongly agree'], ], label='How much would you say you know about how to contribute/organize a community hurricane preparedness plan for your community', widget=widgets.RadioSelectHorizontal, ) #How much personal responsibility do you feel for... Q5_1 = models.StringField( choices=[ [1, 'None'], [2, 'A little'], [3, 'A moderate amount'], [4, 'Quite a bit'], [5, 'A great deal'], ], label='How much personal responsibility do you feel for participating in local hurricane preparedness efforts?', widget=widgets.RadioSelectHorizontal, ) Q5_2 = models.StringField( choices=[ [1, 'None'], [2, 'A little'], [3, 'A moderate amount'], [4, 'Quite a bit'], [5, 'A great deal'], ], label='How much personal responsibility do you feel for privately preparing your home/property for hurricanes and other natural disasters if other members of the community do not.', widget=widgets.RadioSelectHorizontal, ) #In your opinion, how often can… Q6_1 = models.StringField( choices=[ [1, 'Never'], [2, 'Rarely'], [3, 'Sometimes'], [4, 'Very often'], [5, 'Extremely often'], ], label='In your opinion, how often can the federal government in Washington D.C. be trusted to do what is right.', widget=widgets.RadioSelectHorizontal, ) Q6_2 = models.StringField( choices=[ [1, 'Never'], [2, 'Rarely'], [3, 'Sometimes'], [4, 'Very often'], [5, 'Extremely often'], ], label='In your opinion, how often can your town or city government be trusted to do what is right', widget=widgets.RadioSelectHorizontal, ) #Thinking about natural disaster preparedness for your town or city… Q7_1 = models.StringField( choices=[ [1, 'Strongly oppose'], [2, 'Somewhat oppose'], [3, 'Neutral'], [4, 'Somewhat support'], [5, 'Strongly support'], ], label='Thinking about natural disaster preparedness for your town or city to what extent would you support or oppose residents, such as yourself, working with local hurricane preparedness experts to make decisions', ) # FUNCTIONS def vars_for_admin_report(subsession: Subsession): contributions = [p.contribution for p in subsession.get_players() if p.contribution != None] if contributions: return dict( avg_contribution=sum(contributions) / len(contributions), min_contribution=min(contributions), max_contribution=max(contributions), ) else: return dict( avg_contribution='(no data)', min_contribution='(no data)', max_contribution='(no data)', ) def creating_session(subsession): print("in creating session") # Group pairs randomly subsession.group_randomly() #is this a tax round or a non tax round #Are the questions correct? #for g in subsession.get_groups(): #for p in g.get_players(): #p.total_allotted = p.endowment #no tax set_payoffs def set_payoffs(group: Group): for p in group.get_players(): if p.round_number <=2: group.total_contribution = sum([p.contribution for p in group.get_players()]) for p in group.get_players(): if group.total_contribution >= 60: p.payoff = 80 - p.contribution - p.private_insurance else: p.payoff = 80 - p.private_insurance #PAGES class Introduction(Page): """Description of the game: How to play and what to expect""" def is_displayed(player): return player.round_number == 1 pass class IntroQuestions(Page): """Wait page for the first round. This is for questions and for lab person to emphasize points.""" def is_displayed(player): return player.round_number == 1 class Contribute(Page): def is_displayed(player): return player.round_number <= 2 """Player: Choose how much to contribute to the public fund and how much to keep in your personal account""" form_model = 'player' form_fields = ['contribution', 'private_measure', 'private_insurance'] @staticmethod def contribution_error_message(player, value): print('Contribution is', value) if value > 60: return 'Woah there! The price of the public mitigation measure is only 60. You do not need to contribute more than 60.' @staticmethod def get_timeout_seconds(player: Player): return 180 def before_next_page(player, timeout_happened): if timeout_happened: player.contribution = 0 player.private_keep = 0 #class Questions(Page): # """Page to elicit beliefs about the other players.""" #form_model = 'player' #body_text = "You have made the following decision in this round. Please answer the following questions, while you wait for other players to finish." #form_fields = ['how_much', 'should_have'] #@staticmethod #def how_much_error_message(player, value): #print('How much is', value) #if value > 20: #return 'You cannot guess an amount that the other player did not have.' #def should_have_error_message(player, value): #print('How much should have', value) #if value > 20: #return 'You cannot expect an amount that the other player did not have.' #@staticmethod #def get_timeout_seconds(player: Player): #return 120 class ResultsWaitPage(WaitPage): after_all_players_arrive = 'set_payoffs' body_text = "Waiting for other participants to finish making their decisions." class Results(Page): """Congrats! Your community has contributed enough to implement the public measure of risk mitigation.""" def is_displayed(player): return player.round_number <= 2 @staticmethod def vars_for_template(player: Player): group = player.group #@staticmethod #def vars_for_template(player: Player): # group = player.group # return dict(total_earnings=group.total_contribution * Constants.multiplier) class Pause(Page): """This page is only for treatments where new information is presented to participants.""" def is_displayed(player): return player.round_number == 1 #class Results(Page): #"""Players payoff: How much each has earned""" #def is_displayed(player): #return player.round_number <= 5 # @staticmethod #def vars_for_template(player: Player): #group = player.group #return dict(total_earnings=group.total_contribution * Constants.multiplier) class Relevant(Page): """Relevant survey questions.""" def is_displayed(player): return player.round_number == Constants.num_rounds form_model = 'player' form_fields = ['Q1_1', 'Q1_2', 'Q1_3', 'Q1_4', 'Q2_1', 'Q2_2', 'Q2_3', 'Q2_4', 'Q3_1', 'Q3_2', 'Q3_3', 'Q3_4', 'Q4_1', 'Q4_2', 'Q4_3', 'Q5_1', 'Q5_2', 'Q6_1', 'Q6_2', 'Q7_1'] class Demo(Page): """Relevant survey questions.""" def is_displayed(player): return player.round_number == Constants.num_rounds form_model = 'player' form_fields = ['age', 'gender', 'race', 'education', 'hometown', 'current_city', 'major', 'politics'] class End(Page): """Last page. Tells player final payoff and how to collect their earnings.""" def is_displayed(player): return player.round_number == Constants.num_rounds page_sequence = [Introduction, IntroQuestions, Contribute, ResultsWaitPage, Results, Pause, Relevant, Demo, End]