from collections import Counter from otree.api import * import itertools from itertools import cycle import random import json doc = """ Your app description """ class C(BaseConstants): NAME_IN_URL = 'frd_pls' PLAYERS_PER_GROUP = 3 NUM_ROUNDS = 1 base_tasks = 3 extra_tasks = 3 payment_per_task = 40 class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): p_id = models.StringField(label="
Please enter your unique Prolific ID: ") is_chosen = models.BooleanField(initial=False) consent = models.BooleanField(initial=False) num_tasks = models.IntegerField() correct_answers = models.IntegerField(initial=0) task_1 = models.IntegerField(blank=False) task_2 = models.IntegerField(blank=False) task_3 = models.IntegerField(blank=False) task_4 = models.IntegerField(blank=False) task_5 = models.IntegerField(blank=False) task_6 = models.IntegerField(blank=False) points = models.IntegerField(initial=0) declared_balance = models.IntegerField(blank=True, max=240, label="") earned = models.CurrencyField(initial=0) clicked_finish = models.IntegerField(initial=0) submitted_bonus = models.IntegerField(initial=0) skip_until_finish = models.BooleanField(initial=False) misreport_attempts = models.IntegerField(initial=0) ### Demographic Questions (AllSurvey Page) age = models.IntegerField(label="Please enter your age: ", min=18, max=80) sex = models.StringField( label="
What is your sex, as recorded on legal/official documents?", choices=["Male", "Female", "Prefer not to say"], widget=widgets.RadioSelect, ) ethnicity = models.StringField( label="
What ethnic group do you belong to?", choices=["White", "Asian", "Black", "Mixed", "Other"], widget=widgets.RadioSelect ) ethnicity_other = models.StringField(blank=True, label="If Other, please specify:") education = models.StringField( label="
What is the highest level of education you have completed?", choices=["No formal qualifications", "Primary education", "Secondary education", "Vocational qualification", "Undergraduate degree", "Postgraduate degree", "Doctorate degree"], widget=widgets.RadioSelect ) job = models.StringField( label="
What is your current employment status?", choices=["Employed / Self-employed", "Out of work / Unemployed", "Student / Part-time employment", "Retired"], widget=widgets.RadioSelect ) income = models.StringField( label="
What was your total household income before taxes in 2024 (in GBP)?", choices=["Under 10,000", "10,000 to 19,999", "20,000 to 29,999", "30,000 to 39,999", "40,000 to 49,999", "50,000 to 59,999", "60,000 to 69,999", "70,000 to 79,999", "80,000 to 99,999", "100,000 or more"], widget=widgets.RadioSelect ) spectrum = models.StringField( label="
On economic policy matters, where do you see yourself on the liberal/conservative spectrum?", choices=[ ('vlib', 'Very Liberal'), ('lib', 'Liberal'), ('mod', 'Moderate'), ('cons', 'Conservative'), ('vcons', 'Very Conservative'), ], widget=widgets.RadioSelect ) political = models.StringField( label="
Which political party do you most identify with?", choices=["Conservative Party", "Labour Party", "Liberal Democrats", "None / Not affiliated", "Other"], widget=widgets.RadioSelect ) political_other = models.StringField(blank=True, label="If Other, please specify:") comment = models.LongStringField(blank=True, label="
Do you have any comments or suggestions regarding the study? " "If not, you can leave this field blank.") # FUNCTIONS def creating_session(subsession: Subsession): for p in subsession.get_players(): p.num_tasks = C.base_tasks + C.extra_tasks def age_error_message(player, value): if value < 18: return "You need to be at least 18 years old." elif value > 80: return "Please enter your age." def maybe_draw_players(subsession, N=8): for p in subsession.get_players(): p.is_chosen = False fraud_players = [p for p in subsession.get_players()] chosen = random.sample(fraud_players, k=min(N, len(fraud_players))) for p in chosen: p.is_chosen = True # PAGES class P1Intro(Page): form_model = "player" form_fields = ["p_id"] @staticmethod def before_next_page(player, timeout_happened): player.consent = True class P2Instr(Page): form_model = "player" @staticmethod def vars_for_template(player): maybe_draw_players(player.subsession, N=8) # for example return {} class P3Task(Page): form_model = "player" form_fields = ['task_1', 'task_2', 'task_3', 'task_4', 'task_5', 'task_6'] @staticmethod def error_message(player, values): # Check if any submitted field is empty for field in values: if values[field] is None: return "Please answer all questions." @staticmethod def vars_for_template(player): base_tasks = [ {'letter': 't', 'string': 'afdygtuyftdwubtiskjdfttr'}, {'letter': 'f', 'string': 'fdajfuklfdbfjklafjfdkl'}, {'letter': 's', 'string': 'asdhfksjlsakdfjsalkjs'}, ] extra_tasks = [ {'letter': 'k', 'string': 'kjfkdljfkldjfklajfkdljf'}, {'letter': 'j', 'string': 'jafjkjfjkjfjkjfjkjfjkfj'}, {'letter': 'd', 'string': 'dfkdjfkdlfjdklfjdklfjd'}, ] @staticmethod def before_next_page(player, timeout_happened): correct_answers = 0 # Define correct answers in the same order as tasks # Correct answers answers_control = [5, 6, 5] answers_fraud = [5, 6, 5, 5, 11, 6] answers = answers_fraud submitted = [player.task_1, player.task_2, player.task_3, player.task_4, player.task_5, player.task_6] for sub, correct in zip(submitted, answers): if sub == correct: correct_answers += 1 player.correct_answers = correct_answers player.points = correct_answers * C.payment_per_task class P4Info(Page): form_model = "player" @staticmethod def vars_for_template(player): return dict(points = player.points) class P5Instr(Page): form_model = "player" class P6Fund(Page): form_model = "player" form_fields = ["declared_balance", "submitted_bonus", "clicked_finish"] @staticmethod def js_vars(player): correct_balance = player.points return {"correct_balance": correct_balance} @staticmethod def vars_for_template(player): return {"points": player.points} @staticmethod def live_method(player, data): if data.get('misreport_attempt'): player.misreport_attempts += 1 @staticmethod def before_next_page(player, timeout_happened): if player.clicked_finish: player.skip_until_finish = True elif player.submitted_bonus: player.skip_until_finish = True else: player.skip_until_finish = False print("clicked_finish:", player.clicked_finish) print("submitted_bonus:", player.submitted_bonus) class P7Survey(Page): form_model = "player" form_fields = ["age", "sex", "ethnicity", "ethnicity_other", "education", "job", "income", "spectrum", "political", "political_other", "comment"] class P8Finish(Page): form_model = "player" form_fields = ["submitted_bonus"] @staticmethod def vars_for_template(player): player.earned = player.points if player.earned < 80: player.earned = 80 conversion_rate = player.session.config['real_world_currency_per_point'] real_world_earned = player.earned * conversion_rate real_world_earned_formatted = '{:.2f}'.format(round(real_world_earned, 2)) return { "earned": real_world_earned_formatted } page_sequence = [P1Intro, P2Instr, P3Task, P4Info, P5Instr, P6Fund, P7Survey, P8Finish]