コード例 #1
0
    def populate(cls, course_descriptor):
        """
        Returns a fully populated CourseDetails model given the course descriptor
        """
        course_key = course_descriptor.id
        course_details = cls(course_key.org, course_key.course, course_key.run)
        course_details.start_date = course_descriptor.start
        course_details.end_date = course_descriptor.end
        course_details.enrollment_start = course_descriptor.enrollment_start
        course_details.enrollment_end = course_descriptor.enrollment_end
        course_details.enable_enrollment_email = course_descriptor.enable_enrollment_email
        course_details.pre_requisite_courses = course_descriptor.pre_requisite_courses
        course_details.course_image_name = course_descriptor.course_image
        course_details.course_image_asset_path = course_image_url(course_descriptor, 'course_image')
        course_details.banner_image_name = course_descriptor.banner_image
        course_details.banner_image_asset_path = course_image_url(course_descriptor, 'banner_image')
        course_details.video_thumbnail_image_name = course_descriptor.video_thumbnail_image
        course_details.video_thumbnail_image_asset_path = course_image_url(course_descriptor, 'video_thumbnail_image')
        course_details.language = course_descriptor.language
        course_details.self_paced = course_descriptor.self_paced
        course_details.learning_info = course_descriptor.learning_info
        course_details.instructor_info = course_descriptor.instructor_info

        # Default course license is "All Rights Reserved"
        course_details.license = getattr(course_descriptor, "license", "all-rights-reserved")

        course_details.intro_video = cls.fetch_youtube_video_id(course_key)

        for attribute in ABOUT_ATTRIBUTES:
            value = cls.fetch_about_attribute(course_key, attribute)
            if value is not None:
                setattr(course_details, attribute, value)

        return course_details
コード例 #2
0
    def fetch(cls, course_key):
        """
        Fetch the course details for the given course from persistence
        and return a CourseDetails model.
        """
        descriptor = modulestore().get_course(course_key)
        course_details = cls(course_key.org, course_key.course, course_key.run)

        course_details.start_date = descriptor.start
        course_details.end_date = descriptor.end
        course_details.enrollment_start = descriptor.enrollment_start
        course_details.enrollment_end = descriptor.enrollment_end
        course_details.pre_requisite_courses = descriptor.pre_requisite_courses
        course_details.course_image_name = descriptor.course_image
        course_details.course_image_asset_path = course_image_url(descriptor, 'course_image')
        course_details.banner_image_name = descriptor.banner_image
        course_details.banner_image_asset_path = course_image_url(descriptor, 'banner_image')
        course_details.video_thumbnail_image_name = descriptor.video_thumbnail_image
        course_details.video_thumbnail_image_asset_path = course_image_url(descriptor, 'video_thumbnail_image')
        course_details.language = descriptor.language
        course_details.self_paced = descriptor.self_paced

        # Default course license is "All Rights Reserved"
        course_details.license = getattr(descriptor, "license", "all-rights-reserved")

        course_details.intro_video = cls.fetch_youtube_video_id(course_key)

        for attribute in ABOUT_ATTRIBUTES:
            value = cls.fetch_about_attribute(course_key, attribute)
            if value is not None:
                setattr(course_details, attribute, value)

        return course_details
コード例 #3
0
    def _assert_image_urls_all_default(self, modulestore_type, raw_course_image_name, expected_url=None):
        """
        Helper for asserting that all image_urls are defaulting to a particular value.

        Returns the CourseOverview created. This function is useful when you
        know that the thumbnail generation process is going to fail in some way
        (e.g. unspecified source image, disabled config, runtime error) and want
        to verify that all the image URLs are a certain expected value (either
        the source image, or the fallback URL).
        """
        with self.store.default_store(modulestore_type):
            course = CourseFactory.create(
                default_store=modulestore_type, course_image=raw_course_image_name
            )
            if expected_url is None:
                expected_url = course_image_url(course)

            course_overview = CourseOverview.get_from_id(course.id)

            # All the URLs that come back should be for the expected_url
            self.assertEqual(
                course_overview.image_urls,
                {
                    'raw': expected_url,
                    'small': expected_url,
                    'large': expected_url,
                }
            )
            return course_overview
コード例 #4
0
ファイル: webview.py プロジェクト: francishan/edx-platform
def _update_course_context(request, context, course, course_key, platform_name):
    """
    Updates context dictionary with course info.
    """
    context['full_course_image_url'] = request.build_absolute_uri(course_image_url(course))
    course_title_from_cert = context['certificate_data'].get('course_title', '')
    accomplishment_copy_course_name = course_title_from_cert if course_title_from_cert else course.display_name
    context['accomplishment_copy_course_name'] = accomplishment_copy_course_name
    course_number = course.display_coursenumber if course.display_coursenumber else course.number
    context['course_number'] = course_number
    if context['organization_long_name']:
        # Translators:  This text represents the description of course
        context['accomplishment_copy_course_description'] = _('a course of study offered by {partner_short_name}, '
                                                              'an online learning initiative of '
                                                              '{partner_long_name}.').format(
            partner_short_name=context['organization_short_name'],
            partner_long_name=context['organization_long_name'],
            platform_name=platform_name)
    else:
        # Translators:  This text represents the description of course
        context['accomplishment_copy_course_description'] = _('a course of study offered by '
                                                              '{partner_short_name}.').format(
            partner_short_name=context['organization_short_name'],
            platform_name=platform_name)
    # If language specific templates are enabled for the course, add course_run specific information to the context
    if CertificateGenerationCourseSetting.is_language_specific_templates_enabled_for_course(course_key):
        fields = ['start', 'end', 'max_effort', 'language']
        course_run_data = get_course_run_details(course_key, fields)
        context.update(course_run_data)
コード例 #5
0
ファイル: tests.py プロジェクト: pio777/edx-platform
    def verify_success(self, response):
        """
        Verifies user course enrollment response for success
        """
        super(TestUserEnrollmentApi, self).verify_success(response)
        courses = response.data
        self.assertEqual(len(courses), 1)

        found_course = courses[0]['course']
        self.assertIn('courses/{}/about'.format(self.course.id),
                      found_course['course_about'])
        self.assertIn('course_info/{}/updates'.format(self.course.id),
                      found_course['course_updates'])
        self.assertIn('course_info/{}/handouts'.format(self.course.id),
                      found_course['course_handouts'])
        self.assertIn('video_outlines/courses/{}'.format(self.course.id),
                      found_course['video_outline'])
        self.assertEqual(found_course['id'], unicode(self.course.id))
        self.assertEqual(courses[0]['mode'], CourseMode.DEFAULT_MODE_SLUG)
        self.assertEqual(courses[0]['course']['subscription_id'],
                         self.course.clean_id(padding_char='_'))

        expected_course_image_url = course_image_url(self.course)
        self.assertIsNotNone(expected_course_image_url)
        self.assertIn(expected_course_image_url, found_course['course_image'])
        self.assertIn(expected_course_image_url,
                      found_course['media']['course_image']['uri'])
コード例 #6
0
ファイル: test_courses.py プロジェクト: hastexo/edx-platform
 def test_static_asset_path_course_image_default(self):
     """
     Test that without course_image being set, but static_asset_path
     being set that we get the right course_image url.
     """
     course = CourseFactory.create(static_asset_path="foo")
     self.assertEquals(course_image_url(course), "/static/foo/images/course_image.jpg")
