import json import random from collections import namedtuple import numpy as np import pandas as pd from otree import widgets from otree.api import (BaseConstants, BaseGroup, BasePlayer, BaseSubsession, models) author = 'Robert zur Bonsen' doc = """ """ RideInfo = namedtuple('RideInfo', ['time', 'cost']) class Constants(BaseConstants): name_in_url = 'main' players_per_group = 2 num_rounds = 4 data_file = "./data.csv" weekdays = { 1: 'Montag', 2: 'Dienstag', 3: 'Mittwoch', 4: 'Donnerstag', 5: 'Freitag', 6: 'Samstag', 7: 'Sonntag', } class Subsession(BaseSubsession): def initialize_treatments(self): # Group players according to current status --------------------------- # --------------------------------------------------------------------- active = [p for p in self.get_players() if p.participant.vars['status'] > 0] inactive = [p for p in self.get_players() if p.participant.vars['status'] <= 0] self.set_group_matrix([active, inactive]) for subsession in self.in_rounds(2, Constants.num_rounds): subsession.group_like_round(1) # Assign players to treatment cells ----------------------------------- # --------------------------------------------------------------------- random.shuffle(active) cells = json.loads(self.session.config['treatments']) cell_players = {t: list() for t in cells} for player in active: min_prio = min([cells[c]['prio'] for c in cells if len(cell_players[c]) < cells[c]['max'] #and cells[c]['full_info'] == player.participant.vars['full_info'] ]) possible_cells = [c for c in cells if len(cell_players[c]) < cells[c]['max'] #and cells[c]['full_info'] == player.participant.vars['full_info'] and cells[c]['prio'] == min_prio ] cell = random.choice(possible_cells) cell_players[cell].append(player) player.treatment = int(cell) # Assign players to sides --------------------------------------------- # --------------------------------------------------------------------- for cell, players in cell_players.items(): params = cells[cell] if len(players) == 0: continue # Assign players to treatments if params['reduced_side'] in [1, 2]: # one decision side is played by a single player single_side = params['reduced_side'] other_side = 1 if single_side == 2 else 2 single = players.pop(0) single.side = single_side # assign all other players to the other side for player in players: player.side = other_side else: # players are equally distributed over both sides groups = np.array_split(players, 2) for side, group in zip([1, 2], groups): for player in group: player.side = side # Load and store ride data -------------------------------------------- # --------------------------------------------------------------------- data = pd.read_csv(Constants.data_file, sep=';', decimal=',') for player in active: for rnd, future_player in enumerate(player.in_rounds(1, Constants.num_rounds), 1): future_player.treatment = player.treatment future_player.side = player.side ride = data[(data.code.astype(int) == player.treatment) & (data.number == rnd)].iloc[0] future_player.info_level = ride['info_level'] future_player.cost_scheme = ride['cost_scheme'] future_player.detour_dist = ride['detour_dist'] future_player.detour_length = ride['detour_length'] future_player.time_value = ride['time_value'] future_player.budget = ride[f'{player.side}.budget'] future_player.indiv_cost = ride[f'{player.side}.indiv.cost'] future_player.indiv_time = ride[f'{player.side}.indiv.time'] future_player.shared_cost = ride[f'{player.side}.shared.cost'] future_player.shared_time = ride[f'{player.side}.shared.time'] future_player.diff_cost = abs(future_player.indiv_cost - future_player.shared_cost) future_player.diff_time = abs(future_player.indiv_time - future_player.shared_time) future_player.other_indiv_cost = ride[f'{player.other_side}.indiv.cost'] future_player.other_indiv_time = ride[f'{player.other_side}.indiv.time'] future_player.other_shared_cost = ride[f'{player.other_side}.shared.cost'] future_player.other_shared_time = ride[f'{player.other_side}.shared.time'] future_player.other_diff_cost = abs(future_player.other_indiv_cost - future_player.other_shared_cost) future_player.other_diff_time = abs(future_player.other_indiv_time - future_player.other_shared_time) class Group(BaseGroup): pass class Player(BasePlayer): choice = models.BooleanField( # true if this player chose shared service label="Bitte wählen Sie Ihre bevorzugte Option:", choices=[[False, 'Alleine'], [True, 'Zu zweit']], widget=widgets.RadioSelect, ) clicks = models.LongStringField() treatment = models.IntegerField() side = models.IntegerField() info_level = models.BooleanField() cost_scheme = models.FloatField(min=0, max=1) detour_dist = models.FloatField(min=0, max=1) detour_length = models.FloatField(min=0, max=1) time_value = models.FloatField(min=0) budget = models.CurrencyField(min=0) indiv_cost = models.CurrencyField(min=0) shared_cost = models.CurrencyField(min=0) indiv_time = models.IntegerField(min=0) shared_time = models.IntegerField(min=0) diff_cost = models.CurrencyField(min=0) diff_time = models.IntegerField(min=0) other_indiv_cost = models.CurrencyField(min=0) other_shared_cost = models.CurrencyField(min=0) other_indiv_time = models.IntegerField(min=0) other_shared_time = models.IntegerField(min=0) other_diff_cost = models.CurrencyField(min=0) other_diff_time = models.IntegerField(min=0) @property def other_side(self): return 1 if self.side == 2 else 2 def set_payoff(self): active_players = [p for p in self.subsession.get_players() if p.participant.vars['status'] == 1] if self.session.config['payoff_round'] == 0: rnd = random.choice(list(self.participant.vars['choices'].keys())) else: rnd = self.session.config['payoff_round'] candidates = [c for c in active_players if self.in_round(rnd).treatment == c.in_round(rnd).treatment and self.in_round(rnd).side != c.in_round(rnd).side ] self.participant.vars['choice'] = self.in_round(rnd).choice if len(candidates) > 0: other = random.choice(candidates) other_choice = other.in_round(rnd).choice else: # no suitable candidate other_choice = self.participant.vars['choice'] self.participant.vars['other_choice'] = other_choice self.participant.vars['match'] = self.participant.vars['choice'] and other_choice self.participant.vars['payoff_round'] = rnd if self.participant.vars['match']: self.participant.payoff = self.in_round(rnd).budget - self.in_round(rnd).shared_cost self.participant.vars['time'] = self.in_round(rnd).shared_time else: self.participant.payoff = self.in_round(rnd).budget - self.in_round(rnd).indiv_cost self.participant.vars['time'] = self.in_round(rnd).indiv_time