Esempio n. 1
0
def gradebook(request, course_id):
    course = get_object_or_404(Course, pk=course_id)
    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):
        return HttpResponse(status=403,
                            content='You do not have access to this course.')

    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)

    # Freeze these now in case someone else gets in here!
    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)
        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)
                        if created:
                            mark.save()
                else:
                    # a regular item without demonstrations; make only one mark
                    mark, created = Mark.objects.get_or_create(item=item,
                                                               student=student)
                    if created:
                        mark.save()
        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.')
        student.marks = student_marks
        student.average = gradebook_get_average(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)
        # 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

    return render_to_response(
        'benchmark_grade/gradebook.html',
        {
            'items': items,
            'item_pks': ','.join(map(str, items.values_list('pk', flat=True))),
            'students': students,
            'course': course,
            'teacher_courses': teacher_courses,
            'filtered': filtered,
            'filter_form': filter_form,
        },
        RequestContext(request, {}),
    )
Esempio n. 2
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, {}),
    )