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)
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()
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)
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()
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
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