first commit

This commit is contained in:
duartegoncalvesds 2026-03-12 21:05:38 +00:00
commit 73fc2121bf
30 changed files with 13308 additions and 0 deletions

14
.gitignore vendored Normal file
View file

@ -0,0 +1,14 @@
venv
staticfiles
./db.sqlite3
.idea
*~
*.sqlite3
_static_root
_bots*s
__temp*
__pycache__/
*.py[cod]
.DS_Store
*.otreezip
/Sample Code/

42
Finish/Comments.html Normal file
View file

@ -0,0 +1,42 @@
{{ extends "_static/global/Page.html" }}
{{ block title }}
Comments
{{ endblock }}
{{ block styles}}
{{ endblock }}
{{ block scripts}}
{{ endblock }}
{{ block content }}
<p>
We would like to know more about your thought process when doing the task.
Namely,
<br>
- How did you decide which option to choose?
<br>
- What was important in guessing how your opponent chose?
<br>
- How sure were you that you were choosing well?
<br>
<br>
Any feedback you have will be useful for us.
</p>
{{ formfields }}
<p>
Click 'Next' to complete the task. You must do this to receive your payment.
<br>
<br>
Thank you for participating.
</p>
{{ next_button }}
{{ endblock }}

31
Finish/Demographics.html Normal file
View file

@ -0,0 +1,31 @@
{{ extends "_static/global/Page.html" }}
{{ block title }}
Questionnaire
{{ endblock }}
{{ block styles}}
{{ endblock }}
{{ block scripts}}
{{ endblock }}
{{ block content }}
<p>
Before we conclude, we would like to ask you a few questions about yourself.
</p>
{{ formfields }}
<p>
You must answer each question before you can continue.
</p>
{{ next_button }}
{{ endblock }}

View file

@ -0,0 +1,34 @@
{{ extends "_static/global/Page.html" }}
{{ load otree static }}
{{ block title }}
End of experiment
{{ endblock }}
{{ block styles }}
{{ endblock }}
{{ block scripts }}
{{ endblock }}
{{ block content }}
<p>
Thank you for participating.
Click 'Next' to complete the experiment.
</p>
<p>
You need to complete the experiment to receive your payment.
</p>
{{ next_button }}
{{ endblock }}

View file

@ -0,0 +1,22 @@
{{ extends "_static/global/Page.html" }}
{{ load otree static }}
{{ block title }}
Results
{{ endblock }}
{{block scripts}}
<script>
let completionlink = js_vars.completionlink;
window.onload = function(){
window.location.href=completionlink;
}
</script>
{{ endblock }}
{{ block content }}
{{ endblock }}

View file

@ -0,0 +1,21 @@
{{ extends "_static/global/Page.html" }}
{{ load otree static }}
{{ block title }}
Results
{{ endblock }}
{{block scripts}}
<script>
let elfecompletionlink = js_vars.elfecompletionlink;
window.onload = function(){
window.location.href=elfecompletionlink;
}
</script>
{{ endblock }}
{{ block content }}
{{ endblock }}

139
Finish/__init__.py Normal file
View file

@ -0,0 +1,139 @@
from otree.api import *
import numpy as np
import random
import operator
doc = """
App to characterise complexity of decision-making, with sums with varying numbers of elements in each sum.
"""
class C(BaseConstants):
NAME_IN_URL = 'end'
PLAYERS_PER_GROUP = None
NUM_ROUNDS = 1
class Subsession(BaseSubsession):
pass
class Group(BaseGroup):
pass
class Player(BasePlayer):
participation_fee= models.CurrencyField()
# Standard survey questions
Age = models.IntegerField(min=18, label="Please enter your age:")
EducationLevel = models.StringField(
label="What is the highest level of education that you <span style='font-weight:bold'>completed</span>?",
choices=[
"Primary Education or Less",
"Secondary Education/High School",
"Business, technical, or vocational school after Secondary Education/High School",
"Some college or university qualification, but not a Bachelor",
"Bachelor or equivalent",
"Master or Post-graduate training or professional schooling after college (e.g., law or medical school)",
"Ph.D. or equivalent"
],
widget = widgets.RadioSelect
)
EducationField = models.StringField(
label="Choose the field that best describes your <span style='font-weight:bold'>primary field of education</span>.",
choices=[
"Generic",
"Arts and Humanities",
"Social Sciences and Journalism",
"Economics",
"Education",
"Business, Administration, and Law",
"Computer Science, Information, and Communication Technologies",
"Natural Sciences",
"Mathematics and Statistics",
"Engineering, Manufacturing, and Construction",
"Agriculture, Forestry, Fisheries, and Veterinary",
"Health and Welfare",
"Services (Transport, Hygiene and Health, Security, and Other)"
],
widget = widgets.RadioSelect
)
Comments = models.LongStringField(
label="If you have any comments, please leave them below.",
blank=True
)
# PAGES
class Demographics(Page):
"""Socio-demographic questions"""
form_model = 'player'
form_fields = ['Age', 'EducationLevel', 'EducationField']
@staticmethod
def is_displayed(player):
return player.round_number == C.NUM_ROUNDS
class Comments(Page):
"""Comments"""
form_model = 'player'
form_fields = ['Comments']
@staticmethod
def is_displayed(player):
return player.round_number == C.NUM_ROUNDS
class EndOfExperiment(Page):
@staticmethod
def vars_for_template(player):
return dict(
context=player.session.config['context']
)
class RedirectProlific(Page):
@staticmethod
def is_displayed(player):
return (
player.round_number == C.NUM_ROUNDS and
player.session.config['context'] == "prolific" and
not player.participant.vars.get('failed_captcha', False)
)
@staticmethod
def js_vars(player):
return dict(
completionlink=
player.subsession.session.config['completionlink']
)
class RedirectQualtrics(Page):
@staticmethod
def is_displayed(player):
return (
player.round_number == C.NUM_ROUNDS and
player.session.config['context'] == "lab" and
not player.participant.vars.get('failed_captcha', False)
)
@staticmethod
def js_vars(player):
return dict(
elfecompletionlink=
player.session.config['elfecompletionlink']+"participantid="+player.participant.code+"&sessionid="+player.session.config['elfesessionid']
)
page_sequence = [
Demographics,
Comments,
EndOfExperiment,
RedirectProlific,
RedirectQualtrics
]

10
Finish/tests.py Normal file
View file

@ -0,0 +1,10 @@
from otree.api import Currency as c, currency_range, expect, Bot, SubmissionMustFail
from . import *
import random
class PlayerBot(Bot):
def play_round(self):
if self.round_number == 1:
yield Submission(EndOfExperiment, check_html = False)

2
Procfile Normal file
View file

@ -0,0 +1,2 @@
web: otree prodserver1of2
worker: otree prodserver2of2

185
Start/Welcome.html Normal file
View file

@ -0,0 +1,185 @@
{{ extends "_static/global/Page.html" }}
{{ load otree static }}
{{ block title }}
Welcome!
{{ endblock }}
{{ block content }}
<style>
html {
scroll-behavior: smooth;
}
.popup {
display: none;
position: fixed;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.4);
}
.popup-content {
background-color: #fefefe;
margin: 5% auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
height: 80%;
overflow: auto;
}
.close {
color: #aaaaaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close:hover,
.close:focus {
color: #000;
text-decoration: none;
cursor: pointer;
}
iframe {
border: none;
}
.information_sheet_button {
color: blue;
text-decoration: underline;
cursor: pointer;
}
.information_sheet_button:hover {
color: darkblue;
}
.information_sheet_button:active {
color: darkblue;
}
.information_sheet_button:focus {
color: darkblue;
}
</style>
<div>
<p>Thank you for participating in this study.</p>
<p>
In this study, we will ask you to make choices.
You will get {{session.config.currency_symbol}}{{participation_fee|to2}} when completing the study.
<br>
You can earn an additional bonus payment of up to {{session.config.currency_symbol}}{{max_payment|to2}}, depending on your choices.
</p>
<p>
This study is conducted under a strict policy of no deception. You will never be lied to during this study.
The instructions will detail how the study works and how you are paid.
</p>
{{ if use_captchas == 1 }}
<h3>
'Bot'-Detection
</h3>
<p>
This study is designed for humans and cannot be fulfilled using automated answers. You will be asked to transcribe easily legible words in this study. If you fail to transcribe a word three times, the study will be immediately terminated and you will not be able to complete it nor receive payment.
</p>
</p>
{{ endif }}
</div>
<div class="form-group required">
<h2>Informed Consent</h2>
<p>
Your participation in this study is voluntary. You can withdraw consent or discontinue participation at any time.
All data and published work resulting from this study will be anonymised and maintain your individual privacy.
</p>
<p>
Please read this <span class="information_sheet_button">information sheet</span> to decide if you want to participate in this study.
You need to open this document to proceed. Should you have any questions or concerns, please contact the researchers or the UCL Research Ethics Office.
</p>
Participant's statement: I confirm that:
<ol type="a">
<li>I have read and understood the <span class="information_sheet_button">information sheet</span>; </li>
<li>I understand that I can refuse or withdraw from this study at any time without affecting my rights or status; </li>
<li>I am 18 or older and I freely consent to participate in this study; </li>
<li>I may ask the researchers for a copy of these documents for my records.</li>
</ol>
<p>
Do you agree with the statement above and consent to participate in the study?<br>
<label>
<input type="radio" name="consent" value="Yes"> Yes, I agree and wish to participate in the study.
</label><br>
<label>
<input type="radio" name="consent" value="No"> No, I do not agree and do not wish to participate in the study.
</label><br>
<span id="consent_error" style="color: #b94a48; visibility: hidden">Please indicate whether you agree to participate in the study.</span>
</p>
</div>
<div class="otree-form-errors alert alert-danger" id="no_consent_error" style="display: none">
You have chosen not to participate in the study. Please close this page.
</div>
<div>
<p>
Next, we will provide instructions explaining how the study works and how you will be paid.
As a reminder, you will never be lied to during this or any study at the UCL Experimental Laboratory for Finance and Economics (UCL ELFE).
</p>
<p>When you are ready, please click "Next" to go on. </p>
</div>
<div style="justify-content: center; align-items: center; text-align: center;">
<button class="otree-btn-next btn btn-primary next-button otree-next-button" type="button" id="next_button" style="margin:auto;" onclick="checkConsent()">Next</button>
</div>
<div class="popup" id="popup">
<div class="popup-content">
<span class="close">&times;</span>
<iframe src="https://uclelfe.github.io/documents/UCL%20ELFE.%20Information%20Sheet.pdf#toolbar=0" width="100%" height="100%"></iframe>
</div>
</div>
<script>
let popup = document.getElementById("popup");
let information_sheet_button_1 = document.getElementsByClassName("information_sheet_button")[0];
let information_sheet_button_2 = document.getElementsByClassName("information_sheet_button")[1];
let close = document.getElementsByClassName("close")[0];
information_sheet_button_1.onclick = function() {
popup.style.display = "block";
}
information_sheet_button_2.onclick = function() {
popup.style.display = "block";
}
close.onclick = function() {
popup.style.display = "none";
}
window.onclick = function(event) {
if (event.target === popup) {
popup.style.display = "none";
}
}
function checkConsent() {
let consent = document.querySelectorAll('input[name="consent"]');
let consent_error = document.getElementById("consent_error");
let no_consent_error = document.getElementById("no_consent_error");
if (consent[0].checked === false && consent[1].checked === false) {
consent_error.style.visibility = "visible";
consent_error.scrollIntoView();
} else if (consent[1].checked === true) {
no_consent_error.style.display = "block";
no_consent_error.scrollIntoView();
} else {
document.getElementById("form").submit();
}
}
</script>
{{ endblock }}

50
Start/__init__.py Normal file
View file

@ -0,0 +1,50 @@
from otree import session
from otree.api import *
doc = """
Landing page with consent.
"""
class C(BaseConstants):
NAME_IN_URL = 'Start'
PLAYERS_PER_GROUP = None
NUM_ROUNDS = 1
class Subsession(BaseSubsession):
pass
class Group(BaseGroup):
pass
class Player(BasePlayer):
consent = models.BooleanField(blank=True)
exp = models.IntegerField()
# FUNCTIONS
# PAGES
class Welcome(Page):
form_model = 'player'
form_fields = ['consent']
@staticmethod
def vars_for_template(player):
return dict(
participation_fee=float(player.session.config['participation_fee']),
max_payment=float(player.session.config['max_payment']),
use_captchas=player.session.config['use_captchas'],
estimated_completion_time=player.session.config['estimated_completion_time'],
# usinstructions = player.session.config['usinstructions'],
)
@staticmethod
def before_next_page(player: Player, timeout_happened):
player.exp = 1
player.participant.vars['exp'] = 1
page_sequence = [Welcome]

88
Task1/Captcha.html Normal file
View file

@ -0,0 +1,88 @@
{{ extends "_static/global/Page.html" }}
{{ load otree static }}
{{ block title }}
Bot Detection
{{ endblock }}
{{ block styles}}
<style>
#timer_display {
font-size: 2.2rem;
font-weight: 600;
color: #333;
position: fixed;
bottom: 1rem;
right: 1rem;
}
</style>
{{ endblock }}
{{ block scripts}}
<script src="{{ static 'js/vue.js' }}"></script>
<script src="{{ static 'js/lodash.min.js' }}"></script>
<script>
var vm = new Vue({
el: '#main_container',
delimiters: ['${', '}'],
data: {
attempt: {{ attempt }},
time_now: null,
start_time: null,
end_time: null,
time_limit: {{ max_captcha_time }},
time_until_end: {{ max_captcha_time }},
},
computed: {
time_until_end_display: function () {
s = _.clone(this.time_until_end)
if (s>=60) {return (s-(s%=60))/60+(9<s?':':':0')+s}
else if (s>=0) {return s}
else {return '0'}
}
},
mounted: function () {
this.start_time = _.now()/1000
this.end_time = this.start_time + this.time_limit
window.setInterval(() => {
this.time_now = _.now()/1000;
this.time_until_end = (this.end_time - this.time_now).toFixed(0);
if (this.time_until_end <= 0) {
if (document.querySelectorAll("input[type=text]")[0].value == "") {
document.querySelectorAll("input[type=text]")[0].value = "NULL TIMEOUT"
}
document.getElementById("submit_button").click();
}
}
, 1000)
},
})
</script>
{{ endblock }}
{{ block content }}
<div id="main_container" v-cloak>
<br>
<div id="captcha">
<h3>Bot Detection - Attempt ${attempt}</h3>
<p>
Type the following word or phrase into the box below, then press 'Next'. Answers are not case-sensitive.
<br>
You have three attempts. If you fail all three attempts, the task will end and you will not be paid.
<br>
You have one minute per attempt.
</p>
<div id="captcha_image">
<img src="{{ static image_path }}" style='width:300px;'/>
</div>
<br>
{{ formfields }}
<button class="btn btn-primary" id="submit_button">Next</button>
</div>
<div id="timer_display"><span>${time_until_end_display}</span></div>
</div>
{{ endblock }}

19
Task1/CaptchaFailed.html Normal file
View file

@ -0,0 +1,19 @@
{{ extends "_static/global/Page.html" }}
{{ load otree static }}
{{ block title }}
Bot Detection Failed
{{ endblock }}
{{ block styles}}
{{ endblock }}
{{ block scripts}}
{{ endblock }}
{{ block content }}
<p>
You failed the transcription task three times. <br>
You will not be able to continue the experiment.
</p>
{{ endblock }}

195
Task1/Instructions.html Normal file
View file

@ -0,0 +1,195 @@
{{ extends "_static/global/Page.html" }}
{{ load otree static }}
{{ block styles}}
{{ endblock }}
{{ block title }}
Instructions
{{ endblock }}
{{ block content }}
<input name="time_spent_instructions" type="hidden" id="time_spent_instructions">
{{ include "Task1/InstructionsBase.html" }}
<div id="main_container" v-cloak>
<div id="questions_container">
<h3>Questions</h3>
<p>
You must answer the following questions correctly before you can proceed.
</p>
<ol>
<question v-for="(question, question_index) in questions" :question="question" :question_index="question_index" v-on:update_response="question.r=$event">
</question>
</ol>
<div id="check_answers">
<button type="button" class="btn btn-primary" @click="check_answers">Check Answers</button>
<br>
<p v-if="incorrect_answer_numbers != null && incorrect_answer_numbers.length > 0">
<!-- The following answers are incorrect: <span v-for="q in incorrect_answer_numbers.slice(0, -1)">${q}, </span><span>${incorrect_answer_numbers[incorrect_answer_numbers.length-1]}.</span>
<br> -->
Some of the questions are unanswered or incorrect.
You must answer all questions correctly before you can proceed.
<br>
Update your answers and try again.
</p>
<p v-if="incorrect_answer_numbers != null && incorrect_answer_numbers.length == 0">
All answers are correct! Click 'Next' to proceed.
</p>
<button type="button" @click="instructions_time" v-if="incorrect_answer_numbers != null && incorrect_answer_numbers.length == 0" class="btn btn-primary">Next</button>
</div>
</div>
<input type="hidden" v-model="incorrect_answers_string" name="incorrect_instruction_answers">
<input type="hidden" v-model="errors_quiz_count" name="errors_quiz">
<input type="hidden" id="time_spent_instructions" name="time_spent_instructions">
</div>
{{ endblock }}
{{ block scripts }}
<script>
let startTime,submitTime
window.addEventListener('load', function() {
startTime=Date.now()
})
</script>
<!-- <script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-MML-AM_CHTML' async></script> -->
<!--Loads vue.js-->
<script src="{{ static 'js/vue.js' }}"></script>
<!--Defines what a question looks like-->
<script type="text/x-template" id="question-template">
<li>
<br>
<label class="question_text"><span v-html="question.q"></span></label><br>
<template v-for="answer in question.a">
<input type="radio" :value="answer" v-model="response" @change="$emit('update_response', $event.target.value)">
<label v-html="answer" class="question_text" :for="answer"></label>
<br>
</template>
<br>
</li>
</script>
<script>
Vue.component('question', {
props: ['question','question_index'],
template: '#question-template',
name: 'question',
delimiters: ['${', '}'],
data: function () {
return {
response: null
}
},
methods: {
update_response: function() {
console.log(this.question_index,this.response)
},
}
});
var vm = new Vue({
el: '#main_container',
delimiters: ['${', '}'],
data: {
incorrect_answers: [], errors_quiz: 0,
questions: [
{
'q': 'True or False? You and your opponent always have the same baseline points.',
'a': [
'True.',
'False.',
],
'c': 'False.',
'r': null
},
{
'q': 'True or False? The asset is worth the same for you and your opponent.',
'a': [
'True.',
'False.',
],
'c': 'True.',
'r': null
},
{
'q': 'True or False? If you choose Option A (the asset), you always get the points associated with the asset.',
'a': [
'True.',
'False.'
],
'c': 'False.',
'r': null
},
{
'q': 'True or False? If you choose Option B (baseline points), you get the points only if your opponent also chooses Option B.',
'a': [
'True.',
'False.'
],
'c': 'False.',
'r': null
},
{
'q': 'True or False? If you choose Option B (baseline points), you get the points only if your opponent also chooses Option B.',
'a': [
'True.',
'False.'
],
'c': 'False.',
'r': null
},
{
'q': 'Choose the correct answer if you want to maximise the probability of winning the bonus payment of {{ session.config.currency_symbol }}{{ session.config.bonus | to2 }}.',
'a': [
'Your guess does not matter for earning the bonus.',
'It is always better to make the best guess possible.'
],
'c': 'It is always better to make the best guess possible.',
'r': null
}
]
},
computed: {
incorrect_answer_numbers: function() {
ia = this.incorrect_answers
if (ia.length === 0) {return null}
else {return ia[ia.length-1].map((a) => a[0])}
},
incorrect_answers_string: function() {
return JSON.stringify(this.incorrect_answers)
},
errors_quiz_count: function(){
return this.errors_quiz-1
},
questions: function() {
questions = this.common_questions
}
},
methods: {
check_answers: function() {
incorrect_answers = [];
this.questions.forEach((q,i) => {if (q.c !== q.r) {incorrect_answers.push([i+1,q.r])}})
this.incorrect_answers.push(incorrect_answers)
this.errors_quiz = this.errors_quiz + 1
},
instructions_time: function(){
submitTime = Date.now()
if (document.getElementById("time_spent_instructions").value) {
document.getElementById("time_spent_instructions").value
}
else {
document.getElementById("time_spent_instructions").value = submitTime - startTime;
}
document.getElementById("form").submit();
}
}
});
</script>
{{ endblock }}

View file

