from otree.api import * import random import itertools import json from collections import Counter import ast # Necessary to parse the string list from checkboxes doc = """ Your app description """ class C(BaseConstants): NAME_IN_URL = 'contingent_valuation' PLAYERS_PER_GROUP = None NUM_ROUNDS = 1 BID_PAIRS = list(range(1, 7)) # 1 to 6 BID_ORDERS = ['upper_first', 'lower_first'] class Subsession(BaseSubsession): pass class Group(BaseGroup): pass class Player(BasePlayer): bid_pair = models.IntegerField() bid_order = models.StringField() monthly_increase_1 = models.IntegerField() monthly_increase_2 = models.IntegerField() percentage_increase_1 = models.IntegerField() percentage_increase_2 = models.IntegerField() res_knowledge = models.StringField( choices=[ [1, "Noch nie davon gehört"], [2, "Nur selten/ am Rande davon gehört"], [3, "Davon gehört aber kenne mich nicht aus"], [4, "Kenne mich grundlegend damit aus"], [5, "Kenne mich sehr gut damit aus"] ], label="Haben Sie sich vor dieser Befragung schon einmal mit dem Thema Energiesicherheit beschäftigt?", widget=widgets.RadioSelect ) res_interest = models.StringField( choices=[ [1, "Ja"], [2, "Vielleicht"], [3, "Nein"] ], label="Haben Sie grundsätzlich Interesse am Thema Energiesicherheit? " "(Mit dieser Frage wollen wir überprüfen, wie genau Sie die Fragen lesen. " "Bitte wählen Sie als Antwort „Vielleicht“ aus, unabhängig von Ihrem tatsächlichen Interesse.) ", widget=widgets.RadioSelect ) failed_attention_check = models.BooleanField(initial=False) support = models.IntegerField( choices=[ [1, "Lehne ich stark ab"], [2, "Lehne ich eher ab"], [3, "Weder noch"], [4, "Befürworte ich eher"], [5, "Befürworte ich stark"], ], label="Bitte geben Sie an, ob Sie Maßnahmen zur Stärkung der Resilienz des deutschen Energiesystems eher befürworten oder ablehnen.", widget=widgets.RadioSelect ) R1 = models.IntegerField( choices=[ [1, "Ja"], [0, "Nein"] ], label="", widget=widgets.RadioSelect ) R2 = models.IntegerField( choices=[ [1, "Ja"], [0, "Nein"] ], label="", widget=widgets.RadioSelect ) S = models.IntegerField( choices=[ [1, "Ja"], [0, "Nein"] ], label="Wären Sie grundsätzlich bereit, für ein resilienteres Energiesystem irgendeine Strompreiserhöhung in Kauf zu nehmen?", widget=widgets.RadioSelect ) why = models.LongStringField( label="Sie haben angegeben, dass Sie grundsätzlich bereit sind, zusätzliche Kosten für ein resilienteres Energiesystem zu zahlen. " "Warum sind Sie dazu bereit?", blank=True, ) why_dk = models.BooleanField( label="Keine Angabe", blank=True ) why_not = models.StringField( label="Aus welchen Gründen sind Sie nicht bereit, zusätzliche Kosten für ein resilienteres Energiesystem zu tragen? " "Bitte wählen Sie alle zutreffenden Gründe aus.", blank=True ) why_not_other = models.StringField( blank=True, label="Bitte geben Sie andere Gründe an." ) gas_crisis = models.IntegerField( choices=[ [1, "Überhaupt nicht beeinflusst"], [2, "Eher nicht beeinflusst"], [3, "Weder noch"], [4, "Eher beeinflusst"], [5, "Stark beeinflusst"] ], label="Das Risiko von Energieabhängigkeit hat sich während der Gaskrise der Jahre 2022/ 2023 sowie während des Krieges im Nahen Osten gezeigt. " "Wie stark haben diese Erfahrungen Ihre Antwort beeinflusst?", widget=widgets.RadioSelect ) def creating_session(subsession: Subsession): players = subsession.get_players() # All combinations of main group × bid pair × bid order combinations = list(itertools.product(C.BID_PAIRS, C.BID_ORDERS)) # Repeat pattern enough times to cover all players repeats_needed = -(-len(players) // len(combinations)) # ceil division assignment_pool = combinations * repeats_needed # Shuffle while keeping full coverage random.shuffle(assignment_pool) # Assign for player, (bid_pair, bid_order) in zip(players, assignment_pool): player.bid_pair = bid_pair player.bid_order = bid_order # --- Debug summary --- counts_bid_pair = Counter([p.bid_pair for p in players]) counts_bid_order = Counter([p.bid_order for p in players]) # --- Debug summary (nested) --- print("\n=== DEBUG: Randomization Tree ===") for bp in sorted(set(C.BID_PAIRS)): bp_players = [p for p in players if p.bid_pair == bp] if bp_players: print(f" {bp}: {len(bp_players)} players") for bo in sorted(set(C.BID_ORDERS)): bo_players = [p for p in bp_players if p.bid_order == bo] if bo_players: print(f" {bo}: {len(bo_players)} players") print("==================================\n") # PAGES class Res_Intro(Page): form_model = "player" class Res_Knowledge(Page): form_model = "player" form_fields = ["res_knowledge"] class Res_Interest(Page): form_model = "player" form_fields = ["res_interest"] def before_next_page(player, timeout_happened): # The correct answer is "2" (Vielleicht) if player.res_interest != "2": player.failed_attention_check = True class RedirectFailed(Page): @staticmethod def is_displayed(player): # Only show this page if they failed return player.failed_attention_check class Support(Page): form_model = "player" form_fields = ["support"] class CV1(Page): form_model = "player" form_fields = ["R1"] def vars_for_template(player): # Example bid pairs and their base (lower) bid values BID_VALUES = { 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, } base_bid = BID_VALUES.get(player.bid_pair, 0) # Show bid depending on order if player.bid_order == "lower_first": bid_to_show = base_bid else: # UpperFirst bid_to_show = base_bid + 1 # Retrieve stored yearly consumption yearly_consumption = player.participant.vars.get('energy_cons', 0) yearly_consumption_dk = player.participant.vars.get('energy_cons_dk', 0) energy_price = player.participant.vars.get('energy_price', 0) energy_price_dk = player.participant.vars.get('energy_price_dk', 0) if yearly_consumption_dk == False: # Calculate yearly and monthly costs yearly_cost = (yearly_consumption * bid_to_show) / 100 # Cent to Euro monthly_cost = yearly_cost / 12 else: # Calculate consumption based on devices device_washing_machine = player.participant.vars.get('device_washing_machine', 0) device_dryer = player.participant.vars.get('device_dryer', 0) device_oven = player.participant.vars.get('device_oven', 0) device_tv = player.participant.vars.get('device_tv', 0) device_ev = player.participant.vars.get('device_ev', 0) device_heat_pump = player.participant.vars.get('device_heat_pump', 0) device_computer = player.participant.vars.get('device_computer', 0) device_dishwasher = player.participant.vars.get('device_dishwasher', 0) device_microwave = player.participant.vars.get('device_microwave', 0) device_water_heater = player.participant.vars.get('device_water_heater', 0) household_size = player.participant.vars.get('household_size', 0) living_size = player.participant.vars.get('living_size', 0) yearly_consumption = household_size * 200 + living_size * 9 + device_washing_machine * 200 + \ device_dryer * 200 + device_oven * 200 + device_tv * 200 + device_ev * 200 + \ device_heat_pump * 200 + device_computer * 200 + device_dishwasher * 200 + \ device_microwave * 200 + device_water_heater * 200 yearly_cost = (yearly_consumption * bid_to_show) / 100 # Cent to Euro monthly_cost = yearly_cost / 12 if energy_price_dk == False: percentage = (energy_price + bid_to_show)/energy_price - 1 else: percentage = (37 + bid_to_show)/37 - 1 player.monthly_increase_1 = max(round(monthly_cost), 1) player.percentage_increase_1 = round(percentage * 100) info_text = f"