from otree.api import * import random c = Currency doc = """ Your app description """ class Constants(BaseConstants): name_in_url = 'treatment2' players_per_group = 2 num_rounds = 1 instructions_template = 'treatment2/instructions.html' endowment = float(20) multiplier1 = 0.8 # 第一阶段乘数 multiplier2 = 3 # 第二阶段乘数 class Subsession(BaseSubsession): pass class Group(BaseGroup): total_contribution = models.FloatField(doc='每组总投入') individual_share = models.FloatField(doc='项目收益') def make_feeling(label): return models.IntegerField(label=label, choices=[0, 1, 2, 3, 4, 5, 6, 7], widget=widgets.RadioSelectHorizontal) class Player(BasePlayer): # 练习题 exercise_answer_1_1_1 = models.FloatField(label="- your total Stage 1 income?", doc='第一阶段练习题问题1的第1个答案') exercise_answer_1_1_2 = models.FloatField(label="- your partner‘s total Stage 1 income?", doc='第一阶段练习题问题1的第2个答案') exercise_answer_1_2_1 = models.FloatField(label="- your total Stage 1 income?", doc='第一阶段练习题问题2的第1个答案') exercise_answer_1_2_2 = models.FloatField(label="- your partner‘s total Stage 1 income?", doc='第一阶段练习题问题2的第2个答案') exercise_answer_2_1_1 = models.FloatField(label="- your total Stage 2 income?", doc='第二阶段练习题问题1的第1个答案') exercise_answer_2_1_2 = models.FloatField(label="- your partner‘s total Stage 2 income?", doc='第二阶段练习题问题1的第2个答案') exercise_answer_2_2_1 = models.FloatField(label="- your total Stage 2 income?", doc='第二阶段练习题问题2的第1个答案') exercise_answer_2_2_2 = models.FloatField(label="- your partner‘s total Stage 2 income?", doc='第二阶段练习题问题2的第2个答案') # Stage1 stage1_guess = models.FloatField(min=0, max=Constants.endowment, label='What is your best guess of how much your partner is likely to contribute? ' 'Please choose any number between 0 and 20 tokens:', doc='第一阶段猜测值') stage1_decision = models.FloatField(min=0, max=Constants.endowment, label='What is your contribution to the project? Please choose any number ' 'between 0 and 20 tokens: ', doc='第一阶段投资决策') stage1_guess_error = models.FloatField(doc='猜测误差') stage1_guess_profit = models.FloatField(initial=0, doc='猜测收益') stage1_decision_profit = models.FloatField(doc='决策收益') stage1_payoff = models.FloatField(doc='第一阶段收益') stage1_frustration = make_feeling('Frustration: The feeling of being upset or annoyed as a result of being unable ' 'to change or achieve something. ') stage1_anger = make_feeling('Anger: a strong feeling of annoyance, displeasure, or hostility.') stage1_indignation = make_feeling('Indignation: anger or annoyance at what is perceived to be unfair treatment ' 'relative to what you believe is fair treatment, particularly, in your social ' 'group. ') stage1_shame = make_feeling('Shame: a painful feeling of humiliation or distress caused by the self-realization ' 'of socially inappropriate behavior on your part alone which has nothing to do with ' 'your partner’s decision.') stage1_elation = make_feeling('Elation: Great happiness and exhilaration.') stage1_satisfaction = make_feeling('Satisfaction: Fulfilment of your wishes, expectations, or needs.') stage1_dissatisfaction = make_feeling('Dissatisfaction: Non- fulfilment of your wishes, expectations, or needs.') # Stage2 stage2_guess = models.FloatField(min=0, max=5, label='What do you believe is the number of tokens that your partner will give ' 'up to reduce your income? Please choose any number between 0 and 5 ' 'tokens: ', doc='第二阶段猜测值') stage2_decision = models.FloatField(min=0, max=5, label='How many tokens do you wish to give up to reduce your partner’s ' 'income? You know that your partner will be asked to give up 3 times as ' 'many tokens. Please choose any number between 0 and 5 tokens:', doc='第二阶段决策') stage2_guess_error = models.FloatField(doc='猜测误差') stage2_guess_profit = models.FloatField(initial=0, doc='猜测收益') stage2_decision_profit = models.FloatField(doc='决策收益') stage2_payoff = models.FloatField(doc='第一阶段收益') stage2_frustration = make_feeling( 'Frustration: The feeling of being upset or annoyed as a result of being unable to change or achieve ' 'something. ') stage2_anger = make_feeling('Anger: a strong feeling of annoyance, displeasure, or hostility.') stage2_indignation = make_feeling( 'Indignation: anger or annoyance at what is perceived to be unfair treatment relative to what you believe is ' 'fair treatment, particularly, in your social group. ') stage2_shame = make_feeling( 'Shame: a painful feeling of humiliation or distress caused by the self-realization of socially inappropriate ' 'behavior on your part alone which has nothing to do with your partner’s decision.') stage2_elation = make_feeling('Elation: Great happiness and exhilaration.') stage2_satisfaction = make_feeling('Satisfaction: Fulfilment of your wishes, expectations, or needs.') stage2_dissatisfaction = make_feeling('Dissatisfaction: Non- fulfilment of your wishes, expectations, or needs.') payoff_stage = models.IntegerField(doc='收益阶段,随机生成') # 问卷 age = models.IntegerField(min=0, label='1. Age:') gender = models.StringField(label='Gender', choices=['female', 'male'], widget=widgets.RadioSelectHorizontal) grade = models.StringField(label='Grade', choices=['first year, undergraduate', 'second year, undergraduate', 'third year, undergraduate', 'fourth year, undergraduate', 'postgraduate']) study_1 = models.StringField(label='Field of study', choices=['business or economics', 'arts', 'engineering', 'medicine and health sciences', 'science', 'law', 'others, please specify:'], widget=widgets.RadioSelect) study_2 = models.StringField(initial=' ', label=' ') question_2 = models.StringField(label='2. Have you participated in similar experiments in the past?', choices=['Yes', 'No'], widget=widgets.RadioSelectHorizontal) question_3_1 = models.StringField(label="3. How did you guess your partner's contribution in Stage 1? Tick the " "choices that apply. If none of the choices applies then pick the last " "option and provide your own reason.", choices=['I used my own intended contributions and guessed my partner would choose the same.', 'I started with my own intended contributions and added some number to it.', 'I started with my own intended contributions and subtracted some number from it.', 'I chose randomly without giving it much thought.', 'None of the above accurately describes my guess. '], widget=widgets.RadioSelect) question_3_2 = models.StringField(initial=' ', label=' Here is how I chose: ') question_4_1 = models.StringField(label='4. When you were told your partner’s contribution at the end of Stage 1, tick the choices that apply.', choices=['I compared my partner’s contributions to my own contributions.', 'I compared my partner’s contributions to my initial guess of how much my partner would contribute.', 'I compared both of the above, please state their relative importance: '], widget=widgets.RadioSelect) question_4_2 = models.IntegerField(initial=0, min=0, max=100, label='Relative importance(%) of the first option:') question_4_3 = models.IntegerField(initial=0, min=0, max=100, label='Relative importance(%) of the second option:') question_5_A = models.StringField(label='5. You contributed MORE than your partner in Stage 1. Tick the choices below that apply to you.', choices=['I chose zero reduction of my partner’ income because it would have reduced my own Stage 2 income.', 'I chose strictly positive reduction of my partner’ income because I was frustrated and angry that my partner’s contributions were lower than my own contributions.', 'I chose strictly positive reduction of my partner’ income because I was frustrated and angry that my partner’s contributions were lower than my initial guess of the partner’s contributions.', 'I chose strictly positive reduction of my partner’ income because my partner’s Stage 1 contributions revealed to me that the partner was unkind to me. So, I reciprocated with unkindness in Stage 2.', 'I chose strictly positive reduction of my partner’ income because my partner contributed less than the socially appropriate or fair level of contributions.', 'I chose strictly positive reduction of my partner’ income because my partner contributed less than the socially appropriate or fair level and that they should be ashamed.'], widget=widgets.RadioSelect, blank=True) question_5_B = models.StringField(label='5. You contributed LESS than your partner in Stage 1. Tick the choices below that apply to you.', choices=['I choose zero reduction of my partner’ income because it would have reduced my own Stage 2 income.', 'I choose zero reduction of my partner’ income because I felt guilty at having contributed less than my partner.', 'I chose strictly positive reduction of my partner’ income because I was frustrated and angry that my partner’s contributions were lower than my initial guess of the partner’s contributions.', 'I chose strictly positive reduction of my partner’ income because my partner contributed less than the socially appropriate or fair level of contributions.', 'I chose strictly positive reduction of my partner’ income because my partner contributed less than the socially appropriate or fair level and that they should be ashamed.'], widget=widgets.RadioSelect, blank=True) question_5_C = models.StringField(label='5. You contributed EQUAL to your partner in Stage 1. Tick the choices below that apply to you.', choices=['I chose zero reduction of my partner’ income because it would have reduced my own Stage 2 income.', 'I choose zero reduction of my partner’ income because we contributed equally.', 'I chose strictly positive reduction of my partner’ income because I was frustrated and angry that my partner’s contributions were lower than my initial guess of the partner’s contributions.', 'I chose strictly positive reduction of my partner’ income because my partner contributed less than the socially appropriate or fair level of contributions.', 'I chose strictly positive reduction of my partner’ income because my partner contributed less than the socially appropriate or fair level and that they should be ashamed.'], widget=widgets.RadioSelect, blank=True) question_6 = models.StringField(label='6. Recall that a contribution of at least T units was rated to be the “socially desirable contribution” by your social group or peers. Tick all options below that apply to you.', choices=['I contributed less than T units, and I feel no shame.', 'I contributed less than T units and in hindsight I feel some shame.', 'I contributed T or more units and I feel no specific emotion.', 'I contributed T or more units and I feel elated at doing something socially responsible.'], widget=widgets.RadioSelect) question_7 = models.StringField(label='7. If your income was reduced by your partner, do you believe that you deserved it?', choices=['Yes', 'No'], widget=widgets.RadioSelectHorizontal) question_8 = models.StringField(label='8. Suppose that you were asked to play this experiment again. Relative to the contributions that you chose in Stage 1 of this experiment, will your contributions in the new experiment (tick as appropriate):', choices=['Increase', 'Decrease', 'Stay the same'], widget=widgets.RadioSelectHorizontal) # FUNCTIONS def other_player(player: Player): return player.get_others_in_group()[0] def set_stage1_payoffs(subsession): players = subsession.get_players() guess_errors = [] for p in players: # 计算猜测误差 opponent = other_player(p) p.stage1_guess_error = round(abs(p.stage1_guess - opponent.stage1_decision), 2) guess_errors.append(p.stage1_guess_error) # 计算决策收益 p.group.total_contribution = p.stage1_decision + opponent.stage1_decision p.group.individual_share = round(Constants.multiplier1 * p.group.total_contribution, 2) p.stage1_decision_profit = round(((Constants.endowment - p.stage1_decision) + p.group.individual_share), 2) min_guess_error_players = [] min_guess_error = min(guess_errors) for p in players: if p.stage1_guess_error == min_guess_error: min_guess_error_players.append(p) selected_player = random.sample(min_guess_error_players, 1) for p in players: if p in selected_player: if min_guess_error == 0: p.stage1_guess_profit = 5 else: p.stage1_guess_profit = 2 else: p.stage1_guess_profit = 0 for p in players: p.stage1_payoff = p.stage1_guess_profit + p.stage1_decision_profit def set_stage2_payoffs(subsession): players = subsession.get_players() guess_errors = [] for p in players: # 计算猜测误差 opponent = other_player(p) p.stage2_guess_error = round(abs(p.stage2_guess - opponent.stage2_decision), 2) guess_errors.append(p.stage2_guess_error) # 计算决策收益 p.stage2_decision_profit = round((Constants.endowment - p.stage2_decision) - opponent.stage2_decision * Constants.multiplier2, 2) min_guess_error_players = [] min_guess_error = min(guess_errors) for p in players: if p.stage2_guess_error == min_guess_error: min_guess_error_players.append(p) selected_player = random.sample(min_guess_error_players, 1) for p in players: if p in selected_player: if min_guess_error == 0: p.stage2_guess_profit = 5 else: p.stage2_guess_profit = 2 else: p.stage2_guess_profit = 0 for p in players: p.stage2_payoff = p.stage2_guess_profit + p.stage2_decision_profit def set_payoffs(group: Group): players = group.get_players() for p in players: p.payoff_stage = random.randint(1, 2) if p.payoff_stage == 1: p.payoff = p.stage1_payoff if p.payoff_stage == 2: p.payoff = p.stage2_payoff # PAGES class Introduction(Page): pass class Exercise(Page): form_model = 'player' form_fields = [ 'exercise_answer_1_1_1', 'exercise_answer_1_1_2', 'exercise_answer_1_2_1', 'exercise_answer_1_2_2', 'exercise_answer_2_1_1', 'exercise_answer_2_1_2', 'exercise_answer_2_2_1', 'exercise_answer_2_2_2', ] class ExerciseResults(Page): pass class Stage1Decision(Page): form_model = 'player' form_fields = ['stage1_guess', 'stage1_decision'] @staticmethod def vars_for_template(player: Player): return dict( value1=player.session.config['大多数人投资'], value2=player.session.config['大多数人应该投资'], ) class Stage1ResultsWaitPage(WaitPage): wait_for_all_groups = True after_all_players_arrive = set_stage1_payoffs class Stage1Results(Page): form_model = 'player' form_fields = ['stage1_frustration', 'stage1_anger', 'stage1_indignation', 'stage1_shame', 'stage1_elation', 'stage1_satisfaction', 'stage1_dissatisfaction'] @staticmethod def vars_for_template(player: Player): opponent = other_player(player) return dict( opponent_stage1_decision=opponent.stage1_decision, opponent_stage1_decision_profit=opponent.stage1_decision_profit ) class Stage2Decision(Page): form_model = 'player' form_fields = ['stage2_guess', 'stage2_decision'] @staticmethod def vars_for_template(player: Player): opponent = other_player(player) return dict( opponent_stage1_decision=opponent.stage1_decision, opponent_stage1_decision_profit=opponent.stage1_decision_profit ) class Stage2Feeling(Page): form_model = 'player' form_fields = ['stage2_frustration', 'stage2_anger', 'stage2_indignation', 'stage2_shame', 'stage2_elation', 'stage2_satisfaction', 'stage2_dissatisfaction'] class Stage2ResultsWaitPage(WaitPage): wait_for_all_groups = True after_all_players_arrive = set_stage2_payoffs class Stage2Results(Page): @staticmethod def vars_for_template(player: Player): opponent = other_player(player) return dict( opponent_stage2_decision=opponent.stage2_decision, opponent_stage2_decision_profit=opponent.stage2_decision_profit ) class Questionnaire(Page): form_model = 'player' form_fields = ['age', 'gender', 'grade', 'study_1', 'study_2', 'question_2', 'question_3_1', 'question_3_2', 'question_4_1', 'question_4_2', 'question_4_3', 'question_5_A', 'question_5_B', 'question_5_C', 'question_6', 'question_7', 'question_8'] @staticmethod def vars_for_template(player: Player): opponent = other_player(player) return dict( T=player.session.config['大多数人投资'], opponent_stage1_decision=opponent.stage1_decision, ) @staticmethod def error_message(player: Player, values): if values['study_1'] == 'others, please specify:' and values['study_2'] == ' ': return 'Please specify your field of study.' if values['question_3_1'] == 'None of the above accurately describes my guess. ' and values['question_3_2'] == ' ': return 'Please state how you chose in question 3.' if values['question_4_1'] == 'I compared both of the above, please state their relative importance: ' and values['question_4_2'] + values[ 'question_4_3'] != 100: return 'The sum of relative importance of the two options should be 100 in question 4.' class ResultsWaitPage(WaitPage): after_all_players_arrive = set_payoffs class Results(Page): @staticmethod def vars_for_template(player: Player): payoff = float(player.payoff) return dict( payoff=payoff ) page_sequence = [ Introduction, Exercise, ExerciseResults, Stage1Decision, Stage1ResultsWaitPage, Stage1Results, Stage2Decision, Stage2Feeling, Stage2ResultsWaitPage, Stage2Results, Questionnaire, ResultsWaitPage, Results ]