@ -0,0 +1,94 @@
<h3>
Your Task
</h3>
<p>
This task has {{ C.NUM_PAYMENT_ROUNDS }} rounds.
<br>
Every round, you and other participants (1) make a choice and (2) guess the choices of other participants.
Your choice and guess will determine how likely you are to get a bonus of {{ player.session.config.currency_symbol }}{{ player.session.config.bonus | to2 }}.
<br>
At the end of the task, you will be paid the participation fee of {{ player.session.config.currency_symbol }}{{ player.session.config.participation_payment | to2 }} and one round will be chosen randomly to determine your chance of getting the bonus.
</p>
<p>
In each round, you will be randomly matched with another participant (your opponent). Both you and your opponent must choose between an asset (Option A) and the assigned baseline points (Option B).
</p>
<ul>
<li>
<strong>Option A:</strong> You receive the asset's value if your opponent chooses Option B. If both you and your opponent choose Option A, both of you receive 0 points.
</li>
<li>
<strong>Option B:</strong> You receive your assigned baseline points, regardless of what your opponent chooses.
</li>
</ul>
<h3>Key Details</h3>
<ul>
<li>
<strong>Asset's Value:</strong> The asset is worth the same amount of points for both you and your opponent.
</li>
<li>
<strong>Baseline Points:</strong> At the start of each round, every participant is assigned a random number of baseline points between 0 and 100. All values are equally likely and have been randomised for each participant.<br>
<strong>You will not know your opponent's baseline points and your opponent will not know your baseline points.</strong>
</li>
<li>
<strong>Same Incentives for Everyone:</strong> Everyone else in the task follows these same rules and has the same incentives as you.
</li>
<li>
<strong>Independence:</strong> The asset value, your baseline points, and your opponent's baseline points vary each round. Your choices across rounds are independent and do not affect one another.
</li>
</ul>
<h3>Example</h3>
<p>
Suppose the asset (Option A) is worth 50 points and your baseline points (Option B) are 38.4.
</p>
<ul>
<li>
If you choose Option B, you are guaranteed to receive your baseline points of 38.4.
</li>
<li>
If you choose Option A and your opponent chooses Option B, you receive the asset value of 50.
</li>
<li>
If both you and your opponent choose Option A, both of you receive 0 points.
</li>
</ul>
<h3>Guesses</h3>
<p>
In addition, we will ask you what you believe other participants will do.
The more accurate your guess is, the more likely you are to get the bonus.
</p>
<h3>Bonus Payment Rule</h3>
<p>
Your choice and guess will determine how likely you are to get the bonus.
The payment rule is designed so that
(1) you choose to maximise what your points given how likely you think others are to invest or not;
(2) you tell us what you believe the probability is that your opponent chooses each action.
<br>
To ensure this, your payment from guesses is determined as follows:
<ul>
<li>At the end of the study, one round will be randomly selected, and either your choice or your guess is chosen to determine payment, both equally likely.</li>
<li>If your choice is selected for payment, then the probability you get the bonus equals the number of points you got from your choice, as described above.</li>
<li>If your guess is selected for payment, we draw two random choices according to your guess.
You get 50 points for each choice that matches the choice of your opponent.
In addition, you get 50 points if the two drawn choices are different.
The probability you get the bonus equals the resulting total number of points.</li>
</ul>
</p>
<h3>Practice Rounds</h3>
<p>
In order to get familiarised with the task, you will begin with {{ C.NUM_PRACTICE_ROUNDS }} practice rounds.
The practice rounds do not count toward your chance of getting the bonus.
<br>
There is also a brief questionnaire at the end.
</p>
<!-- Treatments:
Baseline
N samples with replacement, forced and free
Sample as many times as they want
-->

19
Task1/NewRound.html Normal file
View file

@ -0,0 +1,19 @@
{{ extends "_static/global/Page.html" }}
{{ load otree static }}
{{ block title }}
Main Task
{{ endblock }}
{{ block content }}
<div>
<p>
You will now make choices {{ C.NUM_PAYMENT_ROUNDS }} rounds.<br>
One of these rounds will be randomly selected to <b>determine your bonus payment</b> at the end of the task. <br>
You will not receive feedback.
</p>
</div>
<div style="justify-content: center; align-items: center; text-align: center;">
{{ next_button }}
</div>
{{ endblock }}

185
Task1/Task.html Normal file
View file

@ -0,0 +1,185 @@
{{ extends "_static/global/Page.html" }}
{{ load otree static }}
{{ block title }}
{{ if player.practice == 1 }}
Practice Round {{ practice_round_number }} out of {{ C.NUM_PRACTICE_ROUNDS }}
{{ endif }}
{{ if player.practice == 0 }}
Round {{ payment_round_number }} out of {{ C.NUM_PAYMENT_ROUNDS }}
{{ endif }}
{{ endblock }}
{{ block scripts }}
<script>
// (A) PREVENT CONTEXT MENU FROM OPENING
window.addEventListener("contextmenu", evt => evt.preventDefault(), false);
// (B) PREVENT CLIPBOARD COPYING
window.addEventListener("copy", evt => {
evt.clipboardData.setData("text/plain", "Copying is not allowed on this page");
evt.preventDefault();
}, false);
window.addEventListener("load", (event) => {
let startTime,submitTime
startTime=Date.now()
document.getElementById("instructions_clicked").value = 0
const div = document.querySelectorAll('div')
div.forEach(function(x){
if (x.classList.contains('otree-form-errors')){
x.innerHTML="Please make your choice and guess."
}
})
// SLIDER
const slider = document.getElementById('belief_slider');
const leftSpan = document.getElementById('left-value');
const rightSpan = document.getElementById('right-value');
const hidden = document.getElementById('id_belief_1');
const sliderContainer = document.getElementById('slider-container');
if (slider) {
slider.addEventListener('input', function() {
// Show the thumb on first move
sliderContainer.classList.remove('empty-slider');
sliderContainer.classList.add('interacted');
// Update text and hidden input
let val = parseFloat(this.value);
leftSpan.innerText = val.toFixed(1);
rightSpan.innerText = (100 - val).toFixed(1);
hidden.value = val.toFixed(1);
});
}
document.getElementById("ok_button").addEventListener('click', function () {
submitTime = Date.now()
if (document.getElementById("time_spent").value) {
document.getElementById("time_spent").value
} else {
document.getElementById("time_spent").value = submitTime - startTime;
}
document.getElementById("form").submit();
})
});
function show_Instructions() {
document.getElementById("instructions_clicked").value = 1
let elem1 = document.getElementById("instructions_button"), text1 = elem1.innerText;
if (text1 === "Show Instructions"){
elem1.innerHTML = "Hide Instructions";}
else{
elem1.innerHTML = "Show Instructions";
}
let x1 = document.getElementById("Instructions");
if (x1.style.display === "none") {
x1.style.display = "block";
}
else {
x1.style.display = "none";
}
};
// Delay choices
setTimeout(function () {
document.getElementById("interface").style.visibility = "visible";
document.getElementById("ok_button").style.visibility = "visible";
document.getElementById("instructions_button").style.visibility = "visible";
}, 2000);
</script>
{{ endblock }}
{{ block styles }}
<style>
/* Slider No-Value Start */
.empty-slider input[type=range]::-webkit-slider-thumb { visibility: hidden; }
.empty-slider input[type=range]::-moz-range-thumb { visibility: hidden; }
.interacted input[type=range]::-webkit-slider-thumb { visibility: visible; }
.interacted input[type=range]::-moz-range-thumb { visibility: visible; }
.form-range { width: 100%; cursor: pointer; }
</style>
{{ endblock }}
{{ block content }}
<input name="instructions_clicked" type="hidden" id="instructions_clicked">
<input name="time_spent" type="hidden" id="time_spent">
<br>
<div class="container">
<div class="center">
<p>
Asset's worth: {{ player.value | to0 }} points.
<br>
Your baseline points: {{ player.cost | to1 }}.
<br>
Opponent's baseline points: a random number between 0 and 100, all equally likely.
<br>
You will not know your opponent's baseline points and your opponent will not know your baseline points.
</p>
</div>
</div>
<br><br><br>
<div id="interface" style="visibility: hidden;">
<p>Which option do you choose?</p>
<table id="alternatives" class="center">
<tr>
<td style="width:10%">
<input class="form-check-input" type="radio" id="id_choice-0" name="decision" required="" value="0">
</td>
<td>Option A: Asset's worth if opponent chooses Option B.</td>
</tr>
<tr>
<td style="width:10%">
<input class="form-check-input" type="radio" id="id_choice-1" name="decision" required="" value="1">
</td>
<td>Option B: Baseline points.</td>
</tr>
</table>
<br><br>
<p>
What do you think is the probability that your opponent chooses each option:
</p>
<div class="center" style="max-width: 600px; margin: 40px auto; margin-top: 0px;">
<!-- <p>Please select a value by clicking on the bar below:</p> -->
<div id="slider-container" class="empty-slider">
<div style="display:flex; align-items:center; justify-content:space-between; gap:12px;">
<div style="width:80px; text-align:left;">
Option A <br>
<span id="left-value">--</span>%
<!-- <br>Asset<br> -->
</div>
<div style="flex:1;">
<input type="range" class="form-range" id="belief_slider" min="0" max="100" step="0.1" value="50">
</div>
<div style="width:80px; text-align:right;">
Option B <br>
<span id="right-value">--</span>%
<!-- <br>Opponent's <br> Baseline Points -->
</div>
</div>
</div>
<input type="hidden" name="belief_1" id="id_belief_1" value="">
</div>
{{ if player.practice == 1 }}
<div class="container"><div class="centering"><button type="button" class="btn btn-primary" style="visibility: hidden;" id="ok_button">Receive feedback</button></div></div>
{{ endif }}
{{ if player.practice == 0 }}
<div class="container"><div class="centering"><button type="button" class="btn btn-primary" style="visibility: hidden;" id="ok_button">Next</button></div></div>
{{ endif }}
<div class="container">
<div class="centering">
<button type="button" style="visibility: hidden;" class="btn btn-secondary" id="instructions_button" onclick="show_Instructions()">Show Instructions</button>
</div>
</div>
<div id="Instructions" style="display: none" class="card bg-light instructions">
<div class="card-body">
{{ include "Task1/InstructionsBase.html"}}
</div>
</div>
</div>
{{ endblock }}

225
Task1/TaskFeedback.html Normal file
View file

@ -0,0 +1,225 @@
{{ extends "_static/global/Page.html" }}
{{ load otree static }}
{{ block title }}
{{ if player.practice == 1 }}
Practice Round {{ practice_round_number }} out of {{ C.NUM_PRACTICE_ROUNDS }}
{{ endif }}
{{ if player.practice == 0 }}
Round {{ payment_round_number }} out of {{ C.NUM_PAYMENT_ROUNDS }}
{{ endif }}
{{ endblock }}
{{ block scripts }}
<script>
// (A) PREVENT CONTEXT MENU FROM OPENING
window.addEventListener("contextmenu", evt => evt.preventDefault(), false);
// (B) PREVENT CLIPBOARD COPYING
window.addEventListener("copy", evt => {
evt.clipboardData.setData("text/plain", "Copying is not allowed on this page");
evt.preventDefault();
}, false);
window.addEventListener("load", (event) => {
let startTime,submitTime
startTime=Date.now()
document.getElementById("instructions_clicked").value = 0
const div = document.querySelectorAll('div')
div.forEach(function(x){
if (x.classList.contains('otree-form-errors')){
x.innerHTML="Please make your choice and guess."
}
})
// SLIDER
const slider = document.getElementById('belief_slider');
const leftSpan = document.getElementById('left-value');
const rightSpan = document.getElementById('right-value');
const hidden = document.getElementById('id_belief_1');
const sliderContainer = document.getElementById('slider-container');
if (slider) {
slider.addEventListener('input', function() {
// Show the thumb on first move
sliderContainer.classList.remove('empty-slider');
sliderContainer.classList.add('interacted');
// Update text and hidden input
let val = parseFloat(this.value);
leftSpan.innerText = val.toFixed(1);
rightSpan.innerText = (100 - val).toFixed(1);
hidden.value = val.toFixed(1);
});
}
document.getElementById("ok_button").addEventListener('click', function () {
submitTime = Date.now()
if (document.getElementById("time_spent").value) {
document.getElementById("time_spent").value
} else {
document.getElementById("time_spent").value = submitTime - startTime;
}
document.getElementById("form").submit();
})
});
function show_Instructions() {
document.getElementById("instructions_clicked").value = 1
let elem1 = document.getElementById("instructions_button"), text1 = elem1.innerText;
if (text1 === "Show Instructions"){
elem1.innerHTML = "Hide Instructions";}
else{
elem1.innerHTML = "Show Instructions";
}
let x1 = document.getElementById("Instructions");
if (x1.style.display === "none") {
x1.style.display = "block";
}
else {
x1.style.display = "none";
}
};
// Delay choices
setTimeout(function () {
document.getElementById("interface").style.visibility = "visible";
document.getElementById("ok_button").style.visibility = "visible";
document.getElementById("instructions_button").style.visibility = "visible";
}, 0);
// Feedback
document.getElementById("id_choice-0").disabled = true;
document.getElementById("id_choice-1").disabled = true;
document.getElementById("belief_slider").disabled = true;
document.getElementById("belief_slider").value = js_vars.belief_1;
document.getElementById("left-value").innerText = parseFloat(js_vars.belief_1).toFixed(1);
document.getElementById("right-value").innerText = (100 - parseFloat(js_vars.belief_1)).toFixed(1);
if (js_vars.decision == 0){
document.getElementById("id_choice-0").checked = true;
}
if (js_vars.decision == 1){
document.getElementById("id_choice-1").checked = true;
}
</script>
{{ endblock }}
{{ block styles }}
<style>
/* Slider No-Value Start */
.empty-slider input[type=range]::-webkit-slider-thumb { visibility: hidden; }
.empty-slider input[type=range]::-moz-range-thumb { visibility: hidden; }
.interacted input[type=range]::-webkit-slider-thumb { visibility: visible; }
.interacted input[type=range]::-moz-range-thumb { visibility: visible; }
.form-range { width: 100%; cursor: pointer; }
</style>
{{ endblock }}
{{ block content }}
<input name="instructions_clicked" type="hidden" id="instructions_clicked">
<input name="time_spent" type="hidden" id="time_spent">
<br>
<div class="container">
<div class="center">
<p>
Asset's worth: {{ player.value | to0 }} points.
<br>
Your baseline points: {{ player.cost | to1 }}.
<br>
Opponent's baseline points: a random number between 0 and 100, all equally likely.
<br>
You will not know your opponent's baseline points and your opponent will not know your baseline points.
</p>
</div>
</div>
<br><br><br>
<div id="interface" style="visibility: hidden;">
<p>Which option do you choose?</p>
<table id="alternatives" class="center">
<tr>
<td style="width:10%">
<input class="form-check-input" type="radio" id="id_choice-0" name="decision" required="" value="0">
</td>
<td>Option A: Asset's worth if opponent chooses Option B.</td>
</tr>
<tr>
<td style="width:10%">
<input class="form-check-input" type="radio" id="id_choice-1" name="decision" required="" value="1">
</td>
<td>Option B: Baseline points.</td>
</tr>
</table>
<br><br>
<p>
What do you think is the probability that your opponent chooses each option:
</p>
<div class="center" style="max-width: 600px; margin: 40px auto; margin-top: 0px;">
<!-- <p>Please select a value by clicking on the bar below:</p> -->
<div id="slider-container" class="interacted">
<div style="display:flex; align-items:center; justify-content:space-between; gap:12px;">
<div style="width:80px; text-align:left;">
Option A <br>
<span id="left-value">--</span>%
<!-- <br>Asset<br> -->
</div>
<div style="flex:1;">
<input type="range" class="form-range" id="belief_slider" min="0" max="100" step="0.1" value="50">
</div>
<div style="width:80px; text-align:right;">
Option B <br>
<span id="right-value">--</span>%
<!-- <br>Opponent's <br> Baseline Points -->
</div>
</div>
</div>
<input type="hidden" name="belief_1" id="id_belief_1" value="">
</div>
<div class="feedback-text">
<p>
In this practice round, you chose Option {{ if player.decision == 0 }}A{{ endif }}{{ if player.decision == 1}}B{{ endif }} and guessed that your opponent chose Option A with {{ belief_A | to1 }}% probability and Option B with {{ belief_B | to1 }}% probability.
</p>
<p>
Recall that only one round is selected for payment and, in that round, only either your choice or your guess counts toward your payment.
</p>
<p>
<ul>
<li>
If your choice in this round were chosen for payment, you would get the {{ player.session.config.currency_symbol }}{{ player.session.config.bonus | to2 }} bonus with {{ if player.decision == 0 }}0% if your opponent chose Option A and {{ player.value | to0 }}% probability if your opponent chose Option B{{ endif }}{{ if player.decision == 1 }}{{ player.cost | to1 }}% probability, regardless of your opponent's choice{{ endif }}.
</li>
<li>
If your guess in this round were chosen for payment, you would get the {{ player.session.config.currency_symbol }}{{ player.session.config.bonus | to2 }} bonus with {{ paymentbelief_A | to1 }}% if your opponent chose Option A and {{ paymentbelief_B | to1 }}% if your opponent chose Option B.
</li>
</ul>
It is always best to submit your best guess to maximise the probability of getting paid the bonus.
</p>
<br>
<p>
Click 'Next' to see your instructions for this study.
</p>
</div>
{{ if player.practice == 1 }}
<div class="container"><div class="centering"><button type="button" class="btn btn-primary" style="visibility: hidden;" id="ok_button">Next</button></div></div>
{{ endif }}
<div class="container">
<div class="centering">
<button type="button" style="visibility: hidden;" class="btn btn-secondary" id="instructions_button" onclick="show_Instructions()">Show Instructions</button>
</div>
</div>
<div id="Instructions" style="display: none" class="card bg-light instructions">
<div class="card-body">
{{ include "Task1/InstructionsBase.html"}}
</div>
</div>
</div>
{{ endblock }}

30
Task1/TaskIntro.html Normal file
View file

@ -0,0 +1,30 @@
{{ extends "_static/global/Page.html" }}
{{ load otree static }}
{{ block title }}
Main Task
{{ endblock }}
{{ block content }}
<div>
{{ if player.practice == 1}}
<p>
You will now have {{C.NUM_PRACTICE_ROUNDS}} practice round{{ if C.NUM_PRACTICE_ROUNDS > 1 }}s{{ endif }} to familiarise yourself with the study.
<br>
You will receive feedback after your choices.
<br>
You will not be paid for {{ if C.NUM_PRACTICE_ROUNDS == 1 }}this round{{ endif }}{{ if C.NUM_PRACTICE_ROUNDS > 1 }}these rounds{{ endif }}.
</p>
{{ endif }}
{{ if player.practice == 0}}
<p>
You will now make choices {{ C.NUM_PAYMENT_ROUNDS }} rounds.<br>
One of these rounds will be randomly selected to <b>determine your bonus payment</b> at the end of the task. <br>
You will not receive feedback.
</p>
{{ endif }}
</div>
<div style="justify-content: center; align-items: center; text-align: center;">
{{ next_button }}
</div>
{{ endblock }}

313
Task1/__init__.py Normal file
View file