コード例 #7
0
ファイル: test_courses.py プロジェクト: hastexo/edx-platform
 def test_spaces_in_image_name(self):
     # Verify that image names with spaces in them are cleaned
     course = CourseFactory.create(course_image=u"before after.jpg")
     self.assertEquals(
         course_image_url(course),
         "/c4x/{org}/{course}/asset/before_after.jpg".format(org=course.location.org, course=course.location.course),
     )
コード例 #8
0
ファイル: tasks.py プロジェクト: mitocw/edx-platform
def _get_course_email_context(course):
    """
    Returns context arguments to apply to all emails, independent of recipient.
    """
    course_id = text_type(course.id)
    course_title = course.display_name
    course_end_date = get_default_time_display(course.end)
    course_root = reverse('course_root', kwargs={'course_id': course_id})
    course_url = '{}{}'.format(
        settings.LMS_ROOT_URL,
        course_root
    )
    image_url = u'{}{}'.format(settings.LMS_ROOT_URL, course_image_url(course))
    email_context = {
        'course_title': course_title,
        'course_root': course_root,
        'course_language': course.language,
        'course_url': course_url,
        'course_image_url': image_url,
        'course_end_date': course_end_date,
        'account_settings_url': '{}{}'.format(settings.LMS_ROOT_URL, reverse('account_settings')),
        'email_settings_url': '{}{}'.format(settings.LMS_ROOT_URL, reverse('dashboard')),
        'platform_name': configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME),
    }
    return email_context
コード例 #9
0
 def get_expected_course_data(
     self, course, enrollment_start, enrollment_end,
     instructor, staff, expected_pacing_type
 ):
     return {
         'id': str(course.id),
         'title': course.display_name,
         'schedule': {
             'start': serialize_datetime(self.course_start),
             'end': serialize_datetime(self.course_end),
             'enrollment_start': enrollment_start,
             'enrollment_end': enrollment_end,
         },
         'team': [
             {
                 'user': instructor.username,
                 'role': 'instructor',
             },
             {
                 'user': staff.username,
                 'role': 'staff',
             },
         ],
         'images': {
             'card_image': self.request.build_absolute_uri(course_image_url(course)),
         },
         'pacing_type': expected_pacing_type,
     }
コード例 #10
0
ファイル: course.py プロジェクト: openfun/fun-apps
def course_infos(course):
    d = {
        'course_image_url': course_image_url(course)
    }
    for section in ['title', 'university']:
        d[section] = get_about_section(course, section)
    return d
コード例 #11
0
ファイル: test_courses.py プロジェクト: hastexo/edx-platform
 def test_static_asset_path_course_image_set(self):
     """
     Test that with course_image and static_asset_path both
     being set, that we get the right course_image url.
     """
     course = CourseFactory.create(course_image=u"things_stuff.jpg", static_asset_path="foo")
     self.assertEquals(course_image_url(course), "/static/foo/things_stuff.jpg")
コード例 #12
0
    def test_images_upload(self):
        # http://www.django-rest-framework.org/api-guide/parsers/#fileuploadparser
        course_run = CourseFactory()
        expected_filename = 'course_image.png'
        content_key = StaticContent.compute_location(course_run.id, expected_filename)

        assert course_run.course_image != expected_filename

        try:
            contentstore().find(content_key)
            self.fail('No image should be associated with a new course run.')
        except NotFoundError:
            pass

        url = reverse('api:v1:course_run-images', kwargs={'pk': str(course_run.id)})
        # PNG. Single black pixel
        content = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90wS' \
                  b'\xde\x00\x00\x00\x0cIDATx\x9cc```\x00\x00\x00\x04\x00\x01\xf6\x178U\x00\x00\x00\x00IEND\xaeB`\x82'

        # We are intentionally passing the incorrect JPEG extension here
        upload = SimpleUploadedFile('card_image.jpg', content, content_type='image/png')
        response = self.client.post(url, {'card_image': upload}, format='multipart')
        assert response.status_code == 200

        course_run = modulestore().get_course(course_run.id)
        assert course_run.course_image == expected_filename

        expected = {'card_image': RequestFactory().get('').build_absolute_uri(course_image_url(course_run))}
        assert response.data == expected

        # There should now be an image stored
        contentstore().find(content_key)
コード例 #13
0
ファイル: test_courses.py プロジェクト: hastexo/edx-platform
 def test_non_ascii_image_name(self):
     # Verify that non-ascii image names are cleaned
     course = CourseFactory.create(course_image=u"before_\N{SNOWMAN}_after.jpg")
     self.assertEquals(
         course_image_url(course),
         "/c4x/{org}/{course}/asset/before___after.jpg".format(
             org=course.location.org, course=course.location.course
         ),
     )
コード例 #14
0
    def test_disabled_with_prior_data(self, modulestore_type):
        """
        Test behavior when entries have been created but we are disabled.

        This might happen because a strange bug was introduced -- e.g. we
        corrupt the images somehow when making thumbnails. Expectations:

        1. We ignore whatever was created for the thumbnails, and image_urls
           returns the same as if no thumbnails had ever been generated. So
           basically, we return the raw source image for every resolution.
        2. We keep the CourseOverviewImageSet data around for debugging
           purposes.
        """
        course_image = "my_course.jpg"
        broken_small_url = "I am small!"
        broken_large_url = "I am big!"
        with self.store.default_store(modulestore_type):
            course = CourseFactory.create(
                default_store=modulestore_type, course_image=course_image
            )
            course_overview_before = CourseOverview.get_from_id(course.id)

        # This initial seeding should create an entry for the image_set.
        self.assertTrue(hasattr(course_overview_before, 'image_set'))

        # Now just throw in some fake data to this image set, something that
        # couldn't possibly work.
        course_overview_before.image_set.small_url = broken_small_url
        course_overview_before.image_set.large_url = broken_large_url
        course_overview_before.image_set.save()

        # Now disable the thumbnail feature
        self.set_config(False)

        # Fetch a new CourseOverview
        course_overview_after = CourseOverview.get_from_id(course.id)

        # Assert that the data still exists for debugging purposes
        self.assertTrue(hasattr(course_overview_after, 'image_set'))
        image_set = course_overview_after.image_set
        self.assertEqual(image_set.small_url, broken_small_url)
        self.assertEqual(image_set.large_url, broken_large_url)

        # But because we've disabled it, asking for image_urls should give us
        # the raw source image for all resolutions, and not our broken images.
        expected_url = course_image_url(course)
        self.assertEqual(
            course_overview_after.image_urls,
            {
                'raw': expected_url,
                'small': expected_url,
                'large': expected_url
            }
        )
コード例 #15
0
    def test_data(self, expected_pacing_type, self_paced):
        start = datetime.datetime.now(pytz.UTC)
        end = start + datetime.timedelta(days=30)
        enrollment_start = start - datetime.timedelta(days=7)
        enrollment_end = end - datetime.timedelta(days=14)

        course = CourseFactory(
            start=start,
            end=end,
            enrollment_start=enrollment_start,
            enrollment_end=enrollment_end,
            self_paced=self_paced
        )
        instructor = UserFactory()
        CourseInstructorRole(course.id).add_users(instructor)
        staff = UserFactory()
        CourseStaffRole(course.id).add_users(staff)

        request = RequestFactory().get('')
        serializer = CourseRunSerializer(course, context={'request': request})
        expected = {
            'id': str(course.id),
            'title': course.display_name,
            'schedule': {
                'start': serialize_datetime(start),
                'end': serialize_datetime(end),
                'enrollment_start': serialize_datetime(enrollment_start),
                'enrollment_end': serialize_datetime(enrollment_end),
            },
            'team': [
                {
                    'user': instructor.username,
                    'role': 'instructor',
                },
                {
                    'user': staff.username,
                    'role': 'staff',
                },
            ],
            'images': {
                'card_image': request.build_absolute_uri(course_image_url(course)),
            },
            'pacing_type': expected_pacing_type,
        }
        assert serializer.data == expected
