def test_learning_context_key(self):
        """
        Test CourseKey
        """
        key = LearningContextKey.from_string('org.id/course_id/run')
        self.assertEqual(key.org, 'org.id')
        self.assertIsInstance(key, CourseKey)

        key = LearningContextKey.from_string('course-v1:org.id+course_id+run')
        self.assertEqual(key.org, 'org.id')
        self.assertIsInstance(key, CourseKey)
Example #2
0
def enqueue_subsection_update(sender, **kwargs):  # pylint: disable=unused-argument
    """
    Handles the PROBLEM_WEIGHTED_SCORE_CHANGED or SUBSECTION_OVERRIDE_CHANGED signals by
    enqueueing a subsection update operation to occur asynchronously.
    """
    events.grade_updated(**kwargs)
    context_key = LearningContextKey.from_string(kwargs['course_id'])
    if not context_key.is_course:
        return  # If it's not a course, it has no subsections, so skip the subsection grading update
    recalculate_subsection_grade_v3.apply_async(
        kwargs=dict(
            user_id=kwargs['user_id'],
            anonymous_user_id=kwargs.get('anonymous_user_id'),
            course_id=kwargs['course_id'],
            usage_id=kwargs['usage_id'],
            only_if_higher=kwargs.get('only_if_higher'),
            expected_modified_time=to_timestamp(kwargs['modified']),
            score_deleted=kwargs.get('score_deleted', False),
            event_transaction_id=six.text_type(get_event_transaction_id()),
            event_transaction_type=six.text_type(get_event_transaction_type()),
            score_db_table=kwargs['score_db_table'],
            force_update_subsections=kwargs.get('force_update_subsections', False),
        ),
        countdown=RECALCULATE_GRADE_DELAY_SECONDS,
    )
Example #3
0
    def test_render_blockcompletion(self):
        """
            Test get data with block completion
        """
        context_key = LearningContextKey.from_string(str(self.course.id))
        for item in self.items:
            usage_key = item.scope_ids.usage_id
            completion = models.BlockCompletion.objects.create(
                user=self.student,
                context_key=context_key,
                block_key=usage_key,
                completion=1.0,
            )

        url = '{}?is_bigcourse=0'.format(
            reverse('completion_data_view',
                    kwargs={'course_id': self.course.id}))
        self.response = self.staff_client.get(url)
        data = json.loads(self.response.content.decode())
        self.assertEqual(data['data'], [[False]])

        self.response = self.staff_client.get(url)
        self.assertEqual(self.response.status_code, 200)
        data = json.loads(self.response.content.decode())
        self.assertEqual(len(data['data']), 12)
        self.assertEqual(
            data['data'][-1],
            ['*****@*****.**', 'student', '', '✔', '1/1', '1/1', 'No'])
Example #4
0
    def test_render_data_big_course(self):
        """
            Test get data normal process when is big course
        """
        context_key = LearningContextKey.from_string(str(self.course.id))
        for item in self.items:
            usage_key = item.scope_ids.usage_id
            completion = models.BlockCompletion.objects.create(
                user=self.student,
                context_key=context_key,
                block_key=usage_key,
                completion=1.0,
            )
        url = '{}?is_bigcourse=1'.format(
            reverse('completion_data_view',
                    kwargs={'course_id': self.course.id}))
        self.response = self.staff_client.get(url)
        data = json.loads(self.response.content.decode())
        self.assertEqual(data['data'], [[False]])

        self.response = self.staff_client.get(url)
        self.assertEqual(self.response.status_code, 200)
        data = json.loads(self.response.content.decode())
        self.assertEqual(len(data['data']), 12)
        self.assertEqual(data['data'][-1][0], self.student.username)
        self.assertEqual(data['data'][-1][1], '')
        self.assertEqual(data['data'][-1][2], self.student.email)
        self.assertEqual(data['data'][-1][3],
                         completion.modified.strftime("%d/%m/%Y, %H:%M:%S"))
Example #5
0
 def _validate_and_parse_context_key(self, context_key):
     """
     Returns a validated parsed LearningContextKey deserialized from the given context_key.
     """
     try:
         return LearningContextKey.from_string(context_key)
     except InvalidKeyError:
         raise ValidationError(_("Invalid learning context key: {}").format(context_key))
 def test_pathway_key_parsing(self):
     """
     Test parsing of pathway keys
     """
     key_str = 'lx-pathway:00000000-e4fe-47af-8ff6-123456789000'
     key = LearningContextKey.from_string(key_str)
     self.assertEqual(str(key), key_str)
     self.assertIsInstance(key, PathwayLocator)
