Example #1
0
 def test_create_bad_params(self):
     """
     Confirms create will fail if params are missing.
     """
     del self.params["earned_graded"]
     with self.assertRaises(IntegrityError):
         PersistentSubsectionGrade.create_grade(**self.params)
Example #2
0
    def test_grade_override(self):
        """
        Creating a subsection grade override should NOT change the score values
        of the related PersistentSubsectionGrade.
        """
        grade = PersistentSubsectionGrade.update_or_create_grade(**self.params)
        override = PersistentSubsectionGradeOverride.update_or_create_override(
            requesting_user=self.user,
            subsection_grade_model=grade,
            earned_all_override=0.0,
            earned_graded_override=0.0,
            feature=PersistentSubsectionGradeOverrideHistory.GRADEBOOK,
        )

        grade = PersistentSubsectionGrade.update_or_create_grade(**self.params)
        self.assertEqual(self.params['earned_all'], grade.earned_all)
        self.assertEqual(self.params['earned_graded'], grade.earned_graded)

        # Any score values that aren't specified should use the values from grade as defaults
        self.assertEqual(0, override.earned_all_override)
        self.assertEqual(0, override.earned_graded_override)
        self.assertEqual(grade.possible_all, override.possible_all_override)
        self.assertEqual(grade.possible_graded, override.possible_graded_override)

        # An override history record should be created
        self.assertEqual(1, PersistentSubsectionGradeOverrideHistory.objects.filter(override_id=override.id).count())
 def test_grade_override(self):
     grade = PersistentSubsectionGrade.create_grade(**self.params)
     override = PersistentSubsectionGradeOverride(grade=grade, earned_all_override=0.0, earned_graded_override=0.0)
     override.save()
     grade = PersistentSubsectionGrade.update_or_create_grade(**self.params)
     self.assertEqual(grade.earned_all, 0.0)
     self.assertEqual(grade.earned_graded, 0.0)
Example #4
0
 def __init__(self, context, users):
     self.certs = _CertificateBulkContext(context, users)
     self.teams = _TeamBulkContext(context, users)
     self.enrollments = _EnrollmentBulkContext(context, users)
     bulk_cache_cohorts(context.course_id, users)
     BulkRoleCache.prefetch(users)
     PersistentCourseGrade.prefetch(context.course_id, users)
     PersistentSubsectionGrade.prefetch(context.course_id, users)
     BulkCourseTags.prefetch(context.course_id, users)
Example #5
0
    def test_update_or_create_grade(self, already_created):
        created_grade = PersistentSubsectionGrade.create_grade(**self.params) if already_created else None

        self.params["earned_all"] = 7
        updated_grade = PersistentSubsectionGrade.update_or_create_grade(**self.params)
        self.assertEqual(updated_grade.earned_all, 7)
        if already_created:
            self.assertEqual(created_grade.id, updated_grade.id)
            self.assertEqual(created_grade.earned_all, 6)
 def test_logging_for_save(self):
     with patch('lms.djangoapps.grades.models.log') as log_mock:
         PersistentSubsectionGrade.save_grade(**self.params)
         read_grade = PersistentSubsectionGrade.read_grade(
             user_id=self.params["user_id"],
             usage_key=self.params["usage_key"],
         )
         log_mock.info.assert_called_with(
             u"Persistent Grades: Grade model saved: {0}".format(read_grade)
         )
Example #7
0
    def _assert_grades_absent_for_courses(self, course_keys):
        """
        Assert grades for given courses do not exist.
        """
        for course_key in course_keys:
            with self.assertRaises(PersistentCourseGrade.DoesNotExist):
                PersistentCourseGrade.read_course_grade(self.user_ids[0], course_key)

            for subsection_key in self.subsection_keys_by_course[course_key]:
                with self.assertRaises(PersistentSubsectionGrade.DoesNotExist):
                    PersistentSubsectionGrade.read_grade(self.user_ids[0], subsection_key)
