コード例 #1
0
ファイル: basic.py プロジェクト: Lektorium-LLC/edx-platform
def list_problem_responses(course_key, problem_location):
    """
    Return responses to a given problem as a dict.

    list_problem_responses(course_key, problem_location)

    would return [
        {'username': u'user1', 'state': u'...'},
        {'username': u'user2', 'state': u'...'},
        {'username': u'user3', 'state': u'...'},
    ]

    where `state` represents a student's response to the problem
    identified by `problem_location`.
    """
    problem_key = UsageKey.from_string(problem_location)
    # Are we dealing with an "old-style" problem location?
    run = problem_key.run
    if not run:
        problem_key = UsageKey.from_string(problem_location).map_into_course(course_key)
    if problem_key.course_key != course_key:
        return []

    smdat = StudentModule.objects.filter(
        course_id=course_key,
        module_state_key=problem_key
    )
    smdat = smdat.order_by('student')

    return [
        {'username': response.student.username, 'state': response.state}
        for response in smdat
    ]
コード例 #2
0
    def test_usage_key(self):
        """
        Test UsageKey
        """
        key = UsageKey.from_string('i4x://org.id/course_id/category/block_id')
        self.assertEqual(key.block_id, 'block_id')

        key = UsageKey.from_string('block-v1:org.id+course_id+run+type@category+block@block_id')
        self.assertEqual(key.block_id, 'block_id')
コード例 #3
0
ファイル: views.py プロジェクト: edx-solutions/edx-platform
    def inner(request, *args, **kwargs):  # pylint: disable=missing-docstring
        usage_key = kwargs.get('usage_key_string')
        if usage_key is not None:
            try:
                UsageKey.from_string(usage_key)
            except InvalidKeyError:
                raise Http404

        response = view_func(request, *args, **kwargs)
        return response
コード例 #4
0
ファイル: validators.py プロジェクト: edx/edx-milestones
def content_key_is_valid(content_key):
    """
    Course module/content/usage key object validation
    """
    if content_key is None:
        return False
    try:
        UsageKey.from_string(six.text_type(content_key))
    except InvalidKeyError:
        return False
    return True
コード例 #5
0
    def test_resume_course_with_completion_api(self, get_patched_current_site):
        """
        Tests completion API resume button functionality
        """
        self.override_waffle_switch(True)
        get_patched_current_site.return_value = self.site

        # Course tree
        course = self.course
        course_key = CourseKey.from_string(str(course.id))
        vertical1 = course.children[0].children[0].children[0]
        vertical2 = course.children[0].children[1].children[0]

        # Fake a visit to sequence1/vertical1
        block_key = UsageKey.from_string(unicode(vertical1.location))
        completion = 1.0
        BlockCompletion.objects.submit_completion(
            user=self.user,
            course_key=course_key,
            block_key=block_key,
            completion=completion
        )

        # Test for 'resume' link
        response = self.visit_course_home(course, resume_count=2)

        # Test for 'resume' link URL - should be vertical 1
        content = pq(response.content)
        self.assertTrue(content('.action-resume-course').attr('href').endswith('/vertical/' + vertical1.url_name))

        # Fake a visit to sequence2/vertical2
        block_key = UsageKey.from_string(unicode(vertical2.location))
        completion = 1.0
        BlockCompletion.objects.submit_completion(
            user=self.user,
            course_key=course_key,
            block_key=block_key,
            completion=completion
        )
        response = self.visit_course_home(course, resume_count=2)

        # Test for 'resume' link URL - should be vertical 2
        content = pq(response.content)
        self.assertTrue(content('.action-resume-course').attr('href').endswith('/vertical/' + vertical2.url_name))

        # visit sequential 1, make sure 'Resume Course' URL is robust against 'Last Visited'
        # (even though I visited seq1/vert1, 'Resume Course' still points to seq2/vert2)
        self.visit_sequential(course, course.children[0], course.children[0].children[0])

        # Test for 'resume' link URL - should be vertical 2 (last completed block, NOT last visited)
        response = self.visit_course_home(course, resume_count=2)
        content = pq(response.content)
        self.assertTrue(content('.action-resume-course').attr('href').endswith('/vertical/' + vertical2.url_name))
コード例 #6
0
ファイル: edx_bridge.py プロジェクト: kriwil/labster
def duplicate_lab_content(user, source_location, parent_location):
    store = modulestore()
    parent_locator = UsageKey.from_string(parent_location)
    source_locator = UsageKey.from_string(source_location)
    source_item = store.get_item(source_locator)
    parent_item = store.get_item(parent_locator)

    # delete parent's children first
    for child in parent_item.get_children():
        store.delete_item(child.location, user.id)

    # duplicate quiz_blocks
    for quiz_block in source_item.get_children():
        new_location = _duplicate_item(parent_locator, quiz_block.location, user=user, display_name=quiz_block.display_name)
        modulestore().publish(new_location, user.id)
コード例 #7
0
ファイル: api.py プロジェクト: Lektorium-LLC/edx-platform
def get_prerequisites(course_key):
    """
    Find all the gating milestones associated with a course and the
    XBlock info associated with those gating milestones.

    Arguments:
        course_key (str|CourseKey): The course key

    Returns:
        list: A list of dicts containing the milestone and associated XBlock info
    """
    course_content_milestones = find_gating_milestones(course_key)

    milestones_by_block_id = {}
    block_ids = []
    for milestone in course_content_milestones:
        prereq_content_key = milestone['namespace'].replace(GATING_NAMESPACE_QUALIFIER, '')
        block_id = UsageKey.from_string(prereq_content_key).block_id
        block_ids.append(block_id)
        milestones_by_block_id[block_id] = milestone

    result = []
    for block in modulestore().get_items(course_key, qualifiers={'name': block_ids}):
        milestone = milestones_by_block_id.get(block.location.block_id)
        if milestone:
            milestone['block_display_name'] = block.display_name
            milestone['block_usage_key'] = unicode(block.location)
            result.append(milestone)

    return result
コード例 #8
0
ファイル: preview.py プロジェクト: ESOedX/edx-platform
def preview_handler(request, usage_key_string, handler, suffix=''):
    """
    Dispatch an AJAX action to an xblock

    usage_key_string: The usage_key_string-id of the block to dispatch to, passed through `quote_slashes`
    handler: The handler to execute
    suffix: The remainder of the url to be passed to the handler
    """
    usage_key = UsageKey.from_string(usage_key_string)

    descriptor = modulestore().get_item(usage_key)
    instance = _load_preview_module(request, descriptor)
    # Let the module handle the AJAX
    req = django_to_webob_request(request)
    try:
        resp = instance.handle(handler, req, suffix)

    except NoSuchHandlerError:
        log.exception("XBlock %s attempted to access missing handler %r", instance, handler)
        raise Http404

    except NotFoundError:
        log.exception("Module indicating to user that request doesn't exist")
        raise Http404

    except ProcessingError:
        log.warning("Module raised an error while processing AJAX request",
                    exc_info=True)
        return HttpResponseBadRequest()

    except Exception:
        log.exception("error processing ajax call")
        raise

    return webob_to_django_response(resp)
コード例 #9
0
def get_entrance_exam_score(request, course):
    """
    Gather the set of modules which comprise the entrance exam
    Note that 'request' may not actually be a genuine request, due to the
    circular nature of module_render calling entrance_exams and get_module_for_descriptor
    being used here.  In some use cases, the caller is actually mocking a request, although
    in these scenarios the 'user' child object can be trusted and used as expected.
    It's a much larger refactoring job to break this legacy mess apart, unfortunately.
    """
    exam_key = UsageKey.from_string(course.entrance_exam_id)
    exam_descriptor = modulestore().get_item(exam_key)

    def inner_get_module(descriptor):
        """
        Delegate to get_module_for_descriptor (imported here to avoid circular reference)
        """
        from courseware.module_render import get_module_for_descriptor
        field_data_cache = FieldDataCache([descriptor], course.id, request.user)
        return get_module_for_descriptor(
            request.user,
            request,
            descriptor,
            field_data_cache,
            course.id,
            course=course
        )

    exam_module_generators = yield_dynamic_descriptor_descendants(
        exam_descriptor,
        request.user.id,
        inner_get_module
    )
    exam_modules = [module for module in exam_module_generators]
    return _calculate_entrance_exam_score(request.user, course, exam_modules)
