<commit_before># -*- coding: utf-8 -*-
from __future__ import division, unicode_literals

import collections
import functools
import itertools
import urllib
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.db.models import F
from django.shortcuts import get_object_or_404, redirect
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView
from . import forms
from . import models
from .views import LoginRequiredMixin


class AdminIndexView(LoginRequiredMixin, TemplateView):
    template_name = 'admin/peerinst/index.html'

    def get_context_data(self, **kwargs):
        context = TemplateView.get_context_data(self, **kwargs)
        context.update(assignments=models.Assignment.objects.all())
        return context


def get_question_aggregates(assignment, question):
    """Get aggregate statistics for the given assignment and question.

    This function returns a pair (sums, students), where 'sums' is a collections.Counter object
    mapping labels to integers, and 'students' is the set of all user tokens of the submitted
    answers.
    """
    # Get indices of the correct answer choices (usually only one)
    answerchoice_correct = question.answerchoice_set.values_list('correct', flat=True)
    correct_choices = list(itertools.compress(itertools.count(1), answerchoice_correct))
    # Select answers entered by students, not example answers
    answers = question.answer_set.filter(assignment=assignment).exclude(user_token='')
    sums = collections.Counter(
        total_answers=answers.count(),
        correct_first_answers=answers.filter(first_answer_choice__in=correct_choices).count(),
        correct_second_answers=answers.filter(second_answer_choice__in=correct_choices).count(),
        switches=answers.exclude(second_answer_choice=F('first_answer_choice')).count(),
    )
    # Get a set of all user tokens.  DISTINCT queries are not implemented for MySQL, so this is the
    # only way I can think of to determine the number of students who answered at least one
    # question in an assignment.
    students = set(answers.values_list('user_token', flat=True))
    return sums, students


def get_assignment_aggregates(assignment):
    """Get aggregate statistics for the given assignment.

    This function returns a pair (sums, question_data), where sums is a collections.Counter object
    mapping labels to integers, and question_data is a list of pairs (question, sums) with the sums
    for the respective question.
    """
    sums = collections.Counter()
    students = set()
    question_data = []
    for question in assignment.questions.all():
        q_sums, q_students = get_question_aggregates(assignment, question)
        sums += q_sums
        students |= q_students
        q_sums.update(total_students=len(q_students))
        question_data.append((question, q_sums))
    sums.update(total_students=len(students))
    return sums, question_data


class AssignmentResultsView(LoginRequiredMixin, TemplateView):
    template_name = "admin/peerinst/assignment_results.html"

    def prepare_stats(self, sums):
        total_answers = sums['total_answers']
        if total_answers:
            def percent(enum):
                return mark_safe('{:.1f}&nbsp;%'.format(100 * enum / total_answers))
        else:
            def percent(enum):
                return ''
        return (
            total_answers,
            sums['total_students'],
            sums['correct_first_answers'],
            percent(sums['correct_first_answers']),
            sums['correct_second_answers'],
            percent(sums['correct_second_answers']),
            sums['switches'],
            percent(sums['switches']),
        )

    def prepare_assignment_data(self, sums):
        return zip((
            _('Total number of answers recorded:'),
            _('Total number of participating students:'),
            _('Correct answer choices – first attempt:'),
            _('↳ Percentage of total answers:'),
            _('Correct answer choices – second attempt:'),
            _('↳ Percentage of total answers:'),
            _('Number of answer choice switches:'),
            _('↳ Percentage of total answers:'),
        ), self.prepare_stats(sums))

    def prepare_question_data(self, question_data):
        rows = []
        for i, (question, sums) in enumerate(question_data, 1):
            get_params = urllib.urlencode(
                dict(assignment=self.assignment_id, question=question.id)
            )
            rows.append(dict(
                data=(i, question.title) + self.prepare_stats(sums),
                link='?'.join([reverse('admin:peerinst_answer_changelist'), get_params]),
            ))
        return dict(
            labels=(
                _('No.'), _('Question ID'), _('Total answers'), _('Total students'),
                _('First correct'), _('Percent'), _('Second correct'), _('Percent'),
                _('Switches'), _('Percent'), ''
            ),
            rows=rows,
        )
        
    def get_context_data(self, **kwargs):
        context = TemplateView.get_context_data(self, **kwargs)
        self.assignment_id = self.kwargs['assignment_id']
        assignment = get_object_or_404(models.Assignment, identifier=self.assignment_id)
        sums, question_data = get_assignment_aggregates(assignment)
        context.update(
            assignment=assignment,
            assignment_data=self.prepare_assignment_data(sums),
            question_data=self.prepare_question_data(question_data),
        )
        return context

class QuestionPreviewView(LoginRequiredMixin, FormView):
    template_name = 'admin/peerinst/question_preview.html'<commit_msg>Require staff login to see the admin pages.<commit_after># -*- coding: utf-8 -*-
from __future__ import division, unicode_literals

import collections
import functools
import itertools
import urllib
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib import messages
from django.core.urlresolvers import reverse
from django.db.models import F
from django.shortcuts import get_object_or_404, redirect
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView
from . import forms
from . import models


