Ejemplo n.º 1
0
    def live_method_stuff(self, player_bot, submission):
        PageClass = submission.page_class
        live_method_name = PageClass.live_method
        if live_method_name:
            record = (player_bot.player.group_id, PageClass)
            if record not in self.executed_live_methods:
                bots_module = inspect.getmodule(player_bot)
                method_calls_fn = getattr(bots_module, 'call_live_method',
                                          None)
                if method_calls_fn:
                    players = {
                        p.id_in_group: p
                        for p in player_bot.group.get_players()
                    }

                    def method(id_in_group, data):
                        return getattr(players[id_in_group],
                                       live_method_name)(data)

                    method_calls_fn(
                        method=method,
                        case=player_bot.case,
                        round_number=player_bot.round_number,
                        page_class=PageClass,
                    )
                    db.commit()
                self.executed_live_methods.add(record)
Ejemplo n.º 2
0
 def set_players(self, players_list):
     Constants = self._Constants
     roles = get_roles(Constants)
     for i, player in enumerate(players_list, start=1):
         player.group = self
         player.id_in_group = i
         player._role = get_role(roles, i)
     db.commit()
Ejemplo n.º 3
0
    def _increment_index_in_pages(self):
        # when is this not the case?
        participant = self.participant
        assert self._index_in_pages == participant._index_in_pages

        # we should allow a user to move beyond the last page if it's mturk
        # also in general maybe we should show the 'out of sequence' page

        # we skip any page that is a sequence page where is_displayed
        # evaluates to False to eliminate unnecessary redirection

        page_index_to_skip_to = self._get_next_page_index_if_skipping_apps()
        is_skipping_apps = bool(page_index_to_skip_to)

        for page_index in range(
            # go to max_page_index+2 because range() skips the last index
            # and it's possible to go to max_page_index + 1 (OutOfRange)
            self._index_in_pages + 1,
            participant._max_page_index + 2,
        ):
            participant._index_in_pages = page_index
            if page_index == participant._max_page_index + 1:
                # break and go to OutOfRangeNotification
                break
            if is_skipping_apps and page_index == page_index_to_skip_to:
                break

            # scope, receive, send
            page = get_page_lookup(
                participant._session_code, page_index
            ).page_class.instantiate_without_request()

            page.set_attributes(self.participant)
            if not is_skipping_apps:
                if page._lookup.is_first_in_round:
                    # we have moved to a new round.
                    page.player.start()
                if page._is_displayed():
                    break

            # if it's a wait page, record that they visited
            if isinstance(page, WaitPage):

                if page.group_by_arrival_time:
                    # keep looping
                    # if 1 participant can skip the page,
                    # then all other participants should skip it also,
                    # as described in the docs
                    # so there is no need to mark as complete.
                    continue

                # save the participant, because tally_unvisited
                # queries index_in_pages directly from the DB
                db.commit()

                is_last, someone_waiting = page._tally_unvisited()
                if is_last and someone_waiting:
                    page._run_aapa_and_notify(page._group_or_subsession)
Ejemplo n.º 4
0
 def payoff(self, value):
     if value is None:
         value = 0
     delta = value - self._payoff
     self._payoff += delta
     self.participant.payoff += delta
     # should save it because it may not be obvious that modifying
     # player.payoff also changes a field on a different model
     db.commit()
Ejemplo n.º 5
0
 async def dispatch(self, request, call_next):
     async with lock2:
         if not NEW_SCOPE_EACH_REQUEST:
             db.new_session()
         try:
             response = await call_next(request)
             db.commit()
         except Exception:
             db.rollback()
             raise
         # closing seems to interfere with errors middleware, which tries to get the value of local vars
         # and therefore queries the db
         # maybe it's not necessary to close since we just overwrite.
         # finally:
         #     db.close()
         return response
