コード例 #1
0
 def get_benchmark_grade(self):
     """ get the benchmark grade for this particular student, 
     course_section and marking_period """
     grade, aggregate_id = gradebook_get_average_and_pk(
         student=self.student,
         course_section=self.course_section,
         marking_period=self.marking_period)
     return grade
コード例 #2
0
ファイル: tests.py プロジェクト: AnneNamuli/schooldriver
 def get_benchmark_grade(self):
     """ get the benchmark grade for this particular student, 
     course_section and marking_period """
     grade, aggregate_id = gradebook_get_average_and_pk(
         student = self.student, 
         course_section = self.course_section, 
         marking_period = self.marking_period
         )
     return grade
コード例 #3
0
ファイル: views.py プロジェクト: Sateanu/django-sis
def gradebook(request, course_id, for_export=False):
    course = get_object_or_404(Course, pk=course_id)
    # lots of stuff will fail unceremoniously if there are no MPs assigned
    if not course.marking_period.count():
        messages.add_message(request, messages.ERROR,
            'The gradebook cannot be opened because there are no marking periods assigned to the course ' +
            course.fullname + '.')
        return HttpResponseRedirect(reverse('admin:index'))

    school_year = course.marking_period.all()[0].school_year
    try:
        calculation_rule = benchmark_find_calculation_rule(school_year)
    except Exception as e:
        if "There is no suitable calculation rule for the school year" not in unicode(e):
            raise
        messages.add_message(request, messages.ERROR, e)
        return HttpResponseRedirect(reverse('admin:index'))
    teacher_courses = get_teacher_courses(request.user.username)
    extra_info = Configuration.get_or_default('Gradebook extra information').value.lower().strip()
    quantizer = Decimal(10) ** (-1 * calculation_rule.decimal_places)
    if not request.user.is_superuser and not request.user.groups.filter(name='registrar').count() and \
    (teacher_courses is None or course not in teacher_courses):
        messages.add_message(request, messages.ERROR,
            'You do not have access to the gradebook for ' + course.fullname + '.')
        return HttpResponseRedirect(reverse('admin:index'))

    students = Student.objects.filter(is_active=True,course=course)
    #students = Student.objects.filter(course=course)
    items = Item.objects.filter(course=course)
    filtered = False
    temporary_aggregate = False
    totals = {
        'filtered_average': Decimal(0),
        'filtered_average_count': Decimal(0),
        'course_average': Decimal(0),
        'course_average_count': Decimal(0),
        'filtered_standards_passing': 0,
        'filtered_standards_all': 0,
        'standards_passing': 0,
        'standards_all': 0
    }

    if request.GET:
        filter_form = GradebookFilterForm(request.GET)
        filter_form.update_querysets(course)
        if filter_form.is_valid():
            for filter_key, filter_value in filter_form.cleaned_data.iteritems():
                if filter_value is not None:
                    try:
                        if not len(filter_value):
                            continue
                    except TypeError:
                        # not everything has a len
                        pass
                    if filter_key == 'cohort': 
                        students = students.filter(cohorts=filter_value)
                        temporary_aggregate = True
                    if filter_key == 'marking_period':
                        items = items.filter(marking_period=filter_value)
                    if filter_key == 'benchmark':
                        items = items.filter(benchmark__in=filter_value)
                        temporary_aggregate = True
                    if filter_key == 'category':
                        items = items.filter(category=filter_value)
                    if filter_key == 'assignment_type':
                        items = items.filter(assignment_type=filter_value)
                        temporary_aggregate = True
                    if filter_key == 'name':
                        items = items.filter(name__icontains=filter_value)
                        temporary_aggregate = True
                    if filter_key == 'date_begin':
                        items = items.filter(date__gt=filter_value)
                        temporary_aggregate = True
                    if filter_key == 'date_end':
                        items = items.filter(date__lt=filter_value)
                        temporary_aggregate = True
                    filtered = True
    else:
        # show only the active marking period by default
        active_mps = course.marking_period.filter(active=True)
        if active_mps and not for_export: # no default filtering on export requests
            filter_form = GradebookFilterForm(initial={'marking_period': active_mps[0]})
            items = items.filter(marking_period=active_mps[0])
            filtered = True
        else:
            filter_form = GradebookFilterForm()
        filter_form.update_querysets(course)
        
    # make a note of any aggregates pending recalculation
    pending_aggregate_pks = Aggregate.objects.filter(course=course, aggregatetask__in=AggregateTask.objects.all()).values_list('pk', flat=True).distinct()
    
    # Freeze these now in case someone else gets in here!
    # TODO: something that actually works. all() does not evaluate a QuerySet.
    # https://docs.djangoproject.com/en/dev/ref/models/querysets/#when-querysets-are-evaluated
    items = items.order_by('id').all()
    # whoa, super roll of the dice. is Item.demonstration_set really guaranteed to be ordered by id?
    # precarious; sorting must match items (and demonstrations!) exactly
    marks = Mark.objects.filter(item__in=items).order_by('item__id', 'demonstration__id').all() 
    items_count = items.filter(demonstration=None).count() + Demonstration.objects.filter(item__in=items).count()
    for student in students:
        student_marks = marks.filter(student=student).select_related('item__category_id')
        student_marks_count = student_marks.count()
        if student_marks_count < items_count:
            # maybe student enrolled after assignments were created
            for item in items:
                if len(item.demonstration_set.all()):
                    # must create mark for each demonstration
                    for demonstration in item.demonstration_set.all():
                        mark, created = Mark.objects.get_or_create(item=item, demonstration=demonstration, student=student)
                else:
                    # a regular item without demonstrations; make only one mark
                    mark, created = Mark.objects.get_or_create(item=item, student=student)
        if student_marks_count > items_count:
            # Yikes, there are multiple marks per student per item. Stop loading the gradebook now.
            if 'dangerous' in request.GET:
                pass
            else:
                raise Exception('Multiple marks per student per item.')
        
        for mark in student_marks:
            mark.category_id = mark.item.category_id
        
        student.marks = student_marks
        student.average, student.average_pk = gradebook_get_average_and_pk(student, course, None, None, None)
        if student.average is not None:
            totals['course_average'] += Aggregate.objects.get(pk=student.average_pk).cached_value # can't use a substitution
            totals['course_average_count'] += 1
        if filtered:
            cleaned_or_initial = getattr(filter_form, 'cleaned_data', filter_form.initial)
            filter_category = cleaned_or_initial.get('category', None)
            filter_marking_period = cleaned_or_initial.get('marking_period', None)
            filter_items = items if temporary_aggregate else None
            student.filtered_average, student.filtered_average_pk = gradebook_get_average_and_pk(
                student, course, filter_category, filter_marking_period, filter_items)
            if student.filtered_average is not None:
                totals['filtered_average'] += Aggregate.objects.get(pk=student.filtered_average_pk).cached_value # can't use a substitution
                totals['filtered_average_count'] += 1
        if school_year.benchmark_grade and extra_info == 'demonstrations':
            # TC's column of counts
            # TODO: don't hardcode
            standards_category = Category.objects.get(name='Standards')
            PASSING_GRADE = 3
            standards_objects = Item.objects.filter(course=course, category=standards_category, mark__student=student).annotate(best_mark=Max('mark__mark')).exclude(best_mark=None)
            standards_count_passing = standards_objects.filter(best_mark__gte=PASSING_GRADE).count()
            standards_count_total = standards_objects.count()
            totals['standards_passing'] += standards_count_passing
            totals['standards_all'] += standards_count_total
            if standards_count_total:
                student.standards_counts = '{} / {} ({:.0f}%)'.format(standards_count_passing, standards_count_total, 100.0 * standards_count_passing / standards_count_total)
            else:
                student.standards_counts = None
            if filtered:
                standards_objects = items.filter(course=course, category=standards_category, mark__student=student).annotate(best_mark=Max('mark__mark')).exclude(best_mark=None)
                standards_count_passing = standards_objects.filter(best_mark__gte=PASSING_GRADE).count()
                standards_count_total = standards_objects.count()
                totals['filtered_standards_passing'] += standards_count_passing
                totals['filtered_standards_all'] += standards_count_total
                if standards_count_total:
                    student.filtered_standards_counts = '{} / {} ({:.0f}%)'.format(standards_count_passing, standards_count_total, 100.0 * standards_count_passing / standards_count_total)
                else:
                    student.filtered_standards_counts = None

            # TC's row of counts
            # TODO: don't hardcode
            for item in items:
                if item.category != standards_category:
                    item.marks_counts = 'N/A'
                    continue
                marks_count_passing = item.mark_set.filter(mark__gte=PASSING_GRADE).count()
                marks_count_total = item.mark_set.exclude(mark=None).count()
                if marks_count_total:
                    item.marks_counts = '{} / {} ({:.0f}%)'.format(marks_count_passing, marks_count_total, 100.0 * marks_count_passing / marks_count_total)
                else:
                    item.marks_counts = None

    if extra_info == 'averages':
        for item in items:
            # listify the QuerySet now so we can modify it and use it in the template
            # if the template just reads the DB and instantiates new objects, they will not have our class_average attribute
            item.demonstration_list = list(item.demonstration_set.all())
            for demonstration in item.demonstration_list:
                # TODO: make sure we only count enrolled students
                demonstration.class_average = demonstration.mark_set.aggregate(Avg('mark'))['mark__avg']
                try:
                    demonstration.class_average = Decimal(demonstration.class_average).quantize(quantizer)
                except TypeError: # e.g. Decimal(None)
                    pass
            item.class_average = item.mark_set.aggregate(Avg('mark'))['mark__avg']
            try:
                item.class_average = Decimal(item.class_average).quantize(quantizer)
            except TypeError: # e.g. Decimal(None)
                pass

    # Gather visual flagging criteria
    absolute_category_flag_criteria = {}
    normalized_category_flag_criteria = {}
    for category in Category.objects.filter(item__in=items).distinct():
        if category.fixed_points_possible:
            # assume the criterion is absolute if the category has fixed # of points possible
            use_dict = absolute_category_flag_criteria
        else:
            # assume we need to divide the mark by points possible before comparing to criterion
            use_dict = normalized_category_flag_criteria
        use_dict[category.pk] = []
        substitutions = calculation_rule.substitution_set.filter(apply_to_departments=course.department, apply_to_categories=category, flag_visually=True)
        for substitution in substitutions:
            use_dict[category.pk].append(substitution.operator + ' ' + str(substitution.match_value))

    # calculate course-wide averages and counts
    if totals['course_average_count']:
        totals['course_average'] = Decimal(totals['course_average'] / totals['course_average_count']).quantize(quantizer)
    else:
        totals['course_average'] = None
    if totals['filtered_average_count']:
        totals['filtered_average'] = Decimal(totals['filtered_average'] / totals['filtered_average_count']).quantize(quantizer)
    else:
        totals['filtered_average'] = None
    if totals['standards_all']:
        totals['standards_text'] = '{} / {} ({:.0f}%)'.format(totals['standards_passing'], totals['standards_all'],
            100.0 * totals['standards_passing'] / totals['standards_all'])
    else:
        totals['standards_text'] = None
    if totals['filtered_standards_all']:
        totals['filtered_standards_text'] = '{} / {} ({:.0f}%)'.format(totals['filtered_standards_passing'], totals['filtered_standards_all'],
            100.0 * totals['filtered_standards_passing'] / totals['filtered_standards_all'])
    else:
        totals['filtered_standards_text'] = None

    data_dictionary = {
        'items': items,
        'item_pks': ','.join(map(str,items.values_list('pk', flat=True))),
        'pending_aggregate_pks': json.dumps(map(str, pending_aggregate_pks)),
        'students': students,
        'course': course,
        'teacher_courses': teacher_courses,
        'filtered' : filtered,
        'filter_form': filter_form,
        'absolute_category_flag_criteria': absolute_category_flag_criteria,
        'normalized_category_flag_criteria': normalized_category_flag_criteria,
        'extra_info': extra_info,
        'totals': totals,
        'item_form_exclude': ItemForm().get_user_excludes(),
    }
    if for_export:
        return data_dictionary
    else:
        return render_to_response('benchmark_grade/gradebook.html', data_dictionary,
            RequestContext(request, {}),)
