import itertools import random from otree.api import * doc = """ App 1: Logic quiz pilot 2x2 treatment: - Easy / Hard - Reveal / NoReveal Participants complete the quiz individually. At the end, they receive score feedback depending on treatment. Quiz score and payment-relevant variables are stored on participant for use in the next app. """ class C(BaseConstants): NAME_IN_URL = 'iq_quiz_pilot' PLAYERS_PER_GROUP = None NUM_ROUNDS = 10 QUESTION_TIME_LIMIT = 20 PART1_PRIZE = cu(1000) TREATMENT_CELLS = [ ('Easy', 'Reveal'), ('Easy', 'NoReveal'), ('Hard', 'Reveal'), ('Hard', 'NoReveal'), ] EASY_ITEMS = [ dict(item_id=1, question='easy_q1.png', ans='easy_a1.png', correct='A'), dict(item_id=2, question='easy_q2.png', ans='easy_a2.png', correct='B'), dict(item_id=3, question='easy_q3.png', ans='easy_a3.png', correct='D'), dict(item_id=4, question='easy_q4.png', ans='easy_a4.png', correct='C'), dict(item_id=5, question='easy_q5.png', ans='easy_a5.png', correct='C'), dict(item_id=6, question='easy_q6.png', ans='easy_a6.png', correct='A'), dict(item_id=7, question='easy_q7.png', ans='easy_a7.png', correct='A'), dict(item_id=8, question='easy_q8.png', ans='easy_a8.png', correct='D'), dict(item_id=9, question='easy_q9.png', ans='easy_a9.png', correct='A'), dict(item_id=10, question='easy_q10.png', ans='easy_a10.png', correct='D'), ] HARD_ITEMS = [ dict(item_id=1, question='hard_q1.png', ans='hard_a1.png', correct='A'), dict(item_id=2, question='hard_q2.png', ans='hard_a2.png', correct='B'), dict(item_id=3, question='hard_q3.png', ans='hard_a3.png', correct='D'), dict(item_id=4, question='hard_q4.png', ans='hard_a4.png', correct='C'), dict(item_id=5, question='hard_q5.png', ans='hard_a5.png', correct='A'), dict(item_id=6, question='hard_q6.png', ans='hard_a6.png', correct='A'), dict(item_id=7, question='hard_q7.png', ans='hard_a7.png', correct='B'), dict(item_id=8, question='hard_q8.png', ans='hard_a8.png', correct='D'), dict(item_id=9, question='hard_q9.png', ans='hard_a9.png', correct='C'), dict(item_id=10, question='hard_q10.png', ans='hard_a10.png', correct='C'), ] class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): difficulty_treatment = models.StringField() reveal_treatment = models.StringField() item_id = models.IntegerField() image_path = models.StringField() image_choice = models.StringField() correct_answer = models.StringField() answer = models.StringField( choices=['A', 'B', 'C', 'D'], blank=True, widget=widgets.RadioSelect, ) selected_option = models.StringField(blank=True) is_correct = models.BooleanField(initial=False) timed_out = models.BooleanField(initial=False) def create_quiz_payment_if_needed(player: Player): if 'quiz_score' in player.participant.vars: return rounds = player.in_all_rounds() total_correct = sum(p.is_correct for p in rounds) player.participant.quiz_score = total_correct chosen_round = random.choice(rounds) correct = chosen_round.is_correct points = 1000 if correct else 0 player.participant.quiz_payment_question = chosen_round.round_number player.participant.quiz_payment_correct = correct player.participant.quiz_payment_points = points def creating_session(subsession: Subsession): players = subsession.get_players() if subsession.round_number == 1: treatment_cells = itertools.cycle(C.TREATMENT_CELLS) for player in players: difficulty, reveal = next(treatment_cells) player.difficulty_treatment = difficulty player.reveal_treatment = reveal player.participant.difficulty_treatment = difficulty player.participant.reveal_treatment = reveal if difficulty == 'Easy': player.participant.quiz_items = [item.copy() for item in C.EASY_ITEMS] else: player.participant.quiz_items = [item.copy() for item in C.HARD_ITEMS] else: for player in players: player.difficulty_treatment = player.in_round(1).difficulty_treatment player.reveal_treatment = player.in_round(1).reveal_treatment for player in players: item = player.participant.quiz_items[subsession.round_number - 1] player.item_id = item['item_id'] player.image_path = item['question'] player.image_choice = item['ans'] player.correct_answer = item['correct'] class Welcome(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 class QuizIntro(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 class QuizPractice(Page): @staticmethod def is_displayed(player: Player): return player.round_number == 1 class Quiz(Page): form_model = 'player' form_fields = ['answer'] timeout_seconds = C.QUESTION_TIME_LIMIT @staticmethod def vars_for_template(player: Player): return dict( round_num=player.round_number, total_rounds=C.NUM_ROUNDS, image_path=player.image_path, image_choice=player.image_choice, timer_seconds=C.QUESTION_TIME_LIMIT, ) @staticmethod def error_message(player: Player, values): if values.get('answer') in [None, '']: return 'Please select A, B, C, or D before continuing.' @staticmethod def before_next_page(player: Player, timeout_happened): answer = player.field_maybe_none('answer') if answer is None: player.selected_option = 'NA' player.is_correct = False if timeout_happened: player.timed_out = True else: player.selected_option = answer player.is_correct = (answer == player.correct_answer) setattr(player.participant, f'q{player.round_number}_answer', player.selected_option) class ScoreFeedback(Page): @staticmethod def is_displayed(player: Player): return player.round_number == C.NUM_ROUNDS @staticmethod def vars_for_template(player: Player): create_quiz_payment_if_needed(player) return dict( total_correct=player.participant.quiz_score, total_questions=C.NUM_ROUNDS, payment_question=player.participant.quiz_payment_question, payment_correct=player.participant.quiz_payment_correct, payment_points=player.participant.quiz_payment_points, reveal_treatment=player.reveal_treatment, ) page_sequence = [ Welcome, QuizIntro, QuizPractice, Quiz, ScoreFeedback, ]