from otree.api import * doc = """ Main part of the game """ class C(BaseConstants): NAME_IN_URL = 'game' PLAYERS_PER_GROUP = None NUM_ROUNDS = 5 ENDOWMENT = cu(1000) TAX_RATE = 5 class Subsession(BaseSubsession): pass def creating_session(subsession): import itertools treatment_list = [True, False] treatments = itertools.cycle (treatment_list) # 1 is bad news, 0 is good news, None is classic if subsession.round_number == 1: for player in subsession.get_players(): participant = player.participant participant.treatment = next(treatments) player.treatment = participant.treatment #print('set treatment to', participant.treatment) class Group(BaseGroup): pass class Player(BasePlayer): audit_score = models.IntegerField() treated = models.BooleanField() declared_income = models.CurrencyField( min=0, max=C.ENDOWMENT, label="Please report how much you have earned" ) undeclared_income = models.CurrencyField() tax_paid_on_declared_income = models.CurrencyField() audited = models.BooleanField(initial=False) additional_tax_payable = models.CurrencyField() income_after_tax = models.CurrencyField(label="Your income after tax is") fine = models.CurrencyField() earnings = models.CurrencyField() amount_embezzled = models.CurrencyField() emigrate_decision = models.IntegerField(initial=99, choices=[ [1, 'Yes'], [0, 'No'], ], label="Do you wish to emigrate?", blank=True) emigration_reasoning = models.LongStringField( label="Please describe in at least 2 sentences the reasoning behind your decision?" ) visa_application = models.LongStringField( label="Please describe in at least 2 sentences why your visa should " "be granted. The participants in Country B " "will read your visa application before making a decision " "on whether or not to grant you a visa.") treatment = models.BooleanField() c_question1 = models.StringField( label='1. Imagine that out of 1000 points you received at the beginning of the tax year,' ' you reported that you earned 500 points. ' 'With the tax rate being 20% (1/5 of reported earnings) and no audit, how many points will' ' you earn at the end of that tax year?', widget=widgets.RadioSelect, choices=["900 points", "500 points", "400 points"]) c_question2 = models.StringField( label='2. Imagine that out of 1000 points you received at the beginning of the tax year,' ' you reported that you earned 100 points. ' 'With the tax rate being 20% (1/5 of reported earnings) and after being audited,' ' how many points will' ' you earn at the end of that tax year?', widget=widgets.RadioSelect, choices=["980 points", "620 points", "800 points"]) c_question3 = models.StringField( initial= '2000 points', label='3. How many points do participants in County B receive in each round?', widget=widgets.RadioSelect, choices=["2000 points", "1000 points", "5000 points"]) #Error messages def c_question1_error_message(player, value): if value != "900 points": return 'This is not a correct answer. Your earnings are calculated by subtracting your ' \ 'initial 1000 points by the amount of tax paid on your reported income (500/5 = 100). Try again.' def c_question2_error_message(player, value): if value != "620 points": return 'This is not a correct answer. Your earnings are calculated by subtracting your ' \ 'initial 1000 points by the amount of tax paid on your reported income (100/5 = 20 points),' \ ' your additional tax payable (180 points) and a fine (1 point for each underreported tax = 180 points).' \ ' Try again.' def c_question3_error_message(player, value): if value != "2000 points": return 'This is not a correct answer. Participants in Country B receive double the points than' \ ' participants in your Country.' def emigrate_decision_error_message(player, value): if value > 1: return 'Please select your decision' def set_payoffs(player: Player): import random player.treated = player.participant.treatment player.undeclared_income = C.ENDOWMENT - player.declared_income player.tax_paid_on_declared_income = player.declared_income / C.TAX_RATE player.income_after_tax = player.declared_income - player.tax_paid_on_declared_income player.additional_tax_payable = player.undeclared_income / C.TAX_RATE player.fine = player.additional_tax_payable player.audit_score = random.randint(1, 5) ####this is the audit rate #print(player.audit_score) if player.declared_income < C.ENDOWMENT: if player.audit_score == 1: player.payoff = C.ENDOWMENT - player.tax_paid_on_declared_income - player.additional_tax_payable - player.fine player.audited = 1 else: player.payoff = C.ENDOWMENT - player.tax_paid_on_declared_income player.audited = 0 player.additional_tax_payable = 0 player.fine = 0 else: player.payoff = C.ENDOWMENT - player.tax_paid_on_declared_income player.amount_embezzled = (player.tax_paid_on_declared_income + player.fine + player.additional_tax_payable) / 2 class Instructions2(Page): @staticmethod def is_displayed(player): if player.round_number == 1: return player.participant.treatment != None class Instructions3(Page): @staticmethod def is_displayed(player): if player.round_number == 1: return player class ComprehensionQuestions(Page): form_model = 'player' form_fields = ['c_question1', 'c_question2'] @staticmethod def is_displayed(player): if player.round_number == 1: return player class TaxReport(Page): form_model = 'player' form_fields = ['declared_income'] @staticmethod def before_next_page(player, timeout_happened): set_payoffs(player) class Results(Page): pass class EmigrationDecision(Page): form_model = 'player' form_fields = ['emigrate_decision'] @staticmethod def before_next_page(player: Player, timeout_happened): player.participant.emig_decision = player.emigrate_decision @staticmethod def is_displayed(player): if player.round_number == 2: return player.participant.treatment != None class VisaApplication(Page): form_model = 'player' form_fields = ['visa_application'] @staticmethod def is_displayed(player): if player.round_number == 2: return player.participant.treatment != None and player.emigrate_decision == 1 class VisaDecision(Page): @staticmethod def is_displayed(player): if player.round_number == 2 and player.emigrate_decision==1: return player.participant.treatment == True class VisaDecisions(Page): @staticmethod def is_displayed(player): if player.round_number >=2 and player.round_number <5 and player.emigrate_decision==1: return player.treated==0 class FinalResultsA(Page): @staticmethod def is_displayed(player): return player.round_number == 5 @staticmethod def app_after_this_page(player, upcoming_apps): #print('upcoming_apps is', upcoming_apps) if player.participant.treatment == False and player.participant.emig_decision == 1: return upcoming_apps[0] else: return upcoming_apps[-1] page_sequence = [Instructions2, Instructions3, ComprehensionQuestions, TaxReport, Results, EmigrationDecision, VisaApplication, VisaDecision, VisaDecisions, FinalResultsA]