Commit a5e721e4 by Eric Fischer Committed by Diana Huang

Use score summaries in data download

The submissions API has a get_score() method which, given a student item,
will look up the relevant ScoreSummary and return the latest Score it
contains. Our data download should do the same.

To avoid confusion, we have elected to hide scores that could not be
returned from api.get_score(). The other options are:
- include a score that may be for a different submission for the same
    student item, if using ScoreSummary.latest in all cases
- include scores that may not "count", because they'll never be the
    return value of api.get_score()

Test has been updated.
parent feb2ae3d
...@@ -414,7 +414,9 @@ def get_all_submissions(course_id, item_id, item_type, read_replica=True): ...@@ -414,7 +414,9 @@ def get_all_submissions(course_id, item_id, item_type, read_replica=True):
def get_all_course_submission_information(course_id, item_type, read_replica=True): def get_all_course_submission_information(course_id, item_type, read_replica=True):
""" For the given course, get all the submissions, student items, and scores of the given item type. """ For the given course, get all student items of the given item type, all the submissions for those itemes,
and the latest scores for each item. If a submission was given a score that is not the latest score for the
relevant student item, it will still be included but without score.
Args: Args:
course_id (str): The course that we are getting submissions from. course_id (str): The course that we are getting submissions from.
...@@ -434,7 +436,8 @@ def get_all_course_submission_information(course_id, item_type, read_replica=Tru ...@@ -434,7 +436,8 @@ def get_all_course_submission_information(course_id, item_type, read_replica=Tru
submitted_at submitted_at
created_at created_at
answer answer
(3) a score with the following fields: (3) a score with the following fields, if one exists and it is the latest score:
(if both conditions are not met, an empty dict is returned here)
student_item student_item
submission submission
points_earned points_earned
...@@ -447,27 +450,26 @@ def get_all_course_submission_information(course_id, item_type, read_replica=Tru ...@@ -447,27 +450,26 @@ def get_all_course_submission_information(course_id, item_type, read_replica=Tru
if read_replica: if read_replica:
submission_qs = _use_read_replica(submission_qs) submission_qs = _use_read_replica(submission_qs)
query = submission_qs.select_related('student_item').prefetch_related('score_set').filter( query = submission_qs.select_related('student_item__scoresummary__latest__submission').filter(
student_item__course_id=course_id, student_item__course_id=course_id,
student_item__item_type=item_type, student_item__item_type=item_type,
).iterator() ).iterator()
for submission in query: for submission in query:
student_item = submission.student_item student_item = submission.student_item
if submission.score_set.count() > 0: serialized_score = {}
for score in submission.score_set.all(): if hasattr(student_item, 'scoresummary'):
yield ( latest_score = student_item.scoresummary.latest
StudentItemSerializer(student_item).data,
SubmissionSerializer(submission).data, # Only include the score if it is not a reset score (is_hidden), and if the current submission is the same
ScoreSerializer(score).data # as the student_item's latest score's submission. This matches the behavior of the API's get_score method.
) if (not latest_score.is_hidden()) and latest_score.submission.uuid == submission.uuid:
else: serialized_score = ScoreSerializer(latest_score).data
# Make sure we return submission information even if there isn't a score associated with it. yield (
yield ( StudentItemSerializer(student_item).data,
StudentItemSerializer(student_item).data, SubmissionSerializer(submission).data,
SubmissionSerializer(submission).data, serialized_score
{} )
)
def get_top_submissions(course_id, item_id, item_type, number_of_top_scores, use_cache=True, read_replica=True): def get_top_submissions(course_id, item_id, item_type, number_of_top_scores, use_cache=True, read_replica=True):
......
...@@ -101,16 +101,18 @@ class TestSubmissionsApi(TestCase): ...@@ -101,16 +101,18 @@ class TestSubmissionsApi(TestCase):
self._assert_submission(submissions[1], ANSWER_TWO, student_item.pk, 2) self._assert_submission(submissions[1], ANSWER_TWO, student_item.pk, 2)
self.assertEqual(submissions[1]['student_id'], STUDENT_ITEM['student_id']) self.assertEqual(submissions[1]['student_id'], STUDENT_ITEM['student_id'])
def test_get_course_submissions(self): @ddt.data(True, False)
def test_get_course_submissions(self, set_scores):
submission1 = api.create_submission(STUDENT_ITEM, ANSWER_ONE) submission1 = api.create_submission(STUDENT_ITEM, ANSWER_ONE)
submission2 = api.create_submission(STUDENT_ITEM, ANSWER_TWO) submission2 = api.create_submission(STUDENT_ITEM, ANSWER_TWO)
submission3 = api.create_submission(SECOND_STUDENT_ITEM, ANSWER_ONE) submission3 = api.create_submission(SECOND_STUDENT_ITEM, ANSWER_ONE)
submission4 = api.create_submission(SECOND_STUDENT_ITEM, ANSWER_TWO) submission4 = api.create_submission(SECOND_STUDENT_ITEM, ANSWER_TWO)
api.set_score(submission1['uuid'], 1, 4) if set_scores:
api.set_score(submission2['uuid'], 2, 4) api.set_score(submission1['uuid'], 1, 4)
api.set_score(submission3['uuid'], 3, 4) api.set_score(submission2['uuid'], 2, 4)
api.set_score(submission4['uuid'], 4, 4) api.set_score(submission3['uuid'], 3, 4)
api.set_score(submission4['uuid'], 4, 4)
submissions_and_scores = list(api.get_all_course_submission_information( submissions_and_scores = list(api.get_all_course_submission_information(
STUDENT_ITEM['course_id'], STUDENT_ITEM['course_id'],
...@@ -123,19 +125,26 @@ class TestSubmissionsApi(TestCase): ...@@ -123,19 +125,26 @@ class TestSubmissionsApi(TestCase):
self.assertDictEqual(SECOND_STUDENT_ITEM, submissions_and_scores[0][0]) self.assertDictEqual(SECOND_STUDENT_ITEM, submissions_and_scores[0][0])
self._assert_submission(submissions_and_scores[0][1], submission4['answer'], student_item2.pk, 2) self._assert_submission(submissions_and_scores[0][1], submission4['answer'], student_item2.pk, 2)
self._assert_score(submissions_and_scores[0][2], 4, 4)
self.assertDictEqual(SECOND_STUDENT_ITEM, submissions_and_scores[1][0]) self.assertDictEqual(SECOND_STUDENT_ITEM, submissions_and_scores[1][0])
self._assert_submission(submissions_and_scores[1][1], submission3['answer'], student_item2.pk, 1) self._assert_submission(submissions_and_scores[1][1], submission3['answer'], student_item2.pk, 1)
self._assert_score(submissions_and_scores[1][2], 3, 4)
self.assertDictEqual(STUDENT_ITEM, submissions_and_scores[2][0]) self.assertDictEqual(STUDENT_ITEM, submissions_and_scores[2][0])
self._assert_submission(submissions_and_scores[2][1], submission2['answer'], student_item1.pk, 2) self._assert_submission(submissions_and_scores[2][1], submission2['answer'], student_item1.pk, 2)
self._assert_score(submissions_and_scores[2][2], 2, 4)
self.assertDictEqual(STUDENT_ITEM, submissions_and_scores[3][0]) self.assertDictEqual(STUDENT_ITEM, submissions_and_scores[3][0])
self._assert_submission(submissions_and_scores[3][1], submission1['answer'], student_item1.pk, 1) self._assert_submission(submissions_and_scores[3][1], submission1['answer'], student_item1.pk, 1)
self._assert_score(submissions_and_scores[3][2], 1, 4)
# These scores will always be empty
self.assertEqual(submissions_and_scores[1][2], {})
self.assertEqual(submissions_and_scores[3][2], {})
if set_scores:
self._assert_score(submissions_and_scores[0][2], 4, 4)
self._assert_score(submissions_and_scores[2][2], 2, 4)
else:
self.assertEqual(submissions_and_scores[0][2], {})
self.assertEqual(submissions_and_scores[2][2], {})
def test_get_submission(self): def test_get_submission(self):
# Test base case that we can create a submission and get it back # Test base case that we can create a submission and get it back
......
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