from otree.api import Currency as c, currency_range from ._builtin import Page, WaitPage from .models import Constants, Player, Group from .logger import logger import random import config_collat as cc debug = True """docstring format Method Description: Input: Output: Class Description: Input: Output: """ """Instructions Pages""" class WelcomePage(Page): def before_next_page(self): players = self.group.get_players() << << << < HEAD == == == = print(self.group.treatment_contract_enforcement) >>>>>> > Marco for p in players: p.example_loan_amount = random.randrange(1, 11, 2) p.example_return_amount = p.example_loan_amount * Constants.productivity p.example_collateral_amount = random.randrange(1, 11, 2) p.example_salvage_max = max(0, p.example_collateral_amount - int(Constants.recovery_fee)) p.prac_collateral_offer = random.randrange(1, 11, 2) p.prac_loan_offer = random.randrange(1, 11, 2) p.prac_max_return = p.prac_loan_offer * Constants.productivity p.prac_return = random.randrange(1, p.prac_max_return, 2) p.prac_collateral_after_cost = max( 0, p.prac_collateral_offer - int(Constants.recovery_fee)) p.prac_recovered_collateral = random.randrange(1, Constants.initial_points , 2) if p.prac_collateral_offer < 3: p.prac_return = 1 else: p.prac_return = random.randrange(1, p.prac_collateral_offer, 2) if self.player.role() == 'lender': self.player.prac_borrower_money -= self.player.prac_collateral_offer class TaskOverview(Page): # Provides general overview of tasks pass class RoleAssignment(Page): # Informs player of the task instructions pass class LoanAgreement(Page): # Explains the loan agreement/package that will be created in the game pass class PracCollateralOffer(Page): form_model = 'player' form_fields = ['prac_collateral_offer_input'] def prac_collateral_offer_input_error_message(self, value): '''returns an error message if the player inputs the wrong value''' if value != self.player.prac_collateral_offer: return 'Enter a collateral offer of ' + str(self.player.prac_collateral_offer) + ' to continue.' def before_next_page(self): self.player.current_practice_page += 1 self.player.prac_borrower_money -= self.player.prac_collateral_offer self.player.prac_lender_money -= self.player.prac_loan_offer def is_displayed(self): if self.player.role() == 'borrower': return True else: return False class PracLoanOffer(Page): form_model = 'player' form_fields = ['prac_loan_offer_input'] def before_next_page(self): self.player.current_practice_page += 1 self.player.prac_lender_money -= self.player.prac_loan_offer self.player.prac_project_return = self.player.prac_loan_offer * Constants.productivity self.player.prac_borrower_money += self.player.prac_project_return self.player.prac_lender_money += self.player.prac_return self.player.prac_borrower_money -= self.player.prac_return def vars_for_template(self): pass def prac_loan_offer_input_error_message(self, value): '''returns an error message if the player inputs the wrong value''' if value != self.player.prac_loan_offer: return 'Enter a loan offer of ' + str(self.player.prac_loan_offer) + ' to continue.' def is_displayed(self): if self.player.role() == 'lender': return True else: return False class PracLoanPackageDecision(Page): form_model = 'player' form_fields = ['prac_loan_package_decision'] def before_next_page(self): self.player.current_practice_page += 1 self.player.prac_borrower_money += self.player.prac_loan_offer * Constants.productivity self.player.prac_project_return = self.player.prac_loan_offer * Constants.productivity def prac_loan_package_decision_error_message(self, value): '''returns an error message if the player inputs the wrong value''' if value != 'Yes': return 'Accept the loan package to continue' def is_displayed(self): if self.player.role() == 'borrower': return True else: return False class LoanFulfillment(Page): # Explains the process of loan fulfillment pass class PracReturnRealization(Page): form_model = 'player' form_fields = ['prac_return_input'] def before_next_page(self): self.player.current_practice_page += 1 self.player.prac_recovered_collateral= self.player.prac_recovered_collateral self.player.prac_returned_collateral = max( 0, self.player.prac_collateral_offer - self.player.prac_recovered_collateral - Constants.recovery_fee) self.player.prac_borrower_money += self.player.prac_returned_collateral self.player.prac_lender_money += self.player.prac_recovered_collateral self.player.prac_borrower_money -= self.player.prac_return self.player.prac_lender_money += self.player.prac_return def prac_return_input_error_message(self, value): if value != self.player.prac_return: return 'Enter a return of ' + str(self.player.prac_return) + ' to continue.' def vars_for_template(self): return {'expected_return': Constants.rate_of_return * self.player.prac_loan_offer, 'initial_points _after_loan': Constants.initial_points + Constants.productivity*self.player.prac_loan_offer, 'loan_value': self.player.prac_loan_offer * Constants.productivity} def is_displayed(self): if self.player.role() == 'borrower': return True else: return False class LoanRemediation(Page): # Explains loan remediation process pass class PracCollateralDecision(Page): form_model = 'player' form_fields = ['prac_collateral_decision'] def before_next_page(self): self.player.current_practice_page += 1 def prac_collateral_decision_error_message(self, value): if value != 'Yes': return 'Select yes to continue.' def vars_for_template(self): return {'expected_return': Constants.rate_of_return * self.player.prac_loan_offer} def is_displayed(self): if Constants.contract_enforcement == False and self.player.role() == 'lender': return True else: return False class PracCollateralSeizure(Page): if Constants.contract_enforcement == False: form_model = 'player' form_fields = ['prac_collateral_seizure_input'] def before_next_page(self): self.player.current_practice_page += 1 self.player.prac_borrower_money += max(0, self.player.prac_collateral_offer - self.player.prac_recovered_collateral - Constants.recovery_fee) self.player.prac_lender_money += self.player.prac_recovered_collateral self.player.prac_recovered_collateral= self.player.prac_recovered_collateral self.player.prac_returned_collateral = max( 0, self.player.prac_collateral_offer - self.player.prac_recovered_collateral - Constants.recovery_fee) def prac_collateral_seizure_input_error_message(self, value): if value != self.player.prac_recovered_collateral: return 'Enter a value of ' + str(self.player.prac_recovered_collateral) + ' to continue.' def vars_for_template(self): return {'expected_return': Constants.rate_of_return*self.player.prac_loan_offer} def is_displayed(self): if self.player.role() == 'lender': return True else: return False class PracResults(Page): def before_next_page(self): players = self.group.get_players() p = self.player p.prac_borrower_money = Constants.initial_points p.prac_lender_money = Constants.initial_points p.example_loan_amount = random.randrange(1, 11, 2) p.example_return_amount = p.example_loan_amount * Constants.productivity p.example_collateral_amount = random.randrange(1, 11, 2) p.example_salvage_max = max(0, p.example_collateral_amount - int(Constants.recovery_fee)) p.prac_collateral_offer = random.randrange(1, 11, 2) p.prac_loan_offer = random.randrange(1, 11, 2) p.prac_max_return = p.prac_loan_offer * Constants.productivity p.prac_return = random.randrange(1, p.prac_max_return, 2) p.prac_collateral_after_cost = max(0, p.prac_collateral_offer - int(Constants.recovery_fee)) p.prac_recovered_collateral = random.randrange(1, Constants.initial_points , 2) if p.prac_collateral_offer < 3: p.prac_return = 1 else: p.prac_return = random.randrange(1, p.prac_collateral_offer, 2) players[0].prac_borrower_money -= players[0].prac_collateral_offer p.current_practice_page = 1 class TaskRepayment(Page): # Explains how task payoffs are determined pass class ScoreSheetOverview(Page): pass class InstructionsResults(Page): def before_next_page(self): self.participant.payoff += Constants.instructions_payoff class QuestionOne(Page): _allow_custom_attributes = True timeout_seconds = Constants.timer form_model = 'player' form_fields = ['question_one_response'] print(timeout_seconds) def question_one_response_error_message(self, value): '''returns an error message if the player answers the question incorrectly''' if value != True: self.player.q1_incorrect_attempts += 1 return "Incorrect. The correct answer is 'True'" def before_next_page(self): self.player.quiz_page_counter += 1 print(self.timeout_seconds) if self.timeout_happened: self.player.q1_timeout = 1 if self.player.q1_incorrect_attempts > 1: self.player.q1_incorrect = 1 if self.player.q1_incorrect_attempts < 1 and self.timeout_happened == False: self.player.num_correct += 1 # awards player for answering correctly on the first attempt self.player.quiz_earnings += c(2) class QuestionTwo(Page): _allow_custom_attributes = True timeout_seconds = Constants.timer form_model = 'player' form_fields = ['question_two_response'] def question_two_response_error_message(self, value): if value != 1: self.player.q2_incorrect_attempts += 1 return 'Incorrect. The correct answer is' + '\nThe lender offers a loan and a repayment rate, the borrower offers collateral, the lender accepts the loan agreement' def before_next_page(self): self.player.quiz_page_counter += 1 if self.timeout_happened: self.player.q2_timeout = 1 if self.player.q2_incorrect_attempts > 1: self.player.q2_incorrect = 1 if self.player.q2_incorrect_attempts < 1 and self.timeout_happened == False: self.player.num_correct += 1 self.player.quiz_earnings += c(2) class QuestionThree(Page): _allow_custom_attributes = True timeout_seconds = Constants.timer form_model = 'player' form_fields = ['question_three_response'] def question_three_response_error_message(self, value): if value != 1: self.player.q3_incorrect_attempts += 1 return 'Incorrect. The correct answer is 0-10' def before_next_page(self): self.player.quiz_page_counter += 1 if self.timeout_happened: self.player.q3_timeout = 1 if self.player.q3_incorrect_attempts > 1: self.player.q3_incorrect = 1 if self.player.q3_incorrect_attempts < 1 and self.timeout_happened == False: self.player.num_correct += 1 self.player.quiz_earnings += c(2) class QuestionFour(Page): _allow_custom_attributes = True timeout_seconds = Constants.timer form_model = 'player' form_fields = ['question_four_response'] def question_four_response_error_message(self, value): if value != 4: self.player.q4_incorrect_attempts += 1 return 'Incorrect. The correct answer is 3' def before_next_page(self): if self.timeout_happened: self.player.q4_timeout = 1 if self.player.q4_incorrect_attempts > 1: self.player.q4_incorrect = 1 if self.player.q4_incorrect_attempts < 1 and self.timeout_happened == False: self.player.num_correct += 1 self.player.quiz_earnings += c(2) class QuestionFive(Page): _allow_custom_attributes = True timeout_seconds = Constants.timer form_model = 'player' form_fields = ['question_five_response'] def question_five_response_error_message(self, value): if value != 2 and (Constants.treatment == 'treatment1' or Constants.treatment == 'treatment2'): self.player.q5_incorrect_attempts += 1 return 'Incorrect. The correct answer is The lender' elif value != 2: self.player.q5_incorrect_attempts += 1 return 'Incorrect. The correct answer is A contractual algorithm' def before_next_page(self): self.player.quiz_page_counter += 1 if self.timeout_happened: self.player.q5_timeout = 1 if self.player.q5_incorrect_attempts > 1: self.player.q5_incorrect = 1 if self.player.q5_incorrect_attempts < 1 and self.timeout_happened == False: self.player.num_correct += 1 self.player.quiz_earnings += c(2) class QuestionSix(Page): _allow_custom_attributes = True timeout_seconds = Constants.timer form_model = 'player' form_fields = ['question_six_response'] def question_six_response_error_message(self, value): if value != 3: self.player.q6_incorrect_attempts += 1 return 'Incorrect. The correct answer is 3' def before_next_page(self): self.player.quiz_page_counter += 1 if self.timeout_happened: self.player.q6_timeout = 1 if self.player.q6_incorrect_attempts > 1: self.player.q6_incorrect = 1 if self.player.q6_incorrect_attempts < 1 and self.timeout_happened == False: self.player.num_correct += 1 self.player.quiz_earnings += c(2) class QuizResults(Page): def vars_for_template(self): return {'dollar_amount': self.player.quiz_earnings.to_real_world_currency(self.session) } # Shows subject how much they earned from the quiz def before_next_page(self): # adds quiz earnings to player's payoff self.participant.payoff += c(self.player.quiz_earnings) class QuizTimeout(Page): _allow_custom_attributes = True timeout_seconds = Constants.timer def vars_for_template(self): player = self.player return {'answer': Constants.quiz_answers[player.quiz_page_counter - 1]} def is_displayed(self): player = self.player timeouts = [player.q1_timeout, player.q2_timeout, player.q3_timeout, player.q4_timeout, player.q5_timeout, player.q6_timeout] incorrect_attempts = [player.q1_incorrect, player.q2_incorrect, player.q3_incorrect, player.q4_incorrect, player.q5_incorrect, player.q6_incorrect] # Auxiliary counters for the timeout display timeout_counter = 0 incor_attempt_counter = 0 for time in timeouts: if time == 1: timeout_counter += 1 for incorrect_attempt in incorrect_attempts: if incorrect_attempt != 0: incor_attempt_counter += 1 if timeout_counter > 0 and incor_attempt_counter == 0: return True else: return False def before_next_page(self): player = self.player # Hardcoded until a unique quiz page is done player.q1_timeout = 0 player.q2_timeout = 0 player.q3_timeout = 0 player.q4_timeout = 0 player.q5_timeout = 0 player.q6_timeout = 0 player.q1_incorrect = 0 player.q2_incorrect = 0 player.q3_incorrect = 0 player.q4_incorrect = 0 player.q5_incorrect = 0 player.q6_incorrect = 0 class Collateral_Offer(Page): """ Description: Collateral_Offer inherits the oTree class Page. The purpose of this page is to ask the borrower for the amount that they are willing to pledge as collateral. Input: Page -(oTree Class) Output: Form Field - (to HTML Collateral_Offer.HTML) """ form_model = 'group' form_fields = ['collateral'] def before_next_page(self): """ Description: before_next_page performs the necessary calculations and modifications of variables before the experiment continues. In this case the value of collateral is taken from the borrowers money and held in escrow. Input: Borrower Money - (Global, Stored in models.py, Player, Currency) Collateral - (Global, Stored in models.py, Group, Currency) Value of Collateral - (Global, Stored in models.py, Group, Currency) Output: Collateral - (Player Input) Value of Collateral - (Collateral - Salvage Cost, Min = 0) Borrower Money - (Money - Collateral) """ group = self.group borrower = self.player '''Places borrowers collateral pledge in escrow''' borrower.money -= group.collateral '''Assigns value of collateral as equal to collateral - salvage cost. If for some reason the salvage cost is higher than the collateral its value is assigned to zero.''' group.collateral_after_feeee = max(0, group.collateral - Constants.recovery_fee) def is_displayed(self): """ Description: is_displayed tells oTree which player to show the relevant page to and under what conditions. Input: Player Role - (Global, Stored in models.py, Player) Output: Bool - (to oTree Page class) """ if self.player.role() == 'borrower': return True else: return False class Loan_Amount(Page): # TODO: Update docstrings to include lender and borrower profit terms """ Description: Loan_Amount inherits the oTree class Page. The purpose of this page is to ask the lender for the amount that they are willing to lend to the borrower. Input: Page -(oTree Class) Output: Form Field - (to HTML Loan_Amount.HTML) """ form_model = 'group' form_fields = ['loan'] def before_next_page(self): """ Description: before_next_page performs the necessary calculations and modifications of variables before the experiment continues. In this case the value of the loan is taken from the lender multiplied by productivity and passed to the borrower. Input: Borrower - (Global, Location of Borrower) Lender - (Global, Location of Lender) Lender Money - (Global, Stored in models.py, Player, Currency) Borrower Money - (Global, Stored in models.py, Player, Currency) Loan - (Global, Stored in models.py, Group, Currency) Productivity - (Global, Stored in models.py, Constants, Integer) Output: Loan - (Player Input) Lender Money - (Money - Loan) Borrower Money - (Money + Loan * Productivity) """ group = self.group lender = self.player '''group.get_players returns a list of players''' borrower = group.get_players()[1] """Passes the loan from the lender to the borrower and multiplies it by productivity""" lender.money -= group.loan borrower.money += group.loan * Constants.productivity group.project_return = group.loan * Constants.productivity group.expected_return = Constants.rate_of_return * group.loan group.expected_borrower_profit = Constants.productivity * group.loan - group.expected_return def is_displayed(self): """ Description: is_displayed tells oTree which player to show the relevant page to and under what conditions. Input: Player Role - (Global, Stored in models.py, Player) Output: Bool - (to oTree Page class) """ if self.player.role() == 'lender': return True else: return False class Loan_Package(Page): # package isn't what is being made """ Description: Loan_Package inherits the oTree class Page. This page confirms with the borrower that they want would like to accept the loan package. Input: Page - (oTree Class) Output: Form Field - (to HTML Loan_Package.HTML) """ form_model = 'group' form_fields = ['accept_loan'] def before_next_page(self): """ Description: before_next_page performs the necessary calculations and modifications of variables before the experiment continues. In this Case if the Loan package is not accepted borrower and lender money is restored to their endowments. Input: Accept Loan - (Global, Location of Borrower, String) Lender - (Global, Location of Lender) Lender Money - (Global, Stored in models.py, Player, Currency) Borrower Money - (Global, Stored in models.py, Player, Currency) Loan - (Global, Stored in models.py, Group, Currency) Productivity - (Global, Stored in models.py, Constants, Integer) Output: Loan - (Player Input) Lender Money - (Money - Loan) Borrower Money - (Money + Loan * Productivity) """ '''if the loan package isn't accepted restores both players money to their endowments''' group = self.group if group.accept_loan == 'No': for i in group.get_players(): i.money = Constants.initial_points def vars_for_template(self): """ Description: vars_for_template passes Django variables to the HTML. Input: repayment rate- (Global, Stored in models.py, Group, Integer) Loan - (Global, Stored in models.py, Group, Currency) Productivity - (Global, Stored in models.py, Constants, Integer) Output: Expected Value - (Loan * repayment rate) Total Value - (Loan * Productivity) Left Over - (Loan * Productivity - Loan * repayment rate) Loan Value - (Loan * Productivity) Package - (Dictionary, for Django Document) """ group = self.group Package = {} Package['expected_value'] = (group.loan * Constants.rate_of_return) Package['project_return'] = (group.loan * Constants.productivity) Package['left_over'] = (group.loan * Constants.productivity) - \ (group.loan * Constants.rate_of_return) Package['loan_value'] = group.loan * Constants.productivity return Package def is_displayed(self): """ Description: is_displayed tells oTree which player to show the relevant page to and under what conditions. Input: Player Role - (Global, Stored in models.py, Player) Output: Bool - (to oTree Page class) """ if self.player.role() == 'borrower': return True else: return False class Realize_Return(Page): """ Description: Realize_Return inherits the oTree class Page. On this page the borrower decides how much of their project return they would like to return to the lender. Input: Page - (oTree Class) Output: Form Field - (to HTML Realize_Return.HTML) """ form_model = 'group' form_fields = ['lender_return'] def before_next_page(self): """ Description: before_next_page performs the necessary calculations and modifications of variables before the experiment continues. In this case the return to the lender is added to their money and substracted from the borrowers additionally if the treatment has contract enforcement == True satisfaction is determined. Input: Lender - (Global, Location of Lender) Borrower - (Global, Location of Borrower) Lender Money - (Global, Stored in models.py, Player, Currency) Borrower Money - (Global, Stored in models.py, Player, Currency) Loan - (Global, Stored in models.py, Group, Currency) Productivity - (Global, Stored in models.py, Constants, Integer) Lender Return - (Global, Stored in models.py, Group, Currency) repayment rate- (Global, Stored in models.py, Group, Float) Actual Return - (Global, Stored in models.pu, Group, Float) Output: Lender Money - (Money - Loan) Borrower Money - (Money + Loan * Productivity) """ group = self.group lender = group.get_players()[0] borrower = group.get_players()[1] """passes the money returned by the borrower to the lender""" lender.money += group.lender_return borrower.money -= group.lender_return """assigns the value of the real return""" if group.loan != 0: """without this oTree was rounding the actual return""" lender_return = float(group.lender_return) loan = float(group.loan) group.actual_return = lender_return/loan else: group.actual_return = 0 '''what this does it auto define satisfaction if contract enforcement == True contract enforcement. This is done by checking actual return vs expected repayment rate''' if Constants.contract_enforcement == True: if Constants.rate_of_return > group.actual_return: group.satisfied = 'No' else: group.satisfied = 'Yes' borrower.money += group.collateral def vars_for_template(self): """ Description: vars_for_template passes Django variables to the HTML. Input: repayment rate- (Global, Stored in models.py, Group, Integer) Loan - (Global, Stored in models.py, Group, Currency) Productivity - (Global, Stored in models.py, Constants, Integer) initial_points - (Global, Stored in models.py, Constants, Currency) Output: Expected Return - (Loan * repayment rate) initial_points After Loan - (initial_points + Loan * Productivity) Loan Value - (Loan * Productivity) """ group = self.group return {'expected_return': Constants.rate_of_return * group.loan, 'initial_points _after_loan': Constants.initial_points + Constants.productivity*group.loan, 'loan_value': group.loan * Constants.productivity} def is_displayed(self): """ Description: is_displayed tells oTree which player to show the relevant page to and under what conditions. Input: Player Role - (Global, Stored in models.py, Player) Accept Loan - (Global, Stored in models.py, Group, String) Output: Bool - (to oTree Page class) """ group = self.group if group.accept_loan == 'Yes' and self.player.role() == 'borrower': return True else: return False class Collateral_Decision(Page): """ Description: Collateral Decision inherits the oTree class Page. On this page the lender decides whether or not to go after the borrowers collateral. This page is only shown if Contract Enforcement == False. Input: Page - (oTree Class) Output: Form Field - (to HTML Collateral_Decision.HTML) """ form_model = 'group' form_fields = ['satisfied'] def before_next_page(self): """ Description: before_next_page performs the necessary calculations and modifications of variables before the experiment continues. In this case it returns the amount of collateral not seized to the borrower. Input: Borrower - (Global, Location of Borrower) Borrower Money - (Global, Stored in models.py, Player, Currency) Satisfied - (Global, Stored in models.py, Group, String) Output: Conditional Satisfied = Yes Borrower Money - (Money + Collateral) """ group = self.group borrower = group.get_players()[1] if group.satisfied == 'Yes': borrower.money += group.collateral def vars_for_template(self): """ Description: vars_for_template passes Django variables to the HTML. Input: repayment rate- (Global, Stored in models.py, Group, Integer) Loan - (Global, Stored in models.py, Group, Currency) Actual Return - (Global, Stored in models.py, Group, Float) Output: Expected Return - (Loan * repayment rate) initial_points After Loan - (initial_points + Loan * Productivity) Loan Value - (Loan * Productivity) """ group = self.group return {'expected_return': Constants.rate_of_return*group.loan} def is_displayed(self): """ Description: is_displayed tells oTree which player to show the relevant page to and under what conditions. Input: Player Role - (Global, Stored in models.py, Player) Accept Loan - (Global, Stored in models.py, Group, String) Contract Enforcement - (Global, Stored in models.py, Constants, Bool) Output: Bool - (to oTree Page class) """ group = self.group if Constants.contract_enforcement == False and group.accept_loan == 'Yes' and self.player.role() == 'lender': return True else: return False class Seize_Collateral(Page): """ Description: Seize Decision inherits the oTree class Page. On this page the lender decides how much of the of the collateral to seize if Contract Enforcement == True else collateral seizure happens automatically. Input: Page - (oTree Class) Output: Conditional Contract Enforcement == False Form Field - (to HTML Seize_Collateral.HTML) """ if Constants.contract_enforcement == False: form_model = 'group' form_fields = ['recovered_collateralralralral'] def before_next_page(self): # TODO: update doc strings """ Description: before_next_page performs the necessary calculations and modifications of variables before the experiment continues. In this case it returns the amount of collateral not seized to the borrower. Input: Lender - (Global, Location of Lender) Borrower - (Global, Location of Borrower) Lender Money - (Global, Stored in models.py, Player, Currency) Borrower Money - (Global, Stored in models.py, Player, Currency) Loan - (Global, Stored in models.py, Group, Currency) Lender Return - (Global, Stored in models.py, Group, Currency) repayment rate- (Global, Stored in models.py, Group, Float) Actual Return - (Global, Stored in models.py, Group, Float) Satisfied - (Global, Stored in models.py, Group, String) Seized Collateral - (Global, Stored in models.py, Group, Currency) Value of Collateral - (Global, Sotred in models.py, Group, Currency) Output: Conditional Contract Enforcement == True Seized Collateral (Value of Collateral Max=Terms of Loan == Fulfilled) Lender Money - (Money - Loan) Borrower Money - (Money + Loan * Productivity) """ group = self.group lender = group.get_players()[0] borrower = group.get_players()[1] lender_return_plus_collateral = group.lender_return + group.collateral_after_feeee expected_return = Constants.rate_of_return * group.loan '''if contract enforcement isn't true the lender seizes collateral and the amount remaining is returned to the borrower''' if Constants.contract_enforcement == False: lender.money += group.recovered_collateralralralral group.returned_collateral = group.collateral_after_feeee - group.recovered_collateralralralral borrower.money += group.collateral_after_feeee - group.recovered_collateralralralral else: '''otherwise if the amount of collateral plus the lender return is less than the expected return the entire available collateral is seized''' if lender_return_plus_collateral < expected_return: group.recovered_collateralralralral = group.collateral_after_feeee group.returned_collateral = 0 lender.money += group.collateral_after_feeee else: '''if the value of the collateral plus lender return is greater than the expected return the amount necessary to fulfill the terms of the loan are seized and the rest is returned to the borrower''' group.recovered_collateralralralral = 0 while group.recovered_collateralralralral + group.lender_return < expected_return: lender.money += 1 group.recovered_collateralralralral += 1 group.returned_collateral = group.collateral_after_feeee - group.recovered_collateralralralral borrower.money += group.collateral_after_feeee - group.recovered_collateralralralral def vars_for_template(self): """ Description: vars_for_template passes Django variables to the HTML. Input: repayment rate- (Global, Stored in models.py, Group, Integer) Loan - (Global, Stored in models.py, Group, Currency) Output: Expected Return - (Loan * repayment rate) """ group = self.group return {'expected_return': Constants.rate_of_return*group.loan} def is_displayed(self): """ Description: is_displayed tells oTree which player to show the relevant page to and under what conditions. Input: Satisfied - (Global, Stored in models.py, Group, String) Role - (Global, Stored in models.py, Player, String) Output: Bool - (to oTree Page class) """ group = self.group if group.satisfied == 'No' and self.player.role() == 'lender': return True else: return False class Results(Page): """ Description: Results inherits the oTree class Page. On this page both the lender and borrower are showed the results of the experiment Input: Page - (oTree Class) Output: None """ def before_next_page(self): """ Description: before_next_page performs the necessary calculations and modifications of variables before the experiment continues. In this case it assigns the final payoffs. Input: Player - (Global, Stored in models.py, Group) Participant - (oTree Specific Represents Actual Person) Money - (Global, Stored in models.py, Player, Currency) Output: Participant Payoff - (Money) Player Money - (Money) """ """adds the players money to their final payoff: this is what we pay them""" self.player.payoff += self.player.money def vars_for_template(self): """ Description: vars_for_template passes Django variables to the HTML. Input: repayment rate- (Global, Stored in models.py, Group, Integer) Loan - (Global, Stored in models.py, Group, Currency) Output: Expected Return - (Loan * repayment rate) """ group = self.group return {'expected_return': Constants.rate_of_return*group.loan} class WaitForPlayer(WaitPage): """ Description: WaitPage confirms that all of the players have arrived before the it continues. The WaitPage has a secondary function of writing to the log after every page has been completed. Input: WaitPage - (oTree Class) Output: None """ def after_all_players_arrive(self): # TODO: update docstrings """ Description: After all players arrive performs calculations and changes variables before players proceed to the next page. Input: None Output: None """ players = self.group.get_players() self.group.lender_money = players[0].money self.group.borrower_money = players[1].money def logger_var(self, new_vars): """ Description: logger_var takes a list of variables to log and adds them to a predefined dict which it passes to a logger. Input: new_vars - (local, dict) variables_for_logging - (local, dict) Output: to logger (new_vars + variables_for_logging) """ """since these variables are always the same they are 'hard' coded""" variables_for_logging = {'debug': debug, 'role': 'group', 'group_id': Constants.group_id} '''this for loop adds the new variables to the original dict and sends them to the logger. if that key already exists it is overwritten ie for name which changes every time''' for var in new_vars: for key in var.keys(): variables_for_logging.update({key: var[key]}) (logger(variables_for_logging)) group_vars = [ {'name': Group.collateral.field_name, 'var': self.group.collateral}, {'name': Group.rate_of_return.field_name, 'var': self.group.rate_of_return}, {'name': Group.loan.field_name, 'var': self.group.loan}, {'name': Group.lender_return.field_name, 'var': self.group.lender_return}, {'name': Group.actual_return.field_name, 'var': self.group.actual_return}, {'name': Group.accept_loan.field_name, 'var': self.group.accept_loan}, {'name': Group.collateral_after_feeee.field_name, 'var': self.groucollateral_after_feee}, {'name': Group.satisfied.field_name, 'var': self.group.satisfied}, {'name': Group.recovered_collateralralralral.field_name, 'var': serecovered_collateralateralateralateral} ] logger_var(self, group_vars) for player in players: player_vars = [{'name': Player.money.field_name + player.role(), 'var': player.money, 'role': player.role()}] logger_var(self, player_vars) class QuizWaitPage(WaitPage): pass class GroupWaitPage(WaitPage): group_by_arrival_time = True def get_players_for_group(self, waiting_players): print('in get_players_for_group') lender_players = [p for p in waiting_players if p.participant.vars['role'] == 'lender'] borrower_players = [p for p in waiting_players if p.participant.vars['role'] == 'borrower'] if len(a_players) >= 1 and len(b_players) >= 1: print('about to create a group') return [lender_players[0], borrower_players[1]] print('not enough players to create a group') # FULL PAGE SEQUENCE page_sequence = [ WelcomePage, TaskOverview, RoleAssignment, LoanAgreement, PracCollateralOffer, PracLoanOffer, PracLoanPackageDecision, LoanFulfillment, PracReturnRealization, LoanRemediation, PracCollateralDecision, PracCollateralSeizure, PracResults, TaskRepayment, PracCollateralOffer, PracLoanOffer, PracLoanPackageDecision, PracReturnRealization, PracCollateralDecision, PracCollateralSeizure, PracResults, QuizWaitPage, QuestionOne, QuizTimeout, QuestionTwo, QuizTimeout, QuestionThree, QuizTimeout, QuestionFour, QuizTimeout, QuestionFive, QuizTimeout, QuestionSix, QuizTimeout, QuizResults, QuizResults, QuizWaitPage, GroupWaitPage, Collateral_Offer, WaitForPlayer, Loan_Amount, WaitForPlayer, Loan_Package, WaitForPlayer, Realize_Return, WaitForPlayer, Collateral_Decision, WaitForPlayer, Seize_Collateral, WaitForPlayer, Results ] """ ''' WelcomePage, TaskOverview, RoleAssignment, LoanAgreement, PracCollateralOffer, PracLoanOffer, PracLoanPackageDecision, LoanFulfillment, PracReturnRealization, LoanRemediation, PracCollateralDecision, PracCollateralSeizure, PracResults, TaskRepayment, PracCollateralOffer, PracLoanOffer, PracLoanPackageDecision, PracReturnRealization, PracCollateralDecision, PracCollateralSeizure, PracResults, QuizWaitPage, QuestionOne, QuizTimeout, QuestionTwo, QuizTimeout, QuestionThree, QuizTimeout, QuestionFour, QuizTimeout, QuestionFive, QuizTimeout, QuestionSix, QuizTimeout, QuizResults, QuizResults, QuizWaitPage, WaitForPlayer, ''' # GAME PAGE SEQUENCE page_sequence = [ WelcomePage, TaskOverview, RoleAssignment, LoanAgreement, LoanFulfillment, LoanRemediation, ScoreSheetOverview, TaskRepayment, QuizWaitPage, QuestionOne, QuizTimeout, QuestionTwo, QuizTimeout, QuestionThree, QuizTimeout, QuestionFour, QuizTimeout, QuestionFive, QuizTimeout, QuestionSix, QuizTimeout, QuizResults, QuizWaitPage, WaitForPlayer, Collateral_Offer, WaitForPlayer, Loan_Amount, WaitForPlayer, Loan_Package, WaitForPlayer, Realize_Return, WaitForPlayer, Collateral_Decision, WaitForPlayer, Seize_Collateral, WaitForPlayer, Results ] """ # QUIZ PAGE SEQUENCE page_sequence = [ QuestionOne, QuizTimeout, QuestionTwo, QuizTimeout, QuestionThree, QuizTimeout, QuestionFour, QuizTimeout, QuestionFive, QuizTimeout, QuestionSix, QuizTimeout, QuizResults, Results, ] """ # removed from game """ class Determine_Rate_of_Return(Page): Description: Determine_Rate_of_Return inherits the oTree class Page. The purpose of this page is to ask the lender for their desired repayment ratefrom a loan. Input: Page - (oTree Class) Output: Form Field - (to HTML Determine_Rate_of_Return.HTML) form_model = 'group' form_fields = ['rate_of_return'] def is_displayed(self): Description: is_displayed tells oTree which player to show the relevant page to and under what conditions. Input: Player Role - (Global, Stored in models.py, Player) Output: Bool - (to oTree Page class) if self.player.role() == 'lender': return True else: return False """