Beispiel #1
0
    def get_context_data(self, **kwargs):
        form = kwargs['form']
        context = super().get_context_data(**kwargs)

        apps_with_admin_report = self.session._admin_report_apps()

        app_name = form.cleaned_data.get('app_name')
        if not app_name or form['app_name'].errors:
            app_name = apps_with_admin_report[0]

        round_number = form.cleaned_data.get('round_number')
        if not round_number or form['round_number'].errors:
            round_number = 1

        models_module = get_models_module(app_name)
        subsession = models_module.Subsession.objects.get(
            session=self.session,
            round_number=round_number,
        )
        context['subsession'] = subsession
        context['session'] = self.session
        context['Constants'] = models_module.Constants
        context.update(subsession.vars_for_admin_report() or {})

        context['user_template'] = '{}/AdminReport.html'.format(
            subsession._meta.app_config.label)

        return context
Beispiel #2
0
def info_about_session_config(session_config):

    app_sequence = []
    seo = set()
    for app_name in session_config['app_sequence']:
        models_module = get_models_module(app_name)
        num_rounds = models_module.Constants.num_rounds
        formatted_app_name = app_name_format(app_name)
        if num_rounds > 1:
            formatted_app_name = '{} ({} rounds)'.format(
                formatted_app_name, num_rounds)
        subsssn = {
            'doc': getattr(models_module, 'doc', ''),
            'source_code': getattr(models_module, 'source_code', ''),
            'bibliography': getattr(models_module, 'bibliography', []),
            'links': sort_links(getattr(models_module, 'links', {})),
            'keywords': keywords_links(getattr(models_module, 'keywords', [])),
            'name': formatted_app_name,
        }
        seo.update(map(lambda (a, b): a, subsssn["keywords"]))
        app_sequence.append(subsssn)
    return {
        'doc': session_config['doc'],
        'app_sequence': app_sequence,
        'page_seo': seo
    }
Beispiel #3
0
    def __init__(
            self, case_number: int, participant_bot: ParticipantBot,
            lookup: ParticipantToPlayerLookup):

        app_name = lookup.app_name
        models_module = get_models_module(app_name)

        self.PlayerClass = models_module.Player
        self.GroupClass = models_module.Group
        self.SubsessionClass = models_module.Subsession
        self._player_pk = lookup.player_pk
        self._subsession_pk = lookup.subsession_pk
        self._session_pk = lookup.session_pk
        self._participant_pk = lookup.participant_id

        self.participant_bot = participant_bot

        if case_number == None:
            # default to case 0
            case_number = 0

        cases = self.cases
        if len(cases) >= 1:
            self.case = cases[case_number % len(cases)]
        else:
            self.case = None
Beispiel #4
0
    def get_context_data(self, **kwargs):

        cleaned_data = kwargs['form'].cleaned_data

        models_module = get_models_module(cleaned_data['app_name'])
        subsession = models_module.Subsession.objects.get(
            session=self.session,
            round_number=cleaned_data['round_number'],
        )

        context = {
            'subsession':
            subsession,
            'Constants':
            models_module.Constants,
            'session':
            self.session,
            'user_template':
            '{}/AdminReport.html'.format(subsession._meta.app_config.label)
        }

        vars_for_admin_report = subsession.vars_for_admin_report() or {}
        self.debug_tables = [
            DebugTable(title='vars_for_admin_report',
                       rows=vars_for_admin_report.items())
        ]
        # determine whether to display debug tables
        self.is_debug = settings.DEBUG
        context.update(vars_for_admin_report)

        # this should take priority, in the event of a clash between
        # a user-defined var and a built-in one
        context.update(super().get_context_data(**kwargs))

        return context
Beispiel #5
0
def info_about_session_config(session_config):

    app_sequence = []
    seo = set()
    for app_name in session_config['app_sequence']:
        models_module = get_models_module(app_name)
        num_rounds = models_module.Constants.num_rounds
        formatted_app_name = app_name_format(app_name)
        if num_rounds > 1:
            formatted_app_name = '{} ({} rounds)'.format(
                formatted_app_name, num_rounds
            )
        subsssn = {
            'doc': getattr(models_module, 'doc', ''),
            'source_code': getattr(models_module, 'source_code', ''),
            'bibliography': getattr(models_module, 'bibliography', []),
            'links': sort_links(getattr(models_module, 'links', {})),
            'keywords': keywords_links(getattr(models_module, 'keywords', [])),
            'name': formatted_app_name,
        }
        seo.update(map(lambda (a, b): a, subsssn["keywords"]))
        app_sequence.append(subsssn)
    return {
        'doc': session_config['doc'],
        'app_sequence': app_sequence,
        'page_seo': seo
    }
Beispiel #6
0
def ws_receive(message, params):
    # ASGI WebSocket packet-received and send-packet message types
    # both have a "text" key for their textual data.
    print("ws_receive called.")
    data = json.loads(message['text'])
    message_type = data['type']

    page, group_id, player_id, session_code = params.split(',')
    player_id = int(player_id)
    group_id = int(group_id)

    models_module = get_models_module('channelsmin')
    print("ws_receive called, page=" + str(page) + ", message_type=" +
          str(message_type))
    if page == 'finished':
        # check the message_type - if we're doing more complicated things later different messages can mean different things
        # ... this helps me organize
        if message_type == 'done':

            # make sure to do all these operations at once so that there's less chance of threading issues
            with transaction.atomic():

                # set the group as being finished, so we can automatically forward anybody who joins the page late
                group_object = models_module.Group.objects.get(id=group_id)
                group_object.firstpage_done = True
                group_object.save()

                reply = {
                    'type': 'done',
                }
                # send the click to the other players in the group
                Group(get_group_name(group_id, session_code)).send(
                    {'text': json.dumps(reply)})
Beispiel #7
0
    def get_context_data(self, **kwargs):

        cleaned_data = kwargs['form'].cleaned_data

        models_module = get_models_module(cleaned_data['app_name'])
        subsession = models_module.Subsession.objects.get(
            session=self.session,
            round_number=cleaned_data['round_number'],
        )

        vars_for_admin_report = subsession.vars_for_admin_report() or {}
        self.debug_tables = [
            DebugTable(title='vars_for_admin_report',
                       rows=vars_for_admin_report.items())
        ]
        # determine whether to display debug tables
        self.is_debug = settings.DEBUG

        app_label = subsession._meta.app_config.label
        user_template = select_template([
            f'{app_label}/admin_report.html',
            f'{app_label}/AdminReport.html',
        ])

        context = super().get_context_data(subsession=subsession,
                                           Constants=models_module.Constants,
                                           user_template=user_template,
                                           **kwargs)
        # it's passed by parent class
        assert 'session' in context

        # this should take priority, in the event of a clash between
        # a user-defined var and a built-in one
        context.update(vars_for_admin_report)
        return context
Beispiel #8
0
    def __init__(self, case_number: int, participant_bot: ParticipantBot,
                 lookup: ParticipantToPlayerLookup):

        app_name = lookup.app_name
        models_module = get_models_module(app_name)

        self.PlayerClass = models_module.Player
        self.GroupClass = models_module.Group
        self.SubsessionClass = models_module.Subsession
        self._player_pk = lookup.player_pk
        self._subsession_pk = lookup.subsession_pk
        self._session_pk = lookup.session_pk
        self._participant_pk = lookup.participant_id

        self.participant_bot = participant_bot

        if case_number == None:
            # default to case 0
            case_number = 0

        cases = self.cases
        if len(cases) >= 1:
            self.case = cases[case_number % len(cases)]
        else:
            self.case = None
