oTree Forum >

Grouping with non-active players (code snippet)

#1 by chasmani (edited )

Since oTree 5.8 the `group_by_arrival_time` method only groups active players, i.e. if the browser tab is focussed. That is good if you want to consider players who are not active as dropouts. In our experiments, we don't want to consider those players as dropouts and we want to group players even if they are inactive. I wrote a small bit of code to achieve that, below. 

It's quite simple. In the group_by_arrival_time_method, I loop through all players in the subsession and add them to the waiting_players list if they are at this group page, even if they are inactive. From there I group them as normal. 

To do that, I created a participant_vars field called "app_stage" in settings.py. That is set at each stage of the app, so it has a value before the group_by_arrival_time method is called (otherwise it will raise an error). I set that to "Quiz Grouping" for each player at the end of the grouping loop, which should set that for each waiting player. Once a group is formed, I then set the app_stage to "Quiz" for all players in the group, so they are no longer grouped.

The code below is grouping players based on a previous app's group id. I put this up here in case it helps anyone else. And also in case anyone notices any problems with the way I am doing this, please do let me know. 

def group_by_arrival_time_method(self, waiting_players):

    # Group ALL players at this stage, not just active players
    # Add ALL waiting_players to the waiting_players list
    for player in self.get_players():
        if player.participant.app_stage == "Quiz Grouping":
            if player not in waiting_players:
                waiting_players.append(player)
    print(waiting_players)
    
    waiting_groups = {}
    for player in waiting_players:
    
        # If the player is a dropout, just put them in a single group
        # and move them on
        if player.participant.is_dropout:
            return [player]

        group_id = player.participant.past_group_id
        if group_id not in waiting_groups:
            # since 'd' is initially empty, we need to initialize an empty list (basket)
            # each time we see a new group ID.
            waiting_groups[group_id] = []
        players_in_my_group = waiting_groups[group_id]
        players_in_my_group.append(player)
        if len(players_in_my_group) == 4:
            # Before returning the group, update the app_stage
            for player in players_in_my_group:
                player.participant.app_stage = "Quiz"
            return players_in_my_group
        
        if waiting_too_long(player):
            # Mark as a drop_out, to fast forward through the rest of the app
            player.participant.is_dropout = True
            # make a single-player group.
            return [player]

        player.participant.app_stage="Quiz Grouping"

#2 by chasmani

UPDATE: This is a bit safer:

    # Group ALL players at this stage, not just active players
    for player in self.get_players():
        if "app_stage" in player.participant.vars.keys():
            if player.participant.app_stage == "Game Grouping":
                if player not in waiting_players:
                    waiting_players.append(player)

Write a reply

Set forum username