Example #1
0
def get_lcm(session_config):
    min_multiple_list = []
    for app_name in session_config["app_sequence"]:
        app_constants = get_app_constants(app_name)
        # if players_per_group is None, 0, etc.
        min_multiple = min_players_multiple(app_constants.players_per_group)
        min_multiple_list.append(min_multiple)
    return lcmm(*min_multiple_list)
Example #2
0
 def get_lcm(self):
     min_multiple_list = []
     for app_name in self['app_sequence']:
         Constants = get_app_constants(app_name)
         # if players_per_group is None, 0, etc.
         min_multiple = Constants.players_per_group or 1
         min_multiple_list.append(min_multiple)
     return lcmm(*min_multiple_list)
Example #3
0
def get_lcm(session_config):
    min_multiple_list = []
    for app_name in session_config['app_sequence']:
        app_constants = get_app_constants(app_name)
        # if players_per_group is None, 0, etc.
        min_multiple = min_players_multiple(app_constants.players_per_group)
        min_multiple_list.append(min_multiple)
    return lcmm(*min_multiple_list)
Example #4
0
 def get_lcm(self):
     min_multiple_list = []
     for app_name in self['app_sequence']:
         Constants = get_app_constants(app_name)
         # if players_per_group is None, 0, etc.
         min_multiple = Constants.players_per_group or 1
         min_multiple_list.append(min_multiple)
     return lcmm(*min_multiple_list)
Example #5
0
def create_session(session_config_name,
                   *,
                   label='',
                   num_participants=None,
                   pre_create_id=None,
                   room_name=None,
                   for_mturk=False,
                   is_demo=False,
                   edited_session_config_fields=None) -> Session:

    session = None
    num_subsessions = 0
    edited_session_config_fields = edited_session_config_fields or {}

    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())
        session_config.update(edited_session_config_fields)

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

    with transaction.atomic():
        # 2014-5-2: i could implement this by overriding the __init__ on the
        # Session model, but I don't really know how that works, and it seems
        # to be a bit discouraged: http://goo.gl/dEXZpv
        # 2014-9-22: preassign to groups for demo mode.

        otree.db.idmap.activate_cache()

        session = Session.objects.create(
            config=session_config,
            label=label,
            _pre_create_id=pre_create_id,
            is_demo=is_demo,
            num_participants=num_participants,
        )  # type: Session

        def bulk_create(model, descriptions):
            model.objects.bulk_create([
                model(session=session, **description)
                for description in descriptions
            ])
            return model.objects.filter(session=session).order_by('pk')

        # check that it divides evenly
        session_lcm = session_config.get_lcm()
        if num_participants % session_lcm:
            msg = ('Session Config {}: Number of participants ({}) does not '
                   'divide evenly into group size ({})').format(
                       session_config['name'], num_participants, session_lcm)
            raise ValueError(msg)

        if for_mturk:
            session.mturk_num_participants = (
                num_participants / settings.MTURK_NUM_PARTICIPANTS_MULTIPLE)

        # TODO: remove start_order
        start_order = list(range(num_participants))
        if session_config.get('random_start_order'):
            random.shuffle(start_order)

        participants = bulk_create(Participant, [{
            'id_in_session': id_in_session,
            'start_order': j,
        } for id_in_session, j in enumerate(start_order, start=1)])

        ParticipantLockModel.objects.bulk_create([
            ParticipantLockModel(participant_code=participant.code)
            for participant in participants
        ])

        for app_name in session_config['app_sequence']:

            models_module = get_models_module(app_name)
            app_constants = get_app_constants(app_name)
            num_subsessions += app_constants.num_rounds

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

            subs = bulk_create(models_module.Subsession,
                               [{
                                   'round_number': round_number
                               } for round_number in round_numbers])

            # Create players
            models_module.Player.objects.bulk_create([
                models_module.Player(session=session,
                                     subsession=subsession,
                                     round_number=round_number,
                                     participant=participant)
                for round_number, subsession in zip(round_numbers, subs)
                for participant in participants
            ])

        session._create_groups_and_initialize()

        session.build_participant_to_player_lookups()
        # automatically save all objects since the cache was activated:
        # Player, Group, Subsession, Participant, Session
        otree.db.idmap.save_objects()
        otree.db.idmap.deactivate_cache()

        # 2017-09-27: moving this inside the transaction
        session._set_admin_report_app_names()
        session.save()
        # we don't need to mark it ready=True here...because it's in a
        # transaction

    # this should happen after session.ready = True
    if room_name is not None:
        from otree.room import ROOM_DICT
        room = ROOM_DICT[room_name]
        room.session = session

    return session