class StaffMemberRequiredMixin(object):
    @classmethod
    def as_view(cls, **initkwargs):
        view = super(StaffMemberRequiredMixin, cls).as_view(**initkwargs)
        return staff_member_required(view)


class AdminIndexView(StaffMemberRequiredMixin, TemplateView):
    template_name = 'admin/peerinst/index.html'

    def get_context_data(self, **kwargs):
        context = TemplateView.get_context_data(self, **kwargs)
        context.update(assignments=models.Assignment.objects.all())
        return context


def get_question_aggregates(assignment, question):
    """Get aggregate statistics for the given assignment and question.

    This function returns a pair (sums, students), where 'sums' is a collections.Counter object
    mapping labels to integers, and 'students' is the set of all user tokens of the submitted
    answers.
    """
    # Get indices of the correct answer choices (usually only one)
    answerchoice_correct = question.answerchoice_set.values_list('correct', flat=True)
    correct_choices = list(itertools.compress(itertools.count(1), answerchoice_correct))
    # Select answers entered by students, not example answers
    answers = question.answer_set.filter(assignment=assignment).exclude(user_token='')
    sums = collections.Counter(
        total_answers=answers.count(),
        correct_first_answers=answers.filter(first_answer_choice__in=correct_choices).count(),
        correct_second_answers=answers.filter(second_answer_choice__in=correct_choices).count(),
        switches=answers.exclude(second_answer_choice=F('first_answer_choice')).count(),
    )
    # Get a set of all user tokens.  DISTINCT queries are not implemented for MySQL, so this is the
    # only way I can think of to determine the number of students who answered at least one
    # question in an assignment.
    students = set(answers.values_list('user_token', flat=True))
    return sums, students


def get_assignment_aggregates(assignment):
    """Get aggregate statistics for the given assignment.

    This function returns a pair (sums, question_data), where sums is a collections.Counter object
    mapping labels to integers, and question_data is a list of pairs (question, sums) with the sums
    for the respective question.
    """
    sums = collections.Counter()
    students = set()
    question_data = []
    for question in assignment.questions.all():
        q_sums, q_students = get_question_aggregates(assignment, question)
        sums += q_sums
        students |= q_students
        q_sums.update(total_students=len(q_students))
        question_data.append((question, q_sums))
    sums.update(total_students=len(students))
    return sums, question_data


class AssignmentResultsView(StaffMemberRequiredMixin, TemplateView):
    template_name = "admin/peerinst/assignment_results.html"

    def prepare_stats(self, sums):
        total_answers = sums['total_answers']
        if total_answers:
            def percent(enum):
                return mark_safe('{:.1f}&nbsp;%'.format(100 * enum / total_answers))
        else:
            def percent(enum):
                return ''
        return (
            total_answers,
            sums['total_students'],
            sums['correct_first_answers'],
            percent(sums['correct_first_answers']),
            sums['correct_second_answers'],
            percent(sums['correct_second_answers']),
            sums['switches'],
            percent(sums['switches']),
        )

    def prepare_assignment_data(self, sums):
        return zip((
            _('Total number of answers recorded:'),
            _('Total number of participating students:'),
            _('Correct answer choices – first attempt:'),
            _('↳ Percentage of total answers:'),
            _('Correct answer choices – second attempt:'),
            _('↳ Percentage of total answers:'),
            _('Number of answer choice switches:'),
            _('↳ Percentage of total answers:'),
        ), self.prepare_stats(sums))

    def prepare_question_data(self, question_data):
        rows = []
        for i, (question, sums) in enumerate(question_data, 1):
            get_params = urllib.urlencode(
                dict(assignment=self.assignment_id, question=question.id)
            )
            rows.append(dict(
                data=(i, question.title) + self.prepare_stats(sums),
                link='?'.join([reverse('admin:peerinst_answer_changelist'), get_params]),
            ))
        return dict(
            labels=(
                _('No.'), _('Question ID'), _('Total answers'), _('Total students'),
                _('First correct'), _('Percent'), _('Second correct'), _('Percent'),
                _('Switches'), _('Percent'), ''
            ),
            rows=rows,
        )
        
    def get_context_data(self, **kwargs):
        context = TemplateView.get_context_data(self, **kwargs)
        self.assignment_id = self.kwargs['assignment_id']
        assignment = get_object_or_404(models.Assignment, identifier=self.assignment_id)
        sums, question_data = get_assignment_aggregates(assignment)
        context.update(
            assignment=assignment,
            assignment_data=self.prepare_assignment_data(sums),
            question_data=self.prepare_question_data(question_data),
        )
        return context

class QuestionPreviewView(StaffMemberRequiredMixin, FormView):
    template_name = 'admin/peerinst/question_preview.html'
{'fraction_non_alphanumeric': 0.09726621103517252, 'fraction_numerical': 0.0021670278379729954, 'mean_word_length': 3.167766585620007, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4703783', 'n_tokens_mistral': 3365, 'n_tokens_neox': 3241, 'n_words': 823}