from itertools import groupby from otree.api import ( models, widgets, BaseConstants, BaseSubsession, BaseGroup, BasePlayer, Currency as c, currency_range, ) import random author = 'seah sze hao' doc = """ Experiment """ class Constants(BaseConstants): # create variables here. define them first here name_in_url = 'chatproject' players_per_group = 2 num_rounds = 6 ratingslist = ['A1', 'B1', 'C1', 'D1', 'A2', 'B2', 'C2', 'D2'] satslist = ['A1satisfaction', 'B1satisfaction', 'C1satisfaction', 'D1satisfaction', 'A2satisfaction', 'B2satisfaction', 'C2satisfaction', 'D2satisfaction'] class Subsession(BaseSubsession): def labeling(self): if self.round_number == 1: for p in self.get_players(): p.participant.label = Constants.ratingslist[p.participant.id_in_session-1] p.participant.vars['A1satisfaction'] = 0 p.participant.vars['B1satisfaction'] = 0 p.participant.vars['C1satisfaction'] = 0 p.participant.vars['D1satisfaction'] = 0 p.participant.vars['A2satisfaction'] = 0 p.participant.vars['B2satisfaction'] = 0 p.participant.vars['C2satisfaction'] = 0 p.participant.vars['D2satisfaction'] = 0 self.session.vars['preshufflegroup1'] = ['A1', 'B1', 'C1', 'D1'] self.session.vars['preshufflegroup2'] = ['A2', 'B2', 'C2', 'D2'] # code here sets the list for pre shuffled groups if self.round_number == 1 or self.round_number == 4: for g in self.get_group_matrix(): g[0].participant.vars['currently_matched'] = [] g[1].participant.vars['currently_matched'] = [] def creating_session(self): self.group_randomly() def set_groups(self): grouping = [] if self.round_number < 4: group1 = self.session.vars['preshufflegroup1'] group2 = self.session.vars['preshufflegroup2'] elif self.round_number == 4: group1 = self.session.vars['postshufflegroup1a'] group2 = self.session.vars['postshufflegroup1b'] elif self.round_number == 5: group1 = self.session.vars['postshufflegroup2a'] group2 = self.session.vars['postshufflegroup2b'] elif self.round_number == 6: if self.session.vars['bestgroupset'] == 'groupset1': group1 = self.session.vars['postshufflegroup1a'] group2 = self.session.vars['postshufflegroup1b'] elif self.session.vars['bestgroupset'] == 'groupset2': group1 = self.session.vars['postshufflegroup2a'] group2 = self.session.vars['postshufflegroup2b'] # after iteration 3 of the chat, the groups in the session are set to the post shuffle groups if self.round_number == 1: grouping = [[group1[0], group1[1]], [group1[2], group1[3]], [ group2[0], group2[1]], [group2[2], group2[3]]] elif self.round_number == 4 or self.round_number == 5 or self.round_number == 2: grouping = [[group1[0], group1[2]], [group1[1], group1[3]], [ group2[0], group2[2]], [group2[1], group2[3]]] elif self.round_number == 3 or self.round_number == 6: grouping = [[group1[0], group1[3]], [group1[1], group1[2]], [ group2[0], group2[3]], [group2[1], group2[2]]] self.set_group_matrix(list(map(self.convert_pair_to_int, grouping))) for g in self.get_group_matrix(): g[0].participant.vars['currently_matched'].append( g[1].id_in_subsession) g[1].participant.vars['currently_matched'].append( g[0].id_in_subsession) # the nature in which the participants are cycled through each participant in their group is fixed # based on their position on the list in the group. # eg first guy will talk to second in chat 1 # first guy will talk to third in chat 2 # first guy will talk to fourth in chat 3 # this method of cycling is same for 3-6 as well def convert_pair_to_int(self, pair): return [Constants.ratingslist.index(pair[0]) + 1, Constants.ratingslist.index(pair[1]) + 1] def combinedsatis(self): combined_s = {} for key in Constants.ratingslist: combined_s[key] = {} for pp in self.get_players(): for i, key in enumerate(Constants.ratingslist): if pp.participant.vars[Constants.satslist[i]] is 0: continue value = combined_s[pp.participant.label].get(key, 0) combined_s[pp.participant.label][key] = value + \ pp.participant.vars[Constants.satslist[i]] value = combined_s[key].get(pp.participant.label, 0) combined_s[key][pp.participant.label] = value + \ pp.participant.vars[Constants.satslist[i]] return combined_s def combinedranking(self): combined_r = {} for key in Constants.ratingslist: combined_r[key] = {} for pp in self.get_players(): for key in Constants.ratingslist: if pp.participant.vars[key] is 0: continue value = combined_r[pp.participant.label].get(key, 0) combined_r[pp.participant.label][key] = value + \ pp.participant.vars[key] value = combined_r[key].get(pp.participant.label, 0) combined_r[key][pp.participant.label] = value + \ pp.participant.vars[key] return combined_r # calculates combined ranking of each pair, to use for finding best pairs later def findSatPairs(self, i, j, pairs, combined, mapping): if i == 4 or j == 4: return self.findSatPairs(i, j+1, pairs[:], combined, mapping) self.findSatPairs(i+1, j+1, pairs[:], combined, mapping) if i == j: return pairs.append([mapping[i], mapping[j]]) if len(pairs) == 2: sum = 0 pps = set() finalPair = [] for pair in pairs: sum += combined[pair[0]][pair[1]] pps.add(pair[0]) pps.add(pair[1]) finalPair.append([combined[pair[0]][pair[1]], pair]) if len(pps) != 4: return self.session.vars['possibilities_s'].append([sum, finalPair]) return self.findSatPairs(0, 0, pairs[:], combined, mapping) # calculation of group scores based on satisfaction scores def findRankPairs(self, i, j, pairs, combined, mapping): if i == 4 or j == 4: return self.findRankPairs(i, j+1, pairs[:], combined, mapping) self.findRankPairs(i+1, j+1, pairs[:], combined, mapping) if i == j: return pairs.append([mapping[i], mapping[j]]) if len(pairs) == 2: sum = 0 pps = set() finalPair = [] for pair in pairs: sum += combined[pair[0]][pair[1]] pps.add(pair[0]) pps.add(pair[1]) finalPair.append([combined[pair[0]][pair[1]], pair]) if len(pps) != 4: return self.session.vars['possibilities_r'].append([sum, finalPair]) return self.findRankPairs(0, 0, pairs[:], combined, mapping) def getBestPair(self, mapping): self.session.vars['possibilities_s'] = [] self.session.vars['possibilities_r'] = [] self.findSatPairs(0, 0, [], self.combinedsatis(), mapping) self.findRankPairs(0, 0, [], self.combinedranking(), mapping) highest = 0 lowest_r = 90 pairs = [] for res in self.session.vars['possibilities_r']: if res[0] < lowest_r: lowest_r = res[0] pairs = [sorted(res[1])] elif res[0] == highest: pairs.append(sorted(res[1])) pairs = [k for k, v in groupby(sorted(pairs))] if len(pairs) == 1: print('combined ranking of best pair = ' + str(lowest_r)) print(pairs) return pairs.pop() for res in self.session.vars['possibilities_s']: if res[0] > highest: highest = res[0] pairs = [sorted(res[1])] elif res[0] == highest: pairs.append(sorted(res[1])) pairs = [k for k, v in groupby(sorted(pairs))] self.session.vars['preshuffle group 1 best satisfaction score'] = highest self.session.vars['preshuffle group 1 best rank score'] = lowest_r print('combined satisfaction of best pair = ' + str(highest)) print('combined rank of best pair = ' + str(lowest_r)) print(pairs) if len(pairs) == 1: print('shuffle decision_by rank') elif len(pairs) > 1: print('shuffle decision was tie broken by ranking scores') return pairs[random.randint(0, len(pairs) - 1)] # # algorithm for finding best pair. # highest satisfaction values will be prioritised into a list of pairs. cmd prompt/terminal also displays this value as participants complete this segment # if satisfaction values are tied, go into calculation of group scores based on ranking scores. # In case of a tiebreaker at ranking stage, a random pair will be chosen # permutations cannot be chosen from existing sats permuations that have been chosen def randompicking(self): if self.round_number == 3: newGroup1a = [] newGroup1b = [] newGroup2a = [] newGroup2b = [] self.session.vars['cell1bestpairing'] = self.getBestPair( self.session.vars['preshufflegroup1']) self.session.vars['cell2bestpairing'] = self.getBestPair( self.session.vars['preshufflegroup2']) newGroup1a.extend(self.session.vars['cell1bestpairing'][0][1]) newGroup1b.extend(self.session.vars['cell1bestpairing'][1][1]) newGroup2a.extend(self.session.vars['cell1bestpairing'][0][1]) newGroup2b.extend(self.session.vars['cell1bestpairing'][1][1]) newGroup1a.extend(self.session.vars['cell2bestpairing'][0][1]) newGroup1b.extend(self.session.vars['cell2bestpairing'][1][1]) newGroup2a.extend(self.session.vars['cell2bestpairing'][1][1]) newGroup2b.extend(self.session.vars['cell2bestpairing'][0][1]) print('newGroup1a = ' + str(newGroup1a)) print('newGroup1b = ' + str(newGroup1b)) print('newGroup2a = ' + str(newGroup2a)) print('newGroup2b = ' + str(newGroup2b)) # randomly picks 1 participant from each pair for new postshuffle groups self.session.vars['postshufflegroup1a'] = newGroup1a self.session.vars['postshufflegroup1b'] = newGroup1b self.session.vars['postshufflegroup2a'] = newGroup2a self.session.vars['postshufflegroup2b'] = newGroup2b pass def groupset(self): if self.round_number == 4: self.session.vars['groupset1sat'] = [] self.session.vars['groupset1rank'] = [] if self.round_number == 5: self.session.vars['groupset2sat'] = [] self.session.vars['groupset2rank'] = [] if self.round_number > 3: for pp in self.get_players(): if self.round_number == 4: if pp.participant.vars['currently_matched'][-1] == 1: round4sat = pp.participant.vars['A1satisfaction'] round4rank = pp.participant.vars['A1'] elif pp.participant.vars['currently_matched'][-1] == 2: round4sat = pp.participant.vars['B1satisfaction'] round4rank = pp.participant.vars['B1'] elif pp.participant.vars['currently_matched'][-1] == 3: round4sat = pp.participant.vars['C1satisfaction'] round4rank = pp.participant.vars['C1'] elif pp.participant.vars['currently_matched'][-1] == 4: round4sat = pp.participant.vars['D1satisfaction'] round4rank = pp.participant.vars['D1'] elif pp.participant.vars['currently_matched'][-1] == 5: round4sat = pp.participant.vars['A2satisfaction'] round4rank = pp.participant.vars['A2'] elif pp.participant.vars['currently_matched'][-1] == 6: round4sat = pp.participant.vars['B2satisfaction'] round4rank = pp.participant.vars['B2'] elif pp.participant.vars['currently_matched'][-1] == 7: round4sat = pp.participant.vars['C2satisfaction'] round4rank = pp.participant.vars['C2'] elif pp.participant.vars['currently_matched'][-1] == 8: round4sat = pp.participant.vars['D2satisfaction'] round4rank = pp.participant.vars['D2'] self.session.vars['groupset1sat'].append(round4sat) self.session.vars['groupset1rank'].append(round4rank) if self.round_number == 5: if pp.participant.vars['currently_matched'][-1] == 1: round5sat = pp.participant.vars['A1satisfaction'] round5rank = pp.participant.vars['A1'] elif pp.participant.vars['currently_matched'][-1] == 2: round5sat = pp.participant.vars['B1satisfaction'] round5rank = pp.participant.vars['B1'] elif pp.participant.vars['currently_matched'][-1] == 3: round5sat = pp.participant.vars['C1satisfaction'] round5rank = pp.participant.vars['C1'] elif pp.participant.vars['currently_matched'][-1] == 4: round5sat = pp.participant.vars['D1satisfaction'] round5rank = pp.participant.vars['D1'] elif pp.participant.vars['currently_matched'][-1] == 5: round5sat = pp.participant.vars['A2satisfaction'] round5rank = pp.participant.vars['A2'] elif pp.participant.vars['currently_matched'][-1] == 6: round5sat = pp.participant.vars['B2satisfaction'] round5rank = pp.participant.vars['B2'] elif pp.participant.vars['currently_matched'][-1] == 7: round5sat = pp.participant.vars['C2satisfaction'] round5rank = pp.participant.vars['C2'] elif pp.participant.vars['currently_matched'][-1] == 8: round5sat = pp.participant.vars['D2satisfaction'] round5rank = pp.participant.vars['D2'] self.session.vars['groupset2sat'].append(round5sat) self.session.vars['groupset2rank'].append(round5rank) def groupsetcomparison(self): self.session.vars['groupset1satsum'] = sum( self.session.vars['groupset1sat']) self.session.vars['groupset2satsum'] = sum( self.session.vars['groupset2sat']) self.session.vars['groupset1ranksum'] = sum( self.session.vars['groupset1rank']) self.session.vars['groupset2ranksum'] = sum( self.session.vars['groupset2rank']) if self.session.vars['groupset1satsum'] > self.session.vars['groupset2satsum']: bestgroupset = 'groupset1' elif self.session.vars['groupset1satsum'] < self.session.vars['groupset2satsum']: bestgroupset = 'groupset2' elif self.session.vars['groupset1ranksum'] < self.session.vars['groupset2ranksum']: bestgroupset = 'groupset1' elif self.session.vars['groupset1ranksum'] > self.session.vars['groupset2ranksum']: bestgroupset = 'groupset2' else: randpicker = random.randint(0, 1) if randpicker == 0: bestgroupset = 'groupset1' else: bestgroupset = 'groupset2' self.session.vars['bestgroupset'] = bestgroupset print(bestgroupset) def lastmanfinder(self): for pp in self.get_players(): if self.session.vars['bestgroupset'] == 'groupset1': for opp in self.get_players(): if pp.participant.label in self.session.vars['postshufflegroup1a'] and \ opp.participant.label in self.session.vars['postshufflegroup1a'] and \ Constants.ratingslist.index(opp.participant.label) + 1 not in pp.participant.vars['currently_matched']: pp.participant.vars['lastman'] = opp.participant.label elif pp.participant.label in self.session.vars['postshufflegroup1b'] and \ opp.participant.label in self.session.vars['postshufflegroup1b'] and \ Constants.ratingslist.index(opp.participant.label) + 1 not in pp.participant.vars['currently_matched']: pp.participant.vars['lastman'] = opp.participant.label print('last man is ' + str(pp.participant.vars['lastman'])) elif self.session.vars['bestgroupset'] == 'groupset2': for opp in self.get_players(): if pp.participant.label in self.session.vars['postshufflegroup2a'] and \ opp.participant.label in self.session.vars['postshufflegroup2a'] and \ Constants.ratingslist.index(opp.participant.label) + 1 not in pp.participant.vars['currently_matched']: pp.participant.vars['lastman'] = pp.participant.label elif pp.participant.label in self.session.vars['postshufflegroup2b'] and \ opp.participant.label in self.session.vars['postshufflegroup2b'] and \ Constants.ratingslist.index(opp.participant.label) + 1 not in pp.participant.vars['currently_matched']: pp.participant.vars['lastman'] = pp.participant.label print('last man is ' + str(pp.participant.vars['lastman'])) class Group(BaseGroup): # for variables shared across the entire group of participants in the session pass def likert7(label): return models.IntegerField( initial=0, choices=[ [1, ''], [2, ''], [3, ''], [4, ''], [5, ''], [6, ''], [7, ''] ], label=label, widget=widgets.RadioSelect ) def likert6(label): return models.IntegerField( choices=[ [1, ''], [2, ''], [3, ''], [4, ''], [5, ''], [6, ''] ], label=label, widget=widgets.RadioSelect ) def likert5(label): return models.IntegerField( choices=[ [1, ''], [2, ''], [3, ''], [4, ''], [5, ''] ], label=label, widget=widgets.RadioSelect ) def likert4(label): return models.IntegerField( choices=[ [1, ''], [2, ''], [3, ''], [4, ''] ], label=label, widget=widgets.RadioSelect ) def likert3(label): return models.IntegerField( choices=[ [1, ''], [2, ''], [3, ''] ], initial=0, label=label, widget=widgets.RadioSelect ) class Player(BasePlayer): # player based (postchat)ratings below # ranking scores A1 = models.IntegerField( initial=0, widget=widgets.RadioSelect, choices=[1, 2, 3], label="Ranking for Participant 1") def A1_choices(self): if self.round_number == 5: choices = [1, 2] else: choices = [1, 2, 3] return choices B1 = models.IntegerField( initial=0, widget=widgets.RadioSelect, choices=[1, 2, 3], label="Ranking for Participant 2") def B1_choices(self): if self.round_number == 5: choices = [1, 2] else: choices = [1, 2, 3] return choices C1 = models.IntegerField( initial=0, widget=widgets.RadioSelect, choices=[1, 2, 3], label="Ranking for Participant 3") def C1_choices(self): if self.round_number == 5: choices = [1, 2] else: choices = [1, 2, 3] return choices D1 = models.IntegerField( initial=0, widget=widgets.RadioSelect, choices=[1, 2, 3], label="Ranking for Participant 4") def D1_choices(self): if self.round_number == 5: choices = [1, 2] else: choices = [1, 2, 3] return choices A2 = models.IntegerField( initial=0, widget=widgets.RadioSelect, choices=[1, 2, 3], label="Ranking for Participant 5") def A2_choices(self): if self.round_number == 5: choices = [1, 2] else: choices = [1, 2, 3] return choices B2 = models.IntegerField( initial=0, widget=widgets.RadioSelect, choices=[1, 2, 3], label="Ranking for Participant 6") def B2_choices(self): if self.round_number == 5: choices = [1, 2] else: choices = [1, 2, 3] return choices C2 = models.IntegerField( initial=0, widget=widgets.RadioSelect, choices=[1, 2, 3], label="Ranking for Participant 7") def C2_choices(self): if self.round_number == 5: choices = [1, 2] else: choices = [1, 2, 3] return choices D2 = models.IntegerField( initial=0, widget=widgets.RadioSelect, choices=[1, 2, 3], label="Ranking for Participant 8") def D2_choices(self): if self.round_number == 5: choices = [1, 2] else: choices = [1, 2, 3] return choices # satisfaction scores A1satisfaction = likert7( "I am satisfied with my partner in the previous conversation ") B1satisfaction = likert7( "I am satisfied with my partner in the previous conversation ") C1satisfaction = likert7( "I am satisfied with my partner in the previous conversation ") D1satisfaction = likert7( "I am satisfied with my partner in the previous conversation ") A2satisfaction = likert7( "I am satisfied with my partner in the previous conversation ") B2satisfaction = likert7( "I am satisfied with my partner in the previous conversation ") C2satisfaction = likert7( "I am satisfied with my partner in the previous conversation ") D2satisfaction = likert7( "I am satisfied with my partner in the previous conversation ") # IOS ratings A1_ios = likert7( "") B1_ios = likert7( "") C1_ios = likert7( "") D1_ios = likert7( "") A2_ios = likert7( "") B2_ios = likert7( "") C2_ios = likert7( "") D2_ios = likert7( "") # liking A1_liking = likert7( "I really like my partner ") B1_liking = likert7( "I really like my partner ") C1_liking = likert7( "I really like my partner ") D1_liking = likert7( "I really like my partner ") A2_liking = likert7( "I really like my partner ") B2_liking = likert7( "I really like my partner ") C2_liking = likert7( "I really like my partner ") D2_liking = likert7( "I really like my partner ") # emotional support A1_emosupp = likert7( "My partner provided good emotional support ") B1_emosupp = likert7( "My partner provided good emotional support ") C1_emosupp = likert7( "My partner provided good emotional support ") D1_emosupp = likert7( "My partner provided good emotional support ") A2_emosupp = likert7( "My partner provided good emotional support ") B2_emosupp = likert7( "My partner provided good emotional support ") C2_emosupp = likert7( "My partner provided good emotional support ") D2_emosupp = likert7( "My partner provided good emotional support ") # instrumental support A1_instrusupp = likert7( "My partner provided helpful advice ") B1_instrusupp = likert7( "My partner provided helpful advice ") C1_instrusupp = likert7( "My partner provided helpful advice ") D1_instrusupp = likert7( "My partner provided helpful advice ") A2_instrusupp = likert7( "My partner provided helpful advice ") B2_instrusupp = likert7( "My partner provided helpful advice ") C2_instrusupp = likert7( "My partner provided helpful advice ") D2_instrusupp = likert7( "My partner provided helpful advice ") # bestgroupsetfrom which lastman was chosen groupset1a = models.StringField() groupset1b = models.StringField() groupset2a = models.StringField() groupset2b = models.StringField() bestgroupset = models.StringField() # CAP Cap = models.FloatField(label="My current CAP score is:", min=0, max=5) ExCAP = models.FloatField( label="My anticipated CAP score at the end of my education at NUS is:", min=0, max=5) # SES SEs = models.StringField(label="", initial=0, widget=widgets.RadioSelect, choices=['Less than $1,000', '$1,000 - $1,999', '$2,000 - $2,999', '$3,000 - $3,999', '$4,000 - $4,999', '$5,000 - $5,999', '$6,000 - $6,999', '$7,000 - $7,999', '$8,000 - $8,999', '$9,000 - $9,999', '$10,000 - $10,999', '$11,000 - $11,999', 'More than $12,000', 'NA / None', "Don't know", 'Prefer not to say']) # Demographics Matric = models.StringField(label="Matriculation No.:") Email = models.StringField(label="Email:") Age = models.IntegerField(label="Age (as of this year)", min=17, max=35) Sex = models.IntegerField( choices=[ [1, 'Male'], [2, 'Female'], ] ) YoS = models.IntegerField( choices=[ [1, "Year 1"], [2, "Year 2"], [3, "Year 3"], [4, "Year 4"], [5, "Year 5"] ], label="Year of Study", widget=widgets.RadioSelect ) Faculty = models.StringField(label="Please state your faculty:") CoS = models.StringField(label="Please state your course of study:") Race = models.IntegerField( choices=[ [1, "Chinese"], [2, "Malay"], [3, "Indian"], [4, "Eurasian"], [5, "Others"] ], label="Race", widget=widgets.RadioSelect ) Race_others = models.StringField( label="If you indicated another race, please state:", blank=True) # test this English = models.IntegerField( choices=[ [1, "Yes"], [2, "No"], ], label="Is English your first language?", widget=widgets.RadioSelect ) Susp = models.LongStringField( label="Were you suspicious about anything during the study? Write 'Nil' if you have none.") Payment = models.IntegerField( choices=[ [1, "RP"], [2, "Paid"] ], label="Please indicate whether you are under the RP programme or a paid participant.", widget=widgets.RadioSelect ) Paid_info = models.LongStringField( label="If you are a paid participant, please enter your mode of payment below, followed by the Paylah! mobile number or Bank account number", blank=True) def chat_nickname(self): return 'Chat User {}'.format(self.role) # can edit this section to change how participants are displayed in the chat window pass