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]