コード例 #16
0
    def verify_success(self, response):
        """
        Verifies user course enrollment response for success
        """
        super(TestUserEnrollmentApi, self).verify_success(response)
        courses = response.data
        self.assertEqual(len(courses), 1)

        found_course = courses[0]["course"]
        self.assertIn("courses/{}/about".format(self.course.id), found_course["course_about"])
        self.assertIn("course_info/{}/updates".format(self.course.id), found_course["course_updates"])
        self.assertIn("course_info/{}/handouts".format(self.course.id), found_course["course_handouts"])
        self.assertIn("video_outlines/courses/{}".format(self.course.id), found_course["video_outline"])
        self.assertEqual(found_course["id"], unicode(self.course.id))
        self.assertEqual(courses[0]["mode"], CourseMode.DEFAULT_MODE_SLUG)
        self.assertEqual(courses[0]["course"]["subscription_id"], self.course.clean_id(padding_char="_"))

        expected_course_image_url = course_image_url(self.course)
        self.assertIsNotNone(expected_course_image_url)
        self.assertIn(expected_course_image_url, found_course["course_image"])
        self.assertIn(expected_course_image_url, found_course["media"]["course_image"]["uri"])
コード例 #17
0
ファイル: tasks.py プロジェクト: 28554010/edx-platform
def _get_course_email_context(course):
    """
    Returns context arguments to apply to all emails, independent of recipient.
    """
    course_id = course.id.to_deprecated_string()
    course_title = course.display_name
    course_end_date = get_default_time_display(course.end)
    course_url = 'https://{}{}'.format(
        settings.SITE_NAME,
        reverse('course_root', kwargs={'course_id': course_id})
    )
    image_url = u'https://{}{}'.format(settings.SITE_NAME, course_image_url(course))
    email_context = {
        'course_title': course_title,
        'course_url': course_url,
        'course_image_url': image_url,
        'course_end_date': course_end_date,
        'account_settings_url': 'https://{}{}'.format(settings.SITE_NAME, reverse('account_settings')),
        'email_settings_url': 'https://{}{}'.format(settings.SITE_NAME, reverse('dashboard')),
        'platform_name': settings.PLATFORM_NAME,
    }
    return email_context
コード例 #18
0
ファイル: webview.py プロジェクト: 28554010/edx-platform
def _update_course_context(request, context, course, platform_name):
    """
    Updates context dictionary with course info.
    """
    context['full_course_image_url'] = request.build_absolute_uri(course_image_url(course))
    course_title_from_cert = context['certificate_data'].get('course_title', '')
    accomplishment_copy_course_name = course_title_from_cert if course_title_from_cert else course.display_name
    context['accomplishment_copy_course_name'] = accomplishment_copy_course_name
    course_number = course.display_coursenumber if course.display_coursenumber else course.number
    context['course_number'] = course_number
    if context['organization_long_name']:
        # Translators:  This text represents the description of course
        context['accomplishment_copy_course_description'] = _('a course of study offered by {partner_short_name}, '
                                                              'an online learning initiative of '
                                                              '{partner_long_name}.').format(
            partner_short_name=context['organization_short_name'],
            partner_long_name=context['organization_long_name'],
            platform_name=platform_name)
    else:
        # Translators:  This text represents the description of course
        context['accomplishment_copy_course_description'] = _('a course of study offered by '
                                                              '{partner_short_name}.').format(
            partner_short_name=context['organization_short_name'],
            platform_name=platform_name)
コード例 #19
0
ファイル: course_runs.py プロジェクト: uetuluk/edx-platform
 def get_attribute(self, instance):
     return course_image_url(instance)
コード例 #20
0
    def index_about_information(cls, modulestore, course):
        """
        Add the given course to the course discovery index

        Arguments:
        modulestore - modulestore object to use for operations

        course - course object from which to take properties, locate about information
        """
        searcher = SearchEngine.get_search_engine(cls.INDEX_NAME)
        if not searcher:
            return

        course_id = text_type(course.id)
        course_info = {
            'id': course_id,
            'course': course_id,
            'content': {},
            'image_url': course_image_url(course),
        }

        # load data for all of the 'about' modules for this course into a dictionary
        about_dictionary = {
            item.location.block_id: item.data
            for item in modulestore.get_items(course.id, qualifiers={"category": "about"})
        }

        about_context = {
            "course": course,
            "about_dictionary": about_dictionary,
        }

        for about_information in cls.ABOUT_INFORMATION_TO_INCLUDE:
            # Broad exception handler so that a single bad property does not scupper the collection of others
            try:
                section_content = about_information.get_value(**about_context)
            except:  # pylint: disable=bare-except
                section_content = None
                log.warning(
                    u"Course discovery could not collect property %s for course %s",
                    about_information.property_name,
                    course_id,
                    exc_info=True,
                )

            if section_content:
                if about_information.index_flags & AboutInfo.ANALYSE:
                    analyse_content = section_content
                    if isinstance(section_content, string_types):
                        analyse_content = strip_html_content_to_text(section_content)
                    course_info['content'][about_information.property_name] = analyse_content
                if about_information.index_flags & AboutInfo.PROPERTY:
                    course_info[about_information.property_name] = section_content

        # Broad exception handler to protect around and report problems with indexing
        try:
            searcher.index(cls.DISCOVERY_DOCUMENT_TYPE, [course_info])
        except:
            log.exception(
                u"Course discovery indexing error encountered, course discovery index may be out of date %s",
                course_id,
            )
            raise

        log.debug(
            u"Successfully added %s course to the course discovery index",
            course_id
        )