Example #6
0
def create_session(
        session_config_name, label='', num_participants=None,
        _pre_create_id=None,
        room_name=None, for_mturk=False, use_cli_bots=False,
        is_demo=False, force_browser_bots=False,
        honor_browser_bots_config=False, bot_case_number=None):

    session = None
    use_browser_bots = False
    num_subsessions = 0

    with transaction.atomic():
        # 2014-5-2: i could implement this by overriding the __init__ on the
        # Session model, but I don't really know how that works, and it seems
        # to be a bit discouraged: http://goo.gl/dEXZpv
        # 2014-9-22: preassign to groups for demo mode.

        otree.db.idmap.activate_cache()

        try:
            session_config = SESSION_CONFIGS_DICT[session_config_name]
        except KeyError:
            msg = 'Session config "{}" not found in settings.SESSION_CONFIGS.'
            raise ValueError(msg.format(session_config_name))

        if force_browser_bots:
            use_browser_bots = True
        elif (session_config.get('use_browser_bots') and
              honor_browser_bots_config):
            use_browser_bots = True
        else:
            use_browser_bots = False
        if use_browser_bots and bot_case_number is None:
            # choose one randomly
            num_bot_cases = session_config.get_num_bot_cases()
            # choose bot case number randomly...maybe reconsider this?
            # we can only run one.
            bot_case_number = random.choice(range(num_bot_cases))

        session = Session.objects.create(
            config=session_config,
            label=label,
            _pre_create_id=_pre_create_id,
            use_browser_bots=use_browser_bots,
            is_demo=is_demo,
            _bot_case_number=bot_case_number)

        def bulk_create(model, descriptions):
            model.objects.bulk_create([
                model(session=session, **description)
                for description in descriptions])
            return model.objects.filter(session=session).order_by('pk')

        # check that it divides evenly
        session_lcm = session_config.get_lcm()
        if num_participants % session_lcm:
            msg = (
                'Session Config {}: Number of participants ({}) does not '
                'divide evenly into group size ({})'
            ).format(session_config['name'], num_participants, session_lcm)
            raise ValueError(msg)

        if for_mturk:
            session.mturk_num_participants = (
                    num_participants /
                    settings.MTURK_NUM_PARTICIPANTS_MULTIPLE)

        start_order = list(range(num_participants))
        if session_config.get('random_start_order'):
            random.shuffle(start_order)

        participants = bulk_create(
            Participant,
            [{
                'id_in_session': id_in_session,
                'start_order': j,
                # check if id_in_session is in the bots ID list
                '_is_bot': use_cli_bots or use_browser_bots,
             }
             for id_in_session, j in enumerate(start_order, start=1)])

        ParticipantLockModel.objects.bulk_create([
            ParticipantLockModel(participant_code=participant.code)
            for participant in participants])

        try:
            for app_name in session_config['app_sequence']:

                models_module = get_models_module(app_name)
                app_constants = get_app_constants(app_name)

                num_subsessions += app_constants.num_rounds
                round_numbers = list(range(1, app_constants.num_rounds + 1))

                subs = bulk_create(
                    models_module.Subsession,
                    [{'round_number': round_number}
                     for round_number in round_numbers])

                # Create players
                models_module.Player.objects.bulk_create([
                    models_module.Player(
                        session=session,
                        subsession=subsession,
                        round_number=round_number,
                        participant=participant)
                    for round_number, subsession in zip(round_numbers, subs)
                    for participant in participants])

            session._create_groups_and_initialize()

        # session.has_bots = any(p.is_bot ...)

        # handle case where DB has missing column or table
        # missing table: OperationalError: no such table: pg_subsession
        # missing column: OperationalError: table pg_player has no column
        # named contribution2
        except OperationalError as exception:
            exception_str = str(exception)
            if 'table' in exception_str:
                ExceptionClass = type(exception)
                six.reraise(
                    ExceptionClass,
                    ExceptionClass('{} - Try resetting the database.'.format(
                        exception_str)),
                    sys.exc_info()[2])
            raise

        session.build_participant_to_player_lookups()
        # automatically save all objects since the cache was activated:
        # Player, Group, Subsession, Participant, Session
        otree.db.idmap.save_objects()
        otree.db.idmap.deactivate_cache()

    if use_browser_bots:
        # what about clear_browser_bots? if session is created through
        # UI, when do we run that? it should be run when the session
        # is deleted
        try:
            num_players_total = num_participants * num_subsessions
            otree.bots.browser.initialize_bots(
                session.code, num_players_total)
        except:
            session.delete()
            raise

    session.ready = True
    session.save()

    # this should happen after session.ready = True
    if room_name is not None:
        from otree.room import ROOM_DICT
        room = ROOM_DICT[room_name]
        room.session = session

    return session