@ -0,0 +1,313 @@
from otree.api import *
import random
import math
import json
import numpy as np
import random
import itertools
doc = """Template"""
class C(BaseConstants):
NAME_IN_URL = 'Decision'
PLAYERS_PER_GROUP = None
# Rounds
NUM_PRACTICE_ROUNDS = 2
NUM_PAYMENT_ROUNDS = 3
NUM_ROUNDS = NUM_PRACTICE_ROUNDS + NUM_PAYMENT_ROUNDS
# Captchas
max_captcha_time = 60
captcha_rounds = [1, 2, ] # The rounds in which captchas occur
captcha_text = ["bonus", "payment", ] # The text the participants need to input (can use upper or lowercase)
treatments_between_list = [0, 1]
cost_list = [5, 15, 25, 35, 45, 55, 65, 75, 85, 95]
value_list = [10, 30, 50, 70, 90]
payoffs_list = []
for cost in cost_list:
for value in value_list:
payoffs_list.append([value, cost])
class Subsession(BaseSubsession):
pass
class Group(BaseGroup):
pass
class Player(BasePlayer):
practice = models.IntegerField()
player_round = models.IntegerField()
decision = models.IntegerField()
belief_1 = models.FloatField(min=0, max=100)
# Between-Participant Treatment
treatment_between = models.IntegerField()
# Within-Participant Treatment
treatment_index_within = models.IntegerField()
value = models.FloatField()
cost = models.FloatField()
# Time fields
time_spent = models.IntegerField()
time_spent_instructions = models.IntegerField()
# Captcha
captcha_attempt_1 = models.StringField(label="", initial="")
captcha_attempt_2 = models.StringField(label="", initial="")
captcha_attempt_3 = models.StringField(label="", initial="")
# Instructions
incorrect_instruction_answers = models.StringField(blank=True)
errors_quiz = models.IntegerField()
instructions_clicked = models.BooleanField()
prolific_id = models.StringField(default=str(" "))
# FUNCTIONS
def creating_session(subsession: Subsession):
# Between-participant treatment
# Extend between treatment list * Total participants in session /2 /Nr Between treatments and shuffle
total_number_participants = 10 # max([1,math.floor(len(player.session.get_participants())/2/len(C.treatments_between_list))])
between_treatment_list_shuffled = C.treatments_between_list * total_number_participants
random.shuffle(between_treatment_list_shuffled)
# Within-participant treatment
reps = len(C.payoffs_list) // C.NUM_PAYMENT_ROUNDS
remainder = len(C.payoffs_list) % C.NUM_PAYMENT_ROUNDS
for player in subsession.get_players():
player.prolific_id = player.participant.label
player.player_round = player.round_number
player.participant.vars['failed_captcha'] = False
# Between-participant treatment assignment
player.treatment_between = between_treatment_list_shuffled[len(between_treatment_list_shuffled) % player.participant.id_in_session-1]
if player.round_number == 1:
# Within-participant treatment assignment
treatments_indices_within = list(range(len(C.payoffs_list))) * reps
remaining_treatments = list(range(len(C.payoffs_list)))
random.shuffle(remaining_treatments)
treatments_indices_within = treatments_indices_within + remaining_treatments[:remainder]
random.shuffle(treatments_indices_within)
for r in range(C.NUM_PAYMENT_ROUNDS):
player.in_round(C.NUM_PRACTICE_ROUNDS+1+r).treatment_index_within = treatments_indices_within[r]
if player.player_round <= C.NUM_PRACTICE_ROUNDS:
player.practice = 1
player.value = random.choice(C.value_list)
player.cost = np.round(random.choice(C.cost_list)+10*(random.random()-.5),1)
if player.player_round > C.NUM_PRACTICE_ROUNDS:
player.practice = 0
player.value = C.payoffs_list[player.treatment_index_within][0]
player.cost = np.round(C.payoffs_list[player.treatment_index_within][1]+10*(random.random()-.5),1)
# PAGES
class Instructions(Page):
form_model = 'player'
form_fields = ['incorrect_instruction_answers', 'errors_quiz', 'time_spent_instructions',]
@staticmethod
def is_displayed(player):
return player.round_number == 1
@staticmethod
def vars_for_template(player: Player):
return dict(
participation_fee=player.session.config['participation_fee'],
min_bonus_payment=player.session.config['min_bonus_payment'],
max_bonus_payment=player.session.config['max_bonus_payment'],
min_payment=player.session.config['min_payment'],
max_payment=player.session.config['max_payment'],
)
@staticmethod
def before_next_page(player, timeout_happened):
player.incorrect_instruction_answers = player.field_maybe_none('incorrect_instruction_answers')
class TaskIntro(Page):
@staticmethod
def is_displayed(player):
return (
player.round_number in [1,C.NUM_PRACTICE_ROUNDS+1] and
not player.participant.vars.get('failed_captcha', False)
)
class Task(Page):
form_model = 'player'
form_fields = ['decision','belief_1','time_spent',]
@staticmethod
def is_displayed(player):
return (
not player.participant.vars.get('failed_captcha', False)
)
@staticmethod
def vars_for_template(player):
return dict(
payment_round_number = player.round_number-C.NUM_PRACTICE_ROUNDS,
practice_round_number = player.round_number,
round_number = player.round_number,
)
class TaskFeedback(Page):
@staticmethod
def is_displayed(player):
return (
player.round_number <= C.NUM_PRACTICE_ROUNDS and
not player.participant.vars.get('failed_captcha', False)
)
@staticmethod
def vars_for_template(player):
return dict(
payment_round_number = player.round_number-C.NUM_PRACTICE_ROUNDS,
practice_round_number = player.round_number,
round_number = player.round_number,
belief_A = player.belief_1,
belief_B = 100-player.belief_1,
paymentbelief_A = 2*player.belief_1-player.belief_1**2/100,
paymentbelief_B = 100-player.belief_1**2/100,
)
@staticmethod
def js_vars(player):
return dict(
decision = player.decision,
belief_1 = player.belief_1,
)
class CaptchaFailed(Page):
@staticmethod
def is_displayed(player):
return (
player.participant.vars.get('failed_captcha', True)
)
class Captcha1(Page):
template_name = 'Task1/Captcha.html'
form_model = 'player'
form_fields = ['captcha_attempt_1']
@staticmethod
def is_displayed(player):
if not (
player.round_number in C.captcha_rounds
and not player.participant.vars.get('failed_captcha', False)
and player.subsession.session.config['use_captchas'] == 1
):
return False
else:
return True
@staticmethod
def vars_for_template(player):
max_captcha_time = C.max_captcha_time
return {
'image_path': "images/captchas/captcha{}.png".format(C.captcha_rounds.index(player.round_number) + 1),
'attempt': 1,
'max_captcha_time': max_captcha_time,
}
class Captcha2(Page):
template_name = 'Task1/Captcha.html'
form_model = 'player'
form_fields = ['captcha_attempt_2']
@staticmethod
def is_displayed(player):
if not (
player.round_number in C.captcha_rounds
and not player.participant.vars.get('failed_captcha', False)
and player.subsession.session.config['use_captchas'] == 1
):
return False
else:
return player.captcha_attempt_1.lower() != C.captcha_text[C.captcha_rounds.index(player.round_number)].lower()
@staticmethod
def vars_for_template(player):
max_captcha_time = C.max_captcha_time
return {
'image_path': "images/captchas/captcha{}.png".format(C.captcha_rounds.index(player.round_number) + 1),
'attempt': 2,
'max_captcha_time': max_captcha_time,
}
class Captcha3(Page):
template_name = 'Task1/Captcha.html'
form_model = 'player'
form_fields = ['captcha_attempt_3']
@staticmethod
def is_displayed(player):
if not (
player.round_number in C.captcha_rounds
and not player.participant.vars.get('failed_captcha', False)
and player.subsession.session.config['use_captchas'] == 1
):
return False
else:
return (
player.captcha_attempt_1.lower() != C.captcha_text[
C.captcha_rounds.index(player.round_number)
].lower()
and player.captcha_attempt_2.lower() != C.captcha_text[
C.captcha_rounds.index(player.round_number)
].lower()
)
@staticmethod
def vars_for_template(player):
max_captcha_time = C.max_captcha_time
return {
'image_path': "images/captchas/captcha{}.png".format(C.captcha_rounds.index(player.round_number) + 1),
'attempt': 3,
'max_captcha_time': max_captcha_time,
}
@staticmethod
def before_next_page(player, timeout_happened):
correct_text = C.captcha_text[C.captcha_rounds.index(player.round_number)].lower()
if (
player.round_number in C.captcha_rounds
and player.captcha_attempt_1.lower() != correct_text
and player.captcha_attempt_2.lower() != correct_text
and player.captcha_attempt_3.lower() != correct_text
):
player.participant.vars['failed_captcha'] = True
# Update page_sequence
page_sequence = [
Instructions,
Captcha1,
Captcha2,
Captcha3,
CaptchaFailed,
TaskIntro,
Task,
TaskFeedback,
]

384
Tutorial oTree.md Normal file
View file

