from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c ) import pandas as pd import random import os import math import numpy as np # from background.tasks import huey # from background.numeric_tasks import huey as numeric_huey base_directory = os.path.abspath(os.curdir) data_directory = os.path.join(base_directory, 'text_exp_bot_bg') reviews_not_seen_file_path = os.path.join(data_directory, 'reviews_not_seen.csv') image_directory = os.path.join(base_directory, '_static', 'text_exp_bot_bg') author = 'Eilam Shapira' doc = """""" class Constants(BaseConstants): name_in_url = 'text_exp_bot_bg' players_per_group = None num_rounds = 60 num_bots = 6 rounds_per_bot = num_rounds / num_bots assert rounds_per_bot * num_bots == num_rounds sender_payoff_per_round = 1 seconds_wait_expert = 240 # 120 seconds_wait_first_rounds_expert = 240 # 150 seconds_wait_dm = 240 seconds_wait_first_rounds_dm = 240 first_rounds = [1] num_rounds_long_wait = first_rounds[-1] bonus = 1 number_reviews_per_hotel = 7 cost = 8 rounds_for_reviews_in_test = [7, 9] num_timeouts_remove = 5 minutes_to_timeout_wait_page = 10 pay_by_time_for_waiting = 0.04 max_payment_for_waiting = 0.4 reviews_not_seen = pd.read_csv(reviews_not_seen_file_path, header=0) intro_timeout_minutes = 10 real_participation_fee = 2.5 class Subsession(BaseSubsession): condition = models.StringField() def creating_session(self): """ This function will run at the beginning of each session and will initial the problems parameters for all the rounds Each row will be the parameters of the index+1 round (index starts at 0) :return: """ problems_data_file_path = os.path.join(data_directory, f"10_reviews_test_data.csv") self.condition = self.session.config['bot_cond'] if self.round_number == 1: for g in self.get_groups(): for p in g.get_players(): if 'round_parameters' in p.participant.vars: print(f'already assign round_parameters for player with id: {p.id_in_group}') continue problems = pd.read_csv(problems_data_file_path, header=0).sample(frac=1).reset_index(drop=True) # Load problems and shuffle the them so each subject will get them in a different order for i in range(Constants.number_reviews_per_hotel): problems[f'score_{i}'] = problems[f'score_{i}'].astype(float) # insert the positive and then the negative or the negative and then the positive randomly for hotel_num in range(Constants.num_rounds): if bool(random.getrandbits(1)): problems.loc[hotel_num, f'random_positive_negative_review_{i}'] = \ problems.loc[hotel_num, f'negative_review_{i}'] + ' ' + problems.loc[ hotel_num, f'positive_review_{i}'] else: problems.loc[hotel_num, f'random_positive_negative_review_{i}'] = \ problems.loc[hotel_num, f'positive_review_{i}'] + ' ' + problems.loc[ hotel_num, f'negative_review_{i}'] # Create problem set: p.participant.vars['problem_parameters'] = problems round_parameters = pd.DataFrame( columns=['pair_id', 'review_id', 'subsession_round_number', 'lottery_result_low', 'lottery_result_med1', 'lottery_result_high', 'group_lottery_result', 'group_receiver_payoff', 'group_sender_answer_index', 'group_sender_payoff', 'group_sender_answer_reviews', 'group_average_score' 'chose_lose', 'chose_earn', 'not_chose_lose', 'not_chose_earn', 'group_sender_answer_scores', 'group_score_0', 'group_score_1', 'group_score_2', 'group_score_3', 'group_score_4', 'group_score_5', 'group_score_6', 'group_random_positive_negative_review_0', 'group_random_positive_negative_review_1', 'group_random_positive_negative_review_2', 'group_random_positive_negative_review_3', 'group_random_positive_negative_review_4', 'group_random_positive_negative_review_5', 'group_random_positive_negative_review_6', 'group_positive_review_5', 'group_positive_review_6', 'group_positive_review_4', 'group_positive_review_3', 'group_positive_review_2', 'group_positive_review_1', 'group_positive_review_0', 'group_negative_review_0', 'group_negative_review_1', 'group_negative_review_2', 'group_negative_review_3', 'group_negative_review_4', 'group_negative_review_5', 'group_negative_review_6', 'status', 'player_id_in_group']) for round_ in range(1, 11): # print(self.participant.vars['problem_parameters']['average_score'],'lplp') round_parameters.at[round_ - 1, 'player_id_in_group'] = 2 round_parameters.at[round_ - 1, 'status'] = 'play' round_parameters.at[round_ - 1, 'group_negative_review_0'] = \ problems.at[round_ - 1, 'negative_review_0'] round_parameters.at[round_ - 1, 'group_negative_review_1'] = \ problems.at[round_ - 1, 'negative_review_1'] round_parameters.at[round_ - 1, 'group_negative_review_2'] = \ problems.at[round_ - 1, 'negative_review_2'] round_parameters.at[round_ - 1, 'group_negative_review_3'] = \ problems.at[round_ - 1, 'negative_review_3'] round_parameters.at[round_ - 1, 'group_negative_review_4'] = \ problems.at[round_ - 1, 'negative_review_4'] round_parameters.at[round_ - 1, 'group_negative_review_5'] = \ problems.at[round_ - 1, 'negative_review_5'] round_parameters.at[round_ - 1, 'group_negative_review_6'] = \ problems.at[round_ - 1, 'negative_review_6'] round_parameters.at[round_ - 1, 'group_positive_review_5'] = \ problems.at[round_ - 1, 'positive_review_5'] round_parameters.at[round_ - 1, 'group_positive_review_4'] = \ problems.at[round_ - 1, 'positive_review_4'] round_parameters.at[round_ - 1, 'group_positive_review_3'] = \ problems.at[round_ - 1, 'positive_review_3'] round_parameters.at[round_ - 1, 'group_positive_review_1'] = \ problems.at[round_ - 1, 'positive_review_1'] round_parameters.at[round_ - 1, 'group_positive_review_2'] = \ problems.at[round_ - 1, 'positive_review_2'] round_parameters.at[round_ - 1, 'group_positive_review_0'] = \ problems.at[round_ - 1, 'positive_review_0'] round_parameters.at[round_ - 1, 'group_random_positive_negative_review_0'] = \ problems.at[round_ - 1, 'random_positive_negative_review_0'] round_parameters.at[round_ - 1, 'group_random_positive_negative_review_1'] = \ problems.at[round_ - 1, 'random_positive_negative_review_1'] round_parameters.at[round_ - 1, 'group_random_positive_negative_review_2'] = \ problems.at[round_ - 1, 'random_positive_negative_review_2'] round_parameters.at[round_ - 1, 'group_random_positive_negative_review_3'] = \ problems.at[round_ - 1, 'random_positive_negative_review_3'] round_parameters.at[round_ - 1, 'group_random_positive_negative_review_4'] = \ problems.at[round_ - 1, 'random_positive_negative_review_4'] round_parameters.at[round_ - 1, 'group_random_positive_negative_review_5'] = \ problems.at[round_ - 1, 'random_positive_negative_review_5'] round_parameters.at[round_ - 1, 'group_random_positive_negative_review_6'] = \ problems.at[round_ - 1, 'random_positive_negative_review_6'] round_parameters.at[round_ - 1, 'group_score_0'] = problems.at[round_ - 1, 'score_0'] round_parameters.at[round_ - 1, 'group_score_1'] = problems.at[round_ - 1, 'score_1'] round_parameters.at[round_ - 1, 'group_score_2'] = problems.at[round_ - 1, 'score_2'] round_parameters.at[round_ - 1, 'group_score_3'] = problems.at[round_ - 1, 'score_3'] round_parameters.at[round_ - 1, 'group_score_4'] = problems.at[round_ - 1, 'score_4'] round_parameters.at[round_ - 1, 'group_score_5'] = problems.at[round_ - 1, 'score_5'] round_parameters.at[round_ - 1, 'group_score_6'] = problems.at[round_ - 1, 'score_6'] round_parameters.at[round_ - 1, 'subsession_round_number'] = round_ round_parameters.at[round_ - 1, 'group_average_score'] =\ round(problems.at[round_ - 1, 'average_score'], 2) p.participant.vars['round_parameters'] = round_parameters # for debug: # print('all problems in creating_session: ', p.participant.vars['problem_parameters']) # calculate the worst cases for receiver, and the probability to get bonus worst_case_receiver = round((problems['min_score']-Constants.cost).sum(), 2) self.session.vars['initial_points_receiver'] = math.fabs(worst_case_receiver) # this will be calculated during the experiment, and will be the sum of the non-negative # payoffs in the game: 0- if Y is the result in the lottery # X- if X is the result --> this is the real maximum points from this experiment p.participant.vars['max_points'] = math.fabs(worst_case_receiver) return class Group(BaseGroup): pass def lottery(score_list): return random.choice(score_list) class Player(BasePlayer): name = models.StringField( verbose_name='What is your name?' ) age = models.IntegerField( verbose_name='What is your age?', min=5 ) gender = models.StringField( choices=['Male', 'Female'], verbose_name='What is your gender?', widget=widgets.RadioSelect) is_student = models.BooleanField( verbose_name='Are you a student?', widget=widgets.RadioSelect) occupation = models.StringField( verbose_name='What is your occupation?' ) residence = models.StringField( verbose_name='What is your home town?' ) intro_test = models.StringField( verbose_name='Do you have any comments on this HIT?', initial='' ) # DM test chosen and not chosen reviews dm_test_chosen_review_1 = models.BooleanField( verbose_name='Did you see this during the experiment?', widget=widgets.RadioSelect ) dm_test_chosen_review_2 = models.BooleanField( verbose_name='Did you see this during the experiment?', widget=widgets.RadioSelect ) dm_test_not_chosen_review_1 = models.BooleanField( verbose_name='Did you see this during the experiment?', widget=widgets.RadioSelect ) dm_test_not_chosen_review_2 = models.BooleanField( verbose_name='Did you see this during the experiment?', widget=widgets.RadioSelect ) action = models.IntegerField() action_id = models.StringField(default='') receiver_choice = models.BooleanField() # True (1) for Certainty and False (0) for Lottery lottery_result = models.FloatField() sender_timeout = models.BooleanField(choices=[True, False], initial=False) # True if the sender had timeout receiver_timeout = models.BooleanField(choices=[True, False], initial=False) # True if the receiver had timeout sender_payoff = models.IntegerField() receiver_payoff = models.FloatField() receiver_passed_test = models.FloatField() failed_intro_test = models.BooleanField(choices=[True, False], initial=False) failed_test = models.IntegerField() instruction_timeout = models.BooleanField(choices=[True, False], initial=False) pass_intro_test = models.BooleanField() # reviews and scores is_done = models.BooleanField() score_0 = models.FloatField() score_1 = models.FloatField() score_2 = models.FloatField() score_3 = models.FloatField() score_4 = models.FloatField() score_5 = models.FloatField() score_6 = models.FloatField() review_0 = models.LongStringField() review_1 = models.LongStringField() review_2 = models.LongStringField() review_3 = models.LongStringField() review_4 = models.LongStringField() review_5 = models.LongStringField() review_6 = models.LongStringField() positive_review_0 = models.LongStringField() positive_review_1 = models.LongStringField() positive_review_2 = models.LongStringField() positive_review_3 = models.LongStringField() positive_review_4 = models.LongStringField() positive_review_5 = models.LongStringField() positive_review_6 = models.LongStringField() negative_review_0 = models.LongStringField() negative_review_1 = models.LongStringField() negative_review_2 = models.LongStringField() negative_review_3 = models.LongStringField() negative_review_4 = models.LongStringField() negative_review_5 = models.LongStringField() negative_review_6 = models.LongStringField() random_positive_negative_review_0 = models.LongStringField() random_positive_negative_review_1 = models.LongStringField() random_positive_negative_review_2 = models.LongStringField() random_positive_negative_review_3 = models.LongStringField() random_positive_negative_review_4 = models.LongStringField() random_positive_negative_review_5 = models.LongStringField() random_positive_negative_review_6 = models.LongStringField() average_score = models.FloatField() sender_answer_reviews = models.LongStringField() sender_answer_negative_reviews = models.LongStringField() sender_answer_positive_reviews = models.LongStringField() sender_answer_scores = models.FloatField() sender_answer_index = models.IntegerField(widget=widgets.RadioSelect, choices=list(range(1, Constants.number_reviews_per_hotel + 1))) finish_mcts = models.BooleanField(initial=False) receiver_finish_round = models.BooleanField(initial=False) bot_name = models.CharField() bot_order = models.IntegerField() def get_payoff_against_bot(self): user_payoff, bot_payoff = [], [] if (self.round_number - 1) % Constants.rounds_per_bot == 0: pass else: for round in self.in_rounds(self.round_number - ((self.round_number - 1) % Constants.rounds_per_bot), self.round_number - 1): user_payoff += [float(round.payoff)] bot_payoff += [round.sender_payoff] return user_payoff, bot_payoff def live_resultcheck(self, data): if data['message'] == 'get_result': try: print('try to get result with id', self.action_id) print('bot name: ', self.bot_name) print([self.score_0, self.score_1, self.score_2, self.score_3, self.score_4, self.score_5, self.score_6], self.get_payoff_against_bot()) # if self.subsession.condition == 'verbal': action = Bot(self.round_number).act([self.score_0, self.score_1, self.score_2, self.score_3, self.score_4, self.score_5, self.score_6], self.get_payoff_against_bot()) + 1 #action = huey.result(self.action_id) + 1 # adding 1 because if 0 in line 408 return False # elif self.subsession.condition == 'numeric': # action = numeric_huey.result(self.action_id) + 1 # adding 1 becuase if 0 in line 408 return False # else: # the highest score # action = 7 except TypeError: action = None if action: print('got the result! action is: ', action) self.action = action - 1 print(f'self.action is: {self.action}') self.sender_answer_index = self.action if self.action is not None else 6 round_parameters = self.participant.vars['problem_parameters'].loc[self.round_number - 1] self.sender_answer_scores = round_parameters[f'score_{self.sender_answer_index}'] self.sender_answer_reviews =\ round_parameters[f'random_positive_negative_review_{self.sender_answer_index}'] self.sender_answer_positive_reviews = round_parameters[f'positive_review_{self.sender_answer_index}'] self.sender_answer_negative_reviews = round_parameters[f'negative_review_{self.sender_answer_index}'] return {self.id_in_group: {'message': 'calculation_done'}} def set_round_parameters(self): round_parameters = self.participant.vars['problem_parameters'].loc[self.round_number - 1] self.average_score = round_parameters['average_score'] self.is_done = False self.score_0 = round_parameters['score_0'] self.score_1 = round_parameters['score_1'] self.score_2 = round_parameters['score_2'] self.score_3 = round_parameters['score_3'] self.score_4 = round_parameters['score_4'] self.score_5 = round_parameters['score_5'] self.score_6 = round_parameters['score_6'] self.review_0 = round_parameters['review_0'] self.review_1 = round_parameters['review_1'] self.review_2 = round_parameters['review_2'] self.review_3 = round_parameters['review_3'] self.review_4 = round_parameters['review_4'] self.review_5 = round_parameters['review_5'] self.review_6 = round_parameters['review_6'] self.positive_review_0 = round_parameters['positive_review_0'] self.positive_review_1 = round_parameters['positive_review_1'] self.positive_review_2 = round_parameters['positive_review_2'] self.positive_review_3 = round_parameters['positive_review_3'] self.positive_review_4 = round_parameters['positive_review_4'] self.positive_review_5 = round_parameters['positive_review_5'] self.positive_review_6 = round_parameters['positive_review_6'] self.negative_review_0 = round_parameters['negative_review_0'] self.negative_review_1 = round_parameters['negative_review_1'] self.negative_review_2 = round_parameters['negative_review_2'] self.negative_review_3 = round_parameters['negative_review_3'] self.negative_review_4 = round_parameters['negative_review_4'] self.negative_review_5 = round_parameters['negative_review_5'] self.negative_review_6 = round_parameters['negative_review_6'] self.random_positive_negative_review_0 = round_parameters['random_positive_negative_review_0'] self.random_positive_negative_review_1 = round_parameters['random_positive_negative_review_1'] self.random_positive_negative_review_2 = round_parameters['random_positive_negative_review_2'] self.random_positive_negative_review_3 = round_parameters['random_positive_negative_review_3'] self.random_positive_negative_review_4 = round_parameters['random_positive_negative_review_4'] self.random_positive_negative_review_5 = round_parameters['random_positive_negative_review_5'] self.random_positive_negative_review_6 = round_parameters['random_positive_negative_review_6'] bot = Bot(self.round_number) self.bot_order = bot.order self.bot_name = bot.name score_columns = [f'score_{i}' for i in range(Constants.number_reviews_per_hotel)] score_list = round_parameters[score_columns] self.lottery_result = lottery(score_list) # if the lottery result is higher than the cost - add it to max_points if self.lottery_result > Constants.cost: self.participant.vars['max_points'] += (self.lottery_result - Constants.cost) def set_payoffs(self): if self.receiver_choice: # if the receiver choose A: Certainty - both get 0 self.payoff = c(0.0) self.receiver_payoff = 0.0 self.sender_payoff = 0 # if the receiver choose B: Lottery - sender get 1 point, and receiver get the result of the lottery else: self.payoff = c(round(self.lottery_result - Constants.cost, 1)) self.receiver_payoff = round(self.lottery_result - Constants.cost, 1) self.sender_payoff = Constants.sender_payoff_per_round return def bot_by_round(round): if 0 <= round <= 10: return "david" else: return "debra" class Bot(): def __init__(self, round): bots = ["David", "Debra", "Michael", "Courtney", "Ann", "Lisa"] self.order = int(round / Constants.num_bots) + 1 self.name = bots[self.order - 1] self.round_against_bot = round % int(Constants.num_rounds / Constants.num_bots) def act(self, scores, payoffs_in_previous_rounds): scores = np.array(scores) if self.name == "David": if scores.mean() >= 8: return np.argmax(scores) else: return np.argmin(scores) elif self.name == "Debra": return np.argmax(scores) elif self.name == "Michael": return np.argmin(np.abs(scores - scores.mean())) elif self.name == "Courtney": if scores.mean() >= 8: return np.argmin(scores + (scores < 8) * 1000) else: return np.argmax(scores - (scores > 8) * 1000) elif self.name == "Ann": if scores.mean() >= 8: return np.argmin(scores + (scores < 8) * 1000) else: user_payoff, bot_payoff = payoffs_in_previous_rounds if len(user_payoff) == 0: return np.argmin(scores) user_payoff = np.array(user_payoff) bot_payoff = np.array(bot_payoff) if user_payoff.sum() > bot_payoff.sum(): return np.argmax(scores) else: return np.argmin(scores) elif self.name == "Lisa": user_payoff, bot_payoff = payoffs_in_previous_rounds if len(user_payoff) == 0: return np.argmin(np.abs(scores - scores.mean())) elif bot_payoff[-1]: # is user choose hotel in the last round return np.argmin(np.abs(scores - scores.mean())) else: return np.argmax(scores) else: print("WHAT BOT IT IS?!") class Session: num_participants = 1