Example #7
0
def create_session(session_config_name, label="", num_participants=None, special_category=None, _pre_create_id=None):

    # 2014-5-2: i could implement this by overriding the __init__ on the
    # Session model, but I don't really know how that works, and it seems to
    # be a bit discouraged: http://goo.gl/dEXZpv
    # 2014-9-22: preassign to groups for demo mode.

    try:
        session_config = get_session_configs_dict()[session_config_name]
    except KeyError:
        msg = 'Session type "{}" not found in settings.py'
        raise ValueError(msg.format(session_config_name))
    session = Session.objects.create(
        config=session_config, label=label, special_category=special_category, _pre_create_id=_pre_create_id
    )

    def bulk_create(model, descriptions):
        model.objects.bulk_create([model(session=session, **description) for description in descriptions])
        return model.objects.filter(session=session).order_by("pk")

    if num_participants is None:
        c_special_catdemo = constants_internal.session_special_category_demo
        c_special_catbots = constants_internal.session_special_category_bots
        if special_category == c_special_catdemo:
            num_participants = session_config["num_demo_participants"]
        elif special_category == c_special_catbots:
            num_participants = session_config["num_bots"]

    # check that it divides evenly
    session_lcm = get_lcm(session_config)
    if num_participants % session_lcm:
        msg = "Session Config {}: Number of participants ({}) does not divide " "evenly into group size ({})"
        raise ValueError(msg.format(session_config["name"], num_participants, session_lcm))

    start_order = range(num_participants)
    if session_config.get("random_start_order"):
        random.shuffle(start_order)

    participants = bulk_create(
        Participant, [{"id_in_session": i + 1, "start_order": j} for i, j in enumerate(start_order)]
    )

    for participant in participants:
        ParticipantLockModel(participant_code=participant.code).save()

    for app_name in session_config["app_sequence"]:

        models_module = get_models_module(app_name)
        app_constants = get_app_constants(app_name)

        round_numbers = range(1, app_constants.num_rounds + 1)

        subs = bulk_create(models_module.Subsession, [{"round_number": round_number} for round_number in round_numbers])

        # Create players
        models_module.Player.objects.bulk_create(
            [
                models_module.Player(
                    session=session, subsession=subsession, round_number=round_number, participant=participant
                )
                for round_number, subsession in zip(round_numbers, subs)
                for participant in participants
            ]
        )

    session._create_groups_and_initialize()
    session.build_participant_to_player_lookups()
    session.ready = True
    session.save()

    return session
