Beispiel #1
0
class FormFieldModel(otree.models.BaseGroup):
    null_boolean = models.BooleanField()
    big_integer = models.BigIntegerField()
    boolean = models.BooleanField(default=False)
    char = models.CharField()
    comma_separated_integer = models.CommaSeparatedIntegerField(max_length=100)
    date = models.DateField()
    date_time = models.DateTimeField()
    alt_date_time = models.DateTimeField(
        widget=otree.forms.SplitDateTimeWidget)
    decimal = models.DecimalField(max_digits=5, decimal_places=2)
    email = models.EmailField()
    file = models.FileField(upload_to='_tmp/uploads')
    file_path = models.FilePathField()
    float = models.FloatField()
    integer = models.IntegerField()
    generic_ip_address = models.GenericIPAddressField()
    positive_integer = models.PositiveIntegerField()
    positive_small_integer = models.PositiveSmallIntegerField()
    slug = models.SlugField()
    small_integer = models.SmallIntegerField()
    text = models.TextField()
    alt_text = models.TextField(widget=otree.forms.TextInput)
    time = models.TimeField()
    url = models.URLField()
    many_to_many = models.ManyToManyField('SimpleModel', related_name='+')
    one_to_one = models.OneToOneField('SimpleModel', related_name='+')

    currency = models.CurrencyField()
    currency_choice = models.CurrencyField(choices=[('0.01',
                                                     '0.01'), ('1.20',
                                                               '1.20')])

    sent_amount = models.CurrencyField(choices=currency_range(0, 0.75, 0.05))
    slider_widget = models.IntegerField(widget=widgets.SliderInput())
Beispiel #2
0
class Player(BasePlayer):
    points = models.TextField()
    x_coordinate_form_pick = models.IntegerField()
    y_coordinate_form_pick = models.IntegerField()
    x_coordinate_form_guess = models.IntegerField()
    y_coordinate_form_guess = models.IntegerField()
    guess = models.TextField()
Beispiel #3
0
class Player(otree.models.BasePlayer):
    # <built-in>
    match = models.ForeignKey(Match, null=True)
    treatment = models.ForeignKey(Treatment, null=True)
    subsession = models.ForeignKey(Subsession)
    # </built-in>

    rating = models.IntegerField(
        choices=[(1, 'very inappropriate'), (2, 'somewhat inappropriate'), (3, 'somewhat appropriate'),
                 (4, 'very appropriate')],
        default=None,
        doc="Players enter norm rating"
    )

    # random_player = models.IntegerField(
    # 		default = None,
    # 		doc="number of random player"
    # 	)
    #
    # 	random_rating = models.CharField(
    #     	default = None,
    #     	doc="rating of random player"
    #     )
    #
    # 	def random(self):
    #  		self.random_player = random.choice(self.other_players_in_subsession())
    #  		self.random_rating = self.random_player.rating

    most_common_rating = models.IntegerField(
        choices=[(1, 'very inappropriate'), (2, 'somewhat inappropriate'), (3, 'somewhat appropriate'),
                 (4, 'very appropriate')],
        default=None,
        doc="rating given most often among players"
    )
Beispiel #4
0
class Player(BasePlayer):
    points = models.TextField()
    x_coordinate_form_pick = models.IntegerField()
    y_coordinate_form_pick = models.IntegerField()
    x_coordinate_form_guess = models.IntegerField()
    y_coordinate_form_guess = models.IntegerField()
    box_1_offset_left_form = models.IntegerField()
    box_1_offset_top_form = models.IntegerField()
    box_2_offset_left_form = models.IntegerField()
    box_2_offset_top_form = models.IntegerField()
    box_3_offset_left_form = models.IntegerField()
    box_3_offset_top_form = models.IntegerField()
    guess = models.TextField()
Beispiel #5
0
class Subsession(otree.models.BaseSubsession):
    name_in_url = 'exp2'

    most_common_rating = models.IntegerField(
        default=None,
        choices=RATING_CHOICES,
        doc="rating given most often among players")

    def most_common_rating_choices(self):
        return self.RATING_CHOICES

    def set_payoffs(self):
        for p in self.get_players():
            # if p.rating == p.random_rating:
            if p.rating == self.most_common_rating:
                p.payoff = self.match_payoff
            else:
                p.payoff = 0

    def most_common(self):
        ratings = [p.rating for p in self.get_players()]
        self.most_common_rating = collections.Counter(ratings).most_common(
            1)[0][0]
        # at the moment, the program does not take care of ties

    match_payoff = models.MoneyField(
        default=200, doc="payoff if answer matches most common answer")
Beispiel #6
0
class Player(otree.models.BasePlayer):

    # <built-in>
    group = models.ForeignKey(Group, null=True)
    subsession = models.ForeignKey(Subsession)
    # </built-in>

    gender = models.CharField(
        max_length=10, choices=['Male', 'Female'],
        widget=widgets.RadioSelectHorizontal())

    age = models.IntegerField(min=10, max=100)

    nationality = models.CharField(
        max_length=10, widget=widgets.RadioSelectHorizontal(),
        choices=["Dutch", "Germany", "Spanish", "Other"])

    education_level = models.CharField(
        verbose_name="Highest finished level of education",
        max_length=29,
        choices=["High school", "First year college", "second year college",
                 "third year college", "Bachelor", "Master", "Doctorate"])
    study = models.CharField(
        max_length=29,
        choices=["Chemistry and physics", "Business and Economics", "Law",
                 "Health and medicine", "language and communication",
                 "technique", "Art and cultures", "other"])
Beispiel #7
0
class Player(otree.models.BasePlayer):
    # <built-in>
    group = models.ForeignKey(Group, null=True)
    subsession = models.ForeignKey(Subsession)
    # </built-in>

    rating = models.IntegerField(default=None,
                                 choices=RATING_CHOICES,
                                 doc="Players enter norm rating",
                                 widget=widgets.RadioSelect())
Beispiel #8
0
class Player(otree.models.BasePlayer):

    # <built-in>
    group = models.ForeignKey(Group, null=True)
    subsession = models.ForeignKey(Subsession)
    # </built-in>

    # training
    training_buyer_earnings = models.IntegerField(
        verbose_name="Buyer's period payoff would be")

    training_seller1_earnings = models.IntegerField(
        verbose_name="Seller 1's period payoff would be")

    training_seller2_earnings = models.IntegerField(
        verbose_name="Seller 2's period payoff would be")

    # seller
    price = models.CurrencyField(
        bounds=[0, Constants.INITIAL],
        verbose_name='Please indicate a price (from 0 to %i) you want to sell'
        % Constants.INITIAL)

    quality = models.CurrencyField(choices=[
        (30, 'High'),
        (20, 'Medium'),
        (10, 'Low')],
        verbose_name='Please select a quality grade you want to produce',
        widget=widgets.RadioSelectHorizontal())

    # buyer
    choice = models.PositiveSmallIntegerField(
        choices=[(i, 'Buy from seller %i' % i) for i in
                 range(1, Constants.players_per_group)] + [(0, 'Buy nothing')],
        blank=True,
        widget=widgets.RadioSelect(),
        verbose_name='And you will')  # seller index

    def role(self):
        if self.id_in_group == 1:
            return 'buyer'
        return 'seller %i' % (self.id_in_group - 1)
Beispiel #9
0
class Group(otree.models.BaseGroup):

    # <built-in>
    subsession = models.ForeignKey(Subsession)
    # </built-in>

    group_type = models.CharField(max_length=20,
                                  choices=[Constants.gadd, Constants.gmul])

    subgroup_type = models.IntegerField(
        choices=[Constants.sg1, Constants.sg2, Constants.sg3, Constants.sg4])
Beispiel #10
0
class Player(BasePlayer):

    contribution = models.IntegerField(
        min=0,
        max=Constants.endowment,
        doc="""The amount contributed by the player""",
    )
    question = models.IntegerField()

    guess_one = models.CharField(widget=widgets.RadioSelect(),
                                 choices=Constants.GUESS_CHOICES)
    guess_two = models.CharField(widget=widgets.RadioSelect(),
                                 choices=Constants.GUESS_CHOICES)

    def guess_correct(self):
        guess_choices = Constants.GUESS_CHOICE
        px, py = self.get_others_in_group()
        return px.contribution in guess_choices[int(
            self.guess_one)] and py.contribution in guess_choices[int(
                self.guess_two)]
Beispiel #11
0
class Player(BasePlayer):
    points = models.IntegerField(default=0)

    CHOICES = [("one", ""), ("two", ""), ("three", ""), ("four", ""), ("five", ""), ("six", "")]
    CHOICES_8 = [("one", ""), ("two", ""), ("three", ""), ("four", ""),
                 ("five", ""), ("six", ""), ("seven", ""), ("eight", "")]

    raven_1 = models.CharField(widget=widgets.RadioSelectHorizontal(), choices=CHOICES)
    raven_2 = models.CharField(widget=widgets.RadioSelectHorizontal(), choices=CHOICES_8)
    raven_3 = models.CharField(widget=widgets.RadioSelectHorizontal(), choices=CHOICES)
    raven_4 = models.CharField(widget=widgets.RadioSelectHorizontal(), choices=CHOICES)
    raven_5 = models.CharField(widget=widgets.RadioSelectHorizontal(), choices=CHOICES_8)
    raven_6 = models.CharField(widget=widgets.RadioSelectHorizontal(), choices=CHOICES_8)
Beispiel #12
0
class RedwoodEvent(models.Model):

    class Meta:
        app_label = "otree"
        # If I don't set this, it could be in an unpredictable order
        ordering = ['-timestamp']

    timestamp = models.DateTimeField(null=False)
    component = models.CharField(max_length=100, null=False)
    session = models.ForeignKey(
        'otree.Session',
        null=False,
        related_name='+')
    subsession = models.IntegerField(null=True)
    round = models.IntegerField(null=False)
    group = models.IntegerField(null=False)
    value = models._JSONField()

    def save(self, *args, **kwargs):
        if self.timestamp is None:
            self.timestamp = timezone.now()
        super().save(*args, **kwargs)
Beispiel #13
0
class Group(BaseGroup):
    total_contribution = models.IntegerField()

    individual_share = models.IntegerField()

    def set_payoffs(self):

        self.total_contribution = sum(
            [p.contribution for p in self.get_players()])
        self.individual_share = self.total_contribution * Constants.efficiency_factor / Constants.players_per_group
        for p in self.get_players():
            if p.guess_correct():
                points = (Constants.endowment - p.contribution
                          ) + self.individual_share + Constants.guess_correct
                p.participant.vars["game_payoff"]["public_goods_game"] = points
                p.participant.vars["carrying_payoff"] += points
                p.payoff = points
            else:
                points = (Constants.endowment -
                          p.contribution) + self.individual_share
                p.participant.vars["game_payoff"]["public_goods_game"] = points
                p.participant.vars["carrying_payoff"] += points
                p.payoff = points
Beispiel #14
0
class Player(BasePlayer):
    sent_amount = models.IntegerField(min=0, max=600)
    sent_back_amount = models.IntegerField()