コード例 #10
0
ファイル: draft.py プロジェクト: AlexxNica/edx-platform
    def _query_children_for_cache_children(self, course_key, items):
        # first get non-draft in a round-trip
        to_process_non_drafts = super(DraftModuleStore, self)._query_children_for_cache_children(course_key, items)

        to_process_dict = {}
        for non_draft in to_process_non_drafts:
            to_process_dict[BlockUsageLocator._from_deprecated_son(non_draft["_id"], course_key.run)] = non_draft

        if self.get_branch_setting() == ModuleStoreEnum.Branch.draft_preferred:
            # now query all draft content in another round-trip
            query = []
            for item in items:
                item_usage_key = UsageKey.from_string(item).map_into_course(course_key)
                if item_usage_key.block_type not in DIRECT_ONLY_CATEGORIES:
                    query.append(as_draft(item_usage_key).to_deprecated_son())
            if query:
                query = {'_id': {'$in': query}}
                to_process_drafts = list(self.collection.find(query))

                # now we have to go through all drafts and replace the non-draft
                # with the draft. This is because the semantics of the DraftStore is to
                # always return the draft - if available
                for draft in to_process_drafts:
                    draft_loc = BlockUsageLocator._from_deprecated_son(draft["_id"], course_key.run)
                    draft_as_non_draft_loc = as_published(draft_loc)

                    # does non-draft exist in the collection
                    # if so, replace it
                    if draft_as_non_draft_loc in to_process_dict:
                        to_process_dict[draft_as_non_draft_loc] = draft

        # convert the dict - which is used for look ups - back into a list
        queried_children = to_process_dict.values()

        return queried_children
コード例 #11
0
ファイル: component.py プロジェクト: jnigig/edx-platform
def component_handler(request, usage_key_string, handler, suffix=''):
    """
    Dispatch an AJAX action to an xblock

    Args:
        usage_id: The usage-id of the block to dispatch to
        handler (str): The handler to execute
        suffix (str): The remainder of the url to be passed to the handler

    Returns:
        :class:`django.http.HttpResponse`: The response from the handler, converted to a
            django response
    """

    usage_key = UsageKey.from_string(usage_key_string)

    descriptor = get_modulestore(usage_key).get_item(usage_key)
    # Let the module handle the AJAX
    req = django_to_webob_request(request)

    try:
        resp = descriptor.handle(handler, req, suffix)

    except NoSuchHandlerError:
        log.info("XBlock %s attempted to access missing handler %r", descriptor, handler, exc_info=True)
        raise Http404

    # unintentional update to handle any side effects of handle call; so, request user didn't author
    # the change
    get_modulestore(usage_key).update_item(descriptor, None)

    return webob_to_django_response(resp)
コード例 #12
0
ファイル: views.py プロジェクト: lemontreeran/edx-platform
    def post(self, request):
        """
        POST /api/bookmarks/v1/bookmarks/
        Request data: {"usage_id": "<usage-id>"}
        """

        if not request.data:
            return self.error_response(ugettext_noop(u"No data provided."), DEFAULT_USER_MESSAGE)

        usage_id = request.data.get("usage_id", None)
        if not usage_id:
            return self.error_response(ugettext_noop(u"Parameter usage_id not provided."), DEFAULT_USER_MESSAGE)

        try:
            usage_key = UsageKey.from_string(unquote_slashes(usage_id))
        except InvalidKeyError:
            error_message = ugettext_noop(u"Invalid usage_id: {usage_id}.").format(usage_id=usage_id)
            log.error(error_message)
            return self.error_response(error_message, DEFAULT_USER_MESSAGE)

        try:
            bookmark = api.create_bookmark(user=self.request.user, usage_key=usage_key)
        except ItemNotFoundError:
            error_message = ugettext_noop(u"Block with usage_id: {usage_id} not found.").format(usage_id=usage_id)
            log.error(error_message)
            return self.error_response(error_message, DEFAULT_USER_MESSAGE)
        except BookmarksLimitReachedError:
            error_message = ugettext_noop(
                u"You can create up to {max_num_bookmarks_per_course} bookmarks."
                u" You must remove some bookmarks before you can add new ones."
            ).format(max_num_bookmarks_per_course=settings.MAX_BOOKMARKS_PER_COURSE)
            log.info(u"Attempted to create more than %s bookmarks", settings.MAX_BOOKMARKS_PER_COURSE)
            return self.error_response(error_message)

        return Response(bookmark, status=status.HTTP_201_CREATED)
コード例 #13
0
ファイル: analyzer.py プロジェクト: zimka/course_validator
 def is_date_changed(self):
     try:
         last_validation = self._get_last_course_validation()
         if not last_validation:
             return True
         updates = self. _get_course_updates_from_validation(last_validation)
         updates = updates.exclude(change_type=CourseUpdate.VIDEO_BLOCK)
         if not updates:
             return False
         change_types = [x.change_type for x in updates]
         if CourseUpdate.COURSE_PART in change_types:
             return True
         if CourseUpdate.OTHER in change_types:
             logging.warning("Unclassified change in course was detected")
             return True
         date = last_validation.created_at
         usage_ids = [x.change for x in updates if x.change_type==CourseUpdate.CANDIDATE]
         usage_keys = [UsageKey.from_string(x) for x in usage_ids]
         items = [self.store.get_item(x) for x in usage_keys]
         for it in items:
             if it.edited_on > date:
                 return True
     except Exception as e:
         logging.error("Date change analysis error: {}".format(str(e)))
         return True
     return False
コード例 #14
0
ファイル: draft.py プロジェクト: AlexxNica/edx-platform
        def _delete_item(current_entry, to_be_deleted):
            """
            Depth first deletion of nodes
            """
            to_be_deleted.append(self._id_dict_to_son(current_entry['_id']))
            next_tier = []
            for child_loc in current_entry.get('definition', {}).get('children', []):
                child_loc = UsageKey.from_string(child_loc).map_into_course(course_key)

                # single parent can have 2 versions: draft and published
                # get draft parents only while deleting draft module
                if draft_only:
                    revision = MongoRevisionKey.draft
                else:
                    revision = ModuleStoreEnum.RevisionOption.all

                parents = self._get_raw_parent_locations(child_loc, revision)
                # Don't delete modules if one of its parents shouldn't be deleted
                # This should only be an issue for courses have ended up in
                # a state where modules have multiple parents
                if all(parent.to_deprecated_son() in to_be_deleted for parent in parents):
                    for rev_func in as_functions:
                        current_loc = rev_func(child_loc)
                        current_son = current_loc.to_deprecated_son()
                        next_tier.append(current_son)

            return next_tier
コード例 #15
0
ファイル: views.py プロジェクト: fmyzjs/edx-serverapi
def _get_course_child(request, user, course_key, content_id):
    """
    Return a course xmodule/xblock to the caller
    """
    content_descriptor = None
    content_key = None
    content = None
    try:
        content_key = UsageKey.from_string(content_id)
    except InvalidKeyError:
        try:
            content_key = Location.from_deprecated_string(content_id)
        except (InvalidKeyError, InvalidLocationError):
            pass
    if content_key:
        store = modulestore()
        content_descriptor = store.get_item(content_key)
    if content_descriptor:
        field_data_cache = FieldDataCache([content_descriptor], course_key, user)
        content = module_render.get_module(
            user,
            request,
            content_key,
            field_data_cache,
            course_key)
    return content_descriptor, content_key, content
