from otree.api import * import random #import streamlit import numpy import locale locale.setlocale(locale.LC_ALL, 'en_US.UTF-8') doc = """ Your app description """ class C(BaseConstants): NAME_IN_URL = 'vehicle_portion' PLAYERS_PER_GROUP = None NUM_ROUNDS = 1 GENERAL_BENEFIT = cu(100) # """Cost incurred by volunteering player""" VOLUNTEER_COST = cu(40) num_pages = 7 class Group(BaseGroup): pass class Subsession(BaseSubsession): pass #Currently this will show up as every question on a single page. Might want to define a new page for each question, so they have # to click next to get to each question. Something to think about class Player(BasePlayer): current_page = models.IntegerField(initial=0) #Assign experimental groups assigned_group = models.StringField( choices= [ 'control', 'treatment1', 'treatment2', ] ) vehicle_choice = models.IntegerField( label = "Which vehicle will you decide to purchase", choices=[ [1, 'Vehicle A'], [2, 'Vehicle B'] ], widget=widgets.RadioSelect ) #Code race, and the other questions from the Google doc attention_check1 = models.StringField( label = ''' On the previous page you were provided two vehicle options to choose from. Which of the following was one of the attributes presented by the sales representative?''', choices=[ [1,'Price'], [2,'Fuel Cost'], [3,'Seating Capacity'], [4,'Range'] ], widget=widgets.RadioSelect, ) attention_check2 = models.StringField( label = ''' On the previous page you were provided two vehicle options to choose from. What did the bold text at the end of the description say?''', choices=[ [1,'Keep in mind that electric vehicles are often chosen by people with liberal views'], [2,'Keep in mind that electric vehicles are now the non-tactical vehicle of the US Army'], [3,'Keep in mind that electric vehicles are often chosen by people with conservative views'], [4,'There was no bold text in the description'] ], widget=widgets.RadioSelect ) attention_check3 = models.StringField( label = ''' On the previous page you were provided two vehicle options to choose from. Was there any bold text in the description?''', choices=[ ['Yes','Yes'], ['No','No'] ], widget=widgets.RadioSelect ) #budget = models.CurrencyField(initial=55000) # define all the volunteer dilemma related stuff volunteer = models.BooleanField( label='Do you wish to volunteer?', doc="""Whether player volunteers""" ) computer_volunteer = models.IntegerField() price_vehicle_a = models.StringField() price_vehicle_b = models.StringField() fuel_vehicle_a = models.StringField() fuel_vehicle_b = models.StringField() range_vehicle_a = models.StringField() range_vehicle_b = models.StringField() ####################################### Baseline Questions ###################################### purchase_durable_goods = models.StringField( choices=[['Yes', 'Yes'], ['No', 'No']], label='Are you one of the members in your household who participates in the decision to purchase durable goods (such as motor vehicles, appliances, computers, etc.)', widget=widgets.RadioSelect) motor_vehicles_num = models.IntegerField(label='How many motor vehicles does your household currently own or lease?') motor_vehicles_type = models.StringField( blank=True, choices=[['Motorcyle', 'Motorcyle'], ['Car', 'Car'], ['Truck', 'Truck'], ['SUV', 'SUV']], label='Do you support the following policies? Check all that apply:', widget=widgets.RadioSelect) transportation_home_work = models.StringField( choices=[['Motorcycle', 'Motorcycle'], ['Car', 'Car'], ['Truck', 'Truck'], ['SUV', 'SUV'], ['Bicycle/Foot', 'Bicycle/Foot'], ['Public Transit', 'Public Transit']], label='What is your main mode of transportation for traveling from home to work?', widget=widgets.RadioSelect) transportation_home_leisure = models.StringField( choices=[['Motorcycle', 'Motorcycle'], ['Car', 'Car'], ['Truck', 'Truck'], ['SUV', 'SUV'], ['Bicycle/Foot', 'Bicycle/Foot'], ['Public Transit', 'Public Transit']], label='What is your main mode of transportation for traveling from home to leisure?', widget=widgets.RadioSelect) miles_traveled = models.IntegerField(label='Approximately how many miles do you travel on a daily basis from home to work? Note that zero can be an appropriate response.') ev_own = models.StringField( choices=[['Yes', 'Yes'], ['No', 'No']], label='Do you currently own an electric vehicle?', widget=widgets.RadioSelect) ev_driven = models.StringField( choices=[['Yes', 'Yes'], ['No', 'No']], label='Have you ever driven in an electric vehicle (either as a passenger or operator)?', widget=widgets.RadioSelect) ######################################### Socioeconomic/demo questions ###################################### age = models.IntegerField(label='What is your age?', min=18, max=125) gender = models.StringField( label='What is your gender?', choices=[ ['Male', 'Male'], ['Female', 'Female'], ['Non-Binary','Non-Binary'] ], widget=widgets.RadioSelect, ) income = models.IntegerField(label='What is your approximate income in dollars?', min=0, max=10000000) state = models.StringField(choices=[['AL', 'AL'], ['AK', 'AK'], ['AZ', 'AZ'], ['AR', 'AR'], ['CA', 'CA'], ['CO', 'CO'], ['CT', 'CT'], ['DE', 'DE'], ['DC', 'DC'], ['FL', 'FL'], ['GA', 'GA'], ['HI', 'HI'], ['ID', 'ID'], ['IL', 'IL'], ['IN', 'IN'], ['IA', 'IA'], ['KS', 'KS'], ['KY', 'KY'], ['LA', 'LA'], ['ME', 'ME'], ['MD', 'MD'], ['MA', 'MA'], ['MI', 'MI'], ['MN', 'MN'], ['MS', 'MS'], ['MO', 'MO'], ['MT', 'MT'], ['NE', 'NE'], ['NV', 'NV'], ['NH', 'NH'], ['NJ', 'NJ'], ['NM', 'NM'], ['NY', 'NY'], ['NC', 'NC'], ['ND', 'ND'], ['OH', 'OH'], ['OK', 'OK'], ['OR', 'OR'], ['PA', 'PA'], ['RI', 'RI'], ['SC', 'SC'], ['SD', 'SD'], ['TN', 'TN'], ['TX', 'TX'], ['UT', 'UT'], ['VT', 'VT'], ['VA', 'VA'], ['WA', 'WA'], ['WV', 'WV'], ['WI', 'WI'], ['WY', 'WY']], label='Which state do you live in?', widgets=widgets.RadioSelectHorizontal) urbanization = models.StringField(choices=[['Urban', 'Urban'], ['Suburban', 'Suburban'], ['Rural', 'Rural']], label='Which level of urbanization best describes where you live?', widget=widgets.RadioSelect) educ = models.StringField(choices=[['High School - no diploma', 'High School - no diploma'], ['High School - diploma', 'High School - diploma'], ['Some college', 'Some college'], ["Bachelor's", "Bachelor's"], ["Master's or above", "Master's or above"]], label='What is the highest level of education you have attained?', widget=widgets.RadioSelect) job_status = models.StringField(choices=[['Employed full-time', 'Employed full-time'], ['Employed part-time', 'Employed part-time'], ['Unemployed', 'Unemployed'], ['Student', 'Student'], ['Self-employed', 'Self-employed'], ['Retired', 'Retired'], ['Full time housewife/househusband', 'Full time housewife/househusband']], label='What is your occupational status?', widget=widgets.RadioSelect) marital_status = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='Are you married?', widget=widgets.RadioSelect) kids = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='Do you have children?', widget=widgets.RadioSelect) household = models.IntegerField(label='What is the size of your household?', min=1, max=100) race = models.StringField(choices=[['African American or Black', 'African American or Black'], ['White', 'White'], ['American Indian or Alaska Native', 'American Indian or Alaska Native'], ['Asian', 'Asian'], ['Native Hawaiian or Other Pacific', 'Native Hawaiian or Other Pacific'], ['Other','Other']], label='What is your race?', widget=widgets.RadioSelect) ethnicity = models.StringField(choices=[['Hispanic or Latino', 'Hispanic or Latino'], ['Not Hispanic or Latino', 'Not Hispanic or Latino']], label='What is your ethnicity?', widget=widgets.RadioSelect) veteran = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='Are you a US Veteran?', widget=widgets.RadioSelect) insurance = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='Do you have health insurance?', widget=widgets.RadioSelect) medicaid = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='Are you enrolled in Medicaid?', widget=widgets.RadioSelect) charity = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='Have you ever donated money or volunteered for a charitable cause?', widget=widgets.RadioSelect) ################################# Political Ideology ###################################### political_affiliation = models.StringField( label = "What best describes your political affiliation", choices = [ ['Democrat','Democrat'], ['Republican','Republican'], ['Independent','Independent'], ['Other','Other'] ], widget=widgets.RadioSelect, ) political_ideology_econ = models.StringField( label = "What best describes your political stance on economic issues", choices = [ [1,'Strongly Conservative'], [2,'Conservative'], [3,'Moderate'], [4,'Liberal'], [5,'Strongly Liberal'] ], widget=widgets.RadioSelect, ) political_ideology_social = models.StringField( label = "What best describes your political stance on social issues", choices = [ [1,'Strongly Conservative'], [2,'Conservative'], [3,'Moderate'], [4,'Liberal'], [5,'Strongly Liberal'] ], widget=widgets.RadioSelect, ) #do some historical voting, rather than hypothetical biden_v_trump = models.IntegerField(label='Who did you vote for in the 2020 Presidential election', choices = [ [1,'Joe Biden'], [2,'Donald Trump'], [3,'Other'], [4,'Did not vote'] ], widget=widgets.RadioSelect) clinton_v_trump = models.IntegerField(label='Who did you vote for in the 2016 Presidential election', choices = [ [1,'Hillary Clinton'], [2,'Donald Trump'], [3,'Other'], [4,'Did not vote'] ], widget=widgets.RadioSelect) obama_v_romney = models.IntegerField(label='Who did you vote for in the 2012 Presidential election', choices = [ [1,'Barack Obama'], [2,'Mitt Romney'], [3,'Other'], [4,'Did not vote'] ], widget=widgets.RadioSelect) #clinton = models.IntegerField(label='Hilary Clinton', min=17, max=125) #### Political Ideology : Check all that apply questions policy_support_capital_punishment = models.BooleanField( label="Capital Punishment", blank=True ) policy_support_abortion = models.BooleanField( label="Access to Abortion", blank=True ) policy_support_gun_control = models.BooleanField( label="Gun Control", blank=True ) policy_support_social_health = models.BooleanField( label="Socialized Health Care", blank=True ) policy_support_samesex_marriage = models.BooleanField( label="Same-sex Marriage", blank=True ) policy_support_immigration = models.BooleanField( label="Deportation of Illegal Immigrants", blank=True ) policy_support_asylum = models.BooleanField( label="Allowing Refugees to Claim Asylum", blank=True ) policy_support_freetrade = models.BooleanField( label="Free Trade", blank=True ) policy_support_landconservation = models.BooleanField( label="Land Conservation", blank=True ) policy_support_nationalparks = models.BooleanField( label="National Parks", blank=True ) #policy_support_gun_publichunting = models.BooleanField( # label="Public Hunting Land", # blank=True #) policy_support_energysecurity = models.BooleanField( label="Energy Security", blank=True ) #OEERE says that maintenance costs ofr EVs are $0.06/mile, # ICEs are $0.10/mile: # ICEs are 1.667 times more expensive for maintenace costs on average # Thus, I can also encorporate these into the choice set: add fuel and maintence over 5 years # This is why it's easier to take the mean prices, a set mileage, to estimate the RVs. # Stick with estimates from OEERE for costs per mile for fuel and costs per mile for maintenace, and then # generate a std_dev based on those means def creating_session(subsession): players = subsession.get_players() treatment_probabilities = [1/3,1/3,1/3] treatments = ['control', 'treatment1', 'treatment2'] for player in players: # Randomly assign the player to a group based on the probability distribution player.assigned_group = random.choices(treatments, treatment_probabilities)[0] #specify the joint normal distribution for the vehicle attributes means_EV = numpy.array([53469,485,234]) #sticker price, fuel cost, range means # std_dev should be about x.bar - lb / 1.96, to capture 95% of the distr std_dev_EV = numpy.array([6000,75,72]) #std_dev_EV = numpy.array([7000,75,72]) #std_dev_EV = numpy.array([7000,75,87]) means_ICE = numpy.array([48334,1117,403]) #sticker price, fuel cost, range means std_dev_ICE = numpy.array([6000,154,53]) #std_dev_ICE = numpy.array([8000,154,53]) #If i specify the correlation between two RVs, then I can get the covariance if i have the variances # cov(x,y) = rho(x,y)*std_x*std_y #I should set the price per mile travelled, and then assume a total number of miles and then generate the yearly cost. # This is because the yearly fuel cost is correlated with the range, but only in that the range might induce more driving # Thus, I want to decrease the correlation a bit cov_matrix_EV = numpy.array([[std_dev_EV[0]**2, .2*std_dev_EV[0]*std_dev_EV[1], .7*std_dev_EV[0]*std_dev_EV[2]],[.2*std_dev_EV[0]*std_dev_EV[1], std_dev_EV[1]**2, .2*std_dev_EV[1]*std_dev_EV[2]],[.7*std_dev_EV[0]*std_dev_EV[2], .2*std_dev_EV[1]*std_dev_EV[2], std_dev_EV[2]**2]]) #numpy.transpose(numpy.array([[8000**2, .3, .7],[.3, 485**2, .5],[.7,.5,234**2]])) cov_matrix_ICE = numpy.array([[std_dev_ICE[0]**2, .2*std_dev_ICE[0]*std_dev_ICE[1], .7*std_dev_ICE[0]*std_dev_ICE[2]],[.2*std_dev_ICE[0]*std_dev_ICE[1], std_dev_ICE[1]**2, .2*std_dev_ICE[1]*std_dev_ICE[2]],[.7*std_dev_ICE[0]*std_dev_ICE[2], .2*std_dev_ICE[1]*std_dev_ICE[2], std_dev_ICE[2]**2]]) #numpy.transpose(cov_matrix_ICE)==cov_matrix_ICE #numpy.linalg.eigvals(cov_matrix_ICE) vehicle_EV_draw = numpy.random.multivariate_normal(means_EV, cov_matrix_EV) vehicle_ICE_draw = numpy.random.multivariate_normal(means_ICE, cov_matrix_ICE) #numpy.quantile(vehicle_EV_draw[1:2500,0]-vehicle_ICE_draw[1:2500,0], q= .282) #28.2% of the EVs are less expensive than the ICEs. Implies that 28% might choose an EV #numpy.quantile(vehicle_EV_draw[0]-vehicle_ICE_draw[0], q= .25) #vehicle_ICE_draw = numpy.random.Generator.multivariate_normal(means_ICE, cov_matrix_ICE) #numpy.linalg.eigvals(cov_matrix_ICE) #numpy.linalg.eigvals(cov_matrix_EV) #price_vehicle_a = random.randint(35,50) * 1000 #price_vehicle_b = random.randint(35,50) * 1000 #savings_vehicle_a = 55000 - price_vehicle_a #savings_vehicle_b = 55000 - price_vehicle_b player.price_vehicle_a = locale.currency(vehicle_EV_draw[0], grouping=True) player.price_vehicle_b = locale.currency(vehicle_ICE_draw[0], grouping=True) player.fuel_vehicle_a = locale.currency(vehicle_EV_draw[1]*5, grouping=True) player.fuel_vehicle_b = locale.currency(vehicle_ICE_draw[1]*5, grouping=True) player.range_vehicle_a = numpy.round(vehicle_EV_draw[2],0) player.range_vehicle_b = numpy.round(vehicle_ICE_draw[2],0) # FUNCTIONS # PAGES class VehicleChoicePage(Page): form_model = "player" form_fields = ['vehicle_choice'] #def before_next_page(player): # def vars_for_template(player: Player): player.current_page = player.current_page + 1 description = '' if player.assigned_group == 'treatment1': description = '''Imagine this scenario: You're shopping for a new everyday vehicle and have decided which make and model you would like to purchase. Before completing the purchase, the salesperson presents the two following options for your choice, one with a gasoline engine and another with an electric engine. Please note the different attributes that are available for each option: sale price, fuel costs over 5 years, and the range in miles for a full charge or full tank of gasoline. Now, choose your final vehicle from the options below. Keep in mind that electric vehicles are often chosen by people with liberal views.''' #group_vehicle_a = 'Liberal Vehicle' if player.assigned_group == 'treatment2': description = '''Imagine this scenario: You're shopping for a new everyday vehicle and have decided which make and model you would like to purchase. Before completing the purchase, the salesperson presents the two following options for your choice, one with a gasoline engine and another with an electric engine. Please note the different attributes that are available for each option: sale price, fuel costs over 5 years, and the range in miles for a full charge or full tank of gasoline. Now, choose your final vehicle from the options below. Keep in mind that lately, electric vehicles have been chosen by people with conservative views.''' #group_vehicle_a = 'Conservative Vehicle' elif player.assigned_group == 'control': description = '''Imagine this scenario: You're shopping for a new everyday vehicle and have decided which make and model you would like to purchase. Before completing the purchase, the salesperson presents the two following options for your choice, one with a gasoline engine and another with an electric engine. Please note the different attributes that are available for each option: sale price, fuel costs over 5 years, and the range in miles for a full charge or full tank of gasoline. Now, choose your final vehicle from the options below.''' #group_vehicle_a = '' return{ 'vehicle_a': { 'label': 'Vehicle A', 'price': player.price_vehicle_a, 'fuelcost': player.fuel_vehicle_a, 'range': player.range_vehicle_a, 'fuel type': 'Electricity' }, 'vehicle_b': { 'label': 'Vehicle B', 'price': player.price_vehicle_b, 'fuelcost': player.fuel_vehicle_b, 'range': player.range_vehicle_b, 'fuel type': 'Gasolilne' }, 'description': description, 'current_page': player.current_page } class AttentionChecks(Page): form_model = 'player' form_fields = ['attention_check1', 'attention_check3'] #def before_next_page(player): #player.current_page += 1 def vars_for_template(player): player.current_page = player.current_page + 1 return{ 'current_page': player.current_page } #perhaps I want to ask the attention checks with respect to the attributes that were presented, as well as for a question that says what did the bold text # say on the previous question? With an option, there was no bold text. #form_fields = ['purchase_durable_goods', 'motor_vehicles_num', 'motor_vehicles_type', 'transportation_home_work', 'transportation_home_leisure', 'miles_traveled', 'ev_own', 'ev_driven'] #form_fields = ['age', 'gender', 'state', 'urbanization', 'edu', 'job_status', 'marital_status', 'kids', 'household', 'income', 'race', 'ethnicity', 'veteran', 'insurance', 'medicaid', 'charity'] baseline_form = ['purchase_durable_goods', 'motor_vehicles_num', 'motor_vehicles_type', 'transportation_home_work', 'transportation_home_leisure', 'miles_traveled', 'ev_own', 'ev_driven'] demographics_form = ['age', 'gender', 'state', 'urbanization', 'political_affiliation','educ', 'job_status', 'marital_status', 'kids', 'household', 'income', 'race', 'ethnicity', 'veteran', 'insurance', 'medicaid', 'charity'] political_form = ['political_ideology_econ', 'political_ideology_social', 'biden_v_trump', 'clinton_v_trump', 'obama_v_romney'] class Baseline(Page): form_model = 'player' form_fields = baseline_form #def before_next_page(player): # player.current_page += 1 def vars_for_template(player: Player): player.current_page = player.current_page + 1 description = 'Please complete the following questions. These questions and your responses are completely anonymous and confidential.' return{ 'description': description, 'current_page': player.current_page } class Demographics(Page): form_model = 'player' form_fields = demographics_form #def before_next_page(player): # player.current_page += 1 def vars_for_template(player: Player): player.current_page = player.current_page + 1 description = 'Please complete the following questions. These questions and your responses are completely anonymous and confidential.' return{ 'description': description, 'current_page': player.current_page } class Political(Page): form_model = 'player' form_fields = political_form #def before_next_page(player): # player.current_page += 1 def vars_for_template(player: Player): player.current_page = player.current_page + 1 description = 'Please complete the following questions. These questions and your responses are completely anonymous and confidential.' return{ 'description': description, 'current_page': player.current_page } class Introduction(Page): pass #this gives the instructions page class Volunteer(Page): form_model = 'player' form_fields = ['volunteer'] #timeout_seconds = 60 #def before_next_page(self): def vars_for_template(player: Player): player.current_page = player.current_page + 1 return{ 'current_page': player.current_page } class Results(Page): #def before_next_page(player): # player.current_page += 1 def vars_for_template(player: Player): player.current_page = player.current_page + 1 computer_volunteer = random.choice([True, False]) num_volunteers = sum([player.volunteer, computer_volunteer]) if num_volunteers > 0: baseline_amount = C.GENERAL_BENEFIT else: baseline_amount = cu(0) if player.volunteer: payoff = baseline_amount - C.VOLUNTEER_COST else: payoff = baseline_amount return {'player': { 'volunteer': player.volunteer, 'volunteers': num_volunteers, 'payoff': payoff }, 'current_page': player.current_page } #Contribution, ResultsWaitPage, Results, #Introduction page_sequence = [Demographics, Volunteer, Results, VehicleChoicePage, AttentionChecks, Political, Baseline] #page_sequence = [Political, Baseline]