from otree.api import * c = cu doc = 'This is a 5 round public goods game with 5 players endowed with equal amounts.\n' class Constants(BaseConstants): name_in_url = 'Public_Goods_Perfect_Equality_5P' players_per_group = 5 num_rounds = 5 threshold = cu(10) endowment = (cu(4), cu(4), cu(4), cu(4), cu(4)) admin_report_template = 'Public_Goods_Perfect_Equality_5P/admin_report.html' instructions_template = 'Public_Goods_Perfect_Equality_5P/instructions.html' def vars_for_admin_report(subsession): session = subsession.session 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)', ) class Subsession(BaseSubsession): vars_for_admin_report = vars_for_admin_report def set_payoffs(group): for p in group.get_players(): p.contribution = p.contribution_p1 + p.contribution_p2 + p.contribution_p3 + p.contribution_p4 + p.contribution_p5 p.partyid = p.participant.vars['partyid'] if group.round_number != 1: previous_tc = group.in_round(group.round_number - 1).total_contribution group.total_contribution = sum([p.contribution for p in group.get_players()]) + previous_tc else: group.total_contribution = sum([p.contribution for p in group.get_players()]) for p in group.get_players(): p.budget = (p.budget - p.contribution) p.payoff = p.budget #Payoff set after budget reset? def set_player_vars(group): if group.round_number != 1: for p in group.get_players(): p.partyid = p.participant.vars['partyid'] p.budget = p.in_round(group.round_number - 1).budget if p.id_in_group == 1: p.contribution_p1.max = p.contribution_p1_max() elif p.id_in_group == 2: p.contribution_p2.max = p.contribution_p2_max() elif p.id_in_group == 3: p.contribution_p3.max = p.contribution_p3_max() elif p.id_in_group == 4: p.contribution_p4.max = p.contribution_p4_max() elif p.id_in_group == 5: p.contribution_p5.max = p.contribution_p5_max() elif group.round_number == 1: import random chance = random.randint(1, 10) group.randomness = chance for p in group.get_players(): p.partyid = p.participant.vars['partyid'] p.budget = Constants.endowment[p.id_in_group - 1] if p.id_in_group == 1: p.contribution_p1_max() elif p.id_in_group == 2: p.contribution_p2_max() elif p.id_in_group == 3: p.contribution_p3_max() elif p.id_in_group == 4: p.contribution_p4_max() elif p.id_in_group == 5: p.contribution_p5_max() def threshold_payoff(group): if group.round_number == Constants.num_rounds: randomvar = group.in_round(1).randomness if group.total_contribution < Constants.threshold: if randomvar >= 6: return 2 else: for p in group.get_players(): p.budget = 0 return 1 else: return 0 return 3 def check_equality(group): equal = True for p in group.get_players(): if p.budget != Constants.endowment[0]: equal = False if equal != True: return 0 else: return 1 def cat_remaining(group): remaining = Constants.threshold if group.round_number != 1: remaining -= group.in_round(group.round_number - 1).total_contribution return remaining class Group(BaseGroup): total_contribution = models.CurrencyField(initial=0) randomness = models.IntegerField() set_payoffs = set_payoffs set_player_vars = set_player_vars threshold_payoff = threshold_payoff check_equality = check_equality cat_remaining = cat_remaining def intro_answer_test(player): if player.first_intro_answer == 0.4 and player.second_intro_answer == 2 and player.third_intro_answer == 50: return True else: return False def contribution_p1_max(player): return player.budget def contribution_p2_max(player): return player.budget def contribution_p3_max(player): return player.budget def makeID(player): import random player.verificationID = random.randint(10000,99999) return(player.verificationID) def contribution_p4_max(player): return player.budget def contribution_p5_max(player): return player.budget class Player(BasePlayer): budget = models.CurrencyField(min=0) first_intro_answer = models.CurrencyField(choices=[[0, '$0.00'], [0.4, '$0.40'], [0.5, '$0.50'], [1, '$1.00'], [5, '$5.00']], widget=widgets.RadioSelectHorizontal) second_intro_answer = models.CurrencyField() third_intro_answer = models.IntegerField(choices=[[10, '10%'], [30, '30%'], [50, '50%'], [70, '70%'], [90, '90%'], [100, '100%']], widget=widgets.RadioSelectHorizontal) contribution_p1 = models.CurrencyField(initial=0, label='Enter your contribution', min=0) contribution_p2 = models.CurrencyField(initial=0, label='Enter your contribution', min=0) contribution_p3 = models.CurrencyField(initial=0, label='Enter your contribution', min=0) contribution_p4 = models.CurrencyField(initial=0, label='Enter your contribution', min=0) contribution_p5 = models.CurrencyField(initial=0, label='Enter your contribution', min=0) contribution = models.CurrencyField(min=0) verificationID = models.IntegerField() age = models.IntegerField(max=130, min=0) gender = models.StringField(choices=[['Male ', 'Male '], ['Female ', 'Female '], ['Other', 'Other']], widget=widgets.RadioSelect) state = models.StringField(choices=[['Alabama', 'Alabama'], ['Alaska', 'Alaska'], ['Arizona', 'Arizona'], ['Arkansas', 'Arkansas'], ['California', 'California'], ['Colorado', 'Colorado'], ['Connecticut', 'Connecticut'], ['Delaware', 'Delaware'], ['District of Columbia', 'District of Columbia'], ['Florida', 'Florida'], ['Georgia', 'Georgia'], ['Hawaii', 'Hawaii'], ['Idaho', 'Idaho'], ['Illinois', 'Illinois'], ['Indiana', 'Indiana'], ['Iowa', 'Iowa'], ['Kansas', 'Kansas'], ['Kentucky', 'Kentucky'], ['Louisiana', 'Louisiana'], ['Maine', 'Maine'], ['Maryland', 'Maryland'], ['Massachusetts', 'Massachusetts'], ['Michigan', 'Michigan'], ['Minnesota', 'Minnesota'], ['Mississippi', 'Mississippi'], ['Missouri', 'Missouri'], ['Montana', 'Montana'], ['Nebraska', 'Nebraska'], ['Nevada', 'Nevada'], ['New Hampshire', 'New Hampshire'], ['New Jersey', 'New Jersey'], ['New Mexico', 'New Mexico'], ['New York', 'New York'], ['North Carolina', 'North Carolina'], ['North Dakota', 'North Dakota'], ['Ohio', 'Ohio'], ['Oklahoma', 'Oklahoma'], ['Oregon', 'Oregon'], ['Pennsylvania', 'Pennsylvania'], ['Rhode Island', 'Rhode Island'], ['South Carolina', 'South Carolina'], ['South Dakota', 'South Dakota'], ['Tennessee', 'Tennessee'], ['Texas', 'Texas'], ['Utah', 'Utah'], ['Vermont', 'Vermont'], ['Virginia', 'Virginia'], ['Washington', 'Washington'], ['West Virginia', 'West Virginia'], ['Wisconsin', 'Wisconsin'], ['Wyoming', 'Wyoming']]) ethnicity = models.StringField(choices=[['African American', 'African American'], ['Asian American', 'Asian American'], ['Hispanic / Latino', 'Hispanic / Latino'], ['Native American', 'Native American'], ['White', 'White'], ['Other', 'Other']], widget=widgets.RadioSelect) ideology_identification = models.IntegerField(max=10, min=0) vote_2016 = models.StringField(choices=[['Trump', 'Trump'], ['Clinton', 'Clinton'], ['Other', 'Other'], ['Did not vote', 'Did not vote']], widget=widgets.RadioSelect) vote_2020 = models.StringField(choices=[['Biden', 'Biden'], ['Trump', 'Trump'], ['Other', 'Other'], ['Did not vote', 'Did not vote']], widget=widgets.RadioSelect) income = models.StringField(choices=[['Less than $40,000', 'Less than $40,000'], ['$40,000 - $90,000', '$40,000 - $90,000'], ['$90,000 - $250,000', '$90,000 - $250,000'], ['More than $250,000', 'More than $250,000']], widget=widgets.RadioSelect) class_identification = models.StringField(choices=[['Working class', 'Working class'], ['Middle class', 'Middle class'], ['Upper-middle class', 'Upper-middle class'], ['Upper class', 'Upper class']], widget=widgets.RadioSelect) risk_aversion = models.StringField(choices=[['$2 or $0, the outcome decided by a coin flip', '$2 or $0, the outcome decided by a coin flip'], ['$1 guaranteed ', '$1 guaranteed '], ['Indifferent', 'Indifferent']], widget=widgets.RadioSelect) motivation_payoff = models.IntegerField(max=10, min=0) distribution_influence = models.IntegerField(max=10, min=0) contribution_fairness = models.StringField(choices=[['Everybody should pay the same amount, regardless of income', 'Everybody should pay the same amount, regardless of income'], ['Everybody should pay the same percentage of their income, regardless of income', 'Everybody should pay the same percentage of their income, regardless of income'], ['People with more money should pay a greater percentage of their income', 'People with more money should pay a greater percentage of their income'], ['Everybody should pay whatever they want', 'Everybody should pay whatever they want']], widget=widgets.RadioSelect) connection_same = models.IntegerField(max=10, min=0) connection_higher = models.IntegerField(max=10, min=0) connection_lower = models.IntegerField(max=10, min=0) group_trust = models.IntegerField(max=10, min=0) motivation_climate = models.IntegerField(max=10, min=0) attention_check = models.IntegerField(max=10, min=0) inequality_concern = models.IntegerField(max=10, min=0) climate_concern = models.IntegerField(max=10, min=0) climate_impact = models.IntegerField(max=10, min=0) platform_id = models.StringField() bonus_acceptance = models.BooleanField(choices=[[True, 'Yes'], [False, 'No']], label='Do you accept your bonus payoff?', widget=widgets.RadioSelectHorizontal) partyid = models.StringField() party_feelings_own = models.IntegerField(max=10, min=0) party_feelings_opposite = models.IntegerField(max=10, min=0) party_influence = models.IntegerField(max=10, min=0) trust_self = models.IntegerField(max=10, min=0) dictator_other = models.IntegerField(max=10, min=0) dictator_self = models.IntegerField(max=10, min=0) otherplayerendowment_1 = models.CurrencyField(label="1st Player's Endowment") otherplayerendowment_2 = models.CurrencyField(label="2nd Player's Endowment") otherplayerendowment_3 = models.CurrencyField(label="3rd Player's Endowment") otherplayerendowment_4 = models.CurrencyField(label="4th Player's Endowment") myendowment = models.CurrencyField(label='My Endowment') otherplayerparty_1 = models.StringField(label="1st Player's Political Party") otherplayerparty_2 = models.StringField(label="2nd Player's Political Party") otherplayerparty_3 = models.StringField(label="3rd Player's Political Party") otherplayerparty_4 = models.StringField(label="4th Player's Political Party") myparty = models.StringField(label='My Political Party') covid1 = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], widget=widgets.RadioSelect) covid2 = models.IntegerField(max=10, min=0) covid3 = models.IntegerField(max=10, min=0) education = models.StringField(choices=[['High school', 'High school'], ['2-year college program', '2-year college program'], ['4-year college program', '4-year college program'], ['Post-graduate program (Masters/Ph.D.)', 'Post-graduate program (Masters/Ph.D.)'], ['None of the above', 'None of the above']], widget=widgets.RadioSelect) intro_answer_test = intro_answer_test contribution_p1_max = contribution_p1_max contribution_p2_max = contribution_p2_max contribution_p3_max = contribution_p3_max makeID = makeID contribution_p4_max = contribution_p4_max contribution_p5_max = contribution_p5_max class GroupPlayers(WaitPage): group_by_arrival_time = True @staticmethod def is_displayed(player): return (player.round_number == 1) class Introduction(Page): form_model = 'player' form_fields = ['first_intro_answer', 'second_intro_answer', 'third_intro_answer'] timeout_seconds = 360 @staticmethod def is_displayed(player): return (player.round_number == 1) class Calculating(WaitPage): after_all_players_arrive = 'set_player_vars' body_text = 'Waiting for the other players to read through the introduction.' class ControlAnswers(Page): form_model = 'player' timeout_seconds = 120 @staticmethod def is_displayed(player): return (player.round_number == 1) class PlayerEndowmentsAndParty(Page): form_model = 'player' form_fields = ['myparty', 'myendowment', 'otherplayerparty_1', 'otherplayerendowment_1', 'otherplayerparty_2', 'otherplayerendowment_2', 'otherplayerparty_3', 'otherplayerendowment_3', 'otherplayerparty_4', 'otherplayerendowment_4'] timeout_seconds = 120 @staticmethod def is_displayed(player): return (player.round_number == 1) class Contribute(Page): form_model = 'player' timeout_seconds = 60 @staticmethod def get_form_fields(player): group = player.group if player.id_in_group == 1: return ['contribution_p1'] elif player.id_in_group == 2: return ['contribution_p2'] elif player.id_in_group == 3: return ['contribution_p3'] elif player.id_in_group == 4: return ['contribution_p4'] elif player.id_in_group == 5: return ['contribution_p5'] @staticmethod def vars_for_template(player): group = player.group return dict(equality = group.check_equality(), remaining = group.cat_remaining(), ) class ResultsWaitPage(WaitPage): after_all_players_arrive = 'set_payoffs' body_text = 'Waiting for other players to contribute.' class Results(Page): form_model = 'player' timeout_seconds = 180 @staticmethod def get_form_fields(player): group = player.group if group.threshold_payoff() == 0: return ['bonus_acceptance'] return [] @staticmethod def vars_for_template(player): group = player.group return dict(failure = group.threshold_payoff()) class FinalResults(Page): form_model = 'player' timeout_seconds = 180 @staticmethod def get_form_fields(player): group = player.group if group.threshold_payoff() == 2: return ['bonus_acceptance'] return [] @staticmethod def is_displayed(player): group = player.group return (player.round_number == Constants.num_rounds and group.total_contribution < Constants.threshold) @staticmethod def vars_for_template(player): group = player.group return dict(failure = group.threshold_payoff()) class Survey1(Page): form_model = 'player' form_fields = ['age', 'gender', 'state', 'ethnicity', 'ideology_identification', 'vote_2016', 'income', 'class_identification', 'risk_aversion', 'motivation_payoff', 'distribution_influence', 'contribution_fairness', 'vote_2020', 'education'] @staticmethod def is_displayed(player): return (player.round_number == Constants.num_rounds) class Survey2(Page): form_model = 'player' form_fields = ['trust_self', 'group_trust', 'connection_same', 'connection_higher', 'connection_lower'] @staticmethod def is_displayed(player): return (player.round_number == Constants.num_rounds) class Survey3(Page): form_model = 'player' form_fields = ['party_feelings_own', 'party_feelings_opposite', 'party_influence', 'dictator_other', 'dictator_self'] @staticmethod def is_displayed(player): return (player.round_number == Constants.num_rounds) class Survey4(Page): form_model = 'player' form_fields = ['motivation_climate', 'attention_check', 'inequality_concern', 'climate_concern', 'climate_impact', 'platform_id', 'covid1', 'covid2', 'covid3'] @staticmethod def is_displayed(player): return (player.round_number == Constants.num_rounds) class VerificationID(Page): form_model = 'player' timeout_seconds = 120 @staticmethod def is_displayed(player): return (player.round_number == Constants.num_rounds) @staticmethod def vars_for_template(player): return dict(ID = player.makeID()) page_sequence = [GroupPlayers, Introduction, Calculating, ControlAnswers, PlayerEndowmentsAndParty, Contribute, ResultsWaitPage, Results, FinalResults, Survey1, Survey2, Survey3, Survey4, VerificationID]