Example #8
0
 def test_save(self, already_created):
     if already_created:
         PersistentSubsectionGrade.objects.create(**self.params)
     module_prefix = "lms.djangoapps.grades.models.PersistentSubsectionGrade."
     with patch(
         module_prefix + "objects.get_or_create",
         wraps=PersistentSubsectionGrade.objects.get_or_create
     ) as mock_get_or_create:
         with patch(module_prefix + "update") as mock_update:
             PersistentSubsectionGrade.save_grade(**self.params)
             self.assertTrue(mock_get_or_create.called)
             self.assertEqual(mock_update.called, already_created)
Example #9
0
    def _assert_grades_absent_for_courses(self, course_keys, db_table=None):
        """
        Assert grades for given courses do not exist.
        """
        for course_key in course_keys:
            if db_table == "course" or db_table is None:
                with self.assertRaises(PersistentCourseGrade.DoesNotExist):
                    PersistentCourseGrade.read_course_grade(self.user_ids[0], course_key)

            if db_table == "subsection" or db_table is None:
                for subsection_key in self.subsection_keys_by_course[course_key]:
                    with self.assertRaises(PersistentSubsectionGrade.DoesNotExist):
                        PersistentSubsectionGrade.read_grade(self.user_ids[0], subsection_key)
Example #10
0
def bulk_gradebook_view_context(course_key, users):
    """
    Prefetches all course and subsection grades in the given course for the given
    list of users, also, fetch all the score relavant data,
    storing the result in a RequestCache and deleting grades on context exit.
    """
    PersistentSubsectionGrade.prefetch(course_key, users)
    PersistentCourseGrade.prefetch(course_key, users)
    CourseEnrollment.bulk_fetch_enrollment_states(users, course_key)
    cohorts.bulk_cache_cohorts(course_key, users)
    BulkRoleCache.prefetch(users)
    yield
    PersistentSubsectionGrade.clear_prefetched_data(course_key)
    PersistentCourseGrade.clear_prefetched_data(course_key)
Example #11
0
 def test_create(self):
     """
     Tests model creation, and confirms error when trying to recreate model.
     """
     created_grade = PersistentSubsectionGrade.create_grade(**self.params)
     with self.assertNumQueries(1):
         read_grade = PersistentSubsectionGrade.read_grade(
             user_id=self.params["user_id"],
             usage_key=self.params["usage_key"],
         )
         self.assertEqual(created_grade, read_grade)
         self.assertEqual(read_grade.visible_blocks.blocks, self.block_records)
     with self.assertRaises(IntegrityError):
         PersistentSubsectionGrade.create_grade(**self.params)
    def update(self, subsection, only_if_higher=None, score_deleted=False):
        """
        Updates the SubsectionGrade object for the student and subsection.
        """
        self._log_event(log.debug, u"update, subsection: {}".format(subsection.location), subsection)

        calculated_grade = CreateSubsectionGrade(
            subsection, self.course_data.structure, self._submissions_scores, self._csm_scores,
        )

        if should_persist_grades(self.course_data.course_key):
            if only_if_higher:
                try:
                    grade_model = PersistentSubsectionGrade.read_grade(self.student.id, subsection.location)
                except PersistentSubsectionGrade.DoesNotExist:
                    pass
                else:
                    orig_subsection_grade = ReadSubsectionGrade(subsection, grade_model, self)
                    if not is_score_higher_or_equal(
                            orig_subsection_grade.graded_total.earned,
                            orig_subsection_grade.graded_total.possible,
                            calculated_grade.graded_total.earned,
                            calculated_grade.graded_total.possible,
                    ):
                        return orig_subsection_grade

            grade_model = calculated_grade.update_or_create_model(self.student, score_deleted)
            self._update_saved_subsection_grade(subsection.location, grade_model)

        return calculated_grade
 def setUp(self, **kwargs):
     super(GradesServiceTests, self).setUp()
     self.service = GradesService()
     self.course = CourseFactory.create(org='edX', number='DemoX', display_name='Demo_Course')
     self.subsection = ItemFactory.create(parent=self.course, category="subsection", display_name="Subsection")
     self.user = UserFactory()
     self.grade = PersistentSubsectionGrade.update_or_create_grade(
         user_id=self.user.id,
         course_id=self.course.id,
         usage_key=self.subsection.location,
         first_attempted=None,
         visible_blocks=[],
         earned_all=6.0,
         possible_all=6.0,
         earned_graded=5.0,
         possible_graded=5.0
     )
     self.signal_patcher = patch('lms.djangoapps.grades.signals.signals.SUBSECTION_OVERRIDE_CHANGED.send')
     self.mock_signal = self.signal_patcher.start()
     self.id_patcher = patch('lms.djangoapps.grades.services.create_new_event_transaction_id')
     self.mock_create_id = self.id_patcher.start()
     self.mock_create_id.return_value = 1
     self.type_patcher = patch('lms.djangoapps.grades.services.set_event_transaction_type')
     self.mock_set_type = self.type_patcher.start()
     self.flag_patcher = patch('lms.djangoapps.grades.services.waffle_flags')
     self.mock_waffle_flags = self.flag_patcher.start()
     self.mock_waffle_flags.return_value = {
         REJECTED_EXAM_OVERRIDES_GRADE: MockWaffleFlag(True)
     }