コード例 #16
0
 def _fulfill_content_milestones(user, course_key, content_key):
     """
     Internal helper to handle milestone fulfillments for the specified content module
     """
     # Fulfillment Use Case: Entrance Exam
     # If this module is part of an entrance exam, we'll need to see if the student
     # has reached the point at which they can collect the associated milestone
     if settings.FEATURES.get('ENTRANCE_EXAMS', False):
         course = modulestore().get_course(course_key)
         content = modulestore().get_item(content_key)
         entrance_exam_enabled = getattr(course, 'entrance_exam_enabled', False)
         in_entrance_exam = getattr(content, 'in_entrance_exam', False)
         if entrance_exam_enabled and in_entrance_exam:
             # We don't have access to the true request object in this context, but we can use a mock
             request = RequestFactory().request()
             request.user = user
             exam_pct = get_entrance_exam_score(request, course)
             if exam_pct >= course.entrance_exam_minimum_score_pct:
                 exam_key = UsageKey.from_string(course.entrance_exam_id)
                 relationship_types = milestones_helpers.get_milestone_relationship_types()
                 content_milestones = milestones_helpers.get_course_content_milestones(
                     course_key,
                     exam_key,
                     relationship=relationship_types['FULFILLS']
                 )
                 # Add each milestone to the user's set...
                 user = {'id': request.user.id}
                 for milestone in content_milestones:
                     milestones_helpers.add_user_milestone(user, milestone)
コード例 #17
0
ファイル: signals.py プロジェクト: louyihua/edx-platform
def recalculate_subsection_grade_handler(sender, **kwargs):  # pylint: disable=unused-argument
    """
    Consume the SCORE_CHANGED signal and trigger an update.
    This method expects that the kwargs dictionary will contain the following
    entries (See the definition of SCORE_CHANGED):
       - points_possible: Maximum score available for the exercise
       - points_earned: Score obtained by the user
       - user: User object
       - course_id: Unicode string representing the course
       - usage_id: Unicode string indicating the courseware instance
    """
    try:
        course_id = kwargs.get('course_id', None)
        usage_id = kwargs.get('usage_id', None)
        student = kwargs.get('user', None)

        course_key = CourseLocator.from_string(course_id)
        if not PersistentGradesEnabledFlag.feature_enabled(course_key):
            return

        usage_key = UsageKey.from_string(usage_id).replace(course_key=course_key)

        from lms.djangoapps.grades.new.subsection_grade import SubsectionGradeFactory
        SubsectionGradeFactory(student).update(usage_key, course_key)
    except Exception as ex:  # pylint: disable=broad-except
        log.exception(
            u"Failed to process SCORE_CHANGED signal. "
            "user: %s, course_id: %s, "
            "usage_id: %s. Exception: %s", unicode(student), course_id, usage_id, ex.message
        )
コード例 #18
0
def component_handler(request, usage_key_string, handler, suffix=''):
    """
    Dispatch an AJAX action to an xblock

    Args:
        usage_id: The usage-id of the block to dispatch to
        handler (str): The handler to execute
        suffix (str): The remainder of the url to be passed to the handler

    Returns:
        :class:`django.http.HttpResponse`: The response from the handler, converted to a
            django response
    """

    usage_key = UsageKey.from_string(usage_key_string)

    descriptor = modulestore().get_item(usage_key)
    descriptor.xmodule_runtime = StudioEditModuleRuntime(request.user)
    # Let the module handle the AJAX
    req = django_to_webob_request(request)

    try:
        resp = descriptor.handle(handler, req, suffix)

    except NoSuchHandlerError:
        log.info("XBlock %s attempted to access missing handler %r", descriptor, handler, exc_info=True)
        raise Http404

    # unintentional update to handle any side effects of handle call
    # could potentially be updating actual course data or simply caching its values
    modulestore().update_item(descriptor, request.user.id)

    return webob_to_django_response(resp)
コード例 #19
0
    def test_gated(self, gated_block_ref, gating_block_ref, gating_block_child, expected_blocks_before_completion):
        """
        First, checks that a student cannot see the gated block when it is gated by the gating block and no
        attempt has been made to complete the gating block.
        Then, checks that the student can see the gated block after the gating block has been completed.

        expected_blocks_before_completion is the set of blocks we expect to be visible to the student
        before the student has completed the gating block.

        The test data includes one special exam and one non-special block as the gating blocks.
        """
        self.course.enable_subsection_gating = True
        self.setup_gated_section(self.blocks[gated_block_ref], self.blocks[gating_block_ref])

        with self.assertNumQueries(3):
            self.get_blocks_and_check_against_expected(self.user, expected_blocks_before_completion)

        # clear the request cache to simulate a new request
        self.clear_caches()

        # mock the api that the lms gating api calls to get the score for each block to always return 1 (ie 100%)
        with patch('gating.api.get_module_score', Mock(return_value=1)):

            # this call triggers reevaluation of prerequisites fulfilled by the parent of the
            # block passed in, so we pass in a child of the gating block
            lms_gating_api.evaluate_prerequisite(
                self.course,
                UsageKey.from_string(unicode(self.blocks[gating_block_child].location)),
                self.user.id)
        with self.assertNumQueries(2):
            self.get_blocks_and_check_against_expected(self.user, self.ALL_BLOCKS_EXCEPT_SPECIAL)
コード例 #20
0
def get_required_content(course, user):
    """
    Queries milestones subsystem to see if the specified course is gated on one or more milestones,
    and if those milestones can be fulfilled via completion of a particular course content module
    """
    required_content = []
    if settings.FEATURES.get('MILESTONES_APP', False):
        # Get all of the outstanding milestones for this course, for this user
        try:
            milestone_paths = get_course_milestones_fulfillment_paths(
                unicode(course.id),
                serialize_user(user)
            )
        except InvalidMilestoneRelationshipTypeException:
            return required_content

        # For each outstanding milestone, see if this content is one of its fulfillment paths
        for path_key in milestone_paths:
            milestone_path = milestone_paths[path_key]
            if milestone_path.get('content') and len(milestone_path['content']):
                for content in milestone_path['content']:
                    required_content.append(content)

    #local imports to avoid circular reference
    from student.models import EntranceExamConfiguration
    can_skip_entrance_exam = EntranceExamConfiguration.user_can_skip_entrance_exam(user, course.id)
    # check if required_content has any entrance exam and user is allowed to skip it
    # then remove it from required content
    if required_content and getattr(course, 'entrance_exam_enabled', False) and can_skip_entrance_exam:
        descriptors = [modulestore().get_item(UsageKey.from_string(content)) for content in required_content]
        entrance_exam_contents = [unicode(descriptor.location)
                                  for descriptor in descriptors if descriptor.is_entrance_exam]
        required_content = list(set(required_content) - set(entrance_exam_contents))
    return required_content
コード例 #21
0
ファイル: tasks.py プロジェクト: digitalsatori/edx-platform
def task_evaluate_subsection_completion_milestones(course_id, block_id, user_id):
    """
    Updates users' milestones related to completion of a subsection.
     Args:
        course_id(str): Course id which triggered a completion event
        block_id(str): Id of the completed block
        user_id(int): Id of the user who completed a block
    """
    store = modulestore()
    course_key = CourseKey.from_string(course_id)
    with store.bulk_operations(course_key):
        course = store.get_course(course_key)
        if not course or not course.enable_subsection_gating:
            log.debug(
                u"Gating: ignoring evaluation of completion milestone because it disabled for course [%s]", course_id
            )
        else:
            try:
                user = User.objects.get(id=user_id)
                course_structure = get_course_blocks(user, store.make_course_usage_key(course_key))
                completed_block_usage_key = UsageKey.from_string(block_id).map_into_course(course.id)
                subsection_block = _get_subsection_of_block(completed_block_usage_key, course_structure)
                subsection = course_structure[subsection_block]
                log.debug(
                    u"Gating: Evaluating completion milestone for subsection [%s] and user [%s]",
                    unicode(subsection.location), user.id
                )
                gating_api.evaluate_prerequisite(course, subsection, user)
            except KeyError:
                log.error(u"Gating: Given prerequisite subsection [%s] not found in course structure", block_id)