Example #7
0
    def get(self, request, username, course_key, subsection_id):
        """
        Returns completion for a (user, subsection, course).
        """

        def get_completion(course_completions, all_blocks, block_id):
            """
            Recursively get the aggregate completion for a subsection,
            given the subsection block and a list of all blocks.

            Parameters:
                course_completions: a dictionary of completion values by block IDs
                all_blocks: a dictionary of the block structure for a subsection
                block_id: an ID of a block for which to get completion
            """
            block = all_blocks.get(block_id)
            child_ids = block.get('children', [])
            if not child_ids:
                return course_completions.get(block.serializer.instance, 0)

            completion = 0
            total_children = 0
            for child_id in child_ids:
                completion += get_completion(course_completions, all_blocks, child_id)
                total_children += 1

            return int(completion == total_children)

        user_id = User.objects.get(username=username).id
        block_types_filter = [
            'course',
            'chapter',
            'sequential',
            'vertical',
            'html',
            'problem',
            'video',
            'discussion',
            'drag-and-drop-v2'
        ]

        blocks = get_blocks(
            request,
            UsageKey.from_string(subsection_id),
            nav_depth=2,
            requested_fields=[
                'children'
            ],
            block_types_filter=block_types_filter
        )

        course_key = LearningContextKey.from_string(course_key)
        context_completions = BlockCompletion.get_learning_context_completions(user_id, course_key)
        aggregated_completion = get_completion(context_completions, blocks['blocks'], blocks['root'])

        return Response({"completion": aggregated_completion}, status=status.HTTP_200_OK)
Example #8
0
 def test_from_string_inheritance(self):
     """
     Test that CourseKey.from_string(...) will never give you a library key
     unexpectedly, but LearningContextKey.from_string(...) will give you
     either.
     """
     lib_string = 'lib:MITx:reallyhardproblems'
     course_string = 'course-v1:org+course+run'
     # This should not work because lib_string is not a course key:
     with self.assertRaises(InvalidKeyError):
         CourseKey.from_string(lib_string)
     # But this should work:
     self.assertIsInstance(
         LearningContextKey.from_string(lib_string),
         LibraryLocatorV2,
     )
     # And this should work:
     self.assertIsInstance(
         LearningContextKey.from_string(course_string),
         CourseLocator,
     )
Example #9
0
    def get_block(self, students_id, course_key):
        """
            Get all completed students block
        """
        context_key = LearningContextKey.from_string(str(course_key))
        aux_blocks = BlockCompletion.objects.filter(
            user_id__in=students_id,
            context_key=context_key,
            completion=1.0).values(
            'user_id',
            'block_key')
        blocks = defaultdict(list)
        for b in aux_blocks:
            blocks[b['user_id']].append(b['block_key'])

        return blocks
Example #10
0
def score_changed_handler(sender, **kwargs):  # pylint: disable=unused-argument
    """
    Consume signals that indicate score changes. See the definition of
    PROBLEM_WEIGHTED_SCORE_CHANGED for a description of the signal.
    """
    points_possible = kwargs.get('weighted_possible', None)
    points_earned = kwargs.get('weighted_earned', None)
    user_id = kwargs.get('user_id', None)
    course_id = kwargs.get('course_id', None)
    usage_id = kwargs.get('usage_id', None)

    # Make sure this came from a course because this code only works with courses
    if not course_id:
        return
    context_key = LearningContextKey.from_string(course_id)
    if not context_key.is_course:
        return  # This is a content library or something else...

    if None not in (points_earned, points_possible, user_id, course_id):
        course_key, usage_key = parse_course_and_usage_keys(
            course_id, usage_id)
        assignments = increment_assignment_versions(course_key, usage_key,
                                                    user_id)
        for assignment in assignments:
            if assignment.usage_key == usage_key:
                send_leaf_outcome.delay(assignment.id, points_earned,
                                        points_possible)
            else:
                send_composite_outcome.apply_async(
                    (user_id, course_id, assignment.id,
                     assignment.version_number),
                    countdown=settings.LTI_AGGREGATE_SCORE_PASSBACK_DELAY)
    else:
        log.error(
            u"Outcome Service: Required signal parameter is None. "
            u"points_possible: %s, points_earned: %s, user_id: %s, "
            u"course_id: %s, usage_id: %s", points_possible, points_earned,
            user_id, course_id, usage_id)
Example #11
0
    def get_context_big_course(self, course_key):
        """
            Return eol completion data
        """
        context_key = LearningContextKey.from_string(str(course_key))
        aux_block_completions = BlockCompletion.objects.filter(context_key=context_key).values('user').annotate(last_completed=Max('modified'))
        last_block_completions= {}
        for x in aux_block_completions:
            last_block_completions[x['user']] = x['last_completed']
        try:
            enrolled_students = User.objects.filter(
                    courseenrollment__course_id=course_key,
                    courseenrollment__is_active=1,
                    courseenrollment__mode='honor'
                ).order_by('username').values('id', 'username', 'email', 'edxloginuser__run', 'last_login')

            context = [
                [x['username'], 
                x['edxloginuser__run'] if x['edxloginuser__run'] else '', 
                x['email'], 
                last_block_completions[x['id']].strftime("%d/%m/%Y, %H:%M:%S") if x['id'] in last_block_completions else '', 
                x['last_login'].strftime("%d/%m/%Y, %H:%M:%S") if x['last_login'] else ''] for x in enrolled_students
            ]
        except FieldError:
            enrolled_students = User.objects.filter(
                    courseenrollment__course_id=course_key,
                    courseenrollment__is_active=1,
                    courseenrollment__mode='honor'
                ).order_by('username').values('id', 'username', 'email', 'last_login')
            context = [
                [x['username'], 
                x['email'], 
                last_block_completions[x['id']].strftime("%d/%m/%Y, %H:%M:%S") if x['id'] in last_block_completions else '',
                x['last_login'].strftime("%d/%m/%Y, %H:%M:%S") if x['last_login'] else ''] for x in enrolled_students
            ]
        if len(context) == 0:
            context = [[True]]
        return {'data': context}