Example #14
0
    def update(self, subsection, only_if_higher=None):
        """
        Updates the SubsectionGrade object for the student and subsection.
        """
        # Save ourselves the extra queries if the course does not persist
        # subsection grades.
        if not PersistentGradesEnabledFlag.feature_enabled(self.course.id):
            return

        self._log_event(log.warning, u"update, subsection: {}".format(subsection.location), subsection)

        calculated_grade = SubsectionGrade(subsection).init_from_structure(
            self.student, self.course_structure, self._submissions_scores, self._csm_scores,
        )

        if only_if_higher:
            try:
                grade_model = PersistentSubsectionGrade.read_grade(self.student.id, subsection.location)
            except PersistentSubsectionGrade.DoesNotExist:
                pass
            else:
                orig_subsection_grade = SubsectionGrade(subsection).init_from_model(
                    self.student, grade_model, self.course_structure, self._submissions_scores, self._csm_scores,
                )
                if not is_score_higher(
                        orig_subsection_grade.graded_total.earned,
                        orig_subsection_grade.graded_total.possible,
                        calculated_grade.graded_total.earned,
                        calculated_grade.graded_total.possible,
                ):
                    return orig_subsection_grade

        grade_model = calculated_grade.update_or_create_model(self.student)
        self._update_saved_subsection_grade(subsection.location, grade_model)
        return calculated_grade
Example #15
0
 def test_update_grade(self):
     """
     Tests model update, and confirms error when updating a nonexistent model.
     """
     with self.assertRaises(PersistentSubsectionGrade.DoesNotExist):
         PersistentSubsectionGrade.update_grade(**self.params)
     PersistentSubsectionGrade.objects.create(**self.params)
     self.params['earned_all'] = 12
     self.params['earned_graded'] = 8
     PersistentSubsectionGrade.update_grade(**self.params)
     read_grade = PersistentSubsectionGrade.read_grade(
         user_id=self.params["user_id"],
         usage_key=self.params["usage_key"],
     )
     self.assertEqual(read_grade.earned_all, 12)
     self.assertEqual(read_grade.earned_graded, 8)
 def update_or_create_model(self, student):
     """
     Saves or updates the subsection grade in a persisted model.
     """
     if self._should_persist_per_attempted:
         self._log_event(log.debug, u"update_or_create_model", student)
         return PersistentSubsectionGrade.update_or_create_grade(**self._persisted_model_params(student))
