#1
by
Gabriela
Hello, I'm working on modifying Mini Twitter. I want to run it for several rounds, and in rounds > 1, I want the game to start from wherever it stopped in the last round. I have tried saving key information (lists) as participant fields and calling them to initialised the game, which seems to work with the follow option of the game; however, when I want to unfollow a participant, it unfollows every other participant except the one I want to unfollow. It seems that when I want to unfollow someone, the lists are not being updated properly. The documentation suggest to retrieving the history of the game from a database; however, I'm having trouble visualising how this looks for this case. Is there any similar code that I could study and adapt? Thanks for your help. I'm attaching my _init_.py code below. Gabriela ### CODE ##### from otree.api import * doc = """ Mini-Twitter """ class C(BaseConstants): NAME_IN_URL = 'twitter' PLAYERS_PER_GROUP = None NUM_ROUNDS = 4 class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): pass def get_follower_ids(player: Player, including_myself): ids = [sub.follower_id_in_group for sub in Subscription.filter(leader=player)] if including_myself: ids.append(player.id_in_group) return ids class Subscription(ExtraModel): # we store the player objects and id_in_group redundantly, # for convenience and performance leader = models.Link(Player) leader_id_in_group = models.IntegerField() follower = models.Link(Player) follower_id_in_group = models.IntegerField() class Message(ExtraModel): player = models.Link(Player) player_id_in_group = models.IntegerField() group = models.Link(Group) text = models.LongStringField() def to_dict(msg: Message): return dict(id_in_group=msg.player_id_in_group, text=msg.text,) def live_method(player: Player, data): group = player.group my_id = player.id_in_group msg_type = data['type'] if msg_type == 'write': text = data['text'] msg = Message.create(player=player, player_id_in_group=my_id, text=text, group=group) followers = get_follower_ids(player, including_myself=True) return {follower: dict(messages=[to_dict(msg)]) for follower in followers} broadcast = {} if player.round_number == 1: followers = [] i_follow = [] i_dont_follow = [p.id_in_group for p in group.get_players() if p.id_in_group not in i_follow] unfiltered_messages = Message.filter(group=group) # i see my own messages in my feed my_feed_authors = i_follow + [my_id] messages = [to_dict(m) for m in unfiltered_messages if m.player_id_in_group in my_feed_authors] else: followers = player.participant.followers i_follow = player.participant.i_follow i_dont_follow = player.participant.i_dont_follow my_feed_authors = player.participant.my_feed_authors messages = player.participant.messages if msg_type == 'toggle_follow': leader_id = data['id_in_group'] leader = group.get_player_by_id(leader_id) subs = Subscription.filter(follower=player, leader=leader) if subs: [sub] = subs sub.delete() else: Subscription.create( leader=leader, leader_id_in_group=leader.id_in_group, follower=player, follower_id_in_group=my_id, ) # notify the other person of the change to their followers broadcast[leader_id] = dict(followers=get_follower_ids(leader, including_myself=True)) followers = get_follower_ids(player, including_myself=True) i_follow = [sub.leader_id_in_group for sub in Subscription.filter(follower=player)] i_dont_follow = [p.id_in_group for p in group.get_players() if p.id_in_group not in i_follow] unfiltered_messages = Message.filter(group=group) # i see my own messages in my feed my_feed_authors = i_follow + [my_id] messages = [to_dict(m) for m in unfiltered_messages if m.player_id_in_group in my_feed_authors] player.participant.followers = followers player.participant.i_follow = i_follow player.participant.i_dont_follow = i_dont_follow player.participant.my_feed_authors = my_feed_authors player.participant.messages = messages broadcast.update( { my_id: dict( full_load=True, followers=followers, i_follow=i_follow, i_dont_follow=i_dont_follow, messages=messages, ) } ) return broadcast # PAGES class MyPage(Page): live_method = live_method @staticmethod def js_vars(player: Player): return dict(my_id=player.id_in_group) @staticmethod def is_displayed(player: Player): return player.round_number == 1 class MyPageRoundTwo(Page): live_method = live_method @staticmethod def js_vars(player: Player): return dict(my_id=player.id_in_group) @staticmethod def is_displayed(player: Player): return player.round_number > 1 class Results(Page): @staticmethod def is_displayed(player: Player): return player.round_number == C.NUM_ROUNDS page_sequence = [MyPage, MyPageRoundTwo, Results]
#2
by
Chris_oTree
Making it run across rounds adds complexity because ExtraModel should be linked to a round. Are you sure you need 2 rounds? What is the difference between the rounds?
#3
by
Gabriela
Thank you for the reply, Chris. That I actually makes a lot of sense. So what's happening is that although the lists that display the connections initially are being loaded correctly, when creating new connections since the ExtraModel object is empty(?) it resets such connections. Yes, we need to run this through multiple rounds. What we want to do is add opinion questions in between and check how the network evolves as a result of sharing different opinions. If linking the ExtraModel from round to round is tricky, would creating it together with the connections history work? I'm thinking about something along these lines: broadcast = {} [ ... ] else: followers = player.participant.followers i_follow = player.participant.i_follow i_dont_follow = player.participant.i_dont_follow my_feed_authors = player.participant.my_feed_authors messages = player.participant.messages for leader in i_follow: Subscription.create( leader=leader, leader_id_in_group=group.get_player_by_id(leader), follower=player, follower_id_in_group=my_id, ) [ ... ] Alternatively, how would one go about linking ExtraModels from round to round? Thanks for your assistance. I appreciate! Best, Gabriela
#4
by
Chris_oTree
I recommend to either use ExtraModel or participant fields, not both. Otherwise it gets confusing about where to get the data from. I guess storing things in participant fields and session fields is the easiest way to go.
#5
by
Gabriela
I'm not sure I understand the suggestion. The ExtraModel takes care of creating/destroying the connection, so what you're suggesting is that I don't use the ExtraModel at all? As the code is written now, it is needed to retrieve the list that is later stored in the participant fields. If I no longer use it then, I should create lists the old fashion way right?
#6
by
Gabriela
UPDATE: I've solved the issue by creating the ExtraModel under an else, I think this logic is similar to what's done in the tictactoe game and works in my case. This way, I can call the history with participant vars but still handle the connections with an ExtraModel. def live_method(player: Player, data): group = player.group my_id = player.id_in_group msg_type = data['type'] if msg_type == 'write': text = data['text'] msg = Message.create(player=player, player_id_in_group=my_id, text=text, group=group) followers = get_follower_ids(player, including_myself=True) return {follower: dict(messages=[to_dict(msg)]) for follower in followers} broadcast = {} if player.round_number == 1: followers = [] i_follow = [] i_dont_follow = [p.id_in_group for p in group.get_players()] unfiltered_messages = Message.filter(group=group) my_feed_authors = i_follow + [my_id] messages = [to_dict(m) for m in unfiltered_messages if m.player_id_in_group in my_feed_authors] else: followers = player.participant.followers i_follow = player.participant.i_follow i_dont_follow = player.participant.i_dont_follow my_feed_authors = player.participant.my_feed_authors messages = player.participant.messages if msg_type == 'toggle_follow': leader_id = data['id_in_group'] leader = group.get_player_by_id(leader_id) subs = Subscription.filter(follower=player, leader=leader) if subs: [sub] = subs sub.delete() else: Subscription.create( leader=leader, leader_id_in_group=leader.id_in_group, follower=player, follower_id_in_group=my_id, ) # notify the other person of the change to their followers broadcast[leader_id] = dict(followers=get_follower_ids(leader, including_myself=True)) followers = get_follower_ids(player, including_myself=True) i_follow = [sub.leader_id_in_group for sub in Subscription.filter(follower=player)] i_dont_follow = [p.id_in_group for p in group.get_players() if p.id_in_group not in i_follow] unfiltered_messages = Message.filter(group=group) my_feed_authors = i_follow + [my_id] messages = [to_dict(m) for m in unfiltered_messages if m.player_id_in_group in my_feed_authors] else: for temp in i_follow: Subscription.create( leader=group.get_player_by_id(temp), leader_id_in_group=group.get_player_by_id(temp).id_in_group, follower=player, follower_id_in_group=my_id, ) player.participant.followers = followers player.participant.i_follow = i_follow player.participant.i_dont_follow = i_dont_follow player.participant.my_feed_authors = my_feed_authors player.participant.messages = messages broadcast.update( { my_id: dict( full_load=True, followers=followers, i_follow=i_follow, i_dont_follow=i_dont_follow, messages=messages, ) } ) return broadcast