コード例 #21
0
    def _create_or_update(cls, course):
        """
        Creates or updates a CourseOverview object from a CourseDescriptor.

        Does not touch the database, simply constructs and returns an overview
        from the given course.

        Arguments:
            course (CourseDescriptor): any course descriptor object

        Returns:
            CourseOverview: created or updated overview extracted from the given course
        """
        from lms.djangoapps.certificates.api import get_active_web_certificate
        from openedx.core.lib.courses import course_image_url

        # Workaround for a problem discovered in https://openedx.atlassian.net/browse/TNL-2806.
        # If the course has a malformed grading policy such that
        # course._grading_policy['GRADE_CUTOFFS'] = {}, then
        # course.lowest_passing_grade will raise a ValueError.
        # Work around this for now by defaulting to None.
        try:
            lowest_passing_grade = course.lowest_passing_grade
        except ValueError:
            lowest_passing_grade = None

        display_name = course.display_name
        start = course.start
        end = course.end
        max_student_enrollments_allowed = course.max_student_enrollments_allowed
        if isinstance(course.id, CCXLocator):
            from lms.djangoapps.ccx.utils import get_ccx_from_ccx_locator
            ccx = get_ccx_from_ccx_locator(course.id)
            display_name = ccx.display_name
            start = ccx.start
            end = ccx.due
            max_student_enrollments_allowed = ccx.max_student_enrollments_allowed

        course_overview = cls.objects.filter(id=course.id)
        if course_overview.exists():
            log.info('Updating course overview for %s.', unicode(course.id))
            course_overview = course_overview.first()
        else:
            log.info('Creating course overview for %s.', unicode(course.id))
            course_overview = cls()

        course_overview.version = cls.VERSION
        course_overview.id = course.id
        course_overview._location = course.location
        course_overview.org = course.location.org
        course_overview.display_name = display_name
        course_overview.display_number_with_default = course.display_number_with_default
        course_overview.display_org_with_default = course.display_org_with_default

        course_overview.start = start
        course_overview.end = end
        course_overview.advertised_start = course.advertised_start
        course_overview.announcement = course.announcement

        course_overview.course_image_url = course_image_url(course)
        course_overview.social_sharing_url = course.social_sharing_url

        course_overview.certificates_display_behavior = course.certificates_display_behavior
        course_overview.certificates_show_before_end = course.certificates_show_before_end
        course_overview.cert_html_view_enabled = course.cert_html_view_enabled
        course_overview.has_any_active_web_certificate = (get_active_web_certificate(course) is not None)
        course_overview.cert_name_short = course.cert_name_short
        course_overview.cert_name_long = course.cert_name_long
        course_overview.certificate_available_date = course.certificate_available_date
        course_overview.lowest_passing_grade = lowest_passing_grade
        course_overview.end_of_course_survey_url = course.end_of_course_survey_url

        course_overview.days_early_for_beta = course.days_early_for_beta
        course_overview.mobile_available = course.mobile_available
        course_overview.visible_to_staff_only = course.visible_to_staff_only
        course_overview._pre_requisite_courses_json = json.dumps(course.pre_requisite_courses)

        course_overview.enrollment_start = course.enrollment_start
        course_overview.enrollment_end = course.enrollment_end
        course_overview.enrollment_domain = course.enrollment_domain
        course_overview.invitation_only = course.invitation_only
        course_overview.max_student_enrollments_allowed = max_student_enrollments_allowed

        course_overview.catalog_visibility = course.catalog_visibility
        course_overview.short_description = CourseDetails.fetch_about_attribute(course.id, 'short_description')
        course_overview.effort = CourseDetails.fetch_about_attribute(course.id, 'effort')
        course_overview.course_video_url = CourseDetails.fetch_video_url(course.id)
        course_overview.self_paced = course.self_paced

        course_overview.language = course.language

        return course_overview
コード例 #22
0
 def get_course_image_url(self, course):
     """
     Builds course image url
     """
     return course.course_image_url if isinstance(
         course, CourseOverview) else course_image_url(course)
コード例 #23
0
 def test_spaces_in_image_name(self):
     # Verify that image names with spaces in them are cleaned
     course = CourseFactory.create(course_image='before after.jpg')
     assert course_image_url(course) == f'/c4x/{course.location.org}/{course.location.course}/asset/before_after.jpg'  # pylint: disable=line-too-long
コード例 #24
0
 def test_get_image_url(self):
     """Test image URL formatting."""
     course = CourseFactory.create(org='edX', course='999')
     self.assertEquals(course_image_url(course), '/c4x/edX/999/asset/{0}'.format(course.course_image))
コード例 #25
0
    def index_about_information(cls, modulestore, course):
        """
        Add the given course to the course discovery index

        Arguments:
        modulestore - modulestore object to use for operations

        course - course object from which to take properties, locate about information
        """
        searcher = SearchEngine.get_search_engine(cls.INDEX_NAME)
        if not searcher:
            return

        course_id = unicode(course.id)
        course_info = {
            'id': course_id,
            'course': course_id,
            'content': {},
            'image_url': course_image_url(course),
        }

        # load data for all of the 'about' modules for this course into a dictionary
        about_dictionary = {
            item.location.name: item.data
            for item in modulestore.get_items(course.id, qualifiers={"category": "about"})
        }

        about_context = {
            "course": course,
            "about_dictionary": about_dictionary,
        }

        for about_information in cls.ABOUT_INFORMATION_TO_INCLUDE:
            # Broad exception handler so that a single bad property does not scupper the collection of others
            try:
                section_content = about_information.get_value(**about_context)
            except:  # pylint: disable=bare-except
                section_content = None
                log.warning(
                    "Course discovery could not collect property %s for course %s",
                    about_information.property_name,
                    course_id,
                    exc_info=True,
                )

            if section_content:
                if about_information.index_flags & AboutInfo.ANALYSE:
                    analyse_content = section_content
                    if isinstance(section_content, basestring):
                        analyse_content = strip_html_content_to_text(section_content)
                    course_info['content'][about_information.property_name] = analyse_content
                    if about_information.property_name == "more_info":
                        course_info[about_information.property_name] = analyse_content
                if about_information.index_flags & AboutInfo.PROPERTY:
                    course_info[about_information.property_name] = section_content

        # Broad exception handler to protect around and report problems with indexing
        try:
            searcher.index(cls.DISCOVERY_DOCUMENT_TYPE, [course_info])
        except:  # pylint: disable=bare-except
            log.exception(
                "Course discovery indexing error encountered, course discovery index may be out of date %s",
                course_id,
            )
            raise

        log.debug(
            "Successfully added %s course to the course discovery index",
            course_id
        )
コード例 #26
0
 def test_non_ascii_image_name(self):
     course = self.process_xml(xml.CourseFactory.build(course_image=u'before_\N{SNOWMAN}_after.jpg'))
     self.assertEquals(course_image_url(course), u'/static/xml_test_course/before_\N{SNOWMAN}_after.jpg')
コード例 #27
0
 def test_get_image_url(self):
     """Test image URL formatting."""
     course = CourseFactory.create(org='edX', course='999')
     self.assertEquals(course_image_url(course), '/c4x/edX/999/asset/{0}'.format(course.course_image))