コード例 #4
0
def gradebook(request, course_id):
    course = get_object_or_404(Course, pk=course_id)
    school_year = course.marking_period.all()[0].school_year
    teacher_courses = get_teacher_courses(request.user.username)
    if not request.user.is_superuser and not request.user.groups.filter(name='registrar').count() and \
    (teacher_courses is None or course not in teacher_courses):
        messages.add_message(
            request, messages.ERROR,
            'You do not have access to the gradebook for ' + course.fullname +
            '.')
        return HttpResponseRedirect(reverse('admin:index'))

    # lots of stuff will fail unceremoniously if there are no MPs assigned
    if not course.marking_period.count():
        messages.add_message(
            request, messages.ERROR,
            'The gradebook cannot be opened because there are no marking periods assigned to the course '
            + course.fullname + '.')
        return HttpResponseRedirect(reverse('admin:index'))

    students = Student.objects.filter(inactive=False, course=course)
    #students = Student.objects.filter(course=course)
    items = Item.objects.filter(course=course)
    filtered = False

    if request.GET:
        filter_form = GradebookFilterForm(request.GET)
        filter_form.update_querysets(course)
        if filter_form.is_valid():
            for filter_key, filter_value in filter_form.cleaned_data.iteritems(
            ):
                if filter_value is not None:
                    try:
                        if not len(filter_value):
                            continue
                    except TypeError:
                        # not everything has a len
                        pass
                    if filter_key == 'cohort':
                        students = students.filter(cohorts=filter_value)
                    if filter_key == 'marking_period':
                        items = items.filter(marking_period=filter_value)
                    if filter_key == 'benchmark':
                        items = items.filter(benchmark__in=filter_value)
                    if filter_key == 'category':
                        items = items.filter(category=filter_value)
                    if filter_key == 'assignment_type':
                        items = items.filter(assignment_type=filter_value)
                    if filter_key == 'name':
                        items = items.filter(name__icontains=filter_value)
                    if filter_key == 'date_begin':
                        items = items.filter(date__gt=filter_value)
                    if filter_key == 'date_end':
                        items = items.filter(date__lt=filter_value)
                    filtered = True
    else:
        # show only the active marking period by default
        active_mps = course.marking_period.filter(active=True)
        if active_mps:
            filter_form = GradebookFilterForm(
                initial={'marking_period': active_mps[0]})
            items = items.filter(marking_period=active_mps[0])
        else:
            filter_form = GradebookFilterForm()
        filter_form.update_querysets(course)

    # make a note of any aggregates pending recalculation
    pending_aggregate_pks = Aggregate.objects.filter(course=course).annotate(
        Count('aggregatetask')).filter(aggregatetask__count__gt=0).values_list(
            'pk', flat=True)

    # Freeze these now in case someone else gets in here!
    # TODO: something that actually works. all() does not evaluate a QuerySet.
    # https://docs.djangoproject.com/en/dev/ref/models/querysets/#when-querysets-are-evaluated
    items = items.order_by('id').all()
    # whoa, super roll of the dice. is Item.demonstration_set really guaranteed to be ordered by id?
    # precarious; sorting must match items (and demonstrations!) exactly
    marks = Mark.objects.filter(item__in=items).order_by(
        'item__id', 'demonstration__id').all()
    items_count = items.filter(
        demonstration=None).count() + Demonstration.objects.filter(
            item__in=items).count()
    for student in students:
        student_marks = marks.filter(
            student=student).select_related('item__category_id')
        student_marks_count = student_marks.count()
        if student_marks_count < items_count:
            # maybe student enrolled after assignments were created
            for item in items:
                if len(item.demonstration_set.all()):
                    # must create mark for each demonstration
                    for demonstration in item.demonstration_set.all():
                        mark, created = Mark.objects.get_or_create(
                            item=item,
                            demonstration=demonstration,
                            student=student)
                else:
                    # a regular item without demonstrations; make only one mark
                    mark, created = Mark.objects.get_or_create(item=item,
                                                               student=student)
        if student_marks_count > items_count:
            # Yikes, there are multiple marks per student per item. Stop loading the gradebook now.
            if 'dangerous' in request.GET:
                pass
            else:
                raise Exception('Multiple marks per student per item.')

        for mark in student_marks:
            mark.category_id = mark.item.category_id

        student.marks = student_marks
        student.average, student.average_pk = gradebook_get_average_and_pk(
            student, course, None, None, None)
        if filtered:
            student.filtered_average = gradebook_get_average(
                student, course, filter_form.cleaned_data['category'],
                filter_form.cleaned_data['marking_period'], items)
        if school_year.benchmark_grade:
            # TC's column of counts
            # TODO: don't hardcode
            standards_category = Category.objects.get(name='Standards')
            PASSING_GRADE = 3
            standards_objects = Item.objects.filter(
                course=course,
                category=standards_category,
                mark__student=student).annotate(
                    best_mark=Max('mark__mark')).exclude(best_mark=None)
            standards_count_passing = standards_objects.filter(
                best_mark__gte=PASSING_GRADE).count()
            standards_count_total = standards_objects.count()
            if standards_count_total:
                student.standards_counts = '{} / {} ({:.0f}%)'.format(
                    standards_count_passing, standards_count_total,
                    100.0 * standards_count_passing / standards_count_total)
            else:
                student.standards_counts_ = None
            if filtered:
                standards_objects = items.filter(
                    course=course,
                    category=standards_category,
                    mark__student=student).annotate(
                        best_mark=Max('mark__mark')).exclude(best_mark=None)
                standards_count_passing = standards_objects.filter(
                    best_mark__gte=PASSING_GRADE).count()
                standards_count_total = standards_objects.count()
                if standards_count_total:
                    student.filtered_standards_counts = '{} / {} ({:.0f}%)'.format(
                        standards_count_passing, standards_count_total, 100.0 *
                        standards_count_passing / standards_count_total)
                else:
                    student.filtered_standards_counts = None

            # TC's row of counts
            # TODO: don't hardcode
            for item in items:
                if item.category != standards_category:
                    item.marks_counts = 'N/A'
                    continue
                marks_count_passing = item.mark_set.filter(
                    mark__gte=PASSING_GRADE).count()
                marks_count_total = item.mark_set.exclude(mark=None).count()
                if marks_count_total:
                    item.marks_counts = '{} / {} ({:.0f}%)'.format(
                        marks_count_passing, marks_count_total,
                        100.0 * marks_count_passing / marks_count_total)
                else:
                    item.marks_counts = None

    # Gather visual flagging criteria
    calculation_rule = benchmark_find_calculation_rule(school_year)
    category_flag_criteria = {}
    for category in Category.objects.filter(item__in=items).distinct():
        category_flag_criteria[category.pk] = []
        substitutions = calculation_rule.substitution_set.filter(
            apply_to_departments=course.department,
            apply_to_categories=category,
            flag_visually=True)
        for substitution in substitutions:
            category_flag_criteria[category.pk].append(
                substitution.operator + ' ' + str(substitution.match_value))

    return render_to_response(
        'benchmark_grade/gradebook.html',
        {
            'items': items,
            'item_pks': ','.join(map(str, items.values_list('pk', flat=True))),
            'pending_aggregate_pks': json.dumps(map(str,
                                                    pending_aggregate_pks)),
            'students': students,
            'course': course,
            'teacher_courses': teacher_courses,
            'filtered': filtered,
            'filter_form': filter_form,
            'category_flag_criteria': category_flag_criteria,
        },
        RequestContext(request, {}),
    )
