Search code examples
pythondjangodictionaryif-statementotree

OTree / Django send player to results/ exit page if StringField input not in my dict


I’m working with OTree with self: https://otree.readthedocs.io/en/self/

On the first page of my experiment, I ask the players to provide an access code / their matriculation number via StringField. If their input is not in my dict in Constants, I want to send them directly to a page where I tell them „Sorry, you can’t participate“ with the only option for them to click the next button and exit the experiment.

I have tried the following:

in models.py

class Constants(BaseConstants):
    name_in_url = 'prisoners_test1'
    players_per_group = 2
    num_rounds = 1
  
    matriculation_dict = {
            '123': ('Adam Smith', 'Economics'),
            '124': ('Ada Lovelace', 'Programming'),
            '125': ('Charles Babbage', 'Mathematics'),
        }

class Player(BasePlayer):
    matriculation = models.StringField(label='Please provide your Matriculation Number')

    access = models.BooleanField()
    def matriculation_not_found(self):
         if self.matriculation in Constants.matriculation_dict:
             self.access = True
         else: self.access = False

in pages.py

class ExcludedPlayer(Page):
    def is_displayed(self):
        return self.player.access == False

page_sequence = [Matriculation, ExcludedPlayer, P1_Decision, P2_Decision, ResultsWaitPage, Results]

The problem is that the value of access is not updated through my if statement.

My second problem is that even if the page ExcludedPlayer is displayed (b/c I set initial value of access = False), the player is directed to the other pages (P1_Decision, ResultsWaitPage, Results) after clicking next. How can I end the game for the excluded player?

Thank you for your help!


Solution

  • To your first problem:

    To update the access field, you need to call your matriculation_not_found method somewhere. A good place for this is the otree built-in method before_next_page in your Matriculation class:

    class Matriculation(Page):
    
        def before_next_page(self):
            self.player.matriculation_not_found()
    

    Or in newer otree versions (no-self format):

    class Matriculation(Page):
    
        @staticmethod
        def before_next_page(player, timeout_happened):
            player.matriculation_not_found()
    

    To your second problem:

    The easiest way to prevent the excluded players from seeing the upcoming pages is to remove the next-button. Simply delete the following line from the ExcludedPlayer.html template:

    {{ next_button }}
    

    If for some reason you don't want that, you can also check on each of the upcoming pages in the is_displayed method whether access is allowed or not. For example for the P1_Decision page:

    class P1_Decision(Page):
        
        def is_displayed(self):
            return self.player.access
    

    And again the same in the new no-self format:

    class P1_Decision(Page):
        
        @staticmethod
        def is_displayed(player):
            return player.access
    

    Another alternative would be to swap out the ExcludedPlayers page to a later app (let's call it 'exculuded_players_app') and skip the pages (and apps) in between using the app_after_this_page method:

    class Matriculation(Page):
    
        def before_next_page(self):
            self.player.matriculation_not_found()
    
        def app_after_this_page(self, upcoming_apps):
            if not self.player.access:
                return 'exculuded_players_app'
    

    And again the same in the new no-self format:

    class Matriculation(Page):
    
        @staticmethod
        def before_next_page(player, timeout_happened):
            player.matriculation_not_found()
        
        @staticmethod
        def app_after_this_page(player, upcoming_apps):
            if not player.access:
                return 'exculuded_players_app'