Beispiel #15
0
class Player(otree.models.BasePlayer):

    treatment = models.IntegerField(
        doc="The player's treatment; identical to subsession.treatment"
    )

    contribution = models.DecimalField(
        min=0, max=Constants.endowment,
        max_digits=12, decimal_places=2,
        doc="The amount the player contributed for the observed round",
    )

    signal = models.DecimalField(max_digits=12, decimal_places=2,
        doc="The signal observed by the player in given round",
    )
    round_points = models.DecimalField(max_digits=12, decimal_places=2,
        doc="This is the total points (tokens) for the subject for a given round. It includes the points from the public good (one forth of the total points present in the public good plus tokens present in the private account).  Notice that points/payoff for any given round will be materially paid to subjects at the end of the experiment only if the game that includes that round is randomly selected for payment."
    )
    points = models.DecimalField(max_digits=12, decimal_places=2,
        doc="The same as round_points except this only gets populated when the paying_game variable is equal to 1.  The variable is required by the application when determining payouts because only one game is selected for actual subject payment."
    )

    def set_signal_value(self):
        rn = self.subsession.round_number
        vr = self.session.vars['varied_round']
        signalVariance = self.session.config['signalVariance']
        if rn in Constants.starting_rounds or rn in range(vr, vr+Constants.rounds_per_game):
            mpcr = self.group.efficiency_rate
            choicesLeft = [mpcr - Decimal(x*.1) for x in range(1, signalVariance + 1)]
            choicesRight = [mpcr + Decimal(x*.1) for x in range(1, signalVariance + 1)]

            #chop off choices if at the tails
            if mpcr < Decimal(.06):
                choices = choicesRight + [mpcr]
            elif Decimal(.06) < mpcr < Decimal(.16) and signalVariance == 2:
                choices = choicesRight + [mpcr] + choicesLeft[:1]
            elif mpcr > Decimal(1.16):
                choices = choicesLeft + [mpcr]
            elif Decimal(1.06) < mpcr < Decimal(1.16) and signalVariance == 2:
                choices = choicesLeft + [mpcr] + choicesRight[:1]
            else:
                choices = [mpcr] + choicesLeft + choicesRight
            signal = random.choice(choices)
            self.signal = signal.quantize(Decimal('.01'))
        else:
            self.signal = self.in_previous_rounds()[-1].signal

    def get_signal_values(self):
        if self.session.config['signalVariance'] == 2:
            signalVariance = Decimal(.2)
        else:
            signalVariance = Decimal(.1)
        left = self.signal - signalVariance
        right = self.signal + signalVariance
        signals = dict(left=left.quantize(Decimal('.01')), right=right.quantize(Decimal('.01')))
        return signals

    def get_game_info(self):
        for p in self.in_all_rounds():
            if p.subsession.round_number in Constants.starting_rounds:
                info = []
            info.append(p)
        return info

    def get_round_period(self):
        x = 1
        for r in Constants.starting_rounds:
            if self.subsession.round_number in range(r, r+Constants.rounds_per_game):
                game_round = self.subsession.round_number - r
                break
            else:
                x += 1
        game_round += 1
        return [game_round, x]

    def get_game_payoffs(self):
        for p in self.in_all_rounds():
            if p.subsession.round_number in Constants.starting_rounds:
                payoffs = []
            payoffs.append(p.round_points)
        return sum(payoffs)

    def set_session_payoffs(self):
        self.participant.vars['pg_points'] = sum([p.points for p in self.in_all_rounds()])
        self.participant.vars['pg_payoff'] = sum([p.payoff for p in self.in_all_rounds()])
Beispiel #16
0
class Player(otree.models.BasePlayer):

    # <built-in>
    group = models.ForeignKey(Group, null=True)
    subsession = models.ForeignKey(Subsession)
    # </built-in>

    player_re_type = models.IntegerField(min=min(Constants.player_types),
                                         max=max(Constants.player_types))

    transcription = models.TextField()
    share = models.BooleanField(default=False, widget=widgets.HiddenInput())

    training_skip = models.BooleanField(default=False,
                                        widget=widgets.HiddenInput())
    training_start_time = models.DateTimeField()
    training_idx = models.PositiveIntegerField(default=0)
    training_intents = models.JSONField()

    round_1_start_time = models.DateTimeField()
    round_1_idx = models.PositiveIntegerField(default=0)
    round_1_transcription_texts = models.JSONField()
    round_1_intents = models.JSONField()
    round_1_a_payoff = models.CurrencyField()

    round_2_start_time = models.DateTimeField()
    round_2_idx = models.PositiveIntegerField(default=0)
    round_2_transcription_texts = models.JSONField()
    round_2_intents = models.JSONField()
    round_2_shared = models.PositiveIntegerField(default=0)
    round_2_a_payoff = models.CurrencyField()
    round_2_b_payoff = models.CurrencyField()

    round_3_start_time = models.DateTimeField()
    round_3_idx = models.PositiveIntegerField(default=0)
    round_3_transcription_texts = models.JSONField()
    round_3_intents = models.JSONField()
    round_3_shared = models.PositiveIntegerField(default=0)
    round_3_a_payoff = models.CurrencyField()
    round_3_b_payoff = models.CurrencyField()

    def set_payoff(self):
        self.payoff = 0

    @property
    def training_transcription_texts(self):
        return Constants.reference_texts

    def training_png(self, idx):
        return Constants.reference_pngs[idx]

    def round_1_png(self, idx):
        if not hasattr(self, "__round_1_png"):
            self.__round_1_png = {}
        if idx not in self.__round_1_png:
            text = self.round_1_transcription_texts[idx]
            self.__round_1_png[idx] = txt2png.render(
                text, encoding=Constants.png_encoding)
        return self.__round_1_png[idx]

    def set_round_1_payoff(self):
        self.round_1_a_payoff = Constants.a_payoff * self.round_1_idx

    def round_1_time_left(self):
        start = self.round_1_start_time
        now = timezone.now()
        time_left = Constants.round_1_seconds - (now - start).seconds
        return time_left if time_left > 0 else 0

    def round_2_png(self, idx):
        if not hasattr(self, "__round_2_png"):
            self.__round_2_png = {}
        if idx not in self.__round_2_png:
            text = self.round_2_transcription_texts[idx]
            self.__round_2_png[idx] = txt2png.render(
                text, encoding=Constants.png_encoding)
        return self.__round_2_png[idx]

    def set_round_2_payoff(self):
        count_b = self.round_2_shared
        if count_b > 0:
            self.round_2_b_payoff = Constants.b_payoff * count_b
        else:
            self.round_2_b_payoff = 0
        count_a = self.round_2_idx - count_b
        if count_a > 0:
            self.round_2_a_payoff = Constants.a_payoff * count_a
        else:
            self.round_2_a_payoff = 0

    def round_2_time_left(self):
        start = self.round_2_start_time
        now = timezone.now()
        time_left = Constants.round_2_seconds - (now - start).seconds
        return time_left if time_left > 0 else 0

    def round_3_png(self, idx):
        if not hasattr(self, "__round_3_png"):
            self.__round_3_png = {}
        if idx not in self.__round_3_png:
            text = self.round_3_transcription_texts[idx]
            self.__round_3_png[idx] = txt2png.render(
                text, encoding=Constants.png_encoding)
        return self.__round_3_png[idx]

    def set_round_3_payoff(self):
        count_b = self.round_3_shared
        if count_b > 0:
            self.round_3_b_payoff = Constants.b_payoff * count_b
        else:
            self.round_3_b_payoff = 0
        count_a = self.round_3_idx - count_b
        if count_a > 0:
            self.round_3_a_payoff = Constants.a_payoff * count_a
        else:
            self.round_3_a_payoff = 0

    def round_3_time_left(self):
        start = self.round_3_start_time
        now = timezone.now()
        time_left = Constants.round_3_seconds - (now - start).seconds
        return time_left if time_left > 0 else 0
Beispiel #17
0
class Session(ModelWithVars):
    class Meta:
        # if i don't set this, it could be in an unpredictable order
        ordering = ['pk']
        app_label = "otree"

    config = models.JSONField(
        default=dict,
        null=True,
        doc=("the session config dict, as defined in the "
             "programmer's settings.py."))

    # label of this session instance
    label = models.CharField(max_length=300,
                             null=True,
                             blank=True,
                             help_text='For internal record-keeping')

    experimenter_name = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        help_text='For internal record-keeping')

    code = models.RandomCharField(
        db_index=True,
        length=8,
        doc="Randomly generated unique identifier for the session.")

    time_scheduled = models.DateTimeField(
        null=True,
        doc="The time at which the session is scheduled",
        help_text='For internal record-keeping',
        blank=True)

    time_started = models.DateTimeField(
        null=True,
        doc="The time at which the experimenter started the session")

    mturk_HITId = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        help_text='Hit id for this session on MTurk')
    mturk_HITGroupId = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        help_text='Hit id for this session on MTurk')
    mturk_qualification_type_id = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        help_text='Qualification type that is '
        'assigned to each worker taking hit')

    # since workers can drop out number of participants on server should be
    # greater than number of participants on mturk
    # value -1 indicates that this session it not intended to run on mturk
    mturk_num_participants = models.IntegerField(
        default=-1, help_text="Number of participants on MTurk")

    mturk_sandbox = models.BooleanField(
        default=True,
        help_text="Should this session be created in mturk sandbox?")

    archived = models.BooleanField(
        default=False,
        db_index=True,
        doc=("If set to True the session won't be visible on the "
             "main ViewList for sessions"))

    git_commit_timestamp = models.CharField(
        max_length=200,
        null=True,
        doc=(
            "Indicates the version of the code (as recorded by Git) that was "
            "used to run the session, so that the session can be replicated "
            "later.\n Search through the Git commit log to find a commit that "
            "was made at this time."))

    comment = models.TextField(blank=True)

    _ready_to_play = models.BooleanField(default=False)

    _anonymous_code = models.RandomCharField(length=10)

    special_category = models.CharField(
        db_index=True,
        max_length=20,
        null=True,
        doc="whether it's a test session, demo session, etc.")

    # whether someone already viewed this session's demo links
    demo_already_used = models.BooleanField(default=False, db_index=True)

    # indicates whether a session has been fully created (not only has the
    # model itself been created, but also the other models in the hierarchy)
    ready = models.BooleanField(default=False)

    _pre_create_id = models.CharField(max_length=300, db_index=True, null=True)

    def __unicode__(self):
        return self.code

    @property
    def participation_fee(self):
        '''This method is deprecated from public API,
        but still useful internally (like data export)'''
        return self.config['participation_fee']

    @property
    def real_world_currency_per_point(self):
        '''This method is deprecated from public API,
        but still useful internally (like data export)'''
        return self.config['real_world_currency_per_point']

    @property
    def session_type(self):
        '''2015-07-10: session_type is deprecated
        this shim method will be removed eventually'''
        return self.config

    def is_open(self):
        return GlobalSingleton.objects.get().default_session == self

    def is_for_mturk(self):
        return (not self.is_demo()) and (self.mturk_num_participants > 0)

    def is_demo(self):
        return (self.special_category ==
                constants_internal.session_special_category_demo)

    def get_subsessions(self):
        lst = []
        app_sequence = self.config['app_sequence']
        for app in app_sequence:
            models_module = otree.common_internal.get_models_module(app)
            subsessions = models_module.Subsession.objects.filter(
                session=self).order_by('round_number')
            lst.extend(list(subsessions))
        return lst

    def delete(self, using=None):
        for subsession in self.get_subsessions():
            subsession.delete()
        super(Session, self).delete(using)

    def get_participants(self):
        return self.participant_set.all()

    def _create_groups_and_initialize(self):
        # group_by_arrival_time code used to be here
        for subsession in self.get_subsessions():
            subsession._create_groups()
            subsession._initialize()
            subsession.save()
        self._ready_to_play = True
        # assert self is subsession.session
        self.save()

    def mturk_requester_url(self):
        if self.mturk_sandbox:
            requester_url = (
                "https://requestersandbox.mturk.com/mturk/manageHITs")
        else:
            requester_url = "https://requester.mturk.com/mturk/manageHITs"
        return requester_url

    def mturk_worker_url(self):
        if self.mturk_sandbox:
            worker_url = (
                "https://workersandbox.mturk.com/mturk/preview?groupId={}"
            ).format(self.mturk_HITGroupId)
        else:
            worker_url = (
                "https://www.mturk.com/mturk/preview?groupId={}").format(
                    self.mturk_HITGroupId)
        return worker_url

    def advance_last_place_participants(self):
        participants = self.get_participants()

        c = django.test.Client()

        # in case some participants haven't started
        unvisited_participants = []
        for p in participants:
            if not p._current_form_page_url:
                unvisited_participants.append(p)
                c.get(p._start_url(), follow=True)

        if unvisited_participants:
            from otree.models import Participant
            for p in unvisited_participants:
                p.save()
                Participant.flush_cached_instance(p)
            # that's it -- just visit the start URL, advancing
            # by 1
            return

        last_place_page_index = min([p._index_in_pages for p in participants])
        last_place_participants = [
            p for p in participants
            if p._index_in_pages == last_place_page_index
        ]

        for p in last_place_participants:
            if not p._current_form_page_url:
                # what if first page is wait page?
                raise
            resp = c.post(p._current_form_page_url,
                          data={constants_internal.auto_submit: True},
                          follow=True)
            assert resp.status_code < 400

    def build_participant_to_player_lookups(self):
        subsession_app_names = self.config['app_sequence']

        num_pages_in_each_app = {}
        for app_name in subsession_app_names:
            views_module = otree.common_internal.get_views_module(app_name)

            num_pages = len(views_module.page_sequence)
            num_pages_in_each_app[app_name] = num_pages

        for participant in self.get_participants():
            participant.build_participant_to_player_lookups(
                num_pages_in_each_app)
