from otree.api import * c = cu doc = 'Frydman & Jin 2021 swipe/tap' class C(BaseConstants): # built-in constants NAME_IN_URL = 'XC_swipetap' PLAYERS_PER_GROUP = None NUM_ROUNDS = 2 # user-defined constants PRACTICEROUNDS = () STIMULISIZE = 10 class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): choicecounter = models.IntegerField(initial=0) direction = models.StringField(initial='') riskyonleft = models.StringField(initial='') stimuliX = models.StringField(initial='') stimuliC = models.StringField(initial='') responsetimes = models.StringField(initial='') choicerisky = models.IntegerField(initial=0) def live_open(player: Player, data): group = player.group # time, loadtime, starttime, opened, direction, pageloads time = data['time'] # this is a single timestamp indicating when the message was sent to server loadtime = data['loadtime'] # this is a timestamp recorded when the new stimulus was loaded, which happens in both frames after 1000 ms from the previous liveSend # this is keeping track of how many times has been slid/tapped player.choicecounter += 1 # keeping track of choices player.direction += data['direction'] + ';' # direction of the slide player.choicerisky += data['choicerisky'] # indicator 1=chose risky option, 0=chose safe option player.riskyonleft += data['riskyonleft'] + ';' #player.pageloads += str(data['pageloads']) + ';' # keeps track of possible refreshs of the page player.responsetimes += str(int(time) - int(loadtime)) + ';' # relevant in both swipe and tap frames #player.slidetimes += str(int(time) - int(starttime)) + ';' # only relevant in the swipe frame response = {'choicecountervalue': player.choicecounter} return {player.id_in_group: response} # using 0: response is an error, broadcasts to the whole group!!! class PreDecision(Page): form_model = 'player' timeout_seconds = 1 @staticmethod def before_next_page(player: Player, timeout_happened): import random # Frydman & Jin: "In the high-volatility condition, X is drawn uniformly from [8,32], and C is drawn uniformly from [4,16]. In the low-volatility condition, X is drawn uniformly from [16,24], and C is drawn uniformly from [8,12]." # the high volatility condition has noisier perception of X and C, lower discriminability between nearby payoffs, and flatter psychometric choice curves # => USE THE HIGH VOLATILITY CONDITION, divide amounts by 4 N = C.STIMULISIZE stimuliX = [round(random.uniform(2,8),2) for x in range(N)] stimuliC = [round(random.uniform(1,4),2) for x in range(N)] player.stimuliX = str(stimuliX) player.stimuliC = str(stimuliC) class Swipe(Page): form_model = 'player' @staticmethod def is_displayed(player: Player): return player.round_number == 1 @staticmethod def js_vars(player: Player): import ast stimuliX = ast.literal_eval(player.stimuliX) stimuliC = ast.literal_eval(player.stimuliC) return dict( stimuliX = stimuliX, stimuliC = stimuliC, practice = player.round_number in C.PRACTICEROUNDS, howmanychoices = len(stimuliX), choicecounter = player.choicecounter ) live_method = 'live_open' class Tap(Page): form_model = 'player' @staticmethod def is_displayed(player: Player): return player.round_number == 2 @staticmethod def js_vars(player: Player): import ast stimuliX = ast.literal_eval(player.stimuliX) stimuliC = ast.literal_eval(player.stimuliC) return dict( stimuliX = stimuliX, stimuliC = stimuliC, practice = player.round_number in C.PRACTICEROUNDS, howmanychoices = len(stimuliX), choicecounter = player.choicecounter ) live_method = 'live_open' page_sequence = [PreDecision, Swipe, Tap]