from otree.api import * import random import math # ========================================================= # CONSTANTS # ========================================================= class C(BaseConstants): NAME_IN_URL = 'venturini_pg' PLAYERS_PER_GROUP = None NUM_ROUNDS = 1 ENDOWMENT = 10 MULTIPLIER = 2.0 INITIAL_POINTS = 5 class Subsession(BaseSubsession): pass def creating_session(subsession: Subsession): players = subsession.get_players() # ===================================== # Assign participant ID (P1, P2, P3...) # ===================================== for p in players: pid = f"P{p.participant.id_in_session}" p.participant.vars['participant_id'] = pid random.shuffle(players) N = len(players) n_stem = N // 2 stem_players = players[:n_stem] control_players = players[n_stem:] for p in stem_players: p.participant.vars['treatment'] = 'STEM' for p in control_players: p.participant.vars['treatment'] = 'CONTROL' stem_groups = make_groups_pref_4(stem_players) control_groups = make_groups_pref_4(control_players) subsession.set_group_matrix(stem_groups + control_groups) def make_groups_pref_4(players): players = list(players) random.shuffle(players) n = len(players) sizes = [] while n > 0: if n in (4, 5, 3): sizes.append(n) break elif n == 6: sizes.extend([3, 3]); break elif n == 7: sizes.extend([4, 3]); break elif n == 8: sizes.extend([4, 4]); break elif n == 9: sizes.extend([4, 5]); break else: sizes.append(4) n -= 4 groups = [] idx = 0 for s in sizes: groups.append(players[idx:idx + s]) idx += s return groups # ========================================================= # GROUP # ========================================================= def round_half_up(x: float) -> int: # arrotonda all'intero più vicino, .5 sempre verso l'alto return int(math.floor(x + 0.5)) class Group(BaseGroup): total_contribution = models.IntegerField() individual_share = models.FloatField() def set_payoffs(self): players = self.get_players() n = len(players) # ========================= # A) STRATEGY: computed conditional (per player i) # basata SOLO sulle unconditional degli altri (contribution_strategy) # ========================= uncond = [p.contribution_strategy for p in players] computed_cond = {} avg_rounded_map = {} for i, p in enumerate(players): avg_others = (sum(uncond) - uncond[i]) / (n - 1) if n > 1 else 0 k = round_half_up(avg_others) k = max(0, min(C.ENDOWMENT, k)) avg_rounded_map[p.id_in_group] = k cc = getattr(p, f"strategy_{k}") computed_cond[p.id_in_group] = cc # salva per debug/risultati p.avg_others_uncond_rounded = k p.computed_conditional = cc # ========================= # B) ONESHOT PGG: contributi "reali" one-shot + beliefs # ========================= oneshot = [p.contribution_oneshot for p in players] oneshot_total = sum(oneshot) oneshot_share = (oneshot_total * C.MULTIPLIER) / n if n > 0 else 0 # BONUS INDIVIDUALE beliefs belief_bonus_map = {} for i, p in enumerate(players): avg_others = (oneshot_total - oneshot[i]) / (n - 1) if n > 1 else 0 correct_value = round_half_up(avg_others) belief_bonus_map[p.id_in_group] = 2 if p.belief_others == correct_value else 0 # ========================= # C) Per ogni player: random 50/50 tra (strategy payoff) e (oneshot+beliefs payoff) # ========================= for i, p in enumerate(players): use_strategy = random.random() < 0.5 if use_strategy: # ----- STRATEGY payoff per i (indipendente) use_unconditional = random.random() < 0.5 if use_unconditional: # i usa la sua unconditional; altri = computed conditional own = uncond[i] others_sum = sum(computed_cond[q.id_in_group] for q in players if q != p) p.payoff_rule = "unconditional" else: # i usa la sua computed conditional; altri = unconditional own = computed_cond[p.id_in_group] others_sum = sum(uncond[j] for j, q in enumerate(players) if q != p) p.payoff_rule = "table" total_used = own + others_sum share_used = (total_used * C.MULTIPLIER) / n if n > 0 else 0 payoff_pgg = C.ENDOWMENT - own + share_used p.payoff_mode = "strategy" p.belief_bonus = 0 else: # ----- ONESHOT + BELIEFS payoff own = oneshot[i] total_used = oneshot_total share_used = oneshot_share payoff_pgg = C.ENDOWMENT - own + share_used p.payoff_mode = "oneshot" p.payoff_rule = "" p.belief_bonus = belief_bonus_map[p.id_in_group] payoff_final = C.INITIAL_POINTS + payoff_pgg + p.belief_bonus # salva componenti per template p.used_own_contribution = int(own) p.used_total_contribution = float(total_used) p.used_share = float(share_used) p.payoff_pgg_component = float(payoff_pgg) # arrotonda (come hai chiesto) p.payoff_final_rounded = round_half_up(payoff_final) # payoff ufficiale oTree p.payoff = p.payoff_final_rounded def set_payoffs_wrapper(group: Group): group.set_payoffs() # ========================================================= # PLAYER # ========================================================= class Player(BasePlayer): writing_answer = models.LongStringField() # --- RESULTS (salvati per-player perché nello strategy cambiano per player) --- payoff_mode = models.StringField() # "strategy" oppure "oneshot" payoff_rule = models.StringField() # "unconditional" oppure "table" (solo se strategy) used_own_contribution = models.IntegerField() used_total_contribution = models.FloatField() used_share = models.FloatField() payoff_pgg_component = models.FloatField() # payoff del PGG (prima del belief bonus) belief_bonus = models.IntegerField(initial=0) payoff_final_rounded = models.IntegerField() # --- per debug/trasparenza (opzionali ma utili) --- avg_others_uncond_rounded = models.IntegerField() computed_conditional = models.IntegerField() # === Word completion: ogni "_" è un campo === # _ _ _ zione wc1_a = models.StringField(max_length=1) wc1_b = models.StringField(max_length=1) wc1_c = models.StringField(max_length=1) # f _ _ _ ione wc2_a = models.StringField(max_length=1) wc2_b = models.StringField(max_length=1) wc2_c = models.StringField(max_length=1) # co _ _ no wc3_a = models.StringField(max_length=1) wc3_b = models.StringField(max_length=1) # ma _ _ a wc4_a = models.StringField(max_length=1) wc4_b = models.StringField(max_length=1) # _ _ tta wc5_a = models.StringField(max_length=1) wc5_b = models.StringField(max_length=1) # _ _ so wc6_a = models.StringField(max_length=1) wc6_b = models.StringField(max_length=1) mc_q1 = models.IntegerField( choices=[ (1, '20 punti'), (2, '10 punti'), (3, '30 punti'), ], widget=widgets.RadioSelect, label="Quanti punti ricevi all’inizio di questa seconda attività dell’esperimento?", ) mc_q2 = models.IntegerField( choices=[ (1, 'I punti che contribuisci vengono raddoppiati e poi redistribuiti equamente tra tutti i partecipanti del gruppo'), (2, 'I punti che contribuisci vengono persi e non tornano a nessuno'), (3, 'I punti che contribuisci vengono raddoppiati e tornano solo a te'), ], widget=widgets.RadioSelect, label="Cosa succede ai punti che contribuisci al Fondo Comune?", ) mc_q3 = models.IntegerField( choices=[ (1, '17 punti'), (2, '7 punti'), (3, '24 punti'), ], widget=widgets.RadioSelect, label="In un gruppo di 4 partecipanti, il Partecipante 1 contribuisce 10 punti " "e gli altri partecipanti del suo gruppo contribuiscono rispettivamente 1, 1 e 2 punti (Totale contribuito = 14 Punti). " "Quanti punti guadagna il Partecipante 1?", ) # Strategy method: per ogni possibile contributo medio 0..10 strategy_0 = models.IntegerField(min=0) strategy_1 = models.IntegerField(min=0) strategy_2 = models.IntegerField(min=0) strategy_3 = models.IntegerField(min=0) strategy_4 = models.IntegerField(min=0) strategy_5 = models.IntegerField(min=0) strategy_6 = models.IntegerField(min=0) strategy_7 = models.IntegerField(min=0) strategy_8 = models.IntegerField(min=0) strategy_9 = models.IntegerField(min=0) strategy_10 = models.IntegerField(min=0) #beliefs belief_others = models.IntegerField( min=0, ) # --- decisione NON condizionata dentro Strategy --- contribution_strategy = models.IntegerField( min=0, ) # --- decisione NON condizionata one-shot PGG --- contribution_oneshot = models.IntegerField( min=0, ) # === Background === age = models.IntegerField(label="Quanti anni hai?", min=13, max=25) gender = models.StringField( label="Qual è il tuo sesso?", choices=[ ("Male", "Maschio"), ("Female", "Femmina"), ("Other", "Altro / Preferisco non rispondere"), ], widget=widgets.RadioSelect, ) school_type = models.StringField( label="Che tipo di scuola frequenti?", choices=[ ("Classic_Lyceum", "Liceo Classico"), ("Scientific_Lyceum", "Liceo Scientifico"), ("Artistic_Lyceum", "Liceo Artistico"), ("Linguistic_Lyceum", "Liceo Linguistico"), ("Music_Lyceum", "Liceo Musicale e Coreutico"), ("Humanities_Lyceum", "Liceo delle Scienze Umane"), ("Made_in_Italy_Lyceum", "Liceo del Made in Italy"), ], widget=widgets.RadioSelect, ) class_level = models.StringField( label="In quale classe sei attualmente?", choices=[ ("1", "Prima"), ("2", "Seconda"), ("3", "Terza"), ("4", "Quarta"), ("5", "Quinta"), ], widget = widgets.RadioSelect, ) father_education = models.StringField( label="Qual è il titolo di studio più alto raggiunto da tuo padre (o figura paterna principale)?", choices=[ ("Elementary_School", "Licenza elementare"), ("Middle_School", "Licenza media"), ("Three-year_Prof_Qualification", "Qualifica professionale triennale"), ("High_School", "Diploma di scuola secondaria superiore (liceo, istituto tecnico o istituto professionale)"), ("Postgraduate_Non-Degree", "Titolo di studio superiore al diploma, diverso dalla Laurea (ISEF, Accademia di Belle Arti, Conservatorio)"), ("University_Degree", "Laurea"), ("PhD", "Dottorato di Ricerca"), ("Dont_Know", "Non so"), ], widget=widgets.RadioSelect, ) mother_education = models.StringField( label="Qual è il titolo di studio più alto raggiunto da tua madre (o figura materna principale)?", choices=[ ("Elementary_School", "Licenza elementare"), ("Middle_School", "Licenza media"), ("Three-year_Prof_Qualification", "Qualifica professionale triennale"), ("High_School", "Diploma di scuola secondaria superiore (liceo, istituto tecnico o istituto professionale)"), ("Postgraduate_Non-Degree", "Titolo di studio superiore al diploma, diverso dalla Laurea (ISEF, Accademia di Belle Arti, Conservatorio)"), ("University_Degree", "Laurea"), ("PhD", "Dottorato di Ricerca"), ("Dont_Know", "Non so"), ], widget=widgets.RadioSelect, ) father_occupation = models.StringField( label="Che cosa fa attualmente tuo padre (o figura paterna principale)?", choices=[ ("Unemployed", "Disoccupato"), ("Houseman", "Si occupa della casa"), ("Executive_Officer", "Dirigente, docente universitario, funzionario, ufficiale militare"), ("Farm_Owner", "Imprenditore, proprietario agricolo"), ("Professional_Employee", "Professionista dipendente, sottufficiale militare, libero professionista (psicologo, ricercatore, medico, avvocato, commissario di polizia, ecc.)"), ("Self_Employed", "Lavoratore in proprio (commerciante, artigiano, coltivatore diretto, meccanico, sarto, ecc.)"), ("Employee", "Insegnante, impiegato, militare graduato"), ("Service_Worker", "Operaio, addetto ai servizi, socio di cooperativa (tecnico, infermiere, cameriere, commessa, ecc.)"), ("Retired", "Pensionato"), ("Dont_Know", "Non so"), ], widget=widgets.RadioSelect, ) mother_occupation = models.StringField( label="Che cosa fa attualmente tua madre (o figura materna principale)?", choices=[ ("Unemployed", "Disoccupata"), ("Houseman", "Si occupa della casa"), ("Executive_Officer", "Dirigente, docente universitario, funzionario, ufficiale militare"), ("Farm_Owner", "Imprenditore, proprietario agricolo"), ("Professional_Employee", "Professionista dipendente, sottufficiale militare, libero professionista (psicologo, ricercatore, medico, avvocato, commissario di polizia, ecc.)"), ("Self_Employed", "Lavoratore in proprio (commerciante, artigiano, coltivatore diretto, meccanico, sarto, ecc.)"), ("Employee", "Insegnante, impiegato, militare graduato"), ("Service_Worker", "Operaio, addetto ai servizi, socio di cooperativa (tecnico, infermiere, cameriere, commessa, ecc.)"), ("Retired", "Pensionata"), ("Dont_Know", "Non so"), ], widget=widgets.RadioSelect, ) siblings = models.StringField( label="Quanti fratelli/sorelle hai?", choices=[ ("0", "Nessuno/a"), ("1", "Uno/a"), ("2", "Due"), ("3+", "Tre o più"), ], widget=widgets.RadioSelect, ) # === Likert 1-5 Influence Questions === q_recommend = models.IntegerField( label="I miei genitori mi hanno consigliato un indirizzo scolastico superiore specifico.", choices=[ (1, "Fortemente in disaccordo"), (2, "In disaccordo"), (3, "Neutrale"), (4, "D'accordo"), (5, "Fortemente d'accordo"), ], widget=widgets.RadioSelect ) q_encourage = models.IntegerField( label="I miei genitori mi hanno incoraggiato/a a scegliere la scuola superiore che frequento ora.", choices=[ (1, "Fortemente in disaccordo"), (2, "In disaccordo"), (3, "Neutrale"), (4, "D'accordo"), (5, "Fortemente d'accordo"), ], widget=widgets.RadioSelect ) q_free_choice = models.IntegerField( label="Mi sono sentito libero/a di scegliere il mio indirizzo scolastico superiore senza pressioni da parte dei miei genitori.", choices=[ (1, "Fortemente in disaccordo"), (2, "In disaccordo"), (3, "Neutrale"), (4, "D'accordo"), (5, "Fortemente d'accordo"), ], widget=widgets.RadioSelect ) changed_school = models.StringField( label="Hai cambiato scuola durante il tuo percorso di scuola superiore?", choices=[ ("Yes", "Sì"), ("No", "No"), ], widget=widgets.RadioSelect ) previous_school_type = models.StringField( label="Da quale scuola provenivi?", choices=[ ("Classic_Lyceum", "Liceo Classico"), ("Scientific_Lyceum", "Liceo Scientifico"), ("Artistic_Lyceum", "Liceo Artistico"), ("Linguistic_Lyceum", "Liceo Linguistico"), ("Music_Lyceum", "Liceo Musicale e Coreutico"), ("Humanities_Lyceum", "Liceo delle Scienze Umane"), ("Made_in_Italy_Lyceum", "Liceo del Made in Italy"), ("Technical", "Istituto Tecnico"), ("Vocational", "Istituto Professionale"), ], blank=True, widget=widgets.RadioSelect, ) comp_soft_heart = models.IntegerField( label="È compassionevole, ha il cuore tenero.", choices = [ (1, "Fortemente in disaccordo"), (2, "In disaccordo"), (3, "Neutrale"), (4, "D'accordo"), (5, "Fortemente d'accordo"), ], widget = widgets.RadioSelect ) comp_helpful = models.IntegerField( label="È di aiuto e generosa con gli altri.", choices=[ (1, "Fortemente in disaccordo"), (2, "In disaccordo"), (3, "Neutrale"), (4, "D'accordo"), (5, "Fortemente d'accordo"), ], widget=widgets.RadioSelect ) comp_little_sympathy = models.IntegerField( label="Prova poca empatia per gli altri.", choices = [ (1, "Fortemente in disaccordo"), (2, "In disaccordo"), (3, "Neutrale"), (4, "D'accordo"), (5, "Fortemente d'accordo"), ], widget = widgets.RadioSelect ) comp_cold_uncaring = models.IntegerField( label="Può essere fredda e insensibile.", choices = [ (1, "Fortemente in disaccordo"), (2, "In disaccordo"), (3, "Neutrale"), (4, "D'accordo"), (5, "Fortemente d'accordo"), ], widget = widgets.RadioSelect ) # --- RESPECTFULNESS --- resp_respectful = models.IntegerField( label="È rispettosa, tratta gli altri con rispetto.", choices=[ (1, "Fortemente in disaccordo"), (2, "In disaccordo"), (3, "Neutrale"), (4, "D'accordo"), (5, "Fortemente d'accordo"), ], widget=widgets.RadioSelect ) resp_polite = models.IntegerField( label="È educata, gentile con gli altri.", choices = [ (1, "Fortemente in disaccordo"), (2, "In disaccordo"), (3, "Neutrale"), (4, "D'accordo"), (5, "Fortemente d'accordo"), ], widget = widgets.RadioSelect ) resp_starts_arguments = models.IntegerField( label="Provoca litigi con gli altri, attacca brighe.", choices=[ (1, "Fortemente in disaccordo"), (2, "In disaccordo"), (3, "Neutrale"), (4, "D'accordo"), (5, "Fortemente d'accordo"), ], widget=widgets.RadioSelect ) resp_rude = models.IntegerField( label="Qualche volta è scortese con gli altri.", choices = [ (1, "Fortemente in disaccordo"), (2, "In disaccordo"), (3, "Neutrale"), (4, "D'accordo"), (5, "Fortemente d'accordo"), ], widget = widgets.RadioSelect ) # --- TRUST --- trust_forgiving = models.IntegerField( label="È, di natura, incline a perdonare.", choices=[ (1, "Fortemente in disaccordo"), (2, "In disaccordo"), (3, "Neutrale"), (4, "D'accordo"), (5, "Fortemente d'accordo"), ], widget=widgets.RadioSelect ) trust_assumes_best = models.IntegerField( label="Pensa il meglio delle persone.", choices=[ (1, "Fortemente in disaccordo"), (2, "In disaccordo"), (3, "Neutrale"), (4, "D'accordo"), (5, "Fortemente d'accordo"), ], widget=widgets.RadioSelect ) trust_finds_fault = models.IntegerField( label="Tende a trovare difetti negli altri.", choices=[ (1, "Fortemente in disaccordo"), (2, "In disaccordo"), (3, "Neutrale"), (4, "D'accordo"), (5, "Fortemente d'accordo"), ], widget=widgets.RadioSelect ) trust_suspicious = models.IntegerField( label="È sospettosa delle intenzioni altrui.", choices=[ (1, "Fortemente in disaccordo"), (2, "In disaccordo"), (3, "Neutrale"), (4, "D'accordo"), (5, "Fortemente d'accordo"), ], widget=widgets.RadioSelect ) math_grade = models.IntegerField( label="Qual è stato, alla fine dello scorso anno scolastico, il tuo voto in Matematica?", choices=[ (4, "4 o meno di 4"), (5, "5"), (6, "6"), (7, "7"), (8, "8"), (9, "9"), (10, "10"), ], widget=widgets.RadioSelect, ) italian_grade = models.IntegerField( label="Qual è stato, alla fine dello scorso anno scolastico, il tuo voto in Italiano?", choices=[ (4, "4 o meno di 4"), (5, "5"), (6, "6"), (7, "7"), (8, "8"), (9, "9"), (10, "10"), ], widget=widgets.RadioSelect, ) future_education = models.StringField( label="Qual è il titolo di studio che hai intenzione di conseguire?", choices=[ ("None", "Nessuno (penso di studiare solo fino alla fine dell’obbligo di istruzione)"), ("Three-year_Vocational", "Qualifica professionale triennale (istituto professionale o centro di formazione)"), ("High_School", "Diploma di scuola secondaria superiore (liceo, istituto tecnico o istituto professionale)"), ("Postgraduate_Non-Degree", "Titolo di studio superiore al diploma, diverso dalla Laurea " "(es. qualifica professionale regionale di II livello, Accademia di Belle Arti, Conservatorio)"), ("Bachelor", "Laurea triennale"), ("Master_PhD", "Laurea magistrale (almeno due anni oltre la Laurea triennale) e/o dottorato di ricerca"), ("Dont_Know", "Non so"), ], widget=widgets.RadioSelect, ) field_future_education = models.StringField( label = "In quale gruppo disciplinare intendi conseguire il tuo titolo universitario?", choices=[ ("Law", "Giuridico"), ("Industrial/Info_Engineering", "Ingegneria industriale e dell'informazione"), ("Architecture/Civil_Engineering", "Architettura e ingegneria civile"), ("Scientific", "Scientifico"), ("Humanities", "Letterario-umanistico"), ("Arts_Design", "Arte e design"), ("Psychology", "Psicologico"), ("Medical_Pharmaceutical", "Medico-sanitario e farmaceutico"), ("ComputerScience_ICT", "Informatica e tecnologie ICT"), ("Economics", "Economico"), ("Political_Communication", "Politico-sociale e comunicazione"), ("Agriculture_Veterinary", "Agrario-forestale e veterinario"), ("Languages", "Linguistico"), ("Sport_Sciences", "Scienze motorie e sportive"), ("Education_Training", "Educazione e formazione"), ("Dont_know", "Non so"), ], widget=widgets.RadioSelect, blank=True, ) competition= models.IntegerField( label = "Quanto ti consideri competitivo? Scegli un valore nella scala sottostante, dove il valore 0 significa 'per niente competitivo' e il valore 10 significa 'molto competitivo'.", choices=[ (0, "0"), (1, "1"), (2, "2"), (3, "3"), (4, "4"), (5, "5"), (6, "6"), (7, "7"), (8, "8"), (9, "9"), (10, "10"), ], widget = widgets.RadioSelect, ) # ========================================================= # PAGES # ========================================================= # Pagina iniziale class Welcome(Page): def is_displayed(self): return self.round_number == 1 # Istruzioni - Priming class Instructions_Priming(Page): def is_displayed(self): return self.round_number == 1 # solo una volta, all'inizio def vars_for_template(self): treatment = self.participant.vars.get('treatment', 'CONTROL') return dict( treatment=treatment, is_stem=(treatment == 'STEM'), ) # Priming class Writing_Task(Page): form_model = 'player' form_fields = ['writing_answer'] def is_displayed(self): return self.round_number == 1 def vars_for_template(self): treatment = self.participant.vars.get('treatment', 'CONTROL') if treatment == 'STEM': prompt = ( "Descrivi dettagliatamente un argomento che hai studiato a scuola relativo alla matematica, alla fisica o alle scienze." ) else: prompt = "Descrivi dettagliatamente la trama di una serie TV o film che hai guardato." return dict(prompt=prompt, is_stem=(treatment == 'STEM')) def error_message(self, values): text = (values.get('writing_answer') or '').strip() if not text: return 'Per favore, scrivi qualcosa prima di continuare.' # Waiting Page class WaitingPage(WaitPage): wait_for_all_groups = True # Se vuoi usare il template custom: template_name = 'venturini_pg/WaitAll.html' def vars_for_template(self): treatment = self.participant.vars.get('treatment', 'CONTROL') return dict(is_stem=(treatment == 'STEM')) #Manipulation Check class Priming_Check(Page): form_model = 'player' def get_form_fields(self): return [ 'wc1_a','wc1_b', 'wc1_c', 'wc2_a','wc2_b','wc2_c', 'wc3_a','wc3_b', 'wc4_a','wc4_b', 'wc5_a','wc5_b', 'wc6_a','wc6_b', ] def is_displayed(self): return self.round_number == 1 def vars_for_template(self): treatment = self.participant.vars.get('treatment', 'CONTROL') return dict(is_stem=(treatment == 'STEM')) # Istruzioni PGG class Instructions_PGG(Page): def is_displayed(self): return self.round_number == 1 def vars_for_template(self): treatment = self.participant.vars.get('treatment', 'CONTROL') return dict(is_stem=(treatment == 'STEM')) #Attività PGG class PGG_Activity(Page): def is_displayed(self): return self.round_number == 1 def vars_for_template(self): treatment = self.participant.vars.get('treatment', 'CONTROL') return dict(is_stem=(treatment == 'STEM')) # Istruzioni PGG - Manipulation Check class Instructions_PGG_Check(Page): form_model = 'player' form_fields = ['mc_q1', 'mc_q2', 'mc_q3'] def is_displayed(self): return self.round_number == 1 def vars_for_template(self): treatment = self.participant.vars.get('treatment', 'CONTROL') # usa self.player se c'è, altrimenti self p = getattr(self, 'player', self) return dict( is_stem=(treatment == 'STEM'), mc_errors=getattr(p, '_mc_errors', {}) ) def error_message(self, values): if values.get('mc_q1') != 2: return "La risposta alla domanda 1 non è corretta. Per favore, correggila." if values.get('mc_q2') != 1: return "La risposta alla domanda 2 non è corretta. Per favore, correggila." if values.get('mc_q3') != 2: return "La risposta alla domanda 3 non è corretta. Per favore, correggila." # Istruzioni Strategy class Instructions_Strategy(Page): def is_displayed(self): return self.round_number == 1 def vars_for_template(self): treatment = self.participant.vars.get('treatment', 'CONTROL') return dict(is_stem=(treatment == 'STEM')) # Strategy Method class Strategy_PGG(Page): form_model = 'player' def get_form_fields(self): return ( [f"strategy_{i}" for i in range(C.ENDOWMENT + 1)] + ["contribution_strategy"] # <-- decisione NON condizionata ) def is_displayed(self): return self.round_number == 1 def vars_for_template(self): treatment = self.participant.vars.get('treatment', 'CONTROL') return dict(is_stem=(treatment == 'STEM'), endowment=C.ENDOWMENT, group_size=len(self.group.get_players()), strategy_fields=[f"strategy_{i}" for i in range(C.ENDOWMENT + 1)], ) def error_message(self, values): # controllo strategy method for k, v in values.items(): if k.startswith("strategy_"): if v is None: return "Per favore compila tutte le celle." if v < 0: return "Non puoi contribuire meno di 0 punti." if v > C.ENDOWMENT: return f"Non puoi contribuire più di {C.ENDOWMENT} punti." # controllo decisione NON condizionata c = values.get("contribution_strategy") if c is None: return "Per favore inserisci il tuo contributo al Fondo Comune." if c < 0: return "Non puoi contribuire meno di 0 punti." if c > C.ENDOWMENT: return f"Non puoi contribuire più di {C.ENDOWMENT} punti." return None # Belief of others' contribution class Belief_PGG(Page): form_model = 'player' form_fields = ['belief_others'] def is_displayed(self): return self.round_number == 1 def vars_for_template(self): treatment = self.participant.vars.get('treatment', 'CONTROL') return dict( endowment=C.ENDOWMENT, is_stem=(treatment == 'STEM'), group_size=len(self.group.get_players()), # <-- AGGIUNGI QUESTO ) def error_message(self, values): x = values.get('belief_others') if x is None: return "Per favore inserisci un numero." if x < 0: return "Non puoi contribuire meno di 0 punti." if x > C.ENDOWMENT: return f"Non puoi inserire più di {C.ENDOWMENT} punti." return None # Istruzioni Strategy class Instructions_PGG_Oneshot(Page): def is_displayed(self): return self.round_number == 1 def vars_for_template(self): treatment = self.participant.vars.get('treatment', 'CONTROL') return dict(is_stem=(treatment == 'STEM')) # Public Good Game class PGG(Page): form_model = "player" form_fields = ["contribution_oneshot"] def vars_for_template(self): treatment = self.participant.vars.get('treatment', 'CONTROL') return dict( endowment=C.ENDOWMENT, is_stem=(treatment == "STEM"), group_size=len(self.group.get_players()), ) def error_message(self, values): x = values.get("contribution_oneshot") if x is None: return "Per favore inserisci un valore." if x < 0: return "Non puoi contribuire meno di 0 punti." if x > C.ENDOWMENT: return f"Non puoi contribuire più di {C.ENDOWMENT} punti." return None # WaitPage calculating payoffs class Results(WaitPage): after_all_players_arrive = set_payoffs_wrapper template_name = 'venturini_pg/WaitResults.html' def vars_for_template(self): t = self.participant.vars.get('treatment', 'CONTROL') return dict(is_stem=(t == 'STEM')) #Istructions page for competition and survey class CompetitionSurvey_Instructions(Page): def is_displayed(self): return self.round_number == 1 def vars_for_template(self): treatment = self.participant.vars.get('treatment', 'CONTROL') return dict(is_stem=(treatment == 'STEM')) # Page with results class Results_Page(Page): def vars_for_template(self): t = self.participant.vars.get('treatment', 'CONTROL') p = getattr(self, 'player', self) return dict( is_stem=(t == 'STEM'), participant_id=self.participant.vars['participant_id'], payoff_mode=p.payoff_mode, payoff_rule=p.payoff_rule, contribution=p.used_own_contribution, total=round_half_up(p.used_total_contribution), share=round_half_up(p.used_share), belief_bonus=p.belief_bonus, payoff=p.payoff_final_rounded, ) #Survey class Survey(Page): form_model = "player" form_fields = [ "age", "gender", "school_type", "class_level", "father_education", "mother_education", "father_occupation", "mother_occupation", "siblings", "q_recommend", "q_encourage", "q_free_choice", "changed_school", "previous_school_type", "math_grade", "italian_grade", "future_education", "field_future_education", "comp_soft_heart", "comp_helpful", "comp_little_sympathy", "comp_cold_uncaring", "resp_respectful", "resp_polite", "resp_starts_arguments", "resp_rude", "trust_forgiving", "trust_assumes_best", "trust_finds_fault", "trust_suspicious", ] def vars_for_template(self): treatment = self.participant.vars.get('treatment', 'CONTROL') return dict(is_stem=(treatment == "STEM")) def error_message(self, values): if values.get("changed_school") == "Yes" and not values.get("previous_school_type"): return "Per favore indica da quale scuola provenivi." class Competition(Page): form_model = "player" form_fields = ["competition"] def vars_for_template(self): treatment = self.participant.vars.get('treatment', 'CONTROL') return dict(is_stem=(treatment == "STEM")) # ========================================================= # page_sequence # ========================================================= page_sequence = [ Welcome, Instructions_Priming, Writing_Task, WaitingPage, Priming_Check, WaitingPage, Instructions_PGG, PGG_Activity, Instructions_PGG_Check, WaitingPage, Instructions_Strategy, Strategy_PGG, WaitingPage, Instructions_PGG_Oneshot, PGG, WaitingPage, Belief_PGG, WaitingPage, CompetitionSurvey_Instructions, Competition, Survey, Results, Results_Page, ]