import random from otree.api import * #from openai import OpenAI from otree.models import session doc = """ Your app description """ class C(BaseConstants): NAME_IN_URL = 'Uncertainty_test' PLAYERS_PER_GROUP = 2 NUM_ROUNDS = 10 class Subsession(BaseSubsession): pass class Group(BaseGroup): clear = models.BooleanField() average_price = models.FloatField() optimization = models.BooleanField() class Player(BasePlayer): # pictures = models.BooleanField() necessary for explanation picture? # if comprehension test is needed, see steph # constants omega_current = models.IntegerField(default = 40) omega_pre = models.IntegerField(default = 40) omega_small = models.IntegerField(default = 20) omega_large = models.IntegerField(default = 60) omega_average = models.IntegerField(default = 40) cost_deduction = models.IntegerField(default = 10) maximum_price = models.IntegerField() shock = models.IntegerField(default=6) shock_announcement = models.BooleanField() display_shock = models.BooleanField() explanation = models.LongStringField(label="Begründung:") optimization_instruction = models.BooleanField() optimization = models.BooleanField() clear = models.BooleanField() clear_instruction = models.BooleanField() subgroup_id = models.IntegerField() final_subgroup_price = models.IntegerField() # player variables price = models.IntegerField(min=10) output = models.IntegerField() unit_costs = models.FloatField() total_costs = models.FloatField() revenue = models.FloatField() earnings = models.FloatField(min=0) revision_price = models.IntegerField(label="Bitte geben Sie Ihren finalen Preisvorschlag für Ihr Gut ab.", min=0) final_price = models.IntegerField(min=10) price_proposal = models.IntegerField(label='Preisvorschlag:',min=10) # variables for payoffs payoff_round = models.FloatField() # if I don't round, then it needs to be a float payoff_acc = models.FloatField() show_up_fee = models.CurrencyField(default=3.5) payoff_mon = models.CurrencyField() payoff_mon_final = models.CurrencyField() # Questionnaire Question_1 = models.IntegerField( choices=[[1, "Mein finaler Preisvorschlag ist der endgültige Preis für das Gut."], [2, "Der Preis wird zufällig zwischen den Preisvorschlägen, die zwischen den Teammitgliedern ausgetauscht werden, ausgewählt."], [3, "Der Preis wird zufällig aus den finalen Preisvorschlägen ausgewählt, die beide Teammitglieder nach dem Austausch der ersten Preisvorschläge festlegen."]], label="Frage 1: Wie wird der endgültige Preis für das Gut Ihres Teams in jeder Runde festgelegt?", widget=widgets.RadioSelect) Question_2 = models.IntegerField( choices=[[1, "Der Preis des eigenen Gutes."], [2, "Der Durchschnittspreis aller Produzenten in allen Runden."], [3, "Der Durchschnittspreis aller Produzenten in der jeweiligen Runde."]], label="Frage 2: Welche der folgenden Variablen hat einen Einfluss auf die Stückkosten Ihres Gutes?", widget=widgets.RadioSelect) Question_3 = models.IntegerField( choices=[[1, "Je höher der Preis p und je höher der exogene Faktor ω, desto höher die Nachfrage y."], [2, "Je höher der Preis p und je niedriger der exogene Faktor ω, desto niedriger die Nachfrage y."], [3, "Je höher der Preis p, desto niedriger die Nachfrage y. Der exogene Faktor ω hat keinen Einfluss auf die Nachfrage y."]], label="Frage 3: Welche Aussage über die Nachfrage nach Ihrem Gut ist richtig?", widget=widgets.RadioSelect) Question_4 = models.IntegerField( choices=[[1, "Das Gut wird automatisch in Höhe der Nachfrage produziert."], [2, "Die Produktion wird immer zufällig bestimmt."], [3, "Die Produktion hängt vom Durchschnittspreis ab."]], label="Frage 4: Wie wird die Produktion Ihres Gutes bestimmt?", widget=widgets.RadioSelect) attempts = models.IntegerField(blank=True) Q1_first_attempt = models.IntegerField(blank=True) Q2_first_attempt = models.IntegerField(blank=True) Q3_first_attempt = models.IntegerField(blank=True) Q4_first_attempt = models.IntegerField(blank=True) bonus = models.IntegerField(blank=True) bonus_eligible = models.BooleanField(blank=True) payoff_bonus = models.IntegerField(blank=True) #Demographics Gender = models.IntegerField( choices=[[1, "Weiblich"], [2, "Männlich"], [3, "Divers"]], label="Welchem Geschlecht fühlen Sie sich zugehörig?", widget=widgets.RadioSelect) Age = models.IntegerField(label="Wie alt sind Sie?", min=10, max=99) Faculty = models.IntegerField( choices=[[1, "Sozial- und Bildungswissenschaftliche Fakultät"], [2, "Geistes- und Kulturwissenschaftliche Fakultät"], [3, "Wirtschaftswissenschaftliche Fakultät"], [4, "Juristische Fakultät"], [5, "Fakultät für Informatik und Mathematik "], [6, "An mehreren Fakultäten"], [7, "Sonstiges"]], label="An welcher Fakultät studieren Sie?", widget=widgets.RadioSelect) #decision data keystroke_data = models.LongStringField(blank=True) # Stores JSON keystroke data keystroke_price_data = models.LongStringField(blank=True) # same for price def price_max(player): return player.omega_current def price_min(player): return player.cost_deduction def final_price_max(player): return player.omega_current def final_price_min(player): return player.cost_deduction def price_proposal_max(player): return player.omega_current def price_proposal_min(player): return player.cost_deduction # assign treatments def creating_session(subsession): for group in subsession.get_groups(): if subsession.round_number == 1: for i, player in enumerate(subsession.get_players()): player.subgroup_id = (i//2) import itertools clear_cycle = itertools.cycle([False, False, False, True]) for group in subsession.get_groups(): group.clear = next(clear_cycle) print(f"Group {group.id_in_subsession}: clear = {group.clear}") import itertools optimization_cycle = itertools.cycle([False, False, True, False]) for group in subsession.get_groups(): group.optimization = next(optimization_cycle) print(f"Group {group.id_in_subsession}: optimization = {group.optimization}") else: group.clear = group.in_round(1).clear group.optimization = group.in_round(1).optimization for player in subsession.get_players(): player.subgroup_id = player.in_round(1).subgroup_id # currently this is only for a positive shock of omega --> group statt player for p in group.get_players(): if 6<= group.round_number <=10: p.omega_current = p.omega_large for group in subsession.get_groups(): if group.round_number==Player.shock: group.display_shock=True else: group.display_shock=False for group in subsession.get_groups(): for p in group.get_players(): p.optimization_instruction = group.optimization #print(f"Player {p.id_in_group}: optimization_instruction = {p.optimization_instruction}") for group in subsession.get_groups(): for p in group.get_players(): p.clear_instruction = group.clear #print(f"Player {p.id_in_group}: clear_instruction = {p.clear_instruction}") def average_price(group): subgroups = {} group.average_price = 0 for p in group.get_players(): subgroups.setdefault(p.subgroup_id,[]).append(p) num_subgroups =len(subgroups) for subgroup in subgroups.values(): if len(subgroup)==2: p1, p2 = subgroup final_subgroup_price = random.choice([p1.price, p2.price]) p1.final_subgroup_price=final_subgroup_price p2.final_subgroup_price=final_subgroup_price for p in group.get_players(): p.final_price = final_subgroup_price group.average_price += p1.final_subgroup_price group.average_price /= num_subgroups def explanation(player): return player.explanation # calculating the earnings def round_results(group): players = group.get_players() for p in players: if p.price < 10: p.price = 10 subgroups = {} for p in group.get_players(): subgroups.setdefault(p.subgroup_id,[]).append(p) num_subgroups = len(subgroups) group.average_price = 0 for subgroup in subgroups.values(): if len(subgroup)==2: p1, p2 = subgroup final_subgroup_price = random.choice([p1.price, p2.price]) p1.final_subgroup_price=final_subgroup_price p2.final_subgroup_price=final_subgroup_price group.average_price += p1.final_subgroup_price group.average_price /= num_subgroups for p in players: p.output = (p.omega_current - p.final_subgroup_price) p.revenue = p.output * p.final_subgroup_price p.unit_costs = group.average_price - p.cost_deduction p.total_costs = p.unit_costs * p.output p.earnings = (p.final_subgroup_price - p.unit_costs) * p.output for p in players: if p.output < 0: p.output =0 if p.earnings < 0: p.earnings = 0 players = group.get_players() for p in players: p.payoff_round = p.earnings if group.round_number>1: player_in_all_rounds=p.in_all_rounds() p.payoff_acc = sum([p.payoff_round for p in player_in_all_rounds]) else: p.payoff_acc = p.payoff_round if p.in_round(1).attempts > 1: p.bonus_eligible = False p.bonus = 0 if p.in_round(1).attempts == 1: p.bonus_eligible = True p.bonus = 5 p.payoff_mon = p.payoff_acc + p.bonus if p.payoff_mon < 0.1: #? p.payoff_mon = 3.5 p.payoff_mon_final = p.payoff_mon + p.show_up_fee # as currently defined, there cannot be negative value in the end. otherwise use if conditions <0 = =0 # further currently there is no test or anything alike included, thus payoff = payoff_total # currently no calibration of the payoffs # PAGES class Introduction(Page): #timeout_seconds = 240 @staticmethod def is_displayed(player: Player): return (player.round_number == 1) class Instructions(Page): #timeout_seconds = 720 @staticmethod def is_displayed(player: Player): return (player.round_number == 1) form_model = 'player' form_fields = ['Question_1', 'Question_2', 'Question_3', 'Question_4', 'attempts', 'Q1_first_attempt', 'Q2_first_attempt', 'Q3_first_attempt', 'Q4_first_attempt'] # yet no dropout marker right now. if I include it, then include it for the payoff as well class StartWait(WaitPage): # after_all_players_arrive = 'assigned_roles' title_text = "Bitte warten Sie einen Moment." body_text = "Bitte warten Sie, bis die anderen Spieler bereit sind, um mit dem Experiment zu beginnen. " \ "Das Experiment wird in Kürze beginnen." class Price_and_Explanation(Page): @staticmethod def is_displayed(player: Player): return player.round_number in [1,5,6,7,10] form_model = 'player' form_fields = ['price_proposal','explanation', 'keystroke_data', 'keystroke_price_data'] class PriceProposal(Page): @staticmethod def is_displayed(player: Player): return player.round_number in [2, 3, 4, 8, 9] form_model = 'player' form_fields = ['price_proposal'] class Explanation(Page): @staticmethod def is_displayed(player: Player): return player.round_number in [1,5,6,7,10] form_model = 'player' form_fields = ['explanation'] class ExplanationWait(WaitPage): title_text = "Bitte warten Sie einen Moment." body_text = "Bitte warten Sie kurz bis die anderen Teilnehmenden bereit sind, das Experiment fortzusetzen. " \ " Es geht in Kürze weiter." class PriceDecision_with_explanation(Page): @staticmethod def is_displayed(player: Player): return player.round_number in [1,5,6,7,10] @staticmethod def vars_for_template(player: Player): group=player.group subgroups={} for p in group.get_players(): subgroups.setdefault(p.subgroup_id, []).append(p) # Find the other player in the same subgroup other_player_price = None other_player_explanation = None for subgroup in subgroups.values(): if len(subgroup) == 2: p1, p2 = subgroup if player.id_in_group == p1.id_in_group: other_player_price = p2.price_proposal # p1 sees p2's price other_player_explanation = p2.explanation elif player.id_in_group == p2.id_in_group: other_player_price = p1.price_proposal # p2 sees p1's price other_player_explanation = p1.explanation return {'other_player_price': other_player_price,'other_player_explanation': other_player_explanation} form_model = 'player' form_fields = ['price'] class PriceDecision_without_explanation(Page): @staticmethod def is_displayed(player: Player): return player.round_number in [2,3,4,8,9] @staticmethod def vars_for_template(player: Player): group=player.group subgroups={} for p in group.get_players(): subgroups.setdefault(p.subgroup_id, []).append(p) # Find the other player in the same subgroup other_player_price = None for subgroup in subgroups.values(): if len(subgroup) == 2: p1, p2 = subgroup if player.id_in_group == p1.id_in_group: other_player_price = p2.price_proposal # p1 sees p2's price elif player.id_in_group == p2.id_in_group: other_player_price = p1.price_proposal # p2 sees p1's price return {'other_player_price': other_player_price} form_model = 'player' form_fields = ['price'] class ResultsWait(WaitPage): after_all_players_arrive='round_results' title_text = "Bitte warten Sie einen Moment." body_text = "Bitte warten Sie, bis die anderen Spieler bereit sind, um mit dem Experiment zu beginnen." \ "Das Experiment wird in Kürze beginnen." class ResultsFeedback(Page): pass class ShockAnnouncement(Page): @staticmethod def is_displayed(player: Player): return (player.round_number == player.shock) class FinalResults(Page): @staticmethod def is_displayed(player: Player): return (player.round_number == 10) class FinalPage(Page): @staticmethod def is_displayed(player: Player): return (player.round_number == 10) page_sequence = [Introduction, Instructions, StartWait, ShockAnnouncement, Price_and_Explanation, PriceProposal, ExplanationWait, PriceDecision_without_explanation, PriceDecision_with_explanation, ResultsWait, ResultsFeedback, FinalResults, FinalPage]