Skip to content

Commit

Permalink
Merge pull request #57 from edx/neem/score-summaries
Browse files Browse the repository at this point in the history
return serialized scores from get_scores()
  • Loading branch information
Cliff Dyer authored Apr 3, 2017
2 parents 7deafc6 + 7a557d7 commit fc232e5
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 18 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def load_requirements(*requirements_paths):

setup(
name='edx-submissions',
version='1.2.0',
version='2.0.0',
author='edX',
description='An API for creating submissions and scores.',
url='http://github.com/edx/edx-submissions.git',
Expand Down
1 change: 1 addition & 0 deletions submissions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = u'2.0.0'
15 changes: 8 additions & 7 deletions submissions/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from dogapi import dog_stats_api

from submissions.serializers import (
SubmissionSerializer, StudentItemSerializer, ScoreSerializer
SubmissionSerializer, StudentItemSerializer, ScoreSerializer, UnannotatedScoreSerializer
)
from submissions.models import Submission, StudentItem, Score, ScoreSummary, ScoreAnnotation, score_set, score_reset

Expand Down Expand Up @@ -626,7 +626,10 @@ def get_score(student_item):


def get_scores(course_id, student_id):
"""Return a dict mapping item_ids -> (points_earned, points_possible).
"""Return a dict mapping item_ids to scores.
Scores are represented by serialized Score objects in JSON-like dict
format.
This method would be used by an LMS to find all the scores for a given
student in a given course.
Expand Down Expand Up @@ -654,18 +657,16 @@ def get_scores(course_id, student_id):
score_summaries = ScoreSummary.objects.filter(
student_item__course_id=course_id,
student_item__student_id=student_id,
).select_related('latest', 'student_item')
).select_related('latest', 'latest__submission', 'student_item')
except DatabaseError:
msg = u"Could not fetch scores for course {}, student {}".format(
course_id, student_id
)
logger.exception(msg)
raise SubmissionInternalError(msg)
scores = {
summary.student_item.item_id:
(summary.latest.points_earned, summary.latest.points_possible)
for summary in score_summaries
if not summary.latest.is_hidden()
summary.student_item.item_id: UnannotatedScoreSerializer(summary.latest).data
for summary in score_summaries if not summary.latest.is_hidden()
}
return scores

Expand Down
21 changes: 20 additions & 1 deletion submissions/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,31 @@ class Meta:
)


class ScoreSerializer(serializers.ModelSerializer):
class UnannotatedScoreSerializer(serializers.ModelSerializer):

# Ensure that the created_at datetime is not converted to a string.
created_at = DateTimeField(format=None, required=False)

class Meta:
model = Score
fields = (
'student_item',
'submission',
'points_earned',
'points_possible',
'created_at',

# Computed
'submission_uuid',
)


class ScoreSerializer(serializers.ModelSerializer):

# Ensure that the created_at datetime is not converted to a string.
created_at = DateTimeField(format=None, required=False)
annotations = serializers.SerializerMethodField()

def get_annotations(self, obj):
"""
Inspect ScoreAnnotations to attach all relevant annotations.
Expand Down
38 changes: 30 additions & 8 deletions submissions/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ def test_get_score_no_student_id(self):
student_item['student_id'] = None
self.assertIs(api.get_score(student_item), None)

@freeze_time(datetime.datetime.now().replace(tzinfo=pytz.UTC))
def test_get_scores(self):
student_item = copy.deepcopy(STUDENT_ITEM)
student_item["course_id"] = "get_scores_course"
Expand All @@ -388,14 +389,35 @@ def test_get_scores(self):
scores = api.get_scores(
student_item["course_id"], student_item["student_id"]
)
self.assertEqual(
scores,
{
u"i4x://a/b/c/s1": (2, 5),
u"i4x://a/b/c/s2": (0, 10),
u"i4x://a/b/c/s3": (4, 4),
}
)
self.assertEqual(
scores,
{
u'i4x://a/b/c/s1': {
'created_at': datetime.datetime.now(),
'points_earned': 2,
'points_possible': 5,
'student_item': 1,
'submission': 1,
'submission_uuid': s1['uuid'],
},
u'i4x://a/b/c/s2': {
'created_at': datetime.datetime.now(),
'points_earned': 0,
'points_possible': 10,
'student_item': 2,
'submission': 2,
'submission_uuid': s2['uuid'],
},
u'i4x://a/b/c/s3': {
'created_at': datetime.datetime.now(),
'points_earned': 4,
'points_possible': 4,
'student_item': 3,
'submission': 3,
'submission_uuid': s3['uuid'],
},
}
)

def test_get_top_submissions(self):
student_item_1 = copy.deepcopy(STUDENT_ITEM)
Expand Down
3 changes: 2 additions & 1 deletion submissions/tests/test_reset_score.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ def test_reset_then_add_score(self):

scores = sub_api.get_scores(self.STUDENT_ITEM['course_id'], self.STUDENT_ITEM['student_id'])
self.assertIn(self.STUDENT_ITEM['item_id'], scores)
self.assertEqual(scores[self.STUDENT_ITEM['item_id']], (3, 4))
item_score = scores[self.STUDENT_ITEM['item_id']]
self.assertEqual((item_score['points_earned'], item_score['points_possible']), (3, 4))

def test_reset_then_get_score_for_submission(self):
# Create a submission for the student and score it
Expand Down

0 comments on commit fc232e5

Please sign in to comment.