Example #8
0
def create_session(session_config_name, label='', num_participants=None,
                   special_category=None, _pre_create_id=None):

    # 2014-5-2: i could implement this by overriding the __init__ on the
    # Session model, but I don't really know how that works, and it seems to
    # be a bit discouraged: http://goo.gl/dEXZpv
    # 2014-9-22: preassign to groups for demo mode.

    try:
        session_config = get_session_configs_dict()[session_config_name]
    except KeyError:
        msg = 'Session type "{}" not found in settings.py'
        raise ValueError(msg.format(session_config_name))
    session = Session.objects.create(
        config=session_config,
        label=label,
        special_category=special_category,
        _pre_create_id=_pre_create_id,)

    def bulk_create(model, descriptions):
        model.objects.bulk_create([
            model(session=session, **description)
            for description in descriptions])
        return model.objects.filter(session=session).order_by('pk')

    if num_participants is None:
        c_special_catdemo = constants_internal.session_special_category_demo
        c_special_catbots = constants_internal.session_special_category_bots
        if special_category == c_special_catdemo:
            num_participants = session_config['num_demo_participants']
        elif special_category == c_special_catbots:
            num_participants = session_config['num_bots']

    # check that it divides evenly
    session_lcm = get_lcm(session_config)
    if num_participants % session_lcm:
        msg = (
            'Session Config {}: Number of participants ({}) does not divide '
            'evenly into group size ({})')
        raise ValueError(
            msg.format(session_config['name'], num_participants, session_lcm))

    start_order = list(range(num_participants))
    if session_config.get('random_start_order'):
        random.shuffle(start_order)

    participants = bulk_create(
        Participant,
        [{'id_in_session': i + 1, 'start_order': j}
         for i, j in enumerate(start_order)])

    for participant in participants:
        ParticipantLockModel(participant_code=participant.code).save()

    for app_name in session_config['app_sequence']:

        models_module = get_models_module(app_name)
        app_constants = get_app_constants(app_name)

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

        subs = bulk_create(
            models_module.Subsession,
            [{'round_number': round_number} for round_number in round_numbers])

        # Create players
        models_module.Player.objects.bulk_create([
            models_module.Player(
                session=session,
                subsession=subsession,
                round_number=round_number,
                participant=participant)
            for round_number, subsession in zip(round_numbers, subs)
            for participant in participants])

    session._create_groups_and_initialize()
    session.build_participant_to_player_lookups()
    session.ready = True
    session.save()

    return session
Example #9
0
def create_session(
        session_config_name, *, label='', num_participants=None,
        pre_create_id=None,
        room_name=None, for_mturk=False, use_cli_bots=False,
        is_demo=False, force_browser_bots=False,
        # i think bot_case_number is unused.
        honor_browser_bots_config=False, bot_case_number=None,
        edited_session_config_fields=None):

    session = None
    use_browser_bots = False
    num_subsessions = 0
    edited_session_config_fields = edited_session_config_fields or {}

    with transaction.atomic():
        # 2014-5-2: i could implement this by overriding the __init__ on the
        # Session model, but I don't really know how that works, and it seems
        # to be a bit discouraged: http://goo.gl/dEXZpv
        # 2014-9-22: preassign to groups for demo mode.

        otree.db.idmap.activate_cache()

        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())
            session_config.update(edited_session_config_fields)

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

        if force_browser_bots:
            use_browser_bots = True
        elif (session_config.get('use_browser_bots') and
              honor_browser_bots_config):
            use_browser_bots = True
        else:
            use_browser_bots = False

        session = Session.objects.create(
            config=session_config,
            label=label,
            _pre_create_id=pre_create_id,
            use_browser_bots=use_browser_bots,
            is_demo=is_demo,
            num_participants=num_participants,
            ) # type: Session

        def bulk_create(model, descriptions):
            model.objects.bulk_create([
                model(session=session, **description)
                for description in descriptions])
            return model.objects.filter(session=session).order_by('pk')

        # check that it divides evenly
        session_lcm = session_config.get_lcm()
        if num_participants % session_lcm:
            msg = (
                'Session Config {}: Number of participants ({}) does not '
                'divide evenly into group size ({})'
            ).format(session_config['name'], num_participants, session_lcm)
            raise ValueError(msg)

        if for_mturk:
            session.mturk_num_participants = (
                    num_participants /
                    settings.MTURK_NUM_PARTICIPANTS_MULTIPLE)

        # TODO: remove start_order
        start_order = list(range(num_participants))
        if session_config.get('random_start_order'):
            random.shuffle(start_order)

        participants = bulk_create(
            Participant,
            [{
                'id_in_session': id_in_session,
                'start_order': j,
                # check if id_in_session is in the bots ID list
                '_is_bot': use_cli_bots or use_browser_bots,
             }
             for id_in_session, j in enumerate(start_order, start=1)])

        ParticipantLockModel.objects.bulk_create([
            ParticipantLockModel(participant_code=participant.code)
            for participant in participants])

        for app_name in session_config['app_sequence']:

            models_module = get_models_module(app_name)
            app_constants = get_app_constants(app_name)
            num_subsessions += app_constants.num_rounds

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

            subs = bulk_create(
                models_module.Subsession,
                [{'round_number': round_number}
                 for round_number in round_numbers])

            # Create players
            models_module.Player.objects.bulk_create([
                models_module.Player(
                    session=session,
                    subsession=subsession,
                    round_number=round_number,
                    participant=participant)
                for round_number, subsession in zip(round_numbers, subs)
                for participant in participants])

        session._create_groups_and_initialize()

        session.build_participant_to_player_lookups()
        # automatically save all objects since the cache was activated:
        # Player, Group, Subsession, Participant, Session
        otree.db.idmap.save_objects()
        otree.db.idmap.deactivate_cache()

    if use_browser_bots:
        # what about clear_browser_bots? if session is created through
        # UI, when do we run that? it should be run when the session
        # is deleted
        try:
            num_players_total = num_participants * num_subsessions
            otree.bots.browser.initialize_bots(
                session_pk=session.pk, num_players_total=num_players_total,
            )
        except:
            session.delete()
            raise

    session._set_admin_report_app_names()
    session.ready = True
    session.save()

    # this should happen after session.ready = True
    if room_name is not None:
        from otree.room import ROOM_DICT
        room = ROOM_DICT[room_name]
        room.session = session

    return session