コード例 #5
0
ファイル: views.py プロジェクト: akarambir/django-sis
def gradebook(request, course_id):
    course = get_object_or_404(Course, pk=course_id)
    school_year = course.marking_period.all()[0].school_year
    teacher_courses = get_teacher_courses(request.user.username)
    if not request.user.is_superuser and not request.user.groups.filter(name='registrar').count() and \
    (teacher_courses is None or course not in teacher_courses):
        messages.add_message(request, messages.ERROR,
            'You do not have access to the gradebook for ' + course.fullname + '.')
        return HttpResponseRedirect(reverse('admin:index'))

    # lots of stuff will fail unceremoniously if there are no MPs assigned
    if not course.marking_period.count():
        messages.add_message(request, messages.ERROR,
            'The gradebook cannot be opened because there are no marking periods assigned to the course ' +
            course.fullname + '.')
        return HttpResponseRedirect(reverse('admin:index'))

    students = Student.objects.filter(inactive=False,course=course)
    #students = Student.objects.filter(course=course)
    items = Item.objects.filter(course=course)
    filtered = False
    temporary_aggregate = False

    if request.GET:
        filter_form = GradebookFilterForm(request.GET)
        filter_form.update_querysets(course)
        if filter_form.is_valid():
            for filter_key, filter_value in filter_form.cleaned_data.iteritems():
                if filter_value is not None:
                    try:
                        if not len(filter_value):
                            continue
                    except TypeError:
                        # not everything has a len
                        pass
                    if filter_key == 'cohort': 
                        students = students.filter(cohorts=filter_value)
                        temporary_aggregate = True
                    if filter_key == 'marking_period':
                        items = items.filter(marking_period=filter_value)
                    if filter_key == 'benchmark':
                        items = items.filter(benchmark__in=filter_value)
                        temporary_aggregate = True
                    if filter_key == 'category':
                        items = items.filter(category=filter_value)
                    if filter_key == 'assignment_type':
                        items = items.filter(assignment_type=filter_value)
                        temporary_aggregate = True
                    if filter_key == 'name':
                        items = items.filter(name__icontains=filter_value)
                        temporary_aggregate = True
                    if filter_key == 'date_begin':
                        items = items.filter(date__gt=filter_value)
                        temporary_aggregate = True
                    if filter_key == 'date_end':
                        items = items.filter(date__lt=filter_value)
                        temporary_aggregate = True
                    filtered = True
    else:
        # show only the active marking period by default
        active_mps = course.marking_period.filter(active=True)
        if active_mps:
            filter_form = GradebookFilterForm(initial={'marking_period': active_mps[0]})
            items = items.filter(marking_period=active_mps[0])
            filtered = True
        else:
            filter_form = GradebookFilterForm()
        filter_form.update_querysets(course)
        
    # make a note of any aggregates pending recalculation
    pending_aggregate_pks = Aggregate.objects.filter(course=course, aggregatetask__in=AggregateTask.objects.all()).values_list('pk', flat=True).distinct()
    
    # Freeze these now in case someone else gets in here!
    # TODO: something that actually works. all() does not evaluate a QuerySet.
    # https://docs.djangoproject.com/en/dev/ref/models/querysets/#when-querysets-are-evaluated
    items = items.order_by('id').all()
    # whoa, super roll of the dice. is Item.demonstration_set really guaranteed to be ordered by id?
    # precarious; sorting must match items (and demonstrations!) exactly
    marks = Mark.objects.filter(item__in=items).order_by('item__id', 'demonstration__id').all() 
    items_count = items.filter(demonstration=None).count() + Demonstration.objects.filter(item__in=items).count()
    for student in students:
        student_marks = marks.filter(student=student).select_related('item__category_id')
        student_marks_count = student_marks.count()
        if student_marks_count < items_count:
            # maybe student enrolled after assignments were created
            for item in items:
                if len(item.demonstration_set.all()):
                    # must create mark for each demonstration
                    for demonstration in item.demonstration_set.all():
                        mark, created = Mark.objects.get_or_create(item=item, demonstration=demonstration, student=student)
                else:
                    # a regular item without demonstrations; make only one mark
                    mark, created = Mark.objects.get_or_create(item=item, student=student)
        if student_marks_count > items_count:
            # Yikes, there are multiple marks per student per item. Stop loading the gradebook now.
            if 'dangerous' in request.GET:
                pass
            else:
                raise Exception('Multiple marks per student per item.')
        
        for mark in student_marks:
            mark.category_id = mark.item.category_id
        
        student.marks = student_marks
        student.average, student.average_pk = gradebook_get_average_and_pk(student, course, None, None, None)
        if filtered:
            cleaned_or_initial = getattr(filter_form, 'cleaned_data', filter_form.initial)
            filter_category = cleaned_or_initial.get('category', None)
            filter_marking_period = cleaned_or_initial.get('marking_period', None)
            filter_items = items if temporary_aggregate else None
            student.filtered_average = gradebook_get_average(student, course, filter_category, filter_marking_period, filter_items)
        if school_year.benchmark_grade:
            # TC's column of counts
            # TODO: don't hardcode
            standards_category = Category.objects.get(name='Standards')
            PASSING_GRADE = 3
            standards_objects = Item.objects.filter(course=course, category=standards_category, mark__student=student).annotate(best_mark=Max('mark__mark')).exclude(best_mark=None)
            standards_count_passing = standards_objects.filter(best_mark__gte=PASSING_GRADE).count()
            standards_count_total = standards_objects.count()
            if standards_count_total:
                student.standards_counts = '{} / {} ({:.0f}%)'.format(standards_count_passing, standards_count_total, 100.0 * standards_count_passing / standards_count_total)
            else:
                student.standards_counts_ = None
            if filtered:
                standards_objects = items.filter(course=course, category=standards_category, mark__student=student).annotate(best_mark=Max('mark__mark')).exclude(best_mark=None)
                standards_count_passing = standards_objects.filter(best_mark__gte=PASSING_GRADE).count()
                standards_count_total = standards_objects.count()
                if standards_count_total:
                    student.filtered_standards_counts = '{} / {} ({:.0f}%)'.format(standards_count_passing, standards_count_total, 100.0 * standards_count_passing / standards_count_total)
                else:
                    student.filtered_standards_counts = None

            # TC's row of counts
            # TODO: don't hardcode
            for item in items:
                if item.category != standards_category:
                    item.marks_counts = 'N/A'
                    continue
                marks_count_passing = item.mark_set.filter(mark__gte=PASSING_GRADE).count()
                marks_count_total = item.mark_set.exclude(mark=None).count()
                if marks_count_total:
                    item.marks_counts = '{} / {} ({:.0f}%)'.format(marks_count_passing, marks_count_total, 100.0 * marks_count_passing / marks_count_total)
                else:
                    item.marks_counts = None

    # Gather visual flagging criteria
    calculation_rule = benchmark_find_calculation_rule(school_year)
    category_flag_criteria = {}
    for category in Category.objects.filter(item__in=items).distinct():
        category_flag_criteria[category.pk] = []
        substitutions = calculation_rule.substitution_set.filter(apply_to_departments=course.department, apply_to_categories=category, flag_visually=True)
        for substitution in substitutions:
            category_flag_criteria[category.pk].append(substitution.operator + ' ' + str(substitution.match_value))

    return render_to_response('benchmark_grade/gradebook.html', {
        'items': items,
        'item_pks': ','.join(map(str,items.values_list('pk', flat=True))),
        'pending_aggregate_pks': json.dumps(map(str, pending_aggregate_pks)),
        'students': students,
        'course': course,
        'teacher_courses': teacher_courses,
        'filtered' : filtered,
        'filter_form': filter_form,
        'category_flag_criteria': category_flag_criteria,
    }, RequestContext(request, {}),)