Beispiel #9
0
def augment_urlpatterns(urlpatterns):

    urlpatterns += urls.patterns(
        '',
        urls.url(r'^$', RedirectView.as_view(url='/demo', permanent=True)),
        urls.url(
            r'^accounts/login/$',
            'django.contrib.auth.views.login',
            {'template_name': 'otree/login.html'},
            name='login_url',
        ),
        urls.url(
            r'^accounts/logout/$',
            'django.contrib.auth.views.logout',
            {'next_page': 'DemoIndex'},
            name='logout',
        ),
    )

    rest_api_urlpatterns = (
        urls.url(r'^ping/$', Ping.as_view(), name="ping"),
        urls.url(
            r'^sessions/(?P<session_code>[a-z0-9]+)/participants/$',
            SessionParticipantsList.as_view(),
            name="session_participants_list")
    )
    urlpatterns += rest_api_urlpatterns

    urlpatterns += staticfiles_urlpatterns()

    used_names_in_url = set()
    for app_name in settings.INSTALLED_OTREE_APPS:
        models_module = get_models_module(app_name)
        name_in_url = models_module.Constants.name_in_url
        if name_in_url in used_names_in_url:
            msg = (
                "App {} has name_in_url='{}', "
                "which is already used by another app"
            ).format(app_name, name_in_url)
            raise ValueError(msg)

        used_names_in_url.add(name_in_url)
        views_module_name = '{}.views'.format(app_name)
        urlpatterns += url_patterns_from_game_module(
            views_module_name, name_in_url)

    urlpatterns += url_patterns_from_module('otree.views.participant')
    urlpatterns += url_patterns_from_module('otree.views.demo')
    urlpatterns += url_patterns_from_module('otree.views.admin')
    urlpatterns += url_patterns_from_module('otree.views.room')
    urlpatterns += url_patterns_from_module('otree.views.mturk')
    urlpatterns += url_patterns_from_module('otree.views.export')

    urlpatterns += extensions_urlpatterns()
    urlpatterns += extensions_export_urlpatterns()

    return urlpatterns
Beispiel #10
0
def get_urlpatterns():

    from django.contrib.auth.views import login, logout

    urlpatterns = [
        urls.url(r'^$', RedirectView.as_view(url='/demo', permanent=True)),
        urls.url(
            r'^accounts/login/$',
            login,
            {'template_name': 'otree/login.html'},
            name='login_url',
        ),
        urls.url(
            r'^accounts/logout/$',
            logout,
            {'next_page': 'DemoIndex'},
            name='logout',
        ),
    ]

    rest_api_urlpatterns = [
        urls.url(r'^ping/$', Ping.as_view(), name="ping"),
        urls.url(r'^sessions/(?P<session_code>[a-z0-9]+)/participants/$',
                 SessionParticipantsList.as_view(),
                 name="session_participants_list")
    ]
    urlpatterns += rest_api_urlpatterns

    urlpatterns += staticfiles_urlpatterns()

    used_names_in_url = set()
    for app_name in settings.INSTALLED_OTREE_APPS:
        models_module = common_internal.get_models_module(app_name)
        name_in_url = models_module.Constants.name_in_url
        if name_in_url in used_names_in_url:
            msg = ("App {} has name_in_url='{}', "
                   "which is already used by another app").format(
                       app_name, name_in_url)
            raise ValueError(msg)

        used_names_in_url.add(name_in_url)

        views_module = common_internal.get_views_module(app_name)
        urlpatterns += url_patterns_from_game_module(views_module.__name__,
                                                     name_in_url)

    urlpatterns += url_patterns_from_module('otree.views.participant')
    urlpatterns += url_patterns_from_module('otree.views.demo')
    urlpatterns += url_patterns_from_module('otree.views.admin')
    urlpatterns += url_patterns_from_module('otree.views.room')
    urlpatterns += url_patterns_from_module('otree.views.mturk')
    urlpatterns += url_patterns_from_module('otree.views.export')

    urlpatterns += extensions_urlpatterns()
    urlpatterns += extensions_export_urlpatterns()

    return urlpatterns
Beispiel #11
0
def augment_urlpatterns(urlpatterns):

    urlpatterns += urls.patterns(
        '',
        urls.url(r'^$', RedirectView.as_view(url='/demo', permanent=True)),
        urls.url(
            r'^accounts/login/$',
            'django.contrib.auth.views.login',
            {'template_name': 'otree/login.html'},
            name='login_url',
        ),
        urls.url(
            r'^accounts/logout/$',
            'django.contrib.auth.views.logout',
            {'next_page': 'demo_index'},
            name='logout',
        ),
    )

    rest_api_urlpatterns = (
        urls.url(r'^ping/$', Ping.as_view(), name="ping"),
        urls.url(
            r'^sessions/(?P<session_code>[a-z]+)/participants/$',
            SessionParticipantsList.as_view(),
            name="session_participants_list")
    )
    urlpatterns += rest_api_urlpatterns

    urlpatterns += staticfiles_urlpatterns()

    used_names_in_url = set()
    for app_name in settings.INSTALLED_OTREE_APPS:
        models_module = get_models_module(app_name)
        name_in_url = models_module.Constants.name_in_url
        if name_in_url in used_names_in_url:
            msg = (
                "App {} has name_in_url='{}', "
                "which is already used by another app"
            ).format(app_name, name_in_url)
            raise ValueError(msg)

        used_names_in_url.add(name_in_url)
        views_module_name = '{}.views'.format(app_name)
        utilities_module_name = '{}._builtin'.format(app_name)
        urlpatterns += url_patterns_from_module(views_module_name)
        urlpatterns += url_patterns_from_module(utilities_module_name)

    urlpatterns += url_patterns_from_module('otree.views.concrete')
    urlpatterns += url_patterns_from_module('otree.views.demo')
    urlpatterns += url_patterns_from_module('otree.views.admin')
    urlpatterns += url_patterns_from_module('otree.views.mturk')
    urlpatterns += url_patterns_from_module('otree.views.export')

    return urlpatterns
Beispiel #12
0
def get_payoff_cache():
    payoff_cache = collections.defaultdict(Decimal)

    for app_name in settings.INSTALLED_OTREE_APPS:
        models_module = get_models_module(app_name)
        Player = models_module.Player
        for d in Player.objects.values('participant_id',
                                       'session_id').annotate(Sum('payoff')):
            payoff_cache[d['participant_id']] += (d['payoff__sum'] or 0)

    return payoff_cache
Beispiel #13
0
def get_payoff_cache():
    payoff_cache = collections.defaultdict(Decimal)

    for app_name in settings.INSTALLED_OTREE_APPS:
        models_module = get_models_module(app_name)
        Player = models_module.Player
        for d in Player.objects.values(
                'participant_id', 'session_id').annotate(Sum('payoff')):
            payoff_cache[d['participant_id']] += (d['payoff__sum'] or 0)

    return payoff_cache
Beispiel #14
0
    def post_connect(self, app_name, player_id, page_index, session_pk):
        models_module = get_models_module(app_name)
        group_id_in_subsession = models_module.Group.objects.filter(
            player__id=player_id).values_list('id_in_subsession', flat=True)[0]

        ready = CompletedGroupWaitPage.objects.filter(
            page_index=page_index,
            id_in_subsession=int(group_id_in_subsession),
            session_id=session_pk,
        ).exists()
        if ready:
            self.send({'status': 'ready'})
Beispiel #15
0
def get_rows_for_wide_csv():

    from collections import OrderedDict
    from django.conf import settings
    from django.db.models import Max, Count


    sessions = Session.objects.order_by('id').annotate(
        num_participants=Count('participant')).values()
    session_cache = {row['id']: row for row in sessions}
    participants = Participant.objects.order_by('id').values()

    session_fields = get_field_names_for_csv(Session)
    participant_fields = get_field_names_for_csv(Participant)
    header_row = ['participant.{}'.format(fname) for fname in participant_fields]
    header_row += ['session.{}'.format(fname) for fname in session_fields]
    rows = [header_row]
    for participant in participants:
        row = [sanitize_for_csv(participant[fname]) for fname in participant_fields]
        session = session_cache[participant['session_id']]
        row += [sanitize_for_csv(session[fname]) for fname in session_fields]
        rows.append(row)

    # heuristic to get the most relevant order of apps
    import json
    app_sequences = collections.Counter()
    for session in sessions:
        config = json.loads(session['config'])
        app_sequence = config['app_sequence']
        app_sequences[tuple(app_sequence)] += session['num_participants']
    most_common_app_sequence = app_sequences.most_common(1)[0][0]

    apps_not_in_popular_sequence = [
        app for app in settings.INSTALLED_OTREE_APPS
        if app not in most_common_app_sequence]

    order_of_apps = list(most_common_app_sequence) + apps_not_in_popular_sequence

    rounds_per_app = OrderedDict()
    for app_name in order_of_apps:
        models_module = get_models_module(app_name)
        agg_dict = models_module.Subsession.objects.all().aggregate(Max('round_number'))
        highest_round_number = agg_dict['round_number__max']

        if highest_round_number is not None:
            rounds_per_app[app_name] = highest_round_number
    for app_name in rounds_per_app:
        for round_number in range(1, rounds_per_app[app_name] + 1):
            new_rows = get_rows_for_wide_csv_round(app_name, round_number, sessions)
            for i in range(len(rows)):
                rows[i].extend(new_rows[i])
    return rows
