def test_validating_completions(self): course_key = CourseKey.from_string('course-v1:abc+def+ghi') other_course_key = CourseKey.from_string('course-v1:ihg+fed+cba') other_user = User.objects.create(username='******') aggregators = [ models.Aggregator.objects.submit_completion( user=other_user, course_key=course_key, block_key=course_key.make_usage_key(block_type='course', block_id='course'), aggregation_name='course', earned=4.0, possible=4.0, last_modified=timezone.now(), )[0], models.Aggregator.objects.submit_completion( user=self.test_user, course_key=other_course_key, block_key=other_course_key.make_usage_key(block_type='course', block_id='course'), aggregation_name='course', earned=1.0, possible=3.0, last_modified=timezone.now(), )[0], ] for agg in aggregators: with pytest.raises(ValueError): serializers.AggregatorAdapter( user=self.test_user, course_key=course_key, aggregators=[agg], )
def test_simple_aggregation_structure(self): course_completion, _ = models.Aggregator.objects.submit_completion( user=self.test_user, course_key=self.course_key, block_key=self.course_key.make_usage_key(block_type='course', block_id='crs'), aggregation_name='course', earned=4.2, possible=9.6, last_modified=timezone.now(), ) sequential_completion, _ = models.Aggregator.objects.submit_completion( user=self.test_user, course_key=self.course_key, block_key=self.course_key.make_usage_key(block_type='sequential', block_id='chap1'), aggregation_name='sequential', earned=1.8, possible=3.4, last_modified=timezone.now(), ) agstruct = serializers.AggregatorAdapter( user=self.test_user, course_key=self.course_key, ) agstruct.add_aggregator(course_completion) agstruct.update_aggregators([sequential_completion]) self.assertEqual(agstruct.course, course_completion) self.assertEqual(agstruct.sequential, [sequential_completion])
def test_mean(self): course_key = CourseKey.from_string('course-v1:abc+def+ghi') data = [ # earned, possible (5., 20.), (7., 14.), (3., 9.), (8., 14.), ] expected_mean = sum(item[0] / item[1] for item in data) / 5. completions = [ models.Aggregator.objects.submit_completion( user=self.test_user, course_key=course_key, block_key=course_key.make_usage_key( block_type='course', block_id='course{}'.format(idx)), aggregation_name='course', earned=data[idx][0], possible=data[idx][1], last_modified=timezone.now(), )[0] for idx in range(4) ] serial = _course_completion_serializer_factory(['mean'])( serializers.AggregatorAdapter(user=self.test_user, course_key=course_key, aggregators=completions), requested_fields={'mean'}) self.assertAlmostEqual(serial.data['mean'], expected_mean)
def test_mean_with_no_completions(self): # A course that has no aggregators recorded. course_key = CourseKey.from_string('course-v1:abc+def+ghj') serial = _course_completion_serializer_factory(['mean'])( serializers.AggregatorAdapter( user=self.test_user, course_key=course_key, aggregators=[], ), requested_fields={'mean'}) self.assertAlmostEqual(serial.data['mean'], 0)
def test_aggregation_without_completions(self): course_key = CourseKey.from_string('course-v1:abc+def+ghi') serial = _course_completion_serializer_factory([])( serializers.AggregatorAdapter(user=self.test_user, course_key=course_key, aggregators=[])) self.assertEqual( serial.data['completion'], { 'earned': 0.0, 'possible': None, 'percent': 0.0, }, )
def test_filtering_completions(self): course_key = CourseKey.from_string('course-v1:abc+def+ghi') aggregator = models.Aggregator.objects.submit_completion( user=self.test_user, course_key=course_key, block_key=course_key.make_usage_key(block_type='video', block_id='neatvideo'), aggregation_name='video', earned=1.0, possible=2.0, last_modified=timezone.now(), )[0] adapted = serializers.AggregatorAdapter( user=self.test_user, course_key=course_key, aggregators=[aggregator], ) assert not adapted.aggregators
def test_invalid_aggregator(self): course_key = CourseKey.from_string('course-v1:abc+def+ghi') completion, _ = models.Aggregator.objects.submit_completion( user=self.test_user, course_key=course_key, block_key=course_key.make_usage_key(block_type='course', block_id='course'), aggregation_name='course', earned=0.0, possible=0.0, last_modified=timezone.now(), ) agg = serializers.AggregatorAdapter(user=self.test_user, course_key=course_key, aggregators=[completion]) # coverage demands this, because we have __getattr__ overridden with self.assertRaises(AttributeError): agg.something_that_doesnt_exist # pylint: disable=pointless-statement
def test_zero_possible(self): course_key = CourseKey.from_string('course-v1:abc+def+ghi') completion, _ = models.Aggregator.objects.submit_completion( user=self.test_user, course_key=course_key, block_key=course_key.make_usage_key(block_type='course', block_id='course'), aggregation_name='course', earned=0.0, possible=0.0, last_modified=timezone.now(), ) serial = _course_completion_serializer_factory([])( serializers.AggregatorAdapter(user=self.test_user, course_key=course_key, aggregators=[completion])) self.assertEqual( serial.data['completion'], { 'earned': 0.0, 'possible': 0.0, 'percent': 1.0, }, )
def assert_serialized_completions(self, serializer_cls_args, extra_body, recalc_stale): """ Ensures that the course completion serializer returns the expected results for this set of submitted completions. """ serializer_cls = _course_completion_serializer_factory( serializer_cls_args) aggregators = [ models.Aggregator.objects.submit_completion( user=self.test_user, course_key=self.course_key, aggregation_name='course', block_key=self.course_key.make_usage_key(block_type='course', block_id='course'), earned=16.0, possible=19.0, last_modified=timezone.now(), )[0], models.Aggregator.objects.submit_completion( user=self.test_user, course_key=self.course_key, aggregation_name='sequential', block_key=self.course_key.make_usage_key( block_type='sequential', block_id='seq1'), earned=6.0, possible=7.0, last_modified=timezone.now(), )[0], models.Aggregator.objects.submit_completion( user=self.test_user, course_key=self.course_key, aggregation_name='sequential', block_key=self.course_key.make_usage_key( block_type='sequential', block_id='seq2'), earned=10.0, possible=12.0, last_modified=timezone.now(), )[0], ] is_stale = recalc_stale and models.StaleCompletion.objects.filter( username=self.test_user.username, course_key=self.course_key, resolved=False) completion = serializers.AggregatorAdapter( user=self.test_user, course_key=self.course_key, aggregators=aggregators, recalculate_stale=recalc_stale, ) serial = serializer_cls(completion) expected = { 'course_key': str(self.course_key), 'completion': { 'earned': 0.0 if is_stale else 16.0, 'possible': None if is_stale else 19.0, 'percent': 0.0 if is_stale else 16 / 19, }, } expected.update(extra_body) # Need to allow for rounding error when retrieving the percent from the test database self.assertEqual(serial.data['course_key'], expected['course_key']) self.assertEqual(serial.data['completion']['earned'], expected['completion']['earned']) self.assertEqual(serial.data['completion']['possible'], expected['completion']['possible']) self.assertAlmostEqual(serial.data['completion']['possible'], expected['completion']['possible'], places=14)