from otree.api import Currency as c, currency_range from ._builtin import Page, WaitPage from .models import Constants import random import re # Transform currency to right form, with spaces and no trailing zeros def c_format(currency): c_str = str(currency) # turn currency into String c_space = re.sub(r"\B(?=(?:\d{3})+\.)", "\xa0", c_str) # add spaces between thousands with regex return c_space.rstrip('0').rstrip('.') # remov trailing zeros # return percentage to right form def percent_format(d): rendite1 = ' + ' + str("%.1f"%(d * 100)).rstrip('0').rstrip('.')+'%' if d < 0: rendite1 = ' - ' + str("%.1f"%(d * 100))[1:].rstrip('0').rstrip('.')+'%' return rendite1 def thousand_format(c): c_str = str(c) # currency to string c_space = re.sub(r"\B(?=(?:\d{3})+\.)", "\xa0", c_str) # add spacec with regex return c_space.rstrip('0').rstrip('.') def round_correct(float): return "{:0.2f}".format(float) #------------------------------------- # Welcome screens and instructions before the start of the experiment: #------------------------------------- class Instructions_WelcomeScreen(Page): template_name = 'PRE2/Instructions_WelcomeScreen.html' form_model = 'player' form_fields = ['browser_first', 'prolific_id'] def error_message(self,value): # print('prolific id is', value) # print(len(value)) if (value['prolific_id'] == None): return('Please enter your Prolific ID. Currently, the ID field is empty') elif (len(value['prolific_id']) != 24): id_len = len(value['prolific_id']) return('You Prolific ID is not correct! The ID you inserted has {} characters. The Prolific ID is 24 characters long'.format(id_len)) def is_displayed(self): return self.round_number == 1 class Demographics_BeforeExperiment(Page): # template_name = 'PRE2COPY/Demographics_BeforeExperiment.html' form_model = 'player' form_fields = ['Demographics_Age','Demographics_Sex'] def is_displayed(self): return self.round_number == 1 class Instructions_Payment(Page): template_name = 'PRE2/Instructions_Payment.html' def vars_for_template(self): # randomize the payoff example inv_asset1 = c(random.randint(1,9)*(10**3)) # turn the interger into currency inv_asset2 = c(10000) - inv_asset1 r_asset1 = random.randint(-3,3)/10 r_asset2 = random.randint(-3,3)/10 total_r = (1+r_asset1) * inv_asset1 + (1+r_asset2) * inv_asset2 optimal_inv2 = c(random.randint(1,9) * (10**3)) deviation_from_optimal = c(abs(float(optimal_inv2) - float(inv_asset2))) if self.player.participant.vars["goal_treatment"] == 4: bonus_pay = c(total_r / 10000) else: bonus_pay = c(2 - deviation_from_optimal / 5000) total_pay = bonus_pay + c(3.0) return {'inv_asset1': thousand_format(inv_asset1), 'inv_asset2': thousand_format(inv_asset2), 'total_r': thousand_format(total_r), 'r_asset1': percent_format(r_asset1), 'r_asset2': percent_format(r_asset2), 'r_plus11': 1+r_asset1, 'r_plus12': 1+r_asset2, 'optimal_inv2': thousand_format(optimal_inv2), 'deviation_from_optimal': thousand_format(deviation_from_optimal), 'bonus_pay': bonus_pay, 'total_pay': total_pay} def is_displayed(self): return self.round_number == 1 #------------------------------------- # Experiment: #------------------------------------- # class Instructions_BeforeRound(Page): # template_name = 'PRE2/Instructions_BeforeRound.html' # # # MU: Seems like this could be slimmed down. By construction, it only calls the first if case for group 1. Ither groups are excluded, so that the elif and else lines are never called ??? # def vars_for_template(self): # # Group 1: do not see portfolio returns, just 50 return pairs, and can only input their investment decision after seeing the 50 return pairs # return { # 'depends': 'We now start the first round of the experiment.\n\n'} # # def is_displayed(self): # return self.round_number == 1 and self.player.in_round(1).format == 2 # class I_SPG(Page): template_name = "PRE2/I_SPG.html" form_model = "player" form_fields = ["SelectedTreatment"] def is_displayed(self): return (self.round_number == 1) and (self.player.participant.vars["goal_treatment"] == 3) class Instructions_BeforeRound_wPFR(Page): template_name = "PRE2/Instructions_BeforeRound_wPFR.html" form_model = 'player' form_fields = ['InvestmentAsset20'] def vars_for_template(self): variable_for_2 = ((self.player.participant.id_in_session - 1) % 16 ) % 4 + 1 if self.player.in_round(1).format == 0: if self.round_number == 1: # Group 2: see portfolio returns and can already input their investment decisions while the 50 first return pairs are shown. Subsequently the investment decision is fixed and another 50 return pairs are drawn. return {'depends': 'We now start the first round of the experiment. \n\n', 'round': self.round_number, 'var': variable_for_2} else: return {'depends': 'We now start the second round of the experiment. The two assets you see in this round are unrelated to those from the first round.\n\n', 'round': self.round_number, 'var': variable_for_2} else: if self.round_number == 1: # Group 3: do not see portfolio returns, but can already input their investment decisions while the 50 first return pairs are shown. Subsequentlz the investment decision is fixed and another 50 return pairs are drawn. return {'depends': 'We now start the first round of the experiment.\n\n', 'round': self.round_number, 'var': variable_for_2} else: return {'depends': 'We now start the second round of the experiment. The two assets you see in this round are unrelated to those from the first round.\n\n', 'round': self.round_number , 'var': variable_for_2} def is_displayed(self): return self.round_number <= Constants.num_rounds and (self.player.in_round(1).format == 0 or self.player.in_round(1).format == 1) class T1n(Page): template_name = 'PRE2/T1n.html' form_model = 'player' form_fields = ['InvestmentAsset21', 'InvestmentAsset22', 'InvestmentAsset23', 'InvestmentAsset24', 'InvestmentAsset25', 'InvestmentAsset26', 'InvestmentAsset27', 'InvestmentAsset28', 'InvestmentAsset29','InvestmentAsset210', 'InvestmentAsset211', 'InvestmentAsset212', 'InvestmentAsset213', 'InvestmentAsset214', 'InvestmentAsset215', 'InvestmentAsset216', 'InvestmentAsset217', 'InvestmentAsset218', 'InvestmentAsset219', 'InvestmentAsset220', 'InvestmentAsset221', 'InvestmentAsset222', 'InvestmentAsset223', 'InvestmentAsset224', 'InvestmentAsset225', 'InvestmentAsset226', 'InvestmentAsset227', 'InvestmentAsset228', 'InvestmentAsset229', 'InvestmentAsset230', 'InvestmentAsset231', 'InvestmentAsset232', 'InvestmentAsset233', 'InvestmentAsset234', 'InvestmentAsset235', 'InvestmentAsset236', 'InvestmentAsset237', 'InvestmentAsset238', 'InvestmentAsset239', 'InvestmentAsset240', 'InvestmentAsset241', 'InvestmentAsset242', 'InvestmentAsset243', 'InvestmentAsset244', 'InvestmentAsset245', 'InvestmentAsset246', 'InvestmentAsset247', 'InvestmentAsset248', 'InvestmentAsset249', 'InvestmentAsset250', ] def is_displayed(self): return self.player.in_round(1).format == 1 # treatment with portfolio graph def vars_for_template(self): variable_for_2 = ((self.player.participant.id_in_session - 1) % 16) % 4 + 1 if self.player.participant.vars["goal_treatment"] == 3: investment_goal = self.player.in_round(1).SelectedTreatment else: investment_goal = self.player.participant.vars["goal_treatment"] == 3 width = 0 if self.round_number <= Constants.num_page and self.player.in_round(1).low_first == 1: return { 'widthgraph': width, 'title': 'das', 'array': self.player.participant.vars['low_cor_returns'], 'format': self.player.in_round(1).format, 'low_first': self.player.in_round(1).low_first, 'round': self.round_number, 'image_path':'PRE2/Empty.png', 'origin': str(self.player.in_round(self.round_number).InvestmentAsset20)[1:], 'investment_goal': investment_goal, 'var': variable_for_2} elif self.round_number <= Constants.num_page and self.player.in_round(1).low_first == 0: # subject.temprnum = subject.round_number return {'widthgraph': width, 'title': 'dafsd', 'round': self.round_number, 'array': self.player.participant.vars['high_cor_returns'], 'format': self.player.in_round(1).format, 'low_first': self.player.in_round(1).low_first, 'image_path':'PRE2/Empty.png', 'origin': str(self.player.in_round(self.round_number).InvestmentAsset20)[1:], 'investment_goal': investment_goal, 'var': variable_for_2} elif self.round_number > Constants.num_page and self.player.in_round(1).low_first == 1: # subject.temprnum = subject.round_number return {'widthgraph': width, 'title': 'das', 'round': self.round_number, 'array': self.player.participant.vars['high_cor_returns'], 'format': self.player.in_round(1).format, 'low_first': self.player.in_round(1).low_first, 'image_path':'PRE2/Empty.png', 'origin': str(self.player.in_round(self.round_number).InvestmentAsset20)[1:], 'investment_goal': investment_goal, 'var': variable_for_2} elif self.round_number > Constants.num_page and self.player.in_round(1).low_first == 0: # subject.temprnum = subject.round_number return {'widthgraph': width, 'title':'das', 'round': self.round_number, 'array': self.player.participant.vars['low_cor_returns'], 'format': self.player.in_round(1).format, 'low_first': self.player.in_round(1).low_first, 'image_path':'PRE2/Empty.png', 'origin': str(self.player.in_round(self.round_number).InvestmentAsset20)[1:], 'investment_goal': investment_goal, 'var': variable_for_2} class TN(Page): template_name = 'PRE2/TN.html' form_model = 'player' form_fields = ['InvestmentAsset21', 'InvestmentAsset22', 'InvestmentAsset23', 'InvestmentAsset24', 'InvestmentAsset25', 'InvestmentAsset26', 'InvestmentAsset27', 'InvestmentAsset28', 'InvestmentAsset29','InvestmentAsset210', 'InvestmentAsset211', 'InvestmentAsset212', 'InvestmentAsset213', 'InvestmentAsset214', 'InvestmentAsset215', 'InvestmentAsset216', 'InvestmentAsset217', 'InvestmentAsset218', 'InvestmentAsset219', 'InvestmentAsset220', 'InvestmentAsset221', 'InvestmentAsset222', 'InvestmentAsset223', 'InvestmentAsset224', 'InvestmentAsset225', 'InvestmentAsset226', 'InvestmentAsset227', 'InvestmentAsset228', 'InvestmentAsset229', 'InvestmentAsset230', 'InvestmentAsset231', 'InvestmentAsset232', 'InvestmentAsset233', 'InvestmentAsset234', 'InvestmentAsset235', 'InvestmentAsset236', 'InvestmentAsset237', 'InvestmentAsset238', 'InvestmentAsset239', 'InvestmentAsset240', 'InvestmentAsset241', 'InvestmentAsset242', 'InvestmentAsset243', 'InvestmentAsset244', 'InvestmentAsset245', 'InvestmentAsset246', 'InvestmentAsset247', 'InvestmentAsset248', 'InvestmentAsset249', 'InvestmentAsset250', ] def is_displayed(self): return self.player.in_round(1).format == 0 # no portfolio return is shown def vars_for_template(self): variable_for_2 = ((self.player.participant.id_in_session - 1) % 16) % 4 + 1 if self.player.participant.vars["goal_treatment"] == 3: investment_goal = self.player.in_round(1).SelectedTreatment else: investment_goal = self.player.participant.vars["goal_treatment"] == 3 width = 0 if self.round_number <= Constants.num_page and self.player.in_round(1).low_first == 1: # subject.temprnum = subject.round_number # print("origin") # print(float(str(self.player.in_round(self.round_number).InvestmentAsset20)[1:])) return { 'widthgraph': width, 'title': 'das', 'array': self.player.participant.vars['low_cor_returns'], 'format': self.player.in_round(1).format, 'low_first': self.player.in_round(1).low_first, 'round': self.round_number, 'image_path':'PRE2/Empty.png', 'origin': str(self.player.in_round(self.round_number).InvestmentAsset20)[1:], 'investment_goal': investment_goal, 'var': variable_for_2} elif self.round_number <= Constants.num_page and self.player.in_round(1).low_first == 0: # subject.temprnum = subject.round_number return {'widthgraph': width, 'title': 'dafsd', 'round': self.round_number, 'array': self.player.participant.vars['high_cor_returns'], 'format': self.player.in_round(1).format, 'low_first': self.player.in_round(1).low_first, 'image_path':'PRE2/Empty.png', 'origin': str(self.player.in_round(self.round_number).InvestmentAsset20)[1:], 'investment_goal': investment_goal, 'var': variable_for_2} elif self.round_number > Constants.num_page and self.player.in_round(1).low_first == 1: # subject.temprnum = subject.round_number return {'widthgraph': width, 'title': 'das', 'round': self.round_number, 'array': self.player.participant.vars['high_cor_returns'], 'format': self.player.in_round(1).format, 'low_first': self.player.in_round(1).low_first, 'image_path':'PRE2/Empty.png', 'origin': str(self.player.in_round(self.round_number).InvestmentAsset20)[1:], 'investment_goal': investment_goal, 'var': variable_for_2} elif self.round_number > Constants.num_page and self.player.in_round(1).low_first == 0: # subject.temprnum = subject.round_number return {'widthgraph': width, 'title':'das', 'round': self.round_number, 'array': self.player.participant.vars['low_cor_returns'], 'format': self.player.in_round(1).format, 'low_first': self.player.in_round(1).low_first, 'image_path':'PRE2/Empty.png', 'origin': str(self.player.in_round(self.round_number).InvestmentAsset20)[1:], 'investment_goal': investment_goal, 'var': variable_for_2} class Task_InvestmentDecision(Page): # template_name = 'PRE2COPY/Task_InvestmentDecision.html' form_model = 'player' form_fields = ['InvestmentAsset2'] def is_displayed(self): return self.player.in_round(1).format == 2 and (self.round_number == Constants.num_page or self.round_number == Constants.num_rounds) def vars_for_template(self): return {'depend': ''} # class Task_ReasonsFree(Page): # form_model = 'player' # form_fields = ['RoundSpecificReasons_Free'] # # def is_displayed(self): # return self.round_number == Constants.num_page or self.round_number == Constants.num_rounds class Task_BeliefsDependence(Page): # template_name = 'PRE2COPY/Task_BeliefsDependence.html' form_model = 'player' form_fields = ['BeliefsDependence_FreqComove2', 'BeliefsDependence_Direction1', 'BeliefsDependence_Overall'] def is_displayed(self): return self.round_number == Constants.num_page or self.round_number == Constants.num_rounds class Task_BeliefsAssets(Page): # template_name = 'PRE2COPY/Task_BeliefsAssets.html' form_model = 'player' form_fields = ['BeliefsDependence_Mu1', 'BeliefsDependence_Mu2', 'BeliefsDependence_PLoss1', 'BeliefsDependence_PLoss2','BeliefsAssets_SubjRisk1','BeliefsAssets_SubjRisk2'] def is_displayed(self): return self.round_number == Constants.num_page or self.round_number == Constants.num_rounds class Task_BeliefsPortfolio(Page): # template_name = 'PRE2COPY/Task_BeliefsPortfolio.html' form_model = 'player' form_fields = ['BeliefsPortfolio_Mu', 'BeliefsPortfolio_PLoss', 'BeliefsPortfolio_PLGain', 'BeliefsPortfolio_PLLoss', 'BeliefsPortfolio_SubjRisk'] def is_displayed(self): return self.round_number == Constants.num_page or self.round_number == Constants.num_rounds class Instructions_Round2(Page): template_name = 'PRE2/Instructions_Round2.html' def is_displayed(self): return self.round_number == Constants.num_page and self.player.in_round(1).format == 2 class Task_RoundComparison(Page): # template_name = 'PRE2COPY/Task_RoundComparison.html' form_model = 'player' form_fields = ['ComparisonRounds_Risk', 'ComparisonRounds_Return'] def is_displayed(self): return self.round_number == Constants.num_rounds #------------------------------------- # Questionnaire after experiment: #------------------------------------- class Task_RFD(Page): template_name = 'PRE2/Task_RFD.html' form_model = 'player' form_fields = ['GenReasons_Mu_Consider', 'GenReasons_SigmaPL_Consider', 'GenReasons_Rho_Consider' ] def is_displayed(self): return self.round_number == Constants.num_rounds class Task_ReasonsContinue(Page): template_name = 'PRE2/Task_ReasonsContinue.html' form_model = 'player' # select the questions asked based on the Task_ReasonsForDecision answers def get_form_fields(self): form_fields = [] player = self.player if player.GenReasons_Mu_Consider == 2: form_fields.append('GenReasons_Mu_Direction') if player.GenReasons_SigmaPL_Consider == 2: form_fields.append('GenReasons_SigmaPL_Direction') if player.GenReasons_Rho_Consider==2: form_fields.append('GenReasons_Rho_Direction') # if player.GenReasons_PD_Consider == 2: # form_fields.append('GenReasons_PD_Direction') return form_fields def is_displayed(self): right_round = self.round_number == Constants.num_rounds player = self.player right_ans = player.GenReasons_Mu_Consider==2 or player.GenReasons_SigmaPL_Consider==2 or player.GenReasons_Rho_Consider==2 # or player.GenReasons_PD_Consider==2 return right_round and right_ans class Demographics_1(Page): # template_name = 'PRE2COPY/Demographics_1.html' form_model = 'player' form_fields = ['Demographics_FinInterest', 'Demographics_Investor', 'Demographics_FinanceProf', 'Demographics_RiskAffinity', 'Demographics_Statistics'] def is_displayed(self): return self.round_number == Constants.num_rounds class Demographics_2(Page): # template_name = 'PRE2COPY/Demographics_2.html' form_model = 'player' form_fields = ['DJ30PF_Return', 'DJ30PF_Risk'] def before_next_page(self): self.player.set_payoffs() def is_displayed(self): return self.round_number == Constants.num_rounds #------------------------------------- # Payment and link to Prolific: #------------------------------------- class FinalPaymentInformation(Page): template_name = 'PRE2/FinalPaymentInformation.html' form_model = 'player' form_fields = ['Satisfaction', 'OpenFeedback'] def is_displayed(self): return self.round_number == Constants.num_rounds def vars_for_template(self): # payoff information for payoff_treatment == 4: # Turn returns to percentage, dropping trailing zeroes and adding +/- to front # rendite1 = ' + ' + str("%.1f" % (self.player.paid_rendite1 * 100)).rstrip('0').rstrip('.') # if self.player.paid_rendite1 < 0: # rendite1 = ' - ' + str("%.1f" % (self.player.paid_rendite1 * 100))[1:].rstrip('0').rstrip('.') # rendite2 = ' + ' + str("%.1f" % (self.player.paid_rendite2 * 100)).rstrip('0').rstrip('.') # if self.player.paid_rendite2 < 0: # rendite2 = ' - ' + str("%.1f" % (self.player.paid_rendite2 * 100))[1:].rstrip('0').rstrip('.') if self.player.participant.vars["goal_treatment"] < 4: rendite1 = None; rendite2 = None; payoff_100 = None bonus_pay = self.player.payoff payoff = c(bonus_pay) base_pay = c(3.0) total_pay = base_pay + bonus_pay input2 = self.participant.vars["final_investment_in_asset2"] input1 = c(10000) - self.participant.vars["final_investment_in_asset2"] optimal_inv1 = c(self.participant.vars["optimal_portfolio"][0] * 10000) optimal_inv2 = c(self.participant.vars["optimal_portfolio"][1] * 10000) abs_diff = c(abs( float(input2 - optimal_inv2))) #THIS VERSION WILL RETURN BOT ASSET RETURNS else: bonus_pay = self.player.payoff payoff = c(bonus_pay) optimal_inv1 = None; optimal_inv2 = None; abs_diff = None rendite1 = percent_format(self.player.paid_rendite1) rendite2 = percent_format(self.player.paid_rendite2) # percent_format(self.player.paid_rendite2) # paid_rendite1 = realized return #WORK IN PROGRESS VERSION bonus_pay = (((1+self.player.paid_rendite1) * self.participant.vars["final_investment_in_asset2"]) + (1+self.player.paid_rendite2) * (1000))/10000 payoff_100 = c_format(c(bonus_pay)*10000) base_pay = c(3.0) total_pay = base_pay + bonus_pay input2 = c_format(self.participant.vars["final_investment_in_asset2"]) input1 = c_format(c(10000) - self.participant.vars["final_investment_in_asset2"]) #READY-TO-GO VERSION - RETURNS ONLY PORTFOLIO RETURN #else: # optimal_inv1 = None; optimal_inv2 = None; abs_diff = None # rendite1 = percent_format(self.player.payoff) ## rendite2 = c(10000)-c(self.player.payoff) # bonus_pay = self.player.payoff # payoff_100 = c_format(bonus_pay * 10000) # base_pay = c(self.session.config['participation_fee']) # total_pay = base_pay + bonus_pay # input2 = c_format(self.participant.vars["final_investment_in_asset2"]) # input1 = c_format(c(10000) - self.participant.vars["final_investment_in_asset2"]) return {'runde': self.participant.vars["paid_subsection"], 'percent_1': rendite1, 'percent_2': rendite2, 'payoff_100': payoff_100, 'paid_input1': input1, 'paid_input2': input2, 'payoff': payoff, 'total_pay': total_pay, 'base_pay': base_pay, 'goal_treatment': self.participant.vars["goal_treatment"], 'input1': c_format(input1), 'input2': c_format(input2), 'optimal_inv1': c_format(optimal_inv1), 'optimal_inv2': c_format(optimal_inv2), 'abs_diff': c_format(abs_diff) } class LinkToProlific(Page): template_name = 'PRE2/LinkToProlific.html' def is_displayed(self): return self.round_number == Constants.num_rounds # ------------------------------------- # Page sequence #------------------------------------- page_sequence = ( Instructions_WelcomeScreen, Demographics_BeforeExperiment, Instructions_Payment, I_SPG, # Instructions_BeforeRound, Instructions_BeforeRound_wPFR, TN, T1n, Task_InvestmentDecision, # Task_ReasonsFree, Task_BeliefsDependence, Task_BeliefsAssets, Task_BeliefsPortfolio, Instructions_Round2, Task_RoundComparison, Task_RFD, Task_ReasonsContinue, Demographics_1, Demographics_2, FinalPaymentInformation, LinkToProlific )