Beispiel #16
0
    def post_connect(self, app_name, player_id, page_index, session_pk):
        models_module = get_models_module(app_name)
        group_id_in_subsession = models_module.Group.objects.filter(
            player__id=player_id).values_list(
            'id_in_subsession', flat=True)[0]

        ready = CompletedGroupWaitPage.objects.filter(
            page_index=page_index,
            id_in_subsession=int(group_id_in_subsession),
            session_id=session_pk,
            ).exists()
        if ready:
            self.send({'status': 'ready'})
Beispiel #17
0
 def app_sequence_display(self):
     app_sequence = []
     for app_name in self['app_sequence']:
         models_module = get_models_module(app_name)
         num_rounds = models_module.Constants.num_rounds
         if num_rounds > 1:
             formatted_app_name = '{} ({} rounds)'.format(
                 app_name, num_rounds)
         else:
             formatted_app_name = app_name
         subsssn = {
             'doc': getattr(models_module, 'doc', ''),
             'name': formatted_app_name}
         app_sequence.append(subsssn)
     return app_sequence
Beispiel #18
0
    def get_info(self):
        app_sequence = []
        for app_name in self['app_sequence']:
            models_module = get_models_module(app_name)
            num_rounds = models_module.Constants.num_rounds
            formatted_app_name = otree.common_internal.app_name_format(
                app_name)
            if num_rounds > 1:
                formatted_app_name = '{} ({} rounds)'.format(
                    formatted_app_name, num_rounds)
            subsssn = {
                'doc': getattr(models_module, 'doc', ''),
                'bibliography': getattr(models_module, 'bibliography', []),
                'name': formatted_app_name}
            app_sequence.append(subsssn)

        return {
            'doc': self['doc'],
            'app_sequence': app_sequence,
            'name': self['name'],
            'display_name': self['display_name'],
            'lcm': self.get_lcm()}
Beispiel #19
0
def info_about_session_config(session_config):
    app_sequence = []
    for app_name in session_config['app_sequence']:
        models_module = get_models_module(app_name)
        num_rounds = models_module.Constants.num_rounds
        formatted_app_name = app_name_format(app_name)
        if num_rounds > 1:
            formatted_app_name = '{} ({} rounds)'.format(
                formatted_app_name, num_rounds)
        subsssn = {
            'doc': getattr(models_module, 'doc', ''),
            'bibliography': getattr(models_module, 'bibliography', []),
            'name': formatted_app_name,
        }
        app_sequence.append(subsssn)
    return {
        'doc': session_config['doc'],
        'app_sequence': app_sequence,
        'name': session_config['name'],
        'display_name': session_config['display_name'],
        'lcm': get_lcm(session_config)
    }
Beispiel #20
0
def connect_group_by_arrival_time(message, params):
    session_pk, page_index, app_name, player_id = params.split(',')
    session_pk = int(session_pk)
    page_index = int(page_index)
    player_id = int(player_id)

    group_name = channels_group_by_arrival_time_group_name(session_pk, page_index)
    group = Group(group_name)
    group.add(message.reply_channel)

    models_module = get_models_module(app_name)
    player = models_module.Player.objects.get(id=player_id)
    group_id_in_subsession = player.group.id_in_subsession

    ready = CompletedGroupWaitPage.objects.filter(
        page_index=page_index,
        id_in_subsession=int(group_id_in_subsession),
        session_id=session_pk,
        fully_completed=True).exists()
    if ready:
        message.reply_channel.send(
            {'text': json.dumps(
                {'status': 'ready'})})
Beispiel #21
0
def info_about_session_config(session_config):
    app_sequence = []
    for app_name in session_config['app_sequence']:
        models_module = get_models_module(app_name)
        num_rounds = models_module.Constants.num_rounds
        formatted_app_name = app_name_format(app_name)
        if num_rounds > 1:
            formatted_app_name = '{} ({} rounds)'.format(
                formatted_app_name, num_rounds
            )
        subsssn = {
            'doc': getattr(models_module, 'doc', ''),
            'bibliography': getattr(models_module, 'bibliography', []),
            'name': formatted_app_name,
        }
        app_sequence.append(subsssn)
    return {
        'doc': session_config['doc'],
        'app_sequence': app_sequence,
        'name': session_config['name'],
        'display_name': session_config['display_name'],
        'lcm': get_lcm(session_config)
    }
Beispiel #22
0
def connect_wait_page(message, params):
    app_label, page_index, model_name, model_pk = params.split(',')
    page_index = int(page_index)
    model_pk = int(model_pk)


    group_name = channels_wait_page_group_name(
        app_label, page_index, model_name, model_pk
    )
    group = Group(group_name)
    group.add(message.reply_channel)

    # in case message was sent before this web socket connects
    # fixme: app name or app label?
    models_module = common_internal.get_models_module(app_label)

    GroupOrSubsession = {
        'subsession': getattr(models_module, 'Subsession'),
        'group': getattr(models_module, 'Group')
    }[model_name]

    group_or_subsession = GroupOrSubsession.objects.get(pk=model_pk)

    participants_for_this_page = set(
        p.participant for p in group_or_subsession.player_set.all()
    )

    unvisited = set(
        p for p in participants_for_this_page if
        p._index_in_pages < page_index
    )

    if not unvisited:
        message.reply_channel.send(
            {'text': json.dumps(
                {'status': 'ready'})})
Beispiel #23
0
def send_message(message, session_code, index_in_pages, participant_code,
                 player_pk, group_pk):
    cursession = Session.objects.get(code=session_code)
    curparticipant = Participant.objects.get(code=participant_code)
    url = curparticipant._url_i_should_be_on()
    Page = get_view_from_url(url)
    app_name = Page.__module__.split('.')[0]
    models_module = get_models_module(app_name)
    curplayer = models_module.Player.objects.get(pk=player_pk)
    subsession = curplayer.subsession
    those_with_us = []
    if hasattr(Page, 'group_by_arrival_time'):
        if getattr(Page, 'group_by_arrival_time'):
            those_with_us = models_module.Player.objects.filter(
                subsession=subsession,
                participant___index_in_pages=index_in_pages,
                _group_by_arrival_time_arrived=True,
                _group_by_arrival_time_grouped=False,
            )
        else:
            those_with_us = models_module.Player.objects.filter(
                subsession=subsession,
                participant___index_in_pages=index_in_pages,
                group=curplayer.group,
            )

    how_many_arrived = len(those_with_us)
    left_to_wait = Constants.players_per_group - how_many_arrived
    textforgroup = json.dumps({
        "how_many_arrived": how_many_arrived,
        "left_to_wait": left_to_wait,
    })
    Group(get_group_name(session_code, index_in_pages, group_pk)).send({
        "text":
        textforgroup,
    })
Beispiel #24
0
def ws_add(message, params):
    print("ws_add called. params = " + params)
    page, group_id, player_id, session_code = params.split(',')
    player_id = int(player_id)
    group_id = int(group_id)

    # add them to the channels group
    group = Group(get_group_name(group_id, session_code))
    group.add(message.reply_channel)

    # need to check to see if someone in the group has already chosen to move on - if so, send the done message upon joining
    with transaction.atomic():
        models_module = get_models_module('channelsmin')
        group_object = models_module.Group.objects.get(id=group_id)

        if page == 'finished' and group_object.firstpage_done:
            print(
                "inside ws_add, checking to see if group's first page is done.  firstpage_done="
                + str(group_object.firstpage_done))

            reply = {
                'type': 'done',
            }
            message.reply_channel.send({'text': json.dumps(reply)})
Beispiel #25
0
    def get_context_data(self, **kwargs):

        cleaned_data = kwargs['form'].cleaned_data

        models_module = get_models_module(cleaned_data['app_name'])
        subsession = models_module.Subsession.objects.get(
            session=self.session,
            round_number=cleaned_data['round_number'],
        )

        context = {
            'subsession': subsession,
            'Constants': models_module.Constants,
            'session': self.session,
            'user_template': '{}/AdminReport.html'.format(
            subsession._meta.app_config.label)
        }

        vars_for_admin_report = subsession.vars_for_admin_report() or {}
        self.debug_tables = [
            DebugTable(
                title='vars_for_admin_report',
                rows=vars_for_admin_report.items()
            )
        ]
        # determine whether to display debug tables
        self.is_debug = settings.DEBUG
        context.update(vars_for_admin_report)

        # this should take priority, in the event of a clash between
        # a user-defined var and a built-in one
        context.update(super().get_context_data(**kwargs))



        return context