Example #17
0
 def test_update_or_create_attempted(self, is_active, expected_first_attempted):
     with freeze_time(now()):
         if expected_first_attempted is None:
             expected_first_attempted = now()
         with waffle.waffle().override(waffle.ESTIMATE_FIRST_ATTEMPTED, active=is_active):
             grade = PersistentSubsectionGrade.update_or_create_grade(**self.params)
             self.assertEqual(grade.first_attempted, expected_first_attempted)
Example #18
0
 def _assert_grades_exist_for_courses(self, course_keys):
     """
     Assert grades for given courses exist.
     """
     for course_key in course_keys:
         self.assertIsNotNone(PersistentCourseGrade.read_course_grade(self.user_ids[0], course_key))
         for subsection_key in self.subsection_keys_by_course[course_key]:
             self.assertIsNotNone(PersistentSubsectionGrade.read_grade(self.user_ids[0], subsection_key))
Example #19
0
    def test_update_or_create_grade(self, already_created):
        created_grade = PersistentSubsectionGrade.update_or_create_grade(**self.params) if already_created else None

        self.params["earned_all"] = 7
        updated_grade = PersistentSubsectionGrade.update_or_create_grade(**self.params)
        self.assertEqual(updated_grade.earned_all, 7)
        if already_created:
            self.assertEqual(created_grade.id, updated_grade.id)
            self.assertEqual(created_grade.earned_all, 6)

        with self.assertNumQueries(1):
            read_grade = PersistentSubsectionGrade.read_grade(
                user_id=self.params["user_id"],
                usage_key=self.params["usage_key"],
            )
            self.assertEqual(updated_grade, read_grade)
            self.assertEqual(read_grade.visible_blocks.blocks, self.block_records)
Example #20
0
 def test_persistent_grades_enabled_on_course(self, default_store, num_mongo_queries, num_sql_queries):
     with self.store.default_store(default_store):
         self.set_up_course(enable_persistent_grades=True)
         with check_mongo_calls(num_mongo_queries):
             with self.assertNumQueries(num_sql_queries):
                 self._apply_recalculate_subsection_grade()
         self.assertIsNotNone(PersistentCourseGrade.read(self.user.id, self.course.id))
         self.assertGreater(len(PersistentSubsectionGrade.bulk_read_grades(self.user.id, self.course.id)), 0)
Example #21
0
 def test_unattempted(self):
     self.params['first_attempted'] = None
     self.params['earned_all'] = 0.0
     self.params['earned_graded'] = 0.0
     grade = PersistentSubsectionGrade.create_grade(**self.params)
     self.assertIsNone(grade.first_attempted)
     self.assertEqual(grade.earned_all, 0.0)
     self.assertEqual(grade.earned_graded, 0.0)
Example #22
0
 def bulk_create_models(cls, student, subsection_grades, course_key):
     """
     Saves the subsection grade in a persisted model.
     """
     return PersistentSubsectionGrade.bulk_create_grades(
         [subsection_grade._persisted_model_params(student) for subsection_grade in subsection_grades],  # pylint: disable=protected-access
         course_key,
     )
Example #23
0
 def test_persistent_grades_not_enabled_on_course(self, default_store, num_mongo_queries, num_sql_queries):
     with self.store.default_store(default_store):
         self.set_up_course(enable_persistent_grades=False)
         with check_mongo_calls(num_mongo_queries):
             with self.assertNumQueries(num_sql_queries):
                 self._apply_recalculate_subsection_grade()
         with self.assertRaises(PersistentCourseGrade.DoesNotExist):
             PersistentCourseGrade.read(self.user.id, self.course.id)
         self.assertEqual(len(PersistentSubsectionGrade.bulk_read_grades(self.user.id, self.course.id)), 0)
 def bulk_create_models(cls, student, subsection_grades, course_key):
     """
     Saves the subsection grade in a persisted model.
     """
     params = [
         subsection_grade._persisted_model_params(student) for subsection_grade in subsection_grades  # pylint: disable=protected-access
         if subsection_grade._should_persist_per_attempted()  # pylint: disable=protected-access
     ]
     return PersistentSubsectionGrade.bulk_create_grades(params, course_key)
 def bulk_create_models(cls, student, subsection_grades, course_key):
     """
     Saves the subsection grade in a persisted model.
     """
     subsection_grades = filter(lambda subs_grade: subs_grade._should_persist_per_attempted, subsection_grades)
     return PersistentSubsectionGrade.bulk_create_grades(
         [subsection_grade._persisted_model_params(student) for subsection_grade in subsection_grades],  # pylint: disable=protected-access
         course_key,
     )
