from otree.api import Currency as c, currency_range from . import models from ._builtin import Page, WaitPage from .models import Constants import random from random import shuffle import itertools from decimal import * def vars_for_all_templates(self): return { 'condition': self.participant.vars['condition'], 'type': self.participant.vars['type'] } class GroupWait(WaitPage): template_name = 'cost_reporting_task_ss/CWaitPage.html' body_text = "Please wait briefly while you are matched with another Manager...(Note: You will be compensated for waiting " \ "(at $0.20/minute) and will wait a maximum of 5 minutes)..." title_text = "" #group_by_arrival_time = True # Group by time but make sure to get Mgr A and Mgr B separated so roles don't get flip-flopped #def get_players_for_group(self, waiting_players): # Don't need to restrict by condition because I won't be running conditions at the same time # mgrs_A = [p for p in waiting_players if p.participant.vars['type'] == 1] # mgrs_B = [p for p in waiting_players if p.participant.vars['type'] == 2] # num_waiting_A = len(mgrs_A) # num_waiting_B = len(mgrs_B) # if num_waiting_A > 0 and num_waiting_B > 0: #As long as one Mgr A and one Mgr B waiting, see if we can form a group... # i = 0 # while i < num_waiting_A: # mgr_match_A = mgrs_A[i] # Take the first waiting mgr A # mgr_partners_A = [p.partner_id for p in mgr_match_A.in_previous_rounds()] # Get all their previous partners # j = 0 # Start with the first waiter who is not the 'source' matcher # while j < num_waiting_B: # While there are Mgr Bs left to check # mgr_potential_partner = mgrs_B[j].participant_id # The first potential new partner # Compare source matcher's previous partners with first potential partner to see if they can match # k = 0 # match_count = 0 # while k < len(mgr_partners_A): # To loop through previous partners # if mgr_partners_A[k] == mgr_potential_partner or mgr_potential_partner == mgr_match_A.participant_id: # See if previous partner id matches first potential match # match_count += 1 # If so flag it # k += 1 # if match_count == 0: # If we got all the way through without finding they had previously matched... # return [mgrs_A[i], mgrs_B[j]] # Match them! # j += 1 # i += 1 # Move on to the next waiter to see if they can find a match # else: # return [mgrs_A[0], mgrs_B[0]] class GroupWait2(WaitPage): # 10/2/2020 - Use if wanting to keep people always matching in same round. Duh, they can't get matched across rounds anyway...right? template_name = 'cost_reporting_task_ss/CWaitPage.html' body_text = "Please wait briefly while the other Manager arrives...(Note: You will be compensated for waiting " \ "(at $0.20/minute) and will wait a maximum of 5 minutes)..." title_text = "" #group_by_arrival_time = True # Group by time but make sure to get Mgr A and Mgr B separated so roles don't get flip-flopped def old_get_players_for_group(self, waiting_players): mgrs_A = [p for p in waiting_players if p.participant.vars['type'] == 1 and p.round_number == 1] mgrs_B = [p for p in waiting_players if p.participant.vars['type'] == 2 and p.round_number == 1] num_waiting_A = len(mgrs_A) num_waiting_B = len(mgrs_B) if num_waiting_A > 0 and num_waiting_B > 0: #As long as one Mgr A and one Mgr B waiting, see if we can form a group... i = 0 while i < num_waiting_A: mgr_match_A = mgrs_A[i] # Take the first waiting mgr A mgr_partners_A = [p.partner_id for p in mgr_match_A.in_previous_rounds()] # Get all their previous partners j = 0 # Start with the first waiter who is not the 'source' matcher while j < num_waiting_B: # While there are Mgr Bs left to check mgr_potential_partner = mgrs_B[j].participant_id # The first potential new partner # Compare source matcher's previous partners with first potential partner to see if they can match k = 0 match_count = 0 while k < len(mgr_partners_A): # To loop through previous partners if mgr_partners_A[k] == mgr_potential_partner or mgr_potential_partner == mgr_match_A.participant_id: # See if previous partner id matches first potential match match_count += 1 # If so flag it k += 1 if match_count == 0: # If we got all the way through without finding they had previously matched... return [mgrs_A[i], mgrs_B[j]] # Match them! j += 1 i += 1 # Move on to the next waiter to see if they can find a match mgrs_A_2 = [p for p in waiting_players if p.participant.vars['type'] == 1 and p.round_number == 2] mgrs_B_2 = [p for p in waiting_players if p.participant.vars['type'] == 2 and p.round_number == 2] num_waiting_A_2 = len(mgrs_A_2) num_waiting_B_2 = len(mgrs_B_2) if num_waiting_A_2 > 0 and num_waiting_B_2 > 0: #As long as one Mgr A and one Mgr B waiting, see if we can form a group... i2 = 0 while i2 < num_waiting_A_2: mgr_match_A_2 = mgrs_A_2[i2] # Take the first waiting mgr A mgr_partners_A_2 = [p.partner_id for p in mgr_match_A_2.in_previous_rounds()] # Get all their previous partners j2 = 0 # Start with the first waiter who is not the 'source' matcher while j2 < num_waiting_B_2: # While there are Mgr Bs left to check mgr_potential_partner_2 = mgrs_B_2[j2].participant_id # The first potential new partner # Compare source matcher's previous partners with first potential partner to see if they can match k2 = 0 match_count2 = 0 while k2 < len(mgr_partners_A_2): # To loop through previous partners if mgr_partners_A_2[k2] == mgr_potential_partner_2 or mgr_potential_partner_2 == mgr_match_A_2.participant_id: # See if previous partner id matches first potential match match_count2 += 1 # If so flag it k2 += 1 if match_count2 == 0: # If we got all the way through without finding they had previously matched... return [mgrs_A_2[i2], mgrs_B_2[j2]] # Match them! j2 += 1 i2 += 1 # Move on to the next waiter to see if they can find a match mgrs_A_3 = [p for p in waiting_players if p.participant.vars['type'] == 1 and p.round_number == 3] mgrs_B_3 = [p for p in waiting_players if p.participant.vars['type'] == 2 and p.round_number == 3] num_waiting_A_3 = len(mgrs_A_3) num_waiting_B_3 = len(mgrs_B_3) if num_waiting_A_3 > 0 and num_waiting_B_3 > 0: #As long as one Mgr A and one Mgr B waiting, see if we can form a group... i3 = 0 while i3 < num_waiting_A_3: mgr_match_A_3 = mgrs_A_3[i3] # Take the first waiting mgr A mgr_partners_A_3 = [p.partner_id for p in mgr_match_A_3.in_previous_rounds()] # Get all their previous partners j3 = 0 # Start with the first waiter who is not the 'source' matcher while j3 < num_waiting_B_3: # While there are Mgr Bs left to check mgr_potential_partner_3 = mgrs_B_3[j3].participant_id # The first potential new partner # Compare source matcher's previous partners with first potential partner to see if they can match k3 = 0 match_count3 = 0 while k3 < len(mgr_partners_A_3): # To loop through previous partners if mgr_partners_A_3[k3] == mgr_potential_partner_3 or mgr_potential_partner_3 == mgr_match_A_3.participant_id: # See if previous partner id matches first potential match match_count3 += 1 # If so flag it k3 += 1 if match_count3 == 0: # If we got all the way through without finding they had previously matched... return [mgrs_A_3[i3], mgrs_B_3[j3]] # Match them! j3 += 1 i3 += 1 # Move on to the next waiter to see if they can find a match mgrs_A_4 = [p for p in waiting_players if p.participant.vars['type'] == 1 and p.round_number == 4] mgrs_B_4 = [p for p in waiting_players if p.participant.vars['type'] == 2 and p.round_number == 4] num_waiting_A_4 = len(mgrs_A_4) num_waiting_B_4 = len(mgrs_B_4) if num_waiting_A_4 > 0 and num_waiting_B_4 > 0: #As long as one Mgr A and one Mgr B waiting, see if we can form a group... i4 = 0 while i4 < num_waiting_A_4: mgr_match_A_4 = mgrs_A_4[i4] # Take the first waiting mgr A mgr_partners_A_4 = [p.partner_id for p in mgr_match_A_4.in_previous_rounds()] # Get all their previous partners j4 = 0 # Start with the first waiter who is not the 'source' matcher while j4 < num_waiting_B_4: # While there are Mgr Bs left to check mgr_potential_partner_4 = mgrs_B_4[j4].participant_id # The first potential new partner # Compare source matcher's previous partners with first potential partner to see if they can match k4 = 0 match_count4 = 0 while k4 < len(mgr_partners_A_4): # To loop through previous partners if mgr_partners_A_4[k4] == mgr_potential_partner_4 or mgr_potential_partner_4 == mgr_match_A_4.participant_id: # See if previous partner id matches first potential match match_count4 += 1 # If so flag it k4 += 1 if match_count4 == 0: # If we got all the way through without finding they had previously matched... return [mgrs_A_4[i4], mgrs_B_4[j4]] # Match them! j4 += 1 i4 += 1 # Move on to the next waiter to see if they can find a match mgrs_A_5 = [p for p in waiting_players if p.participant.vars['type'] == 1 and p.round_number == 5] mgrs_B_5 = [p for p in waiting_players if p.participant.vars['type'] == 2 and p.round_number == 5] num_waiting_A_5 = len(mgrs_A_5) num_waiting_B_5 = len(mgrs_B_5) if num_waiting_A_5 > 0 and num_waiting_B_5 > 0: #As long as one Mgr A and one Mgr B waiting, see if we can form a group... i5 = 0 while i5 < num_waiting_A_5: mgr_match_A_5 = mgrs_A_5[i5] # Take the first waiting mgr A mgr_partners_A_5 = [p.partner_id for p in mgr_match_A_5.in_previous_rounds()] # Get all their previous partners j5 = 0 # Start with the first waiter who is not the 'source' matcher while j5 < num_waiting_B_5: # While there are Mgr Bs left to check mgr_potential_partner_5 = mgrs_B_5[j5].participant_id # The first potential new partner # Compare source matcher's previous partners with first potential partner to see if they can match k5 = 0 match_count5 = 0 while k5 < len(mgr_partners_A_5): # To loop through previous partners if mgr_partners_A_5[k5] == mgr_potential_partner_5 or mgr_potential_partner_5 == mgr_match_A_5.participant_id: # See if previous partner id matches first potential match match_count5 += 1 # If so flag it k5 += 1 if match_count5 == 0: # If we got all the way through without finding they had previously matched... return [mgrs_A_5[i5], mgrs_B_5[j5]] # Match them! j5 += 1 i5 += 1 # Move on to the next waiter to see if they can find a match class Pre_Task(Page): def is_displayed(self): if self.player.round_number == 1: return True else: return False def before_next_page(self): #self.player.condition = self.participant.vars['condition'] #self.player.type = self.participant.vars['type'] #self.player.worker_id = self.participant.vars['worker_id'] #partner = self.player.get_partner() #self.player.partner_id = partner.participant_id pay_period = random.randint(1, 5) self.participant.vars['pay_period'] = pay_period class Interim(Page): def before_next_page(self): self.player.condition = self.participant.vars['condition'] self.player.type = self.participant.vars['type'] self.player.worker_id = self.participant.vars['worker_id'] partner = self.player.get_partner() self.player.partner_id = partner.participant_id if self.player.round_number == 1: self.player.actual_A = 50000 self.player.actual_B = 50000 elif self.player.round_number == 2: self.player.actual_A = 30000 self.player.actual_B = 30000 elif self.player.round_number == 3: self.player.actual_A = 70000 self.player.actual_B = 70000 elif self.player.round_number == 4: self.player.actual_A = 50000 self.player.actual_B = 50000 else: self.player.actual_A = 80000 self.player.actual_B = 80000 #Pick a cost for the pair #cost_list = (30000, 50000, 70000) #cost = random.choice(cost_list) # if self.player.type == 1: # self.player.actual_A = cost # self.player.actual_B = cost # Update each p's record for their 'partner' # models.Player.objects.filter(participant_id=self.player.partner_id).update(actual_A=cost, actual_B=cost) class Interim_Prev(Page): # 10/2/2020 - Not using since I'm going to let people match in different 'rounds' for them def is_displayed(self): if self.player.round_number == 1: return False else: return True def before_next_page(self): self.player.condition = self.participant.vars['condition'] self.player.type = self.participant.vars['type'] partner = self.player.get_partner() self.player.partner_id = partner.participant_id class ChatWait(WaitPage): template_name = 'cost_reporting_task/CWaitPage.html' body_text = "Please wait briefly while the other Manager arrives...(Note: You will be compensated for waiting " \ "(at $0.20/minute) and will wait a maximum of 5 minutes)..." title_text = "" class Cost_Info(Page): form_model = models.Player form_fields = ['chat_time', 'actual_A', 'actual_B'] def vars_for_template(self): if self.round_number == 1: return {'actual_A': 50000, 'actual_B': 50000} elif self.round_number == 2: return {'actual_A': 30000, 'actual_B': 30000} elif self.round_number == 3: return {'actual_A': 70000, 'actual_B': 70000} elif self.round_number == 4: return {'actual_A': 50000, 'actual_B': 50000} elif self.round_number == 5: return {'actual_A': 80000, 'actual_B': 80000} class ReportA(Page): form_model = models.Player form_fields = ['report_A', 'report_B'] def is_displayed(self): if self.player.type == 1: return True else: return False # Update partner's data def before_next_page(self): models.Player.objects.filter(participant_id=self.player.partner_id, round_number=self.player.round_number).update(report_A_partner=self.player.report_A, report_B_partner=self.player.report_B) class WaitReportA(WaitPage): template_name = 'cost_reporting_task/CWaitPage.html' body_text = "Please wait briefly while the other Manager makes their cost reports..." title_text = "" class ReportB(Page): form_model = models.Player form_fields = ['report_A', 'report_B'] def is_displayed(self): if self.player.type == 2: return True else: return False # Update partner's data def before_next_page(self): models.Player.objects.filter(participant_id=self.player.partner_id, round_number=self.player.round_number).update(report_A_partner=self.player.report_A, report_B_partner=self.player.report_B) # Can determine outcomes now that Manager B has reported, so do that before moving to results page # mgr_data = models.Player.objects.get(id=self.player.id) if self.player.report_A != self.player.report_A_partner: audit_A = 1 else: audit_A = 0 if self.player.report_B != self.player.report_B_partner: audit_B = 1 else: audit_B = 0 self.player.audit_flag_A = audit_A self.player.audit_flag_B = audit_B if audit_A == 1 and self.player.report_A != self.player.actual_A: self.player.bust_A = 1 else: self.player.bust_A = 0 if audit_B == 1 and self.player.report_B != self.player.actual_B: self.player.bust_B = 1 else: self.player.bust_B = 0 if audit_A == 1 and self.player.report_A_partner != self.player.actual_A: self.player.bust_A_partner = 1 else: self.player.bust_A_partner = 0 if audit_B == 1 and self.player.report_B_partner != self.player.actual_B: self.player.bust_B_partner = 1 else: self.player.bust_B_partner = 0 if self.player.condition < 5 or self.player.condition > 6: #No shared interest # This is always Manager B hitting this page, so easy to know whose data to look at if audit_A == 0 and self.player.report_A_partner > self.player.actual_A: self.player.slack_A = self.player.report_A_partner - self.player.actual_A self.player.slack_dol_A = Decimal(self.player.slack_A / 5000) * Decimal(0.25) else: self.player.slack_A = 0 self.player.slack_dol_A = 0.00 if audit_B == 0 and self.player.report_B > self.player.actual_B: self.player.slack_B = self.player.report_B_partner - self.player.actual_B self.player.slack_dol_B = Decimal(self.player.slack_B / 5000) * Decimal(0.25) else: self.player.slack_B = 0 self.player.slack_dol_B = 0.00 # Also need to update reporting outcomes, potential lira, and potential comp... if self.player.bust_A == 1 or self.player.bust_B == 1: potential_lira = 0 else: # If nothing is audited, they either got some slack plus salary or just salary if self.player.audit_flag_A == 0 and self.player.audit_flag_B == 0: if self.player.slack_B > 0: potential_lira = self.player.slack_B + 30000 else: potential_lira = 30000 # If only their project was audited (and they didn't get busted), they got an audit bonus/salary elif self.player.audit_flag_B == 1 and self.player.audit_flag_A == 0: potential_lira = 120000 elif self.player.audit_flag_A == 1 and self.player.audit_flag_B == 0: # Could have slack if self.player.slack_B > 0: potential_lira = self.player.slack_B + 120000 else: potential_lira = 120000 elif self.player.audit_flag_A == 1 and self.player.audit_flag_B == 1: potential_lira = 210000 potential_comp = Decimal(potential_lira / 5000) * Decimal(0.25) self.player.lira = potential_lira self.player.potential_comp = potential_comp # Same for Manager A... if self.player.bust_A_partner == 1 or self.player.bust_B_partner == 1: potential_lira_partner = 0 else: # If nothing is audited, they either got some slack plus salary or just salary if self.player.audit_flag_A == 0 and self.player.audit_flag_B == 0: if self.player.slack_A > 0: potential_lira_partner = self.player.slack_A + 30000 else: potential_lira_partner = 30000 # If only their project was audited (and they didn't get busted), they got an audit bonus/salary elif self.player.audit_flag_A == 1 and self.player.audit_flag_B == 0: potential_lira_partner = 120000 elif self.player.audit_flag_B == 1 and self.player.audit_flag_A == 0: # Could have slack if self.player.slack_A > 0: potential_lira_partner = self.player.slack_A + 120000 else: potential_lira_partner = 120000 elif self.player.audit_flag_A == 1 and self.player.audit_flag_B == 1: potential_lira_partner = 210000 potential_comp_partner = Decimal(potential_lira_partner / 5000) * Decimal(0.25) self.player.lira_partner = potential_lira_partner self.player.potential_comp_partner = potential_comp_partner else: #Shared interest with other conversion rate # This is always Manager B hitting this page, so easy to know whose data to look at # Shared interest, have to split the slack - but leave the raw slack as-is if audit_A == 0 and self.player.report_A_partner > self.player.actual_A: self.player.slack_A = self.player.report_A_partner - self.player.actual_A self.player.slack_dol_A = Decimal(self.player.slack_A / 5000) * Decimal(0.50) else: self.player.slack_A = 0 self.player.slack_dol_A = 0.00 if audit_B == 0 and self.player.report_B > self.player.actual_B: self.player.slack_B = self.player.report_1B_partner - self.player.actual_B self.player.slack_dol_B = Decimal(self.player.slack_B / 5000) * Decimal(0.50) else: self.player.slack_B = 0 self.player.slack_dol_B = 0.00 # Also need to update reporting outcomes, potential lira, and potential comp... if self.player.bust_A == 1 or self.player.bust_B == 1: potential_lira = 0 else: # If nothing is audited, they either got some slack plus salary or just salary if self.player.audit_flag_A == 0 and self.player.audit_flag_B == 0: if self.player.slack_B > 0: potential_lira = int(self.player.slack_B / 2) + 15000 else: potential_lira = 15000 # If only their project was audited (and they didn't get busted), they got an audit bonus/salary elif self.player.audit_flag_B == 1 and self.player.audit_flag_A == 0: potential_lira = 60000 elif self.player.audit_flag_A == 1 and self.player.audit_flag_B == 0: # Could have slack if self.player.slack_B > 0: potential_lira = int(self.player.slack_B / 2) + 60000 else: potential_lira = 60000 elif self.player.audit_flag_A == 1 and self.player.audit_flag_B == 1: potential_lira = 105000 potential_comp = Decimal(potential_lira / 5000) * Decimal(0.50) self.player.lira = potential_lira self.player.potential_comp = potential_comp # Same for Manager A... if self.player.bust_A_partner == 1 or self.player.bust_B_partner == 1: potential_lira_partner = 0 else: # If nothing is audited, they either got some slack plus salary or just salary if self.player.audit_flag_A == 0 and self.player.audit_flag_B == 0: if self.player.slack_A > 0: potential_lira_partner = int(self.player.slack_A / 2) + 15000 else: potential_lira_partner = 15000 # If only their project was audited (and they didn't get busted), they got an audit bonus/salary elif self.player.audit_flag_A == 1 and self.player.audit_flag_B == 0: potential_lira_partner = 60000 elif self.player.audit_flag_B == 1 and self.player.audit_flag_A == 0: # Could have slack if self.player.slack_A > 0: potential_lira_partner = int(self.player.slack_A / 2) + 60000 else: potential_lira_partner = 60000 elif self.player.audit_flag_A == 1 and self.player.audit_flag_B == 1: potential_lira_partner = 105000 potential_comp_partner = Decimal(potential_lira_partner / 5000) * Decimal(0.50) self.player.lira_partner = potential_lira_partner self.player.potential_comp_partner = potential_comp_partner if self.player.lira == 0: self.player.slack_B = 0 self.player.slack_dol_B = 0.00 if self.player.lira_partner == 0: self.player.slack_A = 0 self.player.slack_dol_A = 0.00 # ...then update Manager A's stuff on the way out models.Player.objects.filter(participant_id=self.player.partner_id, round_number=self.player.round_number).update(audit_flag_A=self.player.audit_flag_A, audit_flag_B=self.player.audit_flag_B, slack_A=self.player.slack_A, slack_dol_A=self.player.slack_dol_A, slack_B=self.player.slack_B, slack_dol_B=self.player.slack_dol_B, bust_A=self.player.bust_A_partner, bust_A_partner=self.player.bust_A, bust_B=self.player.bust_B_partner, bust_B_partner=self.player.bust_B, lira=self.player.lira_partner, potential_comp=self.player.potential_comp_partner, lira_partner=self.player.lira, potential_comp_partner=self.player.potential_comp) class WaitReportB(WaitPage): template_name = 'cost_reporting_task/CWaitPage.html' body_text = "Please wait briefly while the other Manager makes their cost reports..." title_text = "" class Reporting_Results(Page): def vars_for_template(self): return {'slack_A_share': int(self.player.slack_A / 2), 'slack_B_share': int(self.player.slack_B / 2)} #Before leaving the results, check to see if this is the pay period. If so, store in participant vars for part2 app def before_next_page(self): if self.participant.vars['pay_period'] == self.round_number: comp_1 = Decimal(self.player.lira / 5000) * Decimal(0.25) self.participant.vars['pay_lira'] = self.player.lira self.participant.vars['comp_1'] = '{0:.2f}'.format(round(comp_1, 2)) page_sequence = [ GroupWait, Pre_Task, Interim, ChatWait, Cost_Info, ReportA, WaitReportA, ReportB, WaitReportB, Reporting_Results ]