from otree.api import * import constants from .labels import * import random import constants doc = """ Stufe 1 """ class C(BaseConstants): NAME_IN_URL = "step1" PLAYERS_PER_GROUP = None NUM_ROUNDS = 1 FEMALE, MALE, DIVERSE = "w", "m", "d" # Randomized question blocks BLOCK1, BLOCK2, BLOCK3, BLOCK4, BLOCK5, BLOCK6 = "aa_", "mig_", "environ_", "gay_", "social_", "military_" RANDOMIZED_BLOCK1, RANDOMIZED_BLOCK2 = [BLOCK1, BLOCK2], [BLOCK3, BLOCK4, BLOCK5, BLOCK6] LABELS = Labels() # Participation fees PARTICIPATION_FEE_STEP1 = cu(constants.PARTICIPATION_FEE_STEP1) PARTICIPATION_FEE_STEP1_CENTS = constants.PARTICIPATION_FEE_STEP1_CENTS PARTICIPATION_FEE_STEP2 = cu(constants.PARTICIPATION_FEE_STEP2) # Public Goods Game GAME_POINTS = 20 GAME_POINTS_CONVERSION = cu(0.2) TOTAL_PROJECT_SHARE = 0.4 class Subsession(BaseSubsession): pass def creating_session(subsession: Subsession): # Initialize all questions for block in C.RANDOMIZED_BLOCK1 + C.RANDOMIZED_BLOCK2: for var in vars(Player): if var.startswith(block): Question.create( subsession=subsession, block=block, name=var, label=getattr(Labels, var), is_slider=("_afx" in var), ) # Draw randomized order of question blocks for each participant for p in subsession.get_players(): p.participant.question_order = random.sample(C.RANDOMIZED_BLOCK1, len(C.RANDOMIZED_BLOCK1)) + random.sample( C.RANDOMIZED_BLOCK2, len(C.RANDOMIZED_BLOCK2) ) class Group(BaseGroup): pass def get_scale_field(choices: range, label: str = "") -> models.IntegerField: return models.IntegerField(widget=widgets.RadioSelect, choices=choices, min=-5, label=label) def get_scale_field_pol(label: str) -> models.IntegerField: return get_scale_field(choices=range(-5, 6), label=label) def get_scale_field_val() -> models.IntegerField: return get_scale_field(choices=range(-2, 4)) def get_scale_field_pref(label: str) -> models.IntegerField: return get_scale_field(choices=range(11), label=label) def get_slider_field(label: str) -> models.IntegerField: return get_scale_field(choices=range(101), label=label) class Player(BasePlayer): donation = models.IntegerField(min=0, max=C.PARTICIPATION_FEE_STEP1_CENTS) participates_in_step2 = models.BooleanField( label="Bitte bestätigen Sie:", choices=[ [ True, f"Ja, ich möchte gern an einer weiteren wissenschaftlichen Studie teilnehmen und dazu gesondert eingeladen und mit {C.PARTICIPATION_FEE_STEP2} entlohnt werden.", ], [False, "Nein, ich bin nicht interessiert."], ], ) email_address = models.StringField(blank=True, label="Falls ja, geben Sie bitte Ihre E-Mail-Adresse an:") participation_code = models.StringField(label="Bitte erstellen Sie jetzt Ihren Teilnehmercode:") # Political questions aa_2 = get_scale_field_pol(Labels.aa_2) aa_5 = get_scale_field_pol(Labels.aa_5) aa_8 = get_scale_field_pol(Labels.aa_8) aa_9 = get_scale_field_pol(Labels.aa_9) aa_10 = get_scale_field_pol(Labels.aa_10) aa_afx_s = get_slider_field(Labels.aa_afx_s) aa_afx_r = get_slider_field(Labels.aa_afx_r) mig_1 = get_scale_field_pol(Labels.mig_1) mig_5 = get_scale_field_pol(Labels.mig_5) mig_8 = get_scale_field_pol(Labels.mig_8) mig_15 = get_scale_field_pol(Labels.mig_15) mig_17 = get_scale_field_pol(Labels.mig_17) mig_afx_s = get_slider_field(Labels.mig_afx_s) mig_afx_r = get_slider_field(Labels.mig_afx_r) environ_ess_1 = get_scale_field_pol(Labels.environ_ess_1) environ_ess_2 = get_scale_field_pol(Labels.environ_ess_2) environ_ess_3 = get_scale_field_pol(Labels.environ_ess_3) environ_afx_s = get_slider_field(Labels.environ_afx_s) environ_afx_r = get_slider_field(Labels.environ_afx_r) gay_ess_1 = get_scale_field_pol(Labels.gay_ess_1) gay_ess_2 = get_scale_field_pol(Labels.gay_ess_2) gay_ess_3 = get_scale_field_pol(Labels.gay_ess_3) gay_afx_s = get_slider_field(Labels.gay_afx_s) gay_afx_r = get_slider_field(Labels.gay_afx_r) social_ess_1 = get_scale_field_pol(Labels.social_ess_1) social_ess_2 = get_scale_field_pol(Labels.social_ess_2) social_3 = get_scale_field_pol(Labels.social_3) social_afx_s = get_slider_field(Labels.social_afx_s) social_afx_r = get_slider_field(Labels.social_afx_r) military_1 = get_scale_field_pol(Labels.military_1) military_2 = get_scale_field_pol(Labels.military_2) military_3 = get_scale_field_pol(Labels.military_3) military_afx_s = get_slider_field(Labels.military_afx_s) military_afx_r = get_slider_field(Labels.military_afx_r) # Comprehension questions comprehension1 = models.IntegerField(label="Ihr Einkommen:", min=0) comprehension2 = models.IntegerField(label="Ihr Einkommen:", min=0) # Public goods game project_contribution = models.IntegerField( label="Wie viele Punkte tragen Sie zum Projekt bei:", min=0, max=C.GAME_POINTS ) project_contribution_player2 = models.IntegerField(label="Gruppenmitglied 2:", min=0, max=C.GAME_POINTS) project_contribution_player3 = models.IntegerField(label="Gruppenmitglied 3:", min=0, max=C.GAME_POINTS) project_contribution_player4 = models.IntegerField(label="Gruppenmitglied 4:", min=0, max=C.GAME_POINTS) # Values val_benevolence1 = get_scale_field_val() val_benevolence2 = get_scale_field_val() val_universalism1 = get_scale_field_val() val_universalism2 = get_scale_field_val() val_universalism3 = get_scale_field_val() val_self_direction1 = get_scale_field_val() val_self_direction2 = get_scale_field_val() val_stimulation1 = get_scale_field_val() val_stimulation2 = get_scale_field_val() val_hedonism1 = get_scale_field_val() val_hedonism2 = get_scale_field_val() val_achievement1 = get_scale_field_val() val_achievement2 = get_scale_field_val() val_power1 = get_scale_field_val() val_power2 = get_scale_field_val() val_security1 = get_scale_field_val() val_security2 = get_scale_field_val() val_conformity1 = get_scale_field_val() val_conformity2 = get_scale_field_val() val_tradition1 = get_scale_field_val() val_religion = get_scale_field_val() val_work_ethic = get_scale_field_val() val_modesty = get_scale_field_val() # Preferences pref_s_risk_taking1 = get_scale_field_pref(Labels.pref_risk_taking1) pref_c_risk_taking2 = models.CurrencyField(label=Labels.pref_risk_taking2, min=0) pref_s_time_discounting1 = get_scale_field_pref(Labels.pref_time_discounting1) pref_s_time_discounting2 = get_scale_field_pref(Labels.pref_time_discounting2) pref_s_trust1 = get_scale_field_pref(Labels.pref_trust1) pref_s_trust2 = get_scale_field_pref(Labels.pref_trust2) pref_s_altruism1 = get_scale_field_pref(Labels.pref_altruism1) pref_c_altruism2 = models.CurrencyField(label=Labels.pref_altruism2, min=0, max=1000) pref_s_positive_reciprocity1 = get_scale_field_pref(Labels.pref_positive_reciprocity1) pref_s_positive_reciprocity2 = get_scale_field_pref(Labels.pref_positive_reciprocity2) pref_s_negative_reciprocity1 = get_scale_field_pref(Labels.pref_negative_reciprocity1) pref_s_negative_reciprocity2 = get_scale_field_pref(Labels.pref_negative_reciprocity2) # Sociodemographic questions gender = models.StringField( label="Bitte wählen Sie Ihr Geschlecht:", widget=widgets.RadioSelectHorizontal, choices=[[C.MALE, "männlich"], [C.FEMALE, "weiblich"], [C.DIVERSE, "divers"]], ) birthyear = models.IntegerField( label="In welchem Jahr wurden Sie geboren?", min=1921, max=2022, choices=range(2022, 1922, -1) ) political_affiliation = models.StringField( label="Welche politische Partei haben Sie bei der letzten Bundestagswahl gewählt:", choices=["SPD", "CDU/CSU", "Grüne", "Die Linke", "AfD", "FDP", "andere"], widget=widgets.RadioSelect, ) marital_status = models.StringField( label="Wie ist Ihr aktueller Familienstand?", choices=["Single", "Verheiratet", "Geschieden", "Verwitwet"], widget=widgets.RadioSelect, ) nr_children = models.IntegerField( label="Wie viele Kinder haben Sie?", min=0, ) nr_siblings = models.IntegerField(label="Wie viele Geschwister haben Sie?", min=0) education = models.StringField( label="Welches ist der höchste Bildungsabschluss, den Sie haben?", choices=[ "Hauptschulabschluss", "Realschulabschluss", "Abitur", "Berufsausbildung", "Hochschulabschluss", "Promotion", ], widget=widgets.RadioSelect, ) religious_affiliation = models.StringField( label="Welche Religionszugehörigkeit haben Sie?", choices=[ "Agnostiker", "Atheist", "Buddhist", "Christ", "Hinduist", "Jude", "Muslim", "Andere", "Keine Präferenz", ], widget=widgets.RadioSelect, ) religion_role = models.StringField( label="Spielt Ihre Konfession eine wichtige Rolle in Ihrem täglichen Leben?", choices=[ "Spielt eine sehr wichtige Rolle", "Spiel eine wichtige Rolle", "Spielt eine Rolle", "Spielt eine kleine Rolle", "Ist nicht wichtig", ], widget=widgets.RadioSelect, ) income = models.StringField( label="Wie hoch ist Ihr monatliches verfügbares Einkommen?", choices=["weniger als 500€", "500 € - 1000 €", "1000 € - 2000 €", "2000 € - 5000 €", "mehr als 5000 €"], widget=widgets.RadioSelect, ) travel = models.StringField( label="Wie oft reisen Sie außerhalb Ihres Staates (mit Ausnahme der Zeit der Coronavirus-Pandemie)?", choices=[ "Nie", "Kaum (weniger als 1x in 2-3 Jahren)", "Gelegentlich (1x in 2-3 Jahren)", "Regelmäßig (1x pro Jahr)", "Oft (mehr als 1x im Jahr)", ], widget=widgets.RadioSelect, ) # Social media usage questions smedia_month = models.IntegerField( label="Wie oft haben Sie im letzten Monat soziale Medien genutzt?", choices=[ [1, "Weniger als einmal pro Woche"], [2, "Weniger als einmal pro Tag"], [3, "2-3 Mal pro Tag"], [4, "4-5 Mal pro Tag"], [5, "6 oder mehr Male pro Tag"], ], widget=widgets.RadioSelect, ) smedia_daily_average = models.IntegerField( label="Wie viel Zeit verbringen Sie täglich online in den sozialen Medien (die durchschnittliche Zeit, die Sie täglich in sozialen Medien online sind)", choices=[ [1, "weniger als 30 Minuten"], [2, "31 min - 2 Stunden"], [3, "2 - 6 Stunde"], [4, "6 - 12 Stunden"], [5, "12 oder mehr Stunden"], ], widget=widgets.RadioSelect, ) smedia_daily_actual = models.IntegerField( label="Wie viel Zeit verbringen Sie tatsächlich jeden Tag mit sozialen Medien? (die tatsächliche Zeit, die Sie jeden Tag in den sozialen Medien verbringen)", choices=[ [1, "weniger als 30 Minuten"], [2, "31 min - 60 Minuten"], [3, "1 - 2 Stunden"], [4, "2 - 4 Stunde"], [5, "4 oder mehr Stunden"], ], widget=widgets.RadioSelect, ) smedia_years = models.IntegerField( label="Wie viele Jahre nutzen Sie schon soziale Medien? (die Länge der Zeit, in der Sie sich aktiv in sozialen Medien bewegen)", choices=[ [1, "Weniger als 3 Jahre"], [2, "4 - 5 Jahre"], [3, "6 - 7 Jahre"], [4, "8 - 9 Jahre"], [5, "10 oder mehr Jahre"], ], widget=widgets.RadioSelect, ) smedia_friends = models.IntegerField( label="Wie viele Freunde haben Sie in den Socialen Medien? (die Anzahl der Freunde in den sozialen Medien)", choices=[ [1, "Weniger als 50 Personen"], [2, "51 - 100 Personen"], [3, "101 - 150 Personen"], [4, "151 - 200 Personen"], [5, "mehr als 200 Personen"], ], widget=widgets.RadioSelect, ) smedia_habit = models.IntegerField(label="Die Nutzung sozialer Medien ist meine tägliche Gewohnheit.") smedia_time = models.IntegerField(label="Ich surfe in den sozialen Medien, wann immer ich Zeit habe.") smedia_late = models.IntegerField( label="Selbst wenn es schon spät ist, schaue ich mir die sozialen Medien vor dem Schlafengehen an." ) smedia_relax = models.IntegerField(label="Ich nutze soziale Medien oft, um mich aus Gewohnheit zu entspannen.") smedia_attention = models.IntegerField( label="Ich profitiere von der Aufmerksamkeit und den Kommentaren anderer in den sozialen Medien." ) smedia_support = models.IntegerField( label="Die Unterstützung und Ermutigung durch andere in den sozialen Medien ist mir sehr wichtig." ) smedia_friendships = models.IntegerField( label="Durch die Nutzung sozialer Medien bin ich mit der Beziehung zwischen mir und meinen Freunden zufrieden." ) smedia_comfortable = models.IntegerField( label="Im Vergleich zur realen Welt fühle ich mich in den sozialen Medien wohler." ) smedia_bored = models.IntegerField(label="Ich fühle mich gelangweilt, wenn ich soziale Medien nicht nutzen kann.") smedia_happy = models.IntegerField( label="Im Vergleich zur realen Welt bin ich glücklicher, wenn ich mich über soziale Medien austausche." ) smedia_anxious = models.IntegerField( label="Ich fühle mich ängstlich, wenn ich die sozialen Medien nicht nutzen kann." ) smedia_facebook = models.IntegerField(label="Facebook") smedia_twitter = models.IntegerField(label="Twitter") smedia_instagram = models.IntegerField(label="Instagram") smedia_youtube = models.IntegerField(label="YouTube") smedia_tiktok = models.IntegerField(label="TikTok") smedia_linkedin = models.IntegerField(label="LinkedIn") smedia_other = models.IntegerField(label="Andere") smedia_has_twitter = models.BooleanField(label="Haben Sie einen eigenen Twitter Account?") smedia_activity_read = models.IntegerField(label="Beiträge lesen/ansehen") smedia_activity_post = models.IntegerField(label="Beiträge (Post/Videos,…) veröffentlichen") smedia_activity_comment = models.IntegerField(label="Kommentieren") smedia_activity_like = models.IntegerField(label="Liken") smedia_activity_message = models.IntegerField(label="Privatnachrichten schreiben") smedia_use_exchange_family = models.BooleanField(label="Austausch mit engen Freunden/Familien", blank=True) smedia_use_exchange_acquaint = models.BooleanField(label="Austausch mit Bekannten", blank=True) smedia_use_new_people = models.BooleanField(label="Kontakt zu neuen Personen", blank=True) smedia_use_news = models.BooleanField(label="Informationen über Tagesgeschehen gewinnen (Nachrichten)", blank=True) smedia_use_events = models.BooleanField(label="Informationen über Veranstaltungen und Events", blank=True) smedia_use_present_self = models.BooleanField( label="Um etwas über mich zu zeigen (z.B. Bilder, Posts)", blank=True ) smedia_use_celebs = models.BooleanField(label="Um Personen des öffentlichen Lebens/Promis zu folgen", blank=True) smedia_use_brands = models.BooleanField(label="Um Unternehmen/Marken zu folgen", blank=True) smedia_use_professional = models.BooleanField( label="Um bestehende berufliche Kontakte zu pflegen / Netzwerke zu bilden", blank=True ) smedia_use_entertainment = models.BooleanField(label="Zur Unterhaltung/ Zeitvertreib", blank=True) class Question(ExtraModel): subsession = models.Link(Subsession) block = models.StringField() name = models.StringField() label = models.StringField() is_slider = models.BooleanField() # PAGES class Welcome(Page): pass class Instructions(Page): @staticmethod def vars_for_template(player: Player): return dict(likert_questions=[]) class RandomQuestionBlock(Page): """ A Page which's form fields are determined by the random `question_order` of the participant """ form_model = "player" template_name = "step1/RandomQuestionBlock.html" @staticmethod def get_vars_for_template_by_index(player: Player, i: int): return dict( page_nr=(i + 1), likert_questions=RandomQuestionBlock.get_likert_questions_for_block(player, i), slider_questions=RandomQuestionBlock.get_slider_questions_for_block(player, i), slider_label=RandomQuestionBlock.get_slider_label(player, i), ) @staticmethod def get_field_names_for_block(player: Player, i: int): """ :param player: Each player has a different `question_order` :param i: The index of the requested question block in the player's random `question_order` :return: All formfields that correspond to the question block of `question_order[i]` """ question_block = player.participant.question_order[i] questions = Question.filter(subsession=player.subsession, block=question_block) return [q.name for q in questions] @staticmethod def get_likert_questions_for_block(player: Player, i: int): question_block = player.participant.question_order[i] return Question.filter(subsession=player.subsession, block=question_block, is_slider=False) @staticmethod def get_slider_questions_for_block(player: Player, i: int): question_block = player.participant.question_order[i] return Question.filter(subsession=player.subsession, block=question_block, is_slider=True) @staticmethod def get_slider_label(player: Player, i: int): question_block = player.participant.question_order[i] return getattr(Labels, question_block + "afx") class Questions1(RandomQuestionBlock): @staticmethod def get_form_fields(player: Player): return RandomQuestionBlock.get_field_names_for_block(player, 0) @staticmethod def vars_for_template(player: Player): return RandomQuestionBlock.get_vars_for_template_by_index(player, 0) class Questions2(RandomQuestionBlock): @staticmethod def get_form_fields(player: Player): return RandomQuestionBlock.get_field_names_for_block(player, 1) @staticmethod def vars_for_template(player: Player): return RandomQuestionBlock.get_vars_for_template_by_index(player, 1) class Questions3(RandomQuestionBlock): @staticmethod def get_form_fields(player: Player): return RandomQuestionBlock.get_field_names_for_block(player, 2) @staticmethod def vars_for_template(player: Player): return RandomQuestionBlock.get_vars_for_template_by_index(player, 2) class Questions4(RandomQuestionBlock): @staticmethod def get_form_fields(player: Player): return RandomQuestionBlock.get_field_names_for_block(player, 3) @staticmethod def vars_for_template(player: Player): return RandomQuestionBlock.get_vars_for_template_by_index(player, 3) class Questions5(RandomQuestionBlock): @staticmethod def get_form_fields(player: Player): return RandomQuestionBlock.get_field_names_for_block(player, 4) @staticmethod def vars_for_template(player: Player): return RandomQuestionBlock.get_vars_for_template_by_index(player, 4) class Questions6(RandomQuestionBlock): @staticmethod def get_form_fields(player: Player): return RandomQuestionBlock.get_field_names_for_block(player, 5) @staticmethod def vars_for_template(player: Player): return RandomQuestionBlock.get_vars_for_template_by_index(player, 5) class GameInstructions1(Page): pass class GameInstructions2(Page): pass class GameInstructions3(Page): pass class GameInstructions4(Page): pass class GameInstructions5(Page): pass class GameComprehension(Page): form_model = "player" form_fields = ["comprehension1", "comprehension2"] @staticmethod def error_message(player: Player, values): if values["comprehension1"] != 32: return "Ihre Antwort zur 1. Aufgabe war falsch. Bitte versuchen Sie es erneut." if values["comprehension2"] != 18: return "Ihre Antwort zur 2. Aufgabe war falsch. Bitte versuchen Sie es erneut." class GameDecision1(Page): form_model = "player" form_fields = ["project_contribution"] class GameDecision2(Page): form_model = "player" form_fields = ["project_contribution_player2", "project_contribution_player3", "project_contribution_player4"] class QuestionsGender(Page): form_model = "player" form_fields = ["gender"] class QuestionsValues(Page): form_model = "player" form_fields = [var for var in vars(Player) if var.startswith("val_")] @staticmethod def vars_for_template(player: Player): return dict(labels=C.LABELS.get_value_labels(player.gender)) class QuestionsPreferences1(Page): form_model = "player" @staticmethod def get_form_fields(player: Player): return [var for var in vars(player) if var.startswith("pref_s")] class QuestionsPreferences2(Page): form_model = "player" @staticmethod def get_form_fields(player: Player): return [var for var in vars(player) if var.startswith("pref_c")] class QuestionsDemographics(Page): form_model = "player" form_fields = [ "birthyear", "political_affiliation", "marital_status", "nr_children", "nr_siblings", "education", "religious_affiliation", "religion_role", "income", "travel", ] class QuestionsSocialMediaUsage1(Page): form_model = "player" form_fields = ["smedia_month", "smedia_daily_average", "smedia_daily_actual", "smedia_years", "smedia_friends"] class QuestionsSocialMediaUsage2(Page): form_model = "player" form_fields = [ "smedia_habit", "smedia_time", "smedia_late", "smedia_relax", "smedia_attention", "smedia_support", "smedia_friendships", "smedia_comfortable", "smedia_bored", "smedia_happy", "smedia_anxious", ] class QuestionsSocialMediaUsage3(Page): form_model = "player" form_fields = [ "smedia_facebook", "smedia_twitter", "smedia_instagram", "smedia_youtube", "smedia_tiktok", "smedia_linkedin", "smedia_other", ] class QuestionsSocialMediaUsage4(Page): form_model = "player" form_fields = ["smedia_has_twitter"] class QuestionsSocialMediaUsage5(Page): form_model = "player" form_fields = [ "smedia_activity_read", "smedia_activity_post", "smedia_activity_comment", "smedia_activity_like", "smedia_activity_message", ] class QuestionsSocialMediaUsage6(Page): form_model = "player" form_fields = [ "smedia_use_exchange_family", "smedia_use_exchange_acquaint", "smedia_use_new_people", "smedia_use_news", "smedia_use_events", "smedia_use_present_self", "smedia_use_celebs", "smedia_use_brands", "smedia_use_professional", "smedia_use_entertainment", ] class Donation(Page): form_model = "player" form_fields = ["donation"] @staticmethod def before_next_page(player: Player, timeout_happened): player.participant.payment = C.PARTICIPATION_FEE_STEP1 - cu(player.donation / 100) class Consent(Page): form_model = "player" form_fields = ["participates_in_step2", "email_address"] @staticmethod def error_message(player, values): if values["participates_in_step2"] and "@" not in values["email_address"]: return 'Wenn Sie mit "Ja" antworten, müssen Sie eine gültige E-Mail-Adresse angeben.' @staticmethod def before_next_page(player: Player, timeout_happened): player.participant.payment = C.PARTICIPATION_FEE_STEP1 class ParticipationCode(Page): form_model = "player" form_fields = ["participation_code"] @staticmethod def is_displayed(player: Player): return player.participates_in_step2 page_sequence = [ Welcome, Instructions, Questions1, Questions2, Questions3, Questions4, Questions5, Questions6, GameInstructions1, GameInstructions2, GameInstructions3, GameInstructions4, GameInstructions5, GameComprehension, GameDecision1, GameDecision2, QuestionsGender, QuestionsValues, QuestionsPreferences1, QuestionsPreferences2, QuestionsDemographics, QuestionsSocialMediaUsage1, QuestionsSocialMediaUsage2, QuestionsSocialMediaUsage3, QuestionsSocialMediaUsage4, QuestionsSocialMediaUsage5, QuestionsSocialMediaUsage6, # Donation, Consent, ParticipationCode, ]