コード例 #28
0
ファイル: models.py プロジェクト: CraftAcademy/edx-platform
    def _create_from_course(cls, course):
        """
        Creates a CourseOverview object from a CourseDescriptor.

        Does not touch the database, simply constructs and returns an overview
        from the given course.

        Arguments:
            course (CourseDescriptor): any course descriptor object

        Returns:
            CourseOverview: overview extracted from the given course
        """
        from lms.djangoapps.certificates.api import get_active_web_certificate
        from openedx.core.lib.courses import course_image_url

        log.info('Creating course overview for %s.', unicode(course.id))

        # Workaround for a problem discovered in https://openedx.atlassian.net/browse/TNL-2806.
        # If the course has a malformed grading policy such that
        # course._grading_policy['GRADE_CUTOFFS'] = {}, then
        # course.lowest_passing_grade will raise a ValueError.
        # Work around this for now by defaulting to None.
        try:
            lowest_passing_grade = course.lowest_passing_grade
        except ValueError:
            lowest_passing_grade = None

        display_name = course.display_name
        start = course.start
        end = course.end
        max_student_enrollments_allowed = course.max_student_enrollments_allowed
        if isinstance(course.id, CCXLocator):
            from lms.djangoapps.ccx.utils import get_ccx_from_ccx_locator
            ccx = get_ccx_from_ccx_locator(course.id)
            display_name = ccx.display_name
            start = ccx.start
            end = ccx.due
            max_student_enrollments_allowed = ccx.max_student_enrollments_allowed

        return cls(
            version=cls.VERSION,
            id=course.id,
            _location=course.location,
            org=course.location.org,
            display_name=display_name,
            display_number_with_default=course.display_number_with_default,
            display_org_with_default=course.display_org_with_default,

            start=start,
            end=end,
            advertised_start=course.advertised_start,
            announcement=course.announcement,

            course_image_url=course_image_url(course),
            social_sharing_url=course.social_sharing_url,

            certificates_display_behavior=course.certificates_display_behavior,
            certificates_show_before_end=course.certificates_show_before_end,
            cert_html_view_enabled=course.cert_html_view_enabled,
            has_any_active_web_certificate=(get_active_web_certificate(course) is not None),
            cert_name_short=course.cert_name_short,
            cert_name_long=course.cert_name_long,
            lowest_passing_grade=lowest_passing_grade,
            end_of_course_survey_url=course.end_of_course_survey_url,

            days_early_for_beta=course.days_early_for_beta,
            mobile_available=course.mobile_available,
            visible_to_staff_only=course.visible_to_staff_only,
            _pre_requisite_courses_json=json.dumps(course.pre_requisite_courses),

            enrollment_start=course.enrollment_start,
            enrollment_end=course.enrollment_end,
            enrollment_domain=course.enrollment_domain,
            invitation_only=course.invitation_only,
            max_student_enrollments_allowed=max_student_enrollments_allowed,

            catalog_visibility=course.catalog_visibility,
            short_description=CourseDetails.fetch_about_attribute(course.id, 'short_description'),
            effort=CourseDetails.fetch_about_attribute(course.id, 'effort'),
            course_video_url=CourseDetails.fetch_video_url(course.id),
            self_paced=course.self_paced,
        )
コード例 #29
0
 def test_get_image_url(self):
     """Test image URL formatting."""
     course = self.process_xml(xml.CourseFactory.build())
     self.assertEquals(course_image_url(course), '/static/xml_test_course/images/course_image.jpg')
コード例 #30
0
 def test_non_ascii_image_name(self):
     # Verify that non-ascii image names are cleaned
     course = CourseFactory.create(
         course_image='before_\N{SNOWMAN}_after.jpg')
     assert course_image_url(course) == f'/c4x/{course.location.org}/{course.location.course}/asset/before___after.jpg'  # pylint: disable=line-too-long
コード例 #31
0
 def test_non_ascii_image_name(self):
     course = self.process_xml(xml.CourseFactory.build(course_image=u'before_\N{SNOWMAN}_after.jpg'))
     self.assertEquals(course_image_url(course), u'/static/xml_test_course/before_\N{SNOWMAN}_after.jpg')
コード例 #32
0
ファイル: tests.py プロジェクト: lipper/edx-platform
    def check_course_overview_against_course(self, course):
        """
        Compares a CourseOverview object against its corresponding
        CourseDescriptor object.

        Specifically, given a course, test that data within the following three
        objects match each other:
         - the CourseDescriptor itself
         - a CourseOverview that was newly constructed from _create_from_course
         - a CourseOverview that was loaded from the MySQL database

        Arguments:
            course (CourseDescriptor): the course to be checked.
        """

        def get_seconds_since_epoch(date_time):
            """
            Returns the number of seconds between the Unix Epoch and the given
                datetime. If the given datetime is None, return None.

            Arguments:
                date_time (datetime): the datetime in question.
            """
            if date_time is None:
                return None
            epoch = datetime.datetime.utcfromtimestamp(0).replace(tzinfo=pytz.utc)
            return math.floor((date_time - epoch).total_seconds())

        # Load the CourseOverview from the cache twice. The first load will be a cache miss (because the cache
        # is empty) so the course will be newly created with CourseOverviewDescriptor.create_from_course. The second
        # load will be a cache hit, so the course will be loaded from the cache.
        course_overview_cache_miss = CourseOverview.get_from_id(course.id)
        course_overview_cache_hit = CourseOverview.get_from_id(course.id)

        # Test if value of these attributes match between the three objects
        fields_to_test = [
            'id',
            'display_name',
            'display_number_with_default',
            'display_org_with_default',
            'advertised_start',
            'facebook_url',
            'social_sharing_url',
            'certificates_display_behavior',
            'certificates_show_before_end',
            'cert_name_short',
            'cert_name_long',
            'lowest_passing_grade',
            'end_of_course_survey_url',
            'mobile_available',
            'visible_to_staff_only',
            'location',
            'number',
            'url_name',
            'display_name_with_default',
            'display_name_with_default_escaped',
            'start_date_is_still_default',
            'pre_requisite_courses',
            'enrollment_domain',
            'invitation_only',
            'max_student_enrollments_allowed',
            'catalog_visibility',
        ]
        for attribute_name in fields_to_test:
            course_value = getattr(course, attribute_name)
            cache_miss_value = getattr(course_overview_cache_miss, attribute_name)
            cache_hit_value = getattr(course_overview_cache_hit, attribute_name)
            self.assertEqual(course_value, cache_miss_value)
            self.assertEqual(cache_miss_value, cache_hit_value)

        # Test if return values for all methods are equal between the three objects
        methods_to_test = [
            ('clean_id', ()),
            ('clean_id', ('#',)),
            ('has_ended', ()),
            ('has_started', ()),
            ('start_datetime_text', ('SHORT_DATE',)),
            ('start_datetime_text', ('DATE_TIME',)),
            ('end_datetime_text', ('SHORT_DATE',)),
            ('end_datetime_text', ('DATE_TIME',)),
            ('may_certify', ()),
        ]
        for method_name, method_args in methods_to_test:
            course_value = getattr(course, method_name)(*method_args)
            cache_miss_value = getattr(course_overview_cache_miss, method_name)(*method_args)
            cache_hit_value = getattr(course_overview_cache_hit, method_name)(*method_args)
            self.assertEqual(course_value, cache_miss_value)
            self.assertEqual(cache_miss_value, cache_hit_value)

        # Other values to test

        # Note: we test the time-related attributes here instead of in
        # fields_to_test, because we run into trouble while testing datetimes
        # for equality. When writing and reading dates from databases, the
        # resulting values are often off by fractions of a second. So, as a
        # workaround, we simply test if the start and end times are the same
        # number of seconds from the Unix epoch.
        time_field_accessor = lambda object, field_name: get_seconds_since_epoch(getattr(object, field_name))

        # The course about fields are accessed through the CourseDetail
        # class for the course module, and stored as attributes on the
        # CourseOverview objects.
        course_about_accessor = lambda object, field_name: CourseDetails.fetch_about_attribute(object.id, field_name)

        others_to_test = [
            ('start', time_field_accessor, time_field_accessor),
            ('end', time_field_accessor, time_field_accessor),
            ('enrollment_start', time_field_accessor, time_field_accessor),
            ('enrollment_end', time_field_accessor, time_field_accessor),
            ('announcement', time_field_accessor, time_field_accessor),

            ('short_description', course_about_accessor, getattr),
            ('effort', course_about_accessor, getattr),
            (
                'video',
                lambda c, __: CourseDetails.fetch_video_url(c.id),
                lambda c, __: c.course_video_url,
            ),
            (
                'course_image_url',
                lambda c, __: course_image_url(c),
                getattr,
            ),
            (
                'has_any_active_web_certificate',
                lambda c, field_name: get_active_web_certificate(c) is not None,
                getattr,
            ),
        ]
        for attribute_name, course_accessor, course_overview_accessor in others_to_test:
            course_value = course_accessor(course, attribute_name)
            cache_miss_value = course_overview_accessor(course_overview_cache_miss, attribute_name)
            cache_hit_value = course_overview_accessor(course_overview_cache_hit, attribute_name)
            self.assertEqual(course_value, cache_miss_value)
            self.assertEqual(cache_miss_value, cache_hit_value)

        # test tabs for both cached miss and cached hit courses
        for course_overview in [course_overview_cache_miss, course_overview_cache_hit]:
            course_overview_tabs = course_overview.tabs.all()
            course_resp_tabs = {tab.tab_id for tab in course_overview_tabs}
            self.assertEqual(self.COURSE_OVERVIEW_TABS, course_resp_tabs)