Example #26
0
 def _assert_grades_exist_for_courses(self, course_keys, db_table=None):
     """
     Assert grades for given courses exist.
     """
     for course_key in course_keys:
         if db_table == "course" or db_table is None:
             self.assertIsNotNone(PersistentCourseGrade.read_course_grade(self.user_ids[0], course_key))
         if db_table == "subsection" or db_table is None:
             for subsection_key in self.subsection_keys_by_course[course_key]:
                 self.assertIsNotNone(PersistentSubsectionGrade.read_grade(self.user_ids[0], subsection_key))
Example #27
0
    def _update_or_create_grades(self, courses_keys=None):
        """
        Creates grades for all courses and subsections.
        """
        if courses_keys is None:
            courses_keys = self.course_keys

        course_grade_params = {
            "course_version": "JoeMcEwing",
            "course_edited_timestamp": datetime(
                year=2016,
                month=8,
                day=1,
                hour=18,
                minute=53,
                second=24,
                microsecond=354741,
            ),
            "percent_grade": 77.7,
            "letter_grade": "Great job",
            "passed": True,
        }
        subsection_grade_params = {
            "course_version": "deadbeef",
            "subtree_edited_timestamp": "2016-08-01 18:53:24.354741",
            "earned_all": 6.0,
            "possible_all": 12.0,
            "earned_graded": 6.0,
            "possible_graded": 8.0,
            "visible_blocks": MagicMock(),
            "attempted": True,
        }

        for course_key in courses_keys:
            for user_id in self.user_ids:
                course_grade_params['user_id'] = user_id
                course_grade_params['course_id'] = course_key
                PersistentCourseGrade.update_or_create_course_grade(**course_grade_params)
                for subsection_key in self.subsection_keys_by_course[course_key]:
                    subsection_grade_params['user_id'] = user_id
                    subsection_grade_params['usage_key'] = subsection_key
                    PersistentSubsectionGrade.update_or_create_grade(**subsection_grade_params)
Example #28
0
 def _get_bulk_cached_subsection_grades(self):
     """
     Returns and caches (for future access) the results of
     a bulk retrieval of all subsection grades in the course.
     """
     if self._cached_subsection_grades is None:
         self._cached_subsection_grades = {
             record.full_usage_key: record
             for record in PersistentSubsectionGrade.bulk_read_grades(self.student.id, self.course.id)
         }
     return self._cached_subsection_grades
    def save(self, student, subsection, course):
        """
        Persist the SubsectionGrade.
        """
        visible_blocks = [
            BlockRecord(location, weight, score.possible)
            for location, (score, weight) in self.locations_to_weighted_scores.iteritems()
        ]

        PersistentSubsectionGrade.save_grade(
            user_id=student.id,
            usage_key=self.location,
            course_version=getattr(course, 'course_version', None),
            subtree_edited_timestamp=subsection.subtree_edited_on,
            earned_all=self.all_total.earned,
            possible_all=self.all_total.possible,
            earned_graded=self.graded_total.earned,
            possible_graded=self.graded_total.possible,
            visible_blocks=visible_blocks,
        )
 def _get_saved_subsection_grade(self, subsection_usage_key):
     """
     Returns the saved value of the subsection grade for
     the given subsection usage key, caching the value.
     Returns None if not found.
     """
     if self._cached_subsection_grades is None:
         self._cached_subsection_grades = {
             record.full_usage_key: record
             for record in PersistentSubsectionGrade.bulk_read_grades(self.student.id, self.course.id)
         }
     return self._cached_subsection_grades.get(subsection_usage_key)