コード例 #22
0
 def test_block_constructor(self):
     expected_org = 'mit.eecs'
     expected_course = '6002x'
     expected_run = '2014_T2'
     expected_branch = 'published'
     expected_block_ref = 'HW3'
     testurn = 'block-v1:{}+{}+{}+{}@{}+{}@{}+{}@{}'.format(
         expected_org, expected_course, expected_run, CourseLocator.BRANCH_PREFIX, expected_branch,
         BlockUsageLocator.BLOCK_TYPE_PREFIX, 'problem', BlockUsageLocator.BLOCK_PREFIX, 'HW3'
     )
     testobj = UsageKey.from_string(testurn)
     self.check_block_locn_fields(
         testobj,
         org=expected_org,
         course=expected_course,
         run=expected_run,
         branch=expected_branch,
         block_type='problem',
         block=expected_block_ref
     )
     self.assertEqual(text_type(testobj), testurn)
     testobj = testobj.for_version(ObjectId())
     agnostic = testobj.version_agnostic()
     self.assertIsNone(agnostic.version_guid)
     self.check_block_locn_fields(
         agnostic,
         org=expected_org,
         course=expected_course,
         run=expected_run,
         branch=expected_branch,
         block=expected_block_ref
     )
コード例 #23
0
    def _test_metrics(self):
        module_id = UsageKey.from_string(self.page.video_id).html_id()
        video = [video for video in self.course.videos() if video['encoded_module_id'] == module_id][0]

        expected_metrics = [
            {
                'tooltip': 'Estimated percentage of students who watched the entire video.',
                'data_type': 'watched-percent',
                'metric_value': self.build_display_percentage(
                    video['users_at_end'], max(video['users_at_start'], video['users_at_end']),
                    zero_percent_default='0%')
            },
            {
                'tooltip': 'Students who started watching the video.',
                'data_type': 'started-video',
                'metric_value': self.format_number(video['users_at_start'])
            },
            {
                'tooltip': 'Students who watched the video to the end.',
                'data_type': 'finished-video',
                'metric_value': self.format_number(video['users_at_end'])
            }
        ]

        for expected in expected_metrics:
            data_selector = 'data-type={0}'.format(expected['data_type'])
            self.assertSummaryPointValueEquals(data_selector, expected['metric_value'])
            self.assertSummaryTooltipEquals(data_selector, expected['tooltip'])
コード例 #24
0
    def dump_studentmodules(self, module, display_header, display_prompt, deanonymize):
        '''Identify the list of StudentModule objects of combinedopenended type that belong to the specified module_id'''
        module = UsageKey.from_string(module)
        modules = StudentModule.objects.filter(
                      module_state_key=module,
                      module_type='combinedopenended'
                  )

        filename = "{0}.html".format(module).replace(':','-').replace('/','-')
        
        with io.StringIO() as handle:
            handle.write(u'<html><head></head><body>')
            handle.write(u'<h1>Задание "{0}"</h1>\n\n'.format(display_header))
            handle.write(u'<p>{0}</p>\n\n'.format(display_prompt))
            for module in modules:
                self.dump_studentmodule_answer(module, handle, deanonymize)
            handle.write(u'</body></html>')
            filedata = handle.getvalue()
        
        soup = BeautifulSoup(clean_html(filedata))
        metatag = soup.new_tag('meta')
        metatag.attrs['charset'] = 'UTF-8'
        soup.head.append(metatag)

        return (filename, u"<!DOCTYPE html>\n"+soup.prettify())
コード例 #25
0
ファイル: item.py プロジェクト: GeertHa/edx-platform
def usage_key_with_run(usage_key_string):
    """
    Converts usage_key_string to a UsageKey, adding a course run if necessary
    """
    usage_key = UsageKey.from_string(usage_key_string)
    usage_key = usage_key.replace(course_key=modulestore().fill_in_run(usage_key.course_key))
    return usage_key
コード例 #26
0
 def test_block_constructor_url_version_prefix(self):
     test_id_loc = '519665f6223ebd6980884f2b'
     testobj = UsageKey.from_string(
         'block-v1:mit.eecs+6002x+2014_T2+{}@{}+{}@problem+{}@lab2'.format(
             CourseLocator.VERSION_PREFIX,
             test_id_loc,
             BlockUsageLocator.BLOCK_TYPE_PREFIX,
             BlockUsageLocator.BLOCK_PREFIX
         )
     )
     self.check_block_locn_fields(
         testobj,
         org='mit.eecs',
         course='6002x',
         run='2014_T2',
         block_type='problem',
         block='lab2',
         version_guid=ObjectId(test_id_loc)
     )
     agnostic = testobj.course_agnostic()
     self.check_block_locn_fields(
         agnostic,
         block='lab2',
         org=None,
         course=None,
         run=None,
         version_guid=ObjectId(test_id_loc)
     )
     self.assertIsNone(agnostic.course)
     self.assertIsNone(agnostic.run)
     self.assertIsNone(agnostic.org)
コード例 #27
0
ファイル: seq_module.py プロジェクト: luisvasq/edx-platform
    def handle_ajax(self, dispatch, data):  # TODO: bounds checking
        ''' get = request.POST instance '''
        if dispatch == 'goto_position':
            # set position to default value if either 'position' argument not
            # found in request or it is a non-positive integer
            position = data.get('position', u'1')
            if position.isdigit() and int(position) > 0:
                self.position = int(position)
            else:
                self.position = 1
            return json.dumps({'success': True})

        if dispatch == 'get_completion':
            completion_service = self.runtime.service(self, 'completion')

            usage_key = data.get('usage_key', None)
            if not usage_key:
                return None
            item = self.get_child(UsageKey.from_string(usage_key))
            if not item:
                return None

            complete = completion_service.vertical_is_complete(item)
            return json.dumps({
                'complete': complete
            })
        raise NotFoundError('Unexpected dispatch type')
コード例 #28
0
    def test_get_path_in_case_of_exceptions(self):

        user = UserFactory.create()

        # Block does not exist
        usage_key = UsageKey.from_string('i4x://edX/apis/html/interactive')
        usage_key.replace(course_key=self.course.id)
        self.assertEqual(Bookmark.get_path(usage_key), [])

        # Block is an orphan
        self.other_sequential_1.children = []
        modulestore().update_item(self.other_sequential_1, self.admin.id)  # pylint: disable=no-member

        bookmark_data = self.get_bookmark_data(self.other_vertical_2, user=user)
        bookmark, __ = Bookmark.create(bookmark_data)

        self.assertEqual(bookmark.path, [])
        self.assertIsNotNone(bookmark.xblock_cache)
        self.assertEqual(bookmark.xblock_cache.paths, [])

        # Parent block could not be retrieved
        with mock.patch('openedx.core.djangoapps.bookmarks.models.search.path_to_location') as mock_path_to_location:
            mock_path_to_location.return_value = [usage_key]
            bookmark_data = self.get_bookmark_data(self.other_sequential_1, user=user)
            bookmark, __ = Bookmark.create(bookmark_data)
            self.assertEqual(bookmark.path, [])