Beispiel #18
0
class Group(BaseGroup):
    # before the overthrow, number of reforms is equal to round number
    # refapproved=models.IntegerField()
    # refcalled=models.IntegerField()
    # refpastcalled=models.IntegerField()

    def current_round(self):
        return self.subsession.round_number

    reformed_id = 0

    # pick one player to be reformed
    def reformed_player(self):
        print("get_player_by_id, before while", self.reformed_id)
        while True:
            self.reformed_id = random.randint(1, Constants.players_per_group)
            if self.current_round()  \
                    - self.get_player_by_id(self.reformed_id).participant.vars['called_to_be_reformed']*Constants.players_per_group > 0:
                # break runs if if is satisfied i.e. we picked the "right" player to reform
                self.get_player_by_id(
                    self.reformed_id
                ).participant.vars['called_to_be_reformed'] = 1
                break

        for p in self.get_players():
            if p.id_in_group == self.reformed_id:
                p.participant.vars['reformed_this_round'] = 1
            # remember that if "if" was satisfied, all next conditions are ignored
            elif p.participant.vars['reformed_this_round'] == 1:
                p.participant.vars['reformed_previous_round'] = 1
                p.participant.vars['reformed_this_round'] = 0
            else:
                p.participant.vars['reformed_this_round'] = 0
                p.participant.vars['reformed_previous_round'] = 0

    group_approved_reforms = models.IntegerField(initial=0)
    current_round_reform_approved = False

    def approvals(self):

        if sum(p.approval for p in
               self.get_players()) >= Constants.votes_needed_to_pass_reform:
            self.current_round_reform_approved = True
            if self.current_round() == 1:
                self.group_approved_reforms = 1
            else:
                self.group_approved_reforms = self.in_round(
                    self.current_round() - 1).group_approved_reforms + 1
        else:
            self.group_approved_reforms = self.in_round(
                self.current_round() - 1).group_approved_reforms

        for p in self.get_players():
            if p.participant.vars[
                    'reformed_this_round'] == 1 and self.current_round_reform_approved:
                p.participant.vars['reforms'] += 1

    def approvals_previous_round(self):
        return int(
            sum(p.in_previous_rounds()[-1].approval
                for p in self.get_players()))

    # sums up players votes for overthrow and switches regime, if necessary
    def total_votes_for_overthrow(self):
        if sum(p.vote_to_overthrow for p in self.get_players()
               ) >= Constants.points_to_overthrow and self.session.vars[
                   'overthrow'] == 0:
            self.session.vars[
                'overthrow'] = 1  #never takes place under current settings
            self.session.vars['overthrow_round'] = self.subsession.round_number
            # chaos loses or something
            for p in self.get_players():
                p.payoff -= Constants.losses_from_overthrow

        return sum(p.vote_to_overthrow for p in self.get_players())

    def final_decision(self):
        if sum(p.approval_final for p in self.get_players()) < 3:
            self.session.vars[
                'overthrow'] = 2  #meaning reversal of all reform decisions

    reforms_votes_group = []

    # aggregate proposed number of reforms (after overthrow mechanic)
    def reform(self):
        for p in self.get_players():
            self.reforms_votes_group.append(p.reforms_votes)

    def payoffs(self):
        for p in self.get_players():
            p.payoff = \
                Constants.base_sales \
                - ( p.participant.vars['reforms'] * Constants.reform_penalty ) \
                + (( self.group_approved_reforms - p.participant.vars['reforms'] ) * Constants.reform_benefits)
Beispiel #19
0
class Session(ModelWithVars):
    class Meta:
        app_label = "otree"
        # if i don't set this, it could be in an unpredictable order
        ordering = ['pk']

    _pickle_fields = ['vars', 'config']
    config = models._PickleField(default=dict, null=True)  # type: dict

    # label of this session instance
    label = models.CharField(max_length=300,
                             null=True,
                             blank=True,
                             help_text='For internal record-keeping')

    experimenter_name = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        help_text='For internal record-keeping')

    ready_for_browser = models.BooleanField(default=False)

    code = models.CharField(
        default=random_chars_8,
        max_length=16,
        # set non-nullable, until we make our CharField non-nullable
        null=False,
        unique=True,
        doc="Randomly generated unique identifier for the session.")

    mturk_HITId = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        help_text='Hit id for this session on MTurk')
    mturk_HITGroupId = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        help_text='Hit id for this session on MTurk')

    # since workers can drop out number of participants on server should be
    # greater than number of participants on mturk
    # value -1 indicates that this session it not intended to run on mturk
    mturk_num_participants = models.IntegerField(
        default=-1, help_text="Number of participants on MTurk")

    mturk_use_sandbox = models.BooleanField(
        default=True,
        help_text="Should this session be created in mturk sandbox?")

    # use Float instead of DateTime because DateTime
    # is a pain to work with (e.g. naive vs aware datetime objects)
    # and there is no need here for DateTime
    mturk_expiration = models.FloatField(null=True)

    archived = models.BooleanField(
        default=False,
        db_index=True,
        doc=("If set to True the session won't be visible on the "
             "main ViewList for sessions"))

    comment = models.TextField(blank=True)

    _anonymous_code = models.CharField(default=random_chars_10,
                                       max_length=10,
                                       null=False,
                                       db_index=True)

    _pre_create_id = models.CharField(max_length=255, db_index=True, null=True)

    def use_browser_bots(self):
        return self.participant_set.filter(is_browser_bot=True).exists()

    is_demo = models.BooleanField(default=False)

    _admin_report_app_names = models.TextField(default='')
    _admin_report_num_rounds = models.CharField(default='', max_length=255)

    num_participants = models.PositiveIntegerField()

    def __unicode__(self):
        return self.code

    @property
    def participation_fee(self):
        '''This method is deprecated from public API,
        but still useful internally (like data export)'''
        return self.config['participation_fee']

    @property
    def real_world_currency_per_point(self):
        '''This method is deprecated from public API,
        but still useful internally (like data export)'''
        return self.config['real_world_currency_per_point']

    def is_for_mturk(self):
        return (not self.is_demo) and (self.mturk_num_participants > 0)

    def get_subsessions(self):
        lst = []
        app_sequence = self.config['app_sequence']
        for app in app_sequence:
            models_module = otree.common_internal.get_models_module(app)
            subsessions = models_module.Subsession.objects.filter(
                session=self).order_by('round_number')
            lst.extend(list(subsessions))
        return lst

    def get_participants(self):
        return list(self.participant_set.order_by('id_in_session'))

    def mturk_worker_url(self):
        # different HITs
        # get the same preview page, because they are lumped into the same
        # "hit group". This is not documented, but it seems HITs are lumped
        # if a certain subset of properties are the same:
        # https://forums.aws.amazon.com/message.jspa?messageID=597622#597622
        # this seems like the correct design; the only case where this will
        # not work is if the HIT was deleted from the server, but in that case,
        # the HIT itself should be canceled.

        # 2018-06-04:
        # the format seems to have changed to this:
        # https://worker.mturk.com/projects/{group_id}/tasks?ref=w_pl_prvw
        # but the old format still works.
        # it seems I can't replace groupId by hitID, which i would like to do
        # because it's more precise.
        subdomain = "workersandbox" if self.mturk_use_sandbox else 'www'
        return "https://{}.mturk.com/mturk/preview?groupId={}".format(
            subdomain, self.mturk_HITGroupId)

    def mturk_is_expired(self):
        # self.mturk_expiration is offset-aware, so therefore we must compare
        # it against an offset-aware value.
        return self.mturk_expiration and self.mturk_expiration < time.time()

    def mturk_is_active(self):
        return self.mturk_HITId and not self.mturk_is_expired()

    def advance_last_place_participants(self):
        # django.test takes 0.5 sec to import,
        # if this is done globally then it adds to each startup
        # it's only used here, and often isn't used at all.
        # so best to do it only here
        # it gets cached
        import django.test
        client = django.test.Client()

        participants = self.get_participants()

        # in case some participants haven't started
        unvisited_participants = []
        for p in participants:
            if p._index_in_pages == 0:
                unvisited_participants.append(p)
                client.get(p._start_url(), follow=True)

        if unvisited_participants:
            # that's it -- just visit the start URL, advancing by 1
            return

        last_place_page_index = min([p._index_in_pages for p in participants])
        last_place_participants = [
            p for p in participants
            if p._index_in_pages == last_place_page_index
        ]

        for p in last_place_participants:
            try:
                current_form_page_url = p._current_form_page_url
                if current_form_page_url:
                    resp = client.post(
                        current_form_page_url,
                        data={
                            constants_internal.timeout_happened: True,
                            constants_internal.admin_secret_code:
                            ADMIN_SECRET_CODE
                        },
                        follow=True)
                    # not sure why, but many users are getting HttpResponseNotFound
                    if resp.status_code >= 400:
                        msg = ('Submitting page {} failed, '
                               'returned HTTP status code {}.'.format(
                                   current_form_page_url, resp.status_code))
                        content = resp.content
                        if len(content) < 600:
                            msg += ' response content: {}'.format(content)
                        raise AssertionError(msg)

                else:
                    # it's possible that the slowest user is on a wait page,
                    # especially if their browser is closed.
                    # because they were waiting for another user who then
                    # advanced past the wait page, but they were never
                    # advanced themselves.
                    start_url = p._start_url()
                    resp = client.get(start_url, follow=True)
            except:
                logging.exception("Failed to advance participants.")
                raise

            # do the auto-advancing here,
            # rather than in increment_index_in_pages,
            # because it's only needed here.
            channels.Group('auto-advance-{}'.format(p.code)).send(
                {'text': json.dumps({'auto_advanced': True})})

    def get_room(self):
        from otree.room import ROOM_DICT
        try:
            room_name = RoomToSession.objects.get(session=self).room_name
            return ROOM_DICT[room_name]
        except RoomToSession.DoesNotExist:
            return None

    def _get_payoff_plus_participation_fee(self, payoff):
        '''For a participant who has the given payoff,
        return their payoff_plus_participation_fee
        Useful to define it here, for data export
        '''

        return (self.config['participation_fee'] +
                payoff.to_real_world_currency(self))

    def _set_admin_report_app_names(self):

        admin_report_app_names = []
        num_rounds_list = []
        for app_name in self.config['app_sequence']:
            models_module = otree.common_internal.get_models_module(app_name)
            app_label = get_app_label_from_name(app_name)
            try:
                select_template([
                    f'{app_label}/admin_report.html',
                    f'{app_label}/AdminReport.html',
                ])
            except TemplateDoesNotExist:
                pass
            else:
                admin_report_app_names.append(app_name)
                num_rounds_list.append(models_module.Constants.num_rounds)

        self._admin_report_app_names = ';'.join(admin_report_app_names)
        self._admin_report_num_rounds = ';'.join(
            str(n) for n in num_rounds_list)

    def _admin_report_apps(self):
        return self._admin_report_app_names.split(';')

    def _admin_report_num_rounds_list(self):
        return [int(num) for num in self._admin_report_num_rounds.split(';')]

    def has_admin_report(self):
        return bool(self._admin_report_app_names)