コード例 #33
0
ファイル: course_runs.py プロジェクト: AlexxNica/edx-platform
 def get_attribute(self, instance):
     return course_image_url(instance)
コード例 #34
0
    def _base_view(self, context=None):
        context = context is None and {'is_author_view': False} or context
        annoto_auth = self.get_annoto_settings()
        horizontal, vertical = self.get_position()
        translator = self.runtime.service(self, 'i18n').translator
        lang = getattr(
            translator, 'get_language', lambda: translator.info().get(
                'language', settings.LANGUAGE_CODE))()
        rtl = getattr(translator, 'get_language_bidi',
                      lambda: lang in settings.LANGUAGES_BIDI)()

        course = self.get_course_obj()
        course_overview = CourseOverview.objects.get(id=self.course_id)

        course_id = str(self.course_id)
        course_display_name = course.display_name
        user = self._get_user()
        if not context[
                'is_author_view'] and user and self.discussions_scope == 'cohort':
            from openedx.core.djangoapps.course_groups.cohorts import get_cohort
            cohort = get_cohort(user, self.course_id)
            if cohort:
                course_id = u'{}_{}'.format(course_id, cohort.id)
                course_display_name = u'{} [{}]'.format(
                    course_display_name, cohort.name)

        js_params = {
            'objectType': self.object_type,
            'clientId': annoto_auth.get('client_id'),
            'horizontal': horizontal,
            'vertical': vertical,
            'tabs': self.tabs,
            'overlayVideo': self.overlay_video,
            'initialState': self.initial_state,
            'privateThread': self.discussions_scope != 'site',
            'mediaTitle': self.get_parent().display_name,
            'language': lang,
            'rtl': rtl,
            'courseId': course_id,
            'courseDisplayName': course_display_name,
            'courseDescription': course_overview.short_description,
            'courseImage': course_image_url(course),
            'demoMode': not bool(annoto_auth.get('client_id')),
            'isLive': self.video_type == 'stream',
            'comments': 'comments' in self.features,
            'privateNotes': 'notes' in self.features,
            'videoBlockID': self.video_block_id,
        }

        context['error'] = {}
        if not annoto_auth.get('client_id'):
            context['error']['type'] = 'warning'
            context['error']['messages'] = [
                self.i18n_service.gettext(
                    'You did not provide annoto credentials. And you view it in demo mode.'
                ),
                self.i18n_service.gettext(
                    'Please add "annoto-auth:<CLIENT_ID>:<CLIENT_SECRET>" to "Advanced Settings" > "LTI Passports"'
                ),
            ]
        else:
            try:
                jwt.PyJWS().decode(annoto_auth.get('client_id'), verify=False)
            except:
                context['error']['type'] = 'error'
                context['error']['messages'] = [
                    self.i18n_service.gettext(
                        '"CLIENT_ID" is not a valid JWT token.'),
                    self.i18n_service.gettext(
                        'Please provide valid "CLIENT_ID" in '
                        '"Advanced Settings" > "LTI Passports" > "annoto-auth:<CLIENT_ID>:<CLIENT_SECRET>"'
                    ),
                ]
            else:
                if not annoto_auth.get('client_secret'):
                    context['error']['type'] = 'error'
                    context['error']['messages'] = [
                        self.i18n_service.gettext(
                            '"CLIENT_SECRET" is required when "CLIENT_ID" provided.'
                        ),
                        self.i18n_service.gettext(
                            'Please add "CLIENT_SECRET" to '
                            '"Advanced Settings" > "LTI Passports" > "annoto-auth:<CLIENT_ID>:<CLIENT_SECRET>"'
                        ),
                    ]

        template = Template(self.resource_string("static/html/annoto.html"))
        html = template.render(Context(context))
        frag = Fragment(html)
        frag.add_css(self.resource_string("static/css/annoto.css"))
        frag.add_javascript(self.resource_string("static/js/src/annoto.js"))
        frag.initialize_js('AnnotoXBlock', json_args=js_params)
        return frag
コード例 #35
0
ファイル: test_courses.py プロジェクト: yewtzeee/edx-platform
 def test_spaces_in_image_name(self):
     course = self.process_xml(
         xml.CourseFactory.build(course_image=u'before after.jpg'))
     self.assertEquals(course_image_url(course),
                       u'/static/xml_test_course/before after.jpg')