Example #31
0
    def test_override_is_created(self):
        """
        Test that when we make multiple requests to update grades for the same user/subsection,
        the score from the most recent request is recorded.
        """
        with override_waffle_flag(self.waffle_flag, active=True):
            self.login_staff()
            post_data = [{
                'user_id':
                self.student.id,
                'usage_id':
                text_type(
                    self.subsections[self.chapter_1.location][0].location),
                'grade': {
                    'earned_all_override': 3,
                    'possible_all_override': 3,
                    'earned_graded_override': 2,
                    'possible_graded_override': 2,
                },
            }, {
                'user_id':
                self.student.id,
                'usage_id':
                text_type(
                    self.subsections[self.chapter_1.location][1].location),
                'grade': {
                    'earned_all_override': 1,
                    'possible_all_override': 4,
                    'earned_graded_override': 1,
                    'possible_graded_override': 4,
                },
            }]

            resp = self.client.post(
                self.get_url(),
                data=json.dumps(post_data),
                content_type='application/json',
            )

            expected_data = [
                {
                    'user_id':
                    self.student.id,
                    'usage_id':
                    text_type(
                        self.subsections[self.chapter_1.location][0].location),
                    'success':
                    True,
                    'reason':
                    None,
                },
                {
                    'user_id':
                    self.student.id,
                    'usage_id':
                    text_type(
                        self.subsections[self.chapter_1.location][1].location),
                    'success':
                    True,
                    'reason':
                    None,
                },
            ]
            self.assertEqual(status.HTTP_202_ACCEPTED, resp.status_code)
            self.assertEqual(expected_data, resp.data)

            second_post_data = [
                {
                    'user_id':
                    self.student.id,
                    'usage_id':
                    text_type(
                        self.subsections[self.chapter_1.location][1].location),
                    'grade': {
                        'earned_all_override': 3,
                        'possible_all_override': 4,
                        'earned_graded_override': 3,
                        'possible_graded_override': 4,
                    },
                },
            ]

            self.client.post(
                self.get_url(),
                data=json.dumps(second_post_data),
                content_type='application/json',
            )

            GradeFields = namedtuple('GradeFields', [
                'earned_all', 'possible_all', 'earned_graded',
                'possible_graded'
            ])

            # We should now have PersistentSubsectionGradeOverride records corresponding to
            # our bulk-update request, and PersistentSubsectionGrade records with grade values
            # equal to those of the override.
            for usage_key, expected_grades in (
                (self.subsections[self.chapter_1.location][0].location,
                 GradeFields(3, 3, 2, 2)),
                (self.subsections[self.chapter_1.location][1].location,
                 GradeFields(3, 4, 3, 4)),
            ):
                # this selects related PersistentSubsectionGradeOverride objects.
                grade = PersistentSubsectionGrade.read_grade(
                    user_id=self.student.id,
                    usage_key=usage_key,
                )
                for field_name in expected_grades._fields:
                    expected_value = getattr(expected_grades, field_name)
                    self.assertEqual(expected_value,
                                     getattr(grade, field_name))
                    self.assertEqual(
                        expected_value,
                        getattr(grade.override, field_name + '_override'))
def clear_prefetched_course_grades(course_key):
    _PersistentCourseGrade.clear_prefetched_data(course_key)
    _PersistentSubsectionGrade.clear_prefetched_data(course_key)
def prefetch_course_and_subsection_grades(course_key, users):
    _PersistentCourseGrade.prefetch(course_key, users)
    _PersistentSubsectionGrade.prefetch(course_key, users)