Beispiel #26
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
Beispiel #27
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
Beispiel #28
0
 def _Constants(self):
     return get_models_module(self._meta.app_config.name).Constants
Beispiel #29
0
def export_docs(fp, app_name):
    """Write the dcos of the given app name as csv into the file-like object

    """

    # generate doct_dict
    models_module = get_models_module(app_name)

    model_names = ["Participant", "Player", "Group", "Subsession", "Session"]
    line_break = '\r\n'

    def choices_readable(choices):
        lines = []
        for value, name in choices:
            # unicode() call is for lazy translation strings
            lines.append(u'{}: {}'.format(value, six.text_type(name)))
        return lines

    def generate_doc_dict():
        doc_dict = OrderedDict()

        data_types_readable = {
            'PositiveIntegerField': 'positive integer',
            'IntegerField': 'integer',
            'BooleanField': 'boolean',
            'CharField': 'text',
            'TextField': 'text',
            'FloatField': 'decimal',
            'DecimalField': 'decimal',
            'CurrencyField': 'currency'}

        for model_name in model_names:
            if model_name == 'Participant':
                Model = Participant
            elif model_name == 'Session':
                Model = Session
            else:
                Model = getattr(models_module, model_name)

            field_names = set(field.name for field in Model._meta.fields)

            members = get_field_names_for_csv(Model)
            doc_dict[model_name] = OrderedDict()

            for member_name in members:
                member = getattr(Model, member_name, None)
                doc_dict[model_name][member_name] = OrderedDict()
                if member_name == 'id':
                    doc_dict[model_name][member_name]['type'] = [
                        'positive integer']
                    doc_dict[model_name][member_name]['doc'] = ['Unique ID']
                elif member_name in field_names:
                    member = Model._meta.get_field_by_name(member_name)[0]

                    internal_type = member.get_internal_type()
                    data_type = data_types_readable.get(
                        internal_type, internal_type)

                    doc_dict[model_name][member_name]['type'] = [data_type]

                    # flag error if the model doesn't have a doc attribute,
                    # which it should unless the field is a 3rd party field
                    doc = getattr(member, 'doc', '[error]') or ''
                    doc_dict[model_name][member_name]['doc'] = [
                        line.strip() for line in doc.splitlines()
                        if line.strip()]

                    choices = getattr(member, 'choices', None)
                    if choices:
                        doc_dict[model_name][member_name]['choices'] = (
                            choices_readable(choices))
                elif isinstance(member, collections.Callable):
                    doc_dict[model_name][member_name]['doc'] = [
                        inspect.getdoc(member)]
        return doc_dict

    def docs_as_string(doc_dict):

        first_line = '{}: Documentation'.format(app_name_format(app_name))
        second_line = '*' * len(first_line)

        lines = [
            first_line, second_line, '',
            'Accessed: {}'.format(datetime.date.today().isoformat()), '']

        app_doc = getattr(models_module, 'doc', '')
        if app_doc:
            lines += [app_doc, '']

        for model_name in doc_dict:
            lines.append(model_name)

            for member in doc_dict[model_name]:
                lines.append('\t{}'.format(member))
                for info_type in doc_dict[model_name][member]:
                    lines.append('\t\t{}'.format(info_type))
                    for info_line in doc_dict[model_name][member][info_type]:
                        lines.append(u'{}{}'.format('\t' * 3, info_line))

        output = u'\n'.join(lines)
        return output.replace('\n', line_break).replace('\t', '    ')

    doc_dict = generate_doc_dict()
    doc = docs_as_string(doc_dict)
    fp.write(doc)
Beispiel #30
0
 def _Constants(self):
     return get_models_module(self._meta.app_config.name).Constants
Beispiel #31
0
    def get_dataframe_for_app(cls, app_name):
        """
        Generate data rows for app `app_name`, also adding rows of custom data models.
        """

        models_module = get_models_module(app_name)

        # get the standard models
        Player = models_module.Player
        Group = models_module.Group
        Subsession = models_module.Subsession

        # get custom model configuration, if there is any
        custom_models_conf = get_custom_models_conf(models_module,
                                                    for_action='export_data')

        # identify links between standard and custom models
        links_to_custom_models = get_links_between_std_and_custom_models(
            custom_models_conf, for_action='export_data')

        # find out column names for standard models
        std_models_colnames = {
            m.__name__.lower(): get_field_names_for_csv(m)
            for m in (Session, Subsession, Group, Player, Participant)
        }
        std_models_colnames['player'].append('participant_id')

        # find out column names for custom models
        custom_models_colnames = cls.custom_columns_builder(custom_models_conf)

        # create lists of IDs that will be used for the export
        participant_ids = set(
            Player.objects.values_list('participant_id', flat=True))
        session_ids = set(
            Subsession.objects.values_list('session_id', flat=True))

        filter_in_sess = {'session_id__in': session_ids}

        std_models_querysets = (
            (Session, Session.objects.filter(id__in=session_ids), (None,
                                                                   None)),
            (Subsession, Subsession.objects.filter(**filter_in_sess),
             ('session.id', 'subsession.session_id')),
            (Group, Group.objects.filter(**filter_in_sess),
             ('subsession.id', 'group.subsession_id')),
            (Player, Player.objects.filter(**filter_in_sess),
             ('group.id', 'player.group_id')),
            (Participant, Participant.objects.filter(id__in=participant_ids),
             ('player.participant_id', 'participant.id')),
        )

        # create a dataframe for this app's complete data incl. custom models data
        df = get_dataframe_from_linked_models(std_models_querysets,
                                              links_to_custom_models,
                                              std_models_colnames,
                                              custom_models_colnames)

        # sanitize each value
        df = df.applymap(sanitize_pdvalue_for_csv)

        return df
Beispiel #32
0
def get_urlpatterns():

    from django.contrib.auth.views import login, logout

    urlpatterns = [
        urls.url(r'^$', RedirectView.as_view(url='/demo', permanent=True)),
        urls.url(
            r'^accounts/login/$',
            login,
            {'template_name': 'otree/login.html'},
            name='login_url',
        ),
        urls.url(
            r'^accounts/logout/$',
            logout,
            {'next_page': 'DemoIndex'},
            name='logout',
        ),
    ]

    urlpatterns += staticfiles_urlpatterns()

    used_names_in_url = set()
    for app_name in settings.INSTALLED_OTREE_APPS:
        models_module = common_internal.get_models_module(app_name)
        name_in_url = models_module.Constants.name_in_url
        if name_in_url in used_names_in_url:
            msg = ("App {} has Constants.name_in_url='{}', "
                   "which is already used by another app").format(
                       app_name, name_in_url)
            raise ValueError(msg)

        used_names_in_url.add(name_in_url)

        views_module = common_internal.get_pages_module(app_name)
        urlpatterns += url_patterns_from_game_module(views_module.__name__,
                                                     name_in_url)

    urlpatterns += url_patterns_from_module('otree.views.participant')
    urlpatterns += url_patterns_from_module('otree.views.demo')
    urlpatterns += url_patterns_from_module('otree.views.admin')
    urlpatterns += url_patterns_from_module('otree.views.room')
    urlpatterns += url_patterns_from_module('otree.views.mturk')
    urlpatterns += url_patterns_from_module('otree.views.export')

    urlpatterns += extensions_urlpatterns()
    urlpatterns += extensions_export_urlpatterns()

    # serve an empty favicon?
    # otherwise, the logs will contain:
    # [WARNING] django.request > Not Found: /favicon.ico
    # Not Found: /favicon.ico
    # don't want to add a <link> in base template because even if it exists,
    # browsers will still request /favicon.ico.
    # plus it makes the HTML noisier
    # can't use the static() function here because maybe collectstatic
    # has not been run yet
    # and it seems an empty HttpResponse or even a 204 response makes the browser
    # just keep requesting the file with every page load
    # hmmm...now it seems that chrome is not re-requesting with every page load
    # but firefox does. but if i remove the favicon, there's 1 404 then FF doesn't
    # ask for it again.

    # import os
    # dir_path = os.path.dirname(os.path.realpath(__file__))
    # with open(os.path.join(dir_path, 'favicon_invisible.ico'), 'rb') as f:
    # #with open('favicon.ico', 'rb') as f:
    #     favicon_content = f.read()
    #
    #
    # urlpatterns.append(
    #     urls.url(
    #         r'^favicon\.ico$',
    #         lambda request: HttpResponse(favicon_content, content_type="image/x-icon")
    #     )
    # )

    return urlpatterns