コード例 #36
0
    def check_course_overview_against_course(self, course):
        """
        Compares a CourseOverview object against its corresponding
        CourseDescriptor object.

        Specifically, given a course, test that data within the following three
        objects match each other:
         - the CourseDescriptor itself
         - a CourseOverview that was newly constructed from _create_or_update
         - a CourseOverview that was loaded from the MySQL database

        Arguments:
            course (CourseDescriptor): the course to be checked.
        """

        def get_seconds_since_epoch(date_time):
            """
            Returns the number of seconds between the Unix Epoch and the given
                datetime. If the given datetime is None, return None.

            Arguments:
                date_time (datetime): the datetime in question.
            """
            if date_time is None:
                return None
            epoch = datetime.datetime.utcfromtimestamp(0).replace(tzinfo=pytz.utc)
            return math.floor((date_time - epoch).total_seconds())

        # Load the CourseOverview from the cache twice. The first load will be a cache miss (because the cache
        # is empty) so the course will be newly created with CourseOverview._create_or_update. The second
        # load will be a cache hit, so the course will be loaded from the cache.
        course_overview_cache_miss = CourseOverview.get_from_id(course.id)
        course_overview_cache_hit = CourseOverview.get_from_id(course.id)

        # Test if value of these attributes match between the three objects
        fields_to_test = [
            'id',
            'display_name',
            'display_number_with_default',
            'display_org_with_default',
            'advertised_start',
            'social_sharing_url',
            'certificates_display_behavior',
            'certificates_show_before_end',
            'cert_name_short',
            'cert_name_long',
            'lowest_passing_grade',
            'end_of_course_survey_url',
            'mobile_available',
            'visible_to_staff_only',
            'location',
            'number',
            'url_name',
            'display_name_with_default',
            'display_name_with_default_escaped',
            'start_date_is_still_default',
            'pre_requisite_courses',
            'enrollment_domain',
            'invitation_only',
            'max_student_enrollments_allowed',
            'catalog_visibility',
        ]
        for attribute_name in fields_to_test:
            course_value = getattr(course, attribute_name)
            cache_miss_value = getattr(course_overview_cache_miss, attribute_name)
            cache_hit_value = getattr(course_overview_cache_hit, attribute_name)
            self.assertEqual(course_value, cache_miss_value)
            self.assertEqual(cache_miss_value, cache_hit_value)

        # Test if return values for all methods are equal between the three objects
        methods_to_test = [
            ('clean_id', ()),
            ('clean_id', ('#',)),
            ('has_ended', ()),
            ('has_started', ()),
            ('may_certify', ()),
        ]
        for method_name, method_args in methods_to_test:
            course_value = getattr(course, method_name)(*method_args)
            cache_miss_value = getattr(course_overview_cache_miss, method_name)(*method_args)
            cache_hit_value = getattr(course_overview_cache_hit, method_name)(*method_args)
            self.assertEqual(course_value, cache_miss_value)
            self.assertEqual(cache_miss_value, cache_hit_value)

        # Other values to test

        # Note: we test the time-related attributes here instead of in
        # fields_to_test, because we run into trouble while testing datetimes
        # for equality. When writing and reading dates from databases, the
        # resulting values are often off by fractions of a second. So, as a
        # workaround, we simply test if the start and end times are the same
        # number of seconds from the Unix epoch.
        time_field_accessor = lambda object, field_name: get_seconds_since_epoch(getattr(object, field_name))

        # The course about fields are accessed through the CourseDetail
        # class for the course module, and stored as attributes on the
        # CourseOverview objects.
        course_about_accessor = lambda object, field_name: CourseDetails.fetch_about_attribute(object.id, field_name)

        others_to_test = [
            ('start', time_field_accessor, time_field_accessor),
            ('end', time_field_accessor, time_field_accessor),
            ('enrollment_start', time_field_accessor, time_field_accessor),
            ('enrollment_end', time_field_accessor, time_field_accessor),
            ('announcement', time_field_accessor, time_field_accessor),

            ('short_description', course_about_accessor, getattr),
            ('effort', course_about_accessor, getattr),
            (
                'video',
                lambda c, __: CourseDetails.fetch_video_url(c.id),
                lambda c, __: c.course_video_url,
            ),
            (
                'course_image_url',
                lambda c, __: course_image_url(c),
                getattr,
            ),
            (
                'has_any_active_web_certificate',
                lambda c, field_name: get_active_web_certificate(c) is not None,
                getattr,
            ),
        ]
        for attribute_name, course_accessor, course_overview_accessor in others_to_test:
            course_value = course_accessor(course, attribute_name)
            cache_miss_value = course_overview_accessor(course_overview_cache_miss, attribute_name)
            cache_hit_value = course_overview_accessor(course_overview_cache_hit, attribute_name)
            self.assertEqual(course_value, cache_miss_value)
            self.assertEqual(cache_miss_value, cache_hit_value)

        # test tabs for both cached miss and cached hit courses
        for course_overview in [course_overview_cache_miss, course_overview_cache_hit]:
            course_overview_tabs = course_overview.tabs.all()
            course_resp_tabs = {tab.tab_id for tab in course_overview_tabs}
            self.assertEqual(self.COURSE_OVERVIEW_TABS, course_resp_tabs)
コード例 #37
0
ファイル: test_courses.py プロジェクト: hastexo/edx-platform
 def test_get_image_url(self):
     """Test image URL formatting."""
     course = CourseFactory.create(org="edX", course="999")
     self.assertEquals(course_image_url(course), "/c4x/edX/999/asset/{0}".format(course.course_image))
コード例 #38
0
    def handle(self, *args, **options):

        def get_detail(course_key, attribute):
            usage_key = course_key.make_usage_key('about', attribute)
            try:
                value = modulestore().get_item(usage_key).data
            except ItemNotFoundError:
                value = None
            return value

        def iso_date(thing):
            if isinstance(thing, datetime.datetime):
                return thing.isoformat()
            return thing

        exclusion_list = []
        inclusion_list = []

        if options['exclude_file']:
            try:
                with open(options['exclude_file'],'rb') as exclusion_file:
                    data = exclusion_file.readlines()
                exclusion_list = [x.strip() for x in data]
            except IOError:
                raise CommandError("Could not read exclusion list from '{0}'".format(options['exclude_file']))

        if options['include_file']:
            try:
                with open(options['include_file'],'rb') as inclusion_file:
                    data = inclusion_file.readlines()
                inclusion_list = [x.strip() for x in data]
            except IOError:
                raise CommandError("Could not read inclusion list from '{0}'".format(options['include_file']))

        store = modulestore()
        epoch = int(time.time())
        blob = {
            'epoch': epoch,
            'courses': [],
        }

        # For course TOC we need a user and a request. Find the first superuser defined,
        # that will be our user.
        request_user = User.objects.filter(is_superuser=True).first()
        factory = RequestFactory()

        for course in store.get_courses():

            course_id_string = course.id.to_deprecated_string()

            if options['single_course']:
                if course_id_string not in [options['single_course'].strip()]:
                    continue
            elif inclusion_list:
                if not course_id_string in inclusion_list:
                    continue
            elif exclusion_list:
                if course_id_string in exclusion_list:
                    continue

            print "Processing {}".format(course_id_string)

            students = CourseEnrollment.objects.users_enrolled_in(course.id)

            # The method of getting a table of contents for a course is quite obtuse.
            # We have to go all the way to simulating a request.

            request = factory.get('/')
            request.user = request_user

            raw_blocks = get_blocks(request, store.make_course_usage_key(course.id), request_user, 
                                requested_fields=['id', 'type', 'display_name', 'children', 'lms_web_url'])

            # We got the block structure. Now we need to massage it so we get the proper jump urls without site domain.
            # Because on the test server the site domain is wrong.
            blocks = {}
            for block_key, block in raw_blocks['blocks'].items():
                try:
                    direct_url = '/courses/' + block.get('lms_web_url').split('/courses/')[1]
                except IndexError:
                    direct_url = ''
                blocks[block_key] = {
                    'id': block.get('id', ''),
                    'display_name': block.get('display_name', ''),
                    'type': block.get('type', ''),
                    'children_ids': block.get('children', []),
                    'url': direct_url
                }

            # Then we need to recursively stitch it into a tree.
            # We're only interested in three layers of the hierarchy for now: 'course', 'chapter', 'sequential', 'vertical'.
            # Everything else is the individual blocks and problems we don't care about right now.

            INTERESTING_BLOCKS = ['course', 'chapter', 'sequential', 'vertical']

            def _get_children(parent):
                children = [blocks.get(n) for n in parent['children_ids'] if blocks.get(n)] # and blocks.get(n)['type'] in INTERESTING_BLOCKS]
                for child in children:
                    child['children'] = _get_children(child)
                parent['children'] = children
                del parent['children_ids']
                return children

            block_tree = _get_children(blocks[raw_blocks['root']])

            course_block = {
              'id': course_id_string,
              'meta_data': {
                'about': {
                    'display_name': course.display_name,
                    'media': {
                        'course_image': course_image_url(course),
                    }
                },
                'block_tree': block_tree,
                # Yes, I'm duplicating them for now, because the about section is shot.
                'display_name': course.display_name,
                'banner': course_image_url(course),
                'id_org': course.org,
                'id_number': course.number,
                'graded': course.graded,
                'hidden': course.visible_to_staff_only,
                'ispublic': not (course.visible_to_staff_only or False), # course.ispublic was removed in dogwood.
                'grading_policy': course.grading_policy,
                'advanced_modules': course.advanced_modules,
                'lowest_passing_grade': course.lowest_passing_grade,
                'start': iso_date(course.start),
                'advertised_start': iso_date(course.advertised_start),
                'end': iso_date(course.end),
                'enrollment_end': iso_date(course.enrollment_end),
                'enrollment_start': iso_date(course.enrollment_start),
                'has_started': course.has_started(),
                'has_ended': course.has_ended(),
                'overview': get_detail(course.id,'overview'),
                'short_description': get_detail(course.id,'short_description'),
                'pre_requisite_courses': get_detail(course.id,'pre_requisite_courses'),
                'video': get_detail(course.id,'video'),
              },
              'students': [x.username for x in students],
              'global_anonymous_id': { x.username:anonymous_id_for_user(x, None) for x in students },
              'local_anonymous_id': { x.username:anonymous_id_for_user(x, course.id) for x in students },
            }

            if not options['meta_only']:
                blob['grading_data_epoch'] = epoch
                course_block['grading_data'] = []
                # Grab grades for all students that have ever had anything to do with the course.
                graded_students = User.objects.filter(pk__in=CourseEnrollment.objects.filter(course_id=course.id).values_list('user',flat=True))
                print "{0} graded students in course {1}".format(graded_students.count(),course_id_string)
                if graded_students.count():
                    for student, gradeset, error_message \
                        in iterate_grades_for(course.id, graded_students):
                        if gradeset:
                            course_block['grading_data'].append({
                                'username': student.username,
                                'grades': gradeset,
                            })
                        else:
                            print error_message

            blob['courses'].append(course_block)
        if options['output']:
            # Ensure the dump is atomic.
            with tempfile.NamedTemporaryFile('w', dir=os.path.dirname(options['output']), delete=False) as output_file:
                json.dump(blob, output_file, default=json_util.default)
                tempname = output_file.name
            os.rename(tempname, options['output'])
        else:
            print "Blob output:"
            print json.dumps(blob, indent=2, ensure_ascii=False, default=json_util.default)
