import random from otree.api import * class C(BaseConstants): NAME_IN_URL = 'experiment' PLAYERS_PER_GROUP = None NUM_ROUNDS = 6 PREF_LIST = [[1, '1 - LSR: 89.00 - Payoff: £10.00'], [2, '2 - LSR: 88.50 - Payoff: £9.50'], [3, '3 - LSR: 88.00 - Payoff: £9.00'], [4, '4 - LSR: 87.50 - Payoff: £8.50'], [5, '5 - LSR: 87.00 - Payoff: £8.00'], [6, '6 - LSR: 86.50 - Payoff: £7.50'], [7, '7 - LSR: 86.00 - Payoff: £7.00'], [8, '8 - LSR: 85.50 - Payoff: £6.50'], [9, '9 - LSR: 85.00 - Payoff: £6.00'], [10, '10 - LSR: 84.50 - Payoff: £5.50'], [11, '11 - LSR: 84.00 - Payoff: £5.00'], [12, '12 - LSR: 83.50 - Payoff: £4.50'], [13, '13 - LSR: 83.00 - Payoff: £4.00'], [14, '14 - LSR: 82.50 - Payoff: £3.50'], [15, '15 - LSR: 82.00 - Payoff: £3.00'], [16, '16 - LSR: 81.50 - Payoff: £2.50'], [17, '17 - LSR: 81.00 - Payoff: £2.00'], [18, '18 - LSR: 80.50 - Payoff: £1.50'], [19, '19 - LSR: 80.00 - Payoff: £1.00 - Guaranteed Entry'], [20, '20 - LSR: 79.50 - Payoff: £1.00 - Guaranteed Entry'], [21, '21 - LSR: 79.00 - Payoff: £1.00 - Guaranteed Entry'], [22, '22 - LSR: 78.50 - Payoff: £1.00 - Guaranteed Entry'], [23, '23 - LSR: 78.00 - Payoff: £1.00 - Guaranteed Entry'], [24, '24 - LSR: 77.50 - Payoff: £1.00 - Guaranteed Entry'], [25, '25 - LSR: 77.00 - Payoff: £0.90'], [26, '26 - LSR: 76.50 - Payoff: £0.80'], [27, '27 - LSR: 76.00 - Payoff: £0.70'], [28, '28 - LSR: 75.50 - Payoff: £0.60'], [29, '29 - LSR: 75.00 - Payoff: £0.50'], [30, '30 - LSR: 74.50 - Payoff: £0.40']] ACCEPT_RATE = [0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] PAYOFF_LIST = [ cu(10), cu(9.5), cu(9), cu(8.5), cu(8), cu(7.5), cu(7), cu(6.5), cu(6), cu(5.5), cu(5), cu(4.5), cu(4), cu(3.5), cu(3), cu(2.5), cu(2), cu(1.5), cu(1), cu(1), cu(1), cu(1), cu(1), cu(1), cu(1), cu(1), cu(1), cu(1), cu(1), cu(1)] class Subsession(BaseSubsession): pass def creating_session(subsession): import itertools treatments = itertools.cycle([False, True]) for player in subsession.get_players(): player.treatment_type = next(treatments) class Group(BaseGroup): pass def make_rank_field(label): return models.IntegerField(choices=C.PREF_LIST, label=label, initial=0) class Player(BasePlayer): rank1 = make_rank_field("Preference 1") rank2 = make_rank_field("Preference 2") rank3 = make_rank_field("Preference 3") rank4 = make_rank_field("Preference 4") rank5 = make_rank_field("Preference 5") matched_course = models.StringField(initial='N/A') treatment_type = models.BooleanField() # FUNCTIONS def sorting(player: Player): for rank in range(1, 6): for pref_index in range(30): if getattr(player, f"rank{rank}") == C.PREF_LIST[pref_index][0] and \ random.random() <= C.ACCEPT_RATE[pref_index]: player.payoff = C.PAYOFF_LIST[pref_index] player.matched_course = C.PREF_LIST[pref_index][1] return # PAGES # class Introduction(Page): # timeout_seconds = 100 class Selection(Page): form_model = 'player' form_fields = ['rank1', 'rank2', 'rank3', 'rank4', 'rank5'] @staticmethod def error_message(player: Player, values): choices = [values['rank1'], values['rank2'], values['rank3'], values['rank4'], values['rank5']] player_in_previous_rounds = player.in_previous_rounds() prev_choices = [[p.rank1 for p in player_in_previous_rounds], [p.rank2 for p in player_in_previous_rounds], [p.rank3 for p in player_in_previous_rounds], [p.rank4 for p in player_in_previous_rounds], [p.rank5 for p in player_in_previous_rounds]] if len(set(choices)) != len(choices): return "You cannot choose the same item twice" if player.treatment_type is True: for prev_choice in prev_choices: for choice in choices: if choice in prev_choice: return "You cannot choose previously chosen courses" @staticmethod def before_next_page(player: Player, timeout_happened): sorting(player) @staticmethod def vars_for_template(player: Player): player_in_previous_rounds = player.in_previous_rounds() prev_choices = [[p.rank1 for p in player_in_previous_rounds], [p.rank2 for p in player_in_previous_rounds], [p.rank3 for p in player_in_previous_rounds], [p.rank4 for p in player_in_previous_rounds], [p.rank5 for p in player_in_previous_rounds]] flattened_prev_choices = sum(prev_choices, []) ordered_list = sorted(flattened_prev_choices) new = ', '.join(str(item) for item in ordered_list) return { "chosen_courses": new, } class Results(Page): @staticmethod def vars_for_template(player: Player): payoff = player.payoff matched_course = player.matched_course return { "payoff": payoff, "matched_course": matched_course, } @staticmethod def app_after_this_page(player: Player, upcoming_apps): if player.treatment_type is True and player.payoff > 0: return upcoming_apps[-1] if player.treatment_type is False: return upcoming_apps[-1] page_sequence = [Selection, Results]