# Sky Vercauteren # WageNegotiations # July 2020 from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range, ) from django.conf import settings import random import math author = "Sky Vercauteren" doc = """The setup includes "logging in", selecting a gender identity, completing 2 rounds of practice, and then completing the first initial counting placement task. """ class Constants(BaseConstants): name_in_url = 'WageNegotiations' players_per_group = 2 num_rounds = 2 """ session.vars = [initial_digits, num_rounds] participant.vars = [Role, name, field, gender, score, standing, prev_earnings, initial_score] """ class Subsession(BaseSubsession): num_init_matrix = models.IntegerField(initial=0) matrix_size = models.IntegerField(initial=0) num_main_matrix = models.IntegerField(initial=0) # generate matrix contents for this round here.. def creating_session(self): # initial self.group_randomly(fixed_id_in_group=True) self.num_main_matrix = self.session.config['NUM_MATRICES'] self.num_init_matrix = self.session.config['INITIAL_MATRICES'] self.matrix_size = self.session.config['MATRIX_SIZE'] if self.round_number == 1: # populate initial matrices initlist = [] initmtx = [] initrow = [] for m in range(0, self.num_init_matrix * 2 + 1): for r in range(0, self.matrix_size): for X in range(0, self.matrix_size): digit = random.randint(0, 1) initrow.append(digit) initmtx.append(initrow) initrow = [] initlist.append(initmtx) initmtx = [] self.session.vars['initial_digits'] = initlist mainlist = [] mtx = [] row = [] for m in range(0, self.num_main_matrix * Constants.num_rounds * 2 +1): for r in range(0, self.matrix_size): for x in range(0, self.matrix_size): digit = random.randint(0, 1) row.append(digit) mtx.append(row) row = [] mainlist.append(mtx) mtx = [] self.session.vars['main_digits'] = mainlist def set_scores(self): # calculate average score and each players standing total = 0 for p in self.get_players(): total = total + p.score standing = 1 for op in self.get_players(): if op.score > p.score: standing += 1 p.progress = 0 p.participant.vars['standing'] = standing p.participant.vars['score'] = p.score p.participant.vars['prev_earnings'] = 0 p.participant.vars['sum_negotiations'] = 0 p.participant.vars['sum_work'] = 0 p.participant.vars['practice_earnings'] = p.payoff # Store avg score. for g in self.get_groups(): g.avgScore = round(total / self.session.num_participants, 2) def set_practice_scores(self): # calculate average score for g in self.get_groups(): total = 0 for p in g.get_players(): total = total + p.score for pl in g.get_players(): if g.employeeAgreement == 0: earnings = 0 elif pl.role() == 'Employee': earnings = int(total * (g.employeeAgreement / 100)) else: earnings = int(total * ((100 - g.employeeAgreement) / 100)) pl.participant.vars['practice_earnings'] = earnings + pl.participant.vars['practice_earnings'] pl.participant.vars['score'] = pl.score pl.participant.vars['prev_earnings'] = earnings pass class Group(BaseGroup): # Work Variables avgScore = models.FloatField() # Negotiation Variables employeeOffer = models.IntegerField(initial=0) employerOffer = models.IntegerField(initial=0) employeeAgreement = models.IntegerField(initial=0) agreed = models.BooleanField(initial=False) # This method defines the live behavior for the initial work page def live_initial_work(self, id_in_group, attempt): # First, check the users progress, if the page is new, it will be -999 and will set the initial matrix prog = self.get_player_by_id(id_in_group).progress if attempt == -999: self.get_player_by_id(id_in_group).progress = 0 return {id_in_group: {'correct': 0, 'percent': 0, 'matrix': self.session.vars['initial_digits'][0], 'finished': False}} # Otherwise, if they are somewhere in the middle, calculate correct answer, store it, and advance them if self.subsession.num_init_matrix >= prog >= 0: answer = 0 for row in self.session.vars['initial_digits'][prog]: for col in row: if col == 1: answer += 1 if attempt == answer: self.get_player_by_id(id_in_group).correct_init_answers += 1 cor = self.get_player_by_id(id_in_group).correct_init_answers perc = 0 if cor > 0: perc = cor / self.subsession.num_init_matrix * 100 prog += 1 self.get_player_by_id(id_in_group).progress = prog self.get_player_by_id(id_in_group).score = cor # if the user has finished all the matrices, submit the page. if prog < self.subsession.num_init_matrix: return {id_in_group: {'correct': cor, 'percent': perc, 'matrix': self.session.vars['initial_digits'][prog], 'finished': False}} else: return {id_in_group: {'correct': cor, 'percent': perc, 'matrix': self.session.vars['initial_digits'][ self.subsession.num_init_matrix - 1], 'finished': True}} def live_negotiate(self, id_in_group, offer): if self.agreed: return {id_in_group: {'EE_offer': self.employeeOffer, 'ER_offer': self.employerOffer, 'role': self.get_player_by_id(id_in_group).role(), 'finished': self.agreed}} # This is the "refresh" on the live page. if offer == -100: # first, set the negotiated percent every time, in case of a timeout. if self.employeeAgreement == 0: self.get_player_by_id(id_in_group).participant.vars['negotiation'] = 0 else: if self.get_player_by_id(id_in_group).role() == 'Employee': self.get_player_by_id(id_in_group).participant.vars['negotiation'] = self.employeeAgreement / 100 else: self.get_player_by_id(id_in_group).participant.vars['negotiation'] = ( 100 - self.employeeAgreement) / 100 return {id_in_group: {'EE_offer': self.employeeOffer, 'ER_offer': self.employerOffer, 'role': self.get_player_by_id(id_in_group).role(), 'finished': self.agreed}} # First, check everything against the role. role = self.get_player_by_id(id_in_group).role() if role == 'Employee': # Then we check if the user accepted if offer == -999 or int(offer) == self.employerOffer: if self.employerOffer > 0: self.employeeAgreement = self.employerOffer self.agreed = True # store negotiated amounts self.get_player_by_id(id_in_group).participant.vars['negotiation'] = self.employeeAgreement / 100 for p in self.get_players(): if p.id_in_group != id_in_group: p.participant.vars['negotiation'] = (100 - self.employeeAgreement) / 100 return {id_in_group: {'EE_offer': self.employeeOffer, 'ER_offer': self.employerOffer, 'role': self.get_player_by_id(id_in_group).role(), 'finished': self.agreed}} # If we made it this far we must be making an offer tempOffer = self.employeeOffer if self.employeeOffer == 0: tempOffer = 100 if int(offer) < tempOffer: self.get_player_by_id(id_in_group).current_offer = int(offer) self.employeeOffer = int(offer) return {id_in_group: {'EE_offer': self.employeeOffer, 'ER_offer': self.employerOffer, 'role': self.get_player_by_id(id_in_group).role(), 'finished': self.agreed}} else: # so we check if the user accepted if offer == -999 or int(offer) == self.employeeOffer and self.employeeOffer > 0: if self.employeeOffer > 0: self.agreed = True self.employeeAgreement = self.employeeOffer # store negotiated amounts self.get_player_by_id(id_in_group).participant.vars['negotiation'] = ( 100 - self.employeeAgreement) / 100 for p in self.get_players(): if p.id_in_group != id_in_group: p.participant.vars['negotiation'] = self.employeeAgreement / 100 return {id_in_group: {'EE_offer': self.employeeOffer, 'ER_offer': self.employerOffer, 'role': self.get_player_by_id(id_in_group).role(), 'finished': self.agreed}} # So we must be making an offer if int(offer) > self.employerOffer: self.get_player_by_id(id_in_group).current_offer = int(offer) self.employerOffer = int(offer) return {id_in_group: {'EE_offer': self.employeeOffer, 'ER_offer': self.employerOffer, 'role': self.get_player_by_id(id_in_group).role(), 'finished': self.agreed}} # This method defines the live behavior for the initial work page def live_work(self, id_in_group, attempt): neg = self.get_player_by_id(id_in_group).participant.vars['negotiation'] prog = self.get_player_by_id(id_in_group).progress + ((self.round_number - 1) * self.subsession.num_main_matrix) # This is the refresh method. It will update the "earnings" if attempt == -100: cor = self.get_player_by_id(id_in_group).correct_answers perc = 0 if cor > 0: perc = (cor / self.subsession.num_main_matrix) * 100 totalScore = cor * self.session.config['POINTS_PER_ANSWER'] for p in self.get_players(): if p.id_in_group != id_in_group: opID = p.participant.id_in_session self.get_player_by_id(id_in_group).opponent_session_ID = opID totalScore = totalScore + p.score earn = int(totalScore * neg) return {id_in_group: {'correct': cor, 'earn': earn, 'percent': perc, 'matrix': self.session.vars['main_digits'][prog], 'finished': False}} # Otherwise, if they are somewhere in the middle, calculate correct answer, store it, and advance them if self.subsession.num_main_matrix * self.round_number >= prog >= (self.round_number - 1) * self.subsession.num_main_matrix: answer = 0 for row in self.session.vars['main_digits'][prog]: for col in row: if col == 1: answer += 1 if attempt == answer: self.get_player_by_id(id_in_group).correct_answers += 1 # Calculate the number correct, score, and earnings. cor = self.get_player_by_id(id_in_group).correct_answers score = cor * self.session.config['POINTS_PER_ANSWER'] totalScore = score for p in self.get_players(): if p.id_in_group != id_in_group: totalScore = totalScore + p.score earn = int(totalScore * neg) perc = 0 if cor > 0: perc = int((cor / self.subsession.num_main_matrix) * 100) prog += 1 self.get_player_by_id(id_in_group).progress = prog - ((self.round_number - 1) * self.subsession.num_main_matrix) self.get_player_by_id(id_in_group).score = score # if the user has finished all the matrices, submit the page. if prog < self.round_number * self.subsession.num_main_matrix: return {id_in_group: {'correct': cor, 'earn': earn, 'percent': perc, 'matrix': self.session.vars['main_digits'][prog], 'finished': False}} else: return {id_in_group: {'correct': cor, 'earn': earn, 'percent': perc, 'matrix': self.session.vars['main_digits'][self.subsession.num_main_matrix - 1], 'finished': True}} def special_wait(self): for p in self.get_players(): p.participant.vars['progress'] = self.round_number * self.subsession.num_init_matrix p.participant.vars['negotiation'] = 0 p.participant.vars['score'] = 0 p.participant.vars['prev_earnings'] = 0 p.participant.vars['standing'] = 0 p.participant.vars['initial_score'] = 0 pass class Player(BasePlayer): # Initial Variables name = models.StringField() field = models.StringField() gender = models.StringField(choices=["Male/Man", "Female/Woman"]) def role(self): return self.participant.vars['Role'] # Work Variables progress = models.IntegerField(initial=0) correct_init_answers = models.IntegerField(initial=0) correct_answers = models.IntegerField(initial=0) def num_matrix(self): return self.subsession.num_init_matrix score = models.IntegerField(initial=0) # Practice Variables current_offer = models.IntegerField(initial=0) opponent_session_ID = models.IntegerField() pass