from otree.api import * doc = """ Your app description """ class C(BaseConstants): NAME_IN_URL = 'selection_experiment' PLAYERS_PER_GROUP = 5 NUM_ROUNDS = 1 endowment = 500000 class Subsession(BaseSubsession): pass class Group(BaseGroup): treatment = models.StringField() patronage_chosen = models.IntegerField( label = "Which participant do you want to chose as your public official?", widget=widgets.RadioSelectHorizontal, choices=[ [2, "P2"], [3, "P3"], [4, "P4"], [5, "P5"]]) remaining_amount = models.IntegerField() payoff1 = models.IntegerField( label = "Player 1", min = 0 ) payoff2 = models.IntegerField( min=0, label="Player 2" ) payoff3 = models.IntegerField( min=0, label="Player 3" ) payoff4 = models.IntegerField( min=0, label="Player 4" ) payoff5 = models.IntegerField( min=0, label="Player 5" ) dictator_payoff = models.IntegerField( min=0, max=500000, label="I want to keep:" ) public_official = models.IntegerField() def payoff1_max(self): return 500000 - self.dictator_payoff def payoff2_max(self): return 500000 - self.dictator_payoff def payoff3_max(self): return 500000 - self.dictator_payoff def payoff4_max(self): return 500000 - self.dictator_payoff def payoff5_max(self): return 500000 - self.dictator_payoff #score for merit condition top_score = models.IntegerField() class Player(BasePlayer): # the stuff that will be presented to patron for selection age = models.IntegerField(label = "What's your age?", min = 18, max = 99, blank = True) education = models.IntegerField( label = "What is the highest level of education you completed?", widget = widgets.RadioSelectHorizontal, choices=[ [1, "SD"], [2, "SMP"], [3, "SMA"], [4, "College"]]) gender = models.IntegerField(label = "What is your gender?", widget = widgets.RadioSelectHorizontal, choices = [[1, "Male"], [2, "Female"]]) province = models.IntegerField(label = "What is your province?", # widget = widgets.RadioSelectHorizontal, choices = [[1, "Aceh"], [2, "Sumatera Utara"], [3, "Sumatera Barat"], [4, "Riau"], [5, "Jambi"], [6, "Sumatera Selatan"], [7, "Bengkulu"],[8, "Lampung"], [9, "Bangka Belitung"],[10, "Kepulauan Riau"],[11, "DKI Jakarta"], [12, "Jawa Barat"], [13, "Jawa Tengah"],[14, "DI Yogyakarta"],[15, "Jawa Timur"], [16, "Banten"], [17, "Bali"], [18, "NTB"],[19, "NTT"],[20, "Kalimantan Barat"],[21, "Kalimantan Tengah"], [22, "Kalimantan Selatan"],[23, "Kalimantan Timur"],[24, "Kalimantan Utara"], [25, "Sulawesi Utara"],[26, "Sulawesi Tengah"],[27, "Sulawesi Selatan"], [28, "Sulawesi Tenggara"], [29, "Gorontalo"], [30, "Sulawesi Barat"], [31, "Maluku"], [32, "Maluku Utara"], [33, "Papua Barat"], [34, "Papua"]]) religion = models.IntegerField(label = "What is your religion?", widget = widgets.RadioSelectHorizontal, choices = [[1, "Islam"], [2, "Christianity"], [3, "Catholic"], [4, "Hindu"], [5, "Buddha"], [6, "Other"]]) ethnicity = models.IntegerField(label = "Other than Indonesian, what is your ethnic identity?", widget = widgets.RadioSelectHorizontal, choices = [[1, "Jawa"], [2, "Sunda"], [3, "Melayu"], [4, "Madura"], [5, "Bugis"], [6, "Betawi"], [7, "Batak"], [8, "Minang"], [9, "Bali"], [10, "Other"]]) is_age = models.BooleanField(label ="Do you confirm that you are at least 18 years old?", choices = [ [True, "Yes"], [False, "No"]]) is_consent = models.BooleanField(label ="Do you consent to participate?", choices = [ [True, "Yes"], [False, "No"]]) bf1 = models.IntegerField(label = "I tend to find fault with others easily", widget = widgets.RadioSelectHorizontal, choices = [[1, "Strongly agree"], [2, "Somewhat agree"], [3, "Neither agree nor disagree"], [4, "Somewhat disagree"], [5, "Strongly disagree"]]) bf2 = models.IntegerField(label = "I am generally helpful and unselfish with others", widget = widgets.RadioSelectHorizontal, choices = [[1, "Strongly agree"], [2, "Somewhat agree"], [3, "Neither agree nor disagree"], [4, "Somewhat disagree"], [5, "Strongly disagree"]]) bf3 = models.IntegerField(label = "I often start quarrels with others", widget = widgets.RadioSelectHorizontal, choices = [[1, "Strongly agree"], [2, "Somewhat agree"], [3, "Neither agree nor disagree"], [4, "Somewhat disagree"], [5, "Strongly disagree"]]) bf4 = models.IntegerField(label = "I usually have a forgiving nature", widget = widgets.RadioSelectHorizontal, choices = [[1, "Strongly agree"], [2, "Somewhat agree"], [3, "Neither agree nor disagree"], [4, "Somewhat disagree"], [5, "Strongly disagree"]]) bf5 = models.IntegerField(label = "I am a reliable worker", widget = widgets.RadioSelectHorizontal, choices = [[1, "Strongly agree"], [2, "Somewhat agree"], [3, "Neither agree nor disagree"], [4, "Somewhat disagree"], [5, "Strongly disagree"]]) bf6 = models.IntegerField(label = "I tend to be disorganized", widget = widgets.RadioSelectHorizontal, choices = [[1, "Strongly agree"], [2, "Somewhat agree"], [3, "Neither agree nor disagree"], [4, "Somewhat disagree"], [5, "Strongly disagree"]]) bf7 = models.IntegerField(label = "I always perservere until the task is done", widget = widgets.RadioSelectHorizontal, choices = [[1, "Strongly agree"], [2, "Somewhat agree"], [3, "Neither agree nor disagree"], [4, "Somewhat disagree"], [5, "Strongly disagree"]]) bf8 = models.IntegerField(label = "I am easily distracted", widget = widgets.RadioSelectHorizontal, choices = [[1, "Strongly agree"], [2, "Somewhat agree"], [3, "Neither agree nor disagree"], [4, "Somewhat disagree"], [5, "Strongly disagree"]]) vign_1 = models.IntegerField(label = "Here's a moral trade-off: to what extent do you agree", widget = widgets.RadioSelectHorizontal, choices = [[1, "Strongly agree"], [2, "Somewhat agree"], [3, "Neither agree nor disagree"], [4, "Somewhat disagree"], [5, "Strongly disagree"]]) vign_2 = models.IntegerField(label = "Here's a second moral trade-off: to what extent do you agree", widget = widgets.RadioSelectHorizontal, choices = [[1, "Strongly agree"], [2, "Somewhat agree"], [3, "Neither agree nor disagree"], [4, "Somewhat disagree"], [5, "Strongly disagree"]]) attn_check1 = models.IntegerField( label = "Comprehension Question:", widget = widgets.RadioSelectHorizontal, choices = [ [1, "100,000 IDR"], [2, "200,000 IDR"], [3, "300,000 IDR"], [4, "400,000 IDR"], [5, "500,000 IDR"], ]) #MERIT QUESTIONS score_merit = models.IntegerField() merit1 = models.IntegerField( label = "What is the answer to this question?", widget = widgets.RadioSelectHorizontal, choices=[ [1, "Option A"], [2, "Option B"], [3, "Option C"], [4, "Option D"]]) merit2 = models.IntegerField( label = "What is the answer to this question?", widget = widgets.RadioSelectHorizontal, choices=[ [1, "Option A"], [2, "Option B"], [3, "Option C"], [4, "Option D"]]) merit3 = models.IntegerField( label = "What is the answer to this question?", widget = widgets.RadioSelectHorizontal, choices=[ [1, "Option A"], [2, "Option B"], [3, "Option C"], [4, "Option D"]]) merit4 = models.IntegerField( label = "What is the answer to this question?", widget = widgets.RadioSelectHorizontal, choices=[ [1, "Option A"], [2, "Option B"], [3, "Option C"], [4, "Option D"]]) merit5 = models.IntegerField( label = "What is the answer to this question?", widget = widgets.RadioSelectHorizontal, choices=[ [1, "Option A"], [2, "Option B"], [3, "Option C"], [4, "Option D"]]) post1 = models.IntegerField( label = "What is the answer to this question?", widget = widgets.RadioSelectHorizontal, choices=[ [1, "Option A"], [2, "Option B"], [3, "Option C"], [4, "Option D"]]) post2 = models.IntegerField( label = "What is the answer to this question?", widget = widgets.RadioSelectHorizontal, choices=[ [1, "Option A"], [2, "Option B"], [3, "Option C"], [4, "Option D"]]) post3 = models.IntegerField( label = "What is the answer to this question?", widget = widgets.RadioSelectHorizontal, choices=[ [1, "Option A"], [2, "Option B"], [3, "Option C"], [4, "Option D"]]) post4 = models.IntegerField( label = "What is the answer to this question?", widget = widgets.RadioSelectHorizontal, choices=[ [1, "Option A"], [2, "Option B"], [3, "Option C"], [4, "Option D"]]) post5 = models.IntegerField( label = "What is the answer to this question?", widget = widgets.RadioSelectHorizontal, choices=[ [1, "Option A"], [2, "Option B"], [3, "Option C"], [4, "Option D"]]) post6_dictator = models.IntegerField( label = "What is the answer to this question?", widget = widgets.RadioSelectHorizontal, choices=[ [1, "Option A"], [2, "Option B"], [3, "Option C"], [4, "Option D"]]) post7_dictator = models.IntegerField( label = "What is the answer to this question?", widget = widgets.RadioSelectHorizontal, choices=[ [1, "Option A"], [2, "Option B"], [3, "Option C"], [4, "Option D"]]) phone_number = models.IntegerField( label = "What is the phone number to which we should send the e-wallet payment?", min = 0 ) #class SortitionPlayer(Player): # pass #class PatronagePlayer(Player): # patron = models.BooleanField() # is this player the patron # public_official = models.BooleanField() #is this player the public official #FUNCTIONS #This is for assigning the treatment condition of the group def creating_session(subsession): # Old code # import itertools # treatments = itertools.cycle(["random", "merit", "patronage"]) # Logic of selecting a random remaining treatment import random treatments = ["random", "merit", "patronage"] # empty treatment list is not accounted for in logic below remaining_treatments = list(range(len(treatments))) # integer array keeping track of remaining treatments that are unassigned for group in subsession.get_groups(): if not remaining_treatments: # Case when remaining_treatments list is empty -> meaning all treatments have been already been assigned once before remaining_treatments = list(range(len(treatments))) # resetting the list random_treatment_index = random.choice(remaining_treatments) group.treatment = treatments[random_treatment_index] print('set condition to', group.treatment) remaining_treatments.remove(random_treatment_index) # PAGES #PRE EXPERIMENT PAGES # PAGES class ConsentPage(Page): form_model = "player" form_fields = ["is_age", "is_consent"] class GameEnd(Page): ### Display only to participants who pass all 2 mandatory conditions @staticmethod def is_displayed(player): return player.is_age == False or player.is_consent == False class SurveyPre(Page): form_model = "player" form_fields = ["age","gender", "education", "province", "religion", "ethnicity"] ### Store the variable gender for next use #def before_next_page(player, timeout_happened): # player.participant.gender = player.gender class SurveyPre2(Page): form_model = "player" form_fields = ["bf1", "bf2", "bf3", "bf4", "bf5", "bf6", "bf7", "bf8"] ### Store the variable gender for next use #def before_next_page(player, timeout_happened): # player.participant.gender = player.gender class InstructionsPatronage(Page): form_model = "player" form_fields = ["attn_check1"] @staticmethod def is_displayed(player): group = player.group return group.treatment == "patronage" @staticmethod def error_message(player: Player, values): if values["attn_check1"] != 3: return "That answer is incorect. Please read the instructions closely." class SelectionPatronage(Page): form_model = 'group' timeout_seconds = 60 def get_form_fields(player: Player): group = player.group if player.id_in_group == 1: return ["patronage_chosen"] else: pass def is_displayed(player): group = player.group #return group.treatment == "patronage" and player.id_in_group == 1 return group.treatment == "patronage" def vars_for_template(player: Player): return dict(others=player.get_others_in_group()) def before_next_page(player, timeout_happened): import random group = player.group if timeout_happened and player.id_in_group == 1: group.patronage_chosen = random.randint(2,5) group.public_official = group.patronage_chosen class InstructionsMerit(Page): form_model = "player" form_fields = ["attn_check1"] @staticmethod def is_displayed(player): group = player.group return group.treatment == "merit" @staticmethod def error_message(player: Player, values): if values["attn_check1"] != 3: return "That answer is incorect. Please read the instructions closely." class SelectionMerit(Page): def is_displayed(player): group = player.group return group.treatment == "merit" form_model = 'player' form_fields = ['merit1', 'merit2', 'merit3', 'merit4', 'merit5'] class MeritReveal(Page): def is_displayed(player): group = player.group return group.treatment == "merit" class InstructionsRandom(Page): form_model = "player" form_fields = ["attn_check1"] @staticmethod def is_displayed(player): group = player.group return group.treatment == "random" def before_next_page(player, timeout_happened): import random group = player.group group.public_official = random.randint(1, 5) @staticmethod def error_message(player: Player, values): if values["attn_check1"] != 3: return "That answer is incorect. Please read the instructions closely." class SelectionRandom(Page): pass class VignetteQuestions(Page): form_model = "player" form_fields = ['vign_1', 'vign_2'] class ResultsWaitPage(WaitPage): #for the unselected pass class DictatorKeep(Page): form_model = "group" form_fields = ["dictator_payoff"] def is_displayed(player): group = player.group return player.id_in_group == group.public_official # def before_next_page(player): # group = player.group # group.remaining_amount = 500000 - player.dictator_payoff class DictatorAllocate(Page): form_model = "group" @staticmethod def get_form_fields(player: Player): group = player.group patron_id = group.public_official if patron_id == 1: return ['payoff2', 'payoff3', 'payoff4', 'payoff5'] elif patron_id == 2: return ['payoff1', 'payoff3', 'payoff4', 'payoff5'] elif patron_id == 3: return ['payoff1', 'payoff2', 'payoff4', 'payoff5'] elif patron_id == 4: return ['payoff1', 'payoff2', 'payoff3', 'payoff5'] else: return ['payoff1', 'payoff2', 'payoff3', 'payoff4'] def is_displayed(player): group = player.group return player.id_in_group == group.public_official def vars_for_template(player: Player): group = player.group return dict(others=player.get_others_in_group(), remainder=500000 - group.dictator_payoff) @staticmethod def error_message(player: Player, values): group = player.group public_official_id = group.public_official if public_official_id == 1 and values['payoff2'] + values['payoff3'] + values['payoff4'] + values['payoff5'] > 500000-group.dictator_payoff: return 'The sums cannot exceed' + str(group.remaining_amount) elif public_official_id == 2 and values['payoff1'] + values['payoff3'] + values['payoff4'] + values['payoff5'] > 500000-group.dictator_payoff: return 'The sums cannot exceed' + str(group.remaining_amount) elif public_official_id == 3 and values['payoff2'] + values['payoff1'] + values['payoff4'] + values['payoff5'] > 500000-group.dictator_payoff: return 'The sums cannot exceed' + str(group.remaining_amount) elif public_official_id == 4 and values['payoff2'] + values['payoff3'] + values['payoff1'] + values['payoff5'] > 500000-group.dictator_payoff: return 'The sums cannot exceed' + str(group.remaining_amount) elif public_official_id == 5 and values['payoff2'] + values['payoff3'] + values['payoff4'] + values['payoff1'] > 500000-group.dictator_payoff: return 'The sums cannot exceed' + str(group.remaining_amount) #class ResultsWaitPage(WaitPage): # after_all_players_arrive = set_payoffs class AllocateWaitPage(WaitPage): def compute_remain(group): group.remaining_amount = 500000 - group.dictator_payoff public_official_id = group.public_official if public_official_id == 1: group.payoff1 = group.dictator_payoff elif public_official_id == 2: group.payoff2 = group.dictator_payoff elif public_official_id == 3: group.payoff3 = group.dictator_payoff elif public_official_id == 4: group.payoff4 = group.dictator_payoff elif public_official_id == 5: group.payoff5 = group.dictator_payoff after_all_players_arrive = compute_remain class MeritScoreWaitPage(WaitPage): def is_displayed(player): group = player.group return group.treatment == "merit" def compute_scores(group): players = group.get_players() for p in players: score = 0 if p.merit1 == 1: # change the correct answers, of course... score = score + 1 if p.merit2 == 1: score = score + 1 if p.merit3 == 1: score = score + 1 if p.merit4 == 1: score = score + 1 if p.merit5 == 1: score = score + 1 p.score_merit = score after_all_players_arrive = compute_scores class MeritScoreSortPage(WaitPage): def is_displayed(player): group = player.group return group.treatment == "merit" def sort_players(group): import random players = group.get_players() scores = [p.score_merit for p in players] max_score = max(scores) group.top_score = max_score winner_ids = [] for p in players: if p.score_merit == max_score: winner_ids.append(p.id_in_group) winner_po = random.choice(winner_ids) group.public_official = winner_po after_all_players_arrive = sort_players class WaitForP1(WaitPage): pass class Results(Page): def vars_for_template(player): group = player.group if player.id_in_group == 1: return dict( total_payout=group.payoff1 + 50000 ) if player.id_in_group == 2: return dict( total_payout=group.payoff2 + 50000 ) if player.id_in_group == 3: return dict( total_payout=group.payoff3 + 50000 ) if player.id_in_group == 4: return dict( total_payout=group.payoff4 + 50000 ) if player.id_in_group == 5: return dict( total_payout=group.payoff5 + 50000 ) class SurveyPost(Page): form_model = "player" def get_form_fields(player: Player): group = player.group po_id = group.public_official if po_id == player.id_in_group: return ["post1", "post2", "post3", "post4", "post5", "post6_dictator", "post7_dictator", "phone_number"] elif po_id != player.id_in_group: return ["post1", "post2", "post3", "post4", "post5", "phone_number"] def error_message(player: Player, values): if len(str(values["phone_number"])) < 10 or len(str(values["phone_number"])) > 13: return "Please enter a valid mobile phone number (omit the country code)" class EndDictatorSurvey(Page): pass class EndNonDictatorSurvey(Page): pass class Debrief(Page): pass page_sequence = [ConsentPage, GameEnd, SurveyPre, SurveyPre2, InstructionsPatronage, InstructionsMerit, InstructionsRandom, WaitForP1, SelectionMerit, SelectionPatronage, MeritScoreWaitPage, MeritScoreSortPage, MeritReveal, WaitForP1, VignetteQuestions, DictatorKeep, AllocateWaitPage, DictatorAllocate, ResultsWaitPage, Results, SurveyPost, Debrief]