Skip to content
Closed

WIP #42

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 61 additions & 3 deletions submissions/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from django.conf import settings
from django.core.cache import cache
from django.db import IntegrityError, DatabaseError, transaction
from django.db import IntegrityError, DatabaseError
from dogapi import dog_stats_api

from submissions.serializers import (
Expand Down Expand Up @@ -220,7 +220,7 @@ def get_submission(submission_uuid, read_replica=False):
cache_key = Submission.get_cache_key(submission_uuid)
try:
cached_submission_data = cache.get(cache_key)
except Exception as ex:
except Exception:
# The cache backend could raise an exception
# (for example, memcache keys that contain spaces)
logger.exception("Error occurred while retrieving submission from the cache")
Expand Down Expand Up @@ -413,6 +413,64 @@ def get_all_submissions(course_id, item_id, item_type, read_replica=True):
yield data


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.

Args:
course_id (str): The course that we are getting submissions from.
item_type (str): The type of items that we are getting submissions for.
read_replica (bool): Try to use the database's read replica if it's available.

Yields:
A tuple of three dictionaries representing:
(1) a student item with the following fields:
student_id
course_id
student_item
item_type
(2) a submission with the following fields:
student_item
attempt_number
submitted_at
created_at
answer
(3) a score with the following fields:
student_item
submission
points_earned
points_possible
created_at
submission_uuid
"""

student_item_qs = StudentItem.objects
if read_replica:
student_item_qs = _use_read_replica(student_item_qs)

query = student_item_qs.prefetch_related('submission').select_related('scoresummary').filter(
course_id=course_id,
item_type=item_type,
).iterator()

for student_item in query:
for submission in student_item.submissions:
if submission.status == Submission.DELETED:
continue
if submission.hasattr('scoresummary') and not submission.score_summary.latest.is_hidden():
yield (
StudentItemSerializer(student_item).data,
SubmissionSerializer(submission).data,
ScoreSerializer(submission.score_summary.latest).data
)
else:
# Make sure we return submission information even if there isn't a score associated with it.
yield (
StudentItemSerializer(student_item).data,
SubmissionSerializer(submission).data,
{}
)


def get_top_submissions(course_id, item_id, item_type, number_of_top_scores, use_cache=True, read_replica=True):
"""Get a number of top scores for an assessment based on a particular student item

Expand Down Expand Up @@ -755,7 +813,7 @@ def set_score(submission_uuid, points_earned, points_possible,
u"No submission matching uuid {}".format(submission_uuid)
)
except DatabaseError:
error_msg = u"Could not retrieve student item: {} or submission {}.".format(
error_msg = u"Could not retrieve submission {}.".format(
submission_uuid
)
logger.exception(error_msg)
Expand Down
36 changes: 36 additions & 0 deletions submissions/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,42 @@ def test_get_all_submissions(self):
self._assert_submission(submissions[1], ANSWER_TWO, student_item.pk, 2)
self.assertEqual(submissions[1]['student_id'], STUDENT_ITEM['student_id'])

def test_get_course_submissions(self):
submission1 = api.create_submission(STUDENT_ITEM, ANSWER_ONE)
submission2 = api.create_submission(STUDENT_ITEM, ANSWER_TWO)
submission3 = api.create_submission(SECOND_STUDENT_ITEM, ANSWER_ONE)
submission4 = api.create_submission(SECOND_STUDENT_ITEM, ANSWER_TWO)

api.set_score(submission1['uuid'], 1, 4)
api.set_score(submission2['uuid'], 2, 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(
STUDENT_ITEM['course_id'],
STUDENT_ITEM['item_type'],
read_replica=False,
))

student_item1 = self._get_student_item(STUDENT_ITEM)
student_item2 = self._get_student_item(SECOND_STUDENT_ITEM)

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_score(submissions_and_scores[0][2], 4, 4)

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_score(submissions_and_scores[1][2], 3, 4)

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_score(submissions_and_scores[2][2], 2, 4)

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_score(submissions_and_scores[3][2], 1, 4)

def test_get_submission(self):
# Test base case that we can create a submission and get it back
sub_dict1 = api.create_submission(STUDENT_ITEM, ANSWER_ONE)
Expand Down