from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range, ) import random import csv from otree.db.models import ForeignKey author = 'Frauke Stehr' doc = """ Experiment on motivated beliefs of impact of other-regarding behavior""" class Constants(BaseConstants): name_in_url = 'experiment' players_per_group = None # Get experimental parameters from csv file with open('impact_beliefs/static/Parameters.csv', encoding='utf-8-sig') as parameters: paras = list(csv.DictReader(parameters, dialect='excel')) num_rounds = len(paras) * 2 # same projects are shown in two parts within subject endowment = 300 beliefs_fixed_payment = 150 beliefs_max_accuracy_bonus = beliefs_fixed_payment beliefs_max_payment = beliefs_fixed_payment + beliefs_max_accuracy_bonus sec_intro = 10 sec_per_matrix = 10 sec_to_answer = 15 real_world_kg_co2_per_x = 0.5 car_kg_co2_per_mile = 0.275 # source: carbonindependent.org with avg mpg=52 min_price = 12.75 max_price = 157.25 class Subsession(BaseSubsession): def creating_session(self): if self.round_number == 1: for p in self.session.get_participants(): # randomize the order of projects on participant level, but within part and store as participant varlist paras = Constants.paras.copy() random.shuffle(paras) new_paras = Constants.paras.copy() # defines a helplist of parameters, which is shuffled then appended to original paras list random.shuffle(new_paras) # shuffles helplist paras = paras + new_paras # appends shuffled helplist to list of parameters p.vars['parameters'] = paras # store shuffled list of parameters in participant vars, then access each element by project_id=round number # print(p.vars['parameters']) # prints participant vars to double check randomization # intialize several participant vars p.vars['trial_timeout_counter'] = 0 # initialize trial timeout counter p.vars['timeout_counter'] = 0 # initialize timeout counter p.vars['timeout_in_payment_decision'] = 0 # initialize timeout counter p.vars['forced_timeout'] = 0 p.vars['attention_check_failed'] = 0 # generate participant varlist for beliefs by part, to store beliefs using project_id p.vars['beliefs_part1'] = [0] * len(paras) p.vars['beliefs_part2'] = [0] * len(paras) # randomly assign treatment order to participants orders = ["NeutralMotivated", "MotivatedInfo"] p.vars['order'] = random.choice(orders) # randomly assign payment decision decisions = range(1, Constants.num_rounds * 2 + 1) # 2 decisions per round (belief & donation) p.vars['payment_decision'] = random.choice(decisions) print("Payment decision is", p.vars['payment_decision']) for p in self.get_players(): # Define "part" variable in the beginning of the experiment if self.round_number <= len(Constants.paras): p.part = 1 elif self.round_number <= Constants.num_rounds: p.part = 2 # Assign treatment to players in all rounds using treatment order assigned in first round if p.participant.vars['order'] == "NeutralMotivated": p.order = "NeutralMotivated" if p.part == 1: p.treatment = "Neutral" else: p.treatment = "Motivated" elif p.participant.vars['order'] == "MotivatedInfo": p.order = "MotivatedInfo" if p.part == 1: p.treatment = "Motivated" else: p.treatment = "Info" def vars_for_admin_report(self): participants = [p for p in self.session.get_participants()] for p in self.session.get_participants(): final_payoff = p.payoff final_payoff_plus_part_fee = p.payoff_plus_participation_fee() return { 'participants': participants, 'final_payoff': final_payoff, 'final_payoff_plus_part_fee': final_payoff_plus_part_fee, } class Group(BaseGroup): pass class Player(BasePlayer): starting_time = models.LongStringField(doc="Time at which Informed Consent is given and experiment starts") is_mobile = models.BooleanField(doc="Automatic check through JS whether gadget is phone or not") window_width = models.IntegerField(blank=True, doc="Documents the respondent's browser window's width.") window_height = models.IntegerField(blank=True, doc="Documents the respondent's browser window's height.") part = models.IntegerField() order = models.StringField() treatment = models.StringField() gif_clicked = models.BooleanField(blank=True, doc="automatically filled if people click on gif") gif_watched = models.BooleanField(blank=True, doc="check box field where people confirm they clicked on the gif") clicked_early = models.BooleanField(blank=True, doc="True if person tried to continue from instructions < 60 sec") equation_clicked = models.BooleanField(blank=True, doc="automatically filled if people click on equation for quadratic scoring rule") trial_belief_1 = models.IntegerField(blank=True, min=0, max=400) trial_belief_2 = models.IntegerField(blank=True, min=0, max=400) # https://www-jstor-org.ezproxy.ub.unimaas.nl/stable/24363518?seq=1#metadata_info_tab_contents attention_check = models.IntegerField(blank=True, min=0, max=400, doc="Asks to fill in 54 as random number as attention check.") attention_check_failed = models.BooleanField(blank=True, doc="True if subject failed to put in 23 as random answer") trial_timeout = models.IntegerField(initial=0, blank=True, doc="Counts how many time-outs occured on trial belief page") # Characteristics of Project A project_id_A = models.IntegerField() price_A = models.FloatField() num_x_true_A = models.IntegerField() num_x_belief_A = models.IntegerField(min=0, max=400, doc="records belief on number of Xs in matrix") num_x_belief_min_A = models.IntegerField(min=0, max=400, doc="records belief on number of Xs in matrix") num_x_belief_max_A = models.IntegerField(min=0, max=400, doc="records belief on number of Xs in matrix") donation_A = models.BooleanField(widget=widgets.RadioSelectHorizontal, choices=[ [True, 'Yes'], [False, 'No'], ] ) # Characteristics of Project B project_id_B = models.IntegerField() price_B = models.FloatField() num_x_true_B = models.IntegerField() num_x_belief_B = models.IntegerField(min=0, max=400, doc="records belief on number of Xs in matrix") num_x_belief_min_B = models.IntegerField(min=0, max=400, doc="records belief on number of Xs in matrix") num_x_belief_max_B = models.IntegerField(min=0, max=400, doc="records belief on number of Xs in matrix") donation_B = models.BooleanField(widget=widgets.RadioSelectHorizontal, choices=[ [True, 'Yes'], [False, 'No'], ] ) timeout = models.BooleanField(blank=True, doc="True if a time-out occured on a belief-elicitation page") forced_timeout = models.BooleanField(blank=True, doc="True if subject was forced to quit because of too many time-outs") current_payoff_belief = models.FloatField() current_payoff_donation = models.FloatField() # Comprehension Question Fields wrong_answer1 = models.IntegerField(doc="Counts the number of wrong guesses for cq1.", initial=0) wrong_answer2 = models.IntegerField(doc="Counts the number of wrong guesses for cq2.", initial=0) wrong_answer3 = models.IntegerField(doc="Counts the number of wrong guesses for cq3.", initial=0) wrong_answer4 = models.IntegerField(doc="Counts the number of wrong guesses for cq4.", initial=0) cq1 = models.BooleanField(doc="Comprehension Question 1", choices=[ [True, 'True'], [False, 'False'], ]) def cq1_error_message(self, value): if self.round_number < len(Constants.paras) + 1 and not value: self.wrong_answer1 += 1 return "Wrong answer." elif self.round_number == len(Constants.paras) + 1 and value: self.wrong_answer1 += 1 return "Wrong answer." cq2 = models.IntegerField(doc="Comprehension Question 2", min=0, max=400) def cq2_error_message(self, value): if self.round_number == 1 and value != Constants.beliefs_fixed_payment: self.wrong_answer2 += 1 return "Wrong answer." elif self.round_number == len(Constants.paras) + 1 and value != Constants.endowment: self.wrong_answer2 += 1 return "Wrong answer." cq3 = models.BooleanField(doc="Comprehension Question 3", choices=[ [True, 'True'], [False, 'False'], ]) def cq3_error_message(self, value): if value != True: self.wrong_answer3 += 1 return "Wrong answer." cq4 = models.IntegerField(doc="Comprehension Question 4", widget=widgets.RadioSelect) def cq4_choices(player): if player.round_number == 1: # How many Xs can there be in a given matrix? choices = [ [1, "at least 50 Xs"], [2, "Between 0 and 400 Xs"], # Correct Answer [3, "at most 260 Xs"], [4, "This cannot be known"], ] elif player.round_number == len(Constants.paras) + 1: # Which of the following statements about the consequences of your decision is correct? choices = [ [1, "When I decide not to donate, 50 Points are subtracted from my earnings and donated."], [2, "When I decide to donate, 50 Points are subtracted from my earnings and donated to climatecare.org."], [3, "When I decide not to donate, I earn nothing in this task."], [4, "When I decide to donate, the price of the project is subtracted from my earnings and donated to climatecare.org."], # Correct Answer ] else: choices = [] return choices def cq4_error_message(self, value): if self.round_number == 1 and value != 2: self.wrong_answer4 += 1 return "Wrong answer." elif self.round_number == len(Constants.paras) + 1 and value != 4: self.wrong_answer4 += 1 return "Wrong answer." age = models.IntegerField(label="What is your age?", choices=[ [1, "17 or younger"], [2, "18 - 19"], [3, "20 - 29"], [4, "30 - 39"], [5, "40 - 49"], [6, "50 - 59"], [7, "60 or older"], ]) gender = models.IntegerField( label="What is your gender?", choices=[ [1, "Female"], [2, "Male"], [3, "Non-binary"], [4, "Prefer Not to Specify"], ], widget=widgets.RadioSelectHorizontal, ) levelOfEducation = models.IntegerField( label="What is the highest degree or level of education you have completed?", choices=[ [0, 'Less than High School diploma'], [1, 'High School or equivalent'], [2, 'Bachelor degree (e.g. BA, BSc)'], [3, 'Master degree (e.g. MA, MSc, MEd)'], [4, 'Doctorate (e.g. PhD, EdD, DBA)'], [5, 'other'], ], ) income = models.IntegerField( label="What is your household income per year? In your answer, please consider only people with whom you share finances as part of your household. " "This would, for example, most likely exclude other members from your student dorm.", choices=[ [0, 'Less than £10,000'], [1, '£10,000 - £19,999'], [2, '£20,000 - £29,999'], [3, '£30,000 - £39,999'], [4, '£40,000 - £49,999'], [5, '£50,000 - £59,999'], [6, '£60,000 - £69,999'], [7, '£70,000 - £79,999'], [8, '£80,000 - £89,999'], [9, '£90,000 - £99,999'], [10, '£100,000 - £149,999'], [11, '£150,000 or more'], ], ) # https://medium.com/pew-research-center-decoded/small-changes-in-survey-scales-can-matter-when-measuring-political-ideology-in-europe-4a10d9a015c5 politics_right = models.IntegerField( label= "In political matters people talk of 'the left' and 'the right'. Please pick the category that you identify most with.", choices=[[1, "left"], [2, "leaning left"], [3, "centre"], [4, "leaning right"], [5, "right"] ], widget=widgets.RadioSelectHorizontal, ) altruism = models.IntegerField( choices=range(0, 11), widget=widgets.RadioSelectHorizontal, label='How do you assess your willingness to share with others without expecting anything in ' 'return when it comes to charity? Please use a scale from 0 to 10, where 0 means you ' 'are “completely unwilling to share” and a 10 means you are “very willing to share”. ' 'You can also use the values in between to indicate where you fall on the scale.') env_attitude = models.IntegerField( label="Here are two statements people sometimes make when discussing the environment" " and economic growth. Please use the slider to indicate which of them comes closer to your own point of view?", doc=" environmental attitudes Q111 from 2017 wave of world value survey. " \ "0= Economic growth and creating jobs should be the top priority, even if the" "environment suffers to some extent. 10= Protecting the environment should" "be given priority, even if it causes slower economic growth and some" "loss of jobs.") honeypot = models.IntegerField(blank=True, doc="hidden field which will only be filled by bots")