Beispiel #20
0
class Player(otree.models.BasePlayer):

    # <built-in>
    group = models.ForeignKey(Group, null=True)
    subsession = models.ForeignKey(Subsession)
    # </built-in>

    player_name = models.CharField(max_length=255)
    avatar = models.CharField(max_length=255)

    genero = models.CharField(
        max_length=30, widget=widgets.RadioSelectHorizontal(),
        choices=[Constants.hombre, Constants.mujer], verbose_name="Seleccióne su género")

    # bloque 1
    block_1_last_question_clicked = models.IntegerField(default=0, widget=widgets.HiddenInput())

    satisfecho_con_la_vida = models.PositiveIntegerField(
        verbose_name=(
            "En una escala del 1 al 10, donde 1 es nada satisfecho y 10 "
            "totalmente satisfecho, en general ¿qué tan satisfecha(o) se "
            "encuentra usted con su vida? Usted puede escoger cualquier "
            "número entre 1 y 10."), choices=range(1,11),
            widget=widgets.RadioSelectHorizontal(), default=1)

    cuartos_en_el_hogar = models.PositiveIntegerField(
        widget=widgets.SliderInput(), max=20,
        verbose_name=("¿Cuántos cuartos hay en su hogar sin contar pasillos, ni baños?"), min=1, default=1)

    cuantos_cuartos_se_usan_para_dormir = models.PositiveIntegerField(
        widget=widgets.SliderInput(), max=20,
        verbose_name=("Y de esos cuartos, ¿cuántos usan para dormir?"), min=1, default=1)

    habitantes = models.PositiveIntegerField(
        widget=widgets.SliderInput(), max=20,
        verbose_name=("¿Cuántas personas viven en su hogar contando ancianos y niños?"), min=1, default=1)

    focos = models.PositiveIntegerField(
        widget=widgets.SliderInput(), max=50,
        verbose_name=(
            "Contando todos los focos que utiliza para iluminar su hogar, "
            "incluyendo los de techos, paredes y lámparas de buró o piso, "
            "dígame, ¿Cuántos focos tiene en su vivienda?"), min=1, default=1)

    con_quien_vive = models.CharField(
        verbose_name=("¿Con quién vive actualmente?"), max_length=255, default="---",
        choices=["---", "Solo", "Con una pareja", "Con amigos", "Con esposo(a)", "Con familia", "Otro"])

    cuenta_con_automovil = models.BooleanField(
        verbose_name=("Automóvil propio excluyendo taxis"),
        widget=widgets.RadioSelectHorizontal(), default=False)
    cuantos_automovil = models.PositiveIntegerField(default=0)

    cuenta_con_televisor = models.BooleanField(
        verbose_name=("Televisor"),
        widget=widgets.RadioSelectHorizontal(), default=False)
    cuantos_televisor = models.PositiveIntegerField(default=0)

    cuenta_con_celular = models.BooleanField(
        verbose_name=("Teléfono Celular"),
        widget=widgets.RadioSelectHorizontal(), default=False)
    cuantos_celular = models.PositiveIntegerField(default=0)

    cuenta_con_computadora = models.BooleanField(
        verbose_name=("Computadora de escritorio o portátil"),
        widget=widgets.RadioSelectHorizontal(), default=False)
    cuantos_computadora = models.PositiveIntegerField(default=0)

    cuenta_con_bano = models.BooleanField(
        verbose_name=("Baño completo con regadera y excusado para uso exclusivo del hogar"),
        widget=widgets.RadioSelectHorizontal(), default=False)
    cuantos_bano = models.PositiveIntegerField(default=0)

    cuenta_con_servidumbre = models.BooleanField(
        verbose_name=("Personas de servicio doméstico como limpieza, jardinero o chofer"),
        widget=widgets.RadioSelectHorizontal(), default=False)
    cuantos_servidumbre = models.PositiveIntegerField(default=0)

    tarjeta_de_credito = models.BooleanField(
        verbose_name=("Cuenta usted con tarjeta de crédito"),
        widget=widgets.RadioSelectHorizontal(), default=False)

    altura = models.FloatField(
        verbose_name=("Aproximadamente, ¿qué estatura tiene usted? (en metros)"),
        min=0, widget=widgets.SliderInput(attrs={'step': '0.01', 'max': '2.5'}), default=0)

    peso = models.PositiveIntegerField(
        verbose_name=("Aproximadamente, ¿qué peso tiene usted? (en kilogramos)"),
        min=0, max=200, widget=widgets.SliderInput(), default=0)

    ejercicio_fisico = models.BooleanField(
        verbose_name=("Durante las últimas dos semanas, ¿ha realizado usted ejercicio físico fuera del trabajo?"),
        widget=widgets.RadioSelectHorizontal(), default=False)

    cigarrillo = models.BooleanField(
        verbose_name=("Durante las últimas dos semanas, ¿ha fumado algún cigarrillo o puro?"),
        widget=widgets.RadioSelectHorizontal(), default=False)

    vive_con_padre = models.BooleanField(
        widget=widgets.RadioSelectHorizontal(), default=False)
    vive_con_madre = models.BooleanField(
        widget=widgets.RadioSelectHorizontal(), default=False)

    edad_padre = models.PositiveIntegerField(
        choices=range(30, 101), default=30)
    edad_madre = models.PositiveIntegerField(
        choices=range(30, 101), default=30)

    padre_habla_dialecto_indigena = models.BooleanField(
        widget=widgets.RadioSelectHorizontal(), default=False)
    madre_habla_dialecto_indigena = models.BooleanField(
        widget=widgets.RadioSelectHorizontal(), default=False)

    padre_habla_lengua_extranjera = models.BooleanField(
        widget=widgets.RadioSelectHorizontal(), default=False)
    madre_habla_lengua_extranjera = models.BooleanField(
        widget=widgets.RadioSelectHorizontal(), default=False)

    nivel_educacion_padre = models.CharField(
        choices=["---", "Primaria", "Secundaria", "Preparatoria", "Universitario"],
        max_length=50, default="---")
    nivel_educacion_madre = models.CharField(
        choices=["---", "Primaria", "Secundaria", "Preparatoria", "Universitario"],
        max_length=50, default="---")

    # bloque 2
    block_2_last_question_clicked = models.IntegerField(default=0, widget=widgets.HiddenInput())

    riqueza_hogar_14_anios = models.PositiveIntegerField(
        widget=widgets.RadioSelectHorizontal(),
        verbose_name=(
            "Comparando el hogar donde vivía a los 14 años con todos los "
            "hogares actuales de México y usando una escala de 1 a 10, en la "
            "que 1 son los hogares más pobres y 10 son los más ricos, ¿dónde "
            "pondría usted su hogar de ese entonces?"),
        choices=range(1, 11), default=1)

    TRABAJOS = ("---", "Patrón o empleador ", "Trabajador por cuenta propia ",
            "Empleado u obrero del sector público",
            "Empleado u obrero del sector privado", "Servicio Doméstico (por pago)",
            "Quehaceres del hogar (sin pago) ", "Trabajador sin pago ",
            "Fuerzas armadas y del orden")

    padre_trabajaba_14_anios = models.BooleanField(widget=widgets.RadioSelectHorizontal(), default=False)
    madre_trabajaba_14_anios = models.BooleanField(widget=widgets.RadioSelectHorizontal(), default=False)
    padre_ocupacion_14_anios = models.CharField(max_length=255, choices=TRABAJOS, default=TRABAJOS[0])
    madre_ocupacion_14_anios = models.CharField(max_length=255, choices=TRABAJOS, default=TRABAJOS[0])
    padre_trabajo_servicios_medicos_14_anios = models.BooleanField(widget=widgets.RadioSelectHorizontal(), default=False)
    madre_trabajo_servicios_medicos_14_anios = models.BooleanField(widget=widgets.RadioSelectHorizontal(), default=False)

    CON_QUIEN_VIVIA = (
        "---", "Solo con el padre", "Solo con la madre",
        "Con ambos, padre y madre", "Con otra familia", "Otra persona")
    con_quien_vivia_14_anios = models.CharField(
        verbose_name=("Cuando usted tenía alrededor de 14 años ¿con quién vivía?"),
        choices=CON_QUIEN_VIVIA, default=CON_QUIEN_VIVIA[0], max_length=255)

    MUCHO_REGULAR_POCO_NADA = ["---", "Mucho", "Regular", "Poco", "Nada"]
    padre_emocionalmente_cerano_14_anios = models.CharField(
        max_length=20, choices=MUCHO_REGULAR_POCO_NADA, default=MUCHO_REGULAR_POCO_NADA[0])
    madre_emocionalmente_cerano_14_anios = models.CharField(
        max_length=20, choices=MUCHO_REGULAR_POCO_NADA, default=MUCHO_REGULAR_POCO_NADA[0])
    padre_entendia_problemas_14_anios = models.CharField(
        max_length=20, choices=MUCHO_REGULAR_POCO_NADA, default=MUCHO_REGULAR_POCO_NADA[0])
    madre_entendia_problemas_14_anios = models.CharField(
        max_length=20, choices=MUCHO_REGULAR_POCO_NADA, default=MUCHO_REGULAR_POCO_NADA[0])
    padre_actividades_escolares_14_anios = models.CharField(
        max_length=20, choices=MUCHO_REGULAR_POCO_NADA, default=MUCHO_REGULAR_POCO_NADA[0])
    madre_actividades_escolares_14_anios = models.CharField(
        max_length=20, choices=MUCHO_REGULAR_POCO_NADA, default=MUCHO_REGULAR_POCO_NADA[0])
    padre_actividades_tiempo_libre_14_anios = models.CharField(
        max_length=20, choices=MUCHO_REGULAR_POCO_NADA, default=MUCHO_REGULAR_POCO_NADA[0])
    madre_actividades_tiempo_libre_14_anios = models.CharField(
        max_length=20, choices=MUCHO_REGULAR_POCO_NADA, default=MUCHO_REGULAR_POCO_NADA[0])
    padre_reglas_claras_14_anios = models.CharField(
        max_length=20, choices=MUCHO_REGULAR_POCO_NADA, default=MUCHO_REGULAR_POCO_NADA[0])
    madre_reglas_claras_14_anios = models.CharField(
        max_length=20, choices=MUCHO_REGULAR_POCO_NADA, default=MUCHO_REGULAR_POCO_NADA[0])

    relacion_padres_14_anios = models.CharField(
        verbose_name=("¿La relación entre sus padres generalmente era: excelente, buena, regular, mala o muy mala?"),
        max_length=20, default="---",
        choices=["---", "Excelente", "Buena", "Regular", "Mala", "Muy mala"])

    FAMILIA_FRECUENCIA = ("Siempre", "Frecuentemente", "Pocas veces", "Nunca", "Omitir")
    familia_frecuencia_insultos_14_anios = models.CharField(
        verbose_name=("¿Con qué frecuencia ocurrían INSULTOS, GRITOS o AMENAZAS en su familia?"),
        max_length=20, choices=FAMILIA_FRECUENCIA, default=FAMILIA_FRECUENCIA[-1])
    familia_frecuencia_cercania_14_anios = models.CharField(
        verbose_name=("¿Con qué frecuencia los miembros de su familia se sentían muy cercanos los unos de los otros?"),
        max_length=20, choices=FAMILIA_FRECUENCIA, default=FAMILIA_FRECUENCIA[-1])
    frequencia_miedos_14_anios = models.CharField(
        verbose_name=("¿Con qué frecuencia la/lo molestaban miedos o preocupaciones?"),
        max_length=20, choices=FAMILIA_FRECUENCIA, default=FAMILIA_FRECUENCIA[-1])

    madre_trabajo_por_ingreso_desde_que_nacio = models.BooleanField(
        verbose_name=("¿Su madre trabajo por un ingreso en algún momento desde que usted nació hasta el día de hoy?"),
        widget=widgets.RadioSelectHorizontal(), default=False)

    madre_trabajo_periodos_de_su_vida_0_4_anios = models.BooleanField(
        verbose_name=("0 a 4 años de edad"), widget=widgets.RadioSelectHorizontal(), default=False)

    madre_trabajo_periodos_de_su_vida_5_9_anios = models.BooleanField(
        verbose_name=("5 a 9 años de edad"), widget=widgets.RadioSelectHorizontal(), default=False)

    madre_trabajo_periodos_de_su_vida_10_14_anios = models.BooleanField(
        verbose_name=("10 a 14 años de edad"), widget=widgets.RadioSelectHorizontal(), default=False)

    madre_trabajo_periodos_de_su_vida_15_19_anios = models.BooleanField(
        verbose_name=("15 a 19 años de edad"), widget=widgets.RadioSelectHorizontal(), default=False)

    tiene_hermanos = models.BooleanField(
        verbose_name=("¿Tiene usted hermanos o hermanas?"),
        widget=widgets.RadioSelectHorizontal(), default=False)

    numero_de_hermano_que_es_usted = models.PositiveIntegerField(
        verbose_name=(
            "Ahora piense en sus hermanos ordenándolos del mayor al menor "
            "aunque hayan fallecido y dígame qué número de hermano es usted."),
        choices=range(1, 11), default=1, widget=widgets.RadioSelectHorizontal())

    HERMANOS_1_10 = ["---"] + [str(i) for i in range(1, 11)]
    hermanos_que_son = models.CharField(choices=HERMANOS_1_10, max_length=10, default=HERMANOS_1_10[0])
    hermanas_que_son = models.CharField(choices=HERMANOS_1_10, max_length=10, default=HERMANOS_1_10[0])
    hermanos_viven_con_usted_actualmente = models.CharField(choices=HERMANOS_1_10, max_length=10, default=HERMANOS_1_10[0])
    hermanas_viven_con_usted_actualmente = models.CharField(choices=HERMANOS_1_10, max_length=10, default=HERMANOS_1_10[0])
    hermanos_trabajan_por_pago_actualmente = models.CharField(choices=HERMANOS_1_10, max_length=10, default=HERMANOS_1_10[0])
    hermanas_trabajan_por_pago_actualmente = models.CharField(choices=HERMANOS_1_10, max_length=10, default=HERMANOS_1_10[0])

    espera_trabajar_remonerado_mayor_parte_de_su_vida = models.BooleanField(
        widget=widgets.RadioSelectHorizontal(),
        verbose_name=("¿Espera usted trabajar de forma remunerada la mayor parte de su vida?"), default=False)

    TRABAJO_FUTURO = [
        "---", "Asalariado", "Auto-empleado", "Dueño de negocio",
        "Dueño de empresa", "Ninguna de las anteriores"]
    de_que_manera_espera_trabajar = models.CharField(
        verbose_name="¿De qué manera espera trabajar?",
        choices=TRABAJO_FUTURO, max_length=100, default=TRABAJO_FUTURO[0])

    cuanto_cree_que_ganaria_en_30_anios = models.PositiveIntegerField(
        widget=widgets.SliderInput(), max=100000,
        verbose_name=(
            "Imagine que usted tiene 30 años el día de hoy y está trabajando "
            "de forma remunerada. ¿Cuánto cree que ganaría al mes por su trabajo?"), default=0)

    cree_que_tendra_hijo = models.CharField(
        verbose_name="Cree que usted tenga un hijo en algún momento de la vida?",
        choices=["---", "Si", "No", "Ya lo tuvo"], max_length=100, default="---")

    edad_de_primer_hijo = models.PositiveIntegerField(
        verbose_name=("¿A qué edad espera tener su primer hijo?"), choices=range(1, 100), default=0)

    edad_tuvo_primer_hijo = models.PositiveIntegerField(
        verbose_name=("¿A qué edad tuvo a su primer hijo?"), choices=range(1, 100), default=0)

    # BLOQUE 3
    block_3_last_question_clicked = models.IntegerField(default=0, widget=widgets.HiddenInput())

    RANGO_1_10 = range(1, 11)

    hogar_actual_vs_mexico = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Comparando su hogar actual con todos los hogares actuales de México y usando una escala de 1 a 10, en la que 1 son los hogares más pobres y 10 son los más ricos, ¿dónde pondría usted su hogar actual?"))

    cuanto_depende_de_usted_que_le_vaya_bien = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("En una escala del 1 al 10, donde 1 es “nada depende de usted” y 10 es “todo depende de usted”, ¿qué tanto depende de usted misma(o) que le vaya bien en este año y el próximo?"))

    gobierno_o_sociedad_pobreza = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0],
        widget=widgets.RadioSelectHorizontal(), verbose_name=("Pobreza"))
    gobierno_o_sociedad_delincuencia = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0],
        widget=widgets.RadioSelectHorizontal(), verbose_name=("Delincuencia"))
    gobierno_o_sociedad_narcotrafico = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0],
        widget=widgets.RadioSelectHorizontal(), verbose_name=("Narcotráfico"))
    gobierno_o_sociedad_corrupcion = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0],
        widget=widgets.RadioSelectHorizontal(), verbose_name=("Corrupción"))
    gobierno_o_sociedad_educacion = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0],
        widget=widgets.RadioSelectHorizontal(), verbose_name=("Mala o Poca Educación"))
    gobierno_o_sociedad_discriminacion = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0],
        widget=widgets.RadioSelectHorizontal(), verbose_name=("Discriminación"))
    gobierno_o_sociedad_adicciones_y_enfermedades = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0],
        widget=widgets.RadioSelectHorizontal(), verbose_name=("Atención de adicciones y enfermedades"))

    dispuesto_a_tomar_riesgos = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("En una escala del 1 al 10, donde 1 es nada dispuesto y 10 totalmente dispuesto, ¿qué tan dispuesto está a tomar riesgos?"))

    describe_como_persona_abstengo_hacer_cosas_hoy = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Me abstengo de cosas hoy para poder tener más mañana"))
    describe_como_persona_retrazo_cosas = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Tiendo a retrasar cosas aun cuando sería mejor hacerlas de una vez"))
    describe_como_persona_asumo_gente_tiene_mejores_intenciones = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Mientras no esté convencido de lo contrario, siempre asumo que la gente tiene las mejores intenciones"))
    describe_como_persona_no_entiendo_pelea_no_le_beneficia = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("No entiendo por qué alguna gente dedica su vida a pelear por una causa que no les beneficia directamente"))
    describe_como_persona_ayudo_al_que_me_ayudo = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Realizo un gran esfuerzo para ayudar a alguien que me ha ayudado antes"))
    describe_como_persona_me_vengo = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Si alguien me hace algo malo a propósito, trataré de regresárselo"))

    que_tan_impulsivo_se_considera = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("En una escala del 1 al 10, donde 1 es totalmente y 10 es nada, ¿qué tan impulsivo se considera?"))

    dispuesto_a_confiar = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Confiar en otras personas"))
    dispuesto_a_compartir = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Compartir algo con otras personas sin nada a cambio"))
    dispuesto_a_regresar_favor = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Regresar un favor a un extraño"))
    dispuesto_a_castigar = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Castigar a alguien debido a una conducta injusta aun cuando es costoso. Ej. Decirle a una persona que no tire basura en la calle aun cuando esa persona se puede enojar y decirle algo."))

    que_tan_paciente_se_considera = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("En una escala del 1 al 10, donde 1 es muy paciente y 10 es muy impaciente, ¿qué tan paciente o impaciente se considera? Usted puede escoger cualquier número entre 1 y 10."))

    acuerdo_con_educacion_genera_ingreso = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("El nivel educativo determina el nivel de ingreso de una persona"))
    acuerdo_con_hombres_mas_trabajo_que_mujeres = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Cuando no hay mucho trabajo, los hombres deberían de tener preferencia a un trabajo antes que las mujeres"))
    acuerdo_con_esposa_que_gana_mas_genera_dinero = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("La esposa que gana más dinero que el esposo genera problemas"))
    acuerdo_con_no_se_puede_confiar_en_nadie = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Hoy en día, no se puede confiar en nadie más"))
    acuerdo_con_camino_de_mi_vida = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("El camino de mi vida depende de mí"))
    acuerdo_con_he_logrado_lo_que_merezco = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("En comparación con otros, no he logrado lo que merezco"))
    acuerdo_con_logros_suerte = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Lo que se logra en la vida es principalmente una cuestión de destino o suerte"))
    acuerdo_con_otros_deciden_por_mi = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Frecuentemente tengo la sensación de que otros toman decisiones sobre mi vida"))
    acuerdo_con_puedo_influir_en_condicion_social = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Si uno es social o políticamente activo, se puede influir en las condiciones sociales"))
    acuerdo_con_trabajar_duro_exito = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Hay que trabajar duro para alcanzar el éxito"))
    acuerdo_con_dudo_de_mi_mismo = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Si enfrento dificultades en la vida, frecuentemente dudo de mí mismo"))
    acuerdo_con_oportunidades_dadas_por_condiciones_sociales = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Las oportunidades que tengo en la vida están determinadas por las condiciones sociales"))
    acuerdo_con_habilidades_mas_que_esfuerzo = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Las habilidades con las que nací son más importantes que todo el esfuerzo que yo pueda hacer"))
    acuerdo_con_poco_control_de_la_vida = models.PositiveIntegerField(
        choices=RANGO_1_10, default=RANGO_1_10[0], widget=widgets.RadioSelectHorizontal(),
        verbose_name=("Tengo poco control sobre las cosas que suceden en mi vida"))

    TOTALMENTE_MUCHO_REGULAR_POCO_NADA = ("---", "Totalmente", "Mucho", "Regular", "Poco", "Nada")
    que_tanto_es_es_reservado = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Es reservado(a)"))
    que_tanto_es_confiable = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Es generalmente confiable"))
    que_tanto_es_flojo = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Tiende a ser flojo(a)"))
    que_tanto_es_relajado = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Es relajado(a) o maneja bien el estrés"))
    que_tanto_es_artista = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Tiene intereses artísticos"))
    que_tanto_es_sociable = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Es extrovertido(a) o sociable"))
    que_tanto_es_falla_en_los_demas = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Tiende a encontrar fallas en los demás"))
    que_tanto_es_nervioso = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Se pone nervioso(a) fácilmente"))
    que_tanto_es_imaginador = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Tiene una imaginación activa"))

    que_tanto_lo_describe_ideas_me_distraen = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Algunas veces nuevas ideas y proyectos me distraen de mis proyectos e ideas anteriores"))
    que_tanto_lo_describe_contratiempos_desanima = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Los contratiempos no me desaniman"))
    que_tanto_lo_describe_persona_trabajadora = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Soy una persona muy trabajadora"))
    que_tanto_lo_describe_pierdo_interes = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Me obsesiono con ideas o proyectos por un tiempo, pero pierdo el interés rápidamente."))
    que_tanto_lo_describe_persigo_diferentes_metas = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Frecuentemente me pongo una meta pero más tarde persigo una diferente."))
    que_tanto_lo_describe_dificultades_para_concentracion = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Tengo dificultades para mantener mi concentración en proyectos que toman más de unos cuantos meses en completarse."))
    que_tanto_lo_describe_termino_lo_que_comienzo = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Termino todo lo que comienzo"))
    que_tanto_lo_describe_efuerzo_en_mi_trabajo = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Pongo mucho esfuerzo en los trabajos que realizo"))
    que_tanto_lo_describe_malos_habitos = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Me cuesta romper malos hábitos"))
    que_tanto_lo_describe_cosas_inapropiadas = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Frecuentemente digo cosas inapropiadas"))
    que_tanto_lo_describe_resisto_tentaciones = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Soy muy bueno para resistir tentaciones"))
    que_tanto_lo_describe_me_arrepiento = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Hago cosas que se sienten bien en el momento pero después me arrepiento de ellas"))
    que_tanto_lo_describe_hago_cosas_sin_pensar = models.CharField(
        choices=TOTALMENTE_MUCHO_REGULAR_POCO_NADA, default=TOTALMENTE_MUCHO_REGULAR_POCO_NADA[0],
        max_length=30, verbose_name=("Frecuentemente hago cosas sin pensar en todas las opciones"))

    apuesta_loteria_1000 = models.PositiveIntegerField(
        verbose_name=("Imagine un juego de lotería con diez boletos numerados del 1 al 10, cuyo premio al número ganador es de $1,000. ¿Cuánto estaría dispuesto a pagar por un boleto para participar en ella?"),
        min=0, max=1000, widget=widgets.SliderInput(), default=0)

    donar_1000 = models.PositiveIntegerField(
        verbose_name=("Imagine la siguiente situación: El día de hoy usted recibe inesperadamente $1,000. ¿Cuánto consideraría donar a una buena causa?"),
        min=0, max=1000, widget=widgets.SliderInput(), default=0)

    pagan_100_esperar_3_meses = models.PositiveIntegerField(
        min=1000, max=50000,  widget=widgets.SliderInput(),
        verbose_name=("¿Cuánto le tendrían que pagar dentro de tres meses para que pueda esperar este tiempo?"), default=0)
    que_tanto_lo_describe_1_anio = models.PositiveIntegerField(
        min=1000, max=50000,  widget=widgets.SliderInput(),
        verbose_name=("Y ahora, ¿cuánto le tendrían que pagar dentro de un año para que pueda esperar ese tiempo?"), default=0)

    # bloque 4
    block_4_last_question_clicked = models.IntegerField(default=0, widget=widgets.HiddenInput())

    # MUJERES
    mestruando = models.BooleanField(
        verbose_name="¿Se encuentra usted menstruando el día de hoy?", default=False, widget=widgets.RadioSelectHorizontal())

    fecha_ultima_regla = models.DateField(
        default=lambda: timezone.now().date(),
        verbose_name="Fecha de comienzo de última regla (si no recuerda con precisión, favor de indicar la fecha más próxima).")
    fecha_siguiente_regla = models.DateField(
        default=lambda: timezone.now().date(),
        verbose_name="Fecha esperada de comienzo de siguiente regla (si no puede calcular con precisión, favor de indicar la fecha más próxima independientemente de si usted es regular o irregular)")

    anticonceptivos = models.BooleanField(
        verbose_name="¿Utiliza pastillas anticonceptivas?", default=False, widget=widgets.RadioSelectHorizontal())

    duracion_del_sangrado = models.PositiveIntegerField(
        verbose_name=("¿Normalmente, cuántos días dura el sangrado?"),
        widget=widgets.SliderInput(), default=5, min=1, max=10)

    # HOMBRES
    trabaja_empresa_dependencia_publica = models.BooleanField(
        verbose_name="Actualmente se encuentra trabajando en alguna empresa o dependencia pública?",
        default=False, widget=widgets.RadioSelectHorizontal())

    fecha_busqueda_empleo = models.DateField(
        default=lambda: timezone.now().date(),
        verbose_name="Aproximadamente, cuando fue la última fecha en que buscó empleo?.")
    fecha_fin_de_estudios  = models.DateField(
        default=lambda: timezone.now().date(),
        verbose_name="Fecha en la que dio por terminados sus estudios?")

    discriminacion_laboral = models.BooleanField(
        verbose_name="Desde su perspectiva, alguna vez ha sido victima de discriminación laboral?",
        default=False, widget=widgets.RadioSelectHorizontal())

    que_tanto_presencio_discriminacion = models.PositiveIntegerField(
        verbose_name=("En una escala del 1 al 10, que tanto ha presenciado discriminación, para usted o algún compañero, en el campo laboral?"),
        widget=widgets.SliderInput(), default=5, min=1, max=10)


    # Bloque 5
    FIGURE_CHOICES_6 = ['---', '1', '2', '3', '4', '5', '6']

    fig1 = models.CharField(
        verbose_name="fig1.jpg",
        max_length=10, choices=FIGURE_CHOICES_6, default=FIGURE_CHOICES_6[0])
    fig2 = models.CharField(
        verbose_name="fig2.jpg",
        max_length=10, choices=FIGURE_CHOICES_6, default=FIGURE_CHOICES_6[0])
    fig3 = models.CharField(
        verbose_name="fig3.jpg",
        max_length=10, choices=FIGURE_CHOICES_6, default=FIGURE_CHOICES_6[0])
    fig4 = models.CharField(
        verbose_name="fig4.jpg",
        max_length=10, choices=FIGURE_CHOICES_6, default=FIGURE_CHOICES_6[0])

    FIGURE_CHOICES_8 = ['---', '1', '2', '3', '4', '5', '6', '7', '8']
    fig5 = models.CharField(
        verbose_name="fig5.jpg",
        max_length=10, choices=FIGURE_CHOICES_8, default=FIGURE_CHOICES_8[0])
    fig6 = models.CharField(
        verbose_name="fig6.jpg",
        max_length=10, choices=FIGURE_CHOICES_8, default=FIGURE_CHOICES_8[0])
    fig7 = models.CharField(
        verbose_name="fig7.jpg",
        max_length=10, choices=FIGURE_CHOICES_8, default=FIGURE_CHOICES_8[0])
    fig8 = models.CharField(
        verbose_name="fig8.jpg",
        max_length=10, choices=FIGURE_CHOICES_8, default=FIGURE_CHOICES_8[0])
    fig9 = models.CharField(
        verbose_name="fig9.jpg",
        max_length=10, choices=FIGURE_CHOICES_8, default=FIGURE_CHOICES_8[0])
    fig10 = models.CharField(
        verbose_name="fig10.jpg",
        max_length=10, choices=FIGURE_CHOICES_8, default=FIGURE_CHOICES_8[0])
