Commit 5d883009 by Shawn Milochik

Added max score to output of get_student_grade_summary_data.

This will put the actual score and actual max score in scope for
the the return_csv function, so actual scores can be dumped.

The ultimate goal is to provide this data in the CSV dump that is
passed to Stellar via pylmod.

This is PR #10395 on edX, and issue 95 on mitocw's edx fork.

https://github.com/edx/edx-platform/pull/10395

https://github.com/mitocw/edx-platform/issues/95
parent 947538bb
......@@ -254,3 +254,4 @@ Arbab Nazar <arbab@edx.org>
Douglas Hall <dhall@edx.org>
Awais Jibran <awaisdar001@gmail.com>
Muhammad Rehan <muhammadrehan69@gmail.com>
Shawn Milochik <shawn@milochik.com>
......@@ -69,7 +69,7 @@ class TestRawGradeCSV(TestSubmittingProblems):
def get_expected_grade_data(
self, get_grades=True, get_raw_scores=False,
use_offline=False):
use_offline=False, get_score_max=False):
"""
Return expected results from the get_student_grade_summary_data call
with any options selected.
......@@ -79,6 +79,11 @@ class TestRawGradeCSV(TestSubmittingProblems):
get_student_grade_summary_data for this function to be accurate.
If kwargs are added or removed, or the functionality triggered by
them changes, this function must be updated to match.
If get_score_max is True, instead of a single score between 0 and 1,
the actual score and total possible are returned. For example, if the
student got one out of two possible points, the values (1, 2) will be
returned instead of 0.5.
"""
expected_data = {
'students': [self.student_user, self.student_user2],
......@@ -155,6 +160,10 @@ class TestRawGradeCSV(TestSubmittingProblems):
u'ID', u'Username', u'Full Name', u'edX email',
u'External email', u'p3', u'p2', u'p1'
]
# Strip out the single-value float scores and replace them
# with two-tuples of actual and possible scores (see docstring).
if get_score_max:
expected_data["data"][-1][-3:] = (0.0, 1), (1.0, 1.0), (0.0, 1)
return expected_data
......@@ -197,7 +206,7 @@ class TestRawGradeCSV(TestSubmittingProblems):
request, self.course, get_grades=False
)
expected_data = self.get_expected_grade_data(get_grades=False)
# if get_grades == False, get_expected_grade_data does not
# if get_grades is False, get_expected_grade_data does not
# add an "assignments" key.
self.assertNotIn("assignments", expected_data)
self.compare_data(data, expected_data)
......@@ -228,6 +237,22 @@ class TestRawGradeCSV(TestSubmittingProblems):
)
self.compare_data(data, expected_data)
def test_grade_summary_data_get_score_max(self):
"""
Test grade summary data report generation with get_score_max set
to True (also requires get_raw_scores to be True).
"""
request = DummyRequest()
self.answer_question()
data = get_student_grade_summary_data(
request, self.course, use_offline=True, get_raw_scores=True,
get_score_max=True,
)
expected_data = self.get_expected_grade_data(
use_offline=True, get_raw_scores=True, get_score_max=True,
)
self.compare_data(data, expected_data)
def compare_data(self, data, expected_data):
"""
Compare the output of the get_student_grade_summary_data
......
......@@ -631,17 +631,21 @@ class GradeTable(object):
self.grades = {}
self._current_row = {}
def _add_grade_to_row(self, component, score):
def _add_grade_to_row(self, component, score, possible=None):
"""Creates component if needed, and assigns score
Args:
component (str): Course component being graded
score (float): Score of student on component
possible (float): Max possible score for the component
Returns:
None
"""
component_index = self.components.setdefault(component, len(self.components))
if possible is not None:
# send a tuple instead of a single value
score = (score, possible)
self._current_row[component_index] = score
@contextmanager
......@@ -681,7 +685,10 @@ class GradeTable(object):
return self.components.keys()
def get_student_grade_summary_data(request, course, get_grades=True, get_raw_scores=False, use_offline=False):
def get_student_grade_summary_data(
request, course, get_grades=True, get_raw_scores=False,
use_offline=False, get_score_max=False
):
"""
Return data arrays with student identity and grades for specified course.
......@@ -697,6 +704,11 @@ def get_student_grade_summary_data(request, course, get_grades=True, get_raw_sco
data = list (one per student) of lists of data corresponding to the fields
If get_raw_scores=True, then instead of grade summaries, the raw grades for all graded modules are returned.
If get_score_max is True, two values will be returned for each grade -- the
total number of points earned and the total number of points possible. For
example, if two points are possible and one is earned, (1, 2) will be
returned instead of 0.5 (the default).
"""
course_key = course.id
enrolled_students = User.objects.filter(
......@@ -723,9 +735,18 @@ def get_student_grade_summary_data(request, course, get_grades=True, get_raw_sco
log.debug(u'student=%s, gradeset=%s', student, gradeset)
with gtab.add_row(student.id) as add_grade:
if get_raw_scores:
# TODO (ichuang) encode Score as dict instead of as list, so score[0] -> score['earned']
# The following code calls add_grade, which is an alias
# for the add_row method on the GradeTable class. This adds
# a grade for each assignment. Depending on whether
# get_score_max is True, it will return either a single
# value as a float between 0 and 1, or a two-tuple
# containing the earned score and possible score for
# the assignment (see docstring).
for score in gradeset['raw_scores']:
add_grade(score.section, getattr(score, 'earned', score[0]))
if get_score_max is True:
add_grade(score.section, score.earned, score.possible)
else:
add_grade(score.section, score.earned)
else:
for grade_item in gradeset['section_breakdown']:
add_grade(grade_item['label'], grade_item['percent'])
......
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