Ejemplo n.º 6
0
def create_session(
    session_config_name,
    *,
    num_participants,
    label='',
    room_name=None,
    is_mturk=False,
    is_demo=False,
    modified_session_config_fields=None,
) -> Session:

    num_subsessions = 0

    try:
        session_config = SESSION_CONFIGS_DICT[session_config_name]
    except KeyError:
        msg = 'Session config "{}" not found in settings.SESSION_CONFIGS.'
        raise KeyError(msg.format(session_config_name)) from None
    else:
        # copy so that we don't mutate the original
        # .copy() returns a dict, so need to convert back to SessionConfig
        session_config = SessionConfig(session_config.copy())

        modified_config = modified_session_config_fields or {}
        # this is for API. don't want to mislead people
        # to put stuff in the session config that should be in the session.
        bad_keys = modified_config.keys() & NON_EDITABLE_FIELDS
        if bad_keys:
            raise Exception(
                f'The following session config fields are not editable: {bad_keys}'
            )
        session_config.update(modified_config)

        # check validity and converts serialized decimal & currency values
        # back to their original data type (because they were serialized
        # when passed through channels
        session_config.clean()

    # check that it divides evenly
    session_lcm = session_config.get_lcm()
    if num_participants is None:
        # most games are multiplayer, so if it's under 2, we bump it to 2
        num_participants = max(session_lcm, 2)
    else:
        if num_participants % session_lcm:
            msg = (
                'Session Config {}: Number of participants ({}) is not a multiple '
                'of group size ({})'
            ).format(session_config['name'], num_participants, session_lcm)
            raise ValueError(msg)

    session = Session(
        config=session_config,
        label=label,
        is_demo=is_demo,
        num_participants=num_participants,
        is_mturk=is_mturk,
    )
    db.add(session)
    db.commit()

    session_code = session.code

    participants = [
        Participant(
            id_in_session=id_in_session, session=session, _session_code=session_code,
        )
        for id_in_session in list(range(1, num_participants + 1))
    ]

    db.add_all(participants)
    db.commit()

    # participant_values = (
    #     db.query(Participant)
    #     .filter(Session.id == session.id)
    #     .order_by('id')
    #     .with_entities(Participant.id, Participant.code)
    # ).all()

    participant_values = (
        db.query(Participant)
        .join(Session)
        .filter(Session.id == session.id)
        .order_by(Participant.id)
        .with_entities(Participant.id, Participant.code)
    ).all()

    num_pages = 0

    for app_name in session_config['app_sequence']:

        views_module = common.get_pages_module(app_name)
        models_module = get_models_module(app_name)
        Constants: BaseConstants = models_module.Constants
        num_subsessions += Constants.num_rounds

        round_numbers = list(range(1, Constants.num_rounds + 1))

        num_pages += Constants.num_rounds * len(views_module.page_sequence)

        Subsession = models_module.Subsession
        Group = models_module.Group
        Player = models_module.Player

        subsessions = [
            Subsession(round_number=round_number, session=session)
            for round_number in round_numbers
        ]

        db.add_all(subsessions)
        db.commit()

        subsessions = (
            dbq(Subsession)
            .filter_by(session=session)
            .order_by('round_number')
            .with_entities('id', 'round_number')
        )

        ppg = Constants.players_per_group
        if ppg is None or Subsession._has_group_by_arrival_time():
            ppg = num_participants

        num_groups_per_round = int(num_participants / ppg)

        groups_to_create = []
        for ss_id, ss_rd in subsessions:
            for id_in_subsession in range(1, num_groups_per_round + 1):
                groups_to_create.append(
                    Group(
                        session=session,
                        subsession_id=ss_id,
                        round_number=ss_rd,
                        id_in_subsession=id_in_subsession,
                    )
                )

        db.add_all(groups_to_create)

        groups = (
            dbq(Group).filter_by(session=session).order_by('id_in_subsession')
        ).all()

        groups_lookup = defaultdict(list)

        for group in groups:

            groups_lookup[group.subsession_id].append(group.id)

        players_to_create = []

        for ss_id, ss_rd in subsessions:
            roles = get_roles(Constants)
            participant_index = 0
            for group_id in groups_lookup[ss_id]:
                for id_in_group in range(1, ppg + 1):
                    participant = participant_values[participant_index]
                    players_to_create.append(
                        Player(
                            session=session,
                            subsession_id=ss_id,
                            round_number=ss_rd,
                            participant_id=participant[0],
                            group_id=group_id,
                            id_in_group=id_in_group,
                            _role=get_role(roles, id_in_group),
                        )
                    )
                    participant_index += 1

        # Create players
        db.add_all(players_to_create)

    dbq(Participant).filter_by(session=session).update(
        {Participant._max_page_index: num_pages}
    )

    # make creating_session use the current session,
    # so that session.save() below doesn't overwrite everything
    # set earlier
    for subsession in session.get_subsessions():
        subsession.creating_session()

    # 2017-09-27: moving this inside the transaction
    session._set_admin_report_app_names()

    if room_name is not None:
        from otree.room import ROOM_DICT

        room = ROOM_DICT[room_name]
        room.set_session(session)

    db.commit()

    return session