コード例 #39
0
    def handle(self, *args, **options):
        def get_detail(course_key, attribute):
            usage_key = course_key.make_usage_key("about", attribute)
            try:
                value = modulestore().get_item(usage_key).data
            except ItemNotFoundError:
                value = None
            return value

        def iso_date(thing):
            if isinstance(thing, datetime.datetime):
                return thing.isoformat()
            return thing

        exclusion_list = []
        inclusion_list = []

        if options["exclude_file"]:
            try:
                with open(options["exclude_file"], "rb") as exclusion_file:
                    data = exclusion_file.readlines()
                exclusion_list = [x.strip() for x in data]
            except IOError:
                raise CommandError("Could not read exclusion list from '{0}'".format(options["exclude_file"]))

        if options["include_file"]:
            try:
                with open(options["include_file"], "rb") as inclusion_file:
                    data = inclusion_file.readlines()
                inclusion_list = [x.strip() for x in data]
            except IOError:
                raise CommandError("Could not read inclusion list from '{0}'".format(options["include_file"]))

        store = modulestore()
        epoch = int(time.time())
        blob = {"epoch": epoch, "courses": []}

        for course in store.get_courses():

            course_id_string = course.id.to_deprecated_string()

            if options["single_course"]:
                if course_id_string not in [options["single_course"].strip()]:
                    continue
            elif inclusion_list:
                if not course_id_string in inclusion_list:
                    continue
            elif exclusion_list:
                if course_id_string in exclusion_list:
                    continue

            print "Processing {}".format(course_id_string)

            students = CourseEnrollment.objects.users_enrolled_in(course.id)

            course_block = {
                "id": course_id_string,
                "meta_data": {
                    "about": {"display_name": course.display_name, "media": {"course_image": course_image_url(course)}},
                    # Yes, I'm duplicating them for now, because the about section is shot.
                    "display_name": course.display_name,
                    "banner": course_image_url(course),
                    "id_org": course.org,
                    "id_number": course.number,
                    "graded": course.graded,
                    "hidden": course.visible_to_staff_only,
                    "ispublic": not (course.visible_to_staff_only or False),  # course.ispublic was removed in dogwood.
                    "grading_policy": course.grading_policy,
                    "advanced_modules": course.advanced_modules,
                    "lowest_passing_grade": course.lowest_passing_grade,
                    "start": iso_date(course.start),
                    "advertised_start": iso_date(course.advertised_start),
                    "end": iso_date(course.end),
                    "enrollment_end": iso_date(course.enrollment_end),
                    "enrollment_start": iso_date(course.enrollment_start),
                    "has_started": course.has_started(),
                    "has_ended": course.has_ended(),
                    "overview": get_detail(course.id, "overview"),
                    "short_description": get_detail(course.id, "short_description"),
                    "pre_requisite_courses": get_detail(course.id, "pre_requisite_courses"),
                    "video": get_detail(course.id, "video"),
                },
                "students": [x.username for x in students],
                "global_anonymous_id": {x.username: anonymous_id_for_user(x, None) for x in students},
                "local_anonymous_id": {x.username: anonymous_id_for_user(x, course.id) for x in students},
            }

            if not options["meta_only"]:
                blob["grading_data_epoch"] = epoch
                course_block["grading_data"] = []
                # Grab grades for all students that have ever had anything to do with the course.
                graded_students = User.objects.filter(
                    pk__in=CourseEnrollment.objects.filter(course_id=course.id).values_list("user", flat=True)
                )
                print "{0} graded students in course {1}".format(graded_students.count(), course_id_string)
                if graded_students.count():
                    for student, gradeset, error_message in iterate_grades_for(course.id, graded_students):
                        if gradeset:
                            course_block["grading_data"].append({"username": student.username, "grades": gradeset})
                        else:
                            print error_message

            blob["courses"].append(course_block)
        if options["output"]:
            # Ensure the dump is atomic.
            with tempfile.NamedTemporaryFile("w", dir=os.path.dirname(options["output"]), delete=False) as output_file:
                json.dump(blob, output_file)
                tempname = output_file.name
            os.rename(tempname, options["output"])
        else:
            print "Blob output:"
            print json.dumps(blob, indent=2, ensure_ascii=False)
コード例 #40
0
 def test_get_image_url(self):
     """Test image URL formatting."""
     course = self.process_xml(xml.CourseFactory.build())
     self.assertEquals(course_image_url(course), '/static/xml_test_course/images/course_image.jpg')
コード例 #41
0
ファイル: serializers.py プロジェクト: 28554010/edx-platform
 def get_image_url(self, course):
     """ Get the course image URL """
     return course_image_url(course)
コード例 #42
0
 def test_spaces_in_image_name(self):
     course = self.process_xml(xml.CourseFactory.build(course_image=u'before after.jpg'))
     self.assertEquals(course_image_url(course), u'/static/xml_test_course/before after.jpg')
コード例 #43
0
 def test_get_image_url(self):
     """Test image URL formatting."""
     course = CourseFactory.create(org='edX', course='999')
     assert course_image_url(
         course) == f'/c4x/edX/999/asset/{course.course_image}'