from otree.api import * import random import datetime doc = """ Tasks app """ class C(BaseConstants): NAME_IN_URL = 'tasks' PLAYERS_PER_GROUP = None NUM_ROUNDS = 5 emotion_fields = ['sad', 'calm', 'surprise'] emotion_choices = [(1, 'Not at all'), (2, 'A little'), (3, 'Moderately'), (4, 'Very much')] stress_fields = ['stresslevel'] stress_choices = [(1, 'Relaxed'), (2, 'Slightly Relaxed'), (3, 'Slightly Stressed'), (4, 'Very stressed')] baseline_videos = { "baseline1": { "nature1": ("Nature Video", "i-xRCx7llzY"), "landscape1": ("Landscape without voiceover", "KPi9aN6SJPo"), "meditation1": ("Guided meditation", "nVmdZ1TqLis"), }, "baseline2": { "nature2": ("Nature Video", "VUySkWPL7M4"), "landscape2": ("Landscape without voiceover", "4Qpdwnpk198"), "meditation2": ("Guided meditation", "Qi2HhkbnULc"), } } rest_video = "PA4Qa9H6j0w" class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): curcond = models.StringField() # time Taskinstruction_offset = models.StringField() Video_offset = models.StringField() mast_offset = models.StringField() control_offset = models.StringField() rest_offset = models.StringField() Moodgrid_offset = models.StringField() baseline_choice = models.StringField() #form fields for the mood grid sad = models.IntegerField( choices=C.emotion_choices, widget=widgets.RadioSelectHorizontal ) calm = models.IntegerField( choices=C.emotion_choices, widget=widgets.RadioSelectHorizontal ) surprise = models.IntegerField( choices=C.emotion_choices, widget=widgets.RadioSelectHorizontal ) stresslevel = models.IntegerField( choices=C.stress_choices, widget=widgets.RadioSelectHorizontal ) # PAGES class Taskinstructions(Page): @staticmethod def is_displayed(player: Player): return player.participant.experimental_sequence[player.round_number - 1] in ['baseline1', 'baseline2', 'mast', 'control', 'rest'] @staticmethod def vars_for_template(player: Player): curcond = player.participant.experimental_sequence[player.round_number - 1] vidtype = { "baseline1": "Relaxation", "baseline2": "Relaxation"}.get(curcond, "") videmotion = { "baseline1": "Relaxation", "baseline2": "Relaxation"}.get(curcond, "") curinst = { "baseline1": "Now you will watch a video that may evoke feelings of Relaxation. The video will be 10 mins long. You can choose which video you want to see. Please choose the option that you think would best relax you.", "baseline2": "Now you will watch a video that may evoke feelings of Relaxation. The video will be 10 mins long. You can choose which video you want to see. Please choose the option that you think would best relax you.", "mast": "You will now complete the task. The experimenter will join in a few seconds.", "control": "You will not complete the task. The experimenter will join in a few seconds.", "rest": "You will now have a rest period where you will do a small breathing exercise for 5 minutes. Press Next button for instructions" } video_options = [ { "title": "🐾 Nature Video", "description": "A calming clip of animals with an expert explaining their behaviour." }, { "title": "🏔️ Landscape without Voiceover", "description": "A first-person point-of-view journey through beautiful landscapes, beaches, or a small city in Japan, accompanied by pleasant music." }, { "title": "🧘 Guided Meditation", "description": "A 10-minute guided meditation exercise where an expert guides you through a session." }, ] return { 'curcond': curcond, 'vidtype': vidtype, 'videmotion': videmotion, 'curinst': curinst[curcond], 'video_options': video_options if curcond in ['baseline1', 'baseline2'] else [], } @staticmethod def before_next_page(player: Player, timeout_happened): player.Taskinstruction_offset = datetime.datetime.now().isoformat() player.curcond = player.participant.experimental_sequence[player.round_number - 1] if 'experimental_sequence' in player.participant.vars: print(player.participant.vars['experimental_sequence']) class MAST2(Page): @staticmethod def is_displayed(player: Player): return player.participant.experimental_sequence[player.round_number - 1] in ['mast', 'control'] @staticmethod def vars_for_template(player: Player): curcond = player.participant.experimental_sequence[player.round_number - 1] return dict(curcond=curcond) class MAST3(Page): @staticmethod def is_displayed(player: Player): return player.participant.experimental_sequence[player.round_number - 1] in ['mast', 'control'] @staticmethod def vars_for_template(player: Player): curcond = player.participant.experimental_sequence[player.round_number - 1] return dict(curcond=curcond) class BaselineChoice(Page): form_model = 'player' form_fields = ['baseline_choice'] @staticmethod def is_displayed(player: Player): return player.participant.experimental_sequence[player.round_number - 1] in ['baseline1', 'baseline2'] @staticmethod def vars_for_template(player: Player): curcond = player.participant.experimental_sequence[player.round_number - 1] available_videos = C.baseline_videos[curcond] return dict(videos=available_videos) class VideoInstructions(Page): @staticmethod def is_displayed(player: Player): return player.participant.experimental_sequence[player.round_number - 1] in ['baseline1', 'baseline2'] @staticmethod def vars_for_template(player: Player): instructions = { "nature1": { "title": "🐾 Nature Video", "body": "As you watch this nature clip, feel free to settle in and let your body go heavy. There are no surprises here, so you can fully release any tension in your muscles. Simply allow your attention to rest on the sights and sounds of the video. If you notice your mind drifting toward your daily 'to-do' list or other thoughts, gently guide your focus back to the screen and let yourself be fully immersed in the experience." }, "nature2": { "title": "🐾 Nature Video", "body": "As you watch this nature clip, feel free to settle in and let your body go heavy. There are no surprises here, so you can fully release any tension in your muscles. Simply allow your attention to rest on the sights and sounds of the video. If you notice your mind drifting toward your daily 'to-do' list or other thoughts, gently guide your focus back to the screen and let yourself be fully immersed in the experience." }, "landscape1": { "title": "🏔️ Landscape without Voiceover", "body": "As you begin, find a comfortable position and let your body settle. You are about to see a peaceful scene from a small town in Japan. There are no surprises, so you can fully relax. If you enjoy the visual details, feel free to keep your eyes open and let your gaze rest on the landscape. If you find it more relaxing to listen to the music and imagine the scene, you may close your eyes. If you notice your mind wandering to work or other tasks, simply notice that thought and gently guide your focus back to the sounds of the music or the movement on the screen. Allow yourself to be fully present with the journey." }, "landscape2": { "title": "🏔️ Landscape without Voiceover", "body": "As you begin, find a comfortable position and let your body settle. You are about to see a peaceful scene from a small town in Japan. There are no surprises, so you can fully relax. If you enjoy the visual details, feel free to keep your eyes open and let your gaze rest on the landscape. If you find it more relaxing to listen to the music and imagine the scene, you may close your eyes. If you notice your mind wandering to work or other tasks, simply notice that thought and gently guide your focus back to the sounds of the music or the movement on the screen. Allow yourself to be fully present with the journey." }, "meditation1": { "title": "🧘 Guided Meditation", "body": "For this next part, you'll be listening to a guided meditation. There are no surprises here, so feel free to settle into a comfortable position and let your muscles soften. You may find it easiest to close your eyes to focus on the guidance, but if you prefer to keep them open with a soft gaze, that is perfectly fine too — simply do what feels most natural for you. As you listen, you might notice your mind starting to wander toward other thoughts or tasks. When that happens, there's no need to worry; just gently acknowledge the thought and bring your attention back to the sound of the voice and the rhythm of your breath. Allow yourself this time to simply be present." }, "meditation2": { "title": "🧘 Guided Meditation", "body": "For this next part, you'll be listening to a guided meditation. There are no surprises here, so feel free to settle into a comfortable position and let your muscles soften. You may find it easiest to close your eyes to focus on the guidance, but if you prefer to keep them open with a soft gaze, that is perfectly fine too — simply do what feels most natural for you. As you listen, you might notice your mind starting to wander toward other thoughts or tasks. When that happens, there's no need to worry; just gently acknowledge the thought and bring your attention back to the sound of the voice and the rhythm of your breath. Allow yourself this time to simply be present." }, } choice = player.baseline_choice all_videos = {**C.baseline_videos["baseline1"], **C.baseline_videos["baseline2"]} _, video_path = all_videos[choice] return dict( inst=instructions.get(choice, {"title": "", "body": ""}), video_path=video_path ) # class Video(Page): # @staticmethod # def is_displayed(player: Player): # return player.participant.experimental_sequence[player.round_number - 1] in ['baseline1', 'baseline2'] # @staticmethod # def vars_for_template(player: Player): # all_videos = {**C.baseline_videos["baseline1"], **C.baseline_videos["baseline2"]} # _, video_path = all_videos[player.baseline_choice] # return dict(curvid=video_path) # # curcond = player.participant.experimental_sequence[player.round_number -1] # # curvid = { # # "baseline1" : 'tasks/baseline1_video.mp4', # # "baseline2" : 'tasks/baseline2_video.mp4' # # }[curcond] # # return dict(curvid=curvid) # @staticmethod # def before_next_page(player, timeout_happened): # player.Video_offset = datetime.datetime.now().isoformat() class Video(Page): @staticmethod def is_displayed(player: Player): return player.participant.experimental_sequence[player.round_number - 1] in ['baseline1', 'baseline2'] @staticmethod def vars_for_template(player: Player): all_videos = {**C.baseline_videos["baseline1"], **C.baseline_videos["baseline2"]} _, video_path = all_videos[player.baseline_choice] is_meditation = 'yes' if player.baseline_choice in ['meditation1', 'meditation2'] else 'no' return dict(curvid=video_path, is_meditation=is_meditation) # class MAST(Page): # def is_displayed(player): # return player.participant.experimental_sequence[player.round_number - 1] == 'mast' # def before_next_page(player: Player, timeout_happened): # player.mast_offset = datetime.datetime.now().isoformat() # class Control(Page): # def is_displayed(player): # return player.participant.experimental_sequence[player.round_number - 1] == 'control' # def before_next_page(player: Player, timeout_happened): # player.control_offset = datetime.datetime.now().isoformat() class MAST4(Page): @staticmethod def is_displayed(player: Player): return player.participant.experimental_sequence[player.round_number - 1] in ['mast', 'control'] @staticmethod def vars_for_template(player: Player): curcond = player.participant.experimental_sequence[player.round_number - 1] return dict(curcond=curcond) class MAST5(Page): @staticmethod def is_displayed(player: Player): return player.participant.experimental_sequence[player.round_number - 1] in ['mast', 'control'] @staticmethod def vars_for_template(player: Player): curcond = player.participant.experimental_sequence[player.round_number - 1] return dict(curcond=curcond) class Rest(Page): def is_displayed(player): return player.participant.experimental_sequence[player.round_number - 1] == 'rest' @staticmethod def vars_for_template(player: Player): return dict(curvid=C.rest_video) def before_next_page(player: Player, timeout_happened): player.rest_offset = datetime.datetime.now().isoformat() # @staticmethod # def vars_for_template(player: Player): # curcond = player.participant.experimental_sequence[player.round_number - 1] # curinst = { # "stress_taskMAST" : "You will now complete the task" # "stress_taskControl" : "You will not complete the task" # } # return { # 'curcond': curcond, # 'curinst': curinst[curcond] # } class MoodGrid(Page): form_model = 'player' # This links the form to the 'Player' model """ def is_displayed(player: Player): # Display the page if the current condition is either 'fear', 'disgust', or 'baseline' return player.participant.experimental_sequence[player.round_number - 1] in ['fear', 'disgust', 'baseline'] """ def get_form_fields(player: Player): # Determine the current condition for this round curcond = player.participant.experimental_sequence[player.round_number - 1] curfields = C.emotion_fields if curcond in ['mast', 'control']: # If the current condition is 'nback', return n-back task fields curfields = C.stress_fields else: curfields = C.emotion_fields return curfields @staticmethod def vars_for_template(player: Player): # Determine the current condition for this round return dict(curcond = player.participant.experimental_sequence[player.round_number - 1]) @staticmethod def before_next_page(player, timeout_happened): player.Moodgrid_offset = datetime.datetime.now().isoformat() def creating_session(subsession): participants = subsession.session.get_participants() conditions = { "baseline1", "baseline2", "mast", "control", "rest"} for part in participants: # Randomize baseline order baselines = ['baseline1', 'baseline2'] random.shuffle(baselines) # Randomize stress task order stress_tasks = ['mast', 'control'] random.shuffle(stress_tasks) # Build fixed sequence condition_sequence = [ baselines[0], # Round 1: baseline video stress_tasks[0], # Round 2: stress task 'rest', # Round 3: rest (always) baselines[1], # Round 4: baseline video stress_tasks[1] # Round 5: stress task ] part.experimental_sequence = condition_sequence def vars_for_admin_report(subsession): players = subsession.get_players() rows = [] for p in players: seq = p.participant.experimental_sequence current_round = p.round_number - 1 current_cond = seq[current_round] if current_round < len(seq) else "N/A" rows.append({ 'participant': p.participant.code, 'current_round': p.round_number, 'current_condition': current_cond, 'full_sequence': ' → '.join(seq), }) return dict(rows=rows) # def creating_session(subsession): # participants = subsession.session.get_participants() # conditions = ['fear', 'disgust', 'nback'] # baseline_orders = [('baseline1', 'baseline2'), ('baseline2', 'baseline1')] # for part in participants: # random.shuffle(conditions) # chosen_order = random.choice(baseline_orders) # condition_sequence = [] # # alternating baselines: # for i, condition in enumerate(conditions): # baseline_to_use = chosen_order[i % 2] # condition_sequence.append(baseline_to_use) # condition_sequence.append(condition) # # add a final baseline at the end, alternating again # final_baseline = chosen_order[len(conditions) % 2] # condition_sequence.append(final_baseline) # part.experimental_sequence = condition_sequence page_sequence = [ Taskinstructions, MAST2, MAST3, BaselineChoice, VideoInstructions, Video, MAST4, MAST5, MAST4, MAST5, MAST4, MAST5, MAST4, MAST5, MAST4, Rest, MoodGrid ]