from otree.api import * import random doc = """ Indi Give T3 Part 1 """ class C(BaseConstants): NAME_IN_URL = 'Indi_Give_T3_P1' PLAYERS_PER_GROUP = 2 NUM_ROUNDS = 8 MULTIPLIER = 1.5 Pool = 0 Account = 20 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() size_pool = models.IntegerField() payoff_per_group = models.FloatField() additional_payoff1 = models.IntegerField() additional_payoff2 = models.IntegerField() pool = models.FloatField() class Player(BasePlayer): possible_payoff = models.FloatField() contribution1 = models.IntegerField( min=0, max=20, label="So viele Taler (zwischen 0 und 20) gebe ich in das Gemeinschaftsprojekt:" ) contribution2 = models.IntegerField( min=0, max=20, label="So viele Taler (zwischen 0 und 20) gebe ich in das Gemeinschaftsprojekt:" ) Frage1 = models.StringField( label='1. Wie viele Teilnehmer verwalten ein Gemeinschaftsprojekt?', choices=['2', '3', '6'], widget=widgets.RadioSelect, ) Frage2 = models.BooleanField( label='2. Das Experiment besteht aus drei Teilen. Jeder Teilnehmer wird in Teil 2 einem anderen Teilnehmer zugeordnet als in Teil 1.', choices=[[True, 'Richtig'], [False, 'Falsch']], widget=widgets.RadioSelect, ) Frage3 = models.StringField( label='3. Die Taler, die nach den Entscheidungen im Gemeinschaftsprojekt sind,', choices=['werden auf beide Teilnehmer aufgeteilt.', 'wachsen auf das Anderthalbfache an und werden dann auf beide Teilnehmer aufgeteilt.', 'verfallen.'], widget=widgets.RadioSelect, ) Frage4 = models.StringField( label='4. Stellen Sie sich vor, dass beide Teilnehmer keine der 20 Taler aus ihrem privaten Konto ins Gemeinschaftsprojekt geben. Wie viele Taler bekommt jeder Teilnehmer 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 beide Teilnehmer alle 20 Taler aus ihren privaten Konten in das Gemeinschaftsprojekt geben. Wie viele Taler bekommt jeder Teilnehmer 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 Teilnehmer 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 zweite Teilnehmer 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 Teilnehmer?', choices=['Alle Taler in das Gemeinschaftsprojekt geben.', 'Keine Taler in das Gemeinschaftsprojekt geben.'], widget=widgets.RadioSelect, ) Frage9 = models.StringField( label='9. Welche Aktion maximiert Ihre individuelle Auszahlung?', choices=['Alle Taler in das Gemeinschaftsprojekt geben.', 'Keine Taler in das Gemeinschaftsprojekt geben.'], widget=widgets.RadioSelect, ) Strategy_Q1 = models.StringField( label='Was denken Sie: Welche der folgenden Strategien beschreibt das Verhalten eines typischen Teilnehmers im nächsten Teil am besten? Das heißt, welche der folgenden Strategien ist die häufigste?', choices=['(1) Immer wenige/keine Taler (d.h. zwischen 0-6 Taler) geben.', '(2) Immer viele/alle Taler (d.h. zwischen 14-20 Taler) geben.', '(3) In der ersten Runde viele/alle Taler geben. In den folgenden Runden das Verhalten des anderen Teilnehmers aus der vorherigen Runde spiegeln: Hat er viele/alle Taler gegeben, gibt man ebenfalls viele/alle; hat er wenige/keine gegeben, gibt man ebenfalls wenige/keine.', '(4) In der ersten Runde viele/alle Taler geben. In den folgenden Runden weiterhin viele/alle Taler geben, solange der andere Teilnehmer zuvor ebenfalls viele/alle Taler gegeben hat. Gibt der andere Teilnehmer jedoch einmal wenige/keine Taler, ab diesem Zeitpunkt dauerhaft wenig/keine Taler geben.', '(5) Wie Strategie (3), nur in der ersten Runde wenig/keine Taler geben.', '(6) Wie Strategie (3), nur in Runde 8 auf jeden Fall wenig/keine Taler geben.', '(7) Wie Strategie (3), nur in Runden 7 und 8 auf jeden Fall wenig/keine Taler geben.', 'Keine der oben genannten Strategien' ], widget=widgets.RadioSelect, ) Strategy_Q2 = models.StringField( label='Was denken Sie: Welche der folgenden Strategien beschreibt das Verhalten eines typischen Teilnehmern im gerade gespielten Teil am besten? Das heißt, welche der folgenden Strategien war die häufigste? ', choices=['(1) Immer wenige/keine Taler (d.h. zwischen 0-6 Taler) geben.', '(2) Immer viele/alle Taler (d.h. zwischen 14-20 Taler) geben.', '(3) In der ersten Runde viele/alle Taler geben. In den folgenden Runden das Verhalten des anderen Teilnehmers aus der vorherigen Runde spiegeln: Hat er viele/alle Taler gegeben, gibt man ebenfalls viele/alle; hat er wenige/keine gegeben, gibt man ebenfalls wenige/keine.', '(4) In der ersten Runde viele/alle Taler geben. In den folgenden Runden weiterhin viele/alle Taler geben, solange der andere Teilnehmer zuvor ebenfalls viele/alle Taler gegeben hat. Gibt der andere Teilnehmer jedoch einmal wenige/keine Taler, ab diesem Zeitpunkt dauerhaft wenig/keine Taler geben.', '(5) Wie Strategie (3), nur in der ersten Runde wenig/keine Taler geben.', '(6) Wie Strategie (3), nur in Runde 8 auf jeden Fall wenig/keine Taler geben.', '(7) Wie Strategie (3), nur in Runden 7 und 8 auf jeden Fall wenig/keine Taler geben.', 'Keine der oben genannten Strategien' ], widget=widgets.RadioSelect, ) class IncorrectResponse(ExtraModel): player = models.Link(Player) field_name = models.StringField() response = models.StringField() def creating_session(subsession): if subsession.round_number == 1: subsession.group_randomly() else: subsession.group_like_round(1) def set_possible_payoffs(group: Group): p1 = group.get_player_by_id(1) p2 = group.get_player_by_id(2) contribution1 = p1.contribution1 contribution2 = p2.contribution2 group.size_pool = C.Pool + contribution1 + contribution2 group.additional_payoff1 = C.Account - contribution1 group.additional_payoff2 = C.Account - contribution2 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 Teilnehmer aufgeteilt.', Frage4='20', Frage5='30', Frage6='15', Frage7='35', Frage8='Alle Taler in das Gemeinschaftsprojekt geben.', Frage9='Keine Taler in das Gemeinschaftsprojekt geben.') 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 Strategy1(Page): form_model = 'player' form_fields = ['Strategy_Q1'] @staticmethod def is_displayed(player): return player.round_number == 1 class Contribution1(Page): form_model = 'player' form_fields = ['contribution1'] @staticmethod def is_displayed(player): return player.id_in_group == 1 class Contribution2(Page): form_model = 'player' form_fields = ['contribution2'] @staticmethod def is_displayed(player): return player.id_in_group == 2 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, Intro2, RuleQuizPage, RulesWaitPage, Start, Strategy1, Contribution1, Contribution2, 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]