from otree.api import * c = cu doc = '' class C(BaseConstants): NAME_IN_URL = 'desktop_columbiacardtask_live' PLAYERS_PER_GROUP = None NUM_ROUNDS = 54 BOXES = 32 LOSSCARDS = (1, 2, 3) GAINAMOUNTS = (10, 10, 10, 20, 20, 20, 30, 30, 30) LOSSAMOUNTS = (250, 250, 250, 250, 250, 250, 250, 250, 250, 500, 500, 500, 500, 500, 500, 500, 500, 500, 750, 750, 750, 750, 750, 750, 750, 750, 750) NFCLONG = 0 class Subsession(BaseSubsession): pass def creating_session(subsession: Subsession): session = subsession.session # https://otree.readthedocs.io/en/latest/treatments.html#creating-session if subsession.round_number == 1: import itertools import random # create 5 different random orderings indices1 = [i for i in range(1,C.NUM_ROUNDS+1)] random.shuffle(indices1) indices2 = [i for i in range(1,C.NUM_ROUNDS+1)] random.shuffle(indices2) indices3 = [i for i in range(1,C.NUM_ROUNDS+1)] random.shuffle(indices3) indices4 = [i for i in range(1,C.NUM_ROUNDS+1)] random.shuffle(indices4) indices5 = [i for i in range(1,C.NUM_ROUNDS+1)] random.shuffle(indices5) # cycle the players between the random orderings index_cycle = itertools.cycle([indices1,indices2,indices3,indices4,indices5]) for player in subsession.get_players(): participant = player.participant participant.indexlist = next(index_cycle) class Group(BaseGroup): pass class Player(BasePlayer): chosencard = models.IntegerField(initial=0) endedinloss = models.BooleanField() stopped = models.IntegerField(initial=0) profit = models.IntegerField() roundlosscards = models.IntegerField() firstlosscard = models.IntegerField() roundgain = models.IntegerField() roundloss = models.IntegerField() losscardlist = models.StringField() dectimes = models.StringField(initial='') startleft = models.StringField(initial='') touchtimes = models.StringField(initial='') xs = models.StringField(initial='') ys = models.StringField(initial='') polyareas = models.StringField(initial='') computer_randomround1 = models.IntegerField() computer_randomround2 = models.IntegerField() randomprofit1 = models.IntegerField() randomprofit2 = models.IntegerField() reward = models.FloatField() dohmen = models.IntegerField(choices=[[1, '1 = not at all willing to take risks'], [2, '2'], [3, '3'], [4, '4'], [5, '5'], [6, '6'], [7, '7'], [8, '8'], [9, '9'], [10, '10 = very willing to take risks']], widget=widgets.RadioSelect) nfcscore = models.IntegerField() nfcall = models.StringField() ismobile_touchstart = models.IntegerField(blank=True, initial=0) screenwidth = models.StringField(blank=True, initial='') screenheight = models.IntegerField(blank=True, initial=0) browser = models.StringField(blank=True, initial='') ace1 = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='Emotional abuse', widget=widgets.RadioSelect) ace2 = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='Physical abuse', widget=widgets.RadioSelect) ace3 = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='Sexual abuse', widget=widgets.RadioSelect) ace4 = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='Physical neglect', widget=widgets.RadioSelect) ace5 = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='Emotional neglect', widget=widgets.RadioSelect) ace6 = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='Household mental illness', widget=widgets.RadioSelect) ace7 = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='Property victimisation', widget=widgets.RadioSelect) ace8 = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='Peer victimisation', widget=widgets.RadioSelect) ace9 = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='Exposure to community violence', widget=widgets.RadioSelect) ace10 = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='Socioeconomic status', widget=widgets.RadioSelect) ace11 = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='Someone close had a bad accident or illness', widget=widgets.RadioSelect) ace12 = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='Below-average grades', widget=widgets.RadioSelect) ace13 = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='Parents always arguing', widget=widgets.RadioSelect) ace14 = models.StringField(choices=[['Yes', 'Yes'], ['No', 'No']], label='No good childhood friends', widget=widgets.RadioSelect) affectbased = models.IntegerField(choices=[[1, '1 = Strongly disagree'], [2, '2 = Moderately disagree'], [3, '3 = Slightly disagree'], [4, '4 = Slightly agree'], [5, '5 = Moderately agree'], [6, '6 = Strongly agree']], label='
I solved the task on a gut level
', widget=widgets.RadioSelect) deliberative = models.IntegerField(choices=[[1, '1 = Strongly disagree'], [2, '2 = Moderately disagree'], [3, '3 = Slightly disagree'], [4, '4 = Slightly agree'], [5, '5 = Moderately agree'], [6, '6 = Strongly agree']], label='I tried to solve the task mathematically
', widget=widgets.RadioSelect) checkup1 = models.StringField(choices=[['1', 'Points earned from two randomly chosen rounds'], ['3', 'Points earned from half of the rounds'], ['2', 'Points earned from all rounds']], label='My bonus payments for the study consist of ...
', widget=widgets.RadioSelect) checkup2 = models.StringField(choices=[['1', 'True'], ['2', 'False']], label='The number of loss boxes changes from round to another
', widget=widgets.RadioSelect) checkup3 = models.StringField(choices=[['2', 'Changes from round to round'], ['1', 'Always 32'], ['3', 'Depends on the number of loss boxes']], label='How many boxes in total are there in a round?
', widget=widgets.RadioSelect) prolificcode = models.StringField() handedness = models.StringField(choices=[['Right hand', 'Right hand'], ['Left hand', 'Left hand']], label='Which hand do you typically use to browse your mobile device?
', widget=widgets.RadioSelect) def live_open(player: Player, data): time = data['time'] # this is a single timestamp indicating when the message was sent to server touchtimes = data['touchtimes'] # these timestamps indicate when the card was touched starttime = data['starttime'] # this is a timestamp recorded on page, indicating when box moving started opened = data['opened'] # this is zero if swiped right player.chosencard += opened player.dectimes += str(int(time) - int(starttime)) + ';' player.xs += data['x'] + ';' player.ys += data['y'] + ';' # this should correspond to decision times player.touchtimes += touchtimes + ';' player.startleft += data['startleft'] + ';' # this is recorded just in case the polyarea calculations are wrong ### POLYAREA x = data['x'] y = data['y'] xx = x.split(',') xx = xx[:-1] xxx = map(float,xx) x = list(map(int,xxx)) yy = y.split(',') yy = yy[:-1] y = list(map(int,yy)) # flip the y coordinate y = list(reversed(y)) xy = [] j = 0 # append all the points in the curve except the tail points for jj in range(j,len(x)): if x[-1] < int(data['startleft']): # rejected when final x-coordinate is 150 px lower than startleft if x[jj] >= x[0]-150: xy.append([x[jj],y[jj]]) jjj = jj # stopping index else: # accepted when final x-coordinate is 150 px higher than startleft if x[jj] < x[0]+150: xy.append([x[jj],y[jj]]) jjj = jj # stopping index # close the curve to determine the polyarea xy.append([x[jjj],y[jjj]]) # append the end point xy.append([x[jjj],y[0]]) # append the point that is vertically upwards from the end point xy.append([x[0],y[0]]) # append the start point l = len(xy) s = 0.0 for i in range(l): j = (i+1)%l # keep index in [0,l) s += (xy[j][0] - xy[i][0])*(xy[j][1] + xy[i][1]) polyarea = -0.5*s player.polyareas += str(polyarea) + ';' response = {'openedcards': player.chosencard} # how many cards have been opened so far return {0: response} def checkup1_error_message(player: Player, value): if value != '1': return "Your answer is wrong. Hint: as the instructions state, two blocks will be randomly selected at the end of the study and the points from these blocks paid to you." def checkup2_error_message(player: Player, value): if value != '1': return "Your answer is wrong. Hint: there can be 1, 2, or 3 loss boxes within a round and this is randomised from round to another." def checkup3_error_message(player: Player, value): if value != '1': return "Your answer is wrong. Hint: the total number of boxes remains constant, only the number of loss boxes and gain boxes changes from round to another." class Welcome(Page): form_model = 'player' form_fields = ['prolificcode'] @staticmethod def is_displayed(player: Player): session = player.session return player.round_number == 1 and session.config['pilot'] == 0 @staticmethod def vars_for_template(player: Player): session = player.session return dict( icons = session.config['icons'] ) class Instructions(Page): form_model = 'player' form_fields = ['checkup1', 'checkup2', 'checkup3'] @staticmethod def is_displayed(player: Player): session = player.session if session.config['icons'] == 'smileys': return player.round_number < 4 else: return player.round_number == 1 @staticmethod def vars_for_template(player: Player): session = player.session return dict( roundi = player.round_number, icons = session.config['icons'] ) @staticmethod def js_vars(player: Player): session = player.session return dict(boxes = C.BOXES, icons = session.config['icons']) class RoundStart(Page): form_model = 'player' @staticmethod def vars_for_template(player: Player): session = player.session participant = player.participant # THE FOLLOWING VARIABLES ARE SET ONLY AT THE START OF THE ROUND # THESE HAVE BEEN MODIFIED FROM THE BLOCKSTART PAGE OF THE OLD APP # determine the amount of loss cards, gainamount, and lossamount trialnumber = participant.indexlist[player.round_number-1] # vector of how many loss cards in each round [1,2,3,1,2,3,1,2,3,...] losscardsvec = int(C.NUM_ROUNDS / len(C.LOSSCARDS)) * C.LOSSCARDS # how many loss cards in this specific round player.roundlosscards = losscardsvec[trialnumber - 1] # which specific card numbers are loss cards in this block import random numbs = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32] losscardlist = [] num1 = random.choice(numbs) losscardlist.append(num1) numbs.remove(num1) if (player.roundlosscards >= 2): num2 = random.choice(numbs) losscardlist.append(num2) numbs.remove(num2) if (player.roundlosscards == 3): num3 = random.choice(numbs) losscardlist.append(num3) # which one is the first loss card because the block ends in this card player.firstlosscard = min(losscardlist) player.losscardlist = str(losscardlist) # gain amount in this block based on trialnumber (excel file) 10,10,10,20,20,20,30,30,30,10,10,10,... gainsvec = int(C.NUM_ROUNDS / len(C.GAINAMOUNTS)) * C.GAINAMOUNTS player.roundgain = gainsvec[trialnumber - 1] # loss amount in this block based on trialnumber (excel file) 250,250,250,250,250,250,250,250,250,500,500,... lossvec = int(C.NUM_ROUNDS / len(C.LOSSAMOUNTS)) * C.LOSSAMOUNTS player.roundloss = lossvec[trialnumber - 1] return dict( roundi = player.round_number, example1 = player.roundgain*6, example2 = max(player.roundgain*6 - player.roundloss, 0), pilot = session.config['pilot'], icons = session.config['icons'] ) class Decision(Page): form_model = 'player' form_fields = ['stopped'] live_method = 'live_open' @staticmethod def vars_for_template(player: Player): session = player.session return dict( roundi = player.round_number, icons = session.config['icons'] ) @staticmethod def js_vars(player: Player): session = player.session # create the number of boxes opened for js_vars (same as blockround in the old app) # ??? return dict( gainvalue = player.roundgain, openedcards = player.chosencard, # this is only used to display the initial row of cards roundi = player.round_number, firstlosscard = player.firstlosscard, boxes = C.BOXES, icons = session.config['icons'], losscards = player.roundlosscards ) @staticmethod def before_next_page(player: Player, timeout_happened): if player.chosencard == player.firstlosscard: # if round ended in turning over loss card player.endedinloss = True player.chosencard -= 1 # subtract one card from the record of chosen cards player.profit = max(0,player.chosencard * player.roundgain - player.roundloss) else: # if round ended in stopping player.endedinloss = False player.profit = player.chosencard * player.roundgain class RoundEnd(Page): form_model = 'player' @staticmethod def vars_for_template(player: Player): session = player.session # table of block results tabledata = "" for j in range(player.round_number): j_player = player.in_round(j+1) if session.config['icons'] == 'smileys': tabledata += "Did a parent or an adult in your home ever swear at you, insult you, or put you down?
' ace2 = 'Did a parent or an adult in your home ever hit, beat, kick, or physically hurt you in any way?
' ace3 = 'Did you experience unwanted sexual contact (such as fondling or oral/anal/vaginal intercourse/penetration)?
' ace4 = 'Did you feel that you did not have enough to eat, had to wear dirty clothes, or had no one to protect or take care of you?
' ace5 = 'Did you feel that no one in your family loved you or thought you were special?
' ace6 = 'Did you live with anyone who was depressed, mentally ill, or attempted suicide?
' ace7 = 'Did you ever experience robbery, theft, or vandalism from your peers outside of your household (such as from someone from your school)?
' ace8 = 'Were you ever assaulted, intimidated, or seriously harassed by your peers outside of your household (such as by someone from your school)?
' ace9 = 'Did you experience or witness violence or other serious crime in the neighbourhood or community in which you lived?
' ace10 = 'Was your socioeconomic status below average?
' ace11 = 'Did someone close to you experience a bad accident or illness?
' ace12 = 'Did you think that your grades in school were below average?
' ace13 = 'Did your parents or adults in your home ever hit, punch, beat, or threaten to harm each other?
' ace14 = 'Did you feel that you had no good friends while growing up?
' return dict( ace1 = ace1, ace2 = ace2, ace3 = ace3, ace4 = ace4, ace5 = ace5, ace6 = ace6, ace7 = ace7, ace8 = ace8, ace9 = ace9, ace10 = ace10, ace11 = ace11, ace12 = ace12, ace13 = ace13, ace14 = ace14 ) class FinalPage(Page): form_model = 'player' @staticmethod def is_displayed(player: Player): return player.round_number == C.NUM_ROUNDS @staticmethod def vars_for_template(player: Player): session = player.session return dict( prolificurl = session.config['prolificurl'], icons = session.config['icons'] ) page_sequence = [Welcome, Instructions, RoundStart, Decision, RoundEnd, RewardPage, NFC, ACE, FinalPage]