from otree.api import *
import random
class C(BaseConstants):
NAME_IN_URL = 'DCE'
PLAYERS_PER_GROUP = None
NUM_ROUNDS = 6
CAR = 1
EBIKE = 2
BIKE = 3
WALK = 4
DIST_OPTIONS = ["1 km", "3 km", "5 km", "12 km", "18 km"]
WEATHER_OPTIONS = ["Sonnig", "Bewölkt", "Leichter Regen"]
DETOUR_OPTIONS = ["+ 1 km", "+ 3 km", "+ 5 km"]
BIKELANE_OPTIONS = [
"Separat abgegrenzter Fahrradweg",
"Fahrradstreifen direkt neben Hauptstrasse",
"Fahren im gemischten Verkehr",
]
LUGGAGE_OPTIONS = ["Kein Gepäck", "Rucksack"]
PARK_COST_OPTIONS = ["Gratis", "6 €", "12 €"]
PARK_AVAL_OPTIONS = ["Ausreichend Parkplätze", "Begrenzte Parkplätze", "Wenige Parkplätze"]
BIKE_COST_OPTIONS = ["Gratis", "6 € pro E-Bike", "12 € pro E-Bike", "24 € pro E-Bike"]
BIKE_CONVENIENCE_OPTIONS = ["Keine Registrierung/App erforderlich",
"Registrierung an der Rezeption
(Formular ausfüllen, Ausweis vorzeigen)",
"Registrierung über eine App erforderlich
(App herunterladen und registrieren)"]
BIKE_RETURN_OPTIONS = ["Rückgabe nur bei der Unterkunft",
"Rückgabe auch beim Badesse möglich"]
MIA_CHOICES = [
(1, 'Wetter'),
(2, 'Art des Fahrradweges'),
(3, 'Entfernung zum Badesee'),
(4, 'Notwendige Registrierung für Leihrad'),
(5, 'Leihgebühren für E-Bike'),
(6, 'Flexibilität bei der Rückgabe des Leihrads'),
(7, 'Parkgebühren'),
(8, 'Anzahl an Parkplätzen'),
(9, 'Möglichkeit einer alternativen Strecke'),
]
class Subsession(BaseSubsession):
pass
class Group(BaseGroup):
pass
class Player(BasePlayer):
# Generated by code; allow blank so model never throws required-field errors
distance = models.StringField(blank=True)
weather = models.StringField(blank=True)
detour = models.StringField(blank=True)
bikelane = models.StringField(blank=True)
luggage = models.StringField(blank=True)
park_cost = models.StringField(blank=True)
park_aval = models.StringField(blank=True)
bike_cost = models.StringField(blank=True)
bike_conv = models.StringField(blank=True)
bike_return = models.StringField(blank=True)
# Participant choice
DC_choice = models.IntegerField(
choices=[
[C.CAR, 'PKW'],
[C.BIKE, 'Fahrrad'],
[C.EBIKE, 'E-Bike'],
[C.WALK, 'Zu Fuß'],
]
)
DCE_MIA1 = models.IntegerField(label="", blank=True, choices=C.MIA_CHOICES)
DCE_MIA2 = models.IntegerField(label="", blank=True, choices=C.MIA_CHOICES)
DCE_MIA3 = models.IntegerField(label="", blank=True, choices=C.MIA_CHOICES)
DCE_OA = models.LongStringField(
label="Hatten Sie ausreichend Informationen, um Ihre Entscheidung zu treffen, oder fehlten Ihnen bestimmte Informationen?",
blank=True,
max_length=2000, # Adjusting for an approximate word-to-character ratio.
)
DCE_REALITY = models.IntegerField(
min=0,
max=10,
blank=False
)
DCE_REALITY2 = models.IntegerField(
min=0,
max=10,
blank=False
)
DCE_hed_1 = models.IntegerField(
min=0,
max=10,
blank=True
)
DCE_hed_2 = models.IntegerField(
min=0,
max=10,
blank=True
)
DCE_hed_3 = models.IntegerField(
min=0,
max=10,
blank=True
)
DCE_hed_4 = models.IntegerField(
min=0,
max=10,
blank=True
)
DCE_eud_1 = models.IntegerField(
min=0,
max=10,
blank=True
)
DCE_eud_2 = models.IntegerField(
min=0,
max=10,
blank=True
)
DCE_eud_3 = models.IntegerField(
min=0,
max=10,
blank=True
)
DCE_eud_4 = models.IntegerField(
min=0,
max=10,
blank=True
)
DCE_eud_5 = models.IntegerField(
min=0,
max=10,
blank=True
)
class DCE(Page):
form_model = 'player'
form_fields = ['DC_choice']
def is_displayed(player):
return player.participant.vars.get('survey_participate') == 1
@staticmethod
def vars_for_template(player):
app = player.subsession.session.config.get('DCE')
key = f"dce_attrs_round_{player.round_number}"
# 1) If not assigned yet for this participant+round: assign now
if key not in player.participant.vars:
player.participant.vars[key] = dict(
distance=random.choice(C.DIST_OPTIONS),
weather=random.choice(C.WEATHER_OPTIONS),
detour=random.choice(C.DETOUR_OPTIONS),
bikelane=random.choice(C.BIKELANE_OPTIONS),
luggage=random.choice(C.LUGGAGE_OPTIONS),
park_cost=random.choice(C.PARK_COST_OPTIONS),
park_aval=random.choice(C.PARK_AVAL_OPTIONS),
bike_cost=random.choice(C.BIKE_COST_OPTIONS),
bike_conv=random.choice(C.BIKE_CONVENIENCE_OPTIONS),
bike_return=random.choice(C.BIKE_RETURN_OPTIONS)
)
attrs = player.participant.vars[key]
# 2) Copy into model fields (stored in DB)
player.distance = attrs['distance']
player.weather = attrs['weather']
player.detour = attrs['detour']
player.bikelane = attrs['bikelane']
player.luggage = attrs['luggage']
player.park_cost = attrs['park_cost']
player.park_aval = attrs['park_aval']
player.bike_cost = attrs['bike_cost']
player.bike_conv = attrs['bike_conv']
player.bike_return = attrs['bike_return']
# 3) Provide to template
return attrs
@staticmethod
def before_next_page(player, timeout_happened):
"""
Persist per-round attributes + choice in participant.vars
so another app can use them later.
"""
all_rounds_key = "dce_round_data"
if all_rounds_key not in player.participant.vars:
player.participant.vars[all_rounds_key] = {}
r = player.round_number
player.participant.vars[all_rounds_key][r] = dict(
round=r,
distance=player.distance,
weather=player.weather,
detour=player.detour,
bikelane=player.bikelane,
luggage=player.luggage,
park_cost=player.park_cost,
park_aval=player.park_aval,
bike_cost=player.bike_cost,
bike_conv=player.bike_conv,
bike_return=player.bike_return,
choice=player.DC_choice,
)
class Q_post_DCE(Page):
form_model = 'player'
form_fields = ['DCE_MIA1' ,'DCE_MIA2' ,'DCE_MIA3' , 'DCE_REALITY', 'DCE_REALITY2', 'DCE_OA']
def is_displayed(player):
return player.round_number == C.NUM_ROUNDS and player.participant.vars.get('survey_participate') == 1
class DCE_bike(Page):
form_model = 'player'
form_fields = ['DCE_hed_1', 'DCE_hed_2', 'DCE_hed_3', 'DCE_hed_4', 'DCE_eud_1', 'DCE_eud_2', 'DCE_eud_3', 'DCE_eud_4', 'DCE_eud_5']
@staticmethod
def is_displayed(player):
if not (
player.round_number == C.NUM_ROUNDS
and player.participant.vars.get('survey_participate') == 1
):
return False
rounds = player.participant.vars.get("dce_round_data", {})
bike_like = [r for r, d in rounds.items() if d.get("choice") in [C.BIKE, C.EBIKE]]
if not bike_like:
return False
bike_r = min(bike_like)
# store picks so vars_for_template doesn't recompute
player.participant.vars["dce_followup_rounds"] = dict(bike_round=bike_r)
return True
@staticmethod
def vars_for_template(player):
rounds = player.participant.vars.get("dce_round_data", {})
picks = player.participant.vars.get("dce_followup_rounds", {})
bike_r = picks.get("bike_round")
return dict(
bike_r=bike_r,
bike_case=rounds.get(bike_r),
)
class DCE_car(Page):
form_model = 'player'
form_fields = ['DCE_hed_1', 'DCE_hed_2', 'DCE_hed_3', 'DCE_hed_4', 'DCE_eud_1', 'DCE_eud_2', 'DCE_eud_3', 'DCE_eud_4', 'DCE_eud_5']
@staticmethod
def is_displayed(player):
if not (
player.round_number == C.NUM_ROUNDS
and player.participant.vars.get('survey_participate') == 1
):
return False
rounds = player.participant.vars.get("dce_round_data", {})
car_like = [r for r, d in rounds.items() if d.get("choice") == C.CAR]
if not car_like:
return False
car_r = min(car_like)
# store picks so vars_for_template doesn't recompute
player.participant.vars["dce_followup_rounds"] = dict(car_round=car_r)
return True
@staticmethod
def vars_for_template(player):
rounds = player.participant.vars.get("dce_round_data", {})
picks = player.participant.vars.get("dce_followup_rounds", {})
car_r = picks.get("car_round")
return dict(
car_r=car_r,
car_case=rounds.get(car_r),
)
page_sequence = [DCE, DCE_bike, DCE_car, Q_post_DCE]