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())
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()
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" )
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()
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")
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"])
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())
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)
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])
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)]
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)
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)
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
class Player(BasePlayer): sent_amount = models.IntegerField(min=0, max=600) sent_back_amount = models.IntegerField()
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()])
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
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)
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)
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)
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])
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
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
class JSONFieldModel(ModelWithVars): integer = models.IntegerField(default=0) json_field = models.JSONField()
class RadioModel(otree.db.models.Model): f_radio = models.IntegerField(blank=True, choices=[1, 2], widget=widgets.RadioSelect())
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
class JSONFieldModel(ModelWithVars): vars = models._JSONField(default=dict) integer = models.IntegerField(default=0) json_field = models._JSONField()
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)
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)