コード例 #29
0
def get_module_by_usage_id(request, course_id, usage_id, disable_staff_debug_info=False, course=None):
    """
    Gets a module instance based on its `usage_id` in a course, for a given request/user

    Returns (instance, tracking_context)
    """
    user = request.user

    try:
        course_id = CourseKey.from_string(course_id)
        usage_key = UsageKey.from_string(unquote_slashes(usage_id)).map_into_course(course_id)
    except InvalidKeyError:
        raise Http404("Invalid location")

    try:
        descriptor = modulestore().get_item(usage_key)
        descriptor_orig_usage_key, descriptor_orig_version = modulestore().get_block_original_usage(usage_key)
    except ItemNotFoundError:
        log.warn(
            "Invalid location for course id %s: %s",
            usage_key.course_key,
            usage_key
        )
        raise Http404

    tracking_context = {
        'module': {
            'display_name': descriptor.display_name_with_default_escaped,
            'usage_key': unicode(descriptor.location),
        }
    }

    # For blocks that are inherited from a content library, we add some additional metadata:
    if descriptor_orig_usage_key is not None:
        tracking_context['module']['original_usage_key'] = unicode(descriptor_orig_usage_key)
        tracking_context['module']['original_usage_version'] = unicode(descriptor_orig_version)

    unused_masquerade, user = setup_masquerade(request, course_id, has_access(user, 'staff', descriptor, course_id))
    field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
        course_id,
        user,
        descriptor,
        read_only=CrawlersConfig.is_crawler(request),
    )
    instance = get_module_for_descriptor(
        user,
        request,
        descriptor,
        field_data_cache,
        usage_key.course_key,
        disable_staff_debug_info=disable_staff_debug_info,
        course=course
    )
    if instance is None:
        # Either permissions just changed, or someone is trying to be clever
        # and load something they shouldn't have access to.
        log.debug("No module %s for user %s -- access denied?", usage_key, user)
        raise Http404

    return (instance, tracking_context)
コード例 #30
0
    def test_contentstore_views_entrance_exam_post_new_sequential_confirm_grader(self):
        """
        Unit Test: test_contentstore_views_entrance_exam_post
        """
        resp = self.client.post(self.exam_url, {}, http_accept='application/json')
        self.assertEqual(resp.status_code, 201)
        resp = self.client.get(self.exam_url)
        self.assertEqual(resp.status_code, 200)

        # Reload the test course now that the exam module has been added
        self.course = modulestore().get_course(self.course.id)

        # Add a new child sequential to the exam module
        # Confirm that the grader type is 'Entrance Exam'
        chapter_locator_string = json.loads(resp.content).get('locator')
        # chapter_locator = UsageKey.from_string(chapter_locator_string)
        seq_data = {
            'category': "sequential",
            'display_name': "Entrance Exam Subsection",
            'parent_locator': chapter_locator_string,
        }
        resp = self.client.ajax_post(reverse_url('xblock_handler'), seq_data)
        seq_locator_string = json.loads(resp.content).get('locator')
        seq_locator = UsageKey.from_string(seq_locator_string)
        section_grader_type = CourseGradingModel.get_section_grader_type(seq_locator)
        self.assertEqual(GRADER_TYPES['ENTRANCE_EXAM'], section_grader_type['graderType'])
コード例 #31
0
def _get_entrance_exam(request, course_key):  # pylint: disable=W0613
    """
    Internal workflow operation to retrieve an entrance exam
    """
    course = modulestore().get_course(course_key)
    if course is None:
        return HttpResponse(status=400)
    if not getattr(course, 'entrance_exam_id'):
        return HttpResponse(status=404)
    try:
        exam_key = UsageKey.from_string(course.entrance_exam_id)
    except InvalidKeyError:
        return HttpResponse(status=404)
    try:
        exam_descriptor = modulestore().get_item(exam_key)
        return HttpResponse(_serialize_entrance_exam(exam_descriptor),
                            status=200,
                            mimetype='application/json')
    except ItemNotFoundError:
        return HttpResponse(status=404)
コード例 #32
0
    def test_path(self, seconds_delta, paths, get_path_call_count, mock_get_path):

        block_path = [PathItem(UsageKey.from_string(EXAMPLE_USAGE_KEY_1), '1')]
        mock_get_path.return_value = block_path

        html = ItemFactory.create(
            parent_location=self.other_chapter_1.location, category='html', display_name='Other Lesson 1'
        )

        bookmark_data = self.get_bookmark_data(html)
        bookmark, __ = Bookmark.create(bookmark_data)
        assert bookmark.xblock_cache is not None

        modification_datetime = datetime.datetime.now(pytz.utc) + datetime.timedelta(seconds=seconds_delta)
        with freeze_time(modification_datetime):
            bookmark.xblock_cache.paths = paths
            bookmark.xblock_cache.save()

        assert bookmark.path == block_path
        assert mock_get_path.call_count == get_path_call_count
コード例 #33
0
def _get_entrance_exam(request, course_key):  # pylint: disable=W0613
    """
    Internal workflow operation to retrieve an entrance exam
    """
    course = modulestore().get_course(course_key)
    if course is None:
        return HttpResponse(status=400)
    if not course.entrance_exam_id:
        return HttpResponse(status=404)
    try:
        exam_key = UsageKey.from_string(course.entrance_exam_id)
    except InvalidKeyError:
        return HttpResponse(status=404)
    try:
        exam_descriptor = modulestore().get_item(exam_key)
        return HttpResponse(
            dump_js_escaped_json({'locator': unicode(exam_descriptor.location)}),
            status=200, content_type='application/json')
    except ItemNotFoundError:
        return HttpResponse(status=404)
コード例 #34
0
ファイル: courses.py プロジェクト: smarnach/edx-platform
def get_entrance_exam_score(request, course):
    """
    Get entrance exam score
    """
    exam_key = UsageKey.from_string(course.entrance_exam_id)
    exam_descriptor = modulestore().get_item(exam_key)

    def inner_get_module(descriptor):
        """
        Delegate to get_module_for_descriptor.
        """
        field_data_cache = FieldDataCache([descriptor], course.id,
                                          request.user)
        return get_module_for_descriptor(request.user, request, descriptor,
                                         field_data_cache, course.id)

    exam_module_generators = yield_dynamic_descriptor_descendents(
        exam_descriptor, inner_get_module)
    exam_modules = [module for module in exam_module_generators]
    return calculate_entrance_exam_score(request.user, course, exam_modules)
コード例 #35
0
ファイル: index.py プロジェクト: uetuluk/edx-platform
 def microfrontend_url(self):
     """
     Return absolute URL to this section in the courseware micro-frontend.
     """
     try:
         unit_key = UsageKey.from_string(self.request.GET.get('activate_block_id', ''))
         # `activate_block_id` is typically a Unit (a.k.a. Vertical),
         # but it can technically be any block type. Do a check to
         # make sure it's really a Unit before we use it for the MFE.
         if unit_key.block_type != 'vertical':
             unit_key = None
     except InvalidKeyError:
         unit_key = None
     url = make_learning_mfe_courseware_url(
         self.course_key,
         self.section.location if self.section else None,
         unit_key,
         params=self.request.GET,
     )
     return url
コード例 #36
0
def get_score(usage_key, user_id):
    """
    Return score for user_id and usage_key.
    """
    if not isinstance(usage_key, UsageKey):
        usage_key = UsageKey.from_string(usage_key)
    try:
        score = StudentModule.objects.get(course_id=usage_key.course_key,
                                          module_state_key=usage_key,
                                          student_id=user_id)
    except StudentModule.DoesNotExist:
        return None
    else:
        return {
            'grade': score.grade,
            'score': score.grade * (score.max_grade or 1),
            'max_grade': score.max_grade,
            'created': score.created,
            'modified': score.modified
        }
コード例 #37
0
def load_single_xblock(request, user_id, course_id, usage_key_string, course=None):
    """
    Load a single XBlock identified by usage_key_string.
    """
    usage_key = UsageKey.from_string(usage_key_string)
    course_key = CourseKey.from_string(course_id)
    usage_key = usage_key.map_into_course(course_key)
    user = User.objects.get(id=user_id)
    field_data_cache = FieldDataCache.cache_for_descriptor_descendents(
        course_key,
        user,
        modulestore().get_item(usage_key),
        depth=0,
    )
    instance = get_module(user, request, usage_key, field_data_cache, grade_bucket_type='xqueue', course=course)
    if instance is None:
        msg = "No module {0} for user {1}--access denied?".format(usage_key_string, user)
        log.debug(msg)
        raise Http404
    return instance