Beispiel #21
0
class Session(ModelWithVars):
    class Meta:
        app_label = "otree"
        # if i don't set this, it could be in an unpredictable order
        ordering = ['pk']

    config = models.JSONField(default=dict, null=True)  # type: dict
    vars = models.JSONField(default=dict)  # type: dict

    # label of this session instance
    label = models.CharField(max_length=300,
                             null=True,
                             blank=True,
                             help_text='For internal record-keeping')

    experimenter_name = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        help_text='For internal record-keeping')

    ready = models.BooleanField(default=False)

    code = models.CharField(
        default=random_chars_8,
        max_length=16,
        # set non-nullable, until we make our CharField non-nullable
        null=False,
        unique=True,
        doc="Randomly generated unique identifier for the session.")

    time_scheduled = models.DateTimeField(
        null=True,
        doc="The time at which the session is scheduled",
        help_text='For internal record-keeping',
        blank=True)

    time_started = models.DateTimeField(
        null=True,
        doc="The time at which the experimenter started the session")

    mturk_HITId = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        help_text='Hit id for this session on MTurk')
    mturk_HITGroupId = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        help_text='Hit id for this session on MTurk')
    mturk_qualification_type_id = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        help_text='Qualification type that is '
        'assigned to each worker taking hit')

    # since workers can drop out number of participants on server should be
    # greater than number of participants on mturk
    # value -1 indicates that this session it not intended to run on mturk
    mturk_num_participants = models.IntegerField(
        default=-1, help_text="Number of participants on MTurk")

    mturk_sandbox = models.BooleanField(
        default=True,
        help_text="Should this session be created in mturk sandbox?")

    archived = models.BooleanField(
        default=False,
        db_index=True,
        doc=("If set to True the session won't be visible on the "
             "main ViewList for sessions"))

    comment = models.TextField(blank=True)

    _anonymous_code = models.CharField(default=random_chars_10,
                                       max_length=10,
                                       null=False,
                                       db_index=True)

    _pre_create_id = models.CharField(max_length=300, db_index=True, null=True)

    use_browser_bots = models.BooleanField(default=False)

    # if the user clicks 'start bots' twice, this will prevent the bots
    # from being run twice.
    _cannot_restart_bots = models.BooleanField(default=False)
    _bots_finished = models.BooleanField(default=False)
    _bots_errored = models.BooleanField(default=False)
    _bot_case_number = models.PositiveIntegerField()

    is_demo = models.BooleanField(default=False)

    # whether SOME players are bots
    has_bots = models.BooleanField(default=False)

    def __unicode__(self):
        return self.code

    @property
    def participation_fee(self):
        '''This method is deprecated from public API,
        but still useful internally (like data export)'''
        return self.config['participation_fee']

    @property
    def real_world_currency_per_point(self):
        '''This method is deprecated from public API,
        but still useful internally (like data export)'''
        return self.config['real_world_currency_per_point']

    def is_for_mturk(self):
        return (not self.is_demo) and (self.mturk_num_participants > 0)

    def get_subsessions(self):
        lst = []
        app_sequence = self.config['app_sequence']
        for app in app_sequence:
            models_module = otree.common_internal.get_models_module(app)
            subsessions = models_module.Subsession.objects.filter(
                session=self).order_by('round_number')
            lst.extend(list(subsessions))
        return lst

    def delete(self, using=None):
        for subsession in self.get_subsessions():
            subsession.delete()
        super(Session, self).delete(using)

    def get_participants(self):
        return self.participant_set.all()

    def get_human_participants(self):
        return self.participant_set.filter(_is_bot=False)

    def _create_groups_and_initialize(self):
        # group_by_arrival_time code used to be here
        for subsession in self.get_subsessions():
            subsession._create_groups()
            subsession.before_session_starts()
            subsession.save()

    def mturk_requester_url(self):
        if self.mturk_sandbox:
            requester_url = (
                "https://requestersandbox.mturk.com/mturk/manageHITs")
        else:
            requester_url = "https://requester.mturk.com/mturk/manageHITs"
        return requester_url

    def mturk_worker_url(self):
        if self.mturk_sandbox:
            return ("https://workersandbox.mturk.com/mturk/preview?groupId={}"
                    ).format(self.mturk_HITGroupId)
        return ("https://www.mturk.com/mturk/preview?groupId={}").format(
            self.mturk_HITGroupId)

    def advance_last_place_participants(self):

        # can't auto-advance bots, because that could break their
        # pre-determined logic
        # consider pros/cons of doing this
        participants = self.get_human_participants()

        # in case some participants haven't started
        unvisited_participants = []
        for p in participants:
            if p._index_in_pages == 0:
                unvisited_participants.append(p)
                client.get(p._start_url(), follow=True)

        if unvisited_participants:
            # that's it -- just visit the start URL, advancing by 1
            return

        last_place_page_index = min([p._index_in_pages for p in participants])
        last_place_participants = [
            p for p in participants
            if p._index_in_pages == last_place_page_index
        ]

        for p in last_place_participants:
            # what if first page is wait page?
            # that shouldn't happen, because then they must be
            # waiting for some other players who are even further back
            assert p._current_form_page_url
            try:
                resp = client.post(p._current_form_page_url,
                                   data={constants_internal.auto_submit: True},
                                   follow=True)
            except:
                logging.exception("Failed to advance participants.")
                raise

            assert resp.status_code < 400

    def build_participant_to_player_lookups(self):
        subsession_app_names = self.config['app_sequence']

        views_modules = {}
        for app_name in subsession_app_names:
            views_modules[app_name] = (
                otree.common_internal.get_views_module(app_name))

        def views_module_for_player(player):
            return views_modules[player._meta.app_config.name]

        records_to_create = []

        for participant in self.get_participants():
            page_index = 0
            for player in participant.get_players():
                for View in views_module_for_player(player).page_sequence:
                    page_index += 1
                    records_to_create.append(
                        ParticipantToPlayerLookup(
                            participant=participant,
                            page_index=page_index,
                            app_name=player._meta.app_config.name,
                            player_pk=player.pk,
                            url=reverse(View.url_name(),
                                        args=[participant.code, page_index])))

            # technically could be stored at the session level
            participant._max_page_index = page_index
            participant.save()
        ParticipantToPlayerLookup.objects.bulk_create(records_to_create)

    def get_room(self):
        from otree.room import ROOM_DICT
        try:
            room_name = RoomToSession.objects.get(session=self).room_name
            return ROOM_DICT[room_name]
        except RoomToSession.DoesNotExist:
            return None
