from django.conf import settings from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range, ) import random import math author = 'Sky Vercauteren' doc = """This is the main negotiation cycle, where users negotiate for a percentage of work, complete the counting task and repeat the cycle for the chosen number of rounds (this is hard coded). """ class Constants(BaseConstants): name_in_url = 'WN_Negotiations' players_per_group = 2 num_rounds = 13 """ session.vars = [initial_digits, main digits, num_rounds] participant.vars = [Role, name, w_number, gender, initial_score, score, standing, prev_earnings, negotiation, sum_negotiation, sum_work, survey_earnings] """ class Subsession(BaseSubsession): # set Fallback treatment fallback = models.BooleanField() # set matrices num_matrix = models.IntegerField(initial=0) matrix_size = models.IntegerField(initial=0) def creating_session(self): # initialize self.fallback = self.session.config['FALLBACK'] self.num_matrix = self.session.config['NUM_MATRICES'] self.matrix_size = self.session.config['MATRIX_SIZE'] # Create Groups self.group_randomly(fixed_id_in_group=True) i = 1 for p in self.get_players(): print("%s player.group during round %s: %s" % (i, self.round_number, p.group)) i += 1 # Populate Matrices. def set_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.payoff = pl.payoff + earnings pl.participant.vars['score'] = pl.score pl.participant.vars['prev_earnings'] = earnings pl.participant.vars['sum_negotiations'] = pl.participant.vars['sum_negotiations'] + pl.participant.vars[ 'negotiation'] pl.participant.vars['sum_work'] = pl.participant.vars['sum_work'] + pl.correct_answers pl.participant.vars['survey_earnings'] = 0 pass class Group(BaseGroup): # Negotiation Variables employeeOffer = models.IntegerField(initial=0) employerOffer = models.IntegerField(initial=0) employeeAgreement = models.IntegerField(initial=0) agreed = models.BooleanField(initial=False) def live_negotiate(self, id_in_group, offer): # TODO: Use 'FALLBACK' Treatment if self.agreed: return {id_in_group: {'EE_offer': self.employeeOffer, 'ER_offer': self.employerOffer, 'time': self.session.config['NEGOTIATION_TIME'] + 15, '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, 'time': self.session.config['NEGOTIATION_TIME'] + 15, '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).accepted = True 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, 'time': self.session.config['NEGOTIATION_TIME'] + 15, '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.get_player_by_id(id_in_group).set_offer() self.employeeOffer = int(offer) return {id_in_group: {'EE_offer': self.employeeOffer, 'ER_offer': self.employerOffer, 'time': self.session.config['NEGOTIATION_TIME'] + 15, '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 print("Agreed!") # store negotiated amounts self.get_player_by_id(id_in_group).accepted = True 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, 'time': self.session.config['NEGOTIATION_TIME'] + 15, '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.get_player_by_id(id_in_group).set_offer() self.employerOffer = int(offer) return {id_in_group: {'EE_offer': self.employeeOffer, 'ER_offer': self.employerOffer, 'time': self.session.config['NEGOTIATION_TIME'] + 15, 'role': self.get_player_by_id(id_in_group).role(), 'finished': self.agreed}} # This is a customized wait page designed to catch errors caused by a lost server connection or lag. def live_partner_wait(self, id_in_group, info): # set self and partner user = self.get_player_by_id(id_in_group) partner = None for p in self.get_players(): if p.id_in_group != id_in_group: partner = p # set waiting to true user.partnerWaiting = True # Throw broken data if user has been waiting too long. if info == -99: user.brokenData = True partner.brokenData = True # then see if the data matches. if user.participant.vars['negotiation'] > 0 or user.accepted: # If one player has an agreement, then both should. set agreement data. otherwise they did not agree. if self.agreed is False or self.employeeAgreement == 0: print("should only get here if it really broke!") user.brokenData = True partner.brokenData = True # Fix it! self.agreed = True if self.get_player_by_id(id_in_group).role == 'Employee': self.employeeAgreement = user.participant.vars['negotiation'] * 100 else: self.employeeAgreement = (1 - user.participant.vars['negotiation']) * 100 # and lastly reset the correct amount for the disconnected user. partner.participant.vars['negotiation'] = (1 - user.participant.vars['negotiation']) # once we've done that we just keep waiting for the partner to reconnect. if partner.partnerWaiting: if partner.partnerWaiting: # lets do a final check now that both users are on this page. # if either user "Accepts" then the page should not timeout. if (user.accepted and partner.expired) or (partner.accepted and user.expired): user.brokenData = True partner.brokenData = True return {id_in_group: {'finished': 100}} else: return {id_in_group: {'finished': -100}} # This method defines the live behavior for the initial work page def live_work(self, id_in_group, attempt): # TODO: Use 'FALLBACK' Treatment (double check here, make sure you dont need it here) 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_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_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, 'progress': self.get_player_by_id(id_in_group).progress, 'percent': perc, 'matrix': self.session.vars['main_digits'][prog], 'time': self.session.config['MAIN_WORK_TIME'], 'main': self.session.config['NUM_MATRICES'], 'size': self.subsession.matrix_size, 'finished': False}} # Otherwise, if they are somewhere in the middle, calculate correct answer, store it, and advance them if self.subsession.num_matrix * self.round_number >= prog >= ( self.round_number - 1) * self.subsession.num_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_matrix) * 100) prog += 1 self.get_player_by_id(id_in_group).progress = prog - ((self.round_number - 1) * self.subsession.num_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_matrix: return { id_in_group: {'correct': cor, 'earn': earn, 'progress': self.get_player_by_id(id_in_group).progress, 'percent': perc, 'matrix': self.session.vars['main_digits'][prog], 'time': self.session.config['MAIN_WORK_TIME'], 'main': self.session.config['NUM_MATRICES'], 'size': self.subsession.matrix_size, 'finished': False}} else: return {id_in_group: {'correct': cor, 'earn': earn, 'percent': perc, 'progress': self.get_player_by_id(id_in_group).progress, 'time': self.session.config['MAIN_WORK_TIME'], 'matrix': self.session.vars['main_digits'][self.subsession.num_matrix - 1], 'main': self.session.config['NUM_MATRICES'], 'size': self.subsession.matrix_size, 'finished': True}} pass class Player(BasePlayer): # Negotiation Variables accepted = models.BooleanField(initial=False) expired = models.BooleanField(initial=False) partnerWaiting = models.BooleanField(initial=False) brokenData = models.BooleanField(initial=False) def role(self): return self.participant.vars['Role'] opponent_session_ID = models.IntegerField() current_offer = models.IntegerField(initial=0) num_offers_made = models.IntegerField(initial=0) offer_01 = models.IntegerField(initial=0) offer_02 = models.IntegerField(initial=0) offer_03 = models.IntegerField(initial=0) offer_04 = models.IntegerField(initial=0) offer_05 = models.IntegerField(initial=0) offer_06 = models.IntegerField(initial=0) offer_07 = models.IntegerField(initial=0) offer_08 = models.IntegerField(initial=0) offer_09 = models.IntegerField(initial=0) offer_10 = models.IntegerField(initial=0) offer_11 = models.IntegerField(initial=0) offer_12 = models.IntegerField(initial=0) offer_13 = models.IntegerField(initial=0) offer_14 = models.IntegerField(initial=0) offer_15 = models.IntegerField(initial=0) offer_16 = models.IntegerField(initial=0) offer_17 = models.IntegerField(initial=0) offer_18 = models.IntegerField(initial=0) offer_19 = models.IntegerField(initial=0) offer_20 = models.IntegerField(initial=0) offer_21 = models.IntegerField(initial=0) offer_22 = models.IntegerField(initial=0) offer_23 = models.IntegerField(initial=0) offer_24 = models.IntegerField(initial=0) offer_25 = models.IntegerField(initial=0) offer_26 = models.IntegerField(initial=0) offer_27 = models.IntegerField(initial=0) offer_28 = models.IntegerField(initial=0) offer_29 = models.IntegerField(initial=0) offer_30 = models.IntegerField(initial=0) offer_31 = models.IntegerField(initial=0) offer_32 = models.IntegerField(initial=0) offer_33 = models.IntegerField(initial=0) offer_34 = models.IntegerField(initial=0) offer_35 = models.IntegerField(initial=0) offer_36 = models.IntegerField(initial=0) offer_37 = models.IntegerField(initial=0) offer_38 = models.IntegerField(initial=0) offer_39 = models.IntegerField(initial=0) offer_40 = models.IntegerField(initial=0) offer_41 = models.IntegerField(initial=0) offer_42 = models.IntegerField(initial=0) offer_43 = models.IntegerField(initial=0) offer_44 = models.IntegerField(initial=0) offer_45 = models.IntegerField(initial=0) offer_46 = models.IntegerField(initial=0) offer_47 = models.IntegerField(initial=0) offer_48 = models.IntegerField(initial=0) offer_49 = models.IntegerField(initial=0) offer_50 = models.IntegerField(initial=0) offer_51 = models.IntegerField(initial=0) # Work Variables def num_matrix(self): return self.subsession.num_matrix progress = models.IntegerField(initial=0) correct_answers = models.IntegerField(initial=0) score = models.IntegerField(initial=0) # this method places the current offer in the next available slot in the database. # There has got to be a better way to do this. (I think I would append text to a single string variable in the DB). def set_offer(self): if self.current_offer != 0: if self.offer_01 == 0: self.offer_01 = self.current_offer self.num_offers_made = 1 elif self.offer_02 == 0 and self.current_offer != self.offer_01: self.offer_02 = self.current_offer self.num_offers_made = 2 elif self.offer_03 == 0 and self.current_offer != self.offer_02: self.offer_03 = self.current_offer self.num_offers_made = 3 elif self.offer_04 == 0 and self.current_offer != self.offer_03: self.offer_04 = self.current_offer self.num_offers_made = 4 elif self.offer_05 == 0 and self.current_offer != self.offer_04: self.offer_05 = self.current_offer self.num_offers_made = 5 elif self.offer_06 == 0 and self.current_offer != self.offer_05: self.offer_06 = self.current_offer self.num_offers_made = 6 elif self.offer_07 == 0 and self.current_offer != self.offer_06: self.offer_07 = self.current_offer self.num_offers_made = 7 elif self.offer_08 == 0 and self.current_offer != self.offer_07: self.offer_08 = self.current_offer self.num_offers_made = 8 elif self.offer_09 == 0 and self.current_offer != self.offer_08: self.offer_09 = self.current_offer self.num_offers_made = 9 elif self.offer_10 == 0 and self.current_offer != self.offer_09: self.offer_10 = self.current_offer self.num_offers_made = 10 elif self.offer_11 == 0 and self.current_offer != self.offer_10: self.offer_11 = self.current_offer self.num_offers_made = 11 elif self.offer_12 == 0 and self.current_offer != self.offer_11: self.offer_12 = self.current_offer self.num_offers_made = 12 elif self.offer_13 == 0 and self.current_offer != self.offer_12: self.offer_13 = self.current_offer self.num_offers_made = 13 elif self.offer_14 == 0 and self.current_offer != self.offer_13: self.offer_14 = self.current_offer self.num_offers_made = 14 elif self.offer_15 == 0 and self.current_offer != self.offer_14: self.offer_15 = self.current_offer self.num_offers_made = 15 elif self.offer_16 == 0 and self.current_offer != self.offer_15: self.offer_16 = self.current_offer self.num_offers_made = 16 elif self.offer_17 == 0 and self.current_offer != self.offer_16: self.offer_17 = self.current_offer self.num_offers_made = 17 elif self.offer_18 == 0 and self.current_offer != self.offer_17: self.offer_18 = self.current_offer self.num_offers_made = 18 elif self.offer_19 == 0 and self.current_offer != self.offer_18: self.offer_19 = self.current_offer self.num_offers_made = 19 elif self.offer_20 == 0 and self.current_offer != self.offer_19: self.offer_20 = self.current_offer self.num_offers_made = 20 elif self.offer_21 == 0 and self.current_offer != self.offer_20: self.offer_21 = self.current_offer self.num_offers_made = 21 elif self.offer_22 == 0 and self.current_offer != self.offer_21: self.offer_22 = self.current_offer self.num_offers_made = 22 elif self.offer_23 == 0 and self.current_offer != self.offer_22: self.offer_23 = self.current_offer self.num_offers_made = 23 elif self.offer_24 == 0 and self.current_offer != self.offer_23: self.offer_24 = self.current_offer self.num_offers_made = 24 elif self.offer_25 == 0 and self.current_offer != self.offer_24: self.offer_25 = self.current_offer self.num_offers_made = 25 elif self.offer_26 == 0 and self.current_offer != self.offer_25: self.offer_26 = self.current_offer self.num_offers_made = 26 elif self.offer_27 == 0 and self.current_offer != self.offer_26: self.offer_27 = self.current_offer self.num_offers_made = 27 elif self.offer_28 == 0 and self.current_offer != self.offer_27: self.offer_28 = self.current_offer self.num_offers_made = 28 elif self.offer_29 == 0 and self.current_offer != self.offer_28: self.offer_29 = self.current_offer self.num_offers_made = 29 elif self.offer_30 == 0 and self.current_offer != self.offer_29: self.offer_30 = self.current_offer self.num_offers_made = 30 elif self.offer_31 == 0 and self.current_offer != self.offer_30: self.offer_31 = self.current_offer self.num_offers_made = 31 elif self.offer_32 == 0 and self.current_offer != self.offer_31: self.offer_32 = self.current_offer self.num_offers_made = 32 elif self.offer_33 == 0 and self.current_offer != self.offer_32: self.offer_33 = self.current_offer self.num_offers_made = 33 elif self.offer_34 == 0 and self.current_offer != self.offer_33: self.offer_34 = self.current_offer self.num_offers_made = 34 elif self.offer_35 == 0 and self.current_offer != self.offer_34: self.offer_35 = self.current_offer self.num_offers_made = 35 elif self.offer_36 == 0 and self.current_offer != self.offer_35: self.offer_36 = self.current_offer self.num_offers_made = 36 elif self.offer_37 == 0 and self.current_offer != self.offer_36: self.offer_37 = self.current_offer self.num_offers_made = 37 elif self.offer_38 == 0 and self.current_offer != self.offer_37: self.offer_38 = self.current_offer self.num_offers_made = 38 elif self.offer_39 == 0 and self.current_offer != self.offer_38: self.offer_39 = self.current_offer self.num_offers_made = 39 elif self.offer_40 == 0 and self.current_offer != self.offer_39: self.offer_40 = self.current_offer self.num_offers_made = 40 elif self.offer_41 == 0 and self.current_offer != self.offer_40: self.offer_41 = self.current_offer self.num_offers_made = 41 elif self.offer_42 == 0 and self.current_offer != self.offer_40: self.offer_42 = self.current_offer self.num_offers_made = 42 elif self.offer_43 == 0 and self.current_offer != self.offer_40: self.offer_43 = self.current_offer self.num_offers_made = 43 elif self.offer_44 == 0 and self.current_offer != self.offer_40: self.offer_44 = self.current_offer self.num_offers_made = 44 elif self.offer_45 == 0 and self.current_offer != self.offer_40: self.offer_45 = self.current_offer self.num_offers_made = 45 elif self.offer_46 == 0 and self.current_offer != self.offer_40: self.offer_47 = self.current_offer self.num_offers_made = 46 elif self.offer_47 == 0 and self.current_offer != self.offer_40: self.offer_47 = self.current_offer self.num_offers_made = 47 elif self.offer_48 == 0 and self.current_offer != self.offer_40: self.offer_48 = self.current_offer self.num_offers_made = 48 elif self.offer_49 == 0 and self.current_offer != self.offer_40: self.offer_49 = self.current_offer self.num_offers_made = 49 elif self.offer_50 == 0 and self.current_offer != self.offer_40: self.offer_50 = self.current_offer self.num_offers_made = 50 elif self.offer_51 == 0 and self.current_offer != self.offer_40: self.offer_51 = self.current_offer self.num_offers_made = 51 pass