コード例 #38
0
    def get(self, request, usage_key_string, *args, **kwargs):
        """
        Return response to a GET request.
        """
        try:
            usage_key = UsageKey.from_string(usage_key_string)
        except InvalidKeyError:
            raise NotFound("Invalid usage key: '{}'.".format(usage_key_string))

        sequence, _ = get_module_by_usage_id(
            self.request,
            str(usage_key.course_key),
            str(usage_key),
            disable_staff_debug_info=True)

        view = STUDENT_VIEW
        if request.user.is_anonymous:
            view = PUBLIC_VIEW

        return Response(json.loads(sequence.handle_ajax('metadata', None, view=view)))
コード例 #39
0
ファイル: test_services.py プロジェクト: lxp20201/lxp
 def test_submit_group_completion_by_mixed_users_and_user_ids(self):
     third_user = UserFactory.create()
     completable_block = XBlock(Mock(), scope_ids=Mock(spec=ScopeIds))
     completable_block.location = UsageKey.from_string(
         "i4x://edX/100/a/1").replace(course_key=self.course_key)
     service = CompletionService(self.user, self.course_key)
     service.submit_group_completion(
         block_key=completable_block.location,
         completion=0.25,
         users=[self.other_user],
         user_ids=[third_user.id],
     )
     completions = list(
         BlockCompletion.objects.filter(
             block_key=completable_block.location))
     self.assertEqual(len(completions), 2)
     self.assertTrue(all(bc.completion == 0.25 for bc in completions))
     self.assertEqual({bc.user
                       for bc in completions},
                      {self.other_user, third_user})
コード例 #40
0
def block_metadata(request, usage_key_str):
    """
    Get metadata about the specified block.

    Accepts an "include" query parameter which must be a comma separated list of keys to include. Valid keys are
    "index_dictionary" and "student_view_data".
    """
    usage_key = UsageKey.from_string(usage_key_str)
    block = load_block(usage_key, request.user)
    includes = request.GET.get("include", "").split(",")
    metadata_dict = get_block_metadata(block, includes=includes)
    if 'children' in metadata_dict:
        metadata_dict['children'] = [
            str(key) for key in metadata_dict['children']
        ]
    if 'editable_children' in metadata_dict:
        metadata_dict['editable_children'] = [
            str(key) for key in metadata_dict['editable_children']
        ]
    return Response(metadata_dict)
コード例 #41
0
def handle_score_changed(**kwargs):
    """
    Receives the SCORE_CHANGED signal sent by LMS when a student's score has changed
    for a given component and triggers the evaluation of any milestone relationships
    which are attached to the updated content.

    Arguments:
        kwargs (dict): Contains user ID, course key, and content usage key

    Returns:
        None
    """
    course = modulestore().get_course(
        CourseKey.from_string(kwargs.get('course_id')))
    if course.enable_subsection_gating:
        gating_api.evaluate_prerequisite(
            course,
            UsageKey.from_string(kwargs.get('usage_id')),
            kwargs.get('user_id'),
        )
コード例 #42
0
    def get_block_tick(sefl, blocks_unit, blocks):
        """
            Check if unit block is completed
        """
        checker = True
        i = 0
        while checker and i < len(blocks_unit):
            # Iterate each block
            block = blocks_unit[i]
            dicussion_block = block.split('@')
            if dicussion_block[1] != 'discussion+block':
                usage_key = UsageKey.from_string(block)
                aux_block = blocks.filter(
                    block_key=usage_key).values('completion')

                if aux_block.count() == 0 or aux_block[0] == 0.0:
                    # If block hasnt been seen or completed
                    checker = False
            i += 1
        return checker
コード例 #43
0
ファイル: views.py プロジェクト: uetuluk/edx-platform
def get_handler_url(request, usage_key_str, handler_name):
    """
    Get an absolute URL which can be used (without any authentication) to call
    the given XBlock handler.

    The URL will expire but is guaranteed to be valid for a minimum of 2 days.

    The following query parameters will be appended to the returned handler_url:
    * "lx_block_types": optional boolean; set to use the LabXchange XBlock classes to load the requested block.
      The block ID and OLX remain unchanged; they will use the original block type.
    """
    try:
        usage_key = UsageKey.from_string(usage_key_str)
    except InvalidKeyError as e:
        raise NotFound(
            invalid_not_found_fmt.format(usage_key=usage_key_str)) from e

    handler_url = _get_handler_url(usage_key, handler_name, request.user,
                                   request.GET)
    return Response({"handler_url": handler_url})
コード例 #44
0
    def setUp(self):
        super(BatchCompletionMethodTests, self).setUp()
        self.override_waffle_switch(True)

        self.user = UserFactory.create()
        self.other_user = UserFactory.create()
        self.course_key = CourseKey.from_string("edX/MOOC101/2049_T2")
        self.other_course_key = CourseKey.from_string(
            "course-v1:ReedX+Hum110+1904")
        self.block_keys = [
            UsageKey.from_string("i4x://edX/MOOC101/video/{}".format(number))
            for number in xrange(5)
        ]

        submit_completions_for_testing(self.user, self.course_key,
                                       self.block_keys[:3])
        submit_completions_for_testing(self.other_user, self.course_key,
                                       self.block_keys[2:])
        submit_completions_for_testing(self.user, self.other_course_key,
                                       [self.block_keys[4]])
コード例 #45
0
ファイル: views.py プロジェクト: lxp20201/lxp
    def _validate_and_parse_block_key(self, block_key, course_key_obj):
        """
        Returns a validated, parsed UsageKey deserialized from the given block_key.
        """
        try:
            block_key_obj = UsageKey.from_string(block_key)
        except InvalidKeyError:
            raise ValidationError(_("Invalid block key: {}").format(block_key))

        if block_key_obj.run is None:
            expected_matching_course_key = course_key_obj.replace(run=None)
        else:
            expected_matching_course_key = course_key_obj

        if block_key_obj.course_key != expected_matching_course_key:
            raise ValidationError(
                _("Block with key: '{key}' is not in course {course}").format(key=block_key, course=course_key_obj)
            )

        return block_key_obj
コード例 #46
0
def render_block_view(request, usage_key_str, view_name):
    """
    Get the HTML, JS, and CSS needed to render the given XBlock.

    Accepts the following query parameters:
    * "lx_block_types": optional boolean; set to use the LabXchange XBlock classes to load the requested block.
      The block ID and OLX remain unchanged; they will use the original block type.
    """
    try:
        usage_key = UsageKey.from_string(usage_key_str)
    except InvalidKeyError as e:
        raise InvalidNotFound from e

    block = load_block(usage_key,
                       request.user,
                       block_type_overrides=_block_type_overrides(request.GET))
    fragment = _render_block_view(block, view_name, request.user)
    response_data = get_block_metadata(block)
    response_data.update(fragment.to_dict())
    return Response(response_data)
コード例 #47
0
def container_handler(request, usage_key_string):
    """
    The restful handler for container xblock requests.

    GET
        html: returns the HTML page for editing a container
        json: not currently supported
    """
    if 'text/html' in request.META.get('HTTP_ACCEPT', 'text/html'):

        usage_key = UsageKey.from_string(usage_key_string)
        try:
            course, xblock, __ = _get_item_in_course(request, usage_key)
        except ItemNotFoundError:
            return HttpResponseBadRequest()

        component_templates = _get_component_templates(course)
        ancestor_xblocks = []
        parent = get_parent_xblock(xblock)
        while parent and parent.category != 'sequential':
            ancestor_xblocks.append(parent)
            parent = get_parent_xblock(parent)
        ancestor_xblocks.reverse()

        unit = ancestor_xblocks[0] if ancestor_xblocks else None
        unit_publish_state = compute_publish_state(unit) if unit else None

        return render_to_response(
            'container.html',
            {
                'context_course':
                course,  # Needed only for display of menus at top of page.
                'xblock': xblock,
                'unit_publish_state': unit_publish_state,
                'xblock_locator': usage_key,
                'unit': None if not ancestor_xblocks else ancestor_xblocks[0],
                'ancestor_xblocks': ancestor_xblocks,
                'component_templates': json.dumps(component_templates),
            })
    else:
        return HttpResponseBadRequest("Only supports html requests")
