from otree.api import * doc = """ Live coordination (voting with chat/negotiation) """ class Constants(BaseConstants): name_in_url = 'live_coordination' players_per_group = 6 num_rounds = 1 max_points = 5 choices = [0, 1, 2, 3, 4, 5] majority = int(players_per_group / 2) + 1 class Subsession(BaseSubsession): pass class Group(BaseGroup): contribution = models.CurrencyField() class Player(BasePlayer): vote = models.IntegerField() def get_or_none(instance, field): try: return getattr(instance, field) except TypeError: return None # PAGES class Coordinate(Page): @staticmethod def js_vars(player: Player): return dict(my_id=player.id_in_group) @staticmethod def live_method(player: Player, data): group = player.group if 'vote' in data: try: vote = int(data['vote']) except Exception: print('Invalid message received', data) return if not vote in Constants.choices: print('Invalid message received', data) return player.vote = vote players = group.get_players() tallies = {vote: 0 for vote in Constants.choices} votes = [] for p in players: vote = get_or_none(p, 'vote') if vote is not None: tallies[vote] += 1 if tallies[vote] >= Constants.majority: group.contribution = vote return {0: dict(finished=True)} votes.append([p.id_in_group, vote]) # if you don't want to show who voted, use 'tallies' instead of 'votes'. return {0: dict(votes=votes, tallies=tallies)} @staticmethod def is_displayed(player: Player): """Skip this page if a deal has already been made""" group = player.group contribution = get_or_none(group, 'contribution') return contribution is None @staticmethod def error_message(player: Player, values): group = player.group # anti-cheating measure if get_or_none(group, 'contribution') is None: return "Not done with this page" class Results(Page): pass page_sequence = [Coordinate, Results]