Skip pages and timer for multiple rounds

440 views
Skip to first unread message

Jonas

unread,
Sep 1, 2021, 10:05:06 AM9/1/21
to oTree help & discussion
Hello all!

Thanks in advance for anyone taking a look at my issue and making suggestions for a better implementation.
See my simplified code snippet at the bottom.

I am setting up a sequential task for a player.
I want to play multiple rounds while having a timer running.
-> e.g. a player has 60s to play as many rounds as he/she can

(1)
In the first round I want to show an intro page that starts the timer. However, in the following rounds I don't want the player to see the Intro page anymore.
  • If I use the is_displayed function, the timer is automatically set to 0 once the 2nd rounds starts. Even if there is still time remaining.
  • Without the is_displayed function the sequential task continues in a new round with the according time remaining, but the player has to click through the Intro page over and over again. As the task is time sensitive I would like to avoid this.
  • I am guessing the "player.expiry" is set to 0 when I start a round without running the code in the Intro page. However, I have not found a way to work around this.
(2)
At the end of each round there are calculations to be done. I have implemented this in the Results Page. 
  • In the running version I dont want the player to see the results page. Once again if I use the is_displayed function no calculations take place.
  • Is there a way to hide the results page from the player but still do the calculations?

I have the feeling both problems could have a similar solution. I am thankful for any suggestions on how to implement this!

Greetings
Jonas


from time import thread_time
from otree.api import *

doc = """
Your app description
"""

class Constants(BaseConstants):
    name_in_url = 'timer'
    players_per_group = None
    num_rounds = 50
    timer_text = "Time to complete the task:"

class Subsession(BaseSubsession):
    pass

class Group(BaseGroup):
    pass

class Player(BasePlayer):
    startTime = models.FloatField(initial=0)
    timeLeft = models.FloatField(initial=0)
    expiry = models.FloatField(initial=60)

#FUNCTIONS
def time_is_displayed(player: Player):
    import time
    return player.expiry - time.time() > 0


def get_timeout_seconds(player: Player):
    import time
    return player.expiry - time.time()
  
#PAGES
class Intro(Page):
#After the 1st round I don't want to 
#show the Intro Page anymore, but
#if I use the following 3 lines of code 
#in the Intro Page the remaining time 
#is set to 0 once I start the 2nd round
    @staticmethod
    def is_displayed(player):
        return player.round_number == 1
    
    @staticmethod
    def before_next_page(player: Player, timeout_happened):
        import time

        prev_player = player.in_round(1)
        player.startTime = time.time()
        player.expiry = prev_player.startTime + 60

class Input_from_Player(Page):
    @staticmethod
    def is_displayed(player):
        return get_timeout_seconds(player) > 3

    time_is_displayed = time_is_displayed
    get_timeout_seconds = get_timeout_seconds
    timer_text = Constants.timer_text

    #...
    #Player has to give an input
    #on which basis a calculation
    #is done on the page "Results"
    #...

class Results(Page):
    @staticmethod
    def vars_for_template(player: Player):
        sum = 0

            #...
            #here, a calulation takes place,
            #that needs to be done at the
            #end of every round
            #
            #However, the page must not be 
            #shown to the player
            #...

page_sequence = [
    Intro, 
    Input_from_Player, 
    Results
    ]

Chris @ oTree

unread,
Sep 1, 2021, 10:35:10 AM9/1/21
to oTree help & discussion
There is an example in otree-snippets showing how to do a multi-page timeout here: https://www.otreehub.com/projects/?featured=1

(1) You should set the expiry on the participant, not the player. since the participant lasts across all rounds, whereas each round has a separate player object: https://otree.readthedocs.io/en/latest/conceptual_overview.html#participant
and then you can skip that page in the following rounds
(2) put that code in before_next_page in  Input_from_Player

by the way, your  time_is_displayed function will never be used. If you want to add it to a class, you need to assign it to a known attribute, like this:

class MyPage(Page):
    is_displayed = time_is_displayed

Jonas

unread,
Sep 1, 2021, 12:34:42 PM9/1/21
to oTree help & discussion
Hello Chris,

thank you very much for your insight!
I was able to fix both issues.

Greetings
Jonas

Reply all
Reply to author
Forward
0 new messages