Beispiel #22
0
class Player(otree.models.BasePlayer):

    # <built-in>
    group = models.ForeignKey(Group, null=True)
    subsession = models.ForeignKey(Subsession)
    # </built-in>

    demo_field1 = models.CharField(
        choices=['0', '1', '2', 'do not know'],
        doc="""field With radiobutton input.""",
        widget=widgets.RadioSelect(),
    )
    demo_field2 = models.CharField(max_length=5,
                                   doc="""
        field with text input
        """)

    def set_payoff(self):
        self.payoff = c(0)

    training_question_1 = models.IntegerField()
    training_question_2 = models.CharField(
        widget=widgets.RadioSelect(),
        choices=[
            'Embed images', 'Dynamic visualizations using HighCharts',
            'Time travel (opens in pop up window)', 'Embed video',
            'Embed audio'
        ])

    training_question_3 = models.CharField(
        widget=widgets.RadioSelect(),
        choices=['Windows', 'Mac OS X', 'iOS', 'Android', 'Any of the above'])
    training_question_4 = models.CharField(
        widget=widgets.RadioSelect(),
        choices=[
            "Calculation of payoff", "Progress of players through pages",
            "Values submitted by the players",
            "Whether players have visited the game", "All of the above"
        ])
    training_question_5 = models.CharField(
        widget=widgets.RadioSelect(),
        choices=[
            "Any participants' input/choice", "Time spent on each page",
            "Invalid attempts from participants",
            "Answers to understanding questions", "Questionnaire input"
        ])

    def training_question_1_error_message(self, value):
        if value < 0 and abs(value) % 2 == 0:
            return 'Your entry is invalid.'

    # check correct answers
    def is_training_question_1_correct(self):
        return self.training_question_1 == Constants.training_1_correct

    def is_training_question_2_correct(self):
        return self.training_question_2 == Constants.training_2_correct

    def is_training_question_3_correct(self):
        return self.training_question_3 == Constants.training_3_correct

    def is_training_question_4_correct(self):
        return self.training_question_4 == Constants.training_4_correct

    def is_training_question_5_correct(self):
        return self.training_question_5 == Constants.training_5_correct
