# ============================================================================= # pages.py # ============================================================================= import json import random import time from otree.api import Page from situations import ( get_situations_for_participant, DG_PREDICTION_IDS, LT_PREDICTION_IDS, DG_THRESHOLD_IDS, LT_THRESHOLD_IDS, STARTING_AMOUNT, SITUATION_NORMS, SITUATION_APP_MODES, ) from .instructions_text import ( GENERAL_INSTRUCTIONS_HTML, DICTATOR_INSTRUCTIONS_HTML, RISK_INSTRUCTIONS_HTML, APPROPRIATENESS_INSTRUCTIONS_HTML, PREFERENCE_INSTRUCTIONS_HTML, THRESHOLD_INSTRUCTIONS_HTML, ADDITIONAL_DECISIONS_INSTRUCTIONS_HTML, RULE_FOLLOWING_INSTRUCTIONS_HTML, ) def _now(): return time.time() #SUP items SUP_ITEMS = [ {'id': 'financial1', 'text': 'My financial situation feels very instable at the moment.'}, {'id': 'financial2', 'text': 'At the moment, I feel uncertain about my ability to cover all my expenses in the future.'}, {'id': 'financial3', 'text': 'I am worried I do not have enough funds to cover an emergency expense.'}, {'id': 'big_picture1', 'text': 'The situation in the Netherlands feels uncertain.'}, {'id': 'big_picture2', 'text': 'The global situation feels uncertain.'}, {'id': 'big_picture3', 'text': 'It is difficult to predict how the situation in the Netherlands will be in a few years\u2019 time.'}, {'id': 'worry1', 'text': 'Thinking about the future makes me anxious.'}, {'id': 'worry2', 'text': 'Thinking about the future worries me.'}, {'id': 'worry3', 'text': 'I experience tension when I am thinking about my future.'}, {'id': 'personal1', 'text': 'I really do not know what tomorrow holds.'}, {'id': 'personal2', 'text': 'It is difficult to predict how my health situation will be in a few years\u2019 time.'}, {'id': 'personal3', 'text': 'It is difficult to predict how my personal situation will be in a few years\u2019 time.'}, {'id': 'local1', 'text': 'The personal situation of the people around me feels uncertain at the moment.'}, {'id': 'local2', 'text': 'The financial situation of the people around me feels uncertain at the moment.'}, {'id': 'local3', 'text': 'The situation in the city I currently live in feels uncertain.'}, {'id': 'dynamic1', 'text': 'There are a lot of moving parts in my life at the moment.'}, {'id': 'dynamic2', 'text': 'I want my future to be clearer than it is right now.'}, {'id': 'dynamic3', 'text': 'There will be a lot of movement and making new plans in my future.'}, ] # ============================================================================= # Payment calculation helper # ============================================================================= SHOWUP_FEE_EUR = 6.00 MU_TO_EUR = 1.00 TREATMENTS = ['early_group_split_10', 'early_group_split_8', 'early_group_split_6'] def _assign_treatment(player): # Shuffle treatment order once per session using session id as seed, # so the modulo-to-treatment mapping differs across sessions. rng = random.Random(player.session.id) treatments_shuffled = TREATMENTS[:] rng.shuffle(treatments_shuffled) idx = (player.participant.id_in_session - 1) % 3 treatment = treatments_shuffled[idx] player.treatment = treatment player.participant.vars['treatment'] = treatment def _resolve_option_payoff(player, chosen_opt, task_type, sit_id, selected_task_label): """Given a full option dict, compute payoff and update player fields.""" if task_type == 'dg': own = chosen_opt['own_payoff'] transfer = chosen_opt['transfer'] mult = chosen_opt['multiplier'] other_mu = round(transfer * mult, 2) player.selected_task = selected_task_label player.other_payoff = round(other_mu * MU_TO_EUR, 2) player.total_payment_eur = round(SHOWUP_FEE_EUR + own * MU_TO_EUR, 2) player.participant.vars['total_payment_eur'] = player.total_payment_eur return { 'selected_task': selected_task_label, 'task_type': 'allocation', 'sit_id': sit_id, 'chosen_option': chosen_opt['option_id'], 'own_payoff_mu': own, 'transfer_mu': transfer, 'multiplier': mult, 'other_payoff_mu': other_mu, 'total_eur': player.total_payment_eur, } else: l_out = chosen_opt['left_outcome'] r_out = chosen_opt['right_outcome'] p_left = float(chosen_opt['probability_left']) outcome = l_out if random.random() < p_left else r_out player.selected_task = selected_task_label player.other_payoff = None player.risk_outcome_realised = outcome player.total_payment_eur = round(SHOWUP_FEE_EUR + outcome * MU_TO_EUR, 2) player.participant.vars['total_payment_eur'] = player.total_payment_eur return { 'selected_task': selected_task_label, 'task_type': 'lottery', 'sit_id': sit_id, 'chosen_option': chosen_opt['option_id'], 'left_outcome': l_out, 'right_outcome': r_out, 'prob_left': p_left, 'realised_outcome': outcome, 'total_eur': player.total_payment_eur, } def _calculate_payment(player): # Idempotent: return cached result if already calculated try: if player.selected_task: return json.loads(player.payoff_explanation) except Exception: pass # ── Load all decision blobs ─────────────────────────────────────────────── pref_dg = json.loads(player.dg_preference_ratings or '{}') pref_lt = json.loads(player.lt_preference_ratings or '{}') thr_dg = json.loads(player.dg_threshold_decisions or '{}') thr_lt = json.loads(player.lt_threshold_decisions or '{}') norm_dg = json.loads(player.dg_norm_decisions or '{}') norm_lt = json.loads(player.lt_norm_decisions or '{}') app_dg = json.loads(player.dg_app_ratings or '{}') app_lt = json.loads(player.lt_app_ratings or '{}') # ── Build weighted ticket pool ──────────────────────────────────────────── # Each situation in pref/threshold/norm = 1 ticket # Each situation in app = 2 tickets (one per option rated) # Rule following task = 1 ticket tickets = [] for sid, dec in pref_dg.items(): tickets.append(('pref', 'dg', sid, dec)) for sid, dec in pref_lt.items(): tickets.append(('pref', 'lt', sid, dec)) for sid, dec in thr_dg.items(): tickets.append(('threshold', 'dg', sid, dec)) for sid, dec in thr_lt.items(): tickets.append(('threshold', 'lt', sid, dec)) for sid, dec in norm_dg.items(): tickets.append(('norm', 'dg', sid, dec)) for sid, dec in norm_lt.items(): tickets.append(('norm', 'lt', sid, dec)) # Appropriateness: 2 tickets per situation (option A and option B) for sid, dec in app_dg.items(): tickets.append(('app', 'dg', sid, dec, 'a')) tickets.append(('app', 'dg', sid, dec, 'b')) for sid, dec in app_lt.items(): tickets.append(('app', 'lt', sid, dec, 'a')) tickets.append(('app', 'lt', sid, dec, 'b')) # Rule following: 1 ticket rf_div = {} try: rf_div = json.loads(player.rule_following_task_division or '{}') except Exception: pass if rf_div: tickets.append(('rule_following', None, None, rf_div)) if not tickets: return {'error': 'No decisions found'} ticket = random.choice(tickets) task = ticket[0] # ── Resolve payoff by task ──────────────────────────────────────────────── # ── Preference task ─────────────────────────────────────────────────────── if task == 'pref': _, task_type, sit_id, dec = ticket chosen_option_id = dec.get('chosen_option_id', '') # Look up full option data from participant.vars all_sits = ( player.participant.vars.get('pref_block1_situations', []) + player.participant.vars.get('pref_block2_situations', []) ) chosen_opt = None for sit in all_sits: if sit['id'] == sit_id: for side in ['left', 'right']: if sit[side]['option_id'] == chosen_option_id: chosen_opt = sit[side] break if chosen_opt: break if chosen_opt is None: return {'error': 'Preference option data not found', 'sit_id': sit_id} info = _resolve_option_payoff(player, chosen_opt, task_type, sit_id, 'pref') info['binary_choice'] = dec.get('binary_choice') # ── Threshold task ──────────────────────────────────────────────────────── elif task == 'threshold': _, task_type, sit_id, dec = ticket chosen_option_id = dec.get('choice_implied_in_treatment') if not chosen_option_id: return {'error': 'Threshold implied choice missing', 'sit_id': sit_id} all_sits = ( player.participant.vars.get('threshold_block1_situations', []) + player.participant.vars.get('threshold_block2_situations', []) ) chosen_opt = None for sit in all_sits: if sit['id'] == sit_id: for side in ['left', 'right']: if sit[side]['option_id'] == chosen_option_id: chosen_opt = sit[side] break if chosen_opt: break if chosen_opt is None: return {'error': 'Threshold option data not found', 'sit_id': sit_id} info = _resolve_option_payoff(player, chosen_opt, task_type, sit_id, 'threshold') # ── Norm task ───────────────────────────────────────────────────────────── elif task == 'norm': _, task_type, sit_id, dec = ticket chosen_option_id = dec.get('chosen_option_id') if not chosen_option_id: return {'error': 'Norm chosen option missing', 'sit_id': sit_id} all_sits = ( player.participant.vars.get('norm_block1_situations', []) + player.participant.vars.get('norm_block2_situations', []) ) chosen_opt = None for sit in all_sits: if sit['id'] == sit_id: for side in ['left', 'right']: if sit[side]['option_id'] == chosen_option_id: chosen_opt = sit[side] break if chosen_opt: break if chosen_opt is None: return {'error': 'Norm option data not found', 'sit_id': sit_id} info = _resolve_option_payoff(player, chosen_opt, task_type, sit_id, 'norm') # ── Appropriateness task ────────────────────────────────────────────────── elif task == 'app': _, task_type, sit_id, dec, option_slot = ticket # option_slot is 'a' or 'b' rating = dec.get(f'rating_option_{option_slot}') mode_str = dec.get(f'mode_{option_slot}_in_treatment') matched = dec.get(f'matched_mode_{option_slot}') bonus = 10.0 if matched else 0.0 player.selected_task = 'app' player.other_payoff = None player.total_payment_eur = round(SHOWUP_FEE_EUR + bonus * MU_TO_EUR, 2) player.participant.vars['total_payment_eur'] = player.total_payment_eur info = { 'selected_task': 'app', 'task_type': task_type, 'sit_id': sit_id, 'option_slot': option_slot, 'rating': rating, 'mode': mode_str, 'matched_mode': matched, 'bonus_mu': bonus, 'total_eur': player.total_payment_eur, } # ── Rule following task ─────────────────────────────────────────────────── elif task == 'rule_following': _, _, _, rf = ticket total_payoff = rf.get('total_payoff', 0) player.selected_task = 'rule_following' player.other_payoff = None player.total_payment_eur = round(SHOWUP_FEE_EUR + total_payoff * MU_TO_EUR, 2) player.participant.vars['total_payment_eur'] = player.total_payment_eur info = { 'selected_task': 'rule_following', 'yellow': rf.get('yellow'), 'blue': rf.get('blue'), 'yellow_payoff': rf.get('yellow_payoff'), 'blue_payoff': rf.get('blue_payoff'), 'total_payoff_mu': total_payoff, 'total_eur': player.total_payment_eur, } else: return {'error': f'Unknown task: {task}'} player.payoff_explanation = json.dumps(info) return info def _build_block(situation_ids): return get_situations_for_participant( situation_ids=situation_ids, randomise_sides=True, randomise_order=True, ) # ============================================================================= # Initialisation helpers # ============================================================================= def _init_pref(participant): """Initialise participant.vars for both preference blocks if not done yet.""" if 'pref_block1_situations' not in participant.vars: if random.random() < 0.5: block1_ids, block2_ids = (DG_PREDICTION_IDS + DG_THRESHOLD_IDS), (LT_PREDICTION_IDS + LT_THRESHOLD_IDS) participant.vars['pref_block1_task'] = 'dg' participant.vars['pref_block2_task'] = 'lt' else: block1_ids, block2_ids = (LT_PREDICTION_IDS + LT_THRESHOLD_IDS), (DG_PREDICTION_IDS + DG_THRESHOLD_IDS) participant.vars['pref_block1_task'] = 'lt' participant.vars['pref_block2_task'] = 'dg' participant.vars['pref_block1_situations'] = _build_block(block1_ids) participant.vars['pref_block2_situations'] = _build_block(block2_ids) participant.vars['pref_block1_index'] = 0 participant.vars['pref_block2_index'] = 0 participant.vars['pref_dg_ratings'] = {} participant.vars['pref_lt_ratings'] = {} participant.vars['pref_dg_lr'] = {} participant.vars['pref_lt_lr'] = {} def _vars_for_pref_block(participant, block): sits = participant.vars[f'pref_block{block}_situations'] task = participant.vars[f'pref_block{block}_task'] block_label = 'Allocation Task' if task == 'dg' else 'Lottery Task' return dict( situations = json.dumps(sits), task_type = sits[0]['left']['type'], dictator_endowment = STARTING_AMOUNT, block_label = block_label, block_total = len(sits), ) def _save_pref(participant, player, block): task = participant.vars[f'pref_block{block}_task'] sits = participant.vars[f'pref_block{block}_situations'] try: decisions = json.loads(player.pref_decisions_data) except Exception: decisions = {} ratings_out = {} lr_out = {} for sit in sits: sit_id = sit['id'] dec = decisions.get(sit_id, {}) raw = dec.get('raw_rating', 0) saved_rating = raw if sit['a_is_left'] else -raw ratings_out[sit_id] = { 'binary_choice': dec.get('binary_choice', ''), 'pref_rating_B_over_A': saved_rating, 'chosen_option_id': dec.get('chosen_option_id', ''), 'a_is_left': dec.get('a_is_left'), 'left_option_id': dec.get('left_option_id', ''), 'right_option_id': dec.get('right_option_id', ''), } lr_out[sit_id] = { 'left': sit['left']['option_id'], 'right': sit['right']['option_id'], } setattr(player, f'{task}_preference_ratings', json.dumps(ratings_out)) setattr(player, f'{task}_preference_lr', json.dumps(lr_out)) def _save_pref_rating(participant, player, block): sits = participant.vars[f'pref_block{block}_situations'] idx = participant.vars[f'pref_block{block}_index'] sit = sits[idx] task = participant.vars[f'pref_block{block}_task'] raw_rating = player.pref_raw_rating binary_choice = player.pref_binary_choice # 'left' | 'right' | 'indifferent' # Invert signed rating if sides were flipped so it always refers to option A vs B saved_rating = raw_rating if sit['a_is_left'] else -raw_rating sit_id = sit['id'] participant.vars[f'pref_{task}_ratings'][sit_id] = { 'binary_choice': binary_choice, 'rating': saved_rating, } participant.vars[f'pref_{task}_lr'][sit_id] = { 'left': sit['left']['option_id'], 'right': sit['right']['option_id'], } participant.vars[f'pref_block{block}_index'] += 1 if participant.vars[f'pref_block{block}_index'] >= len(sits): setattr(player, f'{task}_preference_ratings', json.dumps(participant.vars[f'pref_{task}_ratings'])) setattr(player, f'{task}_preference_lr', json.dumps(participant.vars[f'pref_{task}_lr'])) # ============================================================================= # Threshold task helpers # ============================================================================= def _init_threshold(participant): """Initialise participant.vars for both threshold blocks if not done yet. Reuses the same task order as the preference block so block 1 is always the same task type across all task types.""" if 'threshold_block1_situations' not in participant.vars: if 'pref_block1_task' in participant.vars: block1_task = participant.vars['pref_block1_task'] block2_task = participant.vars['pref_block2_task'] else: if random.random() < 0.5: block1_task, block2_task = 'dg', 'lt' else: block1_task, block2_task = 'lt', 'dg' block1_ids = DG_THRESHOLD_IDS if block1_task == 'dg' \ else LT_THRESHOLD_IDS block2_ids = LT_THRESHOLD_IDS if block1_task == 'dg' \ else DG_THRESHOLD_IDS participant.vars['threshold_block1_task'] = block1_task participant.vars['threshold_block2_task'] = block2_task participant.vars['threshold_block1_situations'] = _build_block(block1_ids) participant.vars['threshold_block2_situations'] = _build_block(block2_ids) def _vars_for_threshold_block(participant, block): sits = participant.vars[f'threshold_block{block}_situations'] task = participant.vars[f'threshold_block{block}_task'] block_label = 'Allocation Task' if task == 'dg' else 'Lottery Task' return dict( situations = json.dumps(sits), task_type = sits[0]['left']['type'], dictator_endowment = STARTING_AMOUNT, block_label = block_label, num_pairs = len(sits), ) def _save_threshold(participant, player, block): task = participant.vars[f'threshold_block{block}_task'] sits = participant.vars[f'threshold_block{block}_situations'] treatment = participant.vars.get('treatment', 'control') # Map treatment to the norm group whose actual behaviour we use treatment_norm_key = { 'early_group_split_10': 'norm_chosen_A_gr10', 'early_group_split_8': 'norm_chosen_A_gr8', 'early_group_split_6': 'norm_chosen_A_gr6', } norm_key = treatment_norm_key[treatment] try: dec_by_id = json.loads(player.threshold_decisions_data or '{}') except Exception: dec_by_id = {} decisions_out = {} lr_out = {} for sit in sits: sit_id = sit['id'] a_is_left = sit['a_is_left'] dec = dec_by_id.get(sit_id, {}) threshold_raw = dec.get('threshold_raw') threshold_canonical = dec.get('threshold_canonical') # Look up the actual norm value for this situation and treatment norms = SITUATION_NORMS.get(sit_id, {}) n_chose_a = norms.get(norm_key) # integer 0–10, how many chose option A # Derive the single implied choice given the actual norm choice_implied = None if threshold_raw is not None and n_chose_a is not None: thr = int(threshold_raw) n_left = n_chose_a if a_is_left else (10 - n_chose_a) if thr == 0: choice_implied = sit['left']['option_id'] elif thr == 11: choice_implied = sit['right']['option_id'] else: choice_implied = sit['left']['option_id'] if n_left >= thr else sit['right']['option_id'] decisions_out[sit_id] = { 'situation_id': sit_id, 'task_type': task, 'condition': 'threshold', 'left_option_id': dec.get('left_option_id', sit['left']['option_id']), 'right_option_id': dec.get('right_option_id', sit['right']['option_id']), 'a_is_left': a_is_left, 'threshold_raw': threshold_raw, 'threshold_canonical': threshold_canonical, 'treatment': treatment, 'norm_key_used': norm_key, 'n_chose_a_in_treatment': n_chose_a, 'choice_implied_in_treatment': choice_implied, 'decision_time_ms': dec.get('decision_time_ms'), } lr_out[sit_id] = { 'left': sit['left']['option_id'], 'right': sit['right']['option_id'], } setattr(player, f'{"dg" if task == "dg" else "lt"}_threshold_decisions', json.dumps(decisions_out)) setattr(player, f'{"dg" if task == "dg" else "lt"}_threshold_lr', json.dumps(lr_out)) def _init_norm(participant): """Initialise participant.vars for both norm blocks if not done yet.""" if 'norm_block1_situations' not in participant.vars: # Reuse the same block order as the preference task if already set, # otherwise randomise independently if 'pref_block1_task' in participant.vars: block1_task = participant.vars['pref_block1_task'] block2_task = participant.vars['pref_block2_task'] else: if random.random() < 0.5: block1_task, block2_task = 'dg', 'lt' else: block1_task, block2_task = 'lt', 'dg' block1_ids = DG_PREDICTION_IDS if block1_task == 'dg' else LT_PREDICTION_IDS block2_ids = LT_PREDICTION_IDS if block1_task == 'dg' else DG_PREDICTION_IDS participant.vars['norm_block1_task'] = block1_task participant.vars['norm_block2_task'] = block2_task participant.vars['norm_block1_situations'] = _build_block(block1_ids) participant.vars['norm_block2_situations'] = _build_block(block2_ids) participant.vars['norm_block1_index'] = 0 participant.vars['norm_block2_index'] = 0 participant.vars['norm_dg_decisions'] = {} participant.vars['norm_lt_decisions'] = {} participant.vars['norm_dg_lr'] = {} participant.vars['norm_lt_lr'] = {} def _vars_for_norm_block(participant, block): sits = participant.vars[f'norm_block{block}_situations'] task = participant.vars[f'norm_block{block}_task'] treatment = participant.vars.get('treatment', 'early_group_split_10') block_label = 'Allocation Task' if task == 'dg' else 'Lottery Task' treatment_norm_key = { 'early_group_split_10': 'norm_chosen_A_gr10', 'early_group_split_8': 'norm_chosen_A_gr8', 'early_group_split_6': 'norm_chosen_A_gr6', } norm_key = treatment_norm_key[treatment] # Embed n_chose_left into each situation dict for the frontend sits_with_norm = [] for sit in sits: n_chose_a = SITUATION_NORMS[sit['id']][norm_key] n_chose_left = n_chose_a if sit['a_is_left'] else (10 - n_chose_a) sits_with_norm.append({**sit, 'n_chose_left': n_chose_left, 'n_chose_a': n_chose_a}) return dict( situations = json.dumps(sits_with_norm), task_type = sits[0]['left']['type'], dictator_endowment = STARTING_AMOUNT, block_label = block_label, block_total = len(sits), ) def _save_norm_decision(participant, player, block): task = participant.vars[f'norm_block{block}_task'] sits = participant.vars[f'norm_block{block}_situations'] treatment = participant.vars.get('treatment', 'early_group_split_10') treatment_norm_key = { 'early_group_split_10': 'norm_chosen_A_gr10', 'early_group_split_8': 'norm_chosen_A_gr8', 'early_group_split_6': 'norm_chosen_A_gr6', } norm_key = treatment_norm_key[treatment] try: decisions = json.loads(player.norm_decisions_data) except Exception: decisions = {} decisions_out = {} lr_out = {} for sit in sits: sit_id = sit['id'] a_is_left = sit['a_is_left'] n_chose_a = SITUATION_NORMS[sit_id][norm_key] n_chose_left = n_chose_a if a_is_left else (10 - n_chose_a) dec = decisions.get(sit_id, {}) decisions_out[sit_id] = { 'left_option_id': sit['left']['option_id'], 'right_option_id': sit['right']['option_id'], 'a_is_left': a_is_left, 'chosen_side': dec.get('chosen_side', ''), 'chosen_option_id': dec.get('chosen_option_id', ''), 'treatment': treatment, 'norm_key': norm_key, 'n_chose_a': n_chose_a, 'n_chose_left': n_chose_left, 'decision_time_ms': dec.get('decision_time_ms'), } lr_out[sit_id] = { 'left': sit['left']['option_id'], 'right': sit['right']['option_id'], } setattr(player, 'dg_norm_decisions' if task == 'dg' else 'lt_norm_decisions', json.dumps(decisions_out)) setattr(player, 'dg_norm_lr' if task == 'dg' else 'lt_norm_lr', json.dumps(lr_out)) #Normative perception helpers def _init_app(participant): if 'app_block1_situations' not in participant.vars: if 'pref_block1_task' in participant.vars: block1_task = participant.vars['pref_block1_task'] block2_task = participant.vars['pref_block2_task'] else: if random.random() < 0.5: block1_task, block2_task = 'dg', 'lt' else: block1_task, block2_task = 'lt', 'dg' block1_ids = DG_PREDICTION_IDS if block1_task == 'dg' else LT_PREDICTION_IDS block2_ids = LT_PREDICTION_IDS if block1_task == 'dg' else DG_PREDICTION_IDS participant.vars['app_block1_task'] = block1_task participant.vars['app_block2_task'] = block2_task participant.vars['app_block1_situations'] = _build_block(block1_ids) participant.vars['app_block2_situations'] = _build_block(block2_ids) participant.vars['app_block1_index'] = 0 participant.vars['app_block2_index'] = 0 participant.vars['app_dg_ratings'] = {} participant.vars['app_lt_ratings'] = {} participant.vars['app_dg_lr'] = {} participant.vars['app_lt_lr'] = {} def _vars_for_app_block(participant, block): sits = participant.vars[f'app_block{block}_situations'] task = participant.vars[f'app_block{block}_task'] block_label = 'Allocation Task' if task == 'dg' else 'Lottery Task' return dict( situations = json.dumps(sits), task_type = sits[0]['left']['type'], dictator_endowment = STARTING_AMOUNT, block_label = block_label, block_total = len(sits), ) def _save_app(participant, player, block): task = participant.vars[f'app_block{block}_task'] sits = participant.vars[f'app_block{block}_situations'] treatment = participant.vars.get('treatment', 'early_group_split_10') treatment_suffix = { 'early_group_split_10': 'gr10', 'early_group_split_8': 'gr8', 'early_group_split_6': 'gr6', } suffix = treatment_suffix[treatment] try: decisions = json.loads(player.app_decisions_data) except Exception: decisions = {} def matches_mode(rating, mode_str): if mode_str is None or rating is None: return None return rating in [int(v) for v in str(mode_str).split('|')] ratings_out = {} lr_out = {} for sit in sits: sit_id = sit['id'] # e.g. "s_DG16" left_opt_id = sit['left']['option_id'] # from server-side sit, ground truth right_opt_id = sit['right']['option_id'] a_is_left = sit['a_is_left'] dec = decisions.get(sit_id, {}) left_r = dec.get('left_rating', None) # rating as submitted for left box right_r = dec.get('right_rating', None) # rating as submitted for right box # Recode to option-A / option-B orientation using server-side a_is_left rating_a = left_r if a_is_left else right_r rating_b = right_r if a_is_left else left_r modes = SITUATION_APP_MODES.get(sit_id, {}) mode_a_str = modes.get(f'modal_app_a_{suffix}') mode_b_str = modes.get(f'modal_app_b_{suffix}') ratings_out[sit_id] = { 'left_option_id': left_opt_id, 'right_option_id': right_opt_id, 'a_is_left': a_is_left, 'rating_left': left_r, 'rating_right': right_r, 'rating_option_a': rating_a, 'rating_option_b': rating_b, 'treatment': treatment, 'mode_a_in_treatment': mode_a_str, 'mode_b_in_treatment': mode_b_str, 'matched_mode_a': matches_mode(rating_a, mode_a_str), 'matched_mode_b': matches_mode(rating_b, mode_b_str), 'decision_time_ms': dec.get('decision_time_ms'), } lr_out[sit_id] = { 'left': left_opt_id, 'right': right_opt_id, } setattr(player, 'dg_app_ratings' if task == 'dg' else 'lt_app_ratings', json.dumps(ratings_out)) setattr(player, 'dg_app_lr' if task == 'dg' else 'lt_app_lr', json.dumps(lr_out)) # --------------------------------------------------------------------------- # Consent # --------------------------------------------------------------------------- class Consent(Page): form_model = 'player' form_fields = ['consent'] def is_displayed(player): return True def vars_for_template(player): return {} def before_next_page(player, timeout_happened=False): player.record_timestamp('consent', end=_now()) if player.consent == 'consent': _assign_treatment(player) class NoConsent(Page): def is_displayed(player): return player.consent == 'not_consent' # --------------------------------------------------------------------------- # General Instructions # --------------------------------------------------------------------------- class GeneralInstructions(Page): def is_displayed(player): return player.consent == 'consent' def vars_for_template(player): player.record_timestamp('general_instructions', start=_now()) return {'general_instructions_html': GENERAL_INSTRUCTIONS_HTML} def before_next_page(player, timeout_happened=False): player.record_timestamp('general_instructions', end=_now()) class GeneralComprehension(Page): form_model = 'player' form_fields = ['gen_q1'] CORRECT = { 'gen_q1': 'c', } def is_displayed(player): return player.consent == 'consent' def error_message(player, values): wrong_fields = [] for field, correct in GeneralComprehension.CORRECT.items(): if values.get(field) != correct: counter_field = field + '_errors' setattr(player, counter_field, getattr(player, counter_field, 0) + 1) wrong_fields.append(field) player.participant.vars['gen_submitted'] = {f: values.get(f, '') for f in GeneralComprehension.CORRECT} player.participant.vars['gen_wrong_fields'] = wrong_fields if wrong_fields: return 'Not all questions are correct. Please have another careful look at the questions marked below.' return None def vars_for_template(player): player.record_timestamp('general_comprehension', start=_now()) wrong = player.participant.vars.get('gen_wrong_fields', []) subm = player.participant.vars.get('gen_submitted', {}) return { 'general_instructions_html': GENERAL_INSTRUCTIONS_HTML, 'gen_q1_errors': player.gen_q1_errors, 'wrong_json': json.dumps({f: (f in wrong) for f in ['gen_q1']}), 'prev_json': json.dumps({f: subm.get(f, '') for f in ['gen_q1']}), } def before_next_page(player, timeout_happened=False): player.record_timestamp('general_comprehension', end=_now()) player.participant.vars['gen_wrong_fields'] = [] player.participant.vars['gen_submitted'] = {} # --------------------------------------------------------------------------- # Allocation Instructions & Comprehension # --------------------------------------------------------------------------- class AllocationInstructions(Page): def is_displayed(player): return player.consent == 'consent' def vars_for_template(player): player.record_timestamp('dictator_instructions', start=_now()) return { 'dictator_instructions_html': DICTATOR_INSTRUCTIONS_HTML, 'dictator_endowment': STARTING_AMOUNT, } def before_next_page(player, timeout_happened=False): player.record_timestamp('dictator_instructions', end=_now()) class AllocationComprehension(Page): form_model = 'player' form_fields = ['dict_q1', 'dict_q2', 'dict_q3'] CORRECT = {'dict_q1': 'b', 'dict_q2': 'a', 'dict_q3': 'c'} def is_displayed(player): return player.consent == 'consent' def error_message(player, values): wrong_fields = [] for field, correct in AllocationComprehension.CORRECT.items(): if values.get(field) != correct: counter_field = field + '_errors' setattr(player, counter_field, getattr(player, counter_field, 0) + 1) wrong_fields.append(field) player.participant.vars['dict_submitted'] = {f: values.get(f, '') for f in AllocationComprehension.CORRECT} player.participant.vars['dict_wrong_fields'] = wrong_fields if wrong_fields: return 'Not all questions are correct. Please have another careful look at the questions marked below.' return None def vars_for_template(player): player.record_timestamp('dictator_comprehension', start=_now()) wrong = player.participant.vars.get('dict_wrong_fields', []) subm = player.participant.vars.get('dict_submitted', {}) return { 'dictator_instructions_html': DICTATOR_INSTRUCTIONS_HTML, 'wrong_json': json.dumps({f: (f in wrong) for f in ['dict_q1','dict_q2','dict_q3']}), 'prev_json': json.dumps({f: subm.get(f, '') for f in ['dict_q1','dict_q2','dict_q3']}), } def before_next_page(player, timeout_happened=False): player.record_timestamp('dictator_comprehension', end=_now()) player.participant.vars['dict_wrong_fields'] = [] player.participant.vars['dict_submitted'] = {} # --------------------------------------------------------------------------- # Lottery Instructions & Comprehension # --------------------------------------------------------------------------- class LotteryInstructions(Page): def is_displayed(player): return player.consent == 'consent' def vars_for_template(player): player.record_timestamp('risk_instructions', start=_now()) return {'risk_instructions_html': RISK_INSTRUCTIONS_HTML} def before_next_page(player, timeout_happened=False): player.record_timestamp('risk_instructions', end=_now()) class LotteryComprehension(Page): form_model = 'player' form_fields = ['risk_q1', 'risk_q2'] CORRECT = {'risk_q1': 'c', 'risk_q2': 'c'} def is_displayed(player): return player.consent == 'consent' def error_message(player, values): wrong_fields = [] for field, correct in LotteryComprehension.CORRECT.items(): if values.get(field) != correct: counter_field = field + '_errors' setattr(player, counter_field, getattr(player, counter_field, 0) + 1) wrong_fields.append(field) player.participant.vars['risk_submitted'] = {f: values.get(f, '') for f in LotteryComprehension.CORRECT} player.participant.vars['risk_wrong_fields'] = wrong_fields if wrong_fields: return 'Not all questions are correct. Please have another careful look at the questions marked below.' return None def vars_for_template(player): player.record_timestamp('risk_comprehension', start=_now()) wrong = player.participant.vars.get('risk_wrong_fields', []) subm = player.participant.vars.get('risk_submitted', {}) return { 'risk_instructions_html': RISK_INSTRUCTIONS_HTML, 'wrong_json': json.dumps({f: (f in wrong) for f in ['risk_q1', 'risk_q2']}), 'prev_json': json.dumps({f: subm.get(f, '') for f in ['risk_q1', 'risk_q2']}), } def before_next_page(player, timeout_happened=False): player.record_timestamp('risk_comprehension', end=_now()) player.participant.vars['risk_wrong_fields'] = [] player.participant.vars['risk_submitted'] = {} # --------------------------------------------------------------------------- # Preference Instructions & Comprehension # --------------------------------------------------------------------------- class PreferenceInstructions(Page): def is_displayed(player): return player.consent == 'consent' def vars_for_template(player): player.record_timestamp('preference_instructions', start=_now()) return {'preference_instructions_html': PREFERENCE_INSTRUCTIONS_HTML} def before_next_page(player, timeout_happened=False): player.record_timestamp('preference_instructions', end=_now()) class PreferenceComprehension(Page): form_model = 'player' form_fields = ['pref_q1'] CORRECT = {'pref_q1': 'c'} def is_displayed(player): return player.consent == 'consent' def error_message(player, values): wrong_fields = [] for field, correct in PreferenceComprehension.CORRECT.items(): if values.get(field) != correct: counter_field = field + '_errors' setattr(player, counter_field, getattr(player, counter_field, 0) + 1) wrong_fields.append(field) player.participant.vars['pref_submitted'] = {f: values.get(f, '') for f in PreferenceComprehension.CORRECT} player.participant.vars['pref_wrong_fields'] = wrong_fields if wrong_fields: return 'Not all questions are correct. Please have another careful look at the questions marked below.' return None def vars_for_template(player): player.record_timestamp('preference_comprehension', start=_now()) wrong = player.participant.vars.get('pref_wrong_fields', []) subm = player.participant.vars.get('pref_submitted', {}) return { 'preference_instructions_html': PREFERENCE_INSTRUCTIONS_HTML, 'pref_q1_errors': player.pref_q1_errors, 'wrong_json': json.dumps({f: (f in wrong) for f in ['pref_q1']}), 'prev_json': json.dumps({f: subm.get(f, '') for f in ['pref_q1']}), } def before_next_page(player, timeout_happened=False): player.record_timestamp('preference_comprehension', end=_now()) player.participant.vars['pref_wrong_fields'] = [] player.participant.vars['pref_submitted'] = {} # ============================================================================= # Threshold task pages # ============================================================================= class ThresholdInstructions(Page): def is_displayed(player): return player.consent == 'consent' def vars_for_template(player): player.record_timestamp('threshold_instructions', start=_now()) return {'threshold_instructions_html': THRESHOLD_INSTRUCTIONS_HTML} def before_next_page(player, timeout_happened=False): player.record_timestamp('threshold_instructions', end=_now()) class ThresholdComprehension(Page): form_model = 'player' form_fields = ['threshold_q1', 'threshold_q2', 'threshold_q3', 'threshold_q4'] CORRECT = {'threshold_q1': 'c', 'threshold_q2': 'c', 'threshold_q3': 'c', 'threshold_q4': 'c'} def is_displayed(player): return player.consent == 'consent' def error_message(player, values): wrong_fields = [] for field, correct in ThresholdComprehension.CORRECT.items(): if values.get(field) != correct: counter_field = field + '_errors' setattr(player, counter_field, getattr(player, counter_field, 0) + 1) wrong_fields.append(field) player.participant.vars['threshold_submitted'] = {f: values.get(f, '') for f in ThresholdComprehension.CORRECT} player.participant.vars['threshold_wrong_fields'] = wrong_fields if wrong_fields: return 'Not all questions are correct. Please have another careful look at the questions marked below.' return None def vars_for_template(player): player.record_timestamp('threshold_comprehension', start=_now()) wrong = player.participant.vars.get('threshold_wrong_fields', []) subm = player.participant.vars.get('threshold_submitted', {}) return { 'threshold_instructions_html': THRESHOLD_INSTRUCTIONS_HTML, 'wrong_json': json.dumps({f: (f in wrong) for f in ['threshold_q1','threshold_q2','threshold_q3','threshold_q4']}), 'prev_json': json.dumps({f: subm.get(f, '') for f in ['threshold_q1','threshold_q2','threshold_q3','threshold_q4']}), } def before_next_page(player, timeout_happened=False): player.record_timestamp('threshold_comprehension', end=_now()) player.participant.vars['threshold_wrong_fields'] = [] player.participant.vars['threshold_submitted'] = {} class ThresholdPage1(Page): form_model = 'player' form_fields = ['threshold_decisions_data', 'situation_order_data'] template_name = 'main_conditional/ThresholdBlock.html' def is_displayed(player): _init_threshold(player.participant) return player.consent == 'consent' def vars_for_template(player): player.record_timestamp('threshold_block1', start=_now()) return _vars_for_threshold_block(player.participant, block=1) def before_next_page(player, timeout_happened=False): _save_threshold(player.participant, player, block=1) player.record_timestamp('threshold_block1', end=_now()) class ThresholdPage2(Page): form_model = 'player' form_fields = ['threshold_decisions_data', 'situation_order_data'] template_name = 'main_conditional/ThresholdBlock.html' def is_displayed(player): return player.consent == 'consent' def vars_for_template(player): player.record_timestamp('threshold_block2', start=_now()) return _vars_for_threshold_block(player.participant, block=2) def before_next_page(player, timeout_happened=False): _save_threshold(player.participant, player, block=2) player.record_timestamp('threshold_block2', end=_now()) # --------------------------------------------------------------------------- # Appropriateness Instructions & Comprehension # --------------------------------------------------------------------------- class AppropriatenessInstructions(Page): def is_displayed(player): return player.consent == 'consent' def vars_for_template(player): player.record_timestamp('appropriateness_instructions', start=_now()) return { 'appropriateness_instructions_html': APPROPRIATENESS_INSTRUCTIONS_HTML, } def before_next_page(player, timeout_happened=False): player.record_timestamp('appropriateness_instructions', end=_now()) class AppropriatenessComprehension(Page): form_model = 'player' form_fields = ['appr_q1', 'appr_q2'] CORRECT = {'appr_q1': 'c', 'appr_q2': 'c'} def is_displayed(player): return player.consent == 'consent' def error_message(player, values): wrong_fields = [] for field, correct in AppropriatenessComprehension.CORRECT.items(): if values.get(field) != correct: counter_field = field + '_errors' setattr(player, counter_field, getattr(player, counter_field, 0) + 1) wrong_fields.append(field) player.participant.vars['appr_submitted'] = {f: values.get(f, '') for f in AppropriatenessComprehension.CORRECT} player.participant.vars['appr_wrong_fields'] = wrong_fields if wrong_fields: return 'Not all questions are correct. Please have another careful look at the questions marked below.' return None def vars_for_template(player): player.record_timestamp('appropriateness_comprehension', start=_now()) wrong = player.participant.vars.get('appr_wrong_fields', []) subm = player.participant.vars.get('appr_submitted', {}) return { 'appropriateness_instructions_html': APPROPRIATENESS_INSTRUCTIONS_HTML, 'appr_q1_errors': player.appr_q1_errors, 'wrong_json': json.dumps({f: (f in wrong) for f in ['appr_q1','appr_q2']}), 'prev_json': json.dumps({f: subm.get(f, '') for f in ['appr_q1','appr_q2']}), } def before_next_page(player, timeout_happened=False): player.record_timestamp('appropriatenss_comprehension', end=_now()) player.participant.vars['appr_wrong_fields'] = [] player.participant.vars['appr_submitted'] = {} # ============================================================================= # Preference pages # ============================================================================= class PreferencePage1(Page): form_model = 'player' form_fields = ['pref_decisions_data'] template_name = 'main_conditional/PreferencePage.html' def is_displayed(player): _init_pref(player.participant) return True def vars_for_template(player): return _vars_for_pref_block(player.participant, block=1) def before_next_page(player, timeout_happened=False): _save_pref(player.participant, player, block=1) class PreferencePage2(Page): form_model = 'player' form_fields = ['pref_decisions_data'] template_name = 'main_conditional/PreferencePage.html' def is_displayed(player): _init_pref(player.participant) return True def vars_for_template(player): return _vars_for_pref_block(player.participant, block=2) def before_next_page(player, timeout_happened=False): _save_pref(player.participant, player, block=2) # ============================================================================= # Appropriateness task pages (Kruka Weber task) # ============================================================================= class AppropriatenessPage1(Page): form_model = 'player' form_fields = ['app_decisions_data'] template_name = 'main_conditional/AppropriatenessPage.html' def is_displayed(player): _init_app(player.participant) return True def vars_for_template(player): return _vars_for_app_block(player.participant, block=1) def before_next_page(player, timeout_happened=False): _save_app(player.participant, player, block=1) class AppropriatenessPage2(Page): form_model = 'player' form_fields = ['app_decisions_data'] template_name = 'main_conditional/AppropriatenessPage.html' def is_displayed(player): _init_app(player.participant) return True def vars_for_template(player): return _vars_for_app_block(player.participant, block=2) def before_next_page(player, timeout_happened=False): _save_app(player.participant, player, block=2) # ============================================================================= # Additional Decisions pages (norm task) # ============================================================================= class AdditionalDecisionsInstructions(Page): def is_displayed(player): return player.consent == 'consent' def vars_for_template(player): player.record_timestamp('additional_decisions_instructions', start=_now()) return {'additional_decisions_instructions_html': ADDITIONAL_DECISIONS_INSTRUCTIONS_HTML} def before_next_page(player, timeout_happened=False): player.record_timestamp('additional_decisions_instructions', end=_now()) class AdditionalDecisionsComprehension(Page): form_model = 'player' form_fields = ['additional_decisions_q1', 'additional_decisions_q2'] CORRECT = {'additional_decisions_q1': 'c', 'additional_decisions_q2': 'c'} def is_displayed(player): return player.consent == 'consent' def error_message(player, values): wrong_fields = [] for field, correct in AdditionalDecisionsComprehension.CORRECT.items(): if values.get(field) != correct: counter_field = field + '_errors' setattr(player, counter_field, getattr(player, counter_field, 0) + 1) wrong_fields.append(field) player.participant.vars['additional_decisions_submitted'] = {f: values.get(f, '') for f in AdditionalDecisionsComprehension.CORRECT} player.participant.vars['additional_decisions_wrong_fields'] = wrong_fields if wrong_fields: return 'Not all questions are correct. Please have another careful look at the questions marked below.' return None def vars_for_template(player): player.record_timestamp('additional_decisions_comprehension', start=_now()) wrong = player.participant.vars.get('additional_decisions_wrong_fields', []) subm = player.participant.vars.get('additional_decisions_submitted', {}) return { 'additional_decisions_instructions_html': ADDITIONAL_DECISIONS_INSTRUCTIONS_HTML, 'wrong_json': json.dumps({f: (f in wrong) for f in ['additional_decisions_q1','additional_decisions_q2']}), 'prev_json': json.dumps({f: subm.get(f, '') for f in ['additional_decisions_q1','additional_decisions_q2']}), } def before_next_page(player, timeout_happened=False): player.record_timestamp('additional_decisions_comprehension', end=_now()) player.participant.vars['additional_decisions_wrong_fields'] = [] player.participant.vars['additional_decisions_submitted'] = {} class AdditionalDecisionsPage1(Page): form_model = 'player' form_fields = ['norm_decisions_data'] template_name = 'main_conditional/NormPage.html' def is_displayed(player): _init_norm(player.participant) return player.consent == 'consent' def vars_for_template(player): return _vars_for_norm_block(player.participant, block=1) def before_next_page(player, timeout_happened=False): _save_norm_decision(player.participant, player, block=1) class AdditionalDecisionsPage2(Page): form_model = 'player' form_fields = ['norm_decisions_data'] template_name = 'main_conditional/NormPage.html' def is_displayed(player): return player.consent == 'consent' def vars_for_template(player): return _vars_for_norm_block(player.participant, block=2) def before_next_page(player, timeout_happened=False): _save_norm_decision(player.participant, player, block=2) # ============================================================================= # Rule following task # ============================================================================= class UrnsTaskInstructions(Page): def is_displayed(player): return player.consent == 'consent' def vars_for_template(player): player.record_timestamp('rule_following_instructions', start=_now()) return {'rule_following_instructions_html': RULE_FOLLOWING_INSTRUCTIONS_HTML} def before_next_page(player, timeout_happened=False): player.record_timestamp('rule_following_instructions', end=_now()) class UrnsTaskComprehension(Page): form_model = 'player' form_fields = ['rule_following_q1', 'rule_following_q2'] CORRECT = {'rule_following_q1': 'c', 'rule_following_q2': 'c'} def is_displayed(player): return player.consent == 'consent' def error_message(player, values): wrong_fields = [] for field, correct in UrnsTaskComprehension.CORRECT.items(): if values.get(field) != correct: counter_field = field + '_errors' setattr(player, counter_field, getattr(player, counter_field, 0) + 1) wrong_fields.append(field) player.participant.vars['rule_following_submitted'] = {f: values.get(f, '') for f in UrnsTaskComprehension.CORRECT} player.participant.vars['rule_following_wrong_fields'] = wrong_fields if wrong_fields: return 'Not all questions are correct. Please have another careful look at the questions marked below.' return None def vars_for_template(player): player.record_timestamp('rule_following_comprehension', start=_now()) wrong = player.participant.vars.get('rule_following_wrong_fields', []) subm = player.participant.vars.get('rule_following_submitted', {}) return { 'rule_following_instructions_html': RULE_FOLLOWING_INSTRUCTIONS_HTML, 'wrong_json': json.dumps({f: (f in wrong) for f in ['rule_following_q1','rule_following_q2']}), 'prev_json': json.dumps({f: subm.get(f, '') for f in ['rule_following_q1','rule_following_q2']}), } def before_next_page(player, timeout_happened=False): player.record_timestamp('rule_following_comprehension', end=_now()) player.participant.vars['rule_following_wrong_fields'] = [] player.participant.vars['rule_following_submitted'] = {} class UrnsTaskPage(Page): form_model = 'player' form_fields = ['rf_yellow', 'rf_blue', 'rule_following_task_division'] template_name = 'main_conditional/RuleFollowingPage.html' # --------------------------------------------------------------------------- # IOS Scale # --------------------------------------------------------------------------- class IOSScale(Page): form_model = "player" form_fields = ["ios_scale"] def is_displayed(player): return player.consent == "consent" # ============================================================================= # Ex Post Questionnaire # ============================================================================= #SUP class ExPostQuestionnaire3(Page): form_model = 'player' form_fields = ['SUP_data'] template_name = 'main_conditional/SUPPage.html' def is_displayed(player): return player.consent == 'consent' def vars_for_template(player): return dict(SUP_items=json.dumps(SUP_ITEMS)) #norm following scale _SNES_ITEMS = [ ("snes_q1", "I go out of my way to follow social norms."), ("snes_q2", "We should not always have to follow a set of social rules."), ("snes_q3", "People should always be able to behave as they wish rather than trying to fit the norm."), ("snes_q4", "There is a correct way to behave in every situation."), ("snes_q5", "If more people followed society's rules, the world would be a better place."), ("snes_q6", "People need to follow life's unwritten rules every bit as strictly as they follow the written rules."), ("snes_q7", "There are lots of vital customs that people should follow as members of society."), ("snes_q8", "The standards that society expects us to meet are far too restrictive."), ("snes_q9", "People who do what society expects of them lead happier lives."), ("snes_q10", "Our society is built on unwritten rules that members need to follow."), ("snes_q11", "I am at ease only when everyone around me is adhering to society's norms."), ("snes_q12", "We would be happier if we did not try to follow society's norms."), ("snes_q13", "My idea of a perfect world would be one with few social expectations."), ("snes_q14", "I always do my best to follow society's rules."), ] def _seeded_shuffle(lst, seed): """Return a shuffled copy of lst using a deterministic seed.""" import random as _rng r = _rng.Random(seed) out = list(lst) r.shuffle(out) return out class ExPostQuestionnaire2(Page): form_model = "player" form_fields = [ "snes_q1","snes_q2","snes_q3","snes_q4","snes_q5","snes_q6","snes_q7", "snes_q8","snes_q9","snes_q10","snes_q11","snes_q12","snes_q13","snes_q14", ] def is_displayed(player): return player.consent == "consent" def vars_for_template(player): player.record_timestamp("questionnaire2", start=_now()) code = player.participant.code seed_snes = sum(ord(c) for c in code) * 7 snes_shuffled = _seeded_shuffle(_SNES_ITEMS, seed_snes) import json as _json return {"snes_order_json": _json.dumps([f for f, _ in snes_shuffled])} class ExPostQuestionnaire4(Page): form_model = 'player' form_fields = ['q_understood_general', 'q_understood_lottery', 'q_understood_allocation', 'q_understood_preference_task', 'q_understood_threshold_task', 'q_understood_norm_task', 'q_understood_appropriateness_task','q_understood_urns_task', 'q_effort'] def is_displayed(player): return player.consent == 'consent' def error_message(player, values): missing = [f for f in ExPostQuestionnaire4.form_fields if values.get(f) is None] if missing: return 'Please respond to all questions before continuing.' class ExPostQuestionnaire5(Page): form_model = 'player' form_fields = ['q_instructions_unclear'] def is_displayed(player): return player.consent == 'consent' def before_next_page(player, timeout_happened=False): player.record_timestamp('questionnaire', end=_now()) class ExPostQuestionnaire6(Page): form_model = 'player' form_fields = ['q_comments'] def is_displayed(player): return player.consent == 'consent' def before_next_page(player, timeout_happened=False): player.record_timestamp('questionnaire', end=_now()) # --------------------------------------------------------------------------- # Demographics # --------------------------------------------------------------------------- class Demographics(Page): form_model = 'player' form_fields = ['demo_age', 'demo_gender', 'demo_gender_self', 'demo_education', 'demo_ses', 'contrarian'] def is_displayed(player): return player.consent == 'consent' def vars_for_template(player): player.record_timestamp('demographics', start=_now()) return {} def before_next_page(player, timeout_happened=False): player.record_timestamp('demographics', end=_now()) # ============================================================================= # Payment page class # ============================================================================= class PaymentPage(Page): template_name = 'main_conditional/PaymentPage.html' def is_displayed(player): return player.consent == 'consent' def vars_for_template(player): info = _calculate_payment(player) return { 'payment_info': json.dumps(info), 'showup_fee': SHOWUP_FEE_EUR, 'mu_to_eur': MU_TO_EUR, } class PaymentSurveyRedirect(Page): template_name = 'main_conditional/PaymentSurveyRedirect.html' def vars_for_template(player): return { 'participant_id': player.participant.code, 'total_payment': player.participant.vars.get('total_payment_eur', ''), } # ============================================================================= # Page sequence # ============================================================================= page_sequence = [ Consent, NoConsent, GeneralInstructions, GeneralComprehension, LotteryInstructions, LotteryComprehension, AllocationInstructions, AllocationComprehension, PreferenceInstructions, PreferenceComprehension, PreferencePage1, PreferencePage2, ThresholdInstructions, ThresholdComprehension, ThresholdPage1, ThresholdPage2, AdditionalDecisionsInstructions, AdditionalDecisionsComprehension, AdditionalDecisionsPage1, AdditionalDecisionsPage2, AppropriatenessInstructions, AppropriatenessComprehension, AppropriatenessPage1, AppropriatenessPage2, UrnsTaskInstructions, UrnsTaskComprehension, UrnsTaskPage, IOSScale, ExPostQuestionnaire2, ExPostQuestionnaire3, ExPostQuestionnaire4, ExPostQuestionnaire5, ExPostQuestionnaire6, Demographics, PaymentPage, PaymentSurveyRedirect ]