Commit 413ce1de by Stephen Sanchez

Cleaning up the score API and adding a few tests.

In addition to the score API cleanup, there is doc, test fixes,
and some refactoring around API use.
parent f2f73dc1
......@@ -11,8 +11,9 @@ from django.db import DatabaseError
from openassessment.peer.models import PeerEvaluation
from openassessment.peer.serializers import PeerEvaluationSerializer
from submissions import api as submission_api
from submissions.models import Submission, StudentItem, Score
from submissions.serializers import SubmissionSerializer, ScoreSerializer
from submissions.serializers import SubmissionSerializer, StudentItemSerializer
logger = logging.getLogger(__name__)
......@@ -191,26 +192,20 @@ def _check_if_finished_and_create_score(student_item,
finished_evaluating = has_finished_required_evaluating(
student_item.student_id,
int(required_evaluations_for_student)
required_evaluations_for_student
)
evaluations = PeerEvaluation.objects.filter(submission=submission).order_by("-points_earned")
submission_finished = evaluations.count() >= int(required_evaluations_for_submission)
submission_finished = evaluations.count() >= required_evaluations_for_submission
scores = []
for evaluation in evaluations:
scores.append(evaluation.points_earned)
if finished_evaluating and submission_finished:
# Create a score for the submission author
score = ScoreSerializer(data={
"student_item": student_item.pk,
"submission": submission.pk,
"points_earned": _calculate_final_score(scores),
"points_possible": evaluations[0].points_possible,
})
if not score.is_valid():
raise PeerEvaluationInternalError(
"Could not create a score"
)
return score.save()
submission_api.set_score(
StudentItemSerializer(student_item).data,
SubmissionSerializer(submission).data,
_calculate_final_score(scores),
evaluations[0].points_possible
)
def _calculate_final_score(scores):
......@@ -222,10 +217,13 @@ def _calculate_final_score(scores):
"""
total_scores = len(scores)
if total_scores % 2:
if total_scores == 0:
return 0
elif total_scores % 2:
return scores[total_scores / 2]
else:
return (scores[total_scores / 2] + scores[total_scores /2 + 1]) / 2
return (scores[total_scores / 2 - 1] + scores[total_scores / 2]) / 2
def has_finished_required_evaluating(student_id, required_evaluations):
"""Check if a student still needs to evaluate more submissions
......@@ -252,7 +250,7 @@ def has_finished_required_evaluating(student_id, required_evaluations):
while evaluating this workflow rule.
Examples:
>>> has_finished_required_evaluating("Tim")
>>> has_finished_required_evaluating("Tim", 3)
True
"""
......@@ -391,7 +389,7 @@ def _get_first_submission_not_evaluated(student_items, student_id, required_num_
)
for submission in submissions:
evaluations = PeerEvaluation.objects.filter(submission=submission)
if evaluations.count() < int(required_num_evaluations):
if evaluations.count() < required_num_evaluations:
already_evaluated = False
for evaluation in evaluations:
already_evaluated = already_evaluated or evaluation.scorer_id == student_id
......
......@@ -9,7 +9,7 @@ from mock import patch
from openassessment.peer import api
from openassessment.peer.models import PeerEvaluation
from submissions.api import create_submission
from submissions.api import create_submission, SubmissionInternalError
from submissions.models import Submission
from submissions.tests.test_api import STUDENT_ITEM, ANSWER_ONE
......@@ -19,6 +19,9 @@ ASSESSMENT_DICT = dict(
feedback="Your submission was thrilling.",
)
REQUIRED_GRADED = 5
REQUIRED_GRADED_BY = 3
MONDAY = datetime.datetime(2007, 9, 12, 0, 0, 0, 0, pytz.UTC)
TUESDAY = datetime.datetime(2007, 9, 13, 0, 0, 0, 0, pytz.UTC)
WEDNESDAY = datetime.datetime(2007, 9, 15, 0, 0, 0, 0, pytz.UTC)
......@@ -32,6 +35,8 @@ class TestApi(TestCase):
evaluation = api.create_evaluation(
submission["uuid"],
STUDENT_ITEM["student_id"],
REQUIRED_GRADED,
REQUIRED_GRADED_BY,
ASSESSMENT_DICT
)
self._assert_evaluation(evaluation, **ASSESSMENT_DICT)
......@@ -42,6 +47,8 @@ class TestApi(TestCase):
api.create_evaluation(
submission["uuid"],
STUDENT_ITEM["student_id"],
REQUIRED_GRADED,
REQUIRED_GRADED_BY,
assessment_dict
)
evaluations = api.get_evaluations(submission["uuid"])
......@@ -54,6 +61,8 @@ class TestApi(TestCase):
api.create_evaluation(
submission["uuid"],
STUDENT_ITEM["student_id"],
REQUIRED_GRADED,
REQUIRED_GRADED_BY,
assessment_dict,
MONDAY
)
......@@ -63,15 +72,34 @@ class TestApi(TestCase):
self.assertEqual(evaluations[0]["scored_at"], MONDAY)
def test_student_finished_evaluating(self):
bob = self._create_student_and_submission("Tim", "Tim's answer")
bob = self._create_student_and_submission("Bob", "Bob's answer")
sally = self._create_student_and_submission("Sally", "Sally's answer")
jim = self._create_student_and_submission("Jim", "Jim's answer")
self.assertFalse(api.has_finished_required_evaluating("Tim", 3))
api.create_evaluation(bob["uuid"], "Tim", ASSESSMENT_DICT)
api.create_evaluation(sally["uuid"], "Tim", ASSESSMENT_DICT)
api.create_evaluation(
bob["uuid"],
"Tim",
REQUIRED_GRADED,
REQUIRED_GRADED_BY,
ASSESSMENT_DICT
)
api.create_evaluation(
sally["uuid"],
"Tim",
REQUIRED_GRADED,
REQUIRED_GRADED_BY,
ASSESSMENT_DICT
)
self.assertFalse(api.has_finished_required_evaluating("Tim", 3))
api.create_evaluation(jim["uuid"], "Tim", ASSESSMENT_DICT)
api.create_evaluation(
jim["uuid"],
"Tim",
REQUIRED_GRADED,
REQUIRED_GRADED_BY,
ASSESSMENT_DICT
)
self.assertTrue(api.has_finished_required_evaluating("Tim", 3))
@raises(api.PeerEvaluationRequestError)
......@@ -86,7 +114,7 @@ class TestApi(TestCase):
)
self._create_student_and_submission("Jim", "Jim's answer", THURSDAY)
submission = api.get_submission_to_evaluate(STUDENT_ITEM)
submission = api.get_submission_to_evaluate(STUDENT_ITEM, 3)
self.assertIsNotNone(submission)
self.assertEqual(submission["answer"], u"Bob's answer")
self.assertEqual(submission["student_item"], 2)
......@@ -95,7 +123,7 @@ class TestApi(TestCase):
@raises(api.PeerEvaluationWorkflowError)
def test_no_submissions_to_evaluate_for_tim(self):
self._create_student_and_submission("Tim", "Tim's answer", MONDAY)
api.get_submission_to_evaluate(STUDENT_ITEM)
api.get_submission_to_evaluate(STUDENT_ITEM, 3)
"""
Some Error Checking Tests against DB failures.
......@@ -109,23 +137,36 @@ class TestApi(TestCase):
api.create_evaluation(
submission["uuid"],
STUDENT_ITEM["student_id"],
REQUIRED_GRADED,
REQUIRED_GRADED_BY,
ASSESSMENT_DICT,
MONDAY
)
@patch.object(PeerEvaluation.objects, 'filter')
@raises(api.PeerEvaluationInternalError)
@raises(SubmissionInternalError)
def test_error_on_get_evaluation(self, mock_filter):
submission = create_submission(STUDENT_ITEM, ANSWER_ONE)
api.create_evaluation(
submission["uuid"],
STUDENT_ITEM["student_id"],
REQUIRED_GRADED,
REQUIRED_GRADED_BY,
ASSESSMENT_DICT,
MONDAY
)
mock_filter.side_effect = DatabaseError("Bad things happened")
api.get_evaluations(submission["uuid"])
def test_choose_score(self):
self.assertEqual(0, api._calculate_final_score([]))
self.assertEqual(5, api._calculate_final_score([5]))
# average of 5, 6, rounded down.
self.assertEqual(5, api._calculate_final_score([5, 6]))
self.assertEqual(14, api._calculate_final_score([5, 6, 12, 16, 22, 53]))
self.assertEqual(16, api._calculate_final_score([5, 6, 12, 16, 22, 53, 102]))
@staticmethod
def _create_student_and_submission(student, answer, date=None):
new_student_item = STUDENT_ITEM.copy()
......
......@@ -15,6 +15,99 @@ from xblock.fragment import Fragment
mako_default_filters = ['unicode', 'h', 'trim']
EXAMPLE_POVERTY_RUBRIC = (
"OpenAssessmentBlock Poverty Rubric",
"""
<vertical_demo>
<openassessment start="2014-12-19T23:00-7:00" due="2014-12-21T23:00-7:00">
<prompt>
Given the state of the world today, what do you think should be done to
combat poverty? Please answer in a short essay of 200-300 words.
</prompt>
<rubric>
Read for conciseness, clarity of thought, and form.
<criterion name="concise">
How concise is it?
<option val="0">Neal Stephenson (late)</option>
<option val="1">HP Lovecraft</option>
<option val="3">Robert Heinlein</option>
<option val="4">Neal Stephenson (early)</option>
<option val="5">Earnest Hemingway</option>
</criterion>
<criterion name="clearheaded">
How clear is the thinking?
<option val="0">Yogi Berra</option>
<option val="1">Hunter S. Thompson</option>
<option val="2">Robert Heinlein</option>
<option val="3">Isaac Asimov</option>
<option val="10">Spock</option>
</criterion>
<criterion name="form">
Lastly, how is it's form? Punctuation, grammar, and spelling all count.
<option val="0">lolcats</option>
<option val="1">Facebook</option>
<option val="2">Reddit</option>
<option val="3">metafilter</option>
<option val="4">Usenet, 1996</option>
<option val="5">The Elements of Style</option>
</criterion>
</rubric>
<evals>
<peereval start="2014-12-20T19:00-7:00"
due="2014-12-21T22:22-7:00"
must_grade="5"
must_be_graded_by="3" />
<selfeval/>
</evals>
</openassessment>
</vertical_demo>
"""
)
EXAMPLE_CENSORSHIP_RUBRIC = (
"OpenAssessmentBlock Censorship Rubric",
"""
<vertical_demo>
<openassessment start="2013-12-19T23:00-7:00" due="2014-12-21T23:00-7:00">
<prompt>
What do you think about censorship in libraries? I think it's pretty great.
</prompt>
<rubric>
Read for conciseness, clarity of thought, and form.
<criterion name="concise">
How concise is it?
<option val="0">The Bible</option>
<option val="1">Earnest Hemingway</option>
<option val="3">Matsuo Basho</option>
</criterion>
<criterion name="clearheaded">
How clear is the thinking?
<option val="0">Eric</option>
<option val="1">John</option>
<option val="2">Ian</option>
</criterion>
<criterion name="form">
Lastly, how is it's form? Punctuation, grammar, and spelling all count.
<option val="0">IRC</option>
<option val="1">Real Email</option>
<option val="2">Old-timey letters</option>
</criterion>
</rubric>
<evals>
<selfeval/>
<peereval start="2014-12-20T19:00-7:00"
due="2014-12-21T22:22-7:00"
must_grade="5"
must_be_graded_by="3" />
</evals>
</openassessment>
</vertical_demo>
"""
)
class OpenAssessmentBlock(XBlock):
"""Displays a question and gives an area where students can compose a response."""
......@@ -131,8 +224,8 @@ class OpenAssessmentBlock(XBlock):
evaluation = peer_api.create_evaluation(
data["submission_uuid"],
student_item_dict["student_id"],
peer_eval["must_grade"],
peer_eval["must_be_graded_by"],
int(peer_eval["must_grade"]),
int(peer_eval["must_be_graded_by"]),
assessment_dict
)
......@@ -204,94 +297,5 @@ class OpenAssessmentBlock(XBlock):
@staticmethod
def workbench_scenarios():
"""A canned scenario for display in the workbench."""
return [
("OpenAssessmentBlock Poverty Rubric",
"""
<vertical_demo>
<openassessment start="2014-12-19T23:00-7:00" due="2014-12-21T23:00-7:00">
<prompt>
Given the state of the world today, what do you think should be done to
combat poverty? Please answer in a short essay of 200-300 words.
</prompt>
<rubric>
Read for conciseness, clarity of thought, and form.
<criterion name="concise">
How concise is it?
<option val="0">Neal Stephenson (late)</option>
<option val="1">HP Lovecraft</option>
<option val="3">Robert Heinlein</option>
<option val="4">Neal Stephenson (early)</option>
<option val="5">Earnest Hemingway</option>
</criterion>
<criterion name="clearheaded">
How clear is the thinking?
<option val="0">Yogi Berra</option>
<option val="1">Hunter S. Thompson</option>
<option val="2">Robert Heinlein</option>
<option val="3">Isaac Asimov</option>
<option val="10">Spock</option>
</criterion>
<criterion name="form">
Lastly, how is it's form? Punctuation, grammar, and spelling all count.
<option val="0">lolcats</option>
<option val="1">Facebook</option>
<option val="2">Reddit</option>
<option val="3">metafilter</option>
<option val="4">Usenet, 1996</option>
<option val="5">The Elements of Style</option>
</criterion>
</rubric>
<evals>
<peereval start="2014-12-20T19:00-7:00"
due="2014-12-21T22:22-7:00"
must_grade="5"
must_be_graded_by="3" />
<selfeval/>
</evals>
</openassessment>
</vertical_demo>
"""),
("OpenAssessmentBlock Censorship Rubric",
"""
<vertical_demo>
<openassessment start="2013-12-19T23:00-7:00" due="2014-12-21T23:00-7:00">
<prompt>
What do you think about censorship in libraries? I think it's pretty great.
</prompt>
<rubric>
Read for conciseness, clarity of thought, and form.
<criterion name="concise">
How concise is it?
<option val="0">The Bible</option>
<option val="1">Earnest Hemingway</option>
<option val="3">Matsuo Basho</option>
</criterion>
<criterion name="clearheaded">
How clear is the thinking?
<option val="0">Eric</option>
<option val="1">John</option>
<option val="2">Ian</option>
</criterion>
<criterion name="form">
Lastly, how is it's form? Punctuation, grammar, and spelling all count.
<option val="0">IRC</option>
<option val="1">Real Email</option>
<option val="2">Old-timey letters</option>
</criterion>
</rubric>
<evals>
<selfeval/>
<peereval start="2014-12-20T19:00-7:00"
due="2014-12-21T22:22-7:00"
must_grade="5"
must_be_graded_by="3" />
</evals>
</openassessment>
</vertical_demo>
"""),
]
return [EXAMPLE_POVERTY_RUBRIC, EXAMPLE_CENSORSHIP_RUBRIC,]
......@@ -12,6 +12,50 @@ from workbench.runtime import WorkbenchRuntime
from submissions import api
from submissions.api import SubmissionRequestError, SubmissionInternalError
RUBRIC_CONFIG = """
<openassessment start="2014-12-19T23:00-7:00" due="2014-12-21T23:00-7:00">
<prompt>
Given the state of the world today, what do you think should be done to
combat poverty? Please answer in a short essay of 200-300 words.
</prompt>
<rubric>
Read for conciseness, clarity of thought, and form.
<criterion name="concise">
How concise is it?
<option val="0">Neal Stephenson (late)</option>
<option val="1">HP Lovecraft</option>
<option val="3">Robert Heinlein</option>
<option val="4">Neal Stephenson (early)</option>
<option val="5">Earnest Hemingway</option>
</criterion>
<criterion name="clearheaded">
How clear is the thinking?
<option val="0">Yogi Berra</option>
<option val="1">Hunter S. Thompson</option>
<option val="2">Robert Heinlein</option>
<option val="3">Isaac Asimov</option>
<option val="10">Spock</option>
</criterion>
<criterion name="form">
Lastly, how is it's form? Punctuation, grammar, and spelling all count.
<option val="0">lolcats</option>
<option val="1">Facebook</option>
<option val="2">Reddit</option>
<option val="3">metafilter</option>
<option val="4">Usenet, 1996</option>
<option val="5">The Elements of Style</option>
</criterion>
</rubric>
<evals>
<peereval start="2014-12-20T19:00-7:00"
due="2014-12-21T22:22-7:00"
must_grade="5"
must_be_graded_by="3" />
<selfeval/>
</evals>
</openassessment>
"""
class TestOpenAssessment(TestCase):
......@@ -22,11 +66,7 @@ class TestOpenAssessment(TestCase):
self.runtime = WorkbenchRuntime()
self.runtime.user_id = "Bob"
assessment_id = self.runtime.parse_xml_string(
"""<openassessment
prompt="This is my prompt. There are many like it, but this one is mine."
course_id="RopesCourse"
/>
""", self.runtime.id_generator)
RUBRIC_CONFIG, self.runtime.id_generator)
self.assessment = self.runtime.get_block(assessment_id)
self.default_json_submission = json.dumps({"submission": "This is my answer to this test question!"})
......
......@@ -212,17 +212,47 @@ def get_submissions(student_item_dict, limit=None):
if limit:
submission_models = submission_models[:limit]
return [SubmissionSerializer(submission).data for submission in
submission_models]
return SubmissionSerializer(submission_models, many=True).data
def get_score(student_item):
student_item_model = StudentItem.objects.get(
student_id=student_item["student_id"],
course_id=student_item["course_id"],
item_id=student_item["item_id"],
item_type=student_item["item_type"]
)
"""Get the score for a particular student item
Each student item should have a unique score. This function will return the
score if it is available. A score is only calculated for a student item if
it has completed the workflow for a particular assessment module.
Args:
student_item (dict): The dictionary representation of a student item.
Function returns the score related to this student item.
Returns:
score (dict): The score associated with this student item. None if there
is no score found.
Raises:
SubmissionInternalError: Raised if a score cannot be retrieved because
of an internal server error.
Examples:
>>> student_item = {
>>> "student_id":"Tim",
>>> "course_id":"TestCourse",
>>> "item_id":"u_67",
>>> "item_type":"openassessment"
>>> }
>>>
>>> get_score(student_item)
[{
'student_item': 2,
'submission': 2,
'points_earned': 8,
'points_possible': 20,
'created_at': datetime.datetime(2014, 2, 7, 18, 30, 1, 807911, tzinfo=<UTC>)
}]
"""
student_item_model = StudentItem.objects.get(**student_item)
scores = Score.objects.filter(student_item=student_item_model)
return ScoreSerializer(scores, many=True).data
......@@ -231,8 +261,80 @@ def get_scores(course_id, student_id, types=None):
pass
def set_score(student_item):
pass
def set_score(student_item, submission, score, points_possible):
"""Set a score for a particular student item, submission pair.
Sets the score for a particular student item and submission pair. This score
is calculated externally to the API.
Args:
student_item (dict): The student item associated with this score. This
dictionary must contain a course_id, student_id, and item_id.
submission (dict): The submission associated with this score. This
dictionary must contain all submission fields to properly get a
unique submission item.
score (int): The score to associate with the given submission and
student item.
points_possible (int): The total points possible for this particular
student item.
Returns:
(dict): The dictionary representation of the saved score.
Raises:
SubmissionInternalError: Thrown if there was an internal error while
attempting to save the score.
SubmissionRequestError: Thrown if the given student item or submission
are not found.
Examples:
>>> student_item_dict = dict(
>>> student_id="Tim",
>>> item_id="item_1",
>>> course_id="course_1",
>>> item_type="type_one"
>>> )
>>>
>>> submission_dict = dict(
>>> student_item=2,
>>> attempt_number=1,
>>> submitted_at=datetime.datetime(2014, 1, 29, 23, 14, 52, 649284, tzinfo=<UTC>),
>>> created_at=datetime.datetime(2014, 1, 29, 17, 14, 52, 668850, tzinfo=<UTC>),
>>> answer=u'The answer is 42.'
>>> )
>>> set_score(student_item_dict, submission_dict, 11, 12)
{
'student_item': 2,
'submission': 1,
'points_earned': 11,
'points_possible': 12,
'created_at': datetime.datetime(2014, 2, 7, 20, 6, 42, 331156, tzinfo=<UTC>)
}
"""
try:
student_item_model = StudentItem.objects.get(**student_item)
submission_model = Submission.objects.get(**submission)
except DatabaseError:
error_msg = u"Could not retrieve student item: {} or submission {}.".format(
student_item, submission
)
logger.exception(error_msg)
raise SubmissionRequestError(error_msg)
score = ScoreSerializer(
data={
"student_item": student_item_model.pk,
"submission": submission_model.pk,
"points_earned": score,
"points_possible": points_possible,
}
)
if not score.is_valid():
logger.exception(score.errors)
raise SubmissionInternalError(score.errors)
score.save()
return score.data
def _get_or_create_student_item(student_item_dict):
......@@ -269,7 +371,8 @@ def _get_or_create_student_item(student_item_dict):
try:
return StudentItem.objects.get(**student_item_dict)
except StudentItem.DoesNotExist:
student_item_serializer = StudentItemSerializer(data=student_item_dict)
student_item_serializer = StudentItemSerializer(
data=student_item_dict)
if not student_item_serializer.is_valid():
raise SubmissionRequestError(student_item_serializer.errors)
return student_item_serializer.save()
......
......@@ -7,7 +7,7 @@ from nose.tools import raises
from mock import patch
import pytz
from submissions.api import create_submission, get_submissions, SubmissionRequestError, SubmissionInternalError
from submissions import api as api
from submissions.models import Submission
from submissions.serializers import StudentItemSerializer
......@@ -31,79 +31,84 @@ ANSWER_TWO = u"this is my other answer!"
@ddt
class TestApi(TestCase):
"""
Testing Submissions
"""
def test_create_submission(self):
submission = create_submission(STUDENT_ITEM, ANSWER_ONE)
submission = api.create_submission(STUDENT_ITEM, ANSWER_ONE)
self._assert_submission(submission, ANSWER_ONE, 1, 1)
def test_get_submissions(self):
create_submission(STUDENT_ITEM, ANSWER_ONE)
create_submission(STUDENT_ITEM, ANSWER_TWO)
submissions = get_submissions(STUDENT_ITEM)
api.create_submission(STUDENT_ITEM, ANSWER_ONE)
api.create_submission(STUDENT_ITEM, ANSWER_TWO)
submissions = api.get_submissions(STUDENT_ITEM)
self._assert_submission(submissions[1], ANSWER_ONE, 1, 1)
self._assert_submission(submissions[0], ANSWER_TWO, 1, 2)
def test_two_students(self):
create_submission(STUDENT_ITEM, ANSWER_ONE)
create_submission(SECOND_STUDENT_ITEM, ANSWER_TWO)
api.create_submission(STUDENT_ITEM, ANSWER_ONE)
api.create_submission(SECOND_STUDENT_ITEM, ANSWER_TWO)
submissions = get_submissions(STUDENT_ITEM)
submissions = api.get_submissions(STUDENT_ITEM)
self.assertEqual(1, len(submissions))
self._assert_submission(submissions[0], ANSWER_ONE, 1, 1)
submissions = get_submissions(SECOND_STUDENT_ITEM)
submissions = api.get_submissions(SECOND_STUDENT_ITEM)
self.assertEqual(1, len(submissions))
self._assert_submission(submissions[0], ANSWER_TWO, 2, 1)
@file_data('test_valid_student_items.json')
def test_various_student_items(self, valid_student_item):
create_submission(valid_student_item, ANSWER_ONE)
submission = get_submissions(valid_student_item)[0]
api.create_submission(valid_student_item, ANSWER_ONE)
submission = api.get_submissions(valid_student_item)[0]
self._assert_submission(submission, ANSWER_ONE, 1, 1)
def test_get_latest_submission(self):
past_date = datetime.datetime(2007, 9, 12, 0, 0, 0, 0, pytz.UTC)
more_recent_date = datetime.datetime(2007, 9, 13, 0, 0, 0, 0, pytz.UTC)
create_submission(STUDENT_ITEM, ANSWER_ONE, more_recent_date)
create_submission(STUDENT_ITEM, ANSWER_TWO, past_date)
api.create_submission(STUDENT_ITEM, ANSWER_ONE, more_recent_date)
api.create_submission(STUDENT_ITEM, ANSWER_TWO, past_date)
# Test a limit on the submissions
submissions = get_submissions(STUDENT_ITEM, 1)
submissions = api.get_submissions(STUDENT_ITEM, 1)
self.assertEqual(1, len(submissions))
self.assertEqual(ANSWER_ONE, submissions[0]["answer"])
self.assertEqual(more_recent_date.year,
submissions[0]["submitted_at"].year)
def test_set_attempt_number(self):
create_submission(STUDENT_ITEM, ANSWER_ONE, None, 2)
submissions = get_submissions(STUDENT_ITEM)
api.create_submission(STUDENT_ITEM, ANSWER_ONE, None, 2)
submissions = api.get_submissions(STUDENT_ITEM)
self._assert_submission(submissions[0], ANSWER_ONE, 1, 2)
@raises(SubmissionRequestError)
@raises(api.SubmissionRequestError)
@file_data('test_bad_student_items.json')
def test_error_checking(self, bad_student_item):
create_submission(bad_student_item, -100)
api.create_submission(bad_student_item, -100)
@raises(SubmissionRequestError)
@raises(api.SubmissionRequestError)
def test_error_checking_submissions(self):
create_submission(STUDENT_ITEM, ANSWER_ONE, None, -1)
api.create_submission(STUDENT_ITEM, ANSWER_ONE, None, -1)
@patch.object(Submission.objects, 'filter')
@raises(SubmissionInternalError)
@raises(api.SubmissionInternalError)
def test_error_on_submission_creation(self, mock_filter):
mock_filter.side_effect = DatabaseError("Bad things happened")
create_submission(STUDENT_ITEM, ANSWER_ONE)
api.create_submission(STUDENT_ITEM, ANSWER_ONE)
@patch.object(StudentItemSerializer, 'save')
@raises(SubmissionInternalError)
@raises(api.SubmissionInternalError)
def test_create_student_item_validation(self, mock_save):
mock_save.side_effect = DatabaseError("Bad things happened")
create_submission(STUDENT_ITEM, ANSWER_ONE)
api.create_submission(STUDENT_ITEM, ANSWER_ONE)
def test_unicode_enforcement(self):
create_submission(STUDENT_ITEM, "Testing unicode answers.")
submissions = get_submissions(STUDENT_ITEM, 1)
api.create_submission(STUDENT_ITEM, "Testing unicode answers.")
submissions = api.get_submissions(STUDENT_ITEM, 1)
self.assertEqual(u"Testing unicode answers.", submissions[0]["answer"])
def _assert_submission(self, submission, expected_answer, expected_item,
......@@ -111,4 +116,29 @@ class TestApi(TestCase):
self.assertIsNotNone(submission)
self.assertEqual(submission["answer"], expected_answer)
self.assertEqual(submission["student_item"], expected_item)
self.assertEqual(submission["attempt_number"], expected_attempt)
\ No newline at end of file
self.assertEqual(submission["attempt_number"], expected_attempt)
"""
Testing Scores
"""
def test_create_score(self):
submission = api.create_submission(STUDENT_ITEM, ANSWER_ONE)
self._assert_submission(submission, ANSWER_ONE, 1, 1)
score = api.set_score(STUDENT_ITEM, submission, 11, 12)
self._assert_score(score, 11, 12)
def test_get_score(self):
self.test_create_score()
scores = api.get_score(STUDENT_ITEM)
self._assert_score(scores[0], 11, 12)
def _assert_score(
self,
score,
expected_points_earned,
expected_points_possible):
self.assertIsNotNone(score)
self.assertEqual(score["points_earned"], expected_points_earned)
self.assertEqual(score["points_possible"], expected_points_possible)
\ No newline at end of file
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