Beispiel #23
0
class JSONFieldModel(ModelWithVars):
    integer = models.IntegerField(default=0)
    json_field = models.JSONField()
Beispiel #24
0
 class RadioModel(otree.db.models.Model):
     f_radio = models.IntegerField(blank=True,
                                   choices=[1, 2],
                                   widget=widgets.RadioSelect())
Beispiel #25
0
class Group(BaseGroup):

    computer_submission = models.CurrencyField()

    player_5_earnings = models.CurrencyField()

    player_5_acceptance = models.CharField()

    player_4_earnings = models.CurrencyField()

    player_4_acceptance = models.CharField()

    total_contribution = models.CurrencyField()

    individual_share = models.CurrencyField()

    lowest_contribution = models.CurrencyField()

    should_display_warning3 = False

    should_display_warning4 = True

    should_display_warning5 = True

    should_display_warning6 = True

    should_display_warning7 = True

    should_display_warning8 = True

    should_display_warning9 = True

    should_display_warning10 = True

    should_display_warning11 = True

    should_display_warning12 = True

    count3 = models.IntegerField(initial=1)

    count4 = models.IntegerField(initial=1)

    count5 = models.IntegerField(initial=1)

    count6 = models.IntegerField(initial=1)

    count7 = models.IntegerField(initial=1)

    count8 = models.IntegerField(initial=1)

    count9 = models.IntegerField(initial=1)

    count10 = models.IntegerField(initial=1)

    count11 = models.IntegerField(initial=1)

    count12 = models.IntegerField(initial=1)

    def find_lowest_contribution(self):
        self.lowest_contribution = Constants.endowment
        gen = (p for p in self.get_players())
        for p in gen:
            if p.contribution <= self.lowest_contribution:
                self.lowest_contribution = p.contribution

    def should_display_warning3(self):
        gen = (p for p in self.get_players()
               if Constants.player_4_contribution < Constants.threshold)
        for p in gen:
            self.count3 = self.count3 + 1
        if self.count3 >= 2:
            self.should_display_warning3 = False
        else:
            self.should_display_warning3 = False
        return self.should_display_warning3

    def should_display_warning4(self):
        gen = (p for p in self.get_players()
               if Constants.player_4_contribution < Constants.threshold)
        for p in gen:
            self.count4 = self.count4 + 1
        if self.count4 >= 2:
            self.should_display_warning4 = True
        else:
            self.should_display_warning4 = True
        return self.should_display_warning4

    def should_display_warning5(self):
        gen = (p for p in self.get_players()
               if Constants.player_4_contribution < Constants.threshold)
        for p in gen:
            self.count5 = self.count5 + 1
        if self.count5 >= 2:
            self.should_display_warning5 = True
        else:
            self.should_display_warning5 = True
        return self.should_display_warning5

    def should_display_warning6(self):
        gen = (p for p in self.get_players()
               if Constants.player_4_contribution < Constants.threshold)
        for p in gen:
            self.count6 = self.count6 + 1
        if self.count6 >= 2:
            self.should_display_warning6 = True
        else:
            self.should_display_warning6 = True
        return self.should_display_warning6

    def should_display_warning7(self):
        gen = (p for p in self.get_players()
               if Constants.player_4_contribution < Constants.threshold)
        for p in gen:
            self.count7 = self.count7 + 1
        if self.count7 >= 2:
            self.should_display_warning7 = True
        else:
            self.should_display_warning7 = True
        return self.should_display_warning7

    def should_display_warning8(self):
        gen = (p for p in self.get_players()
               if Constants.player_4_contribution < Constants.threshold)
        for p in gen:
            self.count8 = self.count8 + 1
        if self.count8 >= 2:
            self.should_display_warning8 = True
        else:
            self.should_display_warning8 = True
        return self.should_display_warning8

    def should_display_warning9(self):
        gen = (p for p in self.get_players()
               if Constants.player_4_contribution < Constants.threshold)
        for p in gen:
            self.count9 = self.count9 + 1
        if self.count9 >= 2:
            self.should_display_warning9 = True
        else:
            self.should_display_warning9 = True
        return self.should_display_warning9

    def should_display_warning10(self):
        gen = (p for p in self.get_players()
               if Constants.player_4_contribution < Constants.threshold)
        for p in gen:
            self.count10 = self.count10 + 1
        if self.count10 >= 2:
            self.should_display_warning10 = True
        else:
            self.should_display_warning10 = True
        return self.should_display_warning10

    def should_display_warning11(self):
        gen = (p for p in self.get_players()
               if Constants.player_4_contribution < Constants.threshold)
        for p in gen:
            self.count11 = self.count11 + 1
        if self.count11 >= 2:
            self.should_display_warning11 = True
        else:
            self.should_display_warning11 = True
        return self.should_display_warning11

    def should_display_warning12(self):
        gen = (p for p in self.get_players()
               if Constants.player_4_contribution < Constants.threshold)
        for p in gen:
            self.count12 = self.count12 + 1
        if self.count12 >= 2:
            self.should_display_warning12 = True
        else:
            self.should_display_warning12 = True
        return self.should_display_warning12

    def set_payoffs2(self):
        self.total_contribution = sum([
            p.contribution for p in self.get_players()
        ]) + Constants.max_contribution + Constants.player_4_contribution
        self.individual_share = self.total_contribution * Constants.efficiency_factor / (
            Constants.players_per_group + 2)
        self.computer_submission = Constants.max_contribution
        gen1 = (p for p in self.get_players() if p.accept_payment() == True)
        gen2 = (p for p in self.get_players() if p.accept_payment() == False)
        for p in gen1:
            p.payoff = Constants.endowment - p.contribution + self.individual_share
            p.acceptance = Constants.typical_response
        for p in gen2:
            p.payoff = (Constants.endowment - p.contribution)
            p.acceptance = Constants.atypical_response
        gen = (p for p in self.get_players()
               if Constants.player_4_contribution < Constants.threshold)
        for p in gen:
            self.count9 = self.count9 + 1
        if self.count9 >= 2 and self.subsession.round_number > 3:
            self.player_5_earnings = self.individual_share
            self.player_5_acceptance = Constants.typical_response
            self.player_4_earnings = self.individual_share + 7
        else:
            self.player_5_earnings = self.individual_share
            self.player_4_earnings = self.individual_share + 7
            self.player_5_acceptance = Constants.typical_response

    def set_payoffs1(self):
        self.total_contribution = sum([
            p.contribution for p in self.get_players()
        ]) + Constants.max_contribution + Constants.player_4_contribution
        self.individual_share = self.total_contribution * Constants.efficiency_factor / (
            Constants.players_per_group + 2)
        self.computer_submission = Constants.max_contribution
        self.player_5_earnings = self.individual_share
        self.player_4_earnings = self.individual_share + 7
        for p in self.get_players():
            p.payoff = Constants.endowment - p.contribution + self.individual_share
Beispiel #26
0
class JSONFieldModel(ModelWithVars):
    vars = models._JSONField(default=dict)
    integer = models.IntegerField(default=0)
    json_field = models._JSONField()
