from otree.models import player from . import models from ._builtin import Page, WaitPage from .functions import slider, tdm9 from otree.api import Currency as c, currency_range from .models import Constants import logging from otreeutils.pages import ExtendedPage import json logger = logging.getLogger(__name__) import math class Consent(ExtendedPage): timeout_seconds = 300 form_model = 'player' form_fields = ['consent'] timeout_submission = {'consent': False} custom_name_in_url = 'a' def is_displayed(self): return self.round_number == 1 def vars_for_template(self): return {'consent_timeout_min': math.ceil(Constants.consent_timeout / 60)} def consent_error_message(self, value): if not value: return 'You must accept the consent form in order to proceed with the study!' self.player.consent = True def before_next_page(self): if self.timeout_happened: self.player.consent = False self.participant.vars['status'] = 'consent_dropout' self.player.timeout = 1 # participant is excluded from study if time runs out # def app_after_this_page(self, upcoming_apps): # if self.player.consent == False: # logger.info("Player did not consent, moving them on") # return upcoming_apps[-1] # else: # logger.info("Player consented and is good to go...") class InstructionsOnly(ExtendedPage): custom_name_in_url = '2' timeout_seconds = 300 # 20 min is plenty of time! it is here just to show how timer works # and it is the time I tell them they should have to complete the survey anyway def is_displayed(self): return self.subsession.round_number == 1 def before_next_page(self): if self.timeout_happened: self.player.timeout = 1 # participant is excluded from study if time runs out class TimeOutNew(ExtendedPage): # shown if timeout custom_name_in_url = 'time_out' def is_displayed(self): return self.player.timeout == 1 # the participant is kicked out of the experiment if he the timer runs out # except when answering the quiz - in that case, the code returns wrong answer # and the participant goes on to next page (doesn't use the variable "timeout") # also, in the 4 info revealing pages, the participant simply moves on to the next page, without being punished. class SliderPrimaryDiscrete(Page): custom_name_in_url = 'b' form_model = models.Player # form_fields = ['slider1', # 'slider2', # 'slider3', # 'slider4', # 'slider5', # 'slider6', # ] form_fields = ['slider_value', 'checkslider'] timeout_seconds = 300 def js_vars(self): return {"round_number": self.round_number} def before_next_page(self): logger.info(f"Before next page: {self.player.slider_value}") self.player.item_number = self.round_number self.player.participant.vars['slider' + str(self.player.round_number)] = self.player.slider_value # LIA ADDED # self.player.participant.vars['you_receive' + str(self.player.round_number)] = self.player.participant.vars['item' + str(self.player.round_number) + '_self'] # self.player.participant.vars['other_receives' + str(self.player.round_number)] = slider.allocation_other #logger.info(f"The following allocation of resources happened: (You: {self.player.you_receive}, Other: {self.player.other_receives})") prop = slider.proportional_position(self.player.slider_value, 1, 9) stuff = slider.allocation("item" + str(self.round_number), prop) self.player.my_self = stuff['self'] self.player.my_other = stuff['other'] logger.info(f"The following allocation of resources happened: (You: {self.player.my_self}, Other: {self.player.my_other})") #ideas: # self_value_id, allocation_self # other_value_id, allocation_other # console.log("-- item_id: " + item_id + " from " + allocation_bounds[item_id]); # var self_value_id = "item1_self"; # var other_value_id = "item1_other"; # chosen_values = { # 'item1': self.player.slider1, # 'item2': self.player.slider2, # 'item3': self.player.slider3, # 'item4': self.player.slider4, # 'item5': self.player.slider5, # 'item6': self.player.slider6 # } # testing # counting = self.session.vars["counting"] # count_index = 3 # counting[count_index] += 1 # logger.info(f'Updated data: {counting}') if self.timeout_happened: self.player.timeout = 1 # participant is excluded from study if time runs out if self.round_number == Constants.num_rounds: chosen_values = { 'item1': self.player.participant.vars['slider1'], 'item2': self.player.participant.vars['slider2'], 'item3': self.player.participant.vars['slider3'], 'item4': self.player.participant.vars['slider4'], 'item5': self.player.participant.vars['slider5'], 'item6': self.player.participant.vars['slider6'] } mean_allocations = slider.mean_allocations_discrete(chosen_values) svo_slider_angle = slider.svo_angle(mean_allocations['self'], mean_allocations['other']) self.player.slider_angle = svo_slider_angle self.player.slider_classification = slider.svo_classification(svo_slider_angle) # self.player.send_to_next_app() logger.info(f"This player is SVO type: {self.player.slider_classification}") if self.timeout_happened: self.player.timeout = 1 # participant is excluded from study if time runs out ## my simplied categories: # Pro-Social = altruists + prosocials; # Individualistic = individualistic + competitive # Murphy et al, 2011: # • Altruism: SVO◦ > 57.15◦ # • Prosociality: 22.45◦ < SVO◦ < 57.15◦ # • Individualism: –12.04◦ < SVO◦ < 22.45◦ # • Competitiveness: SVO◦ < –12.04◦ if self.player.slider_angle > 22.45: self.player.my_choice = "Pro-Social" else: self.player.my_choice = "Individualistic" # Store the data so we can access it in app2 self.player.participant.vars['choice'] = self.player.my_choice logger.info( f"This player is the following SVO type in the binary categorization: {self.player.participant.vars['choice']}") # Seed the session with the first players index of 0 if 'player_type' not in self.session.vars: self.player.participant.vars['player_type'] = self.player.my_choice player_type = self.player.participant.vars['player_type'] # Open the JSON file in read/write mode with open('svotree/treatment_index_blue.json', 'r+') as reader: data = json.loads(reader.read()) # Get the index values for this player type indexes = data[player_type] # Read the last treatment treatment = indexes['treatment'] # And the last start index for that specific treatment # index = indexes[treatment] index = 0 self.player.participant.vars['paradox_index'] = index self.player.participant.vars['treatment'] = treatment self.player.my_treatment = treatment # ***************** main experiment ****************** # Now increment the values and write them back to the file - conditions for NEXT participant treatment_index = Constants.treatments.index(treatment) + 1 if treatment_index == len(Constants.treatments): treatment_index = 0 # ****************** PILOT 2 ******************** # comment out lines above & uncomment line below # FOR PILOT 2, only treatment d or VeryLikely_Big: # treatment_index = Constants.treatments.index(treatment) # treatment_index = 3 # logger.info(f'My treatment is: {self.player.my_treatment}') # ***************** main experiment ************** # after sub-sample for a certain SVO type * treatment is full, skip that combination # you must monitor the number of subjects per cell # and then freeze the recruitment as soon as a category is full # ** skip treatment "a" in SVO type Pro-Social: # if treatment_index == 0 and player_type == "Pro-Social": # treatment_index = 1 # ** skip treatment "b" in SVO type Pro-Social: # if treatment_index == 1 or treatment_index == 2 and player_type == "Pro-Social": # treatment_index = 2 # ** skip treatment "c" in SVO type Pro-Social: # if treatment_index == 2 and player_type == "Pro-Social": # treatment_index = 3 # ** skip treatment "d" in SVO type Pro-Social: # if treatment_index == 3 and player_type == "Pro-Social": # treatment_index = 0 # if treatment_index in [0, 3] and player_type == "Pro-Social": # treatment_index = 1 # if treatment_index in [0, 3] and player_type == "Individualistic": # treatment_index = 1 data[player_type]['treatment'] = Constants.treatments[treatment_index] index += 1 data[player_type][treatment] = index #data[player_type]["tot_" + treatment] += 1 ### UNCOMMENT AS SOON AS YOU'RE DONE TESTING counting = self.session.vars["counting"] count_index = Constants.treatments.index(treatment) if player_type == "Individualistic": count_index += 4 counting[count_index] += 1 logger.info(f'Updated data: {counting}') self.player.number_in_cell = counting[count_index] ### # # Now increment the values and write them back to the file - conditions for NEXT participant # self.player.participant.vars['number_in_cell'] = index_count # self.player.number_in_cell = index_count reader.seek(0) # Rewind cursor back to the start of the file reader.write(json.dumps(data, indent=2)) # # Open the JSON file in read/write mode # with open('svotree/participants_in_cells.json', 'r+') as reader: # data_count = json.loads(reader.read()) # logger.info( # f'opened json file to count number of participants in each cell and this is the data: {data_count}') # # # Get the index values for this player type # indexes_count = data_count[player_type] # # Read the last treatment # # treatment = data['treatment'] # # index = 0 # # treatment = self.player.participant.vars['treatment'] # index_count = indexes_count[treatment] # self.player.participant.vars['count_index'] = index_count # index_count += 1 # # # Now increment the values and write them back to the file - conditions for NEXT participant # self.player.participant.vars['number_in_cell'] = index_count # self.player.number_in_cell = index_count # # data_count[player_type][treatment] = index_count # # logger.info(f'Number of participants in this cell is now: {index_count}') # logger.info(f'updated data for counting participants: {data_count}') # reader.seek(0) # Rewind cursor back to the start of the file # reader.write(json.dumps(data, indent=2)) # def checkslider_error_message(self, value): # if not value: # return 'Please make your decision using the slider.' # def error_message(self, value): # if value["checkslider"] == None: # logger.info(f"Value of checkslider {self.player.checkslider}") # return 'Please use the slider to make a decision.' class SliderPrimaryContinuous(Page): form_model = models.Player form_fields = ['slider1', 'slider2', 'slider3', 'slider4', 'slider5', 'slider6', ] timeout_seconds = 300 def vars_for_template(self): return {'slider_items': [1, 2, 3, 4, 5, 6]} def before_next_page(self): chosen_values = { 'item1': self.player.slider1, 'item2': self.player.slider2, 'item3': self.player.slider3, 'item4': self.player.slider4, 'item5': self.player.slider5, 'item6': self.player.slider6 } mean_allocations = slider.mean_allocations_continuous(chosen_values) svo_slider_angle = slider.svo_angle(mean_allocations['self'], mean_allocations['other']) self.player.slider_angle = svo_slider_angle self.player.slider_classification = slider.svo_classification(svo_slider_angle) logger.info(f"This player is SVO type: {self.player.slider_classification}") if self.timeout_happened: self.player.timeout = 1 # participant is excluded from study if time runs out ## my simplied categories: # Pro-Social = altruists + prosocials; # Individualistic = individualistic + competitive # Murphy et al, 2011: # • Altruism: SVO◦ > 57.15◦ # • Prosociality: 22.45◦ < SVO◦ < 57.15◦ # • Individualism: –12.04◦ < SVO◦ < 22.45◦ # • Competitiveness: SVO◦ < –12.04◦ if self.player.slider_angle > 22.45: self.player.my_choice = "Pro-Social" else: self.player.my_choice = "Individualistic" # Store the data so we can access it in app2 self.player.participant.vars['choice'] = self.player.my_choice logger.info( f"This player is the following SVO type in the binary categorization: {self.player.participant.vars['choice']}") # Seed the session with the first players index of 0 if 'player_type' not in self.session.vars: self.player.participant.vars['player_type'] = self.player.my_choice player_type = self.player.participant.vars['player_type'] # Open the JSON file in read/write mode with open('app1/treatment_index.json', 'r+') as reader: data = json.loads(reader.read()) logger.info(f'Original data: {data}') # Get the index values for this player type indexes = data[player_type] # Read the last treatment treatment = indexes['treatment'] # And the last start index for that specific treatment # index = indexes[treatment] index = 0 self.player.participant.vars['paradox_index'] = index self.player.participant.vars['treatment'] = treatment # Now increment the values and write them back to the file treatment_index = Constants.treatments.index(treatment) + 1 if treatment_index == len(Constants.treatments): treatment_index = 0 data[player_type]['treatment'] = Constants.treatments[treatment_index] index += 1 # if index == 4 and self.participant.vars['treatment'] in ['a', 'b']: # index = 0 # elif index == 8 and self.participant.vars['treatment'] in ['c','d']: # index = 0 data[player_type][treatment] = index logger.info(f'Updated data: {data}') reader.seek(0) # Rewind cursor back to the start of the file reader.write(json.dumps(data, indent=2)) class NineItemTDM(Page): form_model = models.Player form_fields = [ "nine_item_tdm_1", "nine_item_tdm_2", "nine_item_tdm_3", "nine_item_tdm_4", "nine_item_tdm_5", "nine_item_tdm_6", "nine_item_tdm_7", "nine_item_tdm_8", "nine_item_tdm_9", ] def vars_for_template(self): return {'nine_item_tdm_decisions': tdm9.decisions} def before_next_page(self): chosen_values = { 1: self.player.nine_item_tdm_1, 2: self.player.nine_item_tdm_2, 3: self.player.nine_item_tdm_3, 4: self.player.nine_item_tdm_4, 5: self.player.nine_item_tdm_5, 6: self.player.nine_item_tdm_6, 7: self.player.nine_item_tdm_7, 8: self.player.nine_item_tdm_8, 9: self.player.nine_item_tdm_9 } self.player.nine_item_tdm_prosocial = tdm9.count_decisions_of_type("Prosocial", chosen_values) self.player.nine_item_tdm_individualistic = tdm9.count_decisions_of_type("Individualistic", chosen_values) self.player.nine_item_tdm_competitive = tdm9.count_decisions_of_type("Competitive", chosen_values) self.player.nine_item_tdm_classification = tdm9.svo_type(chosen_values) logger.info(f"This player is SVO type: {self.player.nine_item_tdm_classification}") class DebugDisplayPage(Page): def vars_for_template(self): return {'angle': self.player.slider_angle, 'classification_slider': self.player.slider_classification, 'tdm_prosocial': self.player.nine_item_tdm_prosocial, 'tdm_individualistic': self.player.nine_item_tdm_individualistic, 'tdm_competitive': self.player.nine_item_tdm_competitive, 'classification_tdm': self.player.nine_item_tdm_classification} page_sequence = [ Consent, TimeOutNew, InstructionsOnly, TimeOutNew, SliderPrimaryDiscrete, TimeOutNew, # NineItemTDM, ]