Example #34
0
 def update_or_create_model(self, student):
     """
     Saves or updates the subsection grade in a persisted model.
     """
     self._log_event(log.info, u"update_or_create_model", student)
     return PersistentSubsectionGrade.update_or_create_grade(**self._persisted_model_params(student))
    def get(self, request, subsection_id):
        """
        Returns subection grade data, override grade data and a history of changes made to
        a specific users specific subsection grade.

        Args:
            subsection_id: String representation of a usage_key, which is an opaque key of
            a persistant subection grade.
            user_id: An integer represenation of a user

        """
        try:
            usage_key = UsageKey.from_string(subsection_id)
        except InvalidKeyError:
            raise self.api_error(status_code=status.HTTP_404_NOT_FOUND,
                                 developer_message='Invalid UsageKey',
                                 error_code='invalid_usage_key')

        if not has_course_author_access(request.user, usage_key.course_key):
            raise DeveloperErrorViewMixin.api_error(
                status_code=status.HTTP_403_FORBIDDEN,
                developer_message=
                'The requesting user does not have course author permissions.',
                error_code='user_permissions',
            )

        try:
            user_id = int(request.GET.get('user_id'))
        except ValueError:
            raise self.api_error(status_code=status.HTTP_404_NOT_FOUND,
                                 developer_message='Invalid UserID',
                                 error_code='invalid_user_id')

        try:
            original_grade = PersistentSubsectionGrade.read_grade(
                user_id, usage_key)
        except PersistentSubsectionGrade.DoesNotExist:
            results = SubsectionGradeResponseSerializer({
                'original_grade': None,
                'override': None,
                'history': [],
                'subsection_id': usage_key,
                'user_id': user_id,
                'course_id': None,
            })

            return Response(results.data)

        try:
            override = original_grade.override
            history = PersistentSubsectionGradeOverrideHistory.objects.filter(
                override_id=override.id)
        except PersistentSubsectionGradeOverride.DoesNotExist:
            override = None
            history = []

        results = SubsectionGradeResponseSerializer({
            'original_grade':
            original_grade,
            'override':
            override,
            'history':
            history,
            'subsection_id':
            original_grade.usage_key,
            'user_id':
            original_grade.user_id,
            'course_id':
            original_grade.course_id,
        })

        return Response(results.data)
Example #36
0
 def test_first_attempted_not_changed_on_update(self):
     PersistentSubsectionGrade.update_or_create_grade(**self.params)
     moment = now()
     grade = PersistentSubsectionGrade.update_or_create_grade(**self.params)
     self.assertLess(grade.first_attempted, moment)
Example #37
0
 def test_update_or_create_inconsistent_unattempted(self):
     self.params['attempted'] = False
     self.params['earned_all'] = 1.0
     self.params['earned_graded'] = 1.0
     with self.assertRaises(ValidationError):
         PersistentSubsectionGrade.update_or_create_grade(**self.params)
Example #38
0
 def test_unattempted_save_does_not_remove_attempt(self):
     PersistentSubsectionGrade.update_or_create_grade(**self.params)
     self.params['first_attempted'] = None
     grade = PersistentSubsectionGrade.update_or_create_grade(**self.params)
     self.assertIsInstance(grade.first_attempted, datetime)
     self.assertEqual(grade.earned_all, 6.0)
Example #39
0
 def test_non_optional_fields(self, field, error):
     del self.params[field]
     with self.assertRaises(error):
         PersistentSubsectionGrade.create_grade(**self.params)
Example #40
0
 def test_update_or_create_attempted(self):
     grade = PersistentSubsectionGrade.update_or_create_grade(**self.params)
     self.assertIsInstance(grade.first_attempted, datetime)
Example #41
0
 def test_create_inconsistent_unattempted(self):
     self.params['attempted'] = False
     with self.assertRaises(ValidationError):
         PersistentSubsectionGrade.create_grade(**self.params)
Example #42
0
 def test_create_event(self):
     with patch('lms.djangoapps.grades.events.tracker') as tracker_mock:
         grade = PersistentSubsectionGrade.update_or_create_grade(**self.params)
     self._assert_tracker_emitted_event(tracker_mock, grade)
