from otree.api import *
doc = """
Your app description
"""
class C(BaseConstants):
NAME_IN_URL = 'qlm'
PLAYERS_PER_GROUP = 3
NUM_ROUNDS = 2
ENDOWMENT = 25
CONVERSION_RATE = 0.25
OFFER_MAX = 17
PROB_FED_UP = 0.05
CURRENT_EMPLOYERS = [1,1,2,2,3,3]
OTHER_EMPLOYERS1 = [2,2,1,1,1,1]
OTHER_EMPLOYERS2 = [3,3,3,3,2,2]
WORKERS_PER_PLAYER = [[1,2,3,4,5,6],[3,4,1,2,5,6],[5,6,1,2,3,4]]
COLORS_WORKERS_SAME = [0,4,1,6,2,8]
S=0.5
C1=12
C2=4
D1=0
D2=6
AH=2
AL=1
NUM_COLOR_FIELDS = 19
BONUS_COUNTING = 5
NUM_COMP_QUEST = 10
BONUS_COMP_QUEST = 2
def creating_session(subsession):
from itertools import cycle
import random
from random import choice
session = subsession.session
# Choosing payoff relevant round
session.vars['paying_round'] = random.randint(1,C.NUM_ROUNDS)
# Assigning half of the groups to both baseline and samecolor treatment
same = cycle([True, False])
for group in subsession.get_groups():
group.samecolor = next(same)
# Definition of colors
subsession.session.colors = ['Red', 'Blue', 'Green', 'Orange', 'Gold', 'Pink', 'SaddleBrown', 'Purple', 'Aqua', 'Black']
subsession.session.colors_de = ['rot', 'blau', 'grün', 'orange', 'gelb', 'pink', 'beige', 'violett', 'türkis', 'schwarz']
# Creating 6 workers for each group
for group in subsession.get_groups():
for i in range(6):
Worker.create(
group = group,
number = i+1,
color_id = i+3 if group.samecolor == False else C.COLORS_WORKERS_SAME[i],
current_employer = group.get_player_by_id(C.CURRENT_EMPLOYERS[i]),
other_employer1 = group.get_player_by_id(C.OTHER_EMPLOYERS1[i]),
other_employer2 = group.get_player_by_id(C.OTHER_EMPLOYERS2[i])
)
# Assigning colors to workers
for group in subsession.get_groups():
workers = Worker.filter(group = group)
for worker in workers:
worker.color_name = subsession.session.colors[worker.color_id]
worker.color_de = subsession.session.colors_de[worker.color_id]
worker.color_html = '{}'.format(worker.color_name, worker.color_de)
# Color assignment, switch variables for random order of employees on the promotion page, workerids for players
for player in subsession.get_players():
player.color_id = player.id_in_group-1
player.color_name = subsession.session.colors[player.color_id]
player.color_de = subsession.session.colors_de[player.color_id]
player.color_html = '{}'.format(player.color_name, player.color_de)
add_e = '' if player.color_de[-1] == 'e' else 'e'
player.color_html_akk = '{}{}{}'.format(player.color_name, player.color_de, add_e, 'n')
player.color_html_fem = '{}{}'.format(player.color_name, player.color_de, add_e)
player.switch_emp = choice([True, False])
player.switch_worker12 = choice([True, False])
player.switch_worker34 = choice([True, False])
player.switch_worker56 = choice([True, False])
player.worker1_id = C.WORKERS_PER_PLAYER[player.id_in_group - 1][int(player.switch_worker12)]
player.worker2_id = C.WORKERS_PER_PLAYER[player.id_in_group - 1][1 - int(player.switch_worker12)]
if not(player.switch_emp):
player.other_emp1 = C.OTHER_EMPLOYERS1[player.id_in_group * 2 - 1]
player.other_emp2 = C.OTHER_EMPLOYERS2[player.id_in_group * 2 - 1]
player.worker3_id = C.WORKERS_PER_PLAYER[player.id_in_group - 1][2 + int(player.switch_worker34)]
player.worker4_id = C.WORKERS_PER_PLAYER[player.id_in_group - 1][3 - int(player.switch_worker34)]
player.worker5_id = C.WORKERS_PER_PLAYER[player.id_in_group - 1][4 + int(player.switch_worker56)]
player.worker6_id = C.WORKERS_PER_PLAYER[player.id_in_group - 1][5 - int(player.switch_worker56)]
else:
player.other_emp1 = C.OTHER_EMPLOYERS2[player.id_in_group * 2 - 1]
player.other_emp2 = C.OTHER_EMPLOYERS1[player.id_in_group * 2 - 1]
player.worker3_id = C.WORKERS_PER_PLAYER[player.id_in_group - 1][4 + int(player.switch_worker56)]
player.worker4_id = C.WORKERS_PER_PLAYER[player.id_in_group - 1][5 - int(player.switch_worker56)]
player.worker5_id = C.WORKERS_PER_PLAYER[player.id_in_group - 1][2 + int(player.switch_worker34)]
player.worker6_id = C.WORKERS_PER_PLAYER[player.id_in_group - 1][3 - int(player.switch_worker34)]
player.worker1_html = Worker.filter(group=player.group, number=player.worker1_id)[0].color_html
player.worker2_html = Worker.filter(group=player.group, number=player.worker2_id)[0].color_html
player.worker3_html = Worker.filter(group=player.group, number=player.worker3_id)[0].color_html
player.worker4_html = Worker.filter(group=player.group, number=player.worker4_id)[0].color_html
player.worker5_html = Worker.filter(group=player.group, number=player.worker5_id)[0].color_html
player.worker6_html = Worker.filter(group=player.group, number=player.worker6_id)[0].color_html
for player in subsession.get_players():
player.other_emp1_html = player.group.get_player_by_id(player.other_emp1).color_html
player.other_emp2_html = player.group.get_player_by_id(player.other_emp2).color_html
class Subsession(BaseSubsession):
pass
class Group(BaseGroup):
samecolor = models.BooleanField()
# Function to create promotion field variables
def var_prom():
return models.BooleanField(
choices=[[False, ''], [True, ''],],
label='Beförderung',
)
# Function to create offer field variables
def var_offer(label):
return models.FloatField(
min=0,
max=C.OFFER_MAX,
label=label,
)
# Function to create comprehensve question variables
def var_comp(label):
return models.BooleanField(
choices=[[True, 'Richtig'], [False, 'Falsch']],
label = label,
widget = widgets.RadioSelectHorizontal
)
# Function to create closeness questions
def var_close(label):
return models.IntegerField(
choices=[i for i in range(0,11)],
label = label,
widget = widgets.RadioSelectHorizontal
)
class Player(BasePlayer):
color_id = models.IntegerField()
color_name = models.StringField()
color_de = models.StringField()
color_html = models.StringField()
color_html_akk = models.StringField()
color_html_fem = models.StringField()
picture_count = models.IntegerField()
comp_quest1 = var_comp(label='Aussage 1: Die Zuteilung der TeilnehmerInnen zu einer Farbgruppe, einem Arbeitsmarkt und einer Rolle (AN/AG) bleibt über alle {} Runden erhalten.'.format(C.NUM_ROUNDS))
comp_quest2 = var_comp(label='Aussage 2: Die Farbgruppe jedes AN und jedes AG auf einem Arbeitsmarkt ist für alle AG desselben Arbeitsmarktes sichtbar.')
comp_quest3 = var_comp(label='Aussage 3: Ein AG kann die Lohnangebote für seine aktuellen AN von deren Leistungsfähigkeit abhängig machen.')
comp_quest4 = var_comp(label='Aussage 4: Ein AG kann seine Lohnangebote für die aktuellen AN der anderen AG nur von ihrer Zugehörigkeit zu einer bestimmten Farbgruppe abhängig machen.')
comp_quest5 = var_comp(label='Aussage 5: Wenn das höchste Angebot der beiden abwerbenden AG gleich oder niedriger ist als das Lohnangebot des aktuellen AG, wechselt der AN mit einer Wahrscheinlichkeit von {}%.'.format(C.PROB_FED_UP))
comp_quest6 = var_comp(label='Aussage 6: Die Leistungsfähigkeit eines AN wird in der ersten Runde zufällig bestimmt und bleibt dann für alle weiteren Runden gleich.')
comp_quest7 = var_comp(label='Aussage 7: Ein AN arbeitet nach den Lohnangeboten auf jeden Fall für seinen aktuellen AG.')
comp_quest8 = var_comp(label='Aussage 8: Die aktuellen AN eines AG sind zu Beginn jeder Runde dieselben.')
comp_quest9 = var_comp(label='Aussage 9: Der erwirtschaftete Ertrag eines AN ist nur dann von seiner Leistungsfähigkeit abhängig, wenn er befördert wurde.')
comp_quest10 = var_comp(label='Aussage 10: Der erwirtschaftete Ertrag eines AN ist höher, wenn er bei seinem aktuellen AG bleibt.')
num_comp_correct = models.IntegerField()
first_try = models.BooleanField(initial=True)
promotion1 = var_prom()
promotion2 = var_prom()
promotion3 = var_prom()
promotion4 = var_prom()
promotion1e = var_prom()
promotion2e = var_prom()
promotion3e = var_prom()
promotion4e = var_prom()
offer11 = var_offer('Lohnangebot in Taler')
offer12 = var_offer('Lohnangebot in Taler')
offer13 = var_offer('Lohnangebot in Taler')
offer14 = var_offer('Lohnangebot in Taler')
offer21 = var_offer('')
offer22 = var_offer('')
offer23 = var_offer('')
offer24 = var_offer('')
offer31 = var_offer('')
offer32 = var_offer('')
offer41 = var_offer('')
offer42 = var_offer('')
offer51 = var_offer('')
offer52 = var_offer('')
offer61 = var_offer('')
offer62 = var_offer('')
quest1_1 = var_close('')
quest1_2 = var_close('')
quest1_3 = var_close('')
quest1_4 = var_close('')
quest1_5 = var_close('')
quest2_1 = models.IntegerField(label='Ein Schläger und ein Ball kosten gemeinsam 1,10 Euro. Der Schläger kostet um 1,00 Euro mehr als der Ball. Wieviel Euro kostet der Ball?')
quest2_2 = models.IntegerField(label='5 Maschinen brauchen 5 Minuten, um 5 Einheiten zu produzieren. Wieveile Minuten brauchen 100 Maschinen um 100 Einheiten zu produzieren?')
quest2_3 = models.IntegerField(label='''In einem Teich befindet sich ein Seerosenbeet. Jeden Tag verdoppelt sich die Größe des Beets.
Es dauert 48 Tage bis der ganze Teich mit Seerosen bedeckt ist. Nach wievielen Tagen ist die Hälfte des Teiches mit Seerosen bedeckt?''')
quest2_4 = var_close('Wenn es um Politik geht, werden oft die Begriffe "links" und "rechts" gebraucht. Wie würden Sie sich selbst auf einer Skala von 0 (links) bis 10 (rechts) einordnen?')
quest2_5 = var_close('Wie wichtig ist es Ihnen auf einer Skala von 0 (überhaupt nicht wichtig) bis 10 (sehr wichtig), dass Sie in enem Land leben, das demokratisch regiert wird?')
quest2_6 = var_close('Wie schätzen Sie die Neigung anderer Menschen ein Sie ausnutzen, wenn sie die Gelegenheit dazu haben? (0 = andere sind immer fair zu mir; 10 = andere nutzen mich immer aus)')
quest3_1 = models.IntegerField(label = 'Ihr Geschlecht', choices = [[1, 'weiblich'], [2, 'männlich'], [3, 'divers'], [4, 'anderes']], widget = widgets.RadioSelect)
quest3_2 = models.IntegerField(label = 'Ihr Alter')
quest3_3 = models.IntegerField(choices = [[1, 'Betriebswirtschaftslehre'], [2, 'Formalwissenschaften'], [3, 'Geisteswissenschaften'], [4, 'Ingenieurwissenschaften'],
[5, 'Kulturwisssenschaften'], [6, 'Medizin- Gesundheits- und Pflegewissenschaften'], [7, 'Naturwissenschaften'],
[8, 'Rechtswissenschaften'], [9, 'Sozialwissenschaften'], [10, 'Volkswirtschaftslehre'], [11, 'Sonstige'],
[12, 'kein(e) Student(in)']], widget=widgets.RadioSelect,
label = 'Welcher Kategorie ist Ihr Studiengang am ehesten zuzuordnen?')
case = models.IntegerField()
switch_emp = models.BooleanField()
other_emp1_id = models.IntegerField()
other_emp1_html = models.StringField()
other_emp2_id = models.IntegerField()
other_emp2_html = models.StringField()
switch_worker12 = models.BooleanField()
switch_worker34 = models.BooleanField()
switch_worker56 = models.BooleanField()
worker1_id = models.IntegerField()
worker1_html = models.StringField()
worker2_id = models.IntegerField()
worker2_html = models.StringField()
worker3_id = models.IntegerField()
worker3_html = models.StringField()
worker4_id = models.IntegerField()
worker4_html = models.StringField()
worker5_id = models.IntegerField()
worker5_html = models.StringField()
worker6_id = models.IntegerField()
worker6_html = models.StringField()
payoff_round = models.FloatField(initial=0)
payoff_pay_round = models.FloatField(initial=0)
payoff_final = models.FloatField(initial=0)
payout = models.FloatField(initial=0)
bonus_counting = models.BooleanField(initial=True)
class Worker(ExtraModel):
group = models.Link(Group)
number = models.IntegerField()
color_id = models.IntegerField()
color_name = models.StringField()
color_de = models.StringField()
color_html = models.StringField()
current_employer = models.Link(Player)
other_employer1 = models.Link(Player)
other_employer2 = models.Link(Player)
offer1 = models.FloatField()
offer2 = models.FloatField()
offer3 = models.FloatField()
employer = models.Link(Player)
stay = models.BooleanField(initial=False)
ability = models.BooleanField()
promoted = models.BooleanField()
productivity = models.IntegerField()
wage = models.FloatField()
class Offer(ExtraModel):
group = models.Link(Group)
employer = models.Link(Player)
worker = models.Link(Worker)
promoted = models.BooleanField()
amount = models.FloatField()
# Function to create offers
def make_offer(player, worker_number, promoted, amount):
Offer.create(group = player.group,
employer = player,
worker = Worker.filter(group = player.group, number = worker_number)[0],
promoted = promoted,
amount = amount)
# PAGES
class Welcome(Page):
@staticmethod
def is_displayed(player):
return player.round_number == 1
@staticmethod
def vars_for_template(player: Player):
session = player.session
return dict(inv_exchange_rate = int(1/session.real_world_currency_per_point))
class ColorGroupAssignment(Page):
@staticmethod
def is_displayed(player):
return player.round_number == 1
class Counting(Page):
form_model = 'player'
form_fields = ['picture_count']
@staticmethod
def is_displayed(player):
return player.round_number == 1
class AnnounceInstructions(Page):
@staticmethod
def is_displayed(player):
return player.round_number == 1
class Instructions(Page):
@staticmethod
def is_displayed(player):
return player.round_number == 1
class CompQuest(Page):
form_model = 'player'
@staticmethod
def is_displayed(player):
return player.round_number == 1
form_fields = ['comp_quest1', 'comp_quest2', 'comp_quest3', 'comp_quest4', 'comp_quest5', 'comp_quest6', 'comp_quest7', 'comp_quest8', 'comp_quest9', 'comp_quest10']
@staticmethod
def error_message(player: Player, values):
solutions = dict(comp_quest1 = True, comp_quest2 = True, comp_quest3 = True, comp_quest4 = False, comp_quest5 = True,
comp_quest6 = False, comp_quest7 = False, comp_quest8 = True, comp_quest9 = True, comp_quest10 = True)
wrong_answers = []
for i in range(len(solutions)):
if list(values.values())[i] != list(solutions.values())[i]:
wrong_answers.append(i+1)
if player.first_try == True:
player.num_comp_correct = C.NUM_COMP_QUEST - len(wrong_answers)
player.first_try = False
if values != solutions:
return_string = ', Aussage '.join(str(x) for x in wrong_answers)
return f"Ihre Bewertung folgender Aussagen ist nicht korrekt: Aussage {return_string}"
class AnnounceRound(Page):
pass
class Promotion(Page):
form_model = 'player'
form_fields = ['promotion1', 'promotion2', 'promotion3', 'promotion4', 'offer11', 'offer12', 'offer13', 'offer14', 'offer21',
'offer22', 'offer23', 'offer24', 'offer31', 'offer32', 'offer41', 'offer42', 'offer51', 'offer52', 'offer61', 'offer62']
@staticmethod
def vars_for_template(player: Player):
return dict(workers = Worker.filter(group = player.group))
@staticmethod
def before_next_page(player: Player, timeout_happened):
from random import choice
# Collecting offers and promotions for current workers in lists
offers1 = [player.offer11, player.offer12, player.offer13, player.offer14]
offers2 = [player.offer21, player.offer22, player.offer23, player.offer24]
promotions = [player.promotion1, player.promotion2, player.promotion3, player.promotion4]
# Random decision on case
player.case = choice([0,1,2,3])
# Assigning offers and promotions from current employers to workers (worker1 and worker2)
worker1 = Worker.filter(group = player.group, number = player.worker1_id)[0]
worker1.offer1 = offers1[player.case]
worker1.promoted = promotions[player.case]
worker1.ability = False if player.case in [1,3] else True
worker2 = Worker.filter(group = player.group, number = player.worker2_id)[0]
worker2.offer1 = offers2[player.case]
worker2.promoted = not(promotions[player.case])
worker2.ability = False if player.case in [0,3] else True
# Creating offers for workers of other employers (worker3,4,5,6)
make_offer(player, player.worker3_id, True, player.offer31)
make_offer(player, player.worker3_id, False, player.offer32)
make_offer(player, player.worker4_id, True, player.offer41)
make_offer(player, player.worker4_id, False, player.offer42)
make_offer(player, player.worker5_id, True, player.offer51)
make_offer(player, player.worker5_id, False, player.offer52)
make_offer(player, player.worker6_id, True, player.offer61)
make_offer(player, player.worker6_id, False, player.offer62)
class ResultsWaitPage(WaitPage):
def after_all_players_arrive(group: Group):
from random import random
workers = Worker.filter(group = group)
for worker in workers:
# Identifying relevant offers from other employers (other_employer1 and other_employer2)
worker.offer2 = Offer.filter(group=group, employer=worker.other_employer1, worker=worker, promoted = worker.promoted)[0].amount
worker.offer3 = Offer.filter(group=group, employer=worker.other_employer2, worker=worker, promoted = worker.promoted)[0].amount
# Choosing an employer
if random() > C.PROB_FED_UP and worker.offer1 >= max(worker.offer2, worker.offer3):
worker.employer = worker.current_employer
worker.stay = True
worker.wage = worker.offer1
else:
if (worker.offer2 > worker.offer3) or (worker.offer2 == worker.offer3 and random() > 0.5):
worker.employer = worker.other_employer1
worker.wage = worker.offer2
else:
worker.employer = worker.other_employer2
worker.wage = worker.offer3
# Calculating worker productivity: (1+Si)*(ci + di*ai)
worker.productivity = (1 + (C.S if worker.stay else 0)) \
* ((C.C2 if worker.promoted else C.C1) + (C.D2 if worker.promoted else C.D1) * (C.AH if worker.ability else C.AL))
# Updating employer's payoff: + worker.productivity - wage
worker.employer.payoff_round += worker.productivity - worker.wage
class Results(Page):
form_model = 'player'
@staticmethod
def vars_for_template(player: Player):
return dict(workers = Worker.filter(group = player.group))
class Quest1(Page):
form_model = 'player'
form_fields = ['quest1_1', 'quest1_2', 'quest1_3', 'quest1_4', 'quest1_5']
@staticmethod
def is_displayed(player):
return player.round_number == C.NUM_ROUNDS
@staticmethod
def vars_for_template(player: Player):
return dict(
quest1_1_label = 'Wie verbunden fühlen Sie sich Ihrem Arbeitnehmer aus der Farbgruppe {}?'.format(player.worker1_html),
quest1_2_label = 'Wie verbunden fühlen Sie sich Ihrem Arbeitnehmer aus der Farbgruppe {}?'.format(player.worker2_html),
quest1_3_label = 'Wie verbunden fühlen Sie sich Ihrem Arbeitgeber aus der Farbgruppe {}?'.format(player.other_emp1_html),
quest1_4_label = 'Wie verbunden fühlen Sie sich Ihrem Arbeitnehmer aus der Farbgruppe {}?'.format(player.worker3_html),
quest1_5_label = 'Wie verbunden fühlen Sie sich Ihrem Arbeitnehmer aus der Farbgruppe {}?'.format(player.worker4_html)
)
class Quest2(Page):
form_model = 'player'
form_fields = ['quest2_1', 'quest2_2', 'quest2_3', 'quest2_4', 'quest2_5', 'quest2_6']
@staticmethod
def is_displayed(player):
return player.round_number == C.NUM_ROUNDS
class Quest3(Page):
form_model = 'player'
form_fields = ['quest3_1', 'quest3_2', 'quest3_3']
@staticmethod
def is_displayed(player):
return player.round_number == C.NUM_ROUNDS
class FinalResultsWaitPage(WaitPage):
@staticmethod
def is_displayed(player):
return player.round_number == C.NUM_ROUNDS
wait_for_all_groups = True
@staticmethod
def after_all_players_arrive(subsession):
session = subsession.session
players = subsession.get_players()
bonus_counting_list = [True for i in range(len(session.colors))]
for player in players:
# pick payoff from payoff relevant round
player_in_pay_round = player.in_round(session.vars['paying_round'])
player.payoff_pay_round = player_in_pay_round.payoff_round
# set group bonus for counting to False if count of player is false
player_in_first_round = player.in_round(1)
if player_in_first_round.picture_count != C.NUM_COLOR_FIELDS:
bonus_counting_list[player.color_id] = False
# setting bonus for counting task
for player in players:
player.bonus_counting = bonus_counting_list[player.color_id]
# calculate final payoff and EUR payoff amount
player.payoff_final = player.payoff_pay_round + int(player.bonus_counting) * C.BONUS_COUNTING + player.num_comp_correct * C.BONUS_COMP_QUEST
player.participant.payoff = cu(player.payoff_final)
class FinalResults(Page):
form_model = 'player'
@staticmethod
def is_displayed(player):
return player.round_number == C.NUM_ROUNDS
class End(Page):
form_model = 'player'
@staticmethod
def is_displayed(player):
return player.round_number == C.NUM_ROUNDS
page_sequence = [Quest1, Quest2, Quest3] #, CompQuestResults, FinalResultsWaitPage, FinalResults]
# page_sequence = [Welcome, ColorGroupAssignment, Counting, AnnounceInstructions, Instructions, CompQuest,
# AnnounceRound, Promotion, AnnounceExpectations, Expectations, ResultsWaitPage, Results,
# Quest1, Quest2, Quest3, FinalResultsWaitPage, FinalResults]
# page_sequence = [Counting]
#cd Dropbox\Programming\otree\qlm