from otree.api import * from settings import SESSION_CONFIG_DEFAULTS author = 'Nathaniel Lawrence, LEMMA, Université Panthéon-Assas' doc = """ This app provides a consumption simultion app, combined with surveys on demographics; financial knowledge, behavior, and attitude; and numeracy; as well as tests of time and risk preferences, loss aversion, impulsivity, and learning rates. Sliders provided courtesy of Max R. P. Grossmann, https://gitlab.com/gr0ssmann/otree_slider and Van Pelt, V. F. J. (2020, February 19). Sliders with feedback and without anchoring. Accounting Experiments, Available at: https://www.accountingexperiments.com/post/sliders/. """ ### Define constants here, in all-caps class C(BaseConstants): NAME_IN_URL = 'pda' PLAYERS_PER_GROUP = None NUM_ROUNDS = 1 NUM_QUESTIONS = 33 TIME_LIMIT = SESSION_CONFIG_DEFAULTS['time_limit'] SLIDER_TEST = -73 QUESTION_PRICE = 1000 TABLE_1 = __name__ + '/table_1.html' TABLE_2 = __name__ + '/table_2.html' CONVERSION_FACTOR = SESSION_CONFIG_DEFAULTS['conversion_factor'] CORRECT_ANSWER_FEE = cu(SESSION_CONFIG_DEFAULTS['correct_answer_fee'] * CONVERSION_FACTOR) PERCENT_ESTIMATION_RANGE = SESSION_CONFIG_DEFAULTS['percent_estimation_range'] PERCENT_ESTIMATION_RANGE_CORRECT = 50 PERCENT_ESTIMATION_RANGE_LOW = PERCENT_ESTIMATION_RANGE_CORRECT - PERCENT_ESTIMATION_RANGE PERCENT_ESTIMATION_RANGE_HIGH = PERCENT_ESTIMATION_RANGE_CORRECT + PERCENT_ESTIMATION_RANGE class Subsession(BaseSubsession): pass class Group(BaseGroup): pass ### define the questions a player must answer here class Player(BasePlayer): ## Session indices day = models.IntegerField() app_sequence = models.IntegerField() inf_sequence = models.IntegerField() intervention = models.IntegerField() ### choices = [[value,label],[value,label]] ### Instructions - Test understanding of slider slider = models.FloatField() ### Level of knowledge of previous inflation rates (Macchia et al., 2018) infK_1 = models.FloatField() infK_2 = models.FloatField() infK_3 = models.FloatField() infK_4 = models.FloatField() infK_5 = models.FloatField() infK_6 = models.FloatField() ### Level of knowledge of calculation of impact of compound interest (Macchia et al., 2018) infCI_1 = models.FloatField( label = 'If inflation is 10% a year, and a product currently costs {}, how much will it cost in one year’s time?'.format(cu(C.QUESTION_PRICE)), min = 0, max = 9999 ) infCI_2 = models.FloatField( label = 'If inflation is 50% a year, and a product currently costs {}, how much will it cost in two year’s time?'.format(cu(C.QUESTION_PRICE)), widget = widgets.RadioSelect ) infCI_3 = models.FloatField( label = 'If inflation is 3% a year, and a product currently costs {}, how much will it cost in five year’s time?'.format(cu(C.QUESTION_PRICE)), widget = widgets.RadioSelect ) infCI_4 = models.FloatField( label = 'If inflation is 100% a year, and a product currently costs {}, how much will it cost in five year’s time?'.format(cu(C.QUESTION_PRICE)), min = 0, max = 999999 ) ### Inflation Awareness inf_4 = models.IntegerField( choices = [[1,'Augmenté'],[-1,'Baissé'],[0,'Resté constant'],[5,'Pas concerné'],[9,'Je ne sais pas']], label = 'Votre pouvoir d’achat a-t-il augmenté, baissé, resté constant pendant les 12 derniers mois ?', widget = widgets.RadioSelect ) inf_5 = models.IntegerField( choices = [[1,'Augmenté'],[-1,'Baissé'],[0,'Resté constant'],[5,'Pas concerné'],[9,'Je ne sais pas']], label = 'Votre salarie a-t-il été augmenté, baissé, resté constant pendant les 12 derniers mois ?', widget = widgets.RadioSelect ) inf_6 = models.FloatField( # label = 'Si votre salaire a été augmenté ou baissé cette année, de combien (%) ?' ) inf_7 = models.IntegerField( choices = [[1,'Augmenté'],[-1,'Baissé'],[0,'Resté constant'],[5,'Pas concerné'],[9,'Je ne sais pas']], label = 'Attendez-vous que votre salaire soit augmenté, baissé, resté constant dans les 12 prochains mois ?', widget = widgets.RadioSelect ) inf_8 = models.FloatField( # label = 'Si vous attendez une augmentation ou une baisse cette année, de combien (%) ?' ) inf_9_cheaper = models.IntegerField( choices = [[1,'Increase'],[0,'No change'],[-1,'Decrease']], label = 'Substitution of current products you buy with cheaper versions', widget = widgets.RadioSelect ) inf_9_quantity = models.IntegerField( choices = [[1,'Increase'],[0,'No change'],[-1,'Decrease']], label = 'The quantity of current products you buy', widget = widgets.RadioSelect ) inf_9_stockMaintained = models.IntegerField( choices = [[1,'Increase'],[0,'No change'],[-1,'Decrease']], label = 'The size of the stock you maintain of products whose price changes', widget = widgets.RadioSelect ) inf_9_leisure = models.IntegerField( choices = [[1,'Increase'],[0,'No change'],[-1,'Decrease']], label = 'Spending on leisure activities', widget = widgets.RadioSelect ) inf_9_subscription = models.IntegerField( choices = [[1,'Increase'],[0,'No change'],[-1,'Decrease']], label = 'Cancel subscriptions', widget = widgets.RadioSelect ) inf_9_insurance = models.IntegerField( choices = [[1,'Increase'],[0,'No change'],[-1,'Decrease']], label = 'Investment in life insurance', widget = widgets.RadioSelect ) inf_9_realEstate = models.IntegerField( choices = [[1,'Increase'],[0,'No change'],[-1,'Decrease']], label = 'Investment in real estate', widget = widgets.RadioSelect ) inf_9_livret = models.IntegerField( choices = [[1,'Increase'],[0,'No change'],[-1,'Decrease']], label = 'Investment in savings accounts', widget = widgets.RadioSelect ) inf_9_mutualFunds = models.IntegerField( choices = [[1,'Increase'],[0,'No change'],[-1,'Decrease']], label = 'Investment in mutual funds', widget = widgets.RadioSelect ) inf_9_stocks = models.IntegerField( choices = [[1,'Increase'],[0,'No change'],[-1,'Decrease']], label = 'Investment in stocks', widget = widgets.RadioSelect ) inf_9_indexedBonds = models.IntegerField( choices = [[1,'Increase'],[0,'No change'],[-1,'Decrease']], label = 'Investment in indexed bonds', widget = widgets.RadioSelect ) inf_9_move = models.IntegerField( choices = [[1,'Yes'],[-1,'No'],[0,'Maybe']], label = 'Move to an apartment with lower rent', widget = widgets.RadioSelect ) inf_9_income = models.IntegerField( choices = [[1,'Yes'],[-1,'No'],[0,'Maybe']], label = 'Seek additional sources of income', widget = widgets.RadioSelect ) inf_9_job = models.IntegerField( choices = [[1,'Yes'],[-1,'No'],[0,'Maybe']], label = 'Seek a new job', widget = widgets.RadioSelect ) inf_10 = models.BooleanField( choices = [[True,'Yes'],[False,'No']], label = 'Have you done any of that in the past 12 months?', widget = widgets.RadioSelect ) inf_11 = models.BooleanField( choices = [[True,'Yes'],[False,'No']], label = 'Do you think inflation can be controlled?', widget = widgets.RadioSelect ) inf_12 = models.IntegerField( choices = [[1,'Private businesses'],[2,'Politicians'],[3,'Central banks'],[3,'Private banks'],[4,'No one'],[9,"I don't know"]], label = 'In your opinion, who should fix inflation?', widget = widgets.RadioSelect ) ## Choice randomization def infCI_2_choices(player): import random choices = [ [1,'{}'.format(cu(C.QUESTION_PRICE * 2))], [2,'More than {}'.format(cu(C.QUESTION_PRICE * 2))], [3,'Less than {}'.format(cu(C.QUESTION_PRICE * 2))] ] random.shuffle(choices) return choices def infCI_3_choices(player): import random choices = [ [1,'{}'.format(cu(C.QUESTION_PRICE + 150))], [2,'More than {}'.format(cu(C.QUESTION_PRICE + 150))], [3,'Less than {}'.format(cu(C.QUESTION_PRICE + 150))] ] random.shuffle(choices) return choices # PAGES class InflationInstructions(Page): form_model = 'player' form_fields = ['slider'] counter_questions = 0 @staticmethod def error_message(player, values): solutions = dict( slider=C.SLIDER_TEST ) error_messages = dict() for field_name in solutions: if values[field_name] != solutions[field_name]: error_messages[field_name] = 'That is incorrect. Please, correct your answer by selecting the appropriate value on the Slider.' return error_messages class InflationKnowledge_1(Page): form_model = 'player' form_fields = ['infK_1'] timeout_seconds = C.TIME_LIMIT counter_questions = InflationInstructions.counter_questions + len(InflationInstructions.form_fields) ## For progress bars @staticmethod def vars_for_template(player: Player): return dict( percentage=round(InflationKnowledge_1.counter_questions / C.NUM_QUESTIONS * 100,) ) class InflationTimerStart(Page): pass class InflationKnowledge_2(Page): form_model = 'player' form_fields = ['infK_2'] timeout_seconds = C.TIME_LIMIT counter_questions = InflationKnowledge_1.counter_questions + len(InflationKnowledge_1.form_fields) @staticmethod def vars_for_template(player: Player): return dict( percentage=round(InflationKnowledge_2.counter_questions / C.NUM_QUESTIONS * 100,) ) class InflationKnowledge_3(Page): form_model = 'player' form_fields = ['infK_3'] timeout_seconds = C.TIME_LIMIT counter_questions = InflationKnowledge_2.counter_questions + len(InflationKnowledge_2.form_fields) @staticmethod def vars_for_template(player: Player): return dict( percentage=round(InflationKnowledge_3.counter_questions / C.NUM_QUESTIONS * 100,) ) class InflationNumeracy(Page): form_model = 'player' form_fields = ['infCI_1','infCI_2','infCI_3','infCI_4'] counter_questions = InflationKnowledge_3.counter_questions + len(InflationKnowledge_3.form_fields) @staticmethod def vars_for_template(player: Player): return dict( percentage=round(InflationNumeracy.counter_questions / C.NUM_QUESTIONS * 100,) ) class InflationTimerEnd(Page): pass class InflationAwareness_1(Page): form_model = 'player' form_fields = ['inf_4','inf_5'] counter_questions = InflationNumeracy.counter_questions + len(InflationNumeracy.form_fields) @staticmethod def vars_for_template(player: Player): return dict( percentage=round(InflationAwareness_1.counter_questions / C.NUM_QUESTIONS * 100,) ) class InflationAwareness_2(Page): form_model = 'player' form_fields = ['inf_6'] counter_questions = InflationAwareness_1.counter_questions + len(InflationAwareness_1.form_fields) @staticmethod def vars_for_template(player: Player): return dict( percentage=round(InflationAwareness_2.counter_questions / C.NUM_QUESTIONS * 100,) ) @staticmethod def is_displayed(player): return player.inf_5 == 1 or player.inf_5 == -1 class InflationAwareness_3(Page): form_model = 'player' form_fields = ['inf_7'] counter_questions = InflationAwareness_2.counter_questions + len(InflationAwareness_2.form_fields) @staticmethod def vars_for_template(player: Player): return dict( percentage=round(InflationAwareness_3.counter_questions / C.NUM_QUESTIONS * 100,) ) class InflationAwareness_4(Page): form_model = 'player' form_fields = ['inf_8'] counter_questions = InflationAwareness_3.counter_questions + len(InflationAwareness_3.form_fields) @staticmethod def vars_for_template(player: Player): return dict( percentage=round(InflationAwareness_4.counter_questions / C.NUM_QUESTIONS * 100,) ) @staticmethod def is_displayed(player): return player.inf_7 == 1 or player.inf_7 == -1 class InflationAwareness_5(Page): form_model = 'player' form_fields = [ 'inf_9_cheaper', 'inf_9_quantity', 'inf_9_stockMaintained', 'inf_9_subscription', 'inf_9_leisure', 'inf_9_insurance', 'inf_9_realEstate', 'inf_9_livret', 'inf_9_mutualFunds', 'inf_9_stocks', 'inf_9_indexedBonds', ] counter_questions = InflationAwareness_4.counter_questions + len(InflationAwareness_4.form_fields) @staticmethod def vars_for_template(player: Player): return dict( percentage=round(InflationAwareness_5.counter_questions / C.NUM_QUESTIONS * 100,) ) class InflationAwareness_6(Page): form_model = 'player' form_fields = [ 'inf_9_move', 'inf_9_income', 'inf_9_job', ] counter_questions = InflationAwareness_5.counter_questions + len(InflationAwareness_5.form_fields) @staticmethod def vars_for_template(player: Player): return dict( percentage=round(InflationAwareness_6.counter_questions / C.NUM_QUESTIONS * 100,) ) class InflationAwareness_7(Page): form_model = 'player' form_fields = [ 'inf_10', 'inf_11', 'inf_12' ] counter_questions = InflationAwareness_6.counter_questions + len(InflationAwareness_6.form_fields) @staticmethod def vars_for_template(player: Player): return dict( percentage=round(InflationAwareness_7.counter_questions / C.NUM_QUESTIONS * 100,) ) class InflationKnowledge_4(Page): form_model = 'player' form_fields = ['infK_4'] timeout_seconds = C.TIME_LIMIT counter_questions = InflationAwareness_7.counter_questions + len(InflationAwareness_7.form_fields) @staticmethod def vars_for_template(player: Player): return dict( percentage=round(InflationKnowledge_4.counter_questions / C.NUM_QUESTIONS * 100,) ) class InflationKnowledge_5(Page): form_model = 'player' form_fields = ['infK_5'] timeout_seconds = C.TIME_LIMIT counter_questions = InflationKnowledge_4.counter_questions + len(InflationKnowledge_4.form_fields) @staticmethod def vars_for_template(player: Player): return dict( percentage=round(InflationKnowledge_5.counter_questions / C.NUM_QUESTIONS * 100,) ) class InflationKnowledge_6(Page): form_model = 'player' form_fields = ['infK_6'] timeout_seconds = C.TIME_LIMIT counter_questions = InflationKnowledge_5.counter_questions + len(InflationKnowledge_5.form_fields) @staticmethod def vars_for_template(player: Player): return dict( percentage=round(InflationKnowledge_6.counter_questions / C.NUM_QUESTIONS * 100,) ) class Results(Page): counter_questions = InflationKnowledge_6.counter_questions + len(InflationKnowledge_6.form_fields) @staticmethod def vars_for_template(player: Player): return dict( percentage=round(Results.counter_questions / C.NUM_QUESTIONS * 100,) ) page_sequence = [ InflationInstructions, InflationTimerStart, InflationKnowledge_1, InflationKnowledge_2, InflationKnowledge_3, InflationTimerEnd, InflationNumeracy, InflationAwareness_1, InflationAwareness_2, InflationAwareness_3, InflationAwareness_4, InflationAwareness_5, InflationAwareness_6, InflationAwareness_7, InflationTimerStart, InflationKnowledge_4, InflationKnowledge_5, InflationKnowledge_6, InflationTimerEnd, Results ]