from otree.api import * import math import pandas as pd import itertools import numpy as np from json import dumps as json_dumps, loads as json_loads import time class C(BaseConstants): NAME_IN_URL = 'task3' PLAYERS_PER_GROUP = 4 NUM_ROUNDS = 10 DEFAULT_TEMPLATE = 'cpr_defaults/templates/default_.html' A = 2.3 B = 0.025 # SVO_VALUES = pd.read_csv("cpr_defaults/svo_values.csv") class Subsession(BaseSubsession): group_size = models.IntegerField(initial=4) wait_for_ids = models.LongStringField(initial='[]') arrived_ids = models.LongStringField(initial='[]') def creating_session(subsession): # extractions = itertools.cycle([11, 18]) subsession.group_size = 4 for group in subsession.get_groups(): group.default_extraction = int(subsession.session.config["default_extraction"]) players = subsession.get_players() for p in players: p.participant.finished_participant = False class Group(BaseGroup): group_extraction = models.IntegerField(initial=0) default_extraction = models.IntegerField() group_extraction_last_round = models.IntegerField(initial=0) def set_payoffs(self): for player in self.get_players(): self.group_extraction += player.extraction for player in self.get_players(): player.payoff_task3 += np.round(self.payoff_extraction(player.extraction, self.group_extraction), 2) player.participant.payoff_task3 += player.payoff_task3 # p1 = self.get_player_by_id(1) # p2 = self.get_player_by_id(2) # p3 = self.get_player_by_id(3) # p4 = self.get_player_by_id(4) # self.group_extraction = p1.extraction + p2.extraction + p3.extraction + p4.extraction # p1.payoff_task3 += np.round(self.payoff_extraction(p1.extraction, self.group_extraction),2) # p2.payoff_task3 += np.round(self.payoff_extraction(p2.extraction, self.group_extraction),2) # p3.payoff_task3 += np.round(self.payoff_extraction(p3.extraction, self.group_extraction),2) # p4.payoff_task3 += np.round(self.payoff_extraction(p4.extraction, self.group_extraction),2) # # p1.participant.payoff_task3 += np.round(self.payoff_extraction(p1.extraction, self.group_extraction),2) # p2.participant.payoff_task3 += np.round(self.payoff_extraction(p2.extraction, self.group_extraction),2) # p3.participant.payoff_task3 += np.round(self.payoff_extraction(p3.extraction, self.group_extraction),2) # p4.participant.payoff_task3 += np.round(self.payoff_extraction(p4.extraction, self.group_extraction),2) @staticmethod def payoff_extraction(x, x_total): payoff_player = 0 if x == 0: payoff_player = 0 else: payoff_player = round((x / x_total) * ((C.A * x_total) - (C.B * math.pow(x_total, 2))), 2) return payoff_player wait_for_ids = models.LongStringField(initial='[]') arrived_ids = models.LongStringField(initial='[]') def unarrived_players(group: Group): return set(json_loads(group.wait_for_ids)) - set(json_loads(group.arrived_ids)) def unarrived_players_subsession(subsession: Subsession): return set(json_loads(subsession.wait_for_ids)) - set(json_loads(subsession.arrived_ids)) class Player(BasePlayer): internal_id = models.FloatField() extraction = models.IntegerField(initial=0) payoff_task3 = models.FloatField(initial=0) payoff_last_round = models.FloatField() time_1st_change_button = models.FloatField() time_spent_dialog = models.FloatField() number_changes = models.IntegerField() extraction_last_round = models.IntegerField() time_spent_round = models.FloatField() timeout_last_round = models.BooleanField(initial=False) completed_round = models.BooleanField(initial=False) other0 = models.FloatField() other1 = models.FloatField() other2 = models.FloatField() extended_wait = models.BooleanField(initial=False) # def waiting_too_long(player): # participant = player.participant # import time # return time.time() - participant.wait_page_arrival > 7 * 60 # # # def group_by_arrival_time_method(subsession, waiting_players): # if len(waiting_players) == C.PLAYERS_PER_GROUP: # return waiting_players # for player in waiting_players: # if waiting_too_long(player): # player.participant.finished_participant = True # player.participant.reason_finished = "We were not able to find a group for you. You will be still be paid for your time." # return [player] # FUNCTIONS # PAGES class GroupsWaitPage(WaitPage): title_text = "Task 3" body_text = "Please wait until we find a group for you" group_by_arrival_time = True #@staticmethod #def is_displayed(player): # return player.round_number == 1 @staticmethod def app_after_this_page(player, upcoming_apps): if player.participant.finished_participant: return "finished_experiment" @staticmethod def vars_for_template(player): #mins_elapsed = np.floor((time.time() - player.participant.wait_page_arrival) / 60) response = dict() if player.round_number == 1: response["body_text"] = "Please wait until we find a group for you" else: response["body_text"] = "Please wait for the next round." return response # def grouping_arrival(players, subsession: Subsession): # if subsession.round_number == 1: # if len(players) == 4: # matrix = subsession.get_group_matrix() # # new_matrix = [] # # for m in matrix: # # if len(m) > 4: # # m = set(m) - set(list(players)) # # new_matrix.append(list(m)) # # #new_matrix = np.vstack(new_matrix, m) # # new_matrix.append(list(players)) # # extracted_group = [x for x in matrix if set(players).issubset(set(x))] # # # Remove the extracted elements from the matrix # matrix = [x for x in matrix if set(players).isdisjoint(set(x))] # # # Check if the extracted group already exists in the matrix # if extracted_group not in matrix: # # Append the extracted group as a new element in the matrix # matrix.append(extracted_group) # print(matrix) # subsession.set_group_matrix(matrix) # arrived_ids_set = set(json_loads(subsession.arrived_ids)) # arrived_ids_set = arrived_ids_set - set(list(players)) # subsession.arrived_ids = json_dumps(list(arrived_ids_set)) # else: # subsession.group_like_round(1) # # def wait_group_live_method(player: Player, data): # subsession = player.subsession # arrived_ids_set = set(json_loads(subsession.arrived_ids)) # arrived_ids_set.add(player.id_in_subsession) # subsession.arrived_ids = json_dumps(list(arrived_ids_set)) # # response = {} # if len(arrived_ids_set) >= subsession.group_size: # grouping_arrival(list(arrived_ids_set)[:4], subsession) # response = {0: dict(finished=True, remaining=0)} # else: # response = {0: dict(finished=False, remaining=subsession.group_size - len(arrived_ids_set))} # return response # # # class GroupsWaitPage(Page): # @staticmethod # def is_displayed(player: Player): # subsession = player.subsession # # first time # if not json_loads(subsession.wait_for_ids): # wait_for_ids = [] # for p in subsession.get_players(): # wait_for_ids.append(p.id_in_subsession) # subsession.wait_for_ids = json_dumps(wait_for_ids) # return unarrived_players_subsession(subsession) # # @staticmethod # def live_method(player: Player, data): # if data.get('type') == 'wait_page': # return wait_group_live_method(player, data) # # @staticmethod # def error_message(player: Player, values): # group = player.group # if unarrived_players(group): # return "Wait page not finished" class Default(Page): timer_text = "" @staticmethod def get_timeout_seconds(player): return int(player.subsession.session.config["timeout_seconds_task3"]) @staticmethod def js_vars(player): return dict( default_extraction=int(player.subsession.session.config["default_extraction"]) ) @staticmethod def vars_for_template(player): if player.round_number >= 2: player.payoff_last_round = player.in_round(player.round_number - 1).payoff_task3 player.extraction_last_round = player.in_round(player.round_number - 1).extraction player.group.group_extraction_last_round = player.in_round( player.round_number - 1).group.group_extraction - player.extraction_last_round else: player.participant.payoff_task3 = 0 player.payoff_last_round = 0 player.group.group_extraction_last_round = 0 player.extraction_last_round = 0 @staticmethod def live_method(player, data): player.extraction = int(data["extraction"]) player.time_1st_change_button = float(data["time_1st_change_button"]) player.time_spent_dialog = float(data["time_spent_dialog"]) player.number_changes = int(data["number_changes"]) player.time_spent_round = float(data["time_spent_round"]) player.completed_round = data["completed_round"] @staticmethod def before_next_page(player, timeout_happened): if timeout_happened: player.extraction = 0 player.time_1st_change_button = 0 player.time_spent_dialog = 0 player.number_changes = 0 player.time_spent_round = 0 player.participant.finished_participant = True player.participant.reason_finished = "As we stated, you had 3 minutes to complete the task. You took longer than the maximum time allowed to make your decision. Therefore, you were removed from the experiment. Your payoff will be the fixed fee of £3." player.subsession.group_size -= 1 group = player.group arrived_ids_set = set(json_loads(group.arrived_ids)) arrived_ids_set.add(player.id_in_subsession) group.arrived_ids = json_dumps(list(arrived_ids_set)) # wait_page_live_method(player,"") @staticmethod def app_after_this_page(player, upcoming_apps): if player.participant.finished_participant: return "finished_experiment" class WaitPageRound(WaitPage): template_name = 'cpr_defaults/WaitPageRound.html' title_text = "Please wait" #body_text = "Please wait until we find a group for you" group_by_arrival_time = True @staticmethod def vars_for_template(player): group = player.group.get_players() complete = 0 for p in group: if p.completed_round: complete += 1 return dict(body_text = "Waiting for %d players to complete the task " % (4-complete)) @staticmethod def app_after_this_page(player, upcoming_apps): if player.participant.finished_participant: return "finished_experiment" @staticmethod def after_all_players_arrive(group): group.set_payoffs() # class RoundsWaitPage(WaitPage): # # @staticmethod # def vars_for_template(player): # group = player.group.get_players() # complete = 0 # for p in group: # if p.completed: # complete += 1 # return dict(body_text = "Waiting for %d players to complete the task " % (4-complete)) # # @staticmethod # def after_all_players_arrive(group): # group.set_payoffs() # def wait_page_live_method(player: Player, data): # group = player.group # arrived_ids_set = set(json_loads(group.arrived_ids)) # arrived_ids_set.add(player.id_in_subsession) # group.arrived_ids = json_dumps(list(arrived_ids_set)) # response = {} # if not unarrived_players(group): # response = {0: dict(finished=True, remaining=0)} # if player.group.group_extraction == 0: # group.set_payoffs() # else: # response = {0: dict(finished=False, remaining=len(unarrived_players(group)))} # return response # # # class WaitPageRound(Page): # @staticmethod # def is_displayed(player: Player): # group = player.group # # first time # if not json_loads(group.wait_for_ids): # wait_for_ids = [] # for p in group.get_players(): # if not p.participant.finished_participant: # wait_for_ids.append(p.id_in_subsession) # # wait_for_ids = [p.id_in_subsession for p in group.get_players()] # group.wait_for_ids = json_dumps(wait_for_ids) # return unarrived_players(group) # # @staticmethod # def live_method(player: Player, data): # if data.get('type') == 'wait_page': # return wait_page_live_method(player, data) # # @staticmethod # def error_message(player: Player, values): # group = player.group # if unarrived_players(group): # return "Wait page not finished" class RoundResultsPage(Page): timeout_seconds = 30 timer_text = "" @staticmethod def vars_for_template(player): response = {} others = player.get_others_in_group() for i, o in enumerate(others): response["other%d" % i] = o.extraction return response @staticmethod def js_vars(player): response = dict() response["remaining"] = 4 others = player.get_others_in_group() response["payoff_t3"] = player.payoff_task3 for i, o in enumerate(others): response["other%d" % i] = o.extraction if o.extraction == 0: response["remaining"] -= 1 return response @staticmethod def app_after_this_page(player, upcoming_apps): if player.subsession.group_size <= 2: player.participant.reason_finished = "Your group was composed of less than 3 participants. You will be paid for the remaining tasks." player.participant.payoff_task3 = 0 return "cpr_results" # print(upcoming_apps) # if player.subsession.group_size <= 2: # return upcoming_apps[-1] page_sequence = [GroupsWaitPage, Default, WaitPageRound, RoundResultsPage]