from otree.api import * import random import shared_constants from experiment_text import display_topic, option_label from session_utils import merge_t, get_language, texts_for doc = """ Elicits private opinions and willingness to pay 'c' to punish. Uses Rounds to loop through a list of topics efficiently. Randomizes topic order and option order per participant. Stored opinion codes are 'A' or 'B' (language-independent). """ class C(BaseConstants): NAME_IN_URL = 'topic_elicitation' PLAYERS_PER_GROUP = None TOPICS = shared_constants.TOPICS NUM_ROUNDS = len(TOPICS) PUNISHMENT_FEE = shared_constants.PUNISHMENT_FEE PENALTY_LOW = shared_constants.C_LOW_POINTS PENALTY_HIGH = shared_constants.C_HIGH_POINTS class Subsession(BaseSubsession): pass def creating_session(subsession): if subsession.round_number == 1: lang = subsession.session.config.get('language', 'en') for p in subsession.get_players(): p.participant.vars['language'] = lang p.participant.session_language = lang my_topics = C.TOPICS.copy() random.shuffle(my_topics) p.participant.vars['topic_order'] = my_topics all_round_options = [] for topic_key in my_topics: opts = [ ['A', option_label(lang, 'A', topic_key=topic_key)], ['B', option_label(lang, 'B', topic_key=topic_key)], ] random.shuffle(opts) all_round_options.append(opts) p.participant.vars['option_orders'] = all_round_options p.participant.vars['topic_data'] = {} class Group(BaseGroup): pass class Player(BasePlayer): current_topic_label = models.StringField() opinion = models.StringField( label="", widget=widgets.RadioSelect, ) punish = models.BooleanField( label="", choices=[ [True, "Yes"], [False, "No"], ], widget=widgets.RadioSelect, ) def opinion_choices(player): return player.participant.vars['option_orders'][player.round_number - 1] def punish_choices(player): lang = get_language(player) from experiment_text import TEXT t = TEXT.get(lang, TEXT['en']) return [[True, t['yes']], [False, t['no']]] class QuestionPage(Page): form_model = 'player' form_fields = ['opinion', 'punish'] @staticmethod def vars_for_template(player): topics = player.participant.vars.get('topic_order', C.TOPICS) current_topic = topics[player.round_number - 1] player.current_topic_label = current_topic lang = get_language(player) topic_display = display_topic(lang, current_topic) tx = texts_for(player) cost = C.PUNISHMENT_FEE return merge_t( player, { 'topic': topic_display, 'topic_key': current_topic, 'page_title': tx['topic_page_title'].format(topic=topic_display), 'topic_q2_heading_f': tx['topic_q2_heading'].format(cost=cost), 'topic_q2_help_f': tx['topic_q2_help'].format(cost=cost), 'cost': cost, 'penalty_low': C.PENALTY_LOW, 'penalty_high': C.PENALTY_HIGH, }, ) @staticmethod def before_next_page(player, timeout_happened): topic_name = player.current_topic_label if 'topic_data' not in player.participant.vars: player.participant.vars['topic_data'] = {} player.participant.vars['topic_data'][topic_name] = { 'opinion': player.opinion, 'punish': player.punish, } class Results(Page): @staticmethod def is_displayed(player): return player.round_number == C.NUM_ROUNDS page_sequence = [QuestionPage]