Beispiel #33
0
def get_rows_for_wide_csv():

    sessions = Session.objects.order_by('id').annotate(
        num_participants=Count('participant')).values()
    session_cache = {row['id']: row for row in sessions}

    session_config_fields = set()
    for row in sessions:
        config = json_loads(row['config'])
        config = SessionConfig(config)
        for field_name in config.editable_fields():
            session_config_fields.add(field_name)
        # store it for later, when we need app_sequence
        row['config'] = config
    session_config_fields = list(session_config_fields)

    participants = Participant.objects.order_by('id').values()
    if not participants:
        # 1 empty row
        return [[]]

    payoff_cache = get_payoff_cache()
    payoff_plus_participation_fee_cache = get_payoff_plus_participation_fee_cache(
        payoff_cache)

    session_fields = get_field_names_for_csv(Session)
    participant_fields = get_field_names_for_csv(Participant)
    participant_fields += ['payoff', 'payoff_plus_participation_fee']
    header_row = [
        'participant.{}'.format(fname) for fname in participant_fields
    ]
    header_row += ['session.{}'.format(fname) for fname in session_fields]
    header_row += [
        'session.config.{}'.format(fname) for fname in session_config_fields
    ]
    rows = [header_row]
    for participant in participants:
        participant['payoff'] = payoff_cache[participant['id']]
        participant[
            'payoff_plus_participation_fee'] = payoff_plus_participation_fee_cache[
                participant['id']]
        row = [
            sanitize_for_csv(participant[fname])
            for fname in participant_fields
        ]
        session = session_cache[participant['session_id']]
        row += [sanitize_for_csv(session[fname]) for fname in session_fields]
        config = session['config']
        row += [
            sanitize_for_csv(config.get(fname))
            for fname in session_config_fields
        ]
        rows.append(row)

    # heuristic to get the most relevant order of apps
    app_sequences = collections.Counter()
    for session in sessions:
        # we loaded the config earlier
        app_sequence = session['config']['app_sequence']
        app_sequences[tuple(app_sequence)] += session['num_participants']
    most_common_app_sequence = app_sequences.most_common(1)[0][0]

    apps_not_in_popular_sequence = [
        app for app in settings.INSTALLED_OTREE_APPS
        if app not in most_common_app_sequence
    ]

    order_of_apps = list(
        most_common_app_sequence) + apps_not_in_popular_sequence

    rounds_per_app = OrderedDict()
    for app_name in order_of_apps:
        models_module = get_models_module(app_name)
        agg_dict = models_module.Subsession.objects.all().aggregate(
            Max('round_number'))
        highest_round_number = agg_dict['round_number__max']

        if highest_round_number is not None:
            rounds_per_app[app_name] = highest_round_number
    for app_name in rounds_per_app:
        for round_number in range(1, rounds_per_app[app_name] + 1):
            new_rows = get_rows_for_wide_csv_round(app_name, round_number,
                                                   sessions)
            for i in range(len(rows)):
                rows[i].extend(new_rows[i])
    return rows