Example #10
0
def create_session(
        session_config_name, label='', num_participants=None,
        _pre_create_id=None,
        room_name=None, for_mturk=False, use_cli_bots=False,
        is_demo=False, force_browser_bots=False,
        honor_browser_bots_config=False, bot_case_number=None,
        edited_session_config_fields=None):

    session = None
    use_browser_bots = False
    participants = []
    edited_session_config_fields = edited_session_config_fields or {}

    with transaction.atomic():
        # 2014-5-2: i could implement this by overriding the __init__ on the
        # Session model, but I don't really know how that works, and it seems
        # to be a bit discouraged: http://goo.gl/dEXZpv
        # 2014-9-22: preassign to groups for demo mode.

        otree.db.idmap.activate_cache()

        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:
            # seems i need to copy and convert back to a session config
            # otherwise .copy() converts it to a simple dict
            session_config.update(edited_session_config_fields)
            session_config = SessionConfig(session_config.copy())
            # 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()

        if force_browser_bots:
            use_browser_bots = True
        elif (session_config.get('use_browser_bots') and
              honor_browser_bots_config):
            use_browser_bots = True
        else:
            use_browser_bots = False
        if use_browser_bots and bot_case_number is None:
            # choose one randomly
            num_bot_cases = session_config.get_num_bot_cases()
            # choose bot case number randomly...maybe reconsider this?
            # we can only run one.
            bot_case_number = random.choice(range(num_bot_cases))

        session = Session.objects.create(
            config=session_config,
            label=label,
            _pre_create_id=_pre_create_id,
            use_browser_bots=use_browser_bots,
            is_demo=is_demo,
            _bot_case_number=bot_case_number)

        def bulk_create(model, descriptions):
            model.objects.bulk_create([
                model(session=session, **description)
                for description in descriptions])
            return model.objects.filter(session=session).order_by('pk')

        # check that it divides evenly
        session_lcm = session_config.get_lcm()
        if num_participants % session_lcm:
            msg = (
                'Session Config {}: Number of participants ({}) does not '
                'divide evenly into group size ({})'
            ).format(session_config['name'], num_participants, session_lcm)
            raise ValueError(msg)

        if for_mturk:
            session.mturk_num_participants = (
                    num_participants /
                    settings.MTURK_NUM_PARTICIPANTS_MULTIPLE)

        start_order = list(range(num_participants))
        if session_config.get('random_start_order'):
            random.shuffle(start_order)

        participants = bulk_create(
            Participant,
            [{
                'id_in_session': id_in_session,
                'start_order': j,
                # check if id_in_session is in the bots ID list
                '_is_bot': use_cli_bots or use_browser_bots,
             }
             for id_in_session, j in enumerate(start_order, start=1)])

        ParticipantLockModel.objects.bulk_create([
            ParticipantLockModel(participant_code=participant.code)
            for participant in participants])

        for app_name in session_config['app_sequence']:

            models_module = get_models_module(app_name)
            app_constants = get_app_constants(app_name)

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

            subs = bulk_create(
                models_module.Subsession,
                [{'round_number': round_number}
                 for round_number in round_numbers])

            # Create players
            models_module.Player.objects.bulk_create([
                models_module.Player(
                    session=session,
                    subsession=subsession,
                    round_number=round_number,
                    participant=participant)
                for round_number, subsession in zip(round_numbers, subs)
                for participant in participants])

        session._create_groups_and_initialize()

        session.build_participant_to_player_lookups()
        # automatically save all objects since the cache was activated:
        # Player, Group, Subsession, Participant, Session
        otree.db.idmap.save_objects()
        otree.db.idmap.deactivate_cache()

    if use_browser_bots:
        # what about clear_browser_bots? if session is created through
        # UI, when do we run that? it should be run when the session
        # is deleted
        try:
            for participant in participants:
                otree.bots.browser.initialize_bot(
                    participant.code)
        except:
            session.delete()
            raise

    session.ready = True
    session.save()

    # this should happen after session.ready = True
    if room_name is not None:
        from otree.room import ROOM_DICT
        room = ROOM_DICT[room_name]
        room.session = session

    return session
