from otree.api import * import random doc = """ Asymmetric game Part II """ class C(BaseConstants): NAME_IN_URL = 'Asym' PLAYERS_PER_GROUP = 2 NUM_ROUNDS = 8 MULTIPLIER = 1.5 Pool = 20 Account = 20 treatment = ['nonudge', 'nudge'] class Subsession(BaseSubsession): pass def creating_session(subsession: Subsession): import itertools treatments = itertools.cycle([True, False]) for g in subsession.get_groups(): g.treatment = next(treatments) class Group(BaseGroup): contribution = models.IntegerField() extraction = models.IntegerField() size_pool = models.IntegerField() payoff_per_group = models.FloatField() additional_payoff1 = models.IntegerField() additional_payoff2 = models.IntegerField() pool = models.FloatField() treatment = models.BooleanField( doc="Group", label="Assigned group", choices=[ [True, 'nudge'], [False, 'nonudge'] ] ) class Player(BasePlayer): possible_payoff = models.FloatField() extraction = models.IntegerField( min=0, max=20, label="So viele Taler (zwischen 0 und 20) nehme ich aus dem Gemeinschaftsprojekt:" ) contribution = models.IntegerField( min=0, max=20, label="So viele Taler (zwischen 0 und 20) gebe ich in das Gemeinschaftsprojekt:" ) thoughts1 = models.LongStringField( blank=False, label="Bitte notieren Sie Ihre Gedanken und Überlegungen in folgendem Textfeld:" ) thoughts2 = models.LongStringField( blank= True, label="Bitte notieren Sie Ihre Gedanken und Überlegungen in folgendem Textfeld:" ) Frage1 = models.StringField( label='1. Wie viele Teilnehmende verwalten ein Gemeinschaftsprojekt?', choices=['2', '3', '6'], widget=widgets.RadioSelect, ) Frage2 = models.BooleanField( label='2. Das Experiment besteht aus drei Teilen. Jeder Teilnehmende wird in Teil 2 einem anderen Teilnehmenden zugeordnet als in Teil 1.', choices=[[True, 'Richtig'], [False, 'Falsch']], widget=widgets.RadioSelect, ) Frage3 = models.StringField( label='3. Die Taler, die nach den Entscheidungen der Teilnehmenden im Gemeinschaftsprojekt sind,', choices=['werden auf beide Teilnehmende aufgeteilt.', 'wachsen auf das Anderthalbfache an und werden dann auf beide Teilnehmende aufgeteilt.', 'verfallen.'], widget=widgets.RadioSelect, ) Frage4 = models.StringField( label='4. Stellen Sie sich vor, dass der erste Teilnehmende keine der 20 Taler aus seinem privaten Konto ins Gemeinschaftsprojekt gibt und der zweite Teilnehmende alles (20 Taler) aus dem Gemeinschaftsprojekt nimmt. Wie viele Taler bekommt jeder Teilnehmende insgesamt aus dem Gemeinschaftsprojekt und dem privaten Konto ausgezahlt?', choices=['0', '10', '15', '20', '30', '40'], widget=widgets.RadioSelect, ) Frage5 = models.StringField( label='5. Stellen Sie sich vor, dass der erste Teilnehmende alles (20 Taler) in das Gemeinschaftsprojekt gibt und der zweite Teilnehmende nichts aus dem Gemeinschaftsprojekt nimmt. Wie viele Taler bekommt jeder Teilnehmende insgesamt aus dem Gemeinschaftsprojekt und dem privaten Konto ausgezahlt?', choices=['0', '10', '15', '20', '30', '40'], widget=widgets.RadioSelect, ) Frage6 = models.StringField( label='6. Wie viele Taler bekommt der erste Teilnehmende aus dem Gemeinschaftsprojekt und dem privaten Konto insgesamt ausgezahlt?', choices=['0', '10', '15', '20', '35', '40'], widget=widgets.RadioSelect, ) Frage7 = models.StringField( label='7. Wie viele Taler bekommt der erste Teilnehmende aus dem Gemeinschaftsprojekt und dem privaten Konto insgesamt ausgezahlt?', choices=['0', '10', '15', '20', '35', '40'], widget=widgets.RadioSelect, ) Frage8 = models.StringField( label='8. Welche Aktion maximiert die Gesamtauszahlung beider Teilnehmenden?', choices=['Alle Taler in das Gemeinschaftsprojekt geben und keine Taler aus dem Gemeinschaftsprojekt nehmen.', 'Keine Taler in das Gemeinschaftsprojekt geben und alle Taler aus dem Gemeinschaftsprojekt nehmen.'], widget=widgets.RadioSelect, ) Frage9 = models.StringField( label='9. Welche Aktion maximiert Ihre Auszahlung?', choices=['Alle Taler in das Gemeinschaftsprojekt geben bzw. keine Taler aus dem Gemeinschaftsprojekt nehmen.', 'Keine Taler in das Gemeinschaftsprojekt geben bzw. alle Taler aus dem Gemeinschaftsprojekt nehmen.'], widget=widgets.RadioSelect, ) class IncorrectResponse(ExtraModel): player = models.Link(Player) field_name = models.StringField() response = models.StringField() def set_possible_payoffs(group: Group): p1 = group.get_player_by_id(1) p2 = group.get_player_by_id(2) contribution = p1.contribution extraction = p2.extraction group.size_pool = C.Pool - extraction + contribution group.additional_payoff1 = C.Account - contribution group.additional_payoff2 = extraction group.pool = group.size_pool * C.MULTIPLIER group.payoff_per_group = group.pool / 2 p1.possible_payoff = group.payoff_per_group + group.additional_payoff1 p2.possible_payoff = group.payoff_per_group + group.additional_payoff2 class MyWaitPage(WaitPage): wait_for_all_groups = True after_all_players_arrive = 'creating_session' class Introduction(Page): @staticmethod def is_displayed(player): return player.round_number == 1 class Examples(Page): @staticmethod def is_displayed(player): return player.round_number == 1 class Instruction(Page): @staticmethod def is_displayed(player): return player.round_number == 1 class Intro2(Page): timeout_seconds = 10 @staticmethod def is_displayed(player): return player.round_number >= 2 class RuleQuizPage(Page): form_model = 'player' form_fields = ['Frage1', 'Frage2', 'Frage3', 'Frage4', 'Frage5', 'Frage6', 'Frage7', 'Frage8', 'Frage9'] @staticmethod def is_displayed(player): return player.round_number == 1 @staticmethod def error_message(player: Player, values): solutions = dict(Frage1='2', Frage2=True, Frage3='wachsen auf das Anderthalbfache an und werden dann auf beide Teilnehmende aufgeteilt.', Frage4='20', Frage5='30', Frage6='15', Frage7='35', Frage8='Alle Taler in das Gemeinschaftsprojekt geben und keine Taler aus dem Gemeinschaftsprojekt nehmen.', Frage9='Keine Taler in das Gemeinschaftsprojekt geben bzw. alle Taler aus dem Gemeinschaftsprojekt nehmen.') errors = {f: 'Diese Antwort war nicht richtig.' for f in solutions if values[f] != solutions[f]} if errors: for name in errors: response = values[name] IncorrectResponse.create(player=player, field_name=name, response=str(response)) return errors class RulesWaitPage(WaitPage): @staticmethod def is_displayed(player): return player.round_number == 1 class Start(Page): @staticmethod def is_displayed(player): return player.round_number == 1 class Thoughts(Page): timeout_seconds = 100 form_model = 'player' form_fields = ['thoughts1'] @staticmethod def is_displayed(player): return player.round_number == 1 class Thoughts2(Page): timeout_seconds = 100 form_model = 'player' form_fields = ['thoughts2'] @staticmethod def is_displayed(player): return player.round_number >= 2 class Contribution(Page): form_model = 'player' form_fields = ['contribution'] @staticmethod def is_displayed(player): return player.id_in_group <= 1 class Extraction(Page): form_model = 'player' form_fields = ['extraction'] @staticmethod def is_displayed(player): return player.id_in_group > 1 class ResultsWaitPage(WaitPage): after_all_players_arrive = set_possible_payoffs class Results(Page): timeout_seconds = 30 @staticmethod def vars_for_template(player): group = player.group p1 = group.get_player_by_id(1) p2 = group.get_player_by_id(2) return dict( g=player.group.get_players(), p1=p1, p2=p2, earning=format(float(player.possible_payoff), '.2f').replace(".", ","), ) @staticmethod def before_next_page(player: Player, timeout_happened): import random participant = player.participant # if it's the last round if player.round_number == C.NUM_ROUNDS: random_round = random.randint(1, C.NUM_ROUNDS) participant.selected_round = random_round player_in_selected_round = player.in_round(random_round) player.payoff = player_in_selected_round.possible_payoff participant.vars['app1_payoff'] = player.payoff participant.vars['app1_round'] = random_round class Ende(Page): @staticmethod def is_displayed(player): return player.round_number == C.NUM_ROUNDS class BlankWaitPage(WaitPage): wait_for_all_groups = True @staticmethod def is_displayed(player): return player.round_number == C.NUM_ROUNDS class Blank(Page): @staticmethod def is_displayed(player): return player.round_number == C.NUM_ROUNDS page_sequence = [MyWaitPage, Introduction, Examples, Instruction, Intro2, RuleQuizPage, RulesWaitPage, Start, Thoughts, Thoughts2, Contribution, Extraction, ResultsWaitPage, Results, Ende, BlankWaitPage, Blank] def custom_export(players): """For data export page""" yield ['participant_code', 'id_in_session', 'round_number', 'field_name', 'response'] responses = IncorrectResponse.filter() for resp in responses: player = resp.player participant = player.participant yield [participant.code, participant.id_in_session, player.round_number, resp.field_name, resp.response]