Example #12
0
 def test_usage_key_parsing(self):
     """
     Test parsing of pathway usage keys
     """
     parent_key = LearningContextKey.from_string('lx-pathway:00000000-e4fe-47af-8ff6-123456789000')
     # Key of a normal top-level block in a pathway:
     key_str = 'lx-pb:00000000-e4fe-47af-8ff6-123456789000:unit:0ff24589'
     key = UsageKey.from_string(key_str)
     self.assertEqual(str(key), key_str)
     self.assertIsInstance(key, PathwayUsageLocator)
     self.assertEqual(key.context_key, parent_key)
     self.assertEqual(key, UsageKey.from_string(key_str))  # self equality
     # Key of a child block in a pathway:
     key_str = 'lx-pb:00000000-e4fe-47af-8ff6-123456789000:problem:0ff24589:1-2'
     key = UsageKey.from_string(key_str)
     self.assertEqual(str(key), key_str)
     self.assertIsInstance(key, PathwayUsageLocator)
     self.assertEqual(key.context_key, parent_key)
     self.assertEqual(key, UsageKey.from_string(key_str))  # self equality
     self.assertNotEqual(
         UsageKey.from_string('lx-pb:00000000-e4fe-47af-8ff6-123456789000:unit:0ff24589'),
         UsageKey.from_string('lx-pb:00000000-e4fe-47af-8ff6-123456789000:unit:0ff24589:1-2'),
     )
Example #13
0
def scorable_block_completion(sender, **kwargs):  # pylint: disable=unused-argument
    """
    When a problem is scored, submit a new BlockCompletion for that block.
    """
    if not waffle.waffle().is_enabled(waffle.ENABLE_COMPLETION_TRACKING):
        return
    try:
        block_key = UsageKey.from_string(kwargs['usage_id'])
    except InvalidKeyError:
        log.exception("Unable to parse XBlock usage_id for completion: %s",
                      block_key)
        return

    if block_key.context_key.is_course and block_key.context_key.run is None:
        # In the case of old mongo courses, the context_key cannot be derived
        # from the block key alone since it will be missing run info:
        course_key_with_run = LearningContextKey.from_string(
            kwargs['course_id'])
        block_key = block_key.replace(course_key=course_key_with_run)

    block_cls = XBlock.load_class(block_key.block_type)
    if XBlockCompletionMode.get_mode(
            block_cls) != XBlockCompletionMode.COMPLETABLE:
        return
    if getattr(block_cls, 'has_custom_completion', False):
        return
    user = User.objects.get(id=kwargs['user_id'])
    if kwargs.get('score_deleted'):
        completion = 0.0
    else:
        completion = 1.0
    if not kwargs.get('grader_response'):
        BlockCompletion.objects.submit_completion(
            user=user,
            block_key=block_key,
            completion=completion,
        )
Example #14
0
    def test_render_data_with_rut_big_course(self):
        """
            Test get data normal process with edxloginuser when is big course
        """
        try:
            from unittest.case import SkipTest
            from uchileedxlogin.models import EdxLoginUser
        except ImportError:
            self.skipTest("import error uchileedxlogin")
        edxlogin = EdxLoginUser.objects.create(user=self.student,
                                               run='000000001K')
        context_key = LearningContextKey.from_string(str(self.course.id))
        for item in self.items:
            usage_key = item.scope_ids.usage_id
            completion = models.BlockCompletion.objects.create(
                user=self.student,
                context_key=context_key,
                block_key=usage_key,
                completion=1.0,
            )
        url = '{}?is_bigcourse=1'.format(
            reverse('completion_data_view',
                    kwargs={'course_id': self.course.id}))
        self.response = self.staff_client.get(url)
        data = json.loads(self.response.content.decode())
        self.assertEqual(data['data'], [[False]])

        self.response = self.staff_client.get(url)
        self.assertEqual(self.response.status_code, 200)
        data = json.loads(self.response.content.decode())
        self.assertEqual(len(data['data']), 12)
        self.assertEqual(data['data'][-1][0], self.student.username)
        self.assertEqual(data['data'][-1][1], edxlogin.run)
        self.assertEqual(data['data'][-1][2], self.student.email)
        self.assertEqual(data['data'][-1][3],
                         completion.modified.strftime("%d/%m/%Y, %H:%M:%S"))
Example #15
0
 def test_roundtrip_from_key(self, key_args):
     key = LibraryLocatorV2(**key_args)
     serialized = str(key)
     deserialized = LearningContextKey.from_string(serialized)
     self.assertEqual(key, deserialized)
Example #16
0
 def test_roundtrip_from_string(self, key):
     lib_key = LearningContextKey.from_string(key)
     serialized = str(lib_key)
     self.assertEqual(key, serialized)