コード例 #48
0
ファイル: shim.py プロジェクト: echanna/edx-platform
    def __call__(self, event):
        name = event.get('name')
        if not name:
            return

        if name not in NAME_TO_EVENT_TYPE_MAP:
            return

        event['event_type'] = NAME_TO_EVENT_TYPE_MAP[name]

        if 'event' not in event:
            return

        payload = event['event']

        if 'module_id' in payload:
            module_id = payload['module_id']
            try:
                usage_key = UsageKey.from_string(module_id)
            except InvalidKeyError:
                log.warning('Unable to parse module_id "%s"',
                            module_id,
                            exc_info=True)
            else:
                payload['id'] = usage_key.html_id()

            del payload['module_id']

        if 'current_time' in payload:
            payload['currentTime'] = payload.pop('current_time')

        event['event'] = json.dumps(payload)

        if 'context' not in event:
            return

        context = event['context']

        if 'browser_page' in context:
            page, _sep, _tail = context.pop('browser_page').rpartition('/')
            event['page'] = page
コード例 #49
0
ファイル: views.py プロジェクト: xiaojunxi2008/edx-platform
    def post(self, request, *unused_args, **unused_kwargs):
        """Create a new bookmark for a user."""

        if not request.data:
            return self.error_response(ugettext_noop(u'No data provided.'),
                                       DEFAULT_USER_MESSAGE)

        usage_id = request.data.get('usage_id', None)
        if not usage_id:
            return self.error_response(
                ugettext_noop(u'Parameter usage_id not provided.'),
                DEFAULT_USER_MESSAGE)

        try:
            usage_key = UsageKey.from_string(unquote_slashes(usage_id))
        except InvalidKeyError:
            error_message = ugettext_noop(
                u'Invalid usage_id: {usage_id}.').format(usage_id=usage_id)
            log.error(error_message)
            return self.error_response(error_message, DEFAULT_USER_MESSAGE)

        try:
            bookmark = api.create_bookmark(user=self.request.user,
                                           usage_key=usage_key)
        except ItemNotFoundError:
            error_message = ugettext_noop(
                u'Block with usage_id: {usage_id} not found.').format(
                    usage_id=usage_id)
            log.error(error_message)
            return self.error_response(error_message, DEFAULT_USER_MESSAGE)
        except BookmarksLimitReachedError:
            error_message = ugettext_noop(
                u'You can create up to {max_num_bookmarks_per_course} bookmarks.'
                u' You must remove some bookmarks before you can add new ones.'
            ).format(
                max_num_bookmarks_per_course=settings.MAX_BOOKMARKS_PER_COURSE)
            log.info(u'Attempted to create more than %s bookmarks',
                     settings.MAX_BOOKMARKS_PER_COURSE)
            return self.error_response(error_message)

        return Response(bookmark, status=status.HTTP_201_CREATED)
コード例 #50
0
    def post(self, request, course_key, block_key):
        """
        Handler for POST requests. Attempts to be forward-compatible with the completion API.
        """
        try:
            completion = float(request.data.get('completion'))
        except (TypeError, ValueError):
            return Response(status=status.HTTP_400_BAD_REQUEST)

        if completion > 1.0 or completion < 0.0:
            return Response(status=status.HTTP_400_BAD_REQUEST)

        try:
            course_key = CourseKey.from_string(course_key)
        except InvalidKeyError:
            return Response(status=status.HTTP_404_NOT_FOUND)

        # Check if the user is enrolled in this course.
        if not compat.course_enrollment_model().is_enrolled(
                request.user, course_key):
            return Response(status=status.HTTP_404_NOT_FOUND)

        # Check if content exists for this usage_id.
        try:
            block_key = UsageKey.from_string(block_key)
            compat.get_modulestore().get_item(block_key)
        except (InvalidKeyError, compat.get_item_not_found_error()):
            return Response(status=status.HTTP_404_NOT_FOUND)

        if block_key.course_key.run is None:
            # Old mongo keys must be annotated with course run info before calling submit_completion
            block_key = block_key.replace(course_key=course_key)

        _, created = BlockCompletion.objects.submit_completion(
            user=request.user,
            block_key=block_key,
            completion=completion,
        )

        return Response(
            status=status.HTTP_201_CREATED if created else status.HTTP_200_OK)
コード例 #51
0
ファイル: test_locators.py プロジェクト: smsiva/edx-platform
 def test_block_constructor_url_version_prefix(self):
     test_id_loc = '519665f6223ebd6980884f2b'
     testobj = UsageKey.from_string(
         'edx:mit.eecs+6002x+{}+{}+{}+problem+{}+lab2'.format(
             CourseLocator.VERSION_PREFIX, test_id_loc,
             BlockUsageLocator.BLOCK_TYPE_PREFIX,
             BlockUsageLocator.BLOCK_PREFIX))
     self.check_block_locn_fields(testobj,
                                  org='mit.eecs',
                                  offering='6002x',
                                  block_type='problem',
                                  block='lab2',
                                  version_guid=ObjectId(test_id_loc))
     agnostic = testobj.course_agnostic()
     self.check_block_locn_fields(agnostic,
                                  block='lab2',
                                  org=None,
                                  offering=None,
                                  version_guid=ObjectId(test_id_loc))
     self.assertIsNone(agnostic.offering)
     self.assertIsNone(agnostic.org)
コード例 #52
0
def eol_jump_to(request, course_id, location):
    """
    EOL UPDATE: If item not found redirect to course home page
    Show the page that contains a specific location.
    If the location is invalid or not in any class, return a 404.
    Otherwise, delegates to the index view to figure out whether this user
    has access, and what they should see.
    """
    try:
        course_key = CourseKey.from_string(course_id)
        usage_key = UsageKey.from_string(location).replace(course_key=course_key)
    except InvalidKeyError:
        raise Http404(u"Invalid course_key or usage_key")
    try:
        redirect_url = get_redirect_url(course_key, usage_key, request)
    except ItemNotFoundError:
        # If item not found redirect to course home page
        redirect_url = reverse(course_home_url_name(course_key), kwargs={'course_id': course_id})
    except NoPathToItem:
        raise Http404(u"This location is not in any class: {0}".format(usage_key))
    return redirect(redirect_url)
コード例 #53
0
    def assert_bumper_payload_contains_ids(self, video_event, sources, duration):
        """
        Bumper video events should all contain "host_component_id", "bumper_id",
        "duration", "code" attributes in their payload.

        This function asserts that those fields are present and have correct values.
        """
        self.add_bumper()
        video_descriptors = self.course_fixture.get_nested_xblocks(category='video')
        video_desc = video_descriptors[0]
        video_locator = UsageKey.from_string(video_desc.locator)

        expected_event = {
            'event': {
                'host_component_id': video_locator.html_id(),
                'bumper_id': sources,
                'duration': _parse_time_str(duration),
                'code': 'html5'
            }
        }
        self.assert_events_match([expected_event], [video_event])
コード例 #54
0
def launch_gate_endpoint(request, suffix):
    """
    Gate endpoint that triggers LTI launch endpoint XBlock handler

    This is basically a passthrough function that uses the
    OIDC response parameter `login_hint` to locate the block
    and run the proper handler.
    """
    try:
        usage_key_str = request.GET.get('login_hint')
        usage_key = UsageKey.from_string(usage_key_str)

        return run_xblock_handler(request=request,
                                  course_id=str(usage_key.course_key),
                                  usage_id=str(usage_key),
                                  handler='lti_1p3_launch_callback',
                                  suffix=suffix)
    except Exception as exc:
        log.warning("Error preparing LTI 1.3 launch for hint %r: %s",
                    usage_key_str, exc)
        raise Http404 from exc
