from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range ) import random from django_countries.fields import CountryField from socialgraph.social_conn import SocialGraph from os import listdir from os.path import isfile, join, splitext class Constants(BaseConstants): name_in_url = 'survey1' players_per_group = 7 num_rounds = 10 endowment = c(10) def get_static_img_path(img_path): return "_static/" + img_path def get_images(img_path, extensions=['.jpg', '.png', '.jpeg']): '''获取图片文件的名字和路径''' static_img_path = get_static_img_path(img_path) files = [] for f in listdir(static_img_path): fname, f_ext = splitext(f) if isfile(join(static_img_path, f)) and f_ext in extensions: files.append({'name': f, 'path': img_path + "/" + f}) return files class Subsession(BaseSubsession): def creating_session(self): self.session.vars['avatars'] = get_images('avatars') class Group(BaseGroup): total_contribution = models.CurrencyField() individual_share = models.CurrencyField() #reward字段 individual_contribution = models.TextField() def set_payoffs(self): self.total_contribution = sum( [p.contribution for p in self.get_players()]) if self.total_contribution >= 18: # 总的contribution超过限制,去掉每个人的贡献 for p in self.get_players(): p.payoff = Constants.endowment - p.contribution else: # 没有超过限制,固定为endoment收入 for p in self.get_players(): p.payoff = Constants.endowment self.individual_contribution = '' for p in self.get_players(): self.individual_contribution += str(p.contribution) + ',' def get_social_type(self): return self.session.config.get('social_type', 'null') def get_group_id(self): '''current group id''' return self.id_in_subsession # group成员到齐后执行 def set_social_network(self): sgroups = self.session.vars.get( 'social_graph', {}) # 每个group一个socialgraph字典 players = self.get_players() group_id = self.get_group_id() if not sgroups.get(group_id): names = [p.q_name for p in players] sg = SocialGraph() sg.add_nodes(names) t = self.get_social_type() print("init group %d social network with type: %s" % (group_id, t)) conn_result = sg.gen_conn(t) # 通过group_id作为索引的话,使用group_by_arrival_time会造成group id改变,然后访问后面的round或app会出错 sgroups[group_id] = sg self.session.vars['social_graph'] = sgroups for p in players: p.social_type = t if t == 'star': if p.q_name == conn_result: print("social network center:", conn_result) p.social_leader = True elif t == 'weak_tie': [lparts, center_v, rparts] = conn_result if p.q_name == center_v: print("social network center:", center_v) p.social_leader = True p.social_group = 3 # leader的group定为3(1和2的结合) elif p.q_name in lparts: p.social_group = 1 elif p.q_name in rparts: p.social_group = 2 class Player(BasePlayer): def set_payoff(self): """Calculate payoff, which is zero for the survey""" self.payoff = 0 def get_connected_players(self): '''获取有连接关系的player''' others = self.get_others_in_group() me_name = self.participant.vars['name'] group_id = self.group.get_group_id() sg = self.session.vars['social_graph'][group_id] return [p for p in others if sg.is_direct_conn(me_name, p.participant.vars['name'])] def get_connection_node_edge(self, connected_players): '''获取当前player和connected_players连接的node和edge''' image_dir = "../../../../../../../static/" nodes = [{'id': 0, 'shape': 'circularImage', # 注意不能用self.q_avatar, 每个round会清除掉,用participant持久保存 'image': image_dir + self.participant.vars['avatar'], 'label': 'You', 'font': '15px arial red' }] edges = [] for i, p in enumerate(connected_players): id = i + 1 nodes.append({'id': id, 'shape': 'circularImage', 'image': image_dir + p.participant.vars['avatar'], 'label': p.participant.vars['name'] }) edges.append({'from': 0, 'to': id}) return (nodes, edges) def set_player_name(self): '''设置持久保存的player name.''' print("set player name:", self.q_name) self.participant.vars['name'] = self.q_name self.participant.label = self.q_name def set_player_avatar(self): self.participant.vars['avatar'] = self.q_avatar def get_partners(self): return self.get_others_in_group() contribution = models.CurrencyField(min=0, max=Constants.endowment) # 社交关系的类型 social_type = models.StringField() # 是否为leader? social_leader = models.BooleanField(initial=False) # 是否超时,超时提交的数据为自动生成的数据 timeout = models.BooleanField(initial=False) # 所属的social组,仅在weak tie模式下会不同 social_group = models.PositiveIntegerField(initial=0) q_name = models.StringField(verbose_name="What's your name?") # 头像图片 q_avatar = models.StringField() q_country = CountryField( verbose_name='What is your country of citizenship?') q_age = models.PositiveIntegerField(verbose_name='What is your age?', choices=range(13, 125), initial=None) q_gen = models.CharField(initial=None, choices=['Male', 'Female', 'Not to disclose'], verbose_name='Please select a gender you mostly identify with', widget=widgets.RadioSelect()) q_city = models.StringField(verbose_name='Which city did you live in?' ) q_movie = models.StringField(verbose_name='What is your favorite movie?' ) q_sport = models.StringField(verbose_name='What is your favorite sport?') q_motto = models.StringField(verbose_name='What is your motto?' )