from otree.api import * import os from difflib import SequenceMatcher import json doc = """ Your app description """ class C(BaseConstants): NAME_IN_URL = 'interactiveEval' PLAYERS_PER_GROUP = 2 NUM_ROUNDS = 5 INSTRUCTIONS2_TEMPLATE = 'interactiveEval/instructions2.html' FAILED_TEMPLATE = 'interactiveEval/FailedSimilarityCheckRedirect.html' CHAT_TEMPLATE = 'interactiveEval/chat.html' IMAGES = os.listdir('interactiveEval/static/images') # IMAGES.remove('.DS_Store') # only when running locally PUNC = '''!()-[]{};:'"\,<>./?@#$%^&*_~''' REFERENCE_ROLE = 'Reference' COMPARER_ROLE = 'Comparer' class Subsession(BaseSubsession): def creating_session(self): if self.round_number > 1: self.group_like_round(1) class Group(BaseGroup): image_displayed = models.StringField() class Player(BasePlayer): store_message = models.LongStringField(initial="") textSimilarity = models.FloatField() continueDisimilar = models.BooleanField( label="The text you submitted does not match your partner's. Would you like " "to submit the text again?", choices=[[True, 'Yes'], [False, 'No']], ) suggestedEdit = models.LongStringField( label="Text edit", max_length=4000, ) cleanText = models.LongStringField( label="", ) includeLinks = models.BooleanField( label="Did you link to sources that you believe others will consider trustworthy?", choices=[[True, 'Yes'], [False, 'No']], ) evidenceEval = models.IntegerField( label="Given current evidence, I believe the tweet is:", choices=[ [1, 'Misinformed, or potentially misleading'], [2, 'Somewhat misleading'], [3, 'Not misleading'], ] ) dropped = models.BooleanField(blank=False) class Message(ExtraModel): group = models.Link(Group) sender = models.Link(Player) text = models.StringField() def to_dict(msg: Message): return dict(sender=msg.sender.id_in_group, text=msg.text) def clean_text(player: Player): participant = player.participant text = participant.suggestedEdit text = text.lower() text = text.replace(" ", "").replace('\r', '').replace('\n', '') for ele in text: if ele in C.PUNC: text = text.replace(ele, "") return text def other_player(player: Player): return player.get_others_in_group()[0] # PAGES class ResultsWaitPage(WaitPage): group_by_arrival_time = True title_text = 'Please, wait while we find a match for you.' def is_displayed(self): return self.round_number == 1 class MyPage(Page): # timeout_seconds = 1800 form_model = 'player' form_fields = ['suggestedEdit', 'evidenceEval', 'includeLinks'] @staticmethod def is_displayed(player: Player): if player.round_number == 1: if player.participant.dropped: return False else: return True else: if (not player.participant.dropped) and player.participant.failed_similarity_check: return True else: return False @staticmethod def vars_for_template(player: Player): # print(player.round_number) group = player.group image_to_display = 'images/' + C.IMAGES[group.id_in_subsession - 2] if player.round_number > 1: otherPlayer = other_player(player) previousRound = player.round_number - 1 return dict(reference_text=player.in_round(previousRound).suggestedEdit, comparing_text=otherPlayer.in_round(previousRound).suggestedEdit, textSimilarity=str(round(float(player.in_round(previousRound).textSimilarity), 3) * 100) + '%', nTriesLeft=C.NUM_ROUNDS - player.round_number, image_path=image_to_display, Nickname_chat_one=player.participant.politics + " " + str(player.id_in_group) ) else: return dict(image_path=image_to_display, Nickname_chat_one=player.participant.politics + " " + str(player.id_in_group)) @staticmethod def js_vars(player: Player): otherPlayer = other_player(player) return dict(my_id=player.id_in_group, Nickname_chat_two_ME=player.participant.politics + " " + str(player.id_in_group) + "(Me)", Nickname_chat_two_Other=player.participant.politics + " " + str(player.id_in_group)) @staticmethod def live_method(player: Player, data): my_id = player.id_in_group group = player.group if 'text' in data: text = data['text'] msg = Message.create(group=group, sender=player, text=text) player.store_message += json.dumps(dict(group=group.id_in_subsession, sender=my_id, text=text)) + ';' return {0: [to_dict(msg)]} return {my_id: [to_dict(msg) for msg in Message.filter(group=group)]} @staticmethod def before_next_page(player, timeout_happened): participant = player.participant participant.suggestedEdit = player.suggestedEdit participant.cleanText = clean_text(player) group = player.group group.image_displayed = 'images/' + C.IMAGES[group.id_in_subsession - 2] # timeout happened if timeout_happened: participant.dropped = True else: participant.dropped = False # eval survey type if player.evidenceEval == 3: participant.evidenceEval = False else: participant.evidenceEval = True # initialize failed sim check if player.round_number == 1: player.participant.failed_similarity_check = False player.dropped = participant.dropped class MyWaitPage(WaitPage): @staticmethod def after_all_players_arrive(group: Group): for player in group.get_players(): otherPlayer = other_player(player) player.cleanText = player.participant.cleanText otherPlayer.cleanText = otherPlayer.participant.cleanText if player.role == C.REFERENCE_ROLE: reference_text = player.cleanText comparing_text = otherPlayer.cleanText else: reference_text = otherPlayer.cleanText comparing_text = player.cleanText player.textSimilarity = SequenceMatcher(a=reference_text, b=comparing_text).ratio() player.participant.textSimilarity = player.textSimilarity if (not otherPlayer.participant.dropped) and (player.textSimilarity < 0.70): player.participant.failed_similarity_check = True else: player.participant.failed_similarity_check = False @staticmethod def is_displayed(player: Player): return not player.participant.dropped class SimilarityCheck(Page): timeout_seconds = 30 @staticmethod def vars_for_template(player: Player): otherPlayer = other_player(player) if player.role == C.REFERENCE_ROLE: reference_text = player.cleanText comparing_text = otherPlayer.cleanText else: reference_text = otherPlayer.cleanText comparing_text = player.cleanText player.textSimilarity = SequenceMatcher(a=reference_text, b=comparing_text).ratio() return dict(reference_text=player.suggestedEdit, comparing_text=otherPlayer.suggestedEdit, textSimilarity=str(round(float(player.textSimilarity), 3) * 100) + '%', nTriesLeft=C.NUM_ROUNDS - player.round_number) @staticmethod def is_displayed(player: Player): if not player.participant.dropped and player.participant.failed_similarity_check: return (C.NUM_ROUNDS - player.round_number) > 0 else: return False class Drop(Page): @staticmethod def is_displayed(player: Player): return player.participant.dropped page_sequence = [ResultsWaitPage, MyPage, MyWaitPage, SimilarityCheck, Drop]