Beispiel #27
0
class Session(ModelWithVars):
    class Meta:
        app_label = "otree"
        # if i don't set this, it could be in an unpredictable order
        ordering = ['pk']

    _pickle_fields = ['vars', 'config']
    config = models._PickleField(default=dict, null=True)  # type: dict

    # label of this session instance
    label = models.CharField(max_length=300,
                             null=True,
                             blank=True,
                             help_text='For internal record-keeping')

    experimenter_name = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        help_text='For internal record-keeping')

    ready = models.BooleanField(default=False)

    code = models.CharField(
        default=random_chars_8,
        max_length=16,
        # set non-nullable, until we make our CharField non-nullable
        null=False,
        unique=True,
        doc="Randomly generated unique identifier for the session.")

    mturk_HITId = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        help_text='Hit id for this session on MTurk')
    mturk_HITGroupId = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        help_text='Hit id for this session on MTurk')

    # since workers can drop out number of participants on server should be
    # greater than number of participants on mturk
    # value -1 indicates that this session it not intended to run on mturk
    mturk_num_participants = models.IntegerField(
        default=-1, help_text="Number of participants on MTurk")

    mturk_use_sandbox = models.BooleanField(
        default=True,
        help_text="Should this session be created in mturk sandbox?")

    archived = models.BooleanField(
        default=False,
        db_index=True,
        doc=("If set to True the session won't be visible on the "
             "main ViewList for sessions"))

    comment = models.TextField(blank=True)

    _anonymous_code = models.CharField(default=random_chars_10,
                                       max_length=10,
                                       null=False,
                                       db_index=True)

    _pre_create_id = models.CharField(max_length=255, db_index=True, null=True)

    use_browser_bots = models.BooleanField(default=False)

    # if the user clicks 'start bots' twice, this will prevent the bots
    # from being run twice.
    _cannot_restart_bots = models.BooleanField(default=False)
    _bots_finished = models.BooleanField(default=False)
    _bots_errored = models.BooleanField(default=False)
    _bot_case_number = models.PositiveIntegerField()

    is_demo = models.BooleanField(default=False)

    # whether SOME players are bots
    has_bots = models.BooleanField(default=False)

    _admin_report_app_names = models.TextField(default='')
    _admin_report_num_rounds = models.CharField(default='', max_length=255)

    num_participants = models.PositiveIntegerField()

    def __unicode__(self):
        return self.code

    @property
    def participation_fee(self):
        '''This method is deprecated from public API,
        but still useful internally (like data export)'''
        return self.config['participation_fee']

    @property
    def real_world_currency_per_point(self):
        '''This method is deprecated from public API,
        but still useful internally (like data export)'''
        return self.config['real_world_currency_per_point']

    def is_for_mturk(self):
        return (not self.is_demo) and (self.mturk_num_participants > 0)

    def get_subsessions(self):
        lst = []
        app_sequence = self.config['app_sequence']
        for app in app_sequence:
            models_module = otree.common_internal.get_models_module(app)
            subsessions = models_module.Subsession.objects.filter(
                session=self).order_by('round_number')
            lst.extend(list(subsessions))
        return lst

    def get_participants(self):
        return self.participant_set.all()

    def _create_groups_and_initialize(self):
        # group_by_arrival_time_time code used to be here
        for subsession in self.get_subsessions():
            subsession._create_groups()
            subsession.before_session_starts()
            subsession.creating_session()
            subsession.save()

    def mturk_requester_url(self):
        if self.mturk_use_sandbox:
            requester_url = (
                "https://requestersandbox.mturk.com/mturk/manageHITs")
        else:
            requester_url = "https://requester.mturk.com/mturk/manageHITs"
        return requester_url

    def mturk_worker_url(self):
        if self.mturk_use_sandbox:
            return ("https://workersandbox.mturk.com/mturk/preview?groupId={}"
                    ).format(self.mturk_HITGroupId)
        return ("https://www.mturk.com/mturk/preview?groupId={}").format(
            self.mturk_HITGroupId)

    def advance_last_place_participants(self):

        participants = self.get_participants()

        # in case some participants haven't started
        unvisited_participants = []
        for p in participants:
            if p._index_in_pages == 0:
                unvisited_participants.append(p)
                client.get(p._start_url(), follow=True)

        if unvisited_participants:
            # that's it -- just visit the start URL, advancing by 1
            return

        last_place_page_index = min([p._index_in_pages for p in participants])
        last_place_participants = [
            p for p in participants
            if p._index_in_pages == last_place_page_index
        ]

        for p in last_place_participants:
            try:
                current_form_page_url = p._current_form_page_url
                if current_form_page_url:
                    resp = client.post(
                        current_form_page_url,
                        data={
                            constants_internal.timeout_happened: True,
                            constants_internal.admin_secret_code:
                            ADMIN_SECRET_CODE
                        },
                        follow=True)
                    # not sure why, but many users are getting HttpResponseNotFound
                    if resp.status_code >= 400:
                        msg = ('Submitting page {} failed, '
                               'returned HTTP status code {}.'.format(
                                   current_form_page_url, resp.status_code))
                        content = resp.content
                        if len(content) < 600:
                            msg += ' response content: {}'.format(content)
                        raise AssertionError(msg)

                else:
                    # it's possible that the slowest user is on a wait page,
                    # especially if their browser is closed.
                    # because they were waiting for another user who then
                    # advanced past the wait page, but they were never
                    # advanced themselves.
                    start_url = p._start_url()
                    resp = client.get(start_url, follow=True)
            except:
                logging.exception("Failed to advance participants.")
                raise

            # do the auto-advancing here,
            # rather than in increment_index_in_pages,
            # because it's only needed here.
            channels.Group('auto-advance-{}'.format(p.code)).send(
                {'text': json.dumps({'auto_advanced': True})})

    def pages_auto_reload_when_advanced(self):
        # keep it enable until I determine
        # (a) the usefulness of the feature
        # (b) the impact on performance
        return True
        # return settings.DEBUG or self.is_demo

    def build_participant_to_player_lookups(self):
        subsession_app_names = self.config['app_sequence']

        views_modules = {}
        for app_name in subsession_app_names:
            views_modules[app_name] = (
                otree.common_internal.get_views_module(app_name))

        def views_module_for_player(player):
            return views_modules[player._meta.app_config.name]

        records_to_create = []

        for participant in self.get_participants():
            page_index = 0
            for player in participant.get_players():
                for View in views_module_for_player(player).page_sequence:
                    page_index += 1
                    records_to_create.append(
                        ParticipantToPlayerLookup(
                            participant=participant,
                            page_index=page_index,
                            app_name=player._meta.app_config.name,
                            player_pk=player.pk,
                            url=reverse(View.url_name(),
                                        args=[participant.code, page_index])))

            # technically could be stored at the session level
            participant._max_page_index = page_index
            participant.save()
        ParticipantToPlayerLookup.objects.bulk_create(records_to_create)

    def get_room(self):
        from otree.room import ROOM_DICT
        try:
            room_name = RoomToSession.objects.get(session=self).room_name
            return ROOM_DICT[room_name]
        except RoomToSession.DoesNotExist:
            return None

    def _get_payoff_plus_participation_fee(self, payoff):
        '''For a participant who has the given payoff,
        return their payoff_plus_participation_fee
        Useful to define it here, for data export
        '''

        return (self.config['participation_fee'] +
                payoff.to_real_world_currency(self))

    def _set_admin_report_app_names(self):

        admin_report_app_names = []
        num_rounds_list = []
        for app_name in self.config['app_sequence']:
            models_module = otree.common_internal.get_models_module(app_name)
            app_label = get_app_label_from_name(app_name)
            try:
                get_template('{}/AdminReport.html'.format(app_label))
                admin_report_app_names.append(app_name)
                num_rounds_list.append(models_module.Constants.num_rounds)
            except TemplateDoesNotExist:
                pass
        self._admin_report_app_names = ';'.join(admin_report_app_names)
        self._admin_report_num_rounds = ';'.join(
            str(n) for n in num_rounds_list)

    def _admin_report_apps(self):
        return self._admin_report_app_names.split(';')

    def _admin_report_num_rounds_list(self):
        return [int(num) for num in self._admin_report_num_rounds.split(';')]

    def has_admin_report(self):
        return bool(self._admin_report_app_names)
Beispiel #28
0
class Session(ModelWithVars):
    class Meta:
        # if i don't set this, it could be in an unpredictable order
        ordering = ['pk']
        app_label = "otree"

    config = models.JSONField(
        default=dict,
        null=True,
        doc=("the session config dict, as defined in the "
             "programmer's settings.py."))

    # label of this session instance
    label = models.CharField(max_length=300,
                             null=True,
                             blank=True,
                             help_text='For internal record-keeping')

    experimenter_name = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        help_text='For internal record-keeping')

    code = models.RandomCharField(
        length=8, doc="Randomly generated unique identifier for the session.")

    time_scheduled = models.DateTimeField(
        null=True,
        doc="The time at which the session is scheduled",
        help_text='For internal record-keeping',
        blank=True)

    time_started = models.DateTimeField(
        null=True,
        doc="The time at which the experimenter started the session")

    mturk_HITId = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        help_text='Hit id for this session on MTurk')
    mturk_HITGroupId = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        help_text='Hit id for this session on MTurk')
    mturk_qualification_type_id = models.CharField(
        max_length=300,
        null=True,
        blank=True,
        help_text='Qualification type that is '
        'assigned to each worker taking hit')

    # since workers can drop out number of participants on server should be
    # greater than number of participants on mturk
    # value -1 indicates that this session it not intended to run on mturk
    mturk_num_participants = models.IntegerField(
        default=-1, help_text="Number of participants on MTurk")

    mturk_sandbox = models.BooleanField(
        default=True,
        help_text="Should this session be created in mturk sandbox?")

    archived = models.BooleanField(
        default=False,
        doc=("If set to True the session won't be visible on the "
             "main ViewList for sessions"))

    git_commit_timestamp = models.CharField(
        max_length=200,
        null=True,
        doc=(
            "Indicates the version of the code (as recorded by Git) that was "
            "used to run the session, so that the session can be replicated "
            "later.\n Search through the Git commit log to find a commit that "
            "was made at this time."))

    comment = models.TextField(blank=True)

    _ready_to_play = models.BooleanField(default=False)

    _anonymous_code = models.RandomCharField(length=10)

    special_category = models.CharField(
        max_length=20,
        null=True,
        doc="whether it's a test session, demo session, etc.")

    # whether someone already viewed this session's demo links
    demo_already_used = models.BooleanField(default=False)

    # indicates whether a session has been fully created (not only has the
    # model itself been created, but also the other models in the hierarchy)
    ready = models.BooleanField(default=False)

    _pre_create_id = models.CharField(max_length=300, null=True)

    def __unicode__(self):
        return self.code

    @property
    def participation_fee(self):
        '''This method is deprecated from public API,
        but still useful internally (like data export)'''
        return self.config['participation_fee']

    @property
    def real_world_currency_per_point(self):
        '''This method is deprecated from public API,
        but still useful internally (like data export)'''
        return self.config['real_world_currency_per_point']

    @property
    def session_type(self):
        '''2015-07-10: session_type is deprecated
        this shim method will be removed eventually'''
        return self.config

    def is_open(self):
        return GlobalSingleton.objects.get().default_session == self

    def is_for_mturk(self):
        return (not self.is_demo()) and (self.mturk_num_participants > 0)

    def is_demo(self):
        return (self.special_category ==
                constants_internal.session_special_category_demo)

    def subsession_names(self):
        names = []
        for subsession in self.get_subsessions():
            app_name = subsession._meta.app_config.name
            name = '{} {}'.format(
                otree.common_internal.app_name_format(app_name),
                subsession.name())
            names.append(name)
        if names:
            return ', '.join(names)
        else:
            return '[empty sequence]'

    def get_subsessions(self):
        lst = []
        app_sequence = self.config['app_sequence']
        for app in app_sequence:
            models_module = otree.common_internal.get_models_module(app)
            subsessions = models_module.Subsession.objects.filter(
                session=self).order_by('round_number')
            lst.extend(list(subsessions))
        return lst

    def delete(self, using=None):
        for subsession in self.get_subsessions():
            subsession.delete()
        super(Session, self).delete(using)

    def get_participants(self):
        return self.participant_set.all()

    def payments_ready(self):
        for participants in self.get_participants():
            if not participants.payoff_is_complete():
                return False
        return True

    payments_ready.boolean = True

    def _create_groups_and_initialize(self):
        # if ppg is None, then the arrival time doesn't matter because
        # everyone is assigned to one big group.
        # otherwise, even in single-player games, you would have to wait
        # for other players to arrive
        # the drawback of this approach is that id_in_group is
        # predetermined, rather than by arrival time.
        # alternative design:
        # instead of checking ppg, we could also check if the game
        # contains a wait page
        # another alternative:
        # allow players to start even if the rest of the group hasn't arrived
        # but this might break some assumptions such as len(grp.get_players())
        # also, what happens if you get to the next round before
        # another player has started the first? you can't clone the
        # previous round's groups
        for subsession in self.get_subsessions():
            cond = (self.config.get('group_by_arrival_time')
                    and subsession._Constants.players_per_group is not None)
            if cond:
                if subsession.round_number == 1:
                    subsession._set_players_per_group_list()
                subsession._create_empty_groups()
            else:
                subsession._create_groups()
            subsession._initialize()
            subsession.save()
        self._ready_to_play = True
        # assert self is subsession.session
        self.save()

    def mturk_requester_url(self):
        if self.mturk_sandbox:
            requester_url = (
                "https://requestersandbox.mturk.com/mturk/manageHITs")
        else:
            requester_url = "https://requester.mturk.com/mturk/manageHITs"
        return requester_url

    def mturk_worker_url(self):
        if self.mturk_sandbox:
            worker_url = (
                "https://workersandbox.mturk.com/mturk/preview?groupId={}"
            ).format(self.mturk_HITGroupId)
        else:
            worker_url = (
                "https://www.mturk.com/mturk/preview?groupId={}").format(
                    self.mturk_HITGroupId)
        return worker_url

    def advance_last_place_participants(self):
        participants = self.get_participants()

        c = django.test.Client()

        # in case some participants haven't started
        some_participants_not_visited = False
        for p in participants:
            if not p.visited:
                some_participants_not_visited = True
                c.get(p._start_url(), follow=True)

        if some_participants_not_visited:
            # refresh from DB so that _current_form_page_url gets set
            participants = self.participant_set.all()

        last_place_page_index = min([p._index_in_pages for p in participants])
        last_place_participants = [
            p for p in participants
            if p._index_in_pages == last_place_page_index
        ]

        for p in last_place_participants:
            # what if current_form_page_url hasn't been set yet?
            resp = c.post(p._current_form_page_url,
                          data={constants_internal.auto_submit: True},
                          follow=True)
            assert resp.status_code < 400

    def build_session_user_to_user_lookups(self):
        subsession_app_names = self.config['app_sequence']

        num_pages_in_each_app = {}
        for app_name in subsession_app_names:
            views_module = otree.common_internal.get_views_module(app_name)

            num_pages = len(views_module.page_sequence)
            num_pages_in_each_app[app_name] = num_pages

        for participant in self.get_participants():
            participant.build_session_user_to_user_lookups(
                num_pages_in_each_app)