Commit d201c06f by Matt Drayer Committed by Jonathan Piacenti

mattdrayer/api-course-avg-release: Account for ungraded students

parent fb28f68f
......@@ -1573,7 +1573,7 @@ class CoursesApiTests(TestCase):
parent_location=self.chapter.location,
category='mentoring',
data=StringResponseXMLFactory().build_xml(answer='foo'),
display_name=u"test problem smae points",
display_name=u"test problem same points",
metadata={'rerandomize': 'always', 'graded': True, 'format': "Midterm Exam"}
)
......@@ -1591,7 +1591,6 @@ class CoursesApiTests(TestCase):
grade_dict = {'value': points_scored, 'max_value': points_possible, 'user_id': user.id}
module.system.publish(module, 'grade', grade_dict)
StudentGradebook.objects.filter(user=user).update(created=timezone.now() - timedelta(days=1))
test_uri = '{}/{}/metrics/grades/leaders/'.format(self.base_courses_uri, self.test_course_id)
response = self.do_get(test_uri)
self.assertEqual(response.status_code, 200)
......@@ -1615,11 +1614,16 @@ class CoursesApiTests(TestCase):
# Filter by user who has never accessed a course module
test_user = UserFactory.create(username="testusernocoursemod")
CourseEnrollmentFactory.create(user=test_user, course_id=self.course.id)
user_filter_uri = '{}?user_id={}'.format(test_uri, test_user.id)
response = self.do_get(user_filter_uri)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['user_grade'], 0)
self.assertEqual(response.data['user_position'], 6)
# Also, with this new user now added the course average should be different
self.assertNotEqual(response.data['course_avg'], expected_course_average)
rounded_avg = float("{0:.2f}".format(response.data['course_avg']))
self.assertEqual(rounded_avg, 0.33)
# test with bogus course
bogus_test_uri = '{}/{}/metrics/grades/leaders/'.format(self.base_courses_uri, self.test_bogus_course_id)
......
......@@ -1433,11 +1433,7 @@ class CoursesMetricsGradesList(SecureListAPIView):
queryset_grade_min = queryset.aggregate(Min('grade'))
queryset_grade_count = queryset.aggregate(Count('grade'))
course_queryset = StudentGradebook.objects.filter(course_id__exact=course_key).exclude(user__in=exclude_users)
course_queryset_grade_avg = course_queryset.aggregate(Avg('grade'))
course_queryset_grade_max = course_queryset.aggregate(Max('grade'))
course_queryset_grade_min = course_queryset.aggregate(Min('grade'))
course_queryset_grade_count = course_queryset.aggregate(Count('grade'))
course_metrics = StudentGradebook.generate_leaderboard(course_key, exclude_users=exclude_users)
response_data = {}
base_uri = generate_base_uri(request)
......@@ -1448,10 +1444,10 @@ class CoursesMetricsGradesList(SecureListAPIView):
response_data['grade_minimum'] = queryset_grade_min['grade__min']
response_data['grade_count'] = queryset_grade_count['grade__count']
response_data['course_grade_average'] = course_queryset_grade_avg['grade__avg']
response_data['course_grade_maximum'] = course_queryset_grade_max['grade__max']
response_data['course_grade_minimum'] = course_queryset_grade_min['grade__min']
response_data['course_grade_count'] = course_queryset_grade_count['grade__count']
response_data['course_grade_average'] = course_metrics['course_avg']
response_data['course_grade_maximum'] = course_metrics['course_max']
response_data['course_grade_minimum'] = course_metrics['course_min']
response_data['course_grade_count'] = course_metrics['course_count']
response_data['grades'] = []
for row in queryset:
......
......@@ -5,11 +5,12 @@ from django.utils import timezone
from django.contrib.auth.models import User
from django.db import models
from django.db.models import Avg
from django.db.models import Avg, Max, Min, Count
from django.db.models.signals import post_save
from django.dispatch import receiver
from model_utils.models import TimeStampedModel
from student.models import CourseEnrollment
from xmodule_django.models import CourseKeyField
......@@ -48,19 +49,34 @@ class StudentGradebook(TimeStampedModel):
'user_position': 4,
'user_grade': 0.89
}
If there is a discrepancy between the number of gradebook entries and the overall number of enrolled
users (excluding any users who should be excluded), then we modify the course average to account for
those users who currently lack gradebook entries. We assume zero grades for these users because they
have not yet submitted a response to a scored assessment which means no grade has been calculated.
"""
data = {}
total_user_count = CourseEnrollment.users_enrolled_in(course_key).count()
queryset = StudentGradebook.objects.select_related('user')\
.filter(course_id__exact=course_key, user__is_active=True)
gradebook_user_count = len(queryset)
if exclude_users:
total_user_count = total_user_count - len(exclude_users)
queryset = queryset.exclude(user__in=exclude_users)
gradebook_user_count = len(queryset)
# print StudentGradebook.objects.select_related('user')\
# .filter(course_id__exact=course_key, user__is_active=True).query
# assert 0
# Calculate the class average
course_avg = queryset.aggregate(Avg('grade'))['grade__avg']
if gradebook_user_count < total_user_count:
# Take into account any ungraded students (assumes zeros for grades...)
course_avg = course_avg / total_user_count * gradebook_user_count
data['course_avg'] = course_avg
data['course_max'] = queryset.aggregate(Max('grade'))['grade__max']
data['course_min'] = queryset.aggregate(Min('grade'))['grade__min']
data['course_count'] = queryset.aggregate(Count('grade'))['grade__count']
# Construct the leaderboard as a queryset
data['course_avg'] = queryset.aggregate(Avg('grade'))['grade__avg']
data['queryset'] = queryset.values(
'user__id',
'user__username',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment