from otree.api import * import random, itertools, time # Import verification files import static_data.createVerification as vfb #Script for verification file import static_data.createBonusFiles as bf # Script for bonus files # Import information about treatments import static_data.treatments as treatmentData # Also contains information for bonus files doc = """ Your app description """ class Constants(BaseConstants): name_in_url = 'general' players_per_group = None num_rounds = 1 class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): # Prolific ID prolID = models.StringField( label="Please enter your Prolific ID. This allows us to approve your submission and transfer your payment to your account:", ) # Privacy Policy consent_privacy = models.BooleanField( label="I have read the privacy policy and consent with them:", choices=[[True, "Yes"], [False, "No"]], ) # Consent attention consent_attention = models.BooleanField( choices=[[True, "Yes"], [False, "No"]], blank=True, ) # Consent to screen dimensions consent_screen = models.BooleanField( choices=[[True, "Yes, looks good."], [False, "No, does not work."]], blank=True, ) screenWidth = models.IntegerField() screenHeight = models.IntegerField() # Verification and bonus file information password_verification = models.StringField() password_bonus = models.StringField() # Download of verification file downloadLeft = models.BooleanField() downloadVerification = models.BooleanField(initial=False) # Download of BonusFile download_bool = models.BooleanField( initial=False, ) # Save name of Bonus File user_chosen_decision = models.StringField(initial="None") # Treatment selection for testing only study = models.IntegerField( widget=widgets.RadioSelect ) # FUNCTIONS def study_choices(player): choices = [] for idx, info in treatmentData.treatments.items(): choices.append([idx, info[0]]) return choices def creating_session(subsession): if subsession.round_number == 1: studies = [] for idx in treatmentData.treatments: studies.append(idx) # Initialize sutdy iterator subsession.session.studyIterator = itertools.cycle(studies) # Initialize dropout variable subsession.session.dropouts = [] # Initialize completes subsession.session.completes = { study: 0 for study in studies } # Initialize actives subsession.session.actives = { study: 0 for study in studies } # PAGES class treatmentSelection(Page): form_model = "player" form_fields = [ "study" ] @staticmethod def is_displayed(player): return player.session.config["studySelection"] # Remove for real experiment @staticmethod def app_after_this_page(player, upcoming_apps): if player.study < 5: player.participant.demo = False return # Set basic variables as otherwise code fails player.consent_privacy = True player.consent_attention = True player.consent_screen = True if player.study == 8: player.participant.treatment = 0 #Basic Walking # Set important variables for Walking Task player.participant.decoloring = { 'isClicked': [ True, True, True, True, True, True, True, True, True, True, True, True, True, True, True ], 'continuation': [ 100, 100, 100, 100, 100, 0, 100, 100, 0, 0, 100, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, ], 'drawn': [-1], 'indArray': [ [0], [1, 2], [3, 4, 5], [6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19, 20] ], 'path': {}, 'pdStopDecision' : {}, 'clickCounter': 0, 'currentNode': -1, 'nodeChoiceShow': False, 'showHelp': False, 'nonStandardBalls': 0, } player.participant.resolutionRound = 0 ## Initialize for Walking Tasks player.participant.walkingPath = {(idx+1): [0]*6 for idx in range(10)} player.participant.coinTossOutcome = {(idx+1): [0]*6 for idx in range(10)} player.participant.continuationDec = {(idx+1): [3000]*6 for idx in range(10)} ## Initialize for practice round player.participant.walkingPathTrial = [0]*6 player.participant.coinTossOutcomeTrial = [0]*6 player.participant.continuationDecTrial = [3000]*6 else: player.participant.treatment = player.study - 5 player.participant.treatment_name = treatmentData.treatments[player.study][1] player.participant.expiry = time.time() + 24 * 60 * 60 # 24h player.participant.timeoutHappened = False # Initialize # Get data for bonus file treatmentName = treatmentData.treatments[player.study][1] treatmentColor = treatmentData.treatments[player.study][2] plotColor = treatmentData.treatments[player.study][3] bfData = getattr(__import__('static_data.treatments', fromlist=[treatmentName]), treatmentName) # Generate passwords if player.session.config['testing']: player.password_verification = "Loewe50" player.password_bonus = "Loewe50" elif player.session.config['studySelection']: player.password_verification = "repeatRisk22" player.password_bonus = "repeatRisk22" else: player.password_verification = vfb.createPassword(5) player.password_bonus = vfb.createPassword(5) player.participant.color = treatmentColor # Name of color for display player.participant.plotColor = plotColor # Name of draw color player.participant.password_verification = player.password_verification player.participant.password_bonus = player.password_bonus player.participant.bf = 100 # Set some number // Not important which player.participant.demo = True ## Dictionary with winning Sides try: repetitionsFirstTask = bfData[0][2] raise TypeError except: firstTask = bfData[0][0] repetitionsSecondTask = bfData[1][2] player.participant.winningSideDict = { (key+1): vfb.coinSide(5) for key in range(repetitionsSecondTask) } player.participant.winningSideDict[firstTask] = vfb.coinSide(5) ## Dictionary with card draws player.participant.cardNumbersDictTrial = vfb.cardNumbers(5) player.participant.cardNumbersDict = { 'Walking_' + str(i+1): vfb.cardNumbers(5) for i in range(repetitionsSecondTask) } player.participant.cardNumbersDict["Decoloring"] = vfb.cardNumbers(5) if player.study == 5: return 'basic' elif player.study == 6: return 'path' elif player.study == 7: return 'rand' elif player.study == 8: return 'basicWalking' class welcomePage(Page): form_model = "player" form_fields = [ "prolID", "consent_privacy", "consent_attention", "consent_screen", "screenWidth", "screenHeight", "downloadVerification" ] @staticmethod def is_displayed(player): try: # Ensure that random information is only generated once print(player.participant.password_verification) except: # Treatment assignment if not player.session.config['studySelection']: try: player.study = player.session.dropouts.pop() # Check if there have been any drop-outs whose treatments should be reassigned except: # Dropouts is empty tmpStudy = -1 counter = 0 # to break infinite loop if all sessions are full - should not occur while tmpStudy < 0 or counter <50: tmpStudy = next(player.session.studyIterator) tmpCompletes = player.session.completes[tmpStudy] tmpActives = player.session.actives[tmpStudy] if tmpCompletes + tmpActives >= player.session.config["maxPartStudy"]: # At most 80 completes plus actives tmpStudy = -1 counter += 1 if counter == 50: # break infinite loop player.study = next(player.session.studyIterator) player.study = tmpStudy # Get data for bonus file treatmentName = treatmentData.treatments[player.study][1] treatmentColor = treatmentData.treatments[player.study][2] plotColor = treatmentData.treatments[player.study][3] bfData = getattr(__import__('static_data.treatments', fromlist=[treatmentName]), treatmentName) # Generate passwords if player.session.config['testing']: player.password_verification = "Loewe50" player.password_bonus = "Loewe50" elif player.session.config['studySelection']: player.password_verification = "repeatRisk22" player.password_bonus = "repeatRisk22" else: player.password_verification = vfb.createPassword(5) player.password_bonus = vfb.createPassword(5) player.participant.color = treatmentColor # Name of color for display player.participant.plotColor = plotColor # Name of draw color player.participant.password_verification = player.password_verification player.participant.password_bonus = player.password_bonus ## Dictionary with winning Sides try: repetitionsFirstTask = bfData[0][2] raise TypeError except: firstTask = bfData[0][0] repetitionsSecondTask = bfData[1][2] player.participant.winningSideDict = { (key+1): vfb.coinSide(5) for key in range(repetitionsSecondTask) } player.participant.winningSideDict[firstTask] = vfb.coinSide(5) ## Dictionary with card draws player.participant.cardNumbersDictTrial = vfb.cardNumbers(5) player.participant.cardNumbersDict = { 'Walking_' + str(i+1): vfb.cardNumbers(5) for i in range(repetitionsSecondTask) } player.participant.cardNumbersDict["Decoloring"] = vfb.cardNumbers(5) # Write verification PDF player.participant.bonusFileDict = bf.createBonusFilePDFs( bfData, player.participant.password_bonus, player.participant.id_in_session) vfb.createVerificationPDF( player.participant.bonusFileDict, player.participant.winningSideDict, player.participant.cardNumbersDict, player.password_verification, player.password_bonus, player.participant.id_in_session, player.study in [2,4] ) if player.study >= 5: # Set basic variables as otherwise code fails player.consent_privacy = True player.consent_attention = True player.consent_screen = True if player.study == 8: player.participant.treatment = 0 #Basic Walking else: player.participant.treatment = player.study - 5 player.participant.treatment_name = treatmentData.treatments[player.study][1] player.participant.expiry = time.time() + 24 * 60 * 60 # 24h player.participant.timeoutHappened = False # Initialize return False else: return True @staticmethod def before_next_page(player, timeout_happened): participant = player.participant participant.treatment = player.study participant.treatment_name = treatmentData.treatments[player.study][1] # Expiry time if player.session.config["studySelection"]: participant.expiry = time.time() + 24 * 60 * 60 # 24h else: participant.expiry = time.time() + player.session.config['dropoutTime'] participant.timeoutHappened = False # Initialize # Actives player.session.actives[participant.treatment] = player.session.actives[participant.treatment] + 1 if (player.consent_privacy == False) or (player.consent_attention == False) or (player.consent_screen == False): player.session.dropouts.append(player.study) player.session.actives[participant.treatment] = player.session.actives[participant.treatment] - 1 return @staticmethod def vars_for_template(player: Player): # Randomly determine if download button is left or right random.seed(a= None) # Use system time as seed rnd = random.randint(0,1) if rnd == 0: player.downloadLeft = True elif rnd == 1: player.downloadLeft = False id = player.participant.id_in_session folder = "bonusFiles/" + str(id) + "/" showPW = "" if player.session.config["studySelection"]: showPW = player.password_verification return dict( download_link= folder + "Verification.pdf", testing = player.session.config["testing"], touristVersion = player.session.config["studySelection"], showPW = showPW, ) class exitPage(Page): @staticmethod def is_displayed(player): return (player.consent_privacy == False) or (player.consent_attention == False) or (player.consent_screen == False) class yourEarnings(Page): form_model = "player" form_fields = ["download_bool", "user_chosen_decision"] @staticmethod def is_displayed(player): return ((player.consent_privacy == True) and (player.consent_attention == True) and (player.consent_screen == True) and not player.participant.timeoutHappened) @staticmethod def live_method(player, data): player.user_chosen_decision = data return {player.id_in_group: data} @staticmethod def before_next_page(player, timeout_happened): if timeout_happened: player.participant.timeoutHappened = True else: player.participant.bf = player.user_chosen_decision user_chosen_decision_no = int( player.participant.bf.replace("BonusFile", "") ) player.participant.bfNumber = user_chosen_decision_no @staticmethod def vars_for_template(player: Player): download_links = [] id = player.participant.id_in_session folder = "bonusFiles/" + str(id) + "/" for file in player.participant.bonusFileDict: download_links.append( [file, f"BonusFile{file}", folder + f"BonusFile{file}.pdf"] ) showPW = "" if player.session.config["studySelection"]: showPW = player.password_bonus return dict( download_links=download_links, testing = player.session.config["testing"], touristVersion = player.session.config["studySelection"], showPW = showPW, ) def error_message(player: Player, values): if values["download_bool"] == False: msg = "Please download the file, as you won't have access to it later on." elif values["user_chosen_decision"] == "": msg = "Please decide for a file and save it to a convenient location." else: msg = "" return msg @staticmethod def js_vars(player:Player): return dict( min_time_on_page = player.session.config["min_time_on_page_instructions"], user_chosen_decision = player.user_chosen_decision, testing = player.session.config["testing"], min_time_before_download = player.session.config["min_time_before_download"] ) @staticmethod def get_timeout_seconds(player): participant = player.participant return participant.expiry - time.time() class structure(Page): @staticmethod def is_displayed(player): return ((player.consent_privacy == True) and (player.consent_attention == True) and (player.consent_screen == True) and not player.participant.timeoutHappened) @staticmethod def vars_for_template(player: Player): return dict( user_chosen_decision = player.participant.bf, testing = player.session.config["testing"], ) @staticmethod def get_timeout_seconds(player): participant = player.participant return participant.expiry - time.time() @staticmethod def before_next_page(player, timeout_happened): if timeout_happened: player.participant.timeoutHappened = True player.session.dropouts.append(player.participant.treatment) player.session.actives[player.participant.treatment] = player.session.actives[player.participant.treatment] - 1 page_sequence = [ treatmentSelection, welcomePage, exitPage, yourEarnings, structure, ]