Example #11
0
def create_session(session_config_name, label='', num_participants=None,
                   special_category=None, _pre_create_id=None, room=None, for_mturk=False):

    # 2014-5-2: i could implement this by overriding the __init__ on the
    # Session model, but I don't really know how that works, and it seems to
    # be a bit discouraged: http://goo.gl/dEXZpv
    # 2014-9-22: preassign to groups for demo mode.

    otree.db.idmap.activate_cache()

    try:
        session_config = SESSION_CONFIGS_DICT[session_config_name]
    except KeyError:
        msg = 'Session type "{}" not found in settings.py'
        raise ValueError(msg.format(session_config_name))
    session = Session.objects.create(
        config=session_config,
        label=label,
        special_category=special_category,
        _pre_create_id=_pre_create_id,)

    def bulk_create(model, descriptions):
        model.objects.bulk_create([
            model(session=session, **description)
            for description in descriptions])
        return model.objects.filter(session=session).order_by('pk')

    if num_participants is None:
        c_special_catdemo = constants_internal.session_special_category_demo
        c_special_catbots = constants_internal.session_special_category_bots
        if special_category == c_special_catdemo:
            num_participants = session_config['num_demo_participants']
        elif special_category == c_special_catbots:
            num_participants = session_config['num_bots']

    # check that it divides evenly
    session_lcm = get_lcm(session_config)
    if num_participants % session_lcm:
        msg = (
            'Session Config {}: Number of participants ({}) does not divide '
            'evenly into group size ({})')
        raise ValueError(
            msg.format(session_config['name'], num_participants, session_lcm))

    if for_mturk:
        session.mturk_num_participants = (
                num_participants /
                settings.MTURK_NUM_PARTICIPANTS_MULT
        )


    start_order = list(range(num_participants))
    if session_config.get('random_start_order'):
        random.shuffle(start_order)

    participants = bulk_create(
        Participant,
        [{'id_in_session': i + 1, 'start_order': j}
         for i, j in enumerate(start_order)])

    for participant in participants:
        ParticipantLockModel(participant_code=participant.code).save()

    for app_name in session_config['app_sequence']:

        models_module = get_models_module(app_name)
        app_constants = get_app_constants(app_name)

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

        subs = bulk_create(
            models_module.Subsession,
            [{'round_number': round_number} for round_number in round_numbers])

        # Create players
        models_module.Player.objects.bulk_create([
            models_module.Player(
                session=session,
                subsession=subsession,
                round_number=round_number,
                participant=participant)
            for round_number, subsession in zip(round_numbers, subs)
            for participant in participants])

    session._create_groups_and_initialize()
    session.build_participant_to_player_lookups()
    if room is not None:
        room.session = session
    session.ready = True
    session.save()
    otree.db.idmap.deactivate_cache()

    return session