Beispiel #34
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:

    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.

        if for_mturk:
            mturk_num_participants = (
                    num_participants /
                    settings.MTURK_NUM_PARTICIPANTS_MULTIPLE)
        else:
            mturk_num_participants = -1

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

        # check that it divides evenly
        session_lcm = session_config.get_lcm()
        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)

        Participant.objects.bulk_create(
            [
                Participant(id_in_session=id_in_session, session=session)
                for id_in_session in list(range(1, num_participants+1))
            ]
        )

        participant_values = session.participant_set.order_by('id').values('code', 'id')

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

        participant_to_player_lookups = []
        page_index = 0

        for app_name in session_config['app_sequence']:

            views_module = common_internal.get_pages_module(app_name)
            models_module = get_models_module(app_name)
            Constants = models_module.Constants
            num_subsessions += Constants.num_rounds

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

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

            Subsession.objects.bulk_create(
                [
                    Subsession(round_number=round_number, session=session)
                    for round_number in round_numbers
                ]
            )

            subsessions = Subsession.objects.filter(
                session=session).order_by('round_number').values(
                '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 subsession in subsessions:
                for id_in_subsession in range(1, num_groups_per_round+1):
                    groups_to_create.append(
                        Group(
                            session=session,
                            subsession_id=subsession['id'],
                            round_number=subsession['round_number'],
                            id_in_subsession=id_in_subsession,
                        )
                    )

            Group.objects.bulk_create(groups_to_create)

            groups = Group.objects.filter(session=session).values(
                'id_in_subsession', 'subsession_id', 'id'
            ).order_by('id_in_subsession')

            groups_lookup = defaultdict(list)

            for group in groups:
                subsession_id = group['subsession_id']
                groups_lookup[subsession_id].append(group['id'])

            players_to_create = []

            for subsession in subsessions:
                subsession_id=subsession['id']
                round_number=subsession['round_number']
                participant_index = 0
                for group_id in groups_lookup[subsession_id]:
                    for id_in_group in range(1, ppg+1):
                        participant = participant_values[participant_index]
                        players_to_create.append(
                            Player(
                                session=session,
                                subsession_id=subsession_id,
                                round_number=round_number,
                                participant_id=participant['id'],
                                group_id=group_id,
                                id_in_group=id_in_group
                            )
                        )
                        participant_index += 1

            # Create players
            Player.objects.bulk_create(players_to_create)

            players_flat = Player.objects.filter(session=session).values(
                'id', 'participant__code', 'participant__id', 'subsession__id',
                'round_number'
            )

            players_by_round = [[] for _ in range(Constants.num_rounds)]
            for p in players_flat:
                players_by_round[p['round_number']-1].append(p)

            for round_number, round_players in enumerate(players_by_round, start=1):
                for View in views_module.page_sequence:
                    page_index += 1
                    for p in round_players:

                        participant_code = p['participant__code']

                        url = View.get_url(
                            participant_code=participant_code,
                            name_in_url=Constants.name_in_url,
                            page_index=page_index
                        )

                        participant_to_player_lookups.append(
                            ParticipantToPlayerLookup(
                                participant_id=p['participant__id'],
                                participant_code=participant_code,
                                page_index=page_index,
                                app_name=app_name,
                                player_pk=p['id'],
                                subsession_pk=p['subsession__id'],
                                session_pk=session.pk,
                                url=url))

        ParticipantToPlayerLookup.objects.bulk_create(
            participant_to_player_lookups
        )
        session.participant_set.update(_max_page_index=page_index)

        with otree.db.idmap.use_cache():
            # possible optimization: check if
            # Subsession.creating_session == BaseSubsession.creating_session
            # if so, skip it.
            # but this will only help people who didn't override creating_session
            # in that case, the session usually creates quickly, anyway.
            for subsession in session.get_subsessions():
                subsession.before_session_starts()
                subsession.creating_session()
            otree.db.idmap.save_objects()

        # 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.set_session(session)

    return session
Beispiel #35
0
def get_rows_for_wide_csv():

    sessions = Session.objects.order_by('id')
    session_cache = {row.id: row for row in sessions}

    session_config_fields = set()
    for session in sessions:
        for field_name in SessionConfig(session.config).editable_fields():
            session_config_fields.add(field_name)
    session_config_fields = list(session_config_fields)

    participants = Participant.objects.order_by('id').values()
    if not participants:
        # 1 empty row
        return [[]]

    session_fields = get_field_names_for_csv(Session)
    participant_fields = get_field_names_for_csv(Participant)
    participant_fields.append('payoff_plus_participation_fee')
    header_row = [
        'participant.{}'.format(fname) for fname in participant_fields
    ]
    header_row += ['session.{}'.format(fname) for fname in session_fields]
    header_row += [
        'session.config.{}'.format(fname) for fname in session_config_fields
    ]
    rows = [header_row]
    for participant in participants:
        session = session_cache[participant['session_id']]
        participant[
            'payoff_plus_participation_fee'] = get_payoff_plus_participation_fee(
                session, participant)
        row = [
            sanitize_for_csv(participant[fname])
            for fname in participant_fields
        ]

        row += [
            sanitize_for_csv(getattr(session, fname))
            for fname in session_fields
        ]
        row += [
            sanitize_for_csv(session.config.get(fname))
            for fname in session_config_fields
        ]
        rows.append(row)

    # heuristic to get the most relevant order of apps
    app_sequences = collections.Counter()
    for session in sessions:
        # we loaded the config earlier
        app_sequence = session.config['app_sequence']
        app_sequences[tuple(app_sequence)] += session.num_participants
    most_common_app_sequence = app_sequences.most_common(1)[0][0]

    # can't use settings.INSTALLED_OTREE_APPS, because maybe the app
    # was removed from SESSION_CONFIGS.
    app_names_with_data = set()
    for session in sessions:
        for app_name in session.config['app_sequence']:
            app_names_with_data.add(app_name)

    apps_not_in_popular_sequence = [
        app for app in app_names_with_data
        if app not in most_common_app_sequence
    ]

    order_of_apps = list(
        most_common_app_sequence) + apps_not_in_popular_sequence

    rounds_per_app = OrderedDict()
    for app_name in order_of_apps:
        models_module = get_models_module(app_name)
        agg_dict = models_module.Subsession.objects.all().aggregate(
            Max('round_number'))
        highest_round_number = agg_dict['round_number__max']

        if highest_round_number is not None:
            rounds_per_app[app_name] = highest_round_number
    for app_name in rounds_per_app:
        for round_number in range(1, rounds_per_app[app_name] + 1):
            new_rows = get_rows_for_wide_csv_round(app_name, round_number,
                                                   sessions)
            for i in range(len(rows)):
                rows[i].extend(new_rows[i])
    return rows
Beispiel #36
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
Beispiel #37
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
Beispiel #38
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
Beispiel #39
0
def export_docs(app_name):
    """
        Taken and adapted from the otree core: https://github.com/oTree-org/otree-core/blob/master/otree/export.py
        Adapting to export to csv and to add non-standard models
    """

    # generate doct_dict
    models_module = get_models_module(app_name)

    model_names = [
        "Participant", "Player", "Group", "Subsession", "Session", "Ask",
        "Bid", "Contract", "PriceDim"
    ]
    line_break = '\r\n'

    def choices_readable(choices):
        lines = []
        for value, name in choices:
            # unicode() call is for lazy translation strings
            lines.append(u'{}: {}'.format(value, six.text_type(name)))
        return lines

    def generate_doc_dict():
        doc_dict = OrderedDict()

        data_types_readable = {
            'PositiveIntegerField': 'positive integer',
            'IntegerField': 'integer',
            'BooleanField': 'boolean',
            'CharField': 'text',
            'TextField': 'text',
            'FloatField': 'decimal',
            'DecimalField': 'decimal',
            'CurrencyField': 'currency'
        }

        for model_name in model_names:
            if model_name == 'Participant':
                Model = Participant
            elif model_name == 'Session':
                Model = Session
            else:
                Model = getattr(models_module, model_name)
            # print(model_name)

            field_names = set(field.name for field in Model._meta.fields)

            members = get_field_names_for_csv(Model)
            if not members:
                members = [f for f in inspect_field_names(Model)]
            doc_dict[model_name] = OrderedDict()

            for member_name in members:
                member = getattr(Model, member_name, None)
                doc_dict[model_name][member_name] = OrderedDict()
                if member_name == 'id':
                    doc_dict[model_name][member_name]['type'] = [
                        'positive integer'
                    ]
                    doc_dict[model_name][member_name]['doc'] = ['Unique ID']
                elif member_name in field_names:
                    member = Model._meta.get_field_by_name(member_name)[0]

                    internal_type = member.get_internal_type()
                    data_type = data_types_readable.get(
                        internal_type, internal_type)

                    doc_dict[model_name][member_name]['type'] = [data_type]

                    # flag error if the model doesn't have a doc attribute,
                    # which it should unless the field is a 3rd party field
                    doc = getattr(member, 'doc', '[error]') or ''
                    doc_dict[model_name][member_name]['doc'] = [
                        line.strip() for line in doc.splitlines()
                        if line.strip()
                    ]

                    choices = getattr(member, 'choices', None)
                    if choices:
                        doc_dict[model_name][member_name]['choices'] = (
                            choices_readable(choices))
                elif isinstance(member, collections.Callable):
                    doc_dict[model_name][member_name]['doc'] = [
                        inspect.getdoc(member)
                    ]
        return doc_dict

    def docs_as_lists(doc_dict):
        header = ["Model", "Field", "Type", "Description"]
        body = [['{}: Documentation'.format(app_name_format(app_name))],
                ['*' * 15],
                ['Accessed: {}'.format(datetime.date.today().isoformat())],
                ['']]

        app_doc = getattr(models_module, 'doc', '')
        if app_doc:
            body += [app_doc, '']

        for model_name in doc_dict:
            for member in doc_dict[model_name]:
                # lines.append('\t{}'.format(member))
                for info_type in doc_dict[model_name][member]:
                    # lines.append('\t\t{}'.format(info_type))
                    for info_line in doc_dict[model_name][member][info_type]:
                        # lines.append(u'{}{}'.format('\t' * 3, info_line))
                        body += [[model_name, member, info_type, info_line]]

        return header, body

    doc_dict = generate_doc_dict()
    return docs_as_lists(doc_dict)
Beispiel #40
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
Beispiel #41
0
    def get_hierarchical_data_for_app(cls, app_name, return_columns=False):
        """
        Generate hierarchical structured data for app `app_name`, optionally returning flattened field names.
        """

        models_module = get_models_module(app_name)

        # get the standard models
        Player = models_module.Player
        Group = models_module.Group
        Subsession = models_module.Subsession

        # get the custom models configuration
        custom_models_conf = get_custom_models_conf(models_module,
                                                    'export_data')

        # build standard models' columns
        columns_for_models = {
            m.__name__.lower(): get_field_names_for_csv(m)
            for m in [Player, Group, Subsession, Participant, Session]
        }

        # build custom models' columns
        columns_for_custom_models = cls.custom_columns_builder(
            custom_models_conf)

        custom_models_links = get_links_between_std_and_custom_models(
            custom_models_conf, for_action='export_data')
        std_models_select_related = defaultdict(list)
        for smodel_class, cmodels_links in custom_models_links.items():
            smodel_lwr = smodel_class.__name__.lower()
            for cmodel_class, _ in cmodels_links:
                std_models_select_related[smodel_lwr].append(
                    cmodel_class.__name__.lower())

        # create lists of IDs that will be used for the export
        participant_ids = set(
            Player.objects.values_list('participant_id', flat=True))
        session_ids = set(
            Subsession.objects.values_list('session_id', flat=True))

        # create standard model querysets
        qs_participant = Participant.objects.filter(id__in=participant_ids)
        qs_player = Player.objects.filter(session_id__in=session_ids)\
            .order_by('id')\
            .select_related(*std_models_select_related.get('player', [])).values()
        qs_group = Group.objects.filter(session_id__in=session_ids)\
            .select_related(*std_models_select_related.get('group', []))
        qs_subsession = Subsession.objects.filter(session_id__in=session_ids)\
            .select_related(*std_models_select_related.get('subsession', []))

        # create prefetch dictionaries from querysets that map IDs to subsets of the data

        prefetch_filter_ids_for_custom_models = {
        }  # stores IDs per standard oTree model to be used for
        # custom data prefetching

        # session ID -> subsession rows for this session
        prefetch_subsess = _rows_per_key_from_queryset(qs_subsession,
                                                       'session_id')
        prefetch_filter_ids_for_custom_models[
            'subsession'] = _set_of_ids_from_rows_per_key(
                prefetch_subsess, 'id')

        # subsession ID -> group rows for this subsession
        prefetch_group = _rows_per_key_from_queryset(qs_group, 'subsession_id')
        prefetch_filter_ids_for_custom_models[
            'group'] = _set_of_ids_from_rows_per_key(prefetch_group, 'id')

        # group ID -> player rows for this group
        prefetch_player = _rows_per_key_from_queryset(qs_player, 'group_id')
        prefetch_filter_ids_for_custom_models[
            'player'] = _set_of_ids_from_rows_per_key(prefetch_player, 'id')

        # prefetch dict for custom data models
        prefetch_custom = defaultdict(
            dict
        )  # standard oTree model name -> custom model name -> data rows
        for smodel, cmodel_links in custom_models_links.items(
        ):  # per oTree std. model
            smodel_name_lwr = smodel.__name__.lower()

            # IDs that occur for that model
            filter_ids = prefetch_filter_ids_for_custom_models[smodel_name_lwr]

            # iterate per custom model
            for model, link_field_name in cmodel_links:
                # prefetch custom model objects that are linked to these oTree std. model IDs
                filter_kwargs = {link_field_name + '__in': filter_ids}
                custom_qs = model.objects.filter(**filter_kwargs).values()

                # store to the dict
                m = model.__name__.lower()
                prefetch_custom[smodel_name_lwr][
                    m] = _rows_per_key_from_queryset(custom_qs,
                                                     link_field_name)

        # build the final nested data structure
        output_nested = []
        ordered_columns_per_model = OrderedDict()
        # 1. each session
        for sess in Session.objects.filter(id__in=session_ids).values():
            sess_cols = columns_for_models['session']
            if 'session' not in ordered_columns_per_model:
                ordered_columns_per_model['session'] = sess_cols

            out_sess = _odict_from_row(sess, sess_cols)

            # 1.1. each subsession in the session
            out_sess['__subsession'] = []
            for subsess in prefetch_subsess[sess['id']]:
                subsess_cols = columns_for_models['subsession']
                if 'subsession' not in ordered_columns_per_model:
                    ordered_columns_per_model['subsession'] = subsess_cols

                out_subsess = _odict_from_row(subsess, subsess_cols)

                # 1.1.1. each possible custom models connected to this subsession
                subsess_custom_models_rows = prefetch_custom.get(
                    'subsession', {})
                for subsess_cmodel_name, subsess_cmodel_rows in subsess_custom_models_rows.items(
                ):
                    cmodel_cols = columns_for_custom_models[
                        subsess_cmodel_name]
                    if subsess_cmodel_name not in ordered_columns_per_model:
                        ordered_columns_per_model[
                            subsess_cmodel_name] = cmodel_cols

                    out_subsess['__' + subsess_cmodel_name] = [
                        _odict_from_row(cmodel_row, cmodel_cols)
                        for cmodel_row in subsess_cmodel_rows[subsess['id']]
                    ]

                # 1.1.2. each group in this subsession
                out_subsess['__group'] = []
                for grp in prefetch_group[subsess['id']]:
                    grp_cols = columns_for_models['group']
                    if 'group' not in ordered_columns_per_model:
                        ordered_columns_per_model['group'] = grp_cols

                    out_grp = _odict_from_row(grp, grp_cols)

                    # 1.1.2.1. each possible custom models connected to this group
                    grp_custom_models_rows = prefetch_custom.get('group', {})
                    for grp_cmodel_name, grp_cmodel_rows in grp_custom_models_rows.items(
                    ):
                        cmodel_cols = columns_for_custom_models[
                            grp_cmodel_name]
                        if grp_cmodel_name not in ordered_columns_per_model:
                            ordered_columns_per_model[
                                grp_cmodel_name] = cmodel_cols

                        out_grp['__' + grp_cmodel_name] = [
                            _odict_from_row(cmodel_row, cmodel_cols)
                            for cmodel_row in grp_cmodel_rows[grp['id']]
                        ]

                    # 1.1.2.2. each player in this group
                    out_grp['__player'] = []
                    for player in prefetch_player[grp['id']]:
                        # because player.payoff is a property
                        player['payoff'] = player['_payoff']

                        player_cols = columns_for_models['player'] + [
                            'participant_id'
                        ]
                        if 'player' not in ordered_columns_per_model:
                            ordered_columns_per_model['player'] = player_cols

                        out_player = _odict_from_row(player, player_cols)

                        # 1.1.2.2.1. participant object connected to this player
                        participant_obj = qs_participant.get(
                            id=out_player['participant_id'])
                        out_player['__participant'] = _odict_from_row(
                            participant_obj,
                            columns_for_models['participant'],
                            is_obj=True)

                        # 1.1.2.2.2. each possible custom models connected to this player
                        player_custom_models_rows = prefetch_custom.get(
                            'player', {})
                        for player_cmodel_name, player_cmodel_rows in player_custom_models_rows.items(
                        ):
                            cmodel_cols = columns_for_custom_models[
                                player_cmodel_name]
                            if player_cmodel_name not in ordered_columns_per_model:
                                ordered_columns_per_model[
                                    player_cmodel_name] = cmodel_cols

                            out_player['__' + player_cmodel_name] = [
                                _odict_from_row(cmodel_row, cmodel_cols) for
                                cmodel_row in player_cmodel_rows[player['id']]
                            ]

                        out_grp['__player'].append(out_player)

                    out_subsess['__group'].append(out_grp)

                out_sess['__subsession'].append(out_subsess)

            output_nested.append(out_sess)

        # generate column names
        columns_flat = []
        for model_name, model_cols in ordered_columns_per_model.items():
            columns_flat.extend(
                ['.'.join((model_name, c)) for c in model_cols])

        if return_columns:
            return output_nested, columns_flat
        else:
            return output_nested
Beispiel #42
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
Beispiel #43
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:

    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.

        if for_mturk:
            mturk_num_participants = (num_participants /
                                      settings.MTURK_NUM_PARTICIPANTS_MULTIPLE)
        else:
            mturk_num_participants = -1

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

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

        Participant.objects.bulk_create([
            Participant(id_in_session=id_in_session, session=session)
            for id_in_session in list(range(1, num_participants + 1))
        ])

        participant_values = session.participant_set.order_by('id').values(
            'code', 'id')

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

        participant_to_player_lookups = []
        page_index = 0

        for app_name in session_config['app_sequence']:

            views_module = common_internal.get_views_module(app_name)
            models_module = get_models_module(app_name)
            Constants = models_module.Constants
            num_subsessions += Constants.num_rounds

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

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

            Subsession.objects.bulk_create([
                Subsession(round_number=round_number, session=session)
                for round_number in round_numbers
            ])

            subsessions = Subsession.objects.filter(
                session=session).order_by('round_number').values(
                    '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 subsession in subsessions:
                for id_in_subsession in range(1, num_groups_per_round + 1):
                    groups_to_create.append(
                        Group(
                            session=session,
                            subsession_id=subsession['id'],
                            round_number=subsession['round_number'],
                            id_in_subsession=id_in_subsession,
                        ))

            Group.objects.bulk_create(groups_to_create)

            groups = Group.objects.filter(session=session).values(
                'id_in_subsession', 'subsession_id',
                'id').order_by('id_in_subsession')

            groups_lookup = defaultdict(list)

            for group in groups:
                subsession_id = group['subsession_id']
                groups_lookup[subsession_id].append(group['id'])

            players_to_create = []

            for subsession in subsessions:
                subsession_id = subsession['id']
                round_number = subsession['round_number']
                participant_index = 0
                for group_id in groups_lookup[subsession_id]:
                    for id_in_group in range(1, ppg + 1):
                        participant = participant_values[participant_index]
                        players_to_create.append(
                            Player(session=session,
                                   subsession_id=subsession_id,
                                   round_number=round_number,
                                   participant_id=participant['id'],
                                   group_id=group_id,
                                   id_in_group=id_in_group))
                        participant_index += 1

            # Create players
            Player.objects.bulk_create(players_to_create)

            players_flat = Player.objects.filter(session=session).values(
                'id', 'participant__code', 'participant__id', 'subsession__id',
                'round_number')

            players_by_round = [[] for _ in range(Constants.num_rounds)]
            for p in players_flat:
                players_by_round[p['round_number'] - 1].append(p)

            for round_number, round_players in enumerate(players_by_round,
                                                         start=1):
                for View in views_module.page_sequence:
                    page_index += 1
                    for p in round_players:

                        participant_code = p['participant__code']

                        url = View.get_url(participant_code=participant_code,
                                           name_in_url=Constants.name_in_url,
                                           page_index=page_index)

                        participant_to_player_lookups.append(
                            ParticipantToPlayerLookup(
                                participant_id=p['participant__id'],
                                participant_code=participant_code,
                                page_index=page_index,
                                app_name=app_name,
                                player_pk=p['id'],
                                subsession_pk=p['subsession__id'],
                                session_pk=session.pk,
                                url=url))

        ParticipantToPlayerLookup.objects.bulk_create(
            participant_to_player_lookups)
        session.participant_set.update(_max_page_index=page_index)

        with otree.db.idmap.use_cache():
            # possible optimization: check if
            # Subsession.creating_session == BaseSubsession.creating_session
            # if so, skip it.
            # but this will only help people who didn't override creating_session
            # in that case, the session usually creates quickly, anyway.
            for subsession in session.get_subsessions():
                subsession.before_session_starts()
                subsession.creating_session()
            otree.db.idmap.save_objects()

        # 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.set_session(session)

    return session
Beispiel #44
0
def get_urlpatterns():

    from django.contrib.auth.views import login, logout

    urlpatterns = [
        urls.url(r'^$', RedirectView.as_view(url='/demo', permanent=True)),
        urls.url(
            r'^accounts/login/$',
            login,
            {'template_name': 'otree/login.html'},
            name='login_url',
        ),
        urls.url(
            r'^accounts/logout/$',
            logout,
            {'next_page': 'DemoIndex'},
            name='logout',
        ),
    ]

    urlpatterns += staticfiles_urlpatterns()

    used_names_in_url = set()
    for app_name in settings.INSTALLED_OTREE_APPS:
        models_module = common_internal.get_models_module(app_name)
        name_in_url = models_module.Constants.name_in_url
        if name_in_url in used_names_in_url:
            msg = (
                "App {} has Constants.name_in_url='{}', "
                "which is already used by another app"
            ).format(app_name, name_in_url)
            raise ValueError(msg)

        used_names_in_url.add(name_in_url)

        views_module = common_internal.get_pages_module(app_name)
        urlpatterns += url_patterns_from_game_module(
            views_module.__name__, name_in_url)


    urlpatterns += url_patterns_from_module('otree.views.participant')
    urlpatterns += url_patterns_from_module('otree.views.demo')
    urlpatterns += url_patterns_from_module('otree.views.admin')
    urlpatterns += url_patterns_from_module('otree.views.room')
    urlpatterns += url_patterns_from_module('otree.views.mturk')
    urlpatterns += url_patterns_from_module('otree.views.export')

    urlpatterns += extensions_urlpatterns()
    urlpatterns += extensions_export_urlpatterns()

    # serve an empty favicon?
    # otherwise, the logs will contain:
    # [WARNING] django.request > Not Found: /favicon.ico
    # Not Found: /favicon.ico
    # don't want to add a <link> in base template because even if it exists,
    # browsers will still request /favicon.ico.
    # plus it makes the HTML noisier
    # can't use the static() function here because maybe collectstatic
    # has not been run yet
    # and it seems an empty HttpResponse or even a 204 response makes the browser
    # just keep requesting the file with every page load
    # hmmm...now it seems that chrome is not re-requesting with every page load
    # but firefox does. but if i remove the favicon, there's 1 404 then FF doesn't
    # ask for it again.


    # import os
    # dir_path = os.path.dirname(os.path.realpath(__file__))
    # with open(os.path.join(dir_path, 'favicon_invisible.ico'), 'rb') as f:
    # #with open('favicon.ico', 'rb') as f:
    #     favicon_content = f.read()
    #
    #
    # urlpatterns.append(
    #     urls.url(
    #         r'^favicon\.ico$',
    #         lambda request: HttpResponse(favicon_content, content_type="image/x-icon")
    #     )
    # )

    return urlpatterns
Beispiel #45
0
def export_docs(fp, app_name):
    """Write the dcos of the given app name as csv into the file-like object

    """

    # generate doct_dict
    models_module = get_models_module(app_name)

    model_names = ["Participant", "Player", "Group", "Subsession", "Session"]
    line_break = '\r\n'

    def choices_readable(choices):
        lines = []
        for value, name in choices:
            # unicode() call is for lazy translation strings
            lines.append(u'{}: {}'.format(value, six.text_type(name)))
        return lines

    def generate_doc_dict():
        doc_dict = OrderedDict()

        data_types_readable = {
            'PositiveIntegerField': 'positive integer',
            'IntegerField': 'integer',
            'BooleanField': 'boolean',
            'CharField': 'text',
            'TextField': 'text',
            'FloatField': 'decimal',
            'DecimalField': 'decimal',
            'CurrencyField': 'currency'
        }

        for model_name in model_names:
            if model_name == 'Participant':
                Model = Participant
            elif model_name == 'Session':
                Model = Session
            else:
                Model = getattr(models_module, model_name)

            field_names = set(field.name for field in Model._meta.fields)

            members = get_field_names_for_csv(Model)
            doc_dict[model_name] = OrderedDict()

            for member_name in members:
                member = getattr(Model, member_name, None)
                doc_dict[model_name][member_name] = OrderedDict()
                if member_name == 'id':
                    doc_dict[model_name][member_name]['type'] = [
                        'positive integer'
                    ]
                    doc_dict[model_name][member_name]['doc'] = ['Unique ID']
                elif member_name in field_names:
                    member = Model._meta.get_field(member_name)

                    internal_type = member.get_internal_type()
                    data_type = data_types_readable.get(
                        internal_type, internal_type)

                    doc_dict[model_name][member_name]['type'] = [data_type]

                    # flag error if the model doesn't have a doc attribute,
                    # which it should unless the field is a 3rd party field
                    doc = getattr(member, 'doc', '[error]') or ''
                    doc_dict[model_name][member_name]['doc'] = [
                        line.strip() for line in doc.splitlines()
                        if line.strip()
                    ]

                    choices = getattr(member, 'choices', None)
                    if choices:
                        doc_dict[model_name][member_name]['choices'] = (
                            choices_readable(choices))
                elif isinstance(member, collections.Callable):
                    doc_dict[model_name][member_name]['doc'] = [
                        inspect.getdoc(member)
                    ]
        return doc_dict

    def docs_as_string(doc_dict):

        first_line = '{}: Documentation'.format(app_name)
        second_line = '*' * len(first_line)

        lines = [
            first_line, second_line, '',
            'Accessed: {}'.format(datetime.date.today().isoformat()), ''
        ]

        app_doc = getattr(models_module, 'doc', '')
        if app_doc:
            lines += [app_doc, '']

        for model_name in doc_dict:
            lines.append(model_name)

            for member in doc_dict[model_name]:
                lines.append('\t{}'.format(member))
                for info_type in doc_dict[model_name][member]:
                    lines.append('\t\t{}'.format(info_type))
                    for info_line in doc_dict[model_name][member][info_type]:
                        lines.append(u'{}{}'.format('\t' * 3, info_line))

        output = u'\n'.join(lines)
        return output.replace('\n', line_break).replace('\t', '    ')

    doc_dict = generate_doc_dict()
    doc = docs_as_string(doc_dict)
    fp.write(doc)
Beispiel #46
0
def get_rows_for_wide_csv():

    sessions = Session.objects.order_by('id')
    session_cache = {row.id: row for row in sessions}

    session_config_fields = set()
    for session in sessions:
        for field_name in SessionConfig(session.config).editable_fields():
            session_config_fields.add(field_name)
    session_config_fields = list(session_config_fields)

    participants = Participant.objects.order_by('id').values()
    if not participants:
        # 1 empty row
        return [[]]

    session_fields = get_field_names_for_csv(Session)
    participant_fields = get_field_names_for_csv(Participant)
    participant_fields.append('payoff_plus_participation_fee')
    header_row = ['participant.{}'.format(fname) for fname in participant_fields]
    header_row += ['session.{}'.format(fname)
                   for fname in session_fields]
    header_row += ['session.config.{}'.format(fname)
                   for fname in session_config_fields]
    rows = [header_row]
    for participant in participants:
        session = session_cache[participant['session_id']]
        participant['payoff_plus_participation_fee'] = get_payoff_plus_participation_fee(session, participant)
        row = [sanitize_for_csv(participant[fname]) for fname in participant_fields]

        row += [sanitize_for_csv(getattr(session, fname)) for fname in session_fields]
        row += [sanitize_for_csv(session.config.get(fname)) for fname in session_config_fields]
        rows.append(row)

    # heuristic to get the most relevant order of apps
    app_sequences = collections.Counter()
    for session in sessions:
        # we loaded the config earlier
        app_sequence = session.config['app_sequence']
        app_sequences[tuple(app_sequence)] += session.num_participants
    most_common_app_sequence = app_sequences.most_common(1)[0][0]

    apps_not_in_popular_sequence = [
        app for app in settings.INSTALLED_OTREE_APPS
        if app not in most_common_app_sequence]

    order_of_apps = list(most_common_app_sequence) + apps_not_in_popular_sequence

    rounds_per_app = OrderedDict()
    for app_name in order_of_apps:
        models_module = get_models_module(app_name)
        agg_dict = models_module.Subsession.objects.all().aggregate(Max('round_number'))
        highest_round_number = agg_dict['round_number__max']

        if highest_round_number is not None:
            rounds_per_app[app_name] = highest_round_number
    for app_name in rounds_per_app:
        for round_number in range(1, rounds_per_app[app_name] + 1):
            new_rows = get_rows_for_wide_csv_round(app_name, round_number, sessions)
            for i in range(len(rows)):
                rows[i].extend(new_rows[i])
    return rows