@ -0,0 +1,384 @@
# Lecture: Introduction to oTree
**Goal:** Understand the architecture of an oTree application and build a basic Bayesian game.
---
### 0. Preliminaries:
- [Learn Python in X Minutes](https://learnxinyminutes.com/python/)
- [oTree documentation](https://otree.readthedocs.io/en/latest/)
- [oTree Tutorials](https://www.youtube.com/playlist?list=PLBL9eqPcwzGPli11Yighw5LWwzIifEFd_)
- [Deploying oTree on Heroku via command line](https://www.youtube.com/watch?v=VrPdBEghYEM)
---
## 1. oTree
### What is oTree?
oTree is an open-source, Starlette-based framework for implementing interactive experiments in the browser.
* **Logic:** Written in **Python**.
* **Interface:** Written in **HTML/Starlette** template tags.
* **Data Storage:** Automatic integration with **PostgreSQL** or **SQLite**.
* **Deployment:** Easy to host on **Heroku** or local servers.
---
### Installation + project creation
#### Install
```bash
python -m pip install otree
otree --version
```
#### Create a project
```bash
otree startproject myproject
cd myproject
otree devserver
```
Then open: `http://localhost:8000/`
#### Create an app inside the project
```bash
otree startapp my_app
```
---
### File layout (important ones)
Inside the project:
- `settings.py` — session configs, installed apps, currency settings, etc.
- `_static/` — CSS/JS (optional)
- `_templates/` — global templates (optional)
- `my_app/` — your app code
Inside an app:
- `__init__.py` — main app logic (models + pages + constants)
- `templates/my_app/*.html` — participant-facing pages
- `tests.py` — automated tests/bots (optional but recommended)
---
### The Core Structure
An oTree project is organised into **Apps**. Each app contains:
* **`__init__.py`**: The "brain." Defines your models, variables, and logic.
* **`Pages.py`**: Controls the sequence of what the participant sees.
* **Templates (`.html`)**: The visual interface for each page.
### The Hierarchy
1. **Session:** The entire group of participants.
2. **Subsession:** A specific round within an app.
3. **Group:** A collection of players interacting (e.g., in a prisoner's dilemma; we won't do this today).
4. **Player:** The individual unit.
5. **Pages**: The screens participants see (forms, instructions, results).
Data live in the database. Each **Player** has fields that get saved automatically.
---
### Defining Variables
Variables are defined in `__init__.py` under three classes: `Constants`, `Subsession`, `Group`, and `Player`.
```python
class Constants(BaseConstants):
NAME_IN_URL = 'Decision'
PLAYERS_PER_GROUP = None
NUM_PRACTICE_ROUNDS = 2
NUM_PAYMENT_ROUNDS = 3
NUM_ROUNDS = NUM_PRACTICE_ROUNDS + NUM_PAYMENT_ROUNDS
class Player(BasePlayer):
decision = models.IntegerField()
belief_1 = models.FloatField(min=0, max=100)
captcha_attempt_1 = models.StringField(label="", initial="")
```
More types of fields here https://otree.readthedocs.io/en/latest/models.html#fields.
### Forms and validation
Basic:
```python
class Decision(Page):
form_model = "player"
form_fields = ["choice"]
```
Cross-field validation:
```python
class Player(BasePlayer):
a = models.IntegerField()
b = models.IntegerField()
class SomePage(Page):
form_model = "player"
form_fields = ["a", "b"]
@staticmethod
def error_message(player, values):
if values["a"] + values["b"] != 10:
return "a + b must equal 10."
```
---
### Where to put logic: common hooks
- `Subsession.creating_session()`
Initialise per-session/per-round variables, assign treatments, form groups.
- `Page.is_displayed(player)`
Conditional page display (by round/treatment/role).
- `Page.vars_for_template(player)`
Pass computed values to the template.
- `Page.error_message(player, values)`
Validate across multiple fields.
- `Page.before_next_page(player, timeout_happened)`
Compute payoffs, set state, write vars.
- `Group.set_payoffs()` (your own method)
Common pattern: compute payoffs after everyone submits.
---
### Game Logic & Functions
Logic is triggered by events.
#### Determining if page is displayed
```python
class TaskFeedback(Page):
@staticmethod
def is_displayed(player):
return (
player.round_number <= C.NUM_PRACTICE_ROUNDS and
not player.participant.vars.get('failed_captcha', False)
)
```
#### Setting values in particular rounds
```python
def creating_session(subsession: Subsession):
for player in subsession.get_players():
if player.round_number == 1:
treatments_index = list(range(len(C.payoffs_list)))
random.shuffle(treatments_index)
for r in range(C.NUM_PAYMENT_ROUNDS):
player.in_round(C.NUM_PRACTICE_ROUNDS+1+r).treatment_index = treatments_index[r]
```
Useful built-in methods: https://otree.readthedocs.io/en/latest/models.html#built-in-fields-and-methods.
---
### The User Interface
Templates use HTML combined with oTree tags to display data dynamically.
**Contribute.html**
```html
{{ block title }}
Round {{ payment_round_number }} out of {{ C.NUM_PAYMENT_ROUNDS }}
{{ endblock }}
```
---
### Live pages (real-time interaction)
For truly interactive real-time tasks (e.g., chat, continuous-time trading),
oTree supports **live pages** (WebSocket-style). Basic pattern:
```python
class MyLivePage(Page):
live_method = "live_handler"
@staticmethod
def live_handler(player, data):
# data is a dict sent from JS
return {player.id_in_group: dict(message="received")}
```
You also write JS in the template to send/receive messages.
See more here: https://otree.readthedocs.io/en/latest/multiplayer/intro.html.
---
### Running the Server
To see your work in the browser:
1. **Initialise:** `otree devserver 8000`
2. **Access:** Open `http://localhost:8000`
3. **Data:** Go to the Admin tab to monitor real-time results and export CSV/Excel files.
---
### Quick Reference Table
| Command | Purpose |
| --- | --- |
| `otree startproject <name>` | Create a new project folder |
| `otree startapp <name>` | Create a new app within the project |
| `otree devserver` | Run the local testing server |
| `otree zip` | Package the app for server upload |
---
> **Important:** Always test your app using to ensure your payoff logic works before bringing in real participants.
---
## 2. Deployment
For testing/class projects, focus first on:
- local devserver (`otree devserver`);
- exporting data reliably.
Common option:
1. oTree app on GitHub.
2. Host on [heroku.com](heroku.com).
3. Recruit on [UCL ELFE](https://uclelfe.github.io) or [Prolific.com](https://www.prolific.com).
Template already fitted to deal with this pipeline.
The setting (in config; available when creating a session) completionlink is where you should put the prolific link.
Prolific ID should be saved in participant labels.
---
### On Heroku
- Create new app
- Deploy from GitHub; manual deploy; then turn on automatic deployments
- Add web dyno (GitHub education gives free dynos); worker dyno not needed
- Add-on: postgresql (cheapest for testing)
Running for real:
- Performance M dynos with auto-scalling with max latency of 500ms
- Postgresql Standard 2
- Config vars: OTREE_AUTH_LEVEL: STUDY; OTREE_PRODUCTION: 1; OTREE_ADMIN_PASSWORD: whateveryouwant
Downgrading postgresql is a real pain. Here's my cheatsheet:
```bash
Procedure to copy and remove previous database on heroku
# Step 0. Login
heroku login
# Step 1. Provision a new database
heroku addons:create heroku-postgresql:essential-0 --app example-app
# Step 2. Enter maintenance mode to prevent database writes
heroku maintenance:on --app appname
# Step 3. Transfer data to the new database
## Check name and colour; or on website; it takes a while to provision, give it 2-3 minutes for new database to to show up
heroku addons --all
heroku config --app example-app
## Copy
heroku pg:copy app_name_to_copy_from::HEROKU_POSTGRESQL_COLOUR_TO_COPY_FROM HEROKU_POSTGRESQL_COLOUR_TO_COPY_TO --app app_name_to_copy_to
# Step 4. Promote the new database
heroku pg:promote HEROKU_POSTGRESQL_COLOUR_TO_PROMOTE -a appname
# Step 5. Destroy previous database
heroku addons:destroy HEROKU_POSTGRESQL_COLOUR --confirm appname
# Step 6. Exit maintenance mode
heroku maintenance:off --app appname
Example
heroku login
heroku maintenance:on --app southern-path-2
heroku addons:create heroku-postgresql:essential-0 --app southern-path-2
heroku addons --all
# This gets you the colours of the database; note that it takes a bit to provision, so they won't show immediately
heroku config --app southern-path-2
heroku pg:copy southern-path-2::DATABASE HEROKU_POSTGRESQL_PINK_URL --app southern-path-2 --confirm southern-path-2
heroku pg:promote postgresql-clear-38905 -a southern-path-2
heroku addons:destroy postgresql-round-22954 --confirm southern-path-2
heroku maintenance:off --app southern-path-2
```
---
### On Prolific
My typical study name: Survey on Decision-Making (up to **£X.00/Nmin** with bonus)
Internal name: Acronym YYYY.MM.DD
My typical description:
```
This survey seeks to understand how individuals make choices and is conducted by academic researchers. 
You can earn up to £X.00 for Nmin.
You will earn a minimum of £Y.00. 
Please keep in mind that how much you will receive will depend on your own choices and the choices of other participants. The payment scheme is designed to encourage you to think carefully about your choices.
```
- Study label: None
- Device requirements: Desktop only
- Labels: None
- Limit participant access: Depends on your budget
- What's the URL of your study?
https://HEROKUAPPNAME.herokuapp.com/room/prolific?participant_label={{%PROLIFIC_PID%}}&STUDY_ID={{%STUDY_ID%}}&SESSION_ID={{%SESSION_ID%}}
- How do you want to record IDs? URL parameters
- Completion paths: Manually review
- Screeners:
- Current Country of Residence: United Kingdom (better sample)
- Approval Rate: 95100
- Fluent languages: English
- Exclude participants from other studies: any pilot and other waves of the same study that you've ran
- Do participants require a login & password to access your survey tool? No
- Total times a participant can complete your study: once
- Reject exceptionally fast submissions: NO!
---
### On ELFE
- Login to main terminal.
- Run the app in production mode, create ELFE session.
- Turn on all terminals and open the link.
- Test the Qualtrics questionnaire at the end.
- Test (elfe.lab.run)[elfe.lab.run].
- Schedule session on (elfe.lab.run)[elfe.lab.run] and invite participants.
On the day
- Login to main terminal.
- Run the app in production mode, create ELFE session.
- Turn on all terminals and open the link.
- Sit participants and allocate them on the app.
- Let them start after all seated.
- Take note of anything unusual.
- Show timer and remind participants of the time every half hour or so.
- Make sure everyone filled out the survey at the end before leaving (if they finished).
- Pay participants (lab manager, via Wise account).

9
_static/global/Page.html Normal file
View file

@ -0,0 +1,9 @@
{{ extends "otree/Page.html" }}
{{ load otree static }}
{{ block global_styles }}
<link rel="stylesheet" href="{% static 'global/custom.css' %}">
{{ endblock }}
{{ block global_scripts }}
{{ endblock }}

25
_static/global/custom.css Normal file
View file

@ -0,0 +1,25 @@
* {
margin: 0;
padding: 0;
border: 0;
outline: 0;
}
.center {
margin-left: auto;
margin-right: auto;
}
.otree-title {
margin-top:-30px;
}
.container {
height: 50px;
position: relative;
}
.centering {
margin: 0;
position: absolute;
top: 50%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

136
_static/js/lodash.min.js vendored Normal file
View file

@ -0,0 +1,136 @@
/**
* @license
* Lodash lodash.com/license | Underscore.js 1.8.3 underscorejs.org/LICENSE
*/
;(function(){function n(n,t){return n.set(t[0],t[1]),n}function t(n,t){return n.add(t),n}function r(n,t,r){switch(r.length){case 0:return n.call(t);case 1:return n.call(t,r[0]);case 2:return n.call(t,r[0],r[1]);case 3:return n.call(t,r[0],r[1],r[2])}return n.apply(t,r)}function e(n,t,r,e){for(var u=-1,i=null==n?0:n.length;++u<i;){var o=n[u];t(e,o,r(o),n)}return e}function u(n,t){for(var r=-1,e=null==n?0:n.length;++r<e&&false!==t(n[r],r,n););return n}function i(n,t){for(var r=null==n?0:n.length;r--&&false!==t(n[r],r,n););
return n}function o(n,t){for(var r=-1,e=null==n?0:n.length;++r<e;)if(!t(n[r],r,n))return false;return true}function f(n,t){for(var r=-1,e=null==n?0:n.length,u=0,i=[];++r<e;){var o=n[r];t(o,r,n)&&(i[u++]=o)}return i}function c(n,t){return!(null==n||!n.length)&&-1<d(n,t,0)}function a(n,t,r){for(var e=-1,u=null==n?0:n.length;++e<u;)if(r(t,n[e]))return true;return false}function l(n,t){for(var r=-1,e=null==n?0:n.length,u=Array(e);++r<e;)u[r]=t(n[r],r,n);return u}function s(n,t){for(var r=-1,e=t.length,u=n.length;++r<e;)n[u+r]=t[r];
return n}function h(n,t,r,e){var u=-1,i=null==n?0:n.length;for(e&&i&&(r=n[++u]);++u<i;)r=t(r,n[u],u,n);return r}function p(n,t,r,e){var u=null==n?0:n.length;for(e&&u&&(r=n[--u]);u--;)r=t(r,n[u],u,n);return r}function _(n,t){for(var r=-1,e=null==n?0:n.length;++r<e;)if(t(n[r],r,n))return true;return false}function v(n,t,r){var e;return r(n,function(n,r,u){if(t(n,r,u))return e=r,false}),e}function g(n,t,r,e){var u=n.length;for(r+=e?1:-1;e?r--:++r<u;)if(t(n[r],r,n))return r;return-1}function d(n,t,r){if(t===t)n:{
--r;for(var e=n.length;++r<e;)if(n[r]===t){n=r;break n}n=-1}else n=g(n,b,r);return n}function y(n,t,r,e){--r;for(var u=n.length;++r<u;)if(e(n[r],t))return r;return-1}function b(n){return n!==n}function x(n,t){var r=null==n?0:n.length;return r?k(n,t)/r:P}function j(n){return function(t){return null==t?F:t[n]}}function w(n){return function(t){return null==n?F:n[t]}}function m(n,t,r,e,u){return u(n,function(n,u,i){r=e?(e=false,n):t(r,n,u,i)}),r}function A(n,t){var r=n.length;for(n.sort(t);r--;)n[r]=n[r].c;
return n}function k(n,t){for(var r,e=-1,u=n.length;++e<u;){var i=t(n[e]);i!==F&&(r=r===F?i:r+i)}return r}function E(n,t){for(var r=-1,e=Array(n);++r<n;)e[r]=t(r);return e}function O(n,t){return l(t,function(t){return[t,n[t]]})}function S(n){return function(t){return n(t)}}function I(n,t){return l(t,function(t){return n[t]})}function R(n,t){return n.has(t)}function z(n,t){for(var r=-1,e=n.length;++r<e&&-1<d(t,n[r],0););return r}function W(n,t){for(var r=n.length;r--&&-1<d(t,n[r],0););return r}function B(n){
return"\\"+Tn[n]}function L(n){var t=-1,r=Array(n.size);return n.forEach(function(n,e){r[++t]=[e,n]}),r}function U(n,t){return function(r){return n(t(r))}}function C(n,t){for(var r=-1,e=n.length,u=0,i=[];++r<e;){var o=n[r];o!==t&&"__lodash_placeholder__"!==o||(n[r]="__lodash_placeholder__",i[u++]=r)}return i}function D(n){var t=-1,r=Array(n.size);return n.forEach(function(n){r[++t]=n}),r}function M(n){var t=-1,r=Array(n.size);return n.forEach(function(n){r[++t]=[n,n]}),r}function T(n){if(Bn.test(n)){
for(var t=zn.lastIndex=0;zn.test(n);)++t;n=t}else n=tt(n);return n}function $(n){return Bn.test(n)?n.match(zn)||[]:n.split("")}var F,N=1/0,P=NaN,Z=[["ary",128],["bind",1],["bindKey",2],["curry",8],["curryRight",16],["flip",512],["partial",32],["partialRight",64],["rearg",256]],q=/\b__p\+='';/g,V=/\b(__p\+=)''\+/g,K=/(__e\(.*?\)|\b__t\))\+'';/g,G=/&(?:amp|lt|gt|quot|#39);/g,H=/[&<>"']/g,J=RegExp(G.source),Y=RegExp(H.source),Q=/<%-([\s\S]+?)%>/g,X=/<%([\s\S]+?)%>/g,nn=/<%=([\s\S]+?)%>/g,tn=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,rn=/^\w*$/,en=/^\./,un=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,on=/[\\^$.*+?()[\]{}|]/g,fn=RegExp(on.source),cn=/^\s+|\s+$/g,an=/^\s+/,ln=/\s+$/,sn=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,hn=/\{\n\/\* \[wrapped with (.+)\] \*/,pn=/,? & /,_n=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,vn=/\\(\\)?/g,gn=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,dn=/\w*$/,yn=/^[-+]0x[0-9a-f]+$/i,bn=/^0b[01]+$/i,xn=/^\[object .+?Constructor\]$/,jn=/^0o[0-7]+$/i,wn=/^(?:0|[1-9]\d*)$/,mn=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,An=/($^)/,kn=/['\n\r\u2028\u2029\\]/g,En="[\\ufe0e\\ufe0f]?(?:[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]|\\ud83c[\\udffb-\\udfff])?(?:\\u200d(?:[^\\ud800-\\udfff]|(?:\\ud83c[\\udde6-\\uddff]){2}|[\\ud800-\\udbff][\\udc00-\\udfff])[\\ufe0e\\ufe0f]?(?:[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]|\\ud83c[\\udffb-\\udfff])?)*",On="(?:[\\u2700-\\u27bf]|(?:\\ud83c[\\udde6-\\uddff]){2}|[\\ud800-\\udbff][\\udc00-\\udfff])"+En,Sn="(?:[^\\ud800-\\udfff][\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]?|[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]|(?:\\ud83c[\\udde6-\\uddff]){2}|[\\ud800-\\udbff][\\udc00-\\udfff]|[\\ud800-\\udfff])",In=RegExp("['\u2019]","g"),Rn=RegExp("[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]","g"),zn=RegExp("\\ud83c[\\udffb-\\udfff](?=\\ud83c[\\udffb-\\udfff])|"+Sn+En,"g"),Wn=RegExp(["[A-Z\\xc0-\\xd6\\xd8-\\xde]?[a-z\\xdf-\\xf6\\xf8-\\xff]+(?:['\u2019](?:d|ll|m|re|s|t|ve))?(?=[\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000]|[A-Z\\xc0-\\xd6\\xd8-\\xde]|$)|(?:[A-Z\\xc0-\\xd6\\xd8-\\xde]|[^\\ud800-\\udfff\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\d+\\u2700-\\u27bfa-z\\xdf-\\xf6\\xf8-\\xffA-Z\\xc0-\\xd6\\xd8-\\xde])+(?:['\u2019](?:D|LL|M|RE|S|T|VE))?(?=[\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000]|[A-Z\\xc0-\\xd6\\xd8-\\xde](?:[a-z\\xdf-\\xf6\\xf8-\\xff]|[^\\ud800-\\udfff\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\d+\\u2700-\\u27bfa-z\\xdf-\\xf6\\xf8-\\xffA-Z\\xc0-\\xd6\\xd8-\\xde])|$)|[A-Z\\xc0-\\xd6\\xd8-\\xde]?(?:[a-z\\xdf-\\xf6\\xf8-\\xff]|[^\\ud800-\\udfff\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000\\d+\\u2700-\\u27bfa-z\\xdf-\\xf6\\xf8-\\xffA-Z\\xc0-\\xd6\\xd8-\\xde])+(?:['\u2019](?:d|ll|m|re|s|t|ve))?|[A-Z\\xc0-\\xd6\\xd8-\\xde]+(?:['\u2019](?:D|LL|M|RE|S|T|VE))?|\\d*(?:(?:1ST|2ND|3RD|(?![123])\\dTH)\\b)|\\d*(?:(?:1st|2nd|3rd|(?![123])\\dth)\\b)|\\d+",On].join("|"),"g"),Bn=RegExp("[\\u200d\\ud800-\\udfff\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\ufe0e\\ufe0f]"),Ln=/[a-z][A-Z]|[A-Z]{2,}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,Un="Array Buffer DataView Date Error Float32Array Float64Array Function Int8Array Int16Array Int32Array Map Math Object Promise RegExp Set String Symbol TypeError Uint8Array Uint8ClampedArray Uint16Array Uint32Array WeakMap _ clearTimeout isFinite parseInt setTimeout".split(" "),Cn={};
Cn["[object Float32Array]"]=Cn["[object Float64Array]"]=Cn["[object Int8Array]"]=Cn["[object Int16Array]"]=Cn["[object Int32Array]"]=Cn["[object Uint8Array]"]=Cn["[object Uint8ClampedArray]"]=Cn["[object Uint16Array]"]=Cn["[object Uint32Array]"]=true,Cn["[object Arguments]"]=Cn["[object Array]"]=Cn["[object ArrayBuffer]"]=Cn["[object Boolean]"]=Cn["[object DataView]"]=Cn["[object Date]"]=Cn["[object Error]"]=Cn["[object Function]"]=Cn["[object Map]"]=Cn["[object Number]"]=Cn["[object Object]"]=Cn["[object RegExp]"]=Cn["[object Set]"]=Cn["[object String]"]=Cn["[object WeakMap]"]=false;
var Dn={};Dn["[object Arguments]"]=Dn["[object Array]"]=Dn["[object ArrayBuffer]"]=Dn["[object DataView]"]=Dn["[object Boolean]"]=Dn["[object Date]"]=Dn["[object Float32Array]"]=Dn["[object Float64Array]"]=Dn["[object Int8Array]"]=Dn["[object Int16Array]"]=Dn["[object Int32Array]"]=Dn["[object Map]"]=Dn["[object Number]"]=Dn["[object Object]"]=Dn["[object RegExp]"]=Dn["[object Set]"]=Dn["[object String]"]=Dn["[object Symbol]"]=Dn["[object Uint8Array]"]=Dn["[object Uint8ClampedArray]"]=Dn["[object Uint16Array]"]=Dn["[object Uint32Array]"]=true,
Dn["[object Error]"]=Dn["[object Function]"]=Dn["[object WeakMap]"]=false;var Mn,Tn={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},$n=parseFloat,Fn=parseInt,Nn=typeof global=="object"&&global&&global.Object===Object&&global,Pn=typeof self=="object"&&self&&self.Object===Object&&self,Zn=Nn||Pn||Function("return this")(),qn=typeof exports=="object"&&exports&&!exports.nodeType&&exports,Vn=qn&&typeof module=="object"&&module&&!module.nodeType&&module,Kn=Vn&&Vn.exports===qn,Gn=Kn&&Nn.process;
n:{try{Mn=Gn&&Gn.binding&&Gn.binding("util");break n}catch(n){}Mn=void 0}var Hn=Mn&&Mn.isArrayBuffer,Jn=Mn&&Mn.isDate,Yn=Mn&&Mn.isMap,Qn=Mn&&Mn.isRegExp,Xn=Mn&&Mn.isSet,nt=Mn&&Mn.isTypedArray,tt=j("length"),rt=w({"\xc0":"A","\xc1":"A","\xc2":"A","\xc3":"A","\xc4":"A","\xc5":"A","\xe0":"a","\xe1":"a","\xe2":"a","\xe3":"a","\xe4":"a","\xe5":"a","\xc7":"C","\xe7":"c","\xd0":"D","\xf0":"d","\xc8":"E","\xc9":"E","\xca":"E","\xcb":"E","\xe8":"e","\xe9":"e","\xea":"e","\xeb":"e","\xcc":"I","\xcd":"I","\xce":"I",
"\xcf":"I","\xec":"i","\xed":"i","\xee":"i","\xef":"i","\xd1":"N","\xf1":"n","\xd2":"O","\xd3":"O","\xd4":"O","\xd5":"O","\xd6":"O","\xd8":"O","\xf2":"o","\xf3":"o","\xf4":"o","\xf5":"o","\xf6":"o","\xf8":"o","\xd9":"U","\xda":"U","\xdb":"U","\xdc":"U","\xf9":"u","\xfa":"u","\xfb":"u","\xfc":"u","\xdd":"Y","\xfd":"y","\xff":"y","\xc6":"Ae","\xe6":"ae","\xde":"Th","\xfe":"th","\xdf":"ss","\u0100":"A","\u0102":"A","\u0104":"A","\u0101":"a","\u0103":"a","\u0105":"a","\u0106":"C","\u0108":"C","\u010a":"C",
"\u010c":"C","\u0107":"c","\u0109":"c","\u010b":"c","\u010d":"c","\u010e":"D","\u0110":"D","\u010f":"d","\u0111":"d","\u0112":"E","\u0114":"E","\u0116":"E","\u0118":"E","\u011a":"E","\u0113":"e","\u0115":"e","\u0117":"e","\u0119":"e","\u011b":"e","\u011c":"G","\u011e":"G","\u0120":"G","\u0122":"G","\u011d":"g","\u011f":"g","\u0121":"g","\u0123":"g","\u0124":"H","\u0126":"H","\u0125":"h","\u0127":"h","\u0128":"I","\u012a":"I","\u012c":"I","\u012e":"I","\u0130":"I","\u0129":"i","\u012b":"i","\u012d":"i",
"\u012f":"i","\u0131":"i","\u0134":"J","\u0135":"j","\u0136":"K","\u0137":"k","\u0138":"k","\u0139":"L","\u013b":"L","\u013d":"L","\u013f":"L","\u0141":"L","\u013a":"l","\u013c":"l","\u013e":"l","\u0140":"l","\u0142":"l","\u0143":"N","\u0145":"N","\u0147":"N","\u014a":"N","\u0144":"n","\u0146":"n","\u0148":"n","\u014b":"n","\u014c":"O","\u014e":"O","\u0150":"O","\u014d":"o","\u014f":"o","\u0151":"o","\u0154":"R","\u0156":"R","\u0158":"R","\u0155":"r","\u0157":"r","\u0159":"r","\u015a":"S","\u015c":"S",
"\u015e":"S","\u0160":"S","\u015b":"s","\u015d":"s","\u015f":"s","\u0161":"s","\u0162":"T","\u0164":"T","\u0166":"T","\u0163":"t","\u0165":"t","\u0167":"t","\u0168":"U","\u016a":"U","\u016c":"U","\u016e":"U","\u0170":"U","\u0172":"U","\u0169":"u","\u016b":"u","\u016d":"u","\u016f":"u","\u0171":"u","\u0173":"u","\u0174":"W","\u0175":"w","\u0176":"Y","\u0177":"y","\u0178":"Y","\u0179":"Z","\u017b":"Z","\u017d":"Z","\u017a":"z","\u017c":"z","\u017e":"z","\u0132":"IJ","\u0133":"ij","\u0152":"Oe","\u0153":"oe",
"\u0149":"'n","\u017f":"s"}),et=w({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#39;"}),ut=w({"&amp;":"&","&lt;":"<","&gt;":">","&quot;":'"',"&#39;":"'"}),it=function w(En){function On(n){if(xu(n)&&!af(n)&&!(n instanceof Mn)){if(n instanceof zn)return n;if(ci.call(n,"__wrapped__"))return Pe(n)}return new zn(n)}function Sn(){}function zn(n,t){this.__wrapped__=n,this.__actions__=[],this.__chain__=!!t,this.__index__=0,this.__values__=F}function Mn(n){this.__wrapped__=n,this.__actions__=[],this.__dir__=1,
this.__filtered__=false,this.__iteratees__=[],this.__takeCount__=4294967295,this.__views__=[]}function Tn(n){var t=-1,r=null==n?0:n.length;for(this.clear();++t<r;){var e=n[t];this.set(e[0],e[1])}}function Nn(n){var t=-1,r=null==n?0:n.length;for(this.clear();++t<r;){var e=n[t];this.set(e[0],e[1])}}function Pn(n){var t=-1,r=null==n?0:n.length;for(this.clear();++t<r;){var e=n[t];this.set(e[0],e[1])}}function qn(n){var t=-1,r=null==n?0:n.length;for(this.__data__=new Pn;++t<r;)this.add(n[t])}function Vn(n){
this.size=(this.__data__=new Nn(n)).size}function Gn(n,t){var r,e=af(n),u=!e&&cf(n),i=!e&&!u&&sf(n),o=!e&&!u&&!i&&gf(n),u=(e=e||u||i||o)?E(n.length,ri):[],f=u.length;for(r in n)!t&&!ci.call(n,r)||e&&("length"==r||i&&("offset"==r||"parent"==r)||o&&("buffer"==r||"byteLength"==r||"byteOffset"==r)||Re(r,f))||u.push(r);return u}function tt(n){var t=n.length;return t?n[cr(0,t-1)]:F}function ot(n,t){return Te(Mr(n),gt(t,0,n.length))}function ft(n){return Te(Mr(n))}function ct(n,t,r){(r===F||hu(n[t],r))&&(r!==F||t in n)||_t(n,t,r);
}function at(n,t,r){var e=n[t];ci.call(n,t)&&hu(e,r)&&(r!==F||t in n)||_t(n,t,r)}function lt(n,t){for(var r=n.length;r--;)if(hu(n[r][0],t))return r;return-1}function st(n,t,r,e){return oo(n,function(n,u,i){t(e,n,r(n),i)}),e}function ht(n,t){return n&&Tr(t,Lu(t),n)}function pt(n,t){return n&&Tr(t,Uu(t),n)}function _t(n,t,r){"__proto__"==t&&Ei?Ei(n,t,{configurable:true,enumerable:true,value:r,writable:true}):n[t]=r}function vt(n,t){for(var r=-1,e=t.length,u=Hu(e),i=null==n;++r<e;)u[r]=i?F:Wu(n,t[r]);return u;
}function gt(n,t,r){return n===n&&(r!==F&&(n=n<=r?n:r),t!==F&&(n=n>=t?n:t)),n}function dt(n,t,r,e,i,o){var f,c=1&t,a=2&t,l=4&t;if(r&&(f=i?r(n,e,i,o):r(n)),f!==F)return f;if(!bu(n))return n;if(e=af(n)){if(f=Ee(n),!c)return Mr(n,f)}else{var s=yo(n),h="[object Function]"==s||"[object GeneratorFunction]"==s;if(sf(n))return Wr(n,c);if("[object Object]"==s||"[object Arguments]"==s||h&&!i){if(f=a||h?{}:Oe(n),!c)return a?Fr(n,pt(f,n)):$r(n,ht(f,n))}else{if(!Dn[s])return i?n:{};f=Se(n,s,dt,c)}}if(o||(o=new Vn),
i=o.get(n))return i;o.set(n,f);var a=l?a?ye:de:a?Uu:Lu,p=e?F:a(n);return u(p||n,function(e,u){p&&(u=e,e=n[u]),at(f,u,dt(e,t,r,u,n,o))}),f}function yt(n){var t=Lu(n);return function(r){return bt(r,n,t)}}function bt(n,t,r){var e=r.length;if(null==n)return!e;for(n=ni(n);e--;){var u=r[e],i=t[u],o=n[u];if(o===F&&!(u in n)||!i(o))return false}return true}function xt(n,t,r){if(typeof n!="function")throw new ei("Expected a function");return jo(function(){n.apply(F,r)},t)}function jt(n,t,r,e){var u=-1,i=c,o=true,f=n.length,s=[],h=t.length;
if(!f)return s;r&&(t=l(t,S(r))),e?(i=a,o=false):200<=t.length&&(i=R,o=false,t=new qn(t));n:for(;++u<f;){var p=n[u],_=null==r?p:r(p),p=e||0!==p?p:0;if(o&&_===_){for(var v=h;v--;)if(t[v]===_)continue n;s.push(p)}else i(t,_,e)||s.push(p)}return s}function wt(n,t){var r=true;return oo(n,function(n,e,u){return r=!!t(n,e,u)}),r}function mt(n,t,r){for(var e=-1,u=n.length;++e<u;){var i=n[e],o=t(i);if(null!=o&&(f===F?o===o&&!Au(o):r(o,f)))var f=o,c=i}return c}function At(n,t){var r=[];return oo(n,function(n,e,u){
t(n,e,u)&&r.push(n)}),r}function kt(n,t,r,e,u){var i=-1,o=n.length;for(r||(r=Ie),u||(u=[]);++i<o;){var f=n[i];0<t&&r(f)?1<t?kt(f,t-1,r,e,u):s(u,f):e||(u[u.length]=f)}return u}function Et(n,t){return n&&co(n,t,Lu)}function Ot(n,t){return n&&ao(n,t,Lu)}function St(n,t){return f(t,function(t){return gu(n[t])})}function It(n,t){t=Rr(t,n);for(var r=0,e=t.length;null!=n&&r<e;)n=n[$e(t[r++])];return r&&r==e?n:F}function Rt(n,t,r){return t=t(n),af(n)?t:s(t,r(n))}function zt(n){if(null==n)n=n===F?"[object Undefined]":"[object Null]";else if(ki&&ki in ni(n)){
var t=ci.call(n,ki),r=n[ki];try{n[ki]=F;var e=true}catch(n){}var u=si.call(n);e&&(t?n[ki]=r:delete n[ki]),n=u}else n=si.call(n);return n}function Wt(n,t){return n>t}function Bt(n,t){return null!=n&&ci.call(n,t)}function Lt(n,t){return null!=n&&t in ni(n)}function Ut(n,t,r){for(var e=r?a:c,u=n[0].length,i=n.length,o=i,f=Hu(i),s=1/0,h=[];o--;){var p=n[o];o&&t&&(p=l(p,S(t))),s=Mi(p.length,s),f[o]=!r&&(t||120<=u&&120<=p.length)?new qn(o&&p):F}var p=n[0],_=-1,v=f[0];n:for(;++_<u&&h.length<s;){var g=p[_],d=t?t(g):g,g=r||0!==g?g:0;
if(v?!R(v,d):!e(h,d,r)){for(o=i;--o;){var y=f[o];if(y?!R(y,d):!e(n[o],d,r))continue n}v&&v.push(d),h.push(g)}}return h}function Ct(n,t,r){var e={};return Et(n,function(n,u,i){t(e,r(n),u,i)}),e}function Dt(n,t,e){return t=Rr(t,n),n=2>t.length?n:It(n,vr(t,0,-1)),t=null==n?n:n[$e(Ge(t))],null==t?F:r(t,n,e)}function Mt(n){return xu(n)&&"[object Arguments]"==zt(n)}function Tt(n){return xu(n)&&"[object ArrayBuffer]"==zt(n)}function $t(n){return xu(n)&&"[object Date]"==zt(n)}function Ft(n,t,r,e,u){if(n===t)t=true;else if(null==n||null==t||!xu(n)&&!xu(t))t=n!==n&&t!==t;else n:{
var i=af(n),o=af(t),f=i?"[object Array]":yo(n),c=o?"[object Array]":yo(t),f="[object Arguments]"==f?"[object Object]":f,c="[object Arguments]"==c?"[object Object]":c,a="[object Object]"==f,o="[object Object]"==c;if((c=f==c)&&sf(n)){if(!sf(t)){t=false;break n}i=true,a=false}if(c&&!a)u||(u=new Vn),t=i||gf(n)?_e(n,t,r,e,Ft,u):ve(n,t,f,r,e,Ft,u);else{if(!(1&r)&&(i=a&&ci.call(n,"__wrapped__"),f=o&&ci.call(t,"__wrapped__"),i||f)){n=i?n.value():n,t=f?t.value():t,u||(u=new Vn),t=Ft(n,t,r,e,u);break n}if(c)t:if(u||(u=new Vn),
i=1&r,f=de(n),o=f.length,c=de(t).length,o==c||i){for(a=o;a--;){var l=f[a];if(!(i?l in t:ci.call(t,l))){t=false;break t}}if((c=u.get(n))&&u.get(t))t=c==t;else{c=true,u.set(n,t),u.set(t,n);for(var s=i;++a<o;){var l=f[a],h=n[l],p=t[l];if(e)var _=i?e(p,h,l,t,n,u):e(h,p,l,n,t,u);if(_===F?h!==p&&!Ft(h,p,r,e,u):!_){c=false;break}s||(s="constructor"==l)}c&&!s&&(r=n.constructor,e=t.constructor,r!=e&&"constructor"in n&&"constructor"in t&&!(typeof r=="function"&&r instanceof r&&typeof e=="function"&&e instanceof e)&&(c=false)),
u.delete(n),u.delete(t),t=c}}else t=false;else t=false}}return t}function Nt(n){return xu(n)&&"[object Map]"==yo(n)}function Pt(n,t,r,e){var u=r.length,i=u,o=!e;if(null==n)return!i;for(n=ni(n);u--;){var f=r[u];if(o&&f[2]?f[1]!==n[f[0]]:!(f[0]in n))return false}for(;++u<i;){var f=r[u],c=f[0],a=n[c],l=f[1];if(o&&f[2]){if(a===F&&!(c in n))return false}else{if(f=new Vn,e)var s=e(a,l,c,n,t,f);if(s===F?!Ft(l,a,3,e,f):!s)return false}}return true}function Zt(n){return!(!bu(n)||li&&li in n)&&(gu(n)?_i:xn).test(Fe(n))}function qt(n){
return xu(n)&&"[object RegExp]"==zt(n)}function Vt(n){return xu(n)&&"[object Set]"==yo(n)}function Kt(n){return xu(n)&&yu(n.length)&&!!Cn[zt(n)]}function Gt(n){return typeof n=="function"?n:null==n?Nu:typeof n=="object"?af(n)?Xt(n[0],n[1]):Qt(n):Vu(n)}function Ht(n){if(!Le(n))return Ci(n);var t,r=[];for(t in ni(n))ci.call(n,t)&&"constructor"!=t&&r.push(t);return r}function Jt(n,t){return n<t}function Yt(n,t){var r=-1,e=pu(n)?Hu(n.length):[];return oo(n,function(n,u,i){e[++r]=t(n,u,i)}),e}function Qt(n){
var t=me(n);return 1==t.length&&t[0][2]?Ue(t[0][0],t[0][1]):function(r){return r===n||Pt(r,n,t)}}function Xt(n,t){return We(n)&&t===t&&!bu(t)?Ue($e(n),t):function(r){var e=Wu(r,n);return e===F&&e===t?Bu(r,n):Ft(t,e,3)}}function nr(n,t,r,e,u){n!==t&&co(t,function(i,o){if(bu(i)){u||(u=new Vn);var f=u,c=n[o],a=t[o],l=f.get(a);if(l)ct(n,o,l);else{var l=e?e(c,a,o+"",n,t,f):F,s=l===F;if(s){var h=af(a),p=!h&&sf(a),_=!h&&!p&&gf(a),l=a;h||p||_?af(c)?l=c:_u(c)?l=Mr(c):p?(s=false,l=Wr(a,true)):_?(s=false,l=Lr(a,true)):l=[]:wu(a)||cf(a)?(l=c,
cf(c)?l=Ru(c):(!bu(c)||r&&gu(c))&&(l=Oe(a))):s=false}s&&(f.set(a,l),nr(l,a,r,e,f),f.delete(a)),ct(n,o,l)}}else f=e?e(n[o],i,o+"",n,t,u):F,f===F&&(f=i),ct(n,o,f)},Uu)}function tr(n,t){var r=n.length;if(r)return t+=0>t?r:0,Re(t,r)?n[t]:F}function rr(n,t,r){var e=-1;return t=l(t.length?t:[Nu],S(je())),n=Yt(n,function(n){return{a:l(t,function(t){return t(n)}),b:++e,c:n}}),A(n,function(n,t){var e;n:{e=-1;for(var u=n.a,i=t.a,o=u.length,f=r.length;++e<o;){var c=Ur(u[e],i[e]);if(c){e=e>=f?c:c*("desc"==r[e]?-1:1);
break n}}e=n.b-t.b}return e})}function er(n,t){return ur(n,t,function(t,r){return Bu(n,r)})}function ur(n,t,r){for(var e=-1,u=t.length,i={};++e<u;){var o=t[e],f=It(n,o);r(f,o)&&pr(i,Rr(o,n),f)}return i}function ir(n){return function(t){return It(t,n)}}function or(n,t,r,e){var u=e?y:d,i=-1,o=t.length,f=n;for(n===t&&(t=Mr(t)),r&&(f=l(n,S(r)));++i<o;)for(var c=0,a=t[i],a=r?r(a):a;-1<(c=u(f,a,c,e));)f!==n&&wi.call(f,c,1),wi.call(n,c,1);return n}function fr(n,t){for(var r=n?t.length:0,e=r-1;r--;){var u=t[r];
if(r==e||u!==i){var i=u;Re(u)?wi.call(n,u,1):mr(n,u)}}}function cr(n,t){return n+zi(Fi()*(t-n+1))}function ar(n,t){var r="";if(!n||1>t||9007199254740991<t)return r;do t%2&&(r+=n),(t=zi(t/2))&&(n+=n);while(t);return r}function lr(n,t){return wo(Ce(n,t,Nu),n+"")}function sr(n){return tt(Du(n))}function hr(n,t){var r=Du(n);return Te(r,gt(t,0,r.length))}function pr(n,t,r,e){if(!bu(n))return n;t=Rr(t,n);for(var u=-1,i=t.length,o=i-1,f=n;null!=f&&++u<i;){var c=$e(t[u]),a=r;if(u!=o){var l=f[c],a=e?e(l,c,f):F;
a===F&&(a=bu(l)?l:Re(t[u+1])?[]:{})}at(f,c,a),f=f[c]}return n}function _r(n){return Te(Du(n))}function vr(n,t,r){var e=-1,u=n.length;for(0>t&&(t=-t>u?0:u+t),r=r>u?u:r,0>r&&(r+=u),u=t>r?0:r-t>>>0,t>>>=0,r=Hu(u);++e<u;)r[e]=n[e+t];return r}function gr(n,t){var r;return oo(n,function(n,e,u){return r=t(n,e,u),!r}),!!r}function dr(n,t,r){var e=0,u=null==n?e:n.length;if(typeof t=="number"&&t===t&&2147483647>=u){for(;e<u;){var i=e+u>>>1,o=n[i];null!==o&&!Au(o)&&(r?o<=t:o<t)?e=i+1:u=i}return u}return yr(n,t,Nu,r);
}function yr(n,t,r,e){t=r(t);for(var u=0,i=null==n?0:n.length,o=t!==t,f=null===t,c=Au(t),a=t===F;u<i;){var l=zi((u+i)/2),s=r(n[l]),h=s!==F,p=null===s,_=s===s,v=Au(s);(o?e||_:a?_&&(e||h):f?_&&h&&(e||!p):c?_&&h&&!p&&(e||!v):p||v?0:e?s<=t:s<t)?u=l+1:i=l}return Mi(i,4294967294)}function br(n,t){for(var r=-1,e=n.length,u=0,i=[];++r<e;){var o=n[r],f=t?t(o):o;if(!r||!hu(f,c)){var c=f;i[u++]=0===o?0:o}}return i}function xr(n){return typeof n=="number"?n:Au(n)?P:+n}function jr(n){if(typeof n=="string")return n;
if(af(n))return l(n,jr)+"";if(Au(n))return uo?uo.call(n):"";var t=n+"";return"0"==t&&1/n==-N?"-0":t}function wr(n,t,r){var e=-1,u=c,i=n.length,o=true,f=[],l=f;if(r)o=false,u=a;else if(200<=i){if(u=t?null:po(n))return D(u);o=false,u=R,l=new qn}else l=t?[]:f;n:for(;++e<i;){var s=n[e],h=t?t(s):s,s=r||0!==s?s:0;if(o&&h===h){for(var p=l.length;p--;)if(l[p]===h)continue n;t&&l.push(h),f.push(s)}else u(l,h,r)||(l!==f&&l.push(h),f.push(s))}return f}function mr(n,t){return t=Rr(t,n),n=2>t.length?n:It(n,vr(t,0,-1)),
null==n||delete n[$e(Ge(t))]}function Ar(n,t,r,e){for(var u=n.length,i=e?u:-1;(e?i--:++i<u)&&t(n[i],i,n););return r?vr(n,e?0:i,e?i+1:u):vr(n,e?i+1:0,e?u:i)}function kr(n,t){var r=n;return r instanceof Mn&&(r=r.value()),h(t,function(n,t){return t.func.apply(t.thisArg,s([n],t.args))},r)}function Er(n,t,r){var e=n.length;if(2>e)return e?wr(n[0]):[];for(var u=-1,i=Hu(e);++u<e;)for(var o=n[u],f=-1;++f<e;)f!=u&&(i[u]=jt(i[u]||o,n[f],t,r));return wr(kt(i,1),t,r)}function Or(n,t,r){for(var e=-1,u=n.length,i=t.length,o={};++e<u;)r(o,n[e],e<i?t[e]:F);
return o}function Sr(n){return _u(n)?n:[]}function Ir(n){return typeof n=="function"?n:Nu}function Rr(n,t){return af(n)?n:We(n,t)?[n]:mo(zu(n))}function zr(n,t,r){var e=n.length;return r=r===F?e:r,!t&&r>=e?n:vr(n,t,r)}function Wr(n,t){if(t)return n.slice();var r=n.length,r=yi?yi(r):new n.constructor(r);return n.copy(r),r}function Br(n){var t=new n.constructor(n.byteLength);return new di(t).set(new di(n)),t}function Lr(n,t){return new n.constructor(t?Br(n.buffer):n.buffer,n.byteOffset,n.length)}function Ur(n,t){
if(n!==t){var r=n!==F,e=null===n,u=n===n,i=Au(n),o=t!==F,f=null===t,c=t===t,a=Au(t);if(!f&&!a&&!i&&n>t||i&&o&&c&&!f&&!a||e&&o&&c||!r&&c||!u)return 1;if(!e&&!i&&!a&&n<t||a&&r&&u&&!e&&!i||f&&r&&u||!o&&u||!c)return-1}return 0}function Cr(n,t,r,e){var u=-1,i=n.length,o=r.length,f=-1,c=t.length,a=Di(i-o,0),l=Hu(c+a);for(e=!e;++f<c;)l[f]=t[f];for(;++u<o;)(e||u<i)&&(l[r[u]]=n[u]);for(;a--;)l[f++]=n[u++];return l}function Dr(n,t,r,e){var u=-1,i=n.length,o=-1,f=r.length,c=-1,a=t.length,l=Di(i-f,0),s=Hu(l+a);
for(e=!e;++u<l;)s[u]=n[u];for(l=u;++c<a;)s[l+c]=t[c];for(;++o<f;)(e||u<i)&&(s[l+r[o]]=n[u++]);return s}function Mr(n,t){var r=-1,e=n.length;for(t||(t=Hu(e));++r<e;)t[r]=n[r];return t}function Tr(n,t,r,e){var u=!r;r||(r={});for(var i=-1,o=t.length;++i<o;){var f=t[i],c=e?e(r[f],n[f],f,r,n):F;c===F&&(c=n[f]),u?_t(r,f,c):at(r,f,c)}return r}function $r(n,t){return Tr(n,vo(n),t)}function Fr(n,t){return Tr(n,go(n),t)}function Nr(n,t){return function(r,u){var i=af(r)?e:st,o=t?t():{};return i(r,n,je(u,2),o);
}}function Pr(n){return lr(function(t,r){var e=-1,u=r.length,i=1<u?r[u-1]:F,o=2<u?r[2]:F,i=3<n.length&&typeof i=="function"?(u--,i):F;for(o&&ze(r[0],r[1],o)&&(i=3>u?F:i,u=1),t=ni(t);++e<u;)(o=r[e])&&n(t,o,e,i);return t})}function Zr(n,t){return function(r,e){if(null==r)return r;if(!pu(r))return n(r,e);for(var u=r.length,i=t?u:-1,o=ni(r);(t?i--:++i<u)&&false!==e(o[i],i,o););return r}}function qr(n){return function(t,r,e){var u=-1,i=ni(t);e=e(t);for(var o=e.length;o--;){var f=e[n?o:++u];if(false===r(i[f],f,i))break;
}return t}}function Vr(n,t,r){function e(){return(this&&this!==Zn&&this instanceof e?i:n).apply(u?r:this,arguments)}var u=1&t,i=Hr(n);return e}function Kr(n){return function(t){t=zu(t);var r=Bn.test(t)?$(t):F,e=r?r[0]:t.charAt(0);return t=r?zr(r,1).join(""):t.slice(1),e[n]()+t}}function Gr(n){return function(t){return h($u(Tu(t).replace(In,"")),n,"")}}function Hr(n){return function(){var t=arguments;switch(t.length){case 0:return new n;case 1:return new n(t[0]);case 2:return new n(t[0],t[1]);case 3:
return new n(t[0],t[1],t[2]);case 4:return new n(t[0],t[1],t[2],t[3]);case 5:return new n(t[0],t[1],t[2],t[3],t[4]);case 6:return new n(t[0],t[1],t[2],t[3],t[4],t[5]);case 7:return new n(t[0],t[1],t[2],t[3],t[4],t[5],t[6])}var r=io(n.prototype),t=n.apply(r,t);return bu(t)?t:r}}function Jr(n,t,e){function u(){for(var o=arguments.length,f=Hu(o),c=o,a=xe(u);c--;)f[c]=arguments[c];return c=3>o&&f[0]!==a&&f[o-1]!==a?[]:C(f,a),o-=c.length,o<e?fe(n,t,Xr,u.placeholder,F,f,c,F,F,e-o):r(this&&this!==Zn&&this instanceof u?i:n,this,f);
}var i=Hr(n);return u}function Yr(n){return function(t,r,e){var u=ni(t);if(!pu(t)){var i=je(r,3);t=Lu(t),r=function(n){return i(u[n],n,u)}}return r=n(t,r,e),-1<r?u[i?t[r]:r]:F}}function Qr(n){return ge(function(t){var r=t.length,e=r,u=zn.prototype.thru;for(n&&t.reverse();e--;){var i=t[e];if(typeof i!="function")throw new ei("Expected a function");if(u&&!o&&"wrapper"==be(i))var o=new zn([],true)}for(e=o?e:r;++e<r;)var i=t[e],u=be(i),f="wrapper"==u?_o(i):F,o=f&&Be(f[0])&&424==f[1]&&!f[4].length&&1==f[9]?o[be(f[0])].apply(o,f[3]):1==i.length&&Be(i)?o[u]():o.thru(i);
return function(){var n=arguments,e=n[0];if(o&&1==n.length&&af(e))return o.plant(e).value();for(var u=0,n=r?t[u].apply(this,n):e;++u<r;)n=t[u].call(this,n);return n}})}function Xr(n,t,r,e,u,i,o,f,c,a){function l(){for(var d=arguments.length,y=Hu(d),b=d;b--;)y[b]=arguments[b];if(_){var x,j=xe(l),b=y.length;for(x=0;b--;)y[b]===j&&++x}if(e&&(y=Cr(y,e,u,_)),i&&(y=Dr(y,i,o,_)),d-=x,_&&d<a)return j=C(y,j),fe(n,t,Xr,l.placeholder,r,y,j,f,c,a-d);if(j=h?r:this,b=p?j[n]:n,d=y.length,f){x=y.length;for(var w=Mi(f.length,x),m=Mr(y);w--;){
var A=f[w];y[w]=Re(A,x)?m[A]:F}}else v&&1<d&&y.reverse();return s&&c<d&&(y.length=c),this&&this!==Zn&&this instanceof l&&(b=g||Hr(b)),b.apply(j,y)}var s=128&t,h=1&t,p=2&t,_=24&t,v=512&t,g=p?F:Hr(n);return l}function ne(n,t){return function(r,e){return Ct(r,n,t(e))}}function te(n,t){return function(r,e){var u;if(r===F&&e===F)return t;if(r!==F&&(u=r),e!==F){if(u===F)return e;typeof r=="string"||typeof e=="string"?(r=jr(r),e=jr(e)):(r=xr(r),e=xr(e)),u=n(r,e)}return u}}function re(n){return ge(function(t){
return t=l(t,S(je())),lr(function(e){var u=this;return n(t,function(n){return r(n,u,e)})})})}function ee(n,t){t=t===F?" ":jr(t);var r=t.length;return 2>r?r?ar(t,n):t:(r=ar(t,Ri(n/T(t))),Bn.test(t)?zr($(r),0,n).join(""):r.slice(0,n))}function ue(n,t,e,u){function i(){for(var t=-1,c=arguments.length,a=-1,l=u.length,s=Hu(l+c),h=this&&this!==Zn&&this instanceof i?f:n;++a<l;)s[a]=u[a];for(;c--;)s[a++]=arguments[++t];return r(h,o?e:this,s)}var o=1&t,f=Hr(n);return i}function ie(n){return function(t,r,e){
e&&typeof e!="number"&&ze(t,r,e)&&(r=e=F),t=Eu(t),r===F?(r=t,t=0):r=Eu(r),e=e===F?t<r?1:-1:Eu(e);var u=-1;r=Di(Ri((r-t)/(e||1)),0);for(var i=Hu(r);r--;)i[n?r:++u]=t,t+=e;return i}}function oe(n){return function(t,r){return typeof t=="string"&&typeof r=="string"||(t=Iu(t),r=Iu(r)),n(t,r)}}function fe(n,t,r,e,u,i,o,f,c,a){var l=8&t,s=l?o:F;o=l?F:o;var h=l?i:F;return i=l?F:i,t=(t|(l?32:64))&~(l?64:32),4&t||(t&=-4),u=[n,t,u,h,s,i,o,f,c,a],r=r.apply(F,u),Be(n)&&xo(r,u),r.placeholder=e,De(r,n,t)}function ce(n){
var t=Xu[n];return function(n,r){if(n=Iu(n),r=null==r?0:Mi(Ou(r),292)){var e=(zu(n)+"e").split("e"),e=t(e[0]+"e"+(+e[1]+r)),e=(zu(e)+"e").split("e");return+(e[0]+"e"+(+e[1]-r))}return t(n)}}function ae(n){return function(t){var r=yo(t);return"[object Map]"==r?L(t):"[object Set]"==r?M(t):O(t,n(t))}}function le(n,t,r,e,u,i,o,f){var c=2&t;if(!c&&typeof n!="function")throw new ei("Expected a function");var a=e?e.length:0;if(a||(t&=-97,e=u=F),o=o===F?o:Di(Ou(o),0),f=f===F?f:Ou(f),a-=u?u.length:0,64&t){
var l=e,s=u;e=u=F}var h=c?F:_o(n);return i=[n,t,r,e,u,l,s,i,o,f],h&&(r=i[1],n=h[1],t=r|n,e=128==n&&8==r||128==n&&256==r&&i[7].length<=h[8]||384==n&&h[7].length<=h[8]&&8==r,131>t||e)&&(1&n&&(i[2]=h[2],t|=1&r?0:4),(r=h[3])&&(e=i[3],i[3]=e?Cr(e,r,h[4]):r,i[4]=e?C(i[3],"__lodash_placeholder__"):h[4]),(r=h[5])&&(e=i[5],i[5]=e?Dr(e,r,h[6]):r,i[6]=e?C(i[5],"__lodash_placeholder__"):h[6]),(r=h[7])&&(i[7]=r),128&n&&(i[8]=null==i[8]?h[8]:Mi(i[8],h[8])),null==i[9]&&(i[9]=h[9]),i[0]=h[0],i[1]=t),n=i[0],t=i[1],
r=i[2],e=i[3],u=i[4],f=i[9]=i[9]===F?c?0:n.length:Di(i[9]-a,0),!f&&24&t&&(t&=-25),De((h?lo:xo)(t&&1!=t?8==t||16==t?Jr(n,t,f):32!=t&&33!=t||u.length?Xr.apply(F,i):ue(n,t,r,e):Vr(n,t,r),i),n,t)}function se(n,t,r,e){return n===F||hu(n,ii[r])&&!ci.call(e,r)?t:n}function he(n,t,r,e,u,i){return bu(n)&&bu(t)&&(i.set(t,n),nr(n,t,F,he,i),i.delete(t)),n}function pe(n){return wu(n)?F:n}function _e(n,t,r,e,u,i){var o=1&r,f=n.length,c=t.length;if(f!=c&&!(o&&c>f))return false;if((c=i.get(n))&&i.get(t))return c==t;var c=-1,a=true,l=2&r?new qn:F;
for(i.set(n,t),i.set(t,n);++c<f;){var s=n[c],h=t[c];if(e)var p=o?e(h,s,c,t,n,i):e(s,h,c,n,t,i);if(p!==F){if(p)continue;a=false;break}if(l){if(!_(t,function(n,t){if(!R(l,t)&&(s===n||u(s,n,r,e,i)))return l.push(t)})){a=false;break}}else if(s!==h&&!u(s,h,r,e,i)){a=false;break}}return i.delete(n),i.delete(t),a}function ve(n,t,r,e,u,i,o){switch(r){case"[object DataView]":if(n.byteLength!=t.byteLength||n.byteOffset!=t.byteOffset)break;n=n.buffer,t=t.buffer;case"[object ArrayBuffer]":if(n.byteLength!=t.byteLength||!i(new di(n),new di(t)))break;
return true;case"[object Boolean]":case"[object Date]":case"[object Number]":return hu(+n,+t);case"[object Error]":return n.name==t.name&&n.message==t.message;case"[object RegExp]":case"[object String]":return n==t+"";case"[object Map]":var f=L;case"[object Set]":if(f||(f=D),n.size!=t.size&&!(1&e))break;return(r=o.get(n))?r==t:(e|=2,o.set(n,t),t=_e(f(n),f(t),e,u,i,o),o.delete(n),t);case"[object Symbol]":if(eo)return eo.call(n)==eo.call(t)}return false}function ge(n){return wo(Ce(n,F,Ve),n+"")}function de(n){
return Rt(n,Lu,vo)}function ye(n){return Rt(n,Uu,go)}function be(n){for(var t=n.name+"",r=Ji[t],e=ci.call(Ji,t)?r.length:0;e--;){var u=r[e],i=u.func;if(null==i||i==n)return u.name}return t}function xe(n){return(ci.call(On,"placeholder")?On:n).placeholder}function je(){var n=On.iteratee||Pu,n=n===Pu?Gt:n;return arguments.length?n(arguments[0],arguments[1]):n}function we(n,t){var r=n.__data__,e=typeof t;return("string"==e||"number"==e||"symbol"==e||"boolean"==e?"__proto__"!==t:null===t)?r[typeof t=="string"?"string":"hash"]:r.map;
}function me(n){for(var t=Lu(n),r=t.length;r--;){var e=t[r],u=n[e];t[r]=[e,u,u===u&&!bu(u)]}return t}function Ae(n,t){var r=null==n?F:n[t];return Zt(r)?r:F}function ke(n,t,r){t=Rr(t,n);for(var e=-1,u=t.length,i=false;++e<u;){var o=$e(t[e]);if(!(i=null!=n&&r(n,o)))break;n=n[o]}return i||++e!=u?i:(u=null==n?0:n.length,!!u&&yu(u)&&Re(o,u)&&(af(n)||cf(n)))}function Ee(n){var t=n.length,r=n.constructor(t);return t&&"string"==typeof n[0]&&ci.call(n,"index")&&(r.index=n.index,r.input=n.input),r}function Oe(n){
return typeof n.constructor!="function"||Le(n)?{}:io(bi(n))}function Se(r,e,u,i){var o=r.constructor;switch(e){case"[object ArrayBuffer]":return Br(r);case"[object Boolean]":case"[object Date]":return new o(+r);case"[object DataView]":return e=i?Br(r.buffer):r.buffer,new r.constructor(e,r.byteOffset,r.byteLength);case"[object Float32Array]":case"[object Float64Array]":case"[object Int8Array]":case"[object Int16Array]":case"[object Int32Array]":case"[object Uint8Array]":case"[object Uint8ClampedArray]":
case"[object Uint16Array]":case"[object Uint32Array]":return Lr(r,i);case"[object Map]":return e=i?u(L(r),1):L(r),h(e,n,new r.constructor);case"[object Number]":case"[object String]":return new o(r);case"[object RegExp]":return e=new r.constructor(r.source,dn.exec(r)),e.lastIndex=r.lastIndex,e;case"[object Set]":return e=i?u(D(r),1):D(r),h(e,t,new r.constructor);case"[object Symbol]":return eo?ni(eo.call(r)):{}}}function Ie(n){return af(n)||cf(n)||!!(mi&&n&&n[mi])}function Re(n,t){return t=null==t?9007199254740991:t,
!!t&&(typeof n=="number"||wn.test(n))&&-1<n&&0==n%1&&n<t}function ze(n,t,r){if(!bu(r))return false;var e=typeof t;return!!("number"==e?pu(r)&&Re(t,r.length):"string"==e&&t in r)&&hu(r[t],n)}function We(n,t){if(af(n))return false;var r=typeof n;return!("number"!=r&&"symbol"!=r&&"boolean"!=r&&null!=n&&!Au(n))||(rn.test(n)||!tn.test(n)||null!=t&&n in ni(t))}function Be(n){var t=be(n),r=On[t];return typeof r=="function"&&t in Mn.prototype&&(n===r||(t=_o(r),!!t&&n===t[0]))}function Le(n){var t=n&&n.constructor;
return n===(typeof t=="function"&&t.prototype||ii)}function Ue(n,t){return function(r){return null!=r&&(r[n]===t&&(t!==F||n in ni(r)))}}function Ce(n,t,e){return t=Di(t===F?n.length-1:t,0),function(){for(var u=arguments,i=-1,o=Di(u.length-t,0),f=Hu(o);++i<o;)f[i]=u[t+i];for(i=-1,o=Hu(t+1);++i<t;)o[i]=u[i];return o[t]=e(f),r(n,this,o)}}function De(n,t,r){var e=t+"";t=wo;var u,i=Ne;return u=(u=e.match(hn))?u[1].split(pn):[],r=i(u,r),(i=r.length)&&(u=i-1,r[u]=(1<i?"& ":"")+r[u],r=r.join(2<i?", ":" "),
e=e.replace(sn,"{\n/* [wrapped with "+r+"] */\n")),t(n,e)}function Me(n){var t=0,r=0;return function(){var e=Ti(),u=16-(e-r);if(r=e,0<u){if(800<=++t)return arguments[0]}else t=0;return n.apply(F,arguments)}}function Te(n,t){var r=-1,e=n.length,u=e-1;for(t=t===F?e:t;++r<t;){var e=cr(r,u),i=n[e];n[e]=n[r],n[r]=i}return n.length=t,n}function $e(n){if(typeof n=="string"||Au(n))return n;var t=n+"";return"0"==t&&1/n==-N?"-0":t}function Fe(n){if(null!=n){try{return fi.call(n)}catch(n){}return n+""}return"";
}function Ne(n,t){return u(Z,function(r){var e="_."+r[0];t&r[1]&&!c(n,e)&&n.push(e)}),n.sort()}function Pe(n){if(n instanceof Mn)return n.clone();var t=new zn(n.__wrapped__,n.__chain__);return t.__actions__=Mr(n.__actions__),t.__index__=n.__index__,t.__values__=n.__values__,t}function Ze(n,t,r){var e=null==n?0:n.length;return e?(r=null==r?0:Ou(r),0>r&&(r=Di(e+r,0)),g(n,je(t,3),r)):-1}function qe(n,t,r){var e=null==n?0:n.length;if(!e)return-1;var u=e-1;return r!==F&&(u=Ou(r),u=0>r?Di(e+u,0):Mi(u,e-1)),
g(n,je(t,3),u,true)}function Ve(n){return(null==n?0:n.length)?kt(n,1):[]}function Ke(n){return n&&n.length?n[0]:F}function Ge(n){var t=null==n?0:n.length;return t?n[t-1]:F}function He(n,t){return n&&n.length&&t&&t.length?or(n,t):n}function Je(n){return null==n?n:Ni.call(n)}function Ye(n){if(!n||!n.length)return[];var t=0;return n=f(n,function(n){if(_u(n))return t=Di(n.length,t),true}),E(t,function(t){return l(n,j(t))})}function Qe(n,t){if(!n||!n.length)return[];var e=Ye(n);return null==t?e:l(e,function(n){
return r(t,F,n)})}function Xe(n){return n=On(n),n.__chain__=true,n}function nu(n,t){return t(n)}function tu(){return this}function ru(n,t){return(af(n)?u:oo)(n,je(t,3))}function eu(n,t){return(af(n)?i:fo)(n,je(t,3))}function uu(n,t){return(af(n)?l:Yt)(n,je(t,3))}function iu(n,t,r){return t=r?F:t,t=n&&null==t?n.length:t,le(n,128,F,F,F,F,t)}function ou(n,t){var r;if(typeof t!="function")throw new ei("Expected a function");return n=Ou(n),function(){return 0<--n&&(r=t.apply(this,arguments)),1>=n&&(t=F),
r}}function fu(n,t,r){return t=r?F:t,n=le(n,8,F,F,F,F,F,t),n.placeholder=fu.placeholder,n}function cu(n,t,r){return t=r?F:t,n=le(n,16,F,F,F,F,F,t),n.placeholder=cu.placeholder,n}function au(n,t,r){function e(t){var r=c,e=a;return c=a=F,_=t,s=n.apply(e,r)}function u(n){var r=n-p;return n-=_,p===F||r>=t||0>r||g&&n>=l}function i(){var n=Jo();if(u(n))return o(n);var r,e=jo;r=n-_,n=t-(n-p),r=g?Mi(n,l-r):n,h=e(i,r)}function o(n){return h=F,d&&c?e(n):(c=a=F,s)}function f(){var n=Jo(),r=u(n);if(c=arguments,
a=this,p=n,r){if(h===F)return _=n=p,h=jo(i,t),v?e(n):s;if(g)return h=jo(i,t),e(p)}return h===F&&(h=jo(i,t)),s}var c,a,l,s,h,p,_=0,v=false,g=false,d=true;if(typeof n!="function")throw new ei("Expected a function");return t=Iu(t)||0,bu(r)&&(v=!!r.leading,l=(g="maxWait"in r)?Di(Iu(r.maxWait)||0,t):l,d="trailing"in r?!!r.trailing:d),f.cancel=function(){h!==F&&ho(h),_=0,c=p=a=h=F},f.flush=function(){return h===F?s:o(Jo())},f}function lu(n,t){function r(){var e=arguments,u=t?t.apply(this,e):e[0],i=r.cache;return i.has(u)?i.get(u):(e=n.apply(this,e),
r.cache=i.set(u,e)||i,e)}if(typeof n!="function"||null!=t&&typeof t!="function")throw new ei("Expected a function");return r.cache=new(lu.Cache||Pn),r}function su(n){if(typeof n!="function")throw new ei("Expected a function");return function(){var t=arguments;switch(t.length){case 0:return!n.call(this);case 1:return!n.call(this,t[0]);case 2:return!n.call(this,t[0],t[1]);case 3:return!n.call(this,t[0],t[1],t[2])}return!n.apply(this,t)}}function hu(n,t){return n===t||n!==n&&t!==t}function pu(n){return null!=n&&yu(n.length)&&!gu(n);
}function _u(n){return xu(n)&&pu(n)}function vu(n){if(!xu(n))return false;var t=zt(n);return"[object Error]"==t||"[object DOMException]"==t||typeof n.message=="string"&&typeof n.name=="string"&&!wu(n)}function gu(n){return!!bu(n)&&(n=zt(n),"[object Function]"==n||"[object GeneratorFunction]"==n||"[object AsyncFunction]"==n||"[object Proxy]"==n)}function du(n){return typeof n=="number"&&n==Ou(n)}function yu(n){return typeof n=="number"&&-1<n&&0==n%1&&9007199254740991>=n}function bu(n){var t=typeof n;return null!=n&&("object"==t||"function"==t);
}function xu(n){return null!=n&&typeof n=="object"}function ju(n){return typeof n=="number"||xu(n)&&"[object Number]"==zt(n)}function wu(n){return!(!xu(n)||"[object Object]"!=zt(n))&&(n=bi(n),null===n||(n=ci.call(n,"constructor")&&n.constructor,typeof n=="function"&&n instanceof n&&fi.call(n)==hi))}function mu(n){return typeof n=="string"||!af(n)&&xu(n)&&"[object String]"==zt(n)}function Au(n){return typeof n=="symbol"||xu(n)&&"[object Symbol]"==zt(n)}function ku(n){if(!n)return[];if(pu(n))return mu(n)?$(n):Mr(n);
if(Ai&&n[Ai]){n=n[Ai]();for(var t,r=[];!(t=n.next()).done;)r.push(t.value);return r}return t=yo(n),("[object Map]"==t?L:"[object Set]"==t?D:Du)(n)}function Eu(n){return n?(n=Iu(n),n===N||n===-N?1.7976931348623157e308*(0>n?-1:1):n===n?n:0):0===n?n:0}function Ou(n){n=Eu(n);var t=n%1;return n===n?t?n-t:n:0}function Su(n){return n?gt(Ou(n),0,4294967295):0}function Iu(n){if(typeof n=="number")return n;if(Au(n))return P;if(bu(n)&&(n=typeof n.valueOf=="function"?n.valueOf():n,n=bu(n)?n+"":n),typeof n!="string")return 0===n?n:+n;
n=n.replace(cn,"");var t=bn.test(n);return t||jn.test(n)?Fn(n.slice(2),t?2:8):yn.test(n)?P:+n}function Ru(n){return Tr(n,Uu(n))}function zu(n){return null==n?"":jr(n)}function Wu(n,t,r){return n=null==n?F:It(n,t),n===F?r:n}function Bu(n,t){return null!=n&&ke(n,t,Lt)}function Lu(n){return pu(n)?Gn(n):Ht(n)}function Uu(n){if(pu(n))n=Gn(n,true);else if(bu(n)){var t,r=Le(n),e=[];for(t in n)("constructor"!=t||!r&&ci.call(n,t))&&e.push(t);n=e}else{if(t=[],null!=n)for(r in ni(n))t.push(r);n=t}return n}function Cu(n,t){
if(null==n)return{};var r=l(ye(n),function(n){return[n]});return t=je(t),ur(n,r,function(n,r){return t(n,r[0])})}function Du(n){return null==n?[]:I(n,Lu(n))}function Mu(n){return Nf(zu(n).toLowerCase())}function Tu(n){return(n=zu(n))&&n.replace(mn,rt).replace(Rn,"")}function $u(n,t,r){return n=zu(n),t=r?F:t,t===F?Ln.test(n)?n.match(Wn)||[]:n.match(_n)||[]:n.match(t)||[]}function Fu(n){return function(){return n}}function Nu(n){return n}function Pu(n){return Gt(typeof n=="function"?n:dt(n,1))}function Zu(n,t,r){
var e=Lu(t),i=St(t,e);null!=r||bu(t)&&(i.length||!e.length)||(r=t,t=n,n=this,i=St(t,Lu(t)));var o=!(bu(r)&&"chain"in r&&!r.chain),f=gu(n);return u(i,function(r){var e=t[r];n[r]=e,f&&(n.prototype[r]=function(){var t=this.__chain__;if(o||t){var r=n(this.__wrapped__);return(r.__actions__=Mr(this.__actions__)).push({func:e,args:arguments,thisArg:n}),r.__chain__=t,r}return e.apply(n,s([this.value()],arguments))})}),n}function qu(){}function Vu(n){return We(n)?j($e(n)):ir(n)}function Ku(){return[]}function Gu(){
return false}En=null==En?Zn:it.defaults(Zn.Object(),En,it.pick(Zn,Un));var Hu=En.Array,Ju=En.Date,Yu=En.Error,Qu=En.Function,Xu=En.Math,ni=En.Object,ti=En.RegExp,ri=En.String,ei=En.TypeError,ui=Hu.prototype,ii=ni.prototype,oi=En["__core-js_shared__"],fi=Qu.prototype.toString,ci=ii.hasOwnProperty,ai=0,li=function(){var n=/[^.]+$/.exec(oi&&oi.keys&&oi.keys.IE_PROTO||"");return n?"Symbol(src)_1."+n:""}(),si=ii.toString,hi=fi.call(ni),pi=Zn._,_i=ti("^"+fi.call(ci).replace(on,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),vi=Kn?En.Buffer:F,gi=En.Symbol,di=En.Uint8Array,yi=vi?vi.f:F,bi=U(ni.getPrototypeOf,ni),xi=ni.create,ji=ii.propertyIsEnumerable,wi=ui.splice,mi=gi?gi.isConcatSpreadable:F,Ai=gi?gi.iterator:F,ki=gi?gi.toStringTag:F,Ei=function(){
try{var n=Ae(ni,"defineProperty");return n({},"",{}),n}catch(n){}}(),Oi=En.clearTimeout!==Zn.clearTimeout&&En.clearTimeout,Si=Ju&&Ju.now!==Zn.Date.now&&Ju.now,Ii=En.setTimeout!==Zn.setTimeout&&En.setTimeout,Ri=Xu.ceil,zi=Xu.floor,Wi=ni.getOwnPropertySymbols,Bi=vi?vi.isBuffer:F,Li=En.isFinite,Ui=ui.join,Ci=U(ni.keys,ni),Di=Xu.max,Mi=Xu.min,Ti=Ju.now,$i=En.parseInt,Fi=Xu.random,Ni=ui.reverse,Pi=Ae(En,"DataView"),Zi=Ae(En,"Map"),qi=Ae(En,"Promise"),Vi=Ae(En,"Set"),Ki=Ae(En,"WeakMap"),Gi=Ae(ni,"create"),Hi=Ki&&new Ki,Ji={},Yi=Fe(Pi),Qi=Fe(Zi),Xi=Fe(qi),no=Fe(Vi),to=Fe(Ki),ro=gi?gi.prototype:F,eo=ro?ro.valueOf:F,uo=ro?ro.toString:F,io=function(){
function n(){}return function(t){return bu(t)?xi?xi(t):(n.prototype=t,t=new n,n.prototype=F,t):{}}}();On.templateSettings={escape:Q,evaluate:X,interpolate:nn,variable:"",imports:{_:On}},On.prototype=Sn.prototype,On.prototype.constructor=On,zn.prototype=io(Sn.prototype),zn.prototype.constructor=zn,Mn.prototype=io(Sn.prototype),Mn.prototype.constructor=Mn,Tn.prototype.clear=function(){this.__data__=Gi?Gi(null):{},this.size=0},Tn.prototype.delete=function(n){return n=this.has(n)&&delete this.__data__[n],
this.size-=n?1:0,n},Tn.prototype.get=function(n){var t=this.__data__;return Gi?(n=t[n],"__lodash_hash_undefined__"===n?F:n):ci.call(t,n)?t[n]:F},Tn.prototype.has=function(n){var t=this.__data__;return Gi?t[n]!==F:ci.call(t,n)},Tn.prototype.set=function(n,t){var r=this.__data__;return this.size+=this.has(n)?0:1,r[n]=Gi&&t===F?"__lodash_hash_undefined__":t,this},Nn.prototype.clear=function(){this.__data__=[],this.size=0},Nn.prototype.delete=function(n){var t=this.__data__;return n=lt(t,n),!(0>n)&&(n==t.length-1?t.pop():wi.call(t,n,1),
--this.size,true)},Nn.prototype.get=function(n){var t=this.__data__;return n=lt(t,n),0>n?F:t[n][1]},Nn.prototype.has=function(n){return-1<lt(this.__data__,n)},Nn.prototype.set=function(n,t){var r=this.__data__,e=lt(r,n);return 0>e?(++this.size,r.push([n,t])):r[e][1]=t,this},Pn.prototype.clear=function(){this.size=0,this.__data__={hash:new Tn,map:new(Zi||Nn),string:new Tn}},Pn.prototype.delete=function(n){return n=we(this,n).delete(n),this.size-=n?1:0,n},Pn.prototype.get=function(n){return we(this,n).get(n);
},Pn.prototype.has=function(n){return we(this,n).has(n)},Pn.prototype.set=function(n,t){var r=we(this,n),e=r.size;return r.set(n,t),this.size+=r.size==e?0:1,this},qn.prototype.add=qn.prototype.push=function(n){return this.__data__.set(n,"__lodash_hash_undefined__"),this},qn.prototype.has=function(n){return this.__data__.has(n)},Vn.prototype.clear=function(){this.__data__=new Nn,this.size=0},Vn.prototype.delete=function(n){var t=this.__data__;return n=t.delete(n),this.size=t.size,n},Vn.prototype.get=function(n){
return this.__data__.get(n)},Vn.prototype.has=function(n){return this.__data__.has(n)},Vn.prototype.set=function(n,t){var r=this.__data__;if(r instanceof Nn){var e=r.__data__;if(!Zi||199>e.length)return e.push([n,t]),this.size=++r.size,this;r=this.__data__=new Pn(e)}return r.set(n,t),this.size=r.size,this};var oo=Zr(Et),fo=Zr(Ot,true),co=qr(),ao=qr(true),lo=Hi?function(n,t){return Hi.set(n,t),n}:Nu,so=Ei?function(n,t){return Ei(n,"toString",{configurable:true,enumerable:false,value:Fu(t),writable:true})}:Nu,ho=Oi||function(n){
return Zn.clearTimeout(n)},po=Vi&&1/D(new Vi([,-0]))[1]==N?function(n){return new Vi(n)}:qu,_o=Hi?function(n){return Hi.get(n)}:qu,vo=Wi?function(n){return null==n?[]:(n=ni(n),f(Wi(n),function(t){return ji.call(n,t)}))}:Ku,go=Wi?function(n){for(var t=[];n;)s(t,vo(n)),n=bi(n);return t}:Ku,yo=zt;(Pi&&"[object DataView]"!=yo(new Pi(new ArrayBuffer(1)))||Zi&&"[object Map]"!=yo(new Zi)||qi&&"[object Promise]"!=yo(qi.resolve())||Vi&&"[object Set]"!=yo(new Vi)||Ki&&"[object WeakMap]"!=yo(new Ki))&&(yo=function(n){
var t=zt(n);if(n=(n="[object Object]"==t?n.constructor:F)?Fe(n):"")switch(n){case Yi:return"[object DataView]";case Qi:return"[object Map]";case Xi:return"[object Promise]";case no:return"[object Set]";case to:return"[object WeakMap]"}return t});var bo=oi?gu:Gu,xo=Me(lo),jo=Ii||function(n,t){return Zn.setTimeout(n,t)},wo=Me(so),mo=function(n){n=lu(n,function(n){return 500===t.size&&t.clear(),n});var t=n.cache;return n}(function(n){var t=[];return en.test(n)&&t.push(""),n.replace(un,function(n,r,e,u){
t.push(e?u.replace(vn,"$1"):r||n)}),t}),Ao=lr(function(n,t){return _u(n)?jt(n,kt(t,1,_u,true)):[]}),ko=lr(function(n,t){var r=Ge(t);return _u(r)&&(r=F),_u(n)?jt(n,kt(t,1,_u,true),je(r,2)):[]}),Eo=lr(function(n,t){var r=Ge(t);return _u(r)&&(r=F),_u(n)?jt(n,kt(t,1,_u,true),F,r):[]}),Oo=lr(function(n){var t=l(n,Sr);return t.length&&t[0]===n[0]?Ut(t):[]}),So=lr(function(n){var t=Ge(n),r=l(n,Sr);return t===Ge(r)?t=F:r.pop(),r.length&&r[0]===n[0]?Ut(r,je(t,2)):[]}),Io=lr(function(n){var t=Ge(n),r=l(n,Sr);return(t=typeof t=="function"?t:F)&&r.pop(),
r.length&&r[0]===n[0]?Ut(r,F,t):[]}),Ro=lr(He),zo=ge(function(n,t){var r=null==n?0:n.length,e=vt(n,t);return fr(n,l(t,function(n){return Re(n,r)?+n:n}).sort(Ur)),e}),Wo=lr(function(n){return wr(kt(n,1,_u,true))}),Bo=lr(function(n){var t=Ge(n);return _u(t)&&(t=F),wr(kt(n,1,_u,true),je(t,2))}),Lo=lr(function(n){var t=Ge(n),t=typeof t=="function"?t:F;return wr(kt(n,1,_u,true),F,t)}),Uo=lr(function(n,t){return _u(n)?jt(n,t):[]}),Co=lr(function(n){return Er(f(n,_u))}),Do=lr(function(n){var t=Ge(n);return _u(t)&&(t=F),
Er(f(n,_u),je(t,2))}),Mo=lr(function(n){var t=Ge(n),t=typeof t=="function"?t:F;return Er(f(n,_u),F,t)}),To=lr(Ye),$o=lr(function(n){var t=n.length,t=1<t?n[t-1]:F,t=typeof t=="function"?(n.pop(),t):F;return Qe(n,t)}),Fo=ge(function(n){function t(t){return vt(t,n)}var r=n.length,e=r?n[0]:0,u=this.__wrapped__;return!(1<r||this.__actions__.length)&&u instanceof Mn&&Re(e)?(u=u.slice(e,+e+(r?1:0)),u.__actions__.push({func:nu,args:[t],thisArg:F}),new zn(u,this.__chain__).thru(function(n){return r&&!n.length&&n.push(F),
n})):this.thru(t)}),No=Nr(function(n,t,r){ci.call(n,r)?++n[r]:_t(n,r,1)}),Po=Yr(Ze),Zo=Yr(qe),qo=Nr(function(n,t,r){ci.call(n,r)?n[r].push(t):_t(n,r,[t])}),Vo=lr(function(n,t,e){var u=-1,i=typeof t=="function",o=pu(n)?Hu(n.length):[];return oo(n,function(n){o[++u]=i?r(t,n,e):Dt(n,t,e)}),o}),Ko=Nr(function(n,t,r){_t(n,r,t)}),Go=Nr(function(n,t,r){n[r?0:1].push(t)},function(){return[[],[]]}),Ho=lr(function(n,t){if(null==n)return[];var r=t.length;return 1<r&&ze(n,t[0],t[1])?t=[]:2<r&&ze(t[0],t[1],t[2])&&(t=[t[0]]),
rr(n,kt(t,1),[])}),Jo=Si||function(){return Zn.Date.now()},Yo=lr(function(n,t,r){var e=1;if(r.length)var u=C(r,xe(Yo)),e=32|e;return le(n,e,t,r,u)}),Qo=lr(function(n,t,r){var e=3;if(r.length)var u=C(r,xe(Qo)),e=32|e;return le(t,e,n,r,u)}),Xo=lr(function(n,t){return xt(n,1,t)}),nf=lr(function(n,t,r){return xt(n,Iu(t)||0,r)});lu.Cache=Pn;var tf=lr(function(n,t){t=1==t.length&&af(t[0])?l(t[0],S(je())):l(kt(t,1),S(je()));var e=t.length;return lr(function(u){for(var i=-1,o=Mi(u.length,e);++i<o;)u[i]=t[i].call(this,u[i]);
return r(n,this,u)})}),rf=lr(function(n,t){return le(n,32,F,t,C(t,xe(rf)))}),ef=lr(function(n,t){return le(n,64,F,t,C(t,xe(ef)))}),uf=ge(function(n,t){return le(n,256,F,F,F,t)}),of=oe(Wt),ff=oe(function(n,t){return n>=t}),cf=Mt(function(){return arguments}())?Mt:function(n){return xu(n)&&ci.call(n,"callee")&&!ji.call(n,"callee")},af=Hu.isArray,lf=Hn?S(Hn):Tt,sf=Bi||Gu,hf=Jn?S(Jn):$t,pf=Yn?S(Yn):Nt,_f=Qn?S(Qn):qt,vf=Xn?S(Xn):Vt,gf=nt?S(nt):Kt,df=oe(Jt),yf=oe(function(n,t){return n<=t}),bf=Pr(function(n,t){
if(Le(t)||pu(t))Tr(t,Lu(t),n);else for(var r in t)ci.call(t,r)&&at(n,r,t[r])}),xf=Pr(function(n,t){Tr(t,Uu(t),n)}),jf=Pr(function(n,t,r,e){Tr(t,Uu(t),n,e)}),wf=Pr(function(n,t,r,e){Tr(t,Lu(t),n,e)}),mf=ge(vt),Af=lr(function(n){return n.push(F,se),r(jf,F,n)}),kf=lr(function(n){return n.push(F,he),r(Rf,F,n)}),Ef=ne(function(n,t,r){n[t]=r},Fu(Nu)),Of=ne(function(n,t,r){ci.call(n,t)?n[t].push(r):n[t]=[r]},je),Sf=lr(Dt),If=Pr(function(n,t,r){nr(n,t,r)}),Rf=Pr(function(n,t,r,e){nr(n,t,r,e)}),zf=ge(function(n,t){
var r={};if(null==n)return r;var e=false;t=l(t,function(t){return t=Rr(t,n),e||(e=1<t.length),t}),Tr(n,ye(n),r),e&&(r=dt(r,7,pe));for(var u=t.length;u--;)mr(r,t[u]);return r}),Wf=ge(function(n,t){return null==n?{}:er(n,t)}),Bf=ae(Lu),Lf=ae(Uu),Uf=Gr(function(n,t,r){return t=t.toLowerCase(),n+(r?Mu(t):t)}),Cf=Gr(function(n,t,r){return n+(r?"-":"")+t.toLowerCase()}),Df=Gr(function(n,t,r){return n+(r?" ":"")+t.toLowerCase()}),Mf=Kr("toLowerCase"),Tf=Gr(function(n,t,r){return n+(r?"_":"")+t.toLowerCase();
}),$f=Gr(function(n,t,r){return n+(r?" ":"")+Nf(t)}),Ff=Gr(function(n,t,r){return n+(r?" ":"")+t.toUpperCase()}),Nf=Kr("toUpperCase"),Pf=lr(function(n,t){try{return r(n,F,t)}catch(n){return vu(n)?n:new Yu(n)}}),Zf=ge(function(n,t){return u(t,function(t){t=$e(t),_t(n,t,Yo(n[t],n))}),n}),qf=Qr(),Vf=Qr(true),Kf=lr(function(n,t){return function(r){return Dt(r,n,t)}}),Gf=lr(function(n,t){return function(r){return Dt(n,r,t)}}),Hf=re(l),Jf=re(o),Yf=re(_),Qf=ie(),Xf=ie(true),nc=te(function(n,t){return n+t},0),tc=ce("ceil"),rc=te(function(n,t){
return n/t},1),ec=ce("floor"),uc=te(function(n,t){return n*t},1),ic=ce("round"),oc=te(function(n,t){return n-t},0);return On.after=function(n,t){if(typeof t!="function")throw new ei("Expected a function");return n=Ou(n),function(){if(1>--n)return t.apply(this,arguments)}},On.ary=iu,On.assign=bf,On.assignIn=xf,On.assignInWith=jf,On.assignWith=wf,On.at=mf,On.before=ou,On.bind=Yo,On.bindAll=Zf,On.bindKey=Qo,On.castArray=function(){if(!arguments.length)return[];var n=arguments[0];return af(n)?n:[n]},
On.chain=Xe,On.chunk=function(n,t,r){if(t=(r?ze(n,t,r):t===F)?1:Di(Ou(t),0),r=null==n?0:n.length,!r||1>t)return[];for(var e=0,u=0,i=Hu(Ri(r/t));e<r;)i[u++]=vr(n,e,e+=t);return i},On.compact=function(n){for(var t=-1,r=null==n?0:n.length,e=0,u=[];++t<r;){var i=n[t];i&&(u[e++]=i)}return u},On.concat=function(){var n=arguments.length;if(!n)return[];for(var t=Hu(n-1),r=arguments[0];n--;)t[n-1]=arguments[n];return s(af(r)?Mr(r):[r],kt(t,1))},On.cond=function(n){var t=null==n?0:n.length,e=je();return n=t?l(n,function(n){
if("function"!=typeof n[1])throw new ei("Expected a function");return[e(n[0]),n[1]]}):[],lr(function(e){for(var u=-1;++u<t;){var i=n[u];if(r(i[0],this,e))return r(i[1],this,e)}})},On.conforms=function(n){return yt(dt(n,1))},On.constant=Fu,On.countBy=No,On.create=function(n,t){var r=io(n);return null==t?r:ht(r,t)},On.curry=fu,On.curryRight=cu,On.debounce=au,On.defaults=Af,On.defaultsDeep=kf,On.defer=Xo,On.delay=nf,On.difference=Ao,On.differenceBy=ko,On.differenceWith=Eo,On.drop=function(n,t,r){var e=null==n?0:n.length;
return e?(t=r||t===F?1:Ou(t),vr(n,0>t?0:t,e)):[]},On.dropRight=function(n,t,r){var e=null==n?0:n.length;return e?(t=r||t===F?1:Ou(t),t=e-t,vr(n,0,0>t?0:t)):[]},On.dropRightWhile=function(n,t){return n&&n.length?Ar(n,je(t,3),true,true):[]},On.dropWhile=function(n,t){return n&&n.length?Ar(n,je(t,3),true):[]},On.fill=function(n,t,r,e){var u=null==n?0:n.length;if(!u)return[];for(r&&typeof r!="number"&&ze(n,t,r)&&(r=0,e=u),u=n.length,r=Ou(r),0>r&&(r=-r>u?0:u+r),e=e===F||e>u?u:Ou(e),0>e&&(e+=u),e=r>e?0:Su(e);r<e;)n[r++]=t;
return n},On.filter=function(n,t){return(af(n)?f:At)(n,je(t,3))},On.flatMap=function(n,t){return kt(uu(n,t),1)},On.flatMapDeep=function(n,t){return kt(uu(n,t),N)},On.flatMapDepth=function(n,t,r){return r=r===F?1:Ou(r),kt(uu(n,t),r)},On.flatten=Ve,On.flattenDeep=function(n){return(null==n?0:n.length)?kt(n,N):[]},On.flattenDepth=function(n,t){return null!=n&&n.length?(t=t===F?1:Ou(t),kt(n,t)):[]},On.flip=function(n){return le(n,512)},On.flow=qf,On.flowRight=Vf,On.fromPairs=function(n){for(var t=-1,r=null==n?0:n.length,e={};++t<r;){
var u=n[t];e[u[0]]=u[1]}return e},On.functions=function(n){return null==n?[]:St(n,Lu(n))},On.functionsIn=function(n){return null==n?[]:St(n,Uu(n))},On.groupBy=qo,On.initial=function(n){return(null==n?0:n.length)?vr(n,0,-1):[]},On.intersection=Oo,On.intersectionBy=So,On.intersectionWith=Io,On.invert=Ef,On.invertBy=Of,On.invokeMap=Vo,On.iteratee=Pu,On.keyBy=Ko,On.keys=Lu,On.keysIn=Uu,On.map=uu,On.mapKeys=function(n,t){var r={};return t=je(t,3),Et(n,function(n,e,u){_t(r,t(n,e,u),n)}),r},On.mapValues=function(n,t){
var r={};return t=je(t,3),Et(n,function(n,e,u){_t(r,e,t(n,e,u))}),r},On.matches=function(n){return Qt(dt(n,1))},On.matchesProperty=function(n,t){return Xt(n,dt(t,1))},On.memoize=lu,On.merge=If,On.mergeWith=Rf,On.method=Kf,On.methodOf=Gf,On.mixin=Zu,On.negate=su,On.nthArg=function(n){return n=Ou(n),lr(function(t){return tr(t,n)})},On.omit=zf,On.omitBy=function(n,t){return Cu(n,su(je(t)))},On.once=function(n){return ou(2,n)},On.orderBy=function(n,t,r,e){return null==n?[]:(af(t)||(t=null==t?[]:[t]),
r=e?F:r,af(r)||(r=null==r?[]:[r]),rr(n,t,r))},On.over=Hf,On.overArgs=tf,On.overEvery=Jf,On.overSome=Yf,On.partial=rf,On.partialRight=ef,On.partition=Go,On.pick=Wf,On.pickBy=Cu,On.property=Vu,On.propertyOf=function(n){return function(t){return null==n?F:It(n,t)}},On.pull=Ro,On.pullAll=He,On.pullAllBy=function(n,t,r){return n&&n.length&&t&&t.length?or(n,t,je(r,2)):n},On.pullAllWith=function(n,t,r){return n&&n.length&&t&&t.length?or(n,t,F,r):n},On.pullAt=zo,On.range=Qf,On.rangeRight=Xf,On.rearg=uf,On.reject=function(n,t){
return(af(n)?f:At)(n,su(je(t,3)))},On.remove=function(n,t){var r=[];if(!n||!n.length)return r;var e=-1,u=[],i=n.length;for(t=je(t,3);++e<i;){var o=n[e];t(o,e,n)&&(r.push(o),u.push(e))}return fr(n,u),r},On.rest=function(n,t){if(typeof n!="function")throw new ei("Expected a function");return t=t===F?t:Ou(t),lr(n,t)},On.reverse=Je,On.sampleSize=function(n,t,r){return t=(r?ze(n,t,r):t===F)?1:Ou(t),(af(n)?ot:hr)(n,t)},On.set=function(n,t,r){return null==n?n:pr(n,t,r)},On.setWith=function(n,t,r,e){return e=typeof e=="function"?e:F,
null==n?n:pr(n,t,r,e)},On.shuffle=function(n){return(af(n)?ft:_r)(n)},On.slice=function(n,t,r){var e=null==n?0:n.length;return e?(r&&typeof r!="number"&&ze(n,t,r)?(t=0,r=e):(t=null==t?0:Ou(t),r=r===F?e:Ou(r)),vr(n,t,r)):[]},On.sortBy=Ho,On.sortedUniq=function(n){return n&&n.length?br(n):[]},On.sortedUniqBy=function(n,t){return n&&n.length?br(n,je(t,2)):[]},On.split=function(n,t,r){return r&&typeof r!="number"&&ze(n,t,r)&&(t=r=F),r=r===F?4294967295:r>>>0,r?(n=zu(n))&&(typeof t=="string"||null!=t&&!_f(t))&&(t=jr(t),
!t&&Bn.test(n))?zr($(n),0,r):n.split(t,r):[]},On.spread=function(n,t){if(typeof n!="function")throw new ei("Expected a function");return t=null==t?0:Di(Ou(t),0),lr(function(e){var u=e[t];return e=zr(e,0,t),u&&s(e,u),r(n,this,e)})},On.tail=function(n){var t=null==n?0:n.length;return t?vr(n,1,t):[]},On.take=function(n,t,r){return n&&n.length?(t=r||t===F?1:Ou(t),vr(n,0,0>t?0:t)):[]},On.takeRight=function(n,t,r){var e=null==n?0:n.length;return e?(t=r||t===F?1:Ou(t),t=e-t,vr(n,0>t?0:t,e)):[]},On.takeRightWhile=function(n,t){
return n&&n.length?Ar(n,je(t,3),false,true):[]},On.takeWhile=function(n,t){return n&&n.length?Ar(n,je(t,3)):[]},On.tap=function(n,t){return t(n),n},On.throttle=function(n,t,r){var e=true,u=true;if(typeof n!="function")throw new ei("Expected a function");return bu(r)&&(e="leading"in r?!!r.leading:e,u="trailing"in r?!!r.trailing:u),au(n,t,{leading:e,maxWait:t,trailing:u})},On.thru=nu,On.toArray=ku,On.toPairs=Bf,On.toPairsIn=Lf,On.toPath=function(n){return af(n)?l(n,$e):Au(n)?[n]:Mr(mo(zu(n)))},On.toPlainObject=Ru,
On.transform=function(n,t,r){var e=af(n),i=e||sf(n)||gf(n);if(t=je(t,4),null==r){var o=n&&n.constructor;r=i?e?new o:[]:bu(n)&&gu(o)?io(bi(n)):{}}return(i?u:Et)(n,function(n,e,u){return t(r,n,e,u)}),r},On.unary=function(n){return iu(n,1)},On.union=Wo,On.unionBy=Bo,On.unionWith=Lo,On.uniq=function(n){return n&&n.length?wr(n):[]},On.uniqBy=function(n,t){return n&&n.length?wr(n,je(t,2)):[]},On.uniqWith=function(n,t){return t=typeof t=="function"?t:F,n&&n.length?wr(n,F,t):[]},On.unset=function(n,t){return null==n||mr(n,t);
},On.unzip=Ye,On.unzipWith=Qe,On.update=function(n,t,r){return null==n?n:pr(n,t,Ir(r)(It(n,t)),void 0)},On.updateWith=function(n,t,r,e){return e=typeof e=="function"?e:F,null!=n&&(n=pr(n,t,Ir(r)(It(n,t)),e)),n},On.values=Du,On.valuesIn=function(n){return null==n?[]:I(n,Uu(n))},On.without=Uo,On.words=$u,On.wrap=function(n,t){return rf(Ir(t),n)},On.xor=Co,On.xorBy=Do,On.xorWith=Mo,On.zip=To,On.zipObject=function(n,t){return Or(n||[],t||[],at)},On.zipObjectDeep=function(n,t){return Or(n||[],t||[],pr);
},On.zipWith=$o,On.entries=Bf,On.entriesIn=Lf,On.extend=xf,On.extendWith=jf,Zu(On,On),On.add=nc,On.attempt=Pf,On.camelCase=Uf,On.capitalize=Mu,On.ceil=tc,On.clamp=function(n,t,r){return r===F&&(r=t,t=F),r!==F&&(r=Iu(r),r=r===r?r:0),t!==F&&(t=Iu(t),t=t===t?t:0),gt(Iu(n),t,r)},On.clone=function(n){return dt(n,4)},On.cloneDeep=function(n){return dt(n,5)},On.cloneDeepWith=function(n,t){return t=typeof t=="function"?t:F,dt(n,5,t)},On.cloneWith=function(n,t){return t=typeof t=="function"?t:F,dt(n,4,t)},
On.conformsTo=function(n,t){return null==t||bt(n,t,Lu(t))},On.deburr=Tu,On.defaultTo=function(n,t){return null==n||n!==n?t:n},On.divide=rc,On.endsWith=function(n,t,r){n=zu(n),t=jr(t);var e=n.length,e=r=r===F?e:gt(Ou(r),0,e);return r-=t.length,0<=r&&n.slice(r,e)==t},On.eq=hu,On.escape=function(n){return(n=zu(n))&&Y.test(n)?n.replace(H,et):n},On.escapeRegExp=function(n){return(n=zu(n))&&fn.test(n)?n.replace(on,"\\$&"):n},On.every=function(n,t,r){var e=af(n)?o:wt;return r&&ze(n,t,r)&&(t=F),e(n,je(t,3));
},On.find=Po,On.findIndex=Ze,On.findKey=function(n,t){return v(n,je(t,3),Et)},On.findLast=Zo,On.findLastIndex=qe,On.findLastKey=function(n,t){return v(n,je(t,3),Ot)},On.floor=ec,On.forEach=ru,On.forEachRight=eu,On.forIn=function(n,t){return null==n?n:co(n,je(t,3),Uu)},On.forInRight=function(n,t){return null==n?n:ao(n,je(t,3),Uu)},On.forOwn=function(n,t){return n&&Et(n,je(t,3))},On.forOwnRight=function(n,t){return n&&Ot(n,je(t,3))},On.get=Wu,On.gt=of,On.gte=ff,On.has=function(n,t){return null!=n&&ke(n,t,Bt);
},On.hasIn=Bu,On.head=Ke,On.identity=Nu,On.includes=function(n,t,r,e){return n=pu(n)?n:Du(n),r=r&&!e?Ou(r):0,e=n.length,0>r&&(r=Di(e+r,0)),mu(n)?r<=e&&-1<n.indexOf(t,r):!!e&&-1<d(n,t,r)},On.indexOf=function(n,t,r){var e=null==n?0:n.length;return e?(r=null==r?0:Ou(r),0>r&&(r=Di(e+r,0)),d(n,t,r)):-1},On.inRange=function(n,t,r){return t=Eu(t),r===F?(r=t,t=0):r=Eu(r),n=Iu(n),n>=Mi(t,r)&&n<Di(t,r)},On.invoke=Sf,On.isArguments=cf,On.isArray=af,On.isArrayBuffer=lf,On.isArrayLike=pu,On.isArrayLikeObject=_u,
On.isBoolean=function(n){return true===n||false===n||xu(n)&&"[object Boolean]"==zt(n)},On.isBuffer=sf,On.isDate=hf,On.isElement=function(n){return xu(n)&&1===n.nodeType&&!wu(n)},On.isEmpty=function(n){if(null==n)return true;if(pu(n)&&(af(n)||typeof n=="string"||typeof n.splice=="function"||sf(n)||gf(n)||cf(n)))return!n.length;var t=yo(n);if("[object Map]"==t||"[object Set]"==t)return!n.size;if(Le(n))return!Ht(n).length;for(var r in n)if(ci.call(n,r))return false;return true},On.isEqual=function(n,t){return Ft(n,t);
},On.isEqualWith=function(n,t,r){var e=(r=typeof r=="function"?r:F)?r(n,t):F;return e===F?Ft(n,t,F,r):!!e},On.isError=vu,On.isFinite=function(n){return typeof n=="number"&&Li(n)},On.isFunction=gu,On.isInteger=du,On.isLength=yu,On.isMap=pf,On.isMatch=function(n,t){return n===t||Pt(n,t,me(t))},On.isMatchWith=function(n,t,r){return r=typeof r=="function"?r:F,Pt(n,t,me(t),r)},On.isNaN=function(n){return ju(n)&&n!=+n},On.isNative=function(n){if(bo(n))throw new Yu("Unsupported core-js use. Try https://npms.io/search?q=ponyfill.");
return Zt(n)},On.isNil=function(n){return null==n},On.isNull=function(n){return null===n},On.isNumber=ju,On.isObject=bu,On.isObjectLike=xu,On.isPlainObject=wu,On.isRegExp=_f,On.isSafeInteger=function(n){return du(n)&&-9007199254740991<=n&&9007199254740991>=n},On.isSet=vf,On.isString=mu,On.isSymbol=Au,On.isTypedArray=gf,On.isUndefined=function(n){return n===F},On.isWeakMap=function(n){return xu(n)&&"[object WeakMap]"==yo(n)},On.isWeakSet=function(n){return xu(n)&&"[object WeakSet]"==zt(n)},On.join=function(n,t){
return null==n?"":Ui.call(n,t)},On.kebabCase=Cf,On.last=Ge,On.lastIndexOf=function(n,t,r){var e=null==n?0:n.length;if(!e)return-1;var u=e;if(r!==F&&(u=Ou(r),u=0>u?Di(e+u,0):Mi(u,e-1)),t===t){for(r=u+1;r--&&n[r]!==t;);n=r}else n=g(n,b,u,true);return n},On.lowerCase=Df,On.lowerFirst=Mf,On.lt=df,On.lte=yf,On.max=function(n){return n&&n.length?mt(n,Nu,Wt):F},On.maxBy=function(n,t){return n&&n.length?mt(n,je(t,2),Wt):F},On.mean=function(n){return x(n,Nu)},On.meanBy=function(n,t){return x(n,je(t,2))},On.min=function(n){
return n&&n.length?mt(n,Nu,Jt):F},On.minBy=function(n,t){return n&&n.length?mt(n,je(t,2),Jt):F},On.stubArray=Ku,On.stubFalse=Gu,On.stubObject=function(){return{}},On.stubString=function(){return""},On.stubTrue=function(){return true},On.multiply=uc,On.nth=function(n,t){return n&&n.length?tr(n,Ou(t)):F},On.noConflict=function(){return Zn._===this&&(Zn._=pi),this},On.noop=qu,On.now=Jo,On.pad=function(n,t,r){n=zu(n);var e=(t=Ou(t))?T(n):0;return!t||e>=t?n:(t=(t-e)/2,ee(zi(t),r)+n+ee(Ri(t),r))},On.padEnd=function(n,t,r){
n=zu(n);var e=(t=Ou(t))?T(n):0;return t&&e<t?n+ee(t-e,r):n},On.padStart=function(n,t,r){n=zu(n);var e=(t=Ou(t))?T(n):0;return t&&e<t?ee(t-e,r)+n:n},On.parseInt=function(n,t,r){return r||null==t?t=0:t&&(t=+t),$i(zu(n).replace(an,""),t||0)},On.random=function(n,t,r){if(r&&typeof r!="boolean"&&ze(n,t,r)&&(t=r=F),r===F&&(typeof t=="boolean"?(r=t,t=F):typeof n=="boolean"&&(r=n,n=F)),n===F&&t===F?(n=0,t=1):(n=Eu(n),t===F?(t=n,n=0):t=Eu(t)),n>t){var e=n;n=t,t=e}return r||n%1||t%1?(r=Fi(),Mi(n+r*(t-n+$n("1e-"+((r+"").length-1))),t)):cr(n,t);
},On.reduce=function(n,t,r){var e=af(n)?h:m,u=3>arguments.length;return e(n,je(t,4),r,u,oo)},On.reduceRight=function(n,t,r){var e=af(n)?p:m,u=3>arguments.length;return e(n,je(t,4),r,u,fo)},On.repeat=function(n,t,r){return t=(r?ze(n,t,r):t===F)?1:Ou(t),ar(zu(n),t)},On.replace=function(){var n=arguments,t=zu(n[0]);return 3>n.length?t:t.replace(n[1],n[2])},On.result=function(n,t,r){t=Rr(t,n);var e=-1,u=t.length;for(u||(u=1,n=F);++e<u;){var i=null==n?F:n[$e(t[e])];i===F&&(e=u,i=r),n=gu(i)?i.call(n):i;
}return n},On.round=ic,On.runInContext=w,On.sample=function(n){return(af(n)?tt:sr)(n)},On.size=function(n){if(null==n)return 0;if(pu(n))return mu(n)?T(n):n.length;var t=yo(n);return"[object Map]"==t||"[object Set]"==t?n.size:Ht(n).length},On.snakeCase=Tf,On.some=function(n,t,r){var e=af(n)?_:gr;return r&&ze(n,t,r)&&(t=F),e(n,je(t,3))},On.sortedIndex=function(n,t){return dr(n,t)},On.sortedIndexBy=function(n,t,r){return yr(n,t,je(r,2))},On.sortedIndexOf=function(n,t){var r=null==n?0:n.length;if(r){
var e=dr(n,t);if(e<r&&hu(n[e],t))return e}return-1},On.sortedLastIndex=function(n,t){return dr(n,t,true)},On.sortedLastIndexBy=function(n,t,r){return yr(n,t,je(r,2),true)},On.sortedLastIndexOf=function(n,t){if(null==n?0:n.length){var r=dr(n,t,true)-1;if(hu(n[r],t))return r}return-1},On.startCase=$f,On.startsWith=function(n,t,r){return n=zu(n),r=null==r?0:gt(Ou(r),0,n.length),t=jr(t),n.slice(r,r+t.length)==t},On.subtract=oc,On.sum=function(n){return n&&n.length?k(n,Nu):0},On.sumBy=function(n,t){return n&&n.length?k(n,je(t,2)):0;
},On.template=function(n,t,r){var e=On.templateSettings;r&&ze(n,t,r)&&(t=F),n=zu(n),t=jf({},t,e,se),r=jf({},t.imports,e.imports,se);var u,i,o=Lu(r),f=I(r,o),c=0;r=t.interpolate||An;var a="__p+='";r=ti((t.escape||An).source+"|"+r.source+"|"+(r===nn?gn:An).source+"|"+(t.evaluate||An).source+"|$","g");var l="sourceURL"in t?"//# sourceURL="+t.sourceURL+"\n":"";if(n.replace(r,function(t,r,e,o,f,l){return e||(e=o),a+=n.slice(c,l).replace(kn,B),r&&(u=true,a+="'+__e("+r+")+'"),f&&(i=true,a+="';"+f+";\n__p+='"),
e&&(a+="'+((__t=("+e+"))==null?'':__t)+'"),c=l+t.length,t}),a+="';",(t=t.variable)||(a="with(obj){"+a+"}"),a=(i?a.replace(q,""):a).replace(V,"$1").replace(K,"$1;"),a="function("+(t||"obj")+"){"+(t?"":"obj||(obj={});")+"var __t,__p=''"+(u?",__e=_.escape":"")+(i?",__j=Array.prototype.join;function print(){__p+=__j.call(arguments,'')}":";")+a+"return __p}",t=Pf(function(){return Qu(o,l+"return "+a).apply(F,f)}),t.source=a,vu(t))throw t;return t},On.times=function(n,t){if(n=Ou(n),1>n||9007199254740991<n)return[];
var r=4294967295,e=Mi(n,4294967295);for(t=je(t),n-=4294967295,e=E(e,t);++r<n;)t(r);return e},On.toFinite=Eu,On.toInteger=Ou,On.toLength=Su,On.toLower=function(n){return zu(n).toLowerCase()},On.toNumber=Iu,On.toSafeInteger=function(n){return n?gt(Ou(n),-9007199254740991,9007199254740991):0===n?n:0},On.toString=zu,On.toUpper=function(n){return zu(n).toUpperCase()},On.trim=function(n,t,r){return(n=zu(n))&&(r||t===F)?n.replace(cn,""):n&&(t=jr(t))?(n=$(n),r=$(t),t=z(n,r),r=W(n,r)+1,zr(n,t,r).join("")):n;
},On.trimEnd=function(n,t,r){return(n=zu(n))&&(r||t===F)?n.replace(ln,""):n&&(t=jr(t))?(n=$(n),t=W(n,$(t))+1,zr(n,0,t).join("")):n},On.trimStart=function(n,t,r){return(n=zu(n))&&(r||t===F)?n.replace(an,""):n&&(t=jr(t))?(n=$(n),t=z(n,$(t)),zr(n,t).join("")):n},On.truncate=function(n,t){var r=30,e="...";if(bu(t))var u="separator"in t?t.separator:u,r="length"in t?Ou(t.length):r,e="omission"in t?jr(t.omission):e;n=zu(n);var i=n.length;if(Bn.test(n))var o=$(n),i=o.length;if(r>=i)return n;if(i=r-T(e),1>i)return e;
if(r=o?zr(o,0,i).join(""):n.slice(0,i),u===F)return r+e;if(o&&(i+=r.length-i),_f(u)){if(n.slice(i).search(u)){var f=r;for(u.global||(u=ti(u.source,zu(dn.exec(u))+"g")),u.lastIndex=0;o=u.exec(f);)var c=o.index;r=r.slice(0,c===F?i:c)}}else n.indexOf(jr(u),i)!=i&&(u=r.lastIndexOf(u),-1<u&&(r=r.slice(0,u)));return r+e},On.unescape=function(n){return(n=zu(n))&&J.test(n)?n.replace(G,ut):n},On.uniqueId=function(n){var t=++ai;return zu(n)+t},On.upperCase=Ff,On.upperFirst=Nf,On.each=ru,On.eachRight=eu,On.first=Ke,
Zu(On,function(){var n={};return Et(On,function(t,r){ci.call(On.prototype,r)||(n[r]=t)}),n}(),{chain:false}),On.VERSION="4.17.4",u("bind bindKey curry curryRight partial partialRight".split(" "),function(n){On[n].placeholder=On}),u(["drop","take"],function(n,t){Mn.prototype[n]=function(r){r=r===F?1:Di(Ou(r),0);var e=this.__filtered__&&!t?new Mn(this):this.clone();return e.__filtered__?e.__takeCount__=Mi(r,e.__takeCount__):e.__views__.push({size:Mi(r,4294967295),type:n+(0>e.__dir__?"Right":"")}),e},Mn.prototype[n+"Right"]=function(t){
return this.reverse()[n](t).reverse()}}),u(["filter","map","takeWhile"],function(n,t){var r=t+1,e=1==r||3==r;Mn.prototype[n]=function(n){var t=this.clone();return t.__iteratees__.push({iteratee:je(n,3),type:r}),t.__filtered__=t.__filtered__||e,t}}),u(["head","last"],function(n,t){var r="take"+(t?"Right":"");Mn.prototype[n]=function(){return this[r](1).value()[0]}}),u(["initial","tail"],function(n,t){var r="drop"+(t?"":"Right");Mn.prototype[n]=function(){return this.__filtered__?new Mn(this):this[r](1);
}}),Mn.prototype.compact=function(){return this.filter(Nu)},Mn.prototype.find=function(n){return this.filter(n).head()},Mn.prototype.findLast=function(n){return this.reverse().find(n)},Mn.prototype.invokeMap=lr(function(n,t){return typeof n=="function"?new Mn(this):this.map(function(r){return Dt(r,n,t)})}),Mn.prototype.reject=function(n){return this.filter(su(je(n)))},Mn.prototype.slice=function(n,t){n=Ou(n);var r=this;return r.__filtered__&&(0<n||0>t)?new Mn(r):(0>n?r=r.takeRight(-n):n&&(r=r.drop(n)),
t!==F&&(t=Ou(t),r=0>t?r.dropRight(-t):r.take(t-n)),r)},Mn.prototype.takeRightWhile=function(n){return this.reverse().takeWhile(n).reverse()},Mn.prototype.toArray=function(){return this.take(4294967295)},Et(Mn.prototype,function(n,t){var r=/^(?:filter|find|map|reject)|While$/.test(t),e=/^(?:head|last)$/.test(t),u=On[e?"take"+("last"==t?"Right":""):t],i=e||/^find/.test(t);u&&(On.prototype[t]=function(){function t(n){return n=u.apply(On,s([n],f)),e&&h?n[0]:n}var o=this.__wrapped__,f=e?[1]:arguments,c=o instanceof Mn,a=f[0],l=c||af(o);
l&&r&&typeof a=="function"&&1!=a.length&&(c=l=false);var h=this.__chain__,p=!!this.__actions__.length,a=i&&!h,c=c&&!p;return!i&&l?(o=c?o:new Mn(this),o=n.apply(o,f),o.__actions__.push({func:nu,args:[t],thisArg:F}),new zn(o,h)):a&&c?n.apply(this,f):(o=this.thru(t),a?e?o.value()[0]:o.value():o)})}),u("pop push shift sort splice unshift".split(" "),function(n){var t=ui[n],r=/^(?:push|sort|unshift)$/.test(n)?"tap":"thru",e=/^(?:pop|shift)$/.test(n);On.prototype[n]=function(){var n=arguments;if(e&&!this.__chain__){
var u=this.value();return t.apply(af(u)?u:[],n)}return this[r](function(r){return t.apply(af(r)?r:[],n)})}}),Et(Mn.prototype,function(n,t){var r=On[t];if(r){var e=r.name+"";(Ji[e]||(Ji[e]=[])).push({name:t,func:r})}}),Ji[Xr(F,2).name]=[{name:"wrapper",func:F}],Mn.prototype.clone=function(){var n=new Mn(this.__wrapped__);return n.__actions__=Mr(this.__actions__),n.__dir__=this.__dir__,n.__filtered__=this.__filtered__,n.__iteratees__=Mr(this.__iteratees__),n.__takeCount__=this.__takeCount__,n.__views__=Mr(this.__views__),
n},Mn.prototype.reverse=function(){if(this.__filtered__){var n=new Mn(this);n.__dir__=-1,n.__filtered__=true}else n=this.clone(),n.__dir__*=-1;return n},Mn.prototype.value=function(){var n,t=this.__wrapped__.value(),r=this.__dir__,e=af(t),u=0>r,i=e?t.length:0;n=i;for(var o=this.__views__,f=0,c=-1,a=o.length;++c<a;){var l=o[c],s=l.size;switch(l.type){case"drop":f+=s;break;case"dropRight":n-=s;break;case"take":n=Mi(n,f+s);break;case"takeRight":f=Di(f,n-s)}}if(n={start:f,end:n},o=n.start,f=n.end,n=f-o,
o=u?f:o-1,f=this.__iteratees__,c=f.length,a=0,l=Mi(n,this.__takeCount__),!e||!u&&i==n&&l==n)return kr(t,this.__actions__);e=[];n:for(;n--&&a<l;){for(o+=r,u=-1,i=t[o];++u<c;){var h=f[u],s=h.type,h=(0,h.iteratee)(i);if(2==s)i=h;else if(!h){if(1==s)continue n;break n}}e[a++]=i}return e},On.prototype.at=Fo,On.prototype.chain=function(){return Xe(this)},On.prototype.commit=function(){return new zn(this.value(),this.__chain__)},On.prototype.next=function(){this.__values__===F&&(this.__values__=ku(this.value()));
var n=this.__index__>=this.__values__.length;return{done:n,value:n?F:this.__values__[this.__index__++]}},On.prototype.plant=function(n){for(var t,r=this;r instanceof Sn;){var e=Pe(r);e.__index__=0,e.__values__=F,t?u.__wrapped__=e:t=e;var u=e,r=r.__wrapped__}return u.__wrapped__=n,t},On.prototype.reverse=function(){var n=this.__wrapped__;return n instanceof Mn?(this.__actions__.length&&(n=new Mn(this)),n=n.reverse(),n.__actions__.push({func:nu,args:[Je],thisArg:F}),new zn(n,this.__chain__)):this.thru(Je);
},On.prototype.toJSON=On.prototype.valueOf=On.prototype.value=function(){return kr(this.__wrapped__,this.__actions__)},On.prototype.first=On.prototype.head,Ai&&(On.prototype[Ai]=tu),On}();typeof define=="function"&&typeof define.amd=="object"&&define.amd?(Zn._=it, define(function(){return it})):Vn?((Vn.exports=it)._=it,qn._=it):Zn._=it}).call(this);

10947
_static/js/vue.js Normal file

File diff suppressed because it is too large Load diff

6
requirements.txt Normal file
View file

@ -0,0 +1,6 @@
# oTree-may-overwrite-this-file
# IF YOU MODIFY THIS FILE, remove this comment.
# otherwise, oTree will automatically overwrite it.
otree>=5.0.0a21
psycopg2>=2.8.4
numpy>= 2.1.3

1
runtime.txt Normal file
View file

@ -0,0 +1 @@
python-3.11.5

82
settings.py Normal file
View file

@ -0,0 +1,82 @@
from os import environ
SESSION_CONFIGS = [
dict(
name='test',
app_sequence=['Task1'],
completionlink='',
use_captchas=0,
num_demo_participants=5,
context="test",
),
dict(
name='prolific',
app_sequence=['Start', 'Task1', 'Finish'],
completionlink='https://app.prolific.com/submissions/complete?cc=C15TDZJ3',
use_captchas = 1,
num_demo_participants=6,
estimated_completion_time=15,
context="prolific",
),
dict(
name='elfeonline',
app_sequence=['Start', 'Task1', 'Finish'],
elfecompletionlink = "https://qualtrics.ucl.ac.uk/jfe/form/SV_bKGyy84JJN8c7n8?",
use_captchas = 1,
num_demo_participants=6,
estimated_completion_time=15,
context="lab",
),
]
# if you set a property in SESSION_CONFIG_DEFAULTS, it will be inherited by all configs
# in SESSION_CONFIGS, except those that explicitly override it.
# the session config can be accessed from methods in your apps as self.session.config,
# e.g. self.session.config['participation_fee']
SESSION_CONFIG_DEFAULTS = dict(
real_world_currency_per_point=1.00,
participation_fee=2.50,
participation_payment=2.50,
bonus=3,
min_bonus_payment=0,
max_bonus_payment=20.00,
min_payment=2.50,
max_payment=2.50+20.00,
currency_symbol='£',
)
ROOMS = [
dict(
name='Test',
display_name='Test',
),
dict(
name='Prolific',
display_name='Prolific',
),
dict(
name='elfeonline',
display_name='elfeonline',
),
]
PARTICIPANT_FIELDS = []
SESSION_FIELDS = []
# ISO-639 code
# for example: de, fr, ja, ko, zh-hans
LANGUAGE_CODE = 'en'
# e.g. EUR, GBP, CNY, JPY
REAL_WORLD_CURRENCY_CODE = 'GBP'
USE_POINTS = True
ADMIN_USERNAME = 'admin'
# for security, best to set admin password in an environment variable
ADMIN_PASSWORD = 'templateadminpass'
DEMO_PAGE_INTRO_HTML = """ """
SECRET_KEY = '0112357121931'