oTree Forum >

Results.is_displayed line 1: undefined name 'get_timeout_seconds'

#1 by aarumardi

What can I do if I find this message? 

Results.is_displayed line 1: undefined name 'get_timeout_seconds'

#2 by Chris_oTree

It’s saying that in is_displayed you used get_timeout_seconds which is not defined.

#3 by aarumardi

where should I define get_timeout_seconds?

#4 by BonnEconLab

Reading the documentation helps: https://otree.readthedocs.io/en/latest/timeouts.html#get-timeout-seconds.

In your case, it should be something like the following:

class Results(Page):

    def get_timeout_seconds(player):

#5 by aarumardi (edited )

I'm trying to create multiple rounds with a timeout with otree studio, however the timer stop when it is going to the next round, is there any specific code I should do?

#6 by BonnEconLab

If I understand you correctly, you would like the timeout to span multiple rounds.

Again, reading the documentation helps. There is an example for the code that does what you desire on the very page that I linked to: https://otree.readthedocs.io/en/latest/timeouts.html#timeouts-that-span-multiple-pages.

Following the documentation, let’s assume that you have two HTML templates, Start.html and Page1.html. For instance,


{{ block title }}


{{ endblock }}

{{ block content }}

<p>Press the button when you’re ready to start.</p>

{{ next_button }}

{{ endblock }}


{{ block title }}

Round No.&nbsp;{{ player.round_number }}

{{ endblock }}

{{ block content }}

{{ formfields }}

{{ next_button }}

{{ endblock }}

In the settings.py file, include


An __init__.py file like the following does the job:

from otree.api import *
import time

doc = """
    An app to illustrate a countdown that spans multiple rounds.

class C(BaseConstants):

    NAME_IN_URL = 'multiround_timeout'
    NUM_ROUNDS = 10  # Set to the desired number of rounds
    TIMEOUT_SECONDS_TOTAL = 42  # Set to the desired total timeout, e.g., 30 * 60 for 30 minutes

class Subsession(BaseSubsession):


class Group(BaseGroup):


class Player(BasePlayer):



def get_timeout_seconds(player):
    return player.participant.expiry - time.time()


class Start(Page):

    def is_displayed(player):
        return player.round_number == 1

    def before_next_page(player, timeout_happened):
        player.participant.expiry = C.TIMEOUT_SECONDS_TOTAL + time.time()
            # Remember to add 'expiry' to PARTICIPANT_FIELDS in settings.py

class Page1(Page):

    timer_text = 'Time left to complete this section:'
    get_timeout_seconds = get_timeout_seconds
    def is_displayed(player):
        return get_timeout_seconds(player) > 0

page_sequence = [

#7 by aarumardi

    Your income pre-tax in:
    <tr> Round 1
    <input type="text" id="income_pre_tax1" oninput="recalc()">
    {{ formfield "income_pre_tax1" }}

    <tr> Round 2
    <input type="text" id="income_pre_tax2" oninput="recalc()">
    {{ formfield "income_pre_tax2" }}
    <tr> Round 3
    <input type="text" id="income_pre_tax3" oninput="recalc()">
    {{ formfield "income_pre_tax3" }}
    <tr> Round 4
    <input type="text" id="income_pre_tax4" oninput="recalc()">
    {{ formfield "income_pre_tax4" }}
    <tr> Round 5
    <input type="text" id="income_pre_tax5" oninput="recalc()">
    {{ formfield "income_pre_tax5" }}

<p>Total income: <span id="totalincome"></span> <small>Dollars</small></p>

<p>Tax rate: {{ C.TAX_RATE }}</p>
<p>Your tax payable is:</p>
<h2><span id="taxpayable"></span> <small>Dollars</small></h2>

    .red-text {
        color: red;

<p> Your income after tax is:</p>
<h2><span id="incomeaftertax" class="red-text"></span> <small class="red-text">Dollars</small></h2>
<button name="Submit Tax Returns" value="True" class="blue-text">Submit Tax Returns</button>

    let taxpayableEle = document.getElementById('taxpayable');
    let incomeaftertaxEle = document.getElementById('incomeaftertax');
    let totalincomeEle = document.getElementById('totalincome');

    function recalc() {
        let income_pre_tax1 = parseInt(document.getElementById('income_pre_tax1').value);
        let income_pre_tax2 = parseInt(document.getElementById('income_pre_tax2').value);
        let income_pre_tax3 = parseInt(document.getElementById('income_pre_tax3').value);
        let income_pre_tax4 = parseInt(document.getElementById('income_pre_tax4').value);
        let income_pre_tax5 = parseInt(document.getElementById('income_pre_tax5').value);

        let totalincome = income_pre_tax1 + income_pre_tax2 + income_pre_tax3 + income_pre_tax4 + income_pre_tax5;
        totalincomeEle.innerText = totalincome;

        // Check if totalincome is a valid number
        if (isNaN(totalincome)) {
            taxpayableEle.innerText = '';
            incomeaftertaxEle.innerText = '';
        } else {
            let taxRate = parseFloat('{{ C.TAX_RATE }}'); // Access tax rate directly from your template
            let taxpayable = totalincome * taxRate;
            taxpayableEle.innerText = taxpayable;
            let incomeaftertax = totalincome - taxpayable;
            incomeaftertaxEle.innerText = incomeaftertax;

    // Attach event listeners to input fields
    document.getElementById('income_pre_tax1').oninput = recalc;
    document.getElementById('income_pre_tax2').oninput = recalc;
    document.getElementById('income_pre_tax3').oninput = recalc;
    document.getElementById('income_pre_tax4').oninput = recalc;
    document.getElementById('income_pre_tax5').oninput = recalc;

I'm trying to create inputs from round1 to round5 as form fields not just a live method of authomatic calculation, however, it creates two different boxes.

Write a reply

Set forum username