Example #43
0
    def get(self, request, subsection_id):
        """
        Returns subection grade data, override grade data and a history of changes made to
        a specific users specific subsection grade.

        Args:
            subsection_id: String representation of a usage_key, which is an opaque key of
            a persistant subection grade.
            user_id: An integer represenation of a user

        """
        try:
            usage_key = UsageKey.from_string(subsection_id)
        except InvalidKeyError:
            raise self.api_error(status_code=status.HTTP_404_NOT_FOUND,
                                 developer_message='Invalid UsageKey',
                                 error_code='invalid_usage_key')

        if not has_course_author_access(request.user, usage_key.course_key):
            raise DeveloperErrorViewMixin.api_error(
                status_code=status.HTTP_403_FORBIDDEN,
                developer_message=
                'The requesting user does not have course author permissions.',
                error_code='user_permissions',
            )

        try:
            user_id = int(request.GET.get('user_id'))
        except ValueError:
            raise self.api_error(status_code=status.HTTP_404_NOT_FOUND,
                                 developer_message='Invalid UserID',
                                 error_code='invalid_user_id')
        success = True
        err_msg = ""
        override = None
        history = []
        history_record_limit = request.GET.get('history_record_limit')
        if history_record_limit is not None:
            try:
                history_record_limit = int(history_record_limit)
            except ValueError:
                history_record_limit = 0

        try:
            original_grade = PersistentSubsectionGrade.read_grade(
                user_id, usage_key)
            if original_grade is not None and hasattr(original_grade,
                                                      'override'):
                override = original_grade.override
                # pylint: disable=no-member
                history = list(
                    PersistentSubsectionGradeOverride.history.filter(
                        grade_id=original_grade.id).order_by('history_date')
                    [:history_record_limit])
            grade_data = {
                'earned_all': original_grade.earned_all,
                'possible_all': original_grade.possible_all,
                'earned_graded': original_grade.earned_graded,
                'possible_graded': original_grade.possible_graded,
            }
        except PersistentSubsectionGrade.DoesNotExist:
            try:
                grade_data = self._get_grade_data_for_not_attempted_assignment(
                    user_id, usage_key)
            except SubsectionUnavailableToUserException as exc:
                success = False
                err_msg = str(exc)
                grade_data = {
                    'earned_all': 0,
                    'possible_all': 0,
                    'earned_graded': 0,
                    'possible_graded': 0,
                }

        response_data = {
            'success': success,
            'original_grade': grade_data,
            'override': override,
            'history': history,
            'subsection_id': usage_key,
            'user_id': user_id,
            'course_id': usage_key.course_key,
        }
        if not success:
            response_data['error_message'] = err_msg
        results = SubsectionGradeResponseSerializer(response_data)
        return Response(results.data)
Example #44
0
 def create_model(self, student):
     """
     Saves the subsection grade in a persisted model.
     """
     self._log_event(log.debug, u"create_model", student)
     return PersistentSubsectionGrade.create_grade(**self._persisted_model_params(student))
Example #45
0
 def test_course_version_is_optional(self):
     del self.params["course_version"]
     PersistentSubsectionGrade.create_grade(**self.params)
Example #46
0
 def test_non_optional_fields(self, field, error):
     del self.params[field]
     with pytest.raises(error):
         PersistentSubsectionGrade.update_or_create_grade(**self.params)
Example #47
0
 def test_optional_fields(self, field):
     del self.params[field]
     PersistentSubsectionGrade.update_or_create_grade(**self.params)
 def test_update_or_create_attempted(self, is_active, expected_first_attempted):
     if expected_first_attempted is None:
         expected_first_attempted = now()
     with waffle.waffle().override(waffle.ESTIMATE_FIRST_ATTEMPTED, active=is_active):
         grade = PersistentSubsectionGrade.update_or_create_grade(**self.params)
         self.assertEqual(grade.first_attempted, expected_first_attempted)