from otree.api import * c = cu doc = "Preference rating of potential items for BDM auction. Involves participants ranking their preferences for the selection of items, and then predicting their partner's preferences. " class C(BaseConstants): # built-in constants NAME_IN_URL = 'PrefRating' PLAYERS_PER_GROUP = 2 NUM_ROUNDS = 1 # user-defined constants CHOICES = ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J') INSTRUCTIONS_TEMPLATE = 'PrefRating/instructions.html' UW_TETON_BG_TEMPLATE = 'PrefRating/uw_teton_bg.html' DATA_ID_TEMPLATE = 'PrefRating/data_id.html' class Subsession(BaseSubsession): pass def after_all_players_arrive(subsession: Subsession): session = subsession.session # pull the matrix you stored in the code‐entry app matrix = subsession.session.vars.get('couple_matrix') if not matrix: # safety check in case someone forgot to run grouping first raise RuntimeError("No couple_matrix in session.vars") # re‐apply it in this app's subsession subsession.set_group_matrix(matrix) class Group(BaseGroup): spender_item = models.StringField() def set_pref_diff(group: Group): session = group.session import json # Retrieve all players in the group players = group.get_players() # Map actual preferences for easy reference actual_prefs = {p.id_in_group: p.ranking.split(',') for p in players} for p in players: # Only proceed if there are valid rankings if p.ranking and p.partner_ranking: # Split the comma-separated strings of rankings into lists of item IDs own_pref = p.ranking.split(',') predicted_partner_pref = p.partner_ranking.split(',') # Find the group partner partner = p.get_others_in_group()[0] partner_actual_pref = actual_prefs[partner.id_in_group] # Calculate differences between own preference and partner's actual preference differences = {} for item in own_pref: index1 = own_pref.index(item) index2 = predicted_partner_pref.index(item) differences[item] = index1 - index2 diffs = json.dumps([differences]) min_difference = min(differences.values(), key=lambda x: (x, own_pref.index(item))) selected_item = [key for key, value in differences.items() if value == min_difference][0] # Store selected item and differences p.participant.selected_item = selected_item p.diffs = diffs # Only proceed if there are valid partner rankings if partner_actual_pref != ['']: # Count matching positions between predicted and actual preferences match_count = sum([ predicted_partner_pref[i] == partner_actual_pref[i] for i in range(len(predicted_partner_pref)) ]) # Store the number of matches p.participant.belief_match_count = match_count # Reward if at least 5 match p.participant.correct_belief = 1 if match_count >= session.config['match_requirement'] else 0 # If any partner ranking is left blank else: p.participant.belief_match_count = 0 p.participant.correct_belief = 0 # If any personal ranking is left blank else: p.diffs = json.dumps([{"": 0}]) p.participant.selected_item = "" p.participant.belief_match_count = 0 p.participant.correct_belief = 0 def assign_spender(group: Group): import random # Randomly choosing one of the group members to be the earner in round 1 r = random.randint(1,2) # Gather players players = group.get_players() # Assign roles based on random draw for p in players: if p.id_in_group == r: # If your player id in your group matches the draw, you are the spender (permanent throughout experiment) p.participant.spender = 1 group.spender_item = p.participant.selected_item else: # If not, then you will sit out of the spending tasks p.participant.spender = 0 # set the exit p.participant.exit_1 = 0 def assign_spender_item(group: Group): # Gather players players = group.get_players() # Assign roles based on random draw for p in players: # set spender_selected_item so both players see same item later p.participant.spender_selected_item = group.spender_item class Player(BasePlayer): ranking = models.StringField() partner_ranking = models.StringField() diffs = models.LongStringField() def custom_export(players): # Header yield [ 'session_code', 'participant_id_in_session', 'participant_code', 'id_in_group', 'group_number', 'ranking', 'partner_ranking', 'correct_belief', 'spender_item' ] # Rows for p in players: pp = p.participant pg = p.group ps = p.session # Pull participant fields (needed as these are only saved in 'all apps wide' normally) correct_belief = p.participant.vars.get('correct_belief', '') yield [ ps.code, pp.id_in_session, pp.code, p.id_in_group, pg.id_in_subsession, p.ranking, p.partner_ranking, correct_belief, pg.spender_item ] class GroupingWaitPage(WaitPage): wait_for_all_groups = True after_all_players_arrive = after_all_players_arrive class PrefRanking(Page): form_model = 'player' form_fields = ['ranking'] # vars_for_template is deprecated in favor of python-based renderers @staticmethod def vars_for_template(player: Player): # Define the list of items with unique identifiers, their associated image URLs, and a description imgur_urls = [ {'id': 'A', 'url': 'https://i.imgur.com/OfsrPbR.jpeg', 'description': "Coloring book with pencils"}, # Image and description for item A {'id': 'B', 'url': 'https://i.imgur.com/tmBYFwX.jpeg', 'description': "Headphones"}, # Image and description for item B {'id': 'C', 'url': 'https://i.imgur.com/oBfC4Mz.jpeg', 'description': "Moisturizer"}, # Image and description for item C {'id': 'D', 'url': 'https://i.imgur.com/q7bBuv6.jpeg', 'description': "Spa kit"}, # Image and description for item D {'id': 'E', 'url': 'https://i.imgur.com/Zc7vR6m.jpeg', 'description': "Grilling accessories"}, # Image and description for item E {'id': 'F', 'url': 'https://i.imgur.com/YLEoKGI.jpeg', 'description': "Tactical flashlight"}, # Image and description for item F {'id': 'G', 'url': 'https://i.imgur.com/lmMFuFL.jpeg', 'description': "Lip Balm"}, # Image and description for item G {'id': 'H', 'url': 'https://i.imgur.com/5KfUIfj.jpeg', 'description': "Water bottle"}, # Image and description for item H {'id': 'I', 'url': 'https://i.imgur.com/AS1feBP.jpeg', 'description': "Car detailing kit"}, # Image and description for item I {'id': 'J', 'url': 'https://i.imgur.com/SfJ7GmF.jpeg', 'description': "Multi-tool"} # Image and description for item J ] # Passing Python variables to the HTML template of the page return dict( image_paths=imgur_urls # Key 'image_paths' will be accessible in the HTML template ) class PartnerPrefRanking(Page): form_model = 'player' form_fields = ['partner_ranking'] # vars_for_template is deprecated in favor of python-based renderers @staticmethod def vars_for_template(player: Player): # Define the list of items with unique identifiers, their associated image URLs, and a description imgur_urls = [ {'id': 'A', 'url': 'https://i.imgur.com/OfsrPbR.jpeg', 'description': "Coloring book with pencils"}, # Image and description for item A {'id': 'B', 'url': 'https://i.imgur.com/tmBYFwX.jpeg', 'description': "Headphones"}, # Image and description for item B {'id': 'C', 'url': 'https://i.imgur.com/oBfC4Mz.jpeg', 'description': "Moisturizer"}, # Image and description for item C {'id': 'D', 'url': 'https://i.imgur.com/q7bBuv6.jpeg', 'description': "Spa kit"}, # Image and description for item D {'id': 'E', 'url': 'https://i.imgur.com/Zc7vR6m.jpeg', 'description': "Grilling accessories"}, # Image and description for item E {'id': 'F', 'url': 'https://i.imgur.com/YLEoKGI.jpeg', 'description': "Tactical flashlight"}, # Image and description for item F {'id': 'G', 'url': 'https://i.imgur.com/lmMFuFL.jpeg', 'description': "Lip Balm"}, # Image and description for item G {'id': 'H', 'url': 'https://i.imgur.com/5KfUIfj.jpeg', 'description': "Water bottle"}, # Image and description for item H {'id': 'I', 'url': 'https://i.imgur.com/AS1feBP.jpeg', 'description': "Car detailing kit"}, # Image and description for item I {'id': 'J', 'url': 'https://i.imgur.com/SfJ7GmF.jpeg', 'description': "Multi-tool"} # Image and description for item J ] # Passing Python variables to the HTML template of the page return dict( image_paths=imgur_urls # Key 'image_paths' will be accessible in the HTML template ) class PrefDiffWaitPage(WaitPage): after_all_players_arrive = set_pref_diff class AssignSpenderWaitPage(WaitPage): after_all_players_arrive = assign_spender class AssignSpenderItemWaitPage(WaitPage): after_all_players_arrive = assign_spender_item class PrefRatingEndPage(WaitPage): wait_for_all_groups = True page_sequence = [GroupingWaitPage, PrefRanking, PartnerPrefRanking, PrefDiffWaitPage, AssignSpenderWaitPage, AssignSpenderItemWaitPage, PrefRatingEndPage]