import time from otree.api import * import treatments doc = """ Your app description """ class C(BaseConstants): NAME_IN_URL = "intro" PLAYERS_PER_GROUP = None NUM_ROUNDS = 1 MAX_COMP_ATTEMPTS = 3 # Treatment Names BASE = treatments.TreatmentName.BASE SIM = treatments.TreatmentName.SIM COMM = treatments.TreatmentName.COMM IND = treatments.TreatmentName.IND FOR = treatments.TreatmentName.FOR # Errors Messages EXCEEDED_COMP_ATTEMPTS = "it took you too many attempts to answer the comprehension questions correctly" class Subsession(BaseSubsession): pass def creating_session(subsession: Subsession): if not subsession.session.config["completion_code"]: raise RuntimeError( 'A Prolific Completion Code must be provided for this session in the configs. You can do so by clicking "Sessions" > "Create a new session" > "Configure Session".' ) class Group(BaseGroup): pass class Player(BasePlayer): nr_comp_attempts = models.IntegerField(initial=0) comprehension_question1a = models.CurrencyField(min=0, max=12, label="Person A earns:", blank=True) comprehension_question1b = models.CurrencyField(min=0, max=12, label="Person B earns:", blank=True) comprehension_question2a = models.CurrencyField(min=0, max=12, label="Person A earns:", blank=True) comprehension_question2b = models.CurrencyField(min=0, max=12, label="Person B earns:", blank=True) def set_dropout(self, err_msg: str): pt = self.participant pt.dropout = True pt.err_msg = err_msg pt.is_responsible = True def is_individual_treatment(self): return self.session.config["treatment"].name == C.IND def all_answers_correct(self, values): correct_1 = 0 if self.is_individual_treatment(): correct_2 = 3 else: correct_2 = 2 return ( values["comprehension_question1a"] == correct_1 and values["comprehension_question1b"] == correct_1 and values["comprehension_question2a"] == correct_2 and values["comprehension_question2b"] == correct_2 ) # PAGES class Consent(Page): pass class MeasureTime(Page): @classmethod def live_method(cls, player: Player, data): # TODO: check that this works for InstructionsPart1 if "time_on_page" in data: player.participant.time_spent_on_pages.append(tuple([cls.__name__, data["time_on_page"]])) class Welcome(MeasureTime): pass class Instructions(MeasureTime): @staticmethod def vars_for_template(player: Player): return dict(currency_conversion_rate=cu(1).to_real_world_currency(player.session)) class InstructionsPart1(MeasureTime): form_model = "player" form_fields = [ "comprehension_question1a", "comprehension_question1b", "comprehension_question2a", "comprehension_question2b", ] @staticmethod def live_method(player: Player, data): print(data) if "time_on_page" in data: player.participant.time_spent_on_pages.append(tuple(["InstructionsPart1", data["time_on_page"]])) return pid = player.id_in_group nr_attempts = player.nr_comp_attempts if not player.all_answers_correct(data): nr_attempts += 1 if nr_attempts >= C.MAX_COMP_ATTEMPTS: print("set dropout") player.set_dropout(err_msg=C.EXCEEDED_COMP_ATTEMPTS) return {pid: True} # submit, but drop player.nr_comp_attempts = nr_attempts return {pid: False} # keep on page to try again return {pid: True} # submit and move on @staticmethod def vars_for_template(player: Player): if player.is_individual_treatment(): description = "Imagine Person A sees the following die on his or her screen:" question1 = "Now imagine Person A reports the number 6. How much do Person A and Person B then earn in Part 1 of the experiment?" question2 = "Now imagine Person A reports the number 3. How much do Person A and Person B then earn in Part 1 of the experiment?" else: description = "Imagine Person A and Person B see the following die on their screen:" question1 = "Now imagine Person A reports the number 6. Person B also reports the number 6. How much do Person A and Person B then earn in Part 1 of the experiment?" question2 = "Now imagine Person A reports the number 2. Person B reports the number 4. How much do Person A and Person B then earn in Part 1 of the experiment?" return dict( dice_img="dices/dice6.svg", description=description, question1=question1, question2=question2, ) @staticmethod def before_next_page(player: Player, timeout_happened): """FOR THE LAST PAGE OF THIS APP""" player.participant.matching_arrival_time = time.time() @staticmethod def is_displayed(player: Player): return not player.participant.dropout page_sequence = [Consent, Welcome, Instructions, InstructionsPart1]