コード例 #55
0
 def send(self, event):
     """
     Если в url есть studio_view, значит преподаватель собирался что-то менять в блоке. Мы не можем таким образом
     узнать, были ли сделаны изменения, поэтому запоминаем блок как кандидата на проверку.
     :param event:
     :return:
     """
     username = event['username']
     if "studio_view" in event['event_type']:
         try:
             usage_id = event['event_type'].split('/')[2]
             usage_key = UsageKey.from_string(usage_id)
             course_id = str(usage_key.course_key)
             from course_validator.models import CourseUpdate
             # Этот импорт помещен здесь, т.к. бэкенды загружаются раньше course_validator и иначе выбрасывается Exception
             CourseUpdate.objects.create(
                 course_id=course_id,
                 username=username,
                 change=CourseUpdate.candidate(usage_id))
         except Exception as e:
             print(e)
コード例 #56
0
    def update_parent_if_moved(self, original_parent_location, published_version, delete_draft_only, user_id):
        """
        Update parent of an item if it has moved.

        Arguments:
            original_parent_location (BlockUsageLocator)  : Original parent block locator.
            published_version (dict)   : Published version of the block.
            delete_draft_only (function)    : A callback function to delete draft children if it was moved.
            user_id (int)   : User id
        """
        for child_location in published_version.get('definition', {}).get('children', []):
            item_location = UsageKey.from_string(child_location).map_into_course(original_parent_location.course_key)
            try:
                source_item = self.get_item(item_location)
            except ItemNotFoundError:
                log.error('Unable to find the item %s', unicode(item_location))
                return

            if source_item.parent and source_item.parent.block_id != original_parent_location.block_id:
                if self.update_item_parent(item_location, original_parent_location, source_item.parent, user_id):
                    delete_draft_only(BlockUsageLocator.from_string(child_location))
コード例 #57
0
ファイル: test_services.py プロジェクト: stvstnfrd/completion
    def setUp(self):
        super(CompletionServiceTestCase, self).setUp()
        self.user = UserFactory.create()
        self.other_user = UserFactory.create()
        self.course_key = CourseKey.from_string("edX/MOOC101/2049_T2")
        self.other_course_key = CourseKey.from_string(
            "course-v1:ReedX+Hum110+1904")
        self.block_keys = [
            UsageKey.from_string(
                "i4x://edX/MOOC101/video/{}".format(number)).replace(
                    course_key=self.course_key) for number in range(5)
        ]
        self.other_course_block_keys = [
            self.other_course_key.make_usage_key('video', 'id')
        ]

        self.completion_service = CompletionService(self.user, self.course_key)

        # Proper completions for the given runtime
        for idx, block_key in enumerate(self.block_keys[0:3]):
            BlockCompletion.objects.submit_completion(
                user=self.user,
                block_key=block_key,
                completion=1.0 - (0.2 * idx),
            )

        # Wrong user
        for idx, block_key in enumerate(self.block_keys[2:]):
            BlockCompletion.objects.submit_completion(
                user=self.other_user,
                block_key=block_key,
                completion=0.9 - (0.2 * idx),
            )

        # Wrong course
        BlockCompletion.objects.submit_completion(
            user=self.user,
            block_key=self.other_course_block_keys[0],
            completion=0.75,
        )
コード例 #58
0
ファイル: views.py プロジェクト: cogbookspblc/cogbook_unity
    def post(self, request, usage_key_string):
        """
        Dispatch an AJAX action to an xblock

        Args:
            usage_id: The usage-id of the block to dispatch to
            handler (str): The handler to execute
            suffix (str): The remainder of the url to be passed to the handler

        Returns:
            :class:`django.http.HttpResponse`: The response from the handler, converted to a
                django response
        """
        handler = 'submit_studio_edits'
        suffix = ''
        usage_key = UsageKey.from_string(usage_key_string)
        # Let the module handle the AJAX
        req = django_to_webob_request(request)

        asides = []

        try:
            if isinstance(usage_key, (AsideUsageKeyV1, AsideUsageKeyV2)):
                descriptor = modulestore().get_item(usage_key.usage_key)
                aside_instance = get_xblock_aside_instance(usage_key)
                asides = [aside_instance] if aside_instance else []
                resp = aside_instance.handle(handler, req, suffix)
            else:
                descriptor = modulestore().get_item(usage_key)
                descriptor.xmodule_runtime = StudioEditModuleRuntime(
                    request.user)
                resp = descriptor.handle(handler, req, suffix)
        except NoSuchHandlerError:
            raise Http404

        # unintentional update to handle any side effects of handle call
        # could potentially be updating actual course data or simply caching its values
        modulestore().update_item(descriptor, request.user.id, asides=asides)

        return webob_to_django_response(resp)
コード例 #59
0
ファイル: draft.py プロジェクト: sam1610/ThemeEdx
    def _query_children_for_cache_children(self, course_key, items):
        # first get non-draft in a round-trip
        to_process_non_drafts = super(DraftModuleStore,
                                      self)._query_children_for_cache_children(
                                          course_key, items)

        to_process_dict = {}
        for non_draft in to_process_non_drafts:
            to_process_dict[BlockUsageLocator._from_deprecated_son(
                non_draft["_id"], course_key.run)] = non_draft

        if self.get_branch_setting() == ModuleStoreEnum.Branch.draft_preferred:
            # now query all draft content in another round-trip
            query = []
            for item in items:
                item_usage_key = UsageKey.from_string(item).map_into_course(
                    course_key)
                if item_usage_key.block_type not in DIRECT_ONLY_CATEGORIES:
                    query.append(as_draft(item_usage_key).to_deprecated_son())
            if query:
                query = {'_id': {'$in': query}}
                to_process_drafts = list(self.collection.find(query))

                # now we have to go through all drafts and replace the non-draft
                # with the draft. This is because the semantics of the DraftStore is to
                # always return the draft - if available
                for draft in to_process_drafts:
                    draft_loc = BlockUsageLocator._from_deprecated_son(
                        draft["_id"], course_key.run)
                    draft_as_non_draft_loc = as_published(draft_loc)

                    # does non-draft exist in the collection
                    # if so, replace it
                    if draft_as_non_draft_loc in to_process_dict:
                        to_process_dict[draft_as_non_draft_loc] = draft

        # convert the dict - which is used for look ups - back into a list
        queried_children = to_process_dict.values()

        return queried_children
コード例 #60
0
ファイル: api.py プロジェクト: edx/edx-bulk-grades
    def __init__(self, **kwargs):
        """
        Create GradeCSVProcessor.
        """
        # First, set some default values.
        self.columns = [
            'user_id', 'username', 'student_key', 'course_id', 'track',
            'cohort'
        ]
        self.course_id = None
        self.subsection_grade_max = None
        self.subsection_grade_min = None
        self.course_grade_min = None
        self.course_grade_max = None
        self.subsection = None
        self.track = None
        self.cohort = None
        self.user_id = None
        self.active_only = False
        self.excluded_course_roles = None

        # The CSVProcessor.__init__ method will set attributes on self
        # from items in kwargs, so this super().__init__() call can
        # override any attribute values assigned above.
        super().__init__(**kwargs)

        self._course_key = CourseKey.from_string(self.course_id)
        self._subsection = UsageKey.from_string(
            self.subsection) if self.subsection else None
        self._subsections = self._get_graded_subsections(
            self._course_key,
            filter_subsection=self._subsection,
            filter_assignment_type=kwargs.get('assignment_type', None),
        )
        self.append_columns(
            self._subsection_column_names(
                self._subsections.keys(),  # pylint: disable=useless-suppression
                self.subsection_prefixes))
        self._users_seen = defaultdict(list)
        self._row_num = 0