import plistlib import random import time from otree.api import * from otree import settings doc = """ Your app description """ def creating_session(subsession): subsession.TREATMENT = 'T1' #damit es nicht Null ist if 'wb_treatment' in subsession.session.config: subsession.TREATMENT = subsession.session.config['wb_treatment'] #T1: message wb/non embezzle; T2: message remaining silent/ embezzle; T3: message wb + non-embezzle + remaining silent + embezzle for p in subsession.get_players(): p.participant.vars['playertype'] = '' #config variable setzen subsession.matched_new = False class C(BaseConstants): NAME_IN_URL = 'WB_Norm_Appropriateness' PLAYERS_PER_GROUP = 2 NUM_ROUNDS = 1 #wenn XXX_ROLE eingefügt wird, legt Otree automatisch eine Variable role auf player Ebene an A_ROLE = 'A' B_ROLE = 'B' ## Definition : Gruppe1 A B & Gruppe2 A B werden zu ## Spieler1 G1A Spieler2 G2A ## Spieler1 G1B Spieler2 G2B Player_1 = 1 Player_2 = 2 DONATION_BUDGET = cu(1.5) EMBEZZLE_BUDGET = cu(1) PENALTY_BUDGET_A = cu(1) PENALTY_BUDGET_B = cu(0.5) ESTIMATE_CORRECT_WB = cu(0.25) # wenn jemand bei der Einschätzung für WB richtig liegt ESTIMATE_CORRECT_SILENT = cu(0.25) # wenn jemand bei der Einschätzung für Silent richtig liegt TEAM_PAYOFF = cu(1.5) SOLO_PAYOFF = cu(1.2) DONATION_AFTER_EMBEZZLING = DONATION_BUDGET - EMBEZZLE_BUDGET ## nur für die anzeige DONATION_DECISION_A = __name__ + '/donation_a.html' DONATION_DECISION_B = __name__ + '/donation_b.html' DONATION_FORWARDED = __name__ + '/decision_forward.html' DONATION_SILENCED = __name__ + '/decision_silenced.html' DONATION_REPORTED = __name__ + '/decision_report.html' TREATMENTS_NAMES = ['Control','T1','T2'] class Subsession(BaseSubsession): amount_possible_donations = models.IntegerField(initial=0) amount_real_donations = models.IntegerField(initial=0) amount_embezzling_tries = models.IntegerField(initial=0) amount_embezzling_succeeds = models.FloatField(initial=0) ## hier noch die anzahl TREATMENT = models.StringField(initial='Control') total_donation = models.FloatField(initial=0.0) ##Mehrheit embezzle/non embezzle und wb/non-wb #for Role A: tie=0, embezzle= 1, non-embezzle= 2 #for Role B: tie=0, wb = 1, non-wb=2 #both initial = -1 majority_A = models.IntegerField (initial=-1) majority_B = models.IntegerField(initial=-1) matched_new = models.BooleanField (initial=False) #für T2: ob Mehrheit WB/nonWB oder Embez/nonEmbez macht majority_AT2 = models.IntegerField(initial=-1) majority_BT2 = models.IntegerField(initial=-1) class Group(BaseGroup): donation_amount = models.CurrencyField(initial=cu(0)) class Player(BasePlayer): FirstPartA = models.BooleanField(initial=False) #für die fixe Rollenzuweisung playertype = models.StringField() donation_b_silence = models.BooleanField( label="", choices=[ [False, "Report the behaviour of Player A"], [True, "Overlook the behaviour of Player A"] ], widget=widgets.RadioSelectHorizontal, initial= True if settings.DEBUG else None ) donation_a = models.BooleanField( label="", choices = [ [True, 'Transfer the full donation budget to Cancer Research UK '], [False, 'Keep part of the donation budget for yourself '] ], widget=widgets.RadioSelectHorizontal, initial=True if settings.DEBUG else None ) embezzlingbudget_A = models.IntegerField( label="How appropriate do you personally belief is it to keep part of the donation budget for yourself?", choices=[ [1, 'very inappropriate'], [2, 'somewhat inappropriate'], [3, 'somewhat appropriate'], [4, 'very appropriate'], ], widget=widgets.RadioSelectHorizontal, initial=2 if settings.DEBUG else -1 ) forwardingbudget_A = models.IntegerField( label="How appropriate do you personally believe is it to transfer the full donation budget to Cancer Research UK?", choices=[ [1, 'very inappropriate'], [2, 'somewhat inappropriate'], [3, 'somewhat appropriate'], [4, 'very appropriate'], ], widget=widgets.RadioSelectHorizontal, initial=2 if settings.DEBUG else -1 ) majority_embezzlingbudget_A = models.IntegerField( label="Which option do you think the majority of participants chose in the preceding question? If your answer matches the actual option of the majority you will earn an additional 0.25 GPB.", choices=[ [1, 'very inappropriate'], [2, 'somewhat inappropriate'], [3, 'somewhat appropriate'], [4, 'very appropriate'], ], widget=widgets.RadioSelectHorizontal, initial=2 if settings.DEBUG else -1 ) majority_forwardingbudget_A = models.IntegerField( label="Which option do you think the majority of participants chose in the preceding question? If your answer matches the actual option of the majority you will earn an additional 0.25 GPB.", choices=[ [1, 'very inappropriate'], [2, 'somewhat inappropriate'], [3, 'somewhat appropriate'], [4, 'very appropriate'], ], widget=widgets.RadioSelectHorizontal, initial=2 if settings.DEBUG else -1 ) wb_B = models.IntegerField( label="How appropriate do you personally belief is it to report the behaviour of Player A?", choices=[ [1, 'very inappropriate'], [2, 'somewhat inappropriate'], [3, 'somewhat appropriate'], [4, 'very appropriate'], ], widget=widgets.RadioSelectHorizontal, initial=2 if settings.DEBUG else -1 ) remainingsilent_B = models.IntegerField( label="How appropriate do you personally belief is it to overlook the behaviour of Player A?", choices=[ [1, 'very inappropriate'], [2, 'somewhat inappropriate'], [3, 'somewhat appropriate'], [4, 'very appropriate'], ], widget=widgets.RadioSelectHorizontal, initial = 2 if settings.DEBUG else -1 ) majority_wb_B = models.IntegerField( label="Which option do you think the majority of participants chose in the preceding question? If your answer matches the actual option of the majority you will earn an additional 0.25 GPB.", choices=[ [1, 'very inappropriate'], [2, 'somewhat inappropriate'], [3, 'somewhat appropriate'], [4, 'very appropriate'], ], widget=widgets.RadioSelectHorizontal, initial=2 if settings.DEBUG else -1 ) majority_remainingsilent_B = models.IntegerField( label="Which option do you think the majority of participants chose in the preceding question? If your answer matches the actual option of the majority you will earn an additional 0.25 GPB.", choices=[ [1, 'very inappropriate'], [2, 'somewhat inappropriate'], [3, 'somewhat appropriate'], [4, 'very appropriate'], ], widget=widgets.RadioSelectHorizontal, initial=2 if settings.DEBUG else -1 ) #deskriptive Norm für T0 und T1 ohne zusätzliches Geld majority_action_A = models.IntegerField( label="Unabhängig davon, was glauben Sie, wie hat sich die Mehrheit der Spieler*in A verhalten?", choices=[ [1, 'Das vollständige Spendenbudget an GoAhead weiterzuleiten'], [2, 'Einen Teil des Spendenbudgets einzubehalten'] ], widget=widgets.RadioSelectHorizontal, initial=2 if settings.DEBUG else -1 ) majority_action_B = models.IntegerField( label="Unabhängig davon, was glauben Sie, wie hat sich die Mehrheit der Spieler*in B verhalten?", choices=[ [1, 'Das Verhalten von Spieler*in A zu melden'], [2, 'Stillschweigend über das Verhalten von Spieler*in A hinwegzusehen'] ], widget=widgets.RadioSelectHorizontal, initial=2 if settings.DEBUG else -1 ) #T2 majority_view_honesty_AT2 = models.IntegerField( label="Geben Sie an, was Sie denken, wie sich die Mehrheit der Teilnehmenden in der Rolle 'Spieler*in A' in dieser Sitzung bei der Entscheidung über das Spendenbudget verhalten hat. Wenn Ihre Einschätzung bei dieser Frage korrekt ist, erhalten Sie eine zusätzliche Auszahlung von 1 Taler.", choices=[ [1, 'Das vollständige Spendenbudget an GoAhead weiterleiten'], [2, 'Einen Teil des Spendenbudgets einbehalten'] ], widget=widgets.RadioSelectHorizontal, initial=2 if settings.DEBUG else -1 ) majority_view_wb_BT2 = models.IntegerField( label="Geben Sie an, was Sie denken, wie sich die Mehrheit der Teilnehmenden in der Rolle 'Spieler*in B' in dieser Sitzung bezüglich der Entscheidung von Spieler*in A verhalten hat. Wenn Ihre Einschätzung bei dieser Frage korrekt ist, erhalten Sie eine zusätzliche Auszahlung von 1 Taler.", choices=[ [1, 'Das Verhalten von Spieler*in A melden'], [2, 'Stillschweigend über das Verhalten von Spieler*in A hinwegsehen'] ], widget=widgets.RadioSelectHorizontal, initial=2 if settings.DEBUG else -1 ) Spieler1 = models.BooleanField(initial=False) #ob jemand Spieler 1 oder 2 wird team_embezzle = models.BooleanField(initial=False) ## Nur für eure Auswertung und das leichtere Überprüfen team_penalty = models.BooleanField(initial=False) estimate_correct = models.IntegerField (initial=-1) #wird gesetzt aber für Experiment Norm_Appropriateness nicht gebraucht def fill_control_variables_set_payoff(group:Group): ## Spieler 1 ist A , Spieler 2 ist B , wenn ihr später rnd zuordnung macht evtl nochmal anfassen ## falls dem so ist, die 0 & 1 unten durch die suche nach A und B tauschen ## index = 0 if group.get_player()[0].role == C.A_ROLE else 1 ## und dann 0 und 1 durch index und 1-index ersetzen #determine who is who player1A = True if group.get_players()[0].role == C.B_ROLE: player1A = False embezzle = False report = True if player1A: #A Spieler ist an Stelle 0 group.get_players()[0].donation_b_silence = group.get_players()[1].donation_b_silence group.get_players()[1].donation_a = group.get_players()[0].donation_a if group.get_players()[0].donation_a == False: embezzle=True if group.get_players()[0].donation_b_silence == True: report = False else: #B Spieler ist an Stelle 0 group.get_players()[1].donation_b_silence = group.get_players()[0].donation_b_silence group.get_players()[0].donation_a = group.get_players()[1].donation_a if group.get_players()[0].donation_a==False: embezzle = True if group.get_players()[0].donation_b_silence == True: report = False group.donation_amount = C.DONATION_BUDGET for player in group.get_players(): player.payoff = cu(0) ## kriegen erstmal nix player.team_embezzle = embezzle player.team_penalty = embezzle and report #für Experiment WB Norm Appropriateness: speichern in participant variable player.participant.vars['player_embezzle'] = embezzle player.participant.vars['player_wb'] = report #um nullwerte zu vermeiden werden die Variablen beider Spieler belegt #if player.id_in_group == 1: #player.donation_b_silence = report #else: #ID ist 2 #.donation_a=embezzle #print("Gruppe" + player.group + " , embezzle: " + embezzle + ", wb: " + report) ## nur zum zählen if embezzle and not report: player.subsession.amount_embezzling_succeeds += 1/2 ## wird ja 2mal gezählt ## HIER PAYOFF if embezzle: ## nur wenn unterschlagen wird passiert überhaupt was if report: ## und gemeldet player.payoff = -(C.PENALTY_BUDGET_A if player.role == C.A_ROLE else C.PENALTY_BUDGET_B) else: ## stillschweigend if player.role==C.A_ROLE: player.payoff = C.EMBEZZLE_BUDGET group.donation_amount -= C.EMBEZZLE_BUDGET #calculate total donation: maximum donation possible minus how many times of embezzlement player.session.total_donation = (len(player.subsession.get_groups()) * C.DONATION_BUDGET) - (player.subsession.amount_embezzling_succeeds * C.EMBEZZLE_BUDGET) #store in group_donation in participant variable player.participant.vars['player_team_donation'] = group.donation_amount ''' #wird in WB_Norm_Appropriateness nicht gebraucht #bonus für Einschätzungen ermitteln if not player.subsession.TREATMENT==C.TREATMENTS_NAMES[2]: #für control und T1 if player.role == C.A_ROLE and player.estimate_correct==-1: #noch nicht berechnet player.participant.vars['playertype'] = 'A' #nochmals zur Sicherheit player.estimate_correct=0 if player.majority_view_honesty_A==player.subsession.majority_A: player.payoff += C.ESTIMATE_CORRECT_BUDGET player.estimate_correct=1 elif player.role == C.B_ROLE and player.estimate_correct==-1: #noch nicht für B berechnet player.participant.vars['playertype'] = 'B' # nochmals zur Sicherheit player.estimate_correct=0 if player.majority_view_wb_B == player.subsession.majority_B: player.payoff +=C.ESTIMATE_CORRECT_BUDGET player.estimate_correct=1 else: #T2 if player.role == C.A_ROLE and player.estimate_correct==-1: #noch nicht berechnet player.estimate_correct=0 if player.majority_view_honesty_AT2==player.subsession.majority_AT2: player.payoff += C.ESTIMATE_CORRECT_BUDGET player.estimate_correct=1 elif player.role == C.B_ROLE and player.estimate_correct==-1: #noch nicht für B berechnet player.estimate_correct=0 if player.majority_view_wb_BT2 == player.subsession.majority_BT2: player.payoff +=C.ESTIMATE_CORRECT_BUDGET player.estimate_correct=1 player.participant.estimate_correct = player.estimate_correct ''' #wird in WB_NORM_Approriateness nicht benötigt ''' # count majority vote if in according treatment def countVotes (group: Group): if group.subsession.TREATMENT in C.TREATMENTS_NAMES: allplayers = group.subsession.get_players() counterA_embezzle = 0 counterA_nonembezzle=0 counterB_wb = 0 counterB_nonwb=0 numA_embezzle = 0 numA_nonembezzle = 0 numB_wb = 0 numB_nonwb=0 for p in allplayers: if p.role == C.A_ROLE: #A-players if p.honesty_A == 2: counterA_embezzle = counterA_embezzle+1 else: counterA_nonembezzle = counterA_nonembezzle+1 if p.donation_a: numA_nonembezzle = numA_nonembezzle+1 else: numA_embezzle = numA_embezzle+1 else: #B-players #p.role == C.B_ROLE: # B-players if p.wb_B == 1: counterB_wb = counterB_wb + 1 else: counterB_nonwb = counterB_nonwb + 1 if p.donation_b_silence: numB_nonwb_wb = numB_nonwb + 1 else: numB_wb = numB_wb + 1 #majority ermitteln #role A if counterA_embezzle > counterA_nonembezzle: group.subsession.majority_A = 2 elif counterA_embezzle < counterA_nonembezzle: group.subsession.majority_A = 1 else: group.subsession.majority_A = 0 if numA_embezzle > numA_nonembezzle: group.subsession.majority_AT2 = 2 elif numA_embezzle counterB_nonwb: group.subsession.majority_B = 1 elif counterB_wb < counterB_nonwb: group.subsession.majority_B = 2 else: group.subsession.majority_B = 0 if numB_wb > numB_nonwb: group.subsession.majority_BT2 = 1 elif numB_wb < numB_nonwb: group.subsession.majority_BT2 = 2 else: group.subsession.majority_BT2 = 0 ''' def group_by_arrival_time_method(subsession, waiting_players): from collections import defaultdict # partner finden, und wenn in eine gruppe möglich ist, rausschmeissen players_grouped_by_matched_before = defaultdict(list) for p in waiting_players: print('norm appropriate groupbyarrival', p.participant,p.participant.matched_before) players_grouped_by_matched_before[p.participant.matched_before].append(p) print(players_grouped_by_matched_before) for group in players_grouped_by_matched_before.values(): if len(group) >= 2: return [group[0],group[1]] # PAGES class Donation_Text_A(Page): @staticmethod def vars_for_template(player: Player): if player.role == C.A_ROLE: player.participant.vars['playertype'] = 'A' player.playertype = player.participant.vars['playertype'] @staticmethod def is_displayed(player: Player): return player.role == C.A_ROLE class Donation_Text_B(Page): @staticmethod def vars_for_template(player: Player): if player.role == C.B_ROLE: player.participant.vars['playertype'] = 'B' player.playertype = player.participant.vars['playertype'] @staticmethod def is_displayed(player: Player): return player.role == C.B_ROLE class Donation_Decision_A(Page): form_model = 'player' form_fields = ['donation_a'] @staticmethod def is_displayed(player: Player): if player.role == C.A_ROLE: player.FirstPartA = True ## nur zum speichern return player.role == C.A_ROLE def vars_for_template(player: Player): message="" if player.subsession.TREATMENT=='T1': message = "IMPORTANT: In previous sessions of the same experiment, the majority of participants in role of Player A indicated that transferring the full donation budget is somewhat or very appropriate." elif player.subsession.TREATMENT=='T2': message = "IMPORTANT: In previous sessions of the same experiment, the majority of participants in the role of Player A indicated that keeping part of the donation budget is somewhat or very inappropriate." elif player.subsession.TREATMENT=='T3' or player.subsession.TREATMENT=='T5' : message = "IMPORTANT: In previous sessions of the same experiment, the majority of participants in the role of Player A indicated that transferring the full donation budget is somewhat or very appropriate AND that keeping part of the donation budget is somewhat or very inappropriate." return dict( control=player.subsession.TREATMENT=='Control', t1235=player.subsession.TREATMENT !='Control' and player.subsession.TREATMENT !='T4', message=message ) @staticmethod def before_next_page(player: Player, timeout_happened): ## zum zählen nur if player.role == C.A_ROLE: player.subsession.amount_possible_donations +=1 if player.donation_a: player.subsession.amount_real_donations += 1 else: player.subsession.amount_embezzling_tries += 1 class Donation_Decision_B(Page): form_model = 'player' form_fields = ['donation_b_silence'] @staticmethod def is_displayed(player: Player): return player.role == C.B_ROLE def vars_for_template(player: Player): message = "" if player.subsession.TREATMENT == 'T1': message = "IMPORTANT: In previous sessions of the same experiment, the majority of participants in role of Player B indicated that reporting the behaviour of Player A is somewhat or very appropriate." elif player.subsession.TREATMENT == 'T2': message = "IMPORTANT: In previous sessions of the same experiment, the majority of participants in the role of Player B indicated that overlooking the behaviour of Player A is somewhat or very inappropriate." elif player.subsession.TREATMENT == 'T3' or player.subsession.TREATMENT == 'T5': message = "IMPORTANT: In previous sessions of the same experiment, the majority of participants in the role of Player B indicated that reporting the behaviour of Player A is somewhat or very appropriate AND that overlooking the behaviour of Player A is somewhat or very inappropriate." return dict( control=player.subsession.TREATMENT=='Control', t1235=player.subsession.TREATMENT != 'Control' and player.subsession.TREATMENT != 'T4', message=message ) class Appropriateness_A(Page): form_model = 'player' form_fields = ['forwardingbudget_A', 'majority_forwardingbudget_A', 'embezzlingbudget_A','majority_embezzlingbudget_A'] @staticmethod def is_displayed(player: Player): return player.role == C.A_ROLE and (player.subsession.TREATMENT == "Control" or player.subsession.TREATMENT == "T1" or player.subsession.TREATMENT == "T2" or player.subsession.TREATMENT == "T3") #anzeigen für Spieler A in Control und Treatments T1-T3 #Für T4: Abfrage der Norm NACH der Donation Entscheidung class Appropriateness_A_After(Page): form_model = 'player' form_fields = ['forwardingbudget_A', 'majority_forwardingbudget_A', 'embezzlingbudget_A','majority_embezzlingbudget_A'] @staticmethod def is_displayed(player: Player): return player.role == C.A_ROLE and (player.subsession.TREATMENT == "T4") #anzeigen für Spieler A Treatments T4 #class Questionaire_AT2(Page): #benötigen wir nicht # form_model = 'player' # form_fields = ['honesty_A', 'majority_view_honesty_AT2'] # @staticmethod #def is_displayed(player: Player): #nur anzeigen in T2 # return player.subsession.TREATMENT == C.TREATMENTS_NAMES[2] and player.role == C.A_ROLE class Appropriateness_B(Page): form_model = 'player' form_fields = ['wb_B','majority_wb_B','remainingsilent_B','majority_remainingsilent_B'] @staticmethod def is_displayed(player: Player): return player.role == C.B_ROLE and (player.subsession.TREATMENT == "Control" or player.subsession.TREATMENT == "T1" or player.subsession.TREATMENT == "T2" or player.subsession.TREATMENT == "T3") #anzeigen für Spieler B in Control und Treatments T1-T3 #Für T4: Abfrage der Norm NACH der Donation Entscheidung class Appropriateness_B_After(Page): form_model = 'player' form_fields = ['wb_B','majority_wb_B','remainingsilent_B','majority_remainingsilent_B'] @staticmethod def is_displayed(player: Player): return player.role == C.B_ROLE and (player.subsession.TREATMENT == "T4") #anzeigen für Spieler B T4 #class Questionaire_BT2(Page): #benötigen wir nicht # form_model = 'player' # form_fields = ['wb_B','majority_view_wb_BT2'] # @staticmethod # def is_displayed(player: Player):## Nur T2 # return player.subsession.TREATMENT == C.TREATMENTS_NAMES[2] and player.role == C.B_ROLE class Donation_Payoff_Waitpage(WaitPage): @staticmethod def after_all_players_arrive(group: Group): fill_control_variables_set_payoff(group) def vars_for_template(player: Player): return dict ( body_text = 'Please wait until your group member made a decision.', ) class Intermediate_Result(Page): @staticmethod def vars_for_template(player: Player): return dict( donated = player.donation_a, silenced = player.donation_b_silence, ##für jeden player donation_a und donation_b_silence gespeichert, daher einfach übergeben myroleA = player.role==C.A_ROLE, #mycorrect = player.estimate_correct kommunizieren wir erst ganz am Ende des Experiments ) class Part2_Info(Page): @staticmethod def is_displayed(player: Player): return True def vars_for_template(player: Player): return dict( control=player.subsession.TREATMENT == 'Control' ) class Player_wait_stage(WaitPage): group_by_arrival_time = True #https://otree.readthedocs.io/en/latest/multiplayer/waitpages.html @staticmethod def is_displayed(player): return player.round_number == 1 def vars_for_template(player: Player): return dict (body_text= "Please wait until all roles have been assigned in your group.") page_sequence = [Player_wait_stage, Donation_Text_A, Donation_Text_B, Appropriateness_A,Appropriateness_B, Donation_Decision_A, Donation_Decision_B, Donation_Payoff_Waitpage,Appropriateness_A_After,Appropriateness_B_After,Intermediate_Result,Part2_Info]