コード例 #1
0
    def handle(self, *args, **options):
        course_keys = []

        if options['all']:
            course_keys = [course.id for course in modulestore().get_courses()]
        else:
            if len(args) < 1:
                raise CommandError('At least one course or --all must be specified.')
            try:
                course_keys = [CourseKey.from_string(arg) for arg in args]
            except InvalidKeyError:
                log.fatal('Invalid key specified.')

        if not course_keys:
            log.fatal('No courses specified.')

        log.info('Generating course overview for %d courses.', len(course_keys))
        log.debug('Generating course overview(s) for the following courses: %s', course_keys)

        for course_key in course_keys:
            try:
                CourseOverview.get_from_id(course_key)
            except Exception as ex:  # pylint: disable=broad-except
                log.exception('An error occurred while generating course overview for %s: %s', unicode(
                    course_key), ex.message)

        log.info('Finished generating course overviews.')
コード例 #2
0
 def setUp(self):
     super(TestSyncCourseRunsCommand, self).setUp()
     # create mongo course
     self.course = CourseFactory.create()
     # load this course into course overview
     CourseOverview.get_from_id(self.course.id)
     # create a catalog course run with the same course id.
     self.catalog_course_run = CourseRunFactory(
         key=unicode(self.course.id),
         marketing_url='test_marketing_url'
     )
コード例 #3
0
ファイル: views.py プロジェクト: digitalsatori/edx-platform
 def course_run(self):
     """
     The course run specified by the `course_id` URL parameter.
     """
     try:
         CourseOverview.get_from_id(self.course_key)
     except CourseOverview.DoesNotExist:
         raise Http404()
     for course in self.program["courses"]:
         for course_run in course["course_runs"]:
             if self.course_key == CourseKey.from_string(course_run["key"]):
                 return course_run
     raise Http404()
コード例 #4
0
ファイル: support.py プロジェクト: AlexxNica/edx-platform
def generate_certificate_for_user(request):
    """
    Generate certificates for a user.

    This is meant to be used by support staff through the UI in lms/djangoapps/support

    Arguments:
        request (HttpRequest): The request object

    Returns:
        HttpResponse

    Example Usage:

        POST /certificates/generate
            * username: "******"
            * course_key: "edX/DemoX/Demo_Course"

        Response: 200 OK

    """
    # Check the POST parameters, returning a 400 response if they're not valid.
    params, response = _validate_post_params(request.POST)
    if response is not None:
        return response

    try:
        # Check that the course exists
        CourseOverview.get_from_id(params["course_key"])
    except CourseOverview.DoesNotExist:
        msg = _("The course {course_key} does not exist").format(course_key=params["course_key"])
        return HttpResponseBadRequest(msg)
    else:
        # Check that the user is enrolled in the course
        if not CourseEnrollment.is_enrolled(params["user"], params["course_key"]):
            msg = _("User {username} is not enrolled in the course {course_key}").format(
                username=params["user"].username,
                course_key=params["course_key"]
            )
            return HttpResponseBadRequest(msg)

        # Attempt to generate certificate
        generate_certificates_for_students(
            request,
            params["course_key"],
            student_set="specific_student",
            specific_student_id=params["user"].id
        )
        return HttpResponse(200)
コード例 #5
0
ファイル: factories.py プロジェクト: cpennington/edx-platform
    def _create(cls, model_class, *args, **kwargs):
        manager = cls._get_manager(model_class)
        course_kwargs = {}
        for key in kwargs.keys():
            if key.startswith('course__'):
                course_kwargs[key.split('__')[1]] = kwargs.pop(key)

        if 'course' not in kwargs:
            course_id = kwargs.get('course_id')
            course_overview = None
            if course_id is not None:
                if isinstance(course_id, six.string_types):
                    course_id = CourseKey.from_string(course_id)
                    course_kwargs.setdefault('id', course_id)

                try:
                    course_overview = CourseOverview.get_from_id(course_id)
                except CourseOverview.DoesNotExist:
                    pass

            if course_overview is None:
                course_overview = CourseOverviewFactory(**course_kwargs)
            kwargs['course'] = course_overview

        return manager.create(*args, **kwargs)
コード例 #6
0
    def _assert_supplemented(self, actual, **kwargs):
        """DRY helper used to verify that program data is extended correctly."""
        course_overview = CourseOverview.get_from_id(self.course.id)  # pylint: disable=no-member
        run_mode = dict(
            factories.RunMode(
                certificate_url=None,
                course_image_url=course_overview.course_image_url,
                course_key=unicode(self.course.id),  # pylint: disable=no-member
                course_url=reverse('course_root', args=[self.course.id]),  # pylint: disable=no-member
                end_date=strftime_localized(self.course.end, 'SHORT_DATE'),
                enrollment_open_date=strftime_localized(utils.DEFAULT_ENROLLMENT_START_DATE, 'SHORT_DATE'),
                is_course_ended=self.course.end < timezone.now(),
                is_enrolled=False,
                is_enrollment_open=True,
                marketing_url=MARKETING_URL,
                start_date=strftime_localized(self.course.start, 'SHORT_DATE'),
                upgrade_url=None,
            ),
            **kwargs
        )
        course_code = factories.CourseCode(display_name=self.course_code['display_name'], run_modes=[run_mode])
        expected = copy.deepcopy(self.program)
        expected['course_codes'] = [course_code]

        self.assertEqual(actual, expected)
コード例 #7
0
    def test_masquerade_expired(self, mock_get_course_run_details):
        mock_get_course_run_details.return_value = {'weeks_to_complete': 1}

        audit_student = UserFactory(username='******')
        enrollment = CourseEnrollmentFactory.create(
            user=audit_student,
            course_id=self.course.id,
            mode='audit',
        )
        enrollment.created = self.course.start
        enrollment.save()
        CourseDurationLimitConfig.objects.create(
            enabled=True,
            course=CourseOverview.get_from_id(self.course.id),
            enabled_as_of=self.course.start,
        )

        instructor = UserFactory.create(username='******')
        CourseEnrollmentFactory.create(
            user=instructor,
            course_id=self.course.id,
            mode='audit'
        )
        CourseInstructorRole(self.course.id).add_users(instructor)
        self.client.login(username=instructor.username, password='******')

        self.update_masquerade(username='******')

        course_home_url = reverse('openedx.course_experience.course_home', args=[six.text_type(self.course.id)])
        response = self.client.get(course_home_url, follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertItemsEqual(response.redirect_chain, [])
        banner_text = 'This learner does not have access to this course. Their access expired on'
        self.assertIn(banner_text, response.content)
コード例 #8
0
ファイル: test_access.py プロジェクト: luisvasq/edx-platform
    def test_course_catalog_access_num_queries(self, user_attr_name, action, course_attr_name):
        course = getattr(self, course_attr_name)

        # get a fresh user object that won't have any cached role information
        if user_attr_name == 'user_anonymous':
            user = AnonymousUserFactory()
        else:
            user = getattr(self, user_attr_name)
            user = User.objects.get(id=user.id)

        if (user_attr_name == 'user_staff' and
            action == 'see_exists' and
            course_attr_name in
                ['course_default', 'course_not_started']):
            # checks staff role
            num_queries = 1
        elif user_attr_name == 'user_normal' and action == 'see_exists' and course_attr_name != 'course_started':
            # checks staff role and enrollment data
            num_queries = 2
        else:
            num_queries = 0

        course_overview = CourseOverview.get_from_id(course.id)
        with self.assertNumQueries(num_queries, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST):
            bool(access.has_access(user, action, course_overview, course_key=course.id))
コード例 #9
0
ファイル: utils.py プロジェクト: Fadykhallaf/learnlink
def supplement_program_data(program_data, user):
    """Supplement program course codes with CourseOverview and CourseEnrollment data.

    Arguments:
        program_data (dict): Representation of a program.
        user (User): The user whose enrollments to inspect.
    """
    for course_code in program_data['course_codes']:
        for run_mode in course_code['run_modes']:
            course_key = CourseKey.from_string(run_mode['course_key'])
            course_overview = CourseOverview.get_from_id(course_key)

            run_mode['course_url'] = reverse('course_root', args=[course_key])
            run_mode['course_image_url'] = course_overview.course_image_url

            human_friendly_format = '%x'
            start_date = course_overview.start or DEFAULT_START_DATE
            end_date = course_overview.end or datetime.datetime.max.replace(tzinfo=pytz.UTC)
            run_mode['start_date'] = start_date.strftime(human_friendly_format)
            run_mode['end_date'] = end_date.strftime(human_friendly_format)

            run_mode['is_enrolled'] = CourseEnrollment.is_enrolled(user, course_key)

            enrollment_start = course_overview.enrollment_start or datetime.datetime.min.replace(tzinfo=pytz.UTC)
            enrollment_end = course_overview.enrollment_end or datetime.datetime.max.replace(tzinfo=pytz.UTC)
            is_enrollment_open = enrollment_start <= timezone.now() < enrollment_end
            run_mode['is_enrollment_open'] = is_enrollment_open

            # TODO: Currently unavailable on LMS.
            run_mode['marketing_url'] = ''

    return program_data
コード例 #10
0
    def test_student_has_access(self):
        """
        Tests course student have right access to content w/o preview.
        """
        course_key = self.course.id
        chapter = ItemFactory.create(category="chapter", parent_location=self.course.location)
        overview = CourseOverview.get_from_id(course_key)

        # Enroll student to the course
        CourseEnrollmentFactory(user=self.student, course_id=self.course.id)

        modules = [
            self.course,
            overview,
            chapter,
        ]
        with patch('courseware.access.in_preview_mode') as mock_preview:
            mock_preview.return_value = False
            for obj in modules:
                self.assertTrue(bool(access.has_access(self.student, 'load', obj, course_key=self.course.id)))

        with patch('courseware.access.in_preview_mode') as mock_preview:
            mock_preview.return_value = True
            for obj in modules:
                self.assertFalse(bool(access.has_access(self.student, 'load', obj, course_key=self.course.id)))
コード例 #11
0
    def test_get_course_details_course_dates(self, start_datetime, end_datetime, expected_start, expected_end):
        course = CourseFactory.create(start=start_datetime, end=end_datetime)
        # Load a CourseOverview. This initial load should result in a cache
        # miss; the modulestore is queried and course metadata is cached.
        __ = CourseOverview.get_from_id(course.id)

        self.assert_enrollment_status(course_id=unicode(course.id))

        # Check course details
        url = reverse('courseenrollmentdetails', kwargs={"course_id": unicode(course.id)})
        resp = self.client.get(url)
        self.assertEqual(resp.status_code, status.HTTP_200_OK)

        data = json.loads(resp.content)
        self.assertEqual(data['course_start'], expected_start)
        self.assertEqual(data['course_end'], expected_end)

        # Check enrollment course details
        url = reverse('courseenrollment', kwargs={"course_id": unicode(course.id)})
        resp = self.client.get(url)
        self.assertEqual(resp.status_code, status.HTTP_200_OK)

        data = json.loads(resp.content)
        self.assertEqual(data['course_details']['course_start'], expected_start)
        self.assertEqual(data['course_details']['course_end'], expected_end)

        # Check enrollment list course details
        resp = self.client.get(reverse('courseenrollments'))
        self.assertEqual(resp.status_code, status.HTTP_200_OK)

        data = json.loads(resp.content)
        self.assertEqual(data[0]['course_details']['course_start'], expected_start)
        self.assertEqual(data[0]['course_details']['course_end'], expected_end)
コード例 #12
0
    def test_expired_course(self):
        """
        Ensure that a user accessing an expired course sees a redirect to
        the student dashboard, not a 404.
        """
        CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime(2010, 1, 1))
        course = CourseFactory.create(start=THREE_YEARS_AGO)
        url = course_home_url(course)

        for mode in [CourseMode.AUDIT, CourseMode.VERIFIED]:
            CourseModeFactory.create(course_id=course.id, mode_slug=mode)

        # assert that an if an expired audit user tries to access the course they are redirected to the dashboard
        audit_user = UserFactory(password=self.TEST_PASSWORD)
        self.client.login(username=audit_user.username, password=self.TEST_PASSWORD)
        audit_enrollment = CourseEnrollment.enroll(audit_user, course.id, mode=CourseMode.AUDIT)
        ScheduleFactory(start=THREE_YEARS_AGO, enrollment=audit_enrollment)

        response = self.client.get(url)

        expiration_date = strftime_localized(course.start + timedelta(weeks=4), 'SHORT_DATE')
        expected_params = QueryDict(mutable=True)
        course_name = CourseOverview.get_from_id(course.id).display_name_with_default
        expected_params['access_response_error'] = 'Access to {run} expired on {expiration_date}'.format(
            run=course_name,
            expiration_date=expiration_date
        )
        expected_url = '{url}?{params}'.format(
            url=reverse('dashboard'),
            params=expected_params.urlencode()
        )
        self.assertRedirects(response, expected_url)
コード例 #13
0
ファイル: api.py プロジェクト: cmscom/edx-platform
def certificate_downloadable_status(student, course_key):
    """
    Check the student existing certificates against a given course.
    if status is not generating and not downloadable or error then user can view the generate button.

    Args:
        student (user object): logged-in user
        course_key (CourseKey): ID associated with the course

    Returns:
        Dict containing student passed status also download url, uuid for cert if available
    """
    current_status = certificate_status_for_student(student, course_key)

    # If the certificate status is an error user should view that status is "generating".
    # On the back-end, need to monitor those errors and re-submit the task.

    response_data = {
        'is_downloadable': False,
        'is_generating': True if current_status['status'] in [CertificateStatuses.generating,
                                                              CertificateStatuses.error] else False,
        'is_unverified': True if current_status['status'] == CertificateStatuses.unverified else False,
        'download_url': None,
        'uuid': None,
    }
    may_view_certificate = CourseOverview.get_from_id(course_key).may_certify()

    if current_status['status'] == CertificateStatuses.downloadable and may_view_certificate:
        response_data['is_downloadable'] = True
        response_data['download_url'] = current_status['download_url'] or get_certificate_url(student.id, course_key)
        response_data['uuid'] = current_status['uuid']

    return response_data
コード例 #14
0
    def setUp(self):
        """ Create a course and user, then log in. """
        super(EnrollmentTest, self).setUp()

        self.rate_limit_config = RateLimitConfiguration.current()
        self.rate_limit_config.enabled = False
        self.rate_limit_config.save()

        throttle = EnrollmentUserThrottle()
        self.rate_limit, rate_duration = throttle.parse_rate(throttle.rate)

        self.course = CourseFactory.create()
        # Load a CourseOverview. This initial load should result in a cache
        # miss; the modulestore is queried and course metadata is cached.
        __ = CourseOverview.get_from_id(self.course.id)

        self.user = UserFactory.create(
            username=self.USERNAME,
            email=self.EMAIL,
            password=self.PASSWORD,
        )
        self.other_user = UserFactory.create(
            username=self.OTHER_USERNAME,
            email=self.OTHER_EMAIL,
            password=self.PASSWORD,
        )
        self.client.login(username=self.USERNAME, password=self.PASSWORD)
コード例 #15
0
ファイル: data.py プロジェクト: jolyonb/edx-platform
def get_course_enrollment_info(course_id, include_expired=False):
    """Returns all course enrollment information for the given course.

    Based on the course id, return all related course information.

    Args:
        course_id (str): The course to retrieve enrollment information for.

        include_expired (bool): Boolean denoting whether expired course modes
        should be included in the returned JSON data.

    Returns:
        A serializable dictionary representing the course's enrollment information.

    Raises:
        CourseNotFoundError

    """
    course_key = CourseKey.from_string(course_id)

    try:
        course = CourseOverview.get_from_id(course_key)
    except CourseOverview.DoesNotExist:
        msg = u"Requested enrollment information for unknown course {course}".format(course=course_id)
        log.warning(msg)
        raise CourseNotFoundError(msg)
    else:
        return CourseSerializer(course, include_expired=include_expired).data
コード例 #16
0
ファイル: models.py プロジェクト: albluqmun/edx-platform
    def get_days_until_expiration(self, entitlement):
        """
        Returns an integer of number of days until the entitlement expires.
        Includes the logic for regaining an entitlement.
        """
        now_timestamp = now()
        expiry_date = entitlement.created + self.expiration_period
        days_until_expiry = (expiry_date - now_timestamp).days
        if not entitlement.enrollment_course_run:
            return days_until_expiry
        course_overview = CourseOverview.get_from_id(entitlement.enrollment_course_run.course_id)
        # Compute the days left for the regain
        days_since_course_start = (now_timestamp - course_overview.start).days
        days_since_enrollment = (now_timestamp - entitlement.enrollment_course_run.created).days
        days_since_entitlement_created = (now_timestamp - entitlement.created).days

        # We want to return whichever days value is less since it is then the more recent one
        days_until_regain_ends = (self.regain_period.days -  # pylint: disable=no-member
                                  min(days_since_course_start, days_since_enrollment, days_since_entitlement_created))

        # If the base days until expiration is less than the days until the regain period ends, use that instead
        if days_until_expiry < days_until_regain_ends:
            return days_until_expiry

        return days_until_regain_ends  # pylint: disable=no-member
コード例 #17
0
ファイル: views.py プロジェクト: cmscom/edx-platform
    def delete(self, request, ccx_course_id=None):  # pylint: disable=unused-argument
        """
        Deletes a CCX course.

        Args:
            request (Request): Django request object.
            ccx_course_id (string): URI element specifying the CCX course location.
        """
        ccx_course_object, ccx_course_key, error_code, http_status = self.get_object(ccx_course_id, is_ccx=True)
        if ccx_course_object is None:
            return Response(
                status=http_status,
                data={
                    'error_code': error_code
                }
            )
        ccx_course_overview = CourseOverview.get_from_id(ccx_course_key)
        # clean everything up with a single transaction
        with transaction.atomic():
            CcxFieldOverride.objects.filter(ccx=ccx_course_object).delete()
            # remove all users enrolled in the CCX from the CourseEnrollment model
            CourseEnrollment.objects.filter(course_id=ccx_course_key).delete()
            ccx_course_overview.delete()
            ccx_course_object.delete()
        return Response(
            status=status.HTTP_204_NO_CONTENT,
        )
コード例 #18
0
    def test_generate_force_update(self):
        self.command.handle(all=True)

        # update each course
        updated_course_name = u'test_generate_course_overview.course_edit'
        for course_key in (self.course_key_1, self.course_key_2):
            course = self.store.get_course(course_key)
            course.display_name = updated_course_name
            self.store.update_item(course, self.user.id)

        # force_update course_key_1, but not course_key_2
        self.command.handle(unicode(self.course_key_1), all=False, force_update=True)
        self.command.handle(unicode(self.course_key_2), all=False, force_update=False)

        self.assertEquals(CourseOverview.get_from_id(self.course_key_1).display_name, updated_course_name)
        self.assertNotEquals(CourseOverview.get_from_id(self.course_key_2).display_name, updated_course_name)
コード例 #19
0
    def test_enrollments_not_deleted(self):
        """ Recreating a CourseOverview with an outdated version should not delete the associated enrollment. """
        course = CourseFactory(self_paced=True)
        CourseModeFactory(
            course_id=course.id,
            mode_slug=CourseMode.VERIFIED,
            # This must be in the future to ensure it is returned by downstream code.
            expiration_datetime=datetime.datetime.now(pytz.UTC) + datetime.timedelta(days=30),
        )

        # Create a CourseOverview with an outdated version
        course_overview = CourseOverview.load_from_module_store(course.id)
        course_overview.version = CourseOverview.VERSION - 1
        course_overview.save()

        # Create an inactive enrollment with this course overview
        enrollment = CourseEnrollmentFactory(
            user=self.user,
            course_id=course.id,
            mode=CourseMode.AUDIT,
            course=course_overview,
        )

        # Re-fetch the CourseOverview record.
        # As a side effect, this will recreate the record, and update the version.
        course_overview_new = CourseOverview.get_from_id(course.id)
        self.assertEqual(course_overview_new.version, CourseOverview.VERSION)

        # Ensure that the enrollment record was unchanged during this re-creation
        enrollment_refetched = CourseEnrollment.objects.filter(id=enrollment.id)
        self.assertTrue(enrollment_refetched.exists())
        self.assertEqual(enrollment_refetched.all()[0], enrollment)
コード例 #20
0
ファイル: tasks.py プロジェクト: mitocw/edx-platform
def are_grades_frozen(course_key):
    """ Returns whether grades are frozen for the given course. """
    if waffle_flags()[ENFORCE_FREEZE_GRADE_AFTER_COURSE_END].is_enabled(course_key):
        course = CourseOverview.get_from_id(course_key)
        if course.end:
            freeze_grade_date = course.end + timedelta(30)
            now = timezone.now()
            return now > freeze_grade_date
コード例 #21
0
 def test_course_overview_unsupported_action(self):
     """
     Check that calling has_access with an unsupported action raises a
     ValueError.
     """
     overview = CourseOverview.get_from_id(self.course_default.id)
     with self.assertRaises(ValueError):
         access.has_access(self.user, "_non_existent_action", overview)
コード例 #22
0
ファイル: utils.py プロジェクト: Fadykhallaf/learnlink
def ccx_students_enrolling_center(action, identifiers, email_students, course_key, email_params, coach):
    """
    Function to enroll/add or unenroll/revoke students.

    This function exists for backwards compatibility: in CCX there are
    two different views to manage students that used to implement
    a different logic. Now the logic has been reconciled at the point that
    this function can be used by both.
    The two different views can be merged after some UI refactoring.

    Arguments:
        action (str): type of action to perform (add, Enroll, revoke, Unenroll)
        identifiers (list): list of students username/email
        email_students (bool): Flag to send an email to students
        course_key (CCXLocator): a CCX course key
        email_params (dict): dictionary of settings for the email to be sent
        coach (User): ccx coach

    Returns:
        list: list of error
    """
    errors = []

    if action == 'Enroll' or action == 'add':
        ccx_course_overview = CourseOverview.get_from_id(course_key)
        course_locator = course_key.to_course_locator()
        staff = CourseStaffRole(course_locator).users_with_role()
        admins = CourseInstructorRole(course_locator).users_with_role()

        for identifier in identifiers:
            must_enroll = False
            try:
                email, student = get_valid_student_with_email(identifier)
                if student:
                    must_enroll = student in staff or student in admins or student == coach
            except CCXUserValidationException as exp:
                log.info("%s", exp)
                errors.append("{0}".format(exp))
                continue

            if CourseEnrollment.objects.is_course_full(ccx_course_overview) and not must_enroll:
                error = _('The course is full: the limit is {max_student_enrollments_allowed}').format(
                    max_student_enrollments_allowed=ccx_course_overview.max_student_enrollments_allowed)
                log.info("%s", error)
                errors.append(error)
                break
            enroll_email(course_key, email, auto_enroll=True, email_students=email_students, email_params=email_params)
    elif action == 'Unenroll' or action == 'revoke':
        for identifier in identifiers:
            try:
                email, __ = get_valid_student_with_email(identifier)
            except CCXUserValidationException as exp:
                log.info("%s", exp)
                errors.append("{0}".format(exp))
                continue
            unenroll_email(course_key, email, email_students=email_students, email_params=email_params)
    return errors
コード例 #23
0
 def test_date_with_self_paced_with_enrollment_before_course_start(self):
     """ Enrolling before a course begins should result in the upgrade deadline being set relative to the
     course start date. """
     global_config = DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True)
     course = create_self_paced_course_run(days_till_start=3)
     overview = CourseOverview.get_from_id(course.id)
     expected = overview.start + timedelta(days=global_config.deadline_days)
     enrollment = CourseEnrollmentFactory(course_id=course.id, mode=CourseMode.AUDIT)
     block = VerifiedUpgradeDeadlineDate(course, enrollment.user)
     self.assertEqual(block.date, expected)
コード例 #24
0
ファイル: utils.py プロジェクト: shevious/edx-platform
    def _extend_course_runs(self):
        """Execute course run data handlers."""
        for course in self.data['courses']:
            for course_run in course['course_runs']:
                # State to be shared across handlers.
                self.course_run_key = CourseKey.from_string(course_run['key'])
                self.course_overview = CourseOverview.get_from_id(self.course_run_key)
                self.enrollment_start = self.course_overview.enrollment_start or DEFAULT_ENROLLMENT_START_DATE

                self._execute('_attach_course_run', course_run)
コード例 #25
0
ファイル: signals.py プロジェクト: digitalsatori/edx-platform
def _listen_for_certificate_whitelist_append(sender, instance, **kwargs):  # pylint: disable=unused-argument
    course = CourseOverview.get_from_id(instance.course_id)
    if not auto_certificate_generation_enabled():
        return

    fire_ungenerated_certificate_task(instance.user, instance.course_id)
    log.info(u'Certificate generation task initiated for {user} : {course} via whitelist'.format(
        user=instance.user.id,
        course=instance.course_id
    ))
コード例 #26
0
ファイル: utils.py プロジェクト: krsreenatha/edx-platform
    def _extend_run_modes(self):
        """Execute run mode data handlers."""
        for course_code in self.data['course_codes']:
            for run_mode in course_code['run_modes']:
                # State to be shared across handlers.
                self.course_key = CourseKey.from_string(run_mode['course_key'])
                self.course_overview = CourseOverview.get_from_id(self.course_key)
                self.enrollment_start = self.course_overview.enrollment_start or DEFAULT_ENROLLMENT_START_DATE

                self._execute('_attach_run_mode', run_mode)
コード例 #27
0
    def test_content_availability_date(self, mock_get_course_run_details):
        """
        Content availability date is course start date or enrollment date, whichever is later.
        """
        access_duration = timedelta(weeks=7)
        mock_get_course_run_details.return_value = {'weeks_to_complete': 7}

        # Content availability date is enrollment date
        start_date = now() - timedelta(weeks=10)
        past_course = CourseFactory(start=start_date)
        enrollment = CourseEnrollment.enroll(self.user, past_course.id, CourseMode.AUDIT)
        result = get_user_course_expiration_date(
            self.user,
            CourseOverview.get_from_id(past_course.id),
        )
        self.assertEqual(result, None)

        add_course_mode(past_course, upgrade_deadline_expired=False)
        result = get_user_course_expiration_date(
            self.user,
            CourseOverview.get_from_id(past_course.id),
        )
        content_availability_date = enrollment.created
        self.assertEqual(result, content_availability_date + access_duration)

        # Content availability date is course start date
        start_date = now() + timedelta(weeks=10)
        future_course = CourseFactory(start=start_date)
        enrollment = CourseEnrollment.enroll(self.user, future_course.id, CourseMode.AUDIT)
        result = get_user_course_expiration_date(
            self.user,
            CourseOverview.get_from_id(future_course.id),
        )
        self.assertEqual(result, None)

        add_course_mode(future_course, upgrade_deadline_expired=False)
        result = get_user_course_expiration_date(
            self.user,
            CourseOverview.get_from_id(future_course.id),
        )
        content_availability_date = start_date.replace(microsecond=0)
        self.assertEqual(result, content_availability_date + access_duration)
コード例 #28
0
ファイル: models.py プロジェクト: johnny-nan/edx-platform
    def name(self):
        """ Return course name. """
        course_id = CourseKey.from_string(unicode(self.id))

        try:
            return CourseOverview.get_from_id(course_id).display_name
        except CourseOverview.DoesNotExist:
            # NOTE (CCB): Ideally, the course modes table should only contain data for courses that exist in
            # modulestore. If that is not the case, say for local development/testing, carry on without failure.
            log.warning('Failed to retrieve CourseOverview for [%s]. Using empty course name.', course_id)
            return None
コード例 #29
0
ファイル: views.py プロジェクト: edx/edx-platform
 def get(self, request, course_key_string):
     """
     Return the discount percent, if the user has appropriate permissions.
     """
     course_key = CourseKey.from_string(course_key_string)
     course = CourseOverview.get_from_id(course_key)
     discount_applicable = can_receive_discount(user=request.user, course=course)
     discount_percent = discount_percentage()
     payload = {'discount_applicable': discount_applicable, 'discount_percent': discount_percent}
     return Response({
         'discount_applicable': discount_applicable,
         'jwt': create_jwt_for_user(request.user, additional_claims=payload)})
コード例 #30
0
    def setUp(self):
        """ Create a course and user, then log in. """
        super(EnrollmentEmbargoTest, self).setUp()

        self.course = CourseFactory.create()
        # Load a CourseOverview. This initial load should result in a cache
        # miss; the modulestore is queried and course metadata is cached.
        __ = CourseOverview.get_from_id(self.course.id)

        self.user = UserFactory.create(username=self.USERNAME, email=self.EMAIL, password=self.PASSWORD)
        self.client.login(username=self.USERNAME, password=self.PASSWORD)
        self.url = reverse('courseenrollments')
コード例 #31
0
def has_html_certificates_enabled(course_key, course=None):
    """
    Determine if a course has html certificates enabled.

    Arguments:
        course_key (CourseKey|str): A course key or a string representation
            of one.
        course (CourseDescriptor|CourseOverview): A course.
    """
    # If the feature is disabled, then immediately return a False
    if not settings.FEATURES.get('CERTIFICATES_HTML_VIEW', False):
        return False

    # If we don't have a course object, we'll need to assemble one
    if not course:
        # Initialize a course key if necessary
        if not isinstance(course_key, CourseKey):
            try:
                course_key = CourseKey.from_string(course_key)
            except InvalidKeyError:
                log.warning(
                    ('Unable to parse course_key "%s"', course_key),
                    exc_info=True
                )
                return False
        # Pull the course data from the cache
        try:
            course = CourseOverview.get_from_id(course_key)
        except:  # pylint: disable=bare-except
            log.warning(
                ('Unable to load CourseOverview object for course_key "%s"', unicode(course_key)),
                exc_info=True
            )

    # Return the flag on the course object
    return course.cert_html_view_enabled if course else False
コード例 #32
0
ファイル: access.py プロジェクト: DoTamKma/debug-edx-platform
def course_expiration_wrapper(user, block, view, frag, context):  # pylint: disable=W0613
    """
    An XBlock wrapper that prepends a message to the beginning of a vertical if
    a user's course is about to expire.
    """
    if block.category != "vertical":
        return frag

    course = CourseOverview.get_from_id(block.course_id)
    course_expiration_fragment = generate_course_expired_fragment(user, course)

    if not course_expiration_fragment:
        return frag

    # Course content must be escaped to render correctly due to the way the
    # way the XBlock rendering works. Transforming the safe markup to unicode
    # escapes correctly.
    course_expiration_fragment.content = unicode(
        course_expiration_fragment.content)

    course_expiration_fragment.add_content(frag.content)
    course_expiration_fragment.add_fragment_resources(frag)

    return course_expiration_fragment
コード例 #33
0
    def test_masquerade(self, masquerade_config, show_expiration_banner,
                        mock_get_course_run_details):
        mock_get_course_run_details.return_value = {'weeks_to_complete': 12}
        audit_student = UserFactory(username='******')
        CourseEnrollmentFactory.create(user=audit_student,
                                       course_id=self.course.id,
                                       mode='audit')
        verified_student = UserFactory(username='******')
        CourseEnrollmentFactory.create(user=verified_student,
                                       course_id=self.course.id,
                                       mode='verified')
        CourseDurationLimitConfig.objects.create(
            enabled=True,
            course=CourseOverview.get_from_id(self.course.id),
            enabled_as_of=self.course.start,
        )

        instructor = UserFactory.create(username='******')
        CourseEnrollmentFactory.create(user=instructor,
                                       course_id=self.course.id,
                                       mode='audit')
        CourseInstructorRole(self.course.id).add_users(instructor)
        self.client.login(username=instructor.username, password='******')

        self.update_masquerade(**masquerade_config)

        course_home_url = reverse('openedx.course_experience.course_home',
                                  args=[unicode(self.course.id)])
        response = self.client.get(course_home_url, follow=True)
        self.assertEqual(response.status_code, 200)
        self.assertItemsEqual(response.redirect_chain, [])
        banner_text = 'You lose all access to this course, including your progress,'
        if show_expiration_banner:
            self.assertIn(banner_text, response.content)
        else:
            self.assertNotIn(banner_text, response.content)
コード例 #34
0
    def test_student_has_access(self):
        """
        Tests course student have right access to content w/o preview.
        """
        course_key = self.course.id
        chapter = ItemFactory.create(category="chapter",
                                     parent_location=self.course.location)
        overview = CourseOverview.get_from_id(course_key)

        # Enroll student to the course
        CourseEnrollmentFactory(user=self.student, course_id=self.course.id)

        modules = [
            self.course,
            overview,
            chapter,
        ]
        with patch('courseware.access.in_preview_mode') as mock_preview:
            mock_preview.return_value = False
            for obj in modules:
                self.assertTrue(
                    bool(
                        access.has_access(self.student,
                                          'load',
                                          obj,
                                          course_key=self.course.id)))

        with patch('courseware.access.in_preview_mode') as mock_preview:
            mock_preview.return_value = True
            for obj in modules:
                self.assertFalse(
                    bool(
                        access.has_access(self.student,
                                          'load',
                                          obj,
                                          course_key=self.course.id)))
コード例 #35
0
    def test_expired_course(self):
        """
        Ensure that a user accessing an expired course sees a redirect to
        the student dashboard, not a 404.
        """
        CourseDurationLimitConfig.objects.create(enabled=True,
                                                 enabled_as_of=datetime(
                                                     2010, 1, 1))
        course = CourseFactory.create(start=THREE_YEARS_AGO)
        url = course_home_url(course)

        for mode in [CourseMode.AUDIT, CourseMode.VERIFIED]:
            CourseModeFactory.create(course_id=course.id, mode_slug=mode)

        # assert that an if an expired audit user tries to access the course they are redirected to the dashboard
        audit_user = UserFactory(password=self.TEST_PASSWORD)
        self.client.login(username=audit_user.username,
                          password=self.TEST_PASSWORD)
        audit_enrollment = CourseEnrollment.enroll(audit_user,
                                                   course.id,
                                                   mode=CourseMode.AUDIT)
        ScheduleFactory(start=THREE_YEARS_AGO, enrollment=audit_enrollment)

        response = self.client.get(url)

        expiration_date = strftime_localized(course.start + timedelta(weeks=4),
                                             u'%b. %-d, %Y')
        expected_params = QueryDict(mutable=True)
        course_name = CourseOverview.get_from_id(
            course.id).display_name_with_default
        expected_params[
            'access_response_error'] = u'Access to {run} expired on {expiration_date}'.format(
                run=course_name, expiration_date=expiration_date)
        expected_url = '{url}?{params}'.format(
            url=reverse('dashboard'), params=expected_params.urlencode())
        self.assertRedirects(response, expected_url)
コード例 #36
0
    def _assert_supplemented(self,
                             actual,
                             is_enrolled=False,
                             is_enrollment_open=True):
        """DRY helper used to verify that program data is extended correctly."""
        course_overview = CourseOverview.get_from_id(self.course.id)  # pylint: disable=no-member

        run_mode = factories.RunMode(
            course_key=unicode(self.course.id),  # pylint: disable=no-member
            course_url=reverse('course_root', args=[self.course.id]),  # pylint: disable=no-member
            course_image_url=course_overview.course_image_url,
            start_date=self.course.start.strftime(self.human_friendly_format),
            end_date=self.course.end.strftime(self.human_friendly_format),
            is_enrolled=is_enrolled,
            is_enrollment_open=is_enrollment_open,
            marketing_url='',
        )
        course_code = factories.CourseCode(
            display_name=self.course_code['display_name'],
            run_modes=[run_mode])
        expected = copy.deepcopy(self.program)
        expected['course_codes'] = [course_code]

        self.assertEqual(actual, expected)
コード例 #37
0
    def get(self, request, course_key_string, user_id):
        """
        Return the discount percent, if the user has appropriate permissions.
        """
        course_key = CourseKey.from_string(course_key_string)
        course = CourseOverview.get_from_id(course_key)
        user = User.objects.get(id=user_id)
        # Below code in try/except is temporarily replacing this call
        # discount_applicable = can_receive_discount(user=user, course=course)
        # Only show a discount on the order if the last basket loaded for this course had a discount
        # Do not check any of the discount requirements
        try:
            discount_applicable = ExperimentData.objects.get(
                user=user, experiment_id=REV1008_EXPERIMENT_ID, key='discount_' + str(course)
            ).value == 'True'
        except ExperimentData.DoesNotExist:
            discount_applicable = False

        discount_percent = discount_percentage(course)
        payload = {'discount_applicable': discount_applicable, 'discount_percent': discount_percent}

        return Response({
            'discount_applicable': discount_applicable,
            'jwt': create_jwt_for_user(request.user, additional_claims=payload)})
コード例 #38
0
    def test_all_courses_with_weeks_to_complete(
        self,
        weeks_to_complete,
        access_duration,
        self_paced,
        mock_get_course_run_details,
    ):
        """
        Test that access_duration for a course is equal to the value of the weeks_to_complete field in discovery.
        If weeks_to_complete is None, access_duration will be the MIN_DURATION constant.

        """
        if self_paced:
            self.course.self_paced = True
        mock_get_course_run_details.return_value = {
            'weeks_to_complete': weeks_to_complete
        }
        enrollment = CourseEnrollment.enroll(self.user, self.course.id,
                                             CourseMode.AUDIT)
        result = get_user_course_expiration_date(
            self.user,
            CourseOverview.get_from_id(self.course.id),
        )
        self.assertEqual(result, enrollment.created + access_duration)
コード例 #39
0
    def get_days_until_expiration(self, entitlement):
        """
        Returns an integer of number of days until the entitlement expires.
        Includes the logic for regaining an entitlement.
        """
        now = datetime.now(tz=pytz.UTC)
        expiry_date = entitlement.created + self.expiration_period
        days_until_expiry = (expiry_date - now).days
        if not entitlement.enrollment_course_run:
            return days_until_expiry
        course_overview = CourseOverview.get_from_id(entitlement.enrollment_course_run.course_id)
        # Compute the days left for the regain
        days_since_course_start = (now - course_overview.start).days
        days_since_enrollment = (now - entitlement.enrollment_course_run.created).days

        # We want to return whichever days value is less since it is then the more recent one
        days_until_regain_ends = (self.regain_period.days -  # pylint: disable=no-member
                                  min(days_since_course_start, days_since_enrollment))

        # If the base days until expiration is less than the days until the regain period ends, use that instead
        if days_until_expiry < days_until_regain_ends:
            return days_until_expiry

        return days_until_regain_ends  # pylint: disable=no-member
コード例 #40
0
 def course_overview(self):
     return CourseOverview.get_from_id(self.course_id)
コード例 #41
0
 def test_enrollment_mode(self):
     """Tests that verified enrollments do not have an expiration"""
     CourseEnrollment.enroll(self.user, self.course.id, CourseMode.VERIFIED)
     result = get_user_course_expiration_date(self.user, CourseOverview.get_from_id(self.course.id))
     self.assertEqual(result, None)
コード例 #42
0
    def get(self, request, *args, **kwargs):
        course_key_string = kwargs.get('course_key_string')
        course_key = CourseKey.from_string(course_key_string)
        course_usage_key = modulestore().make_course_usage_key(course_key)

        if not course_home_mfe_outline_tab_is_active(course_key):
            raise Http404

        # Enable NR tracing for this view based on course
        monitoring_utils.set_custom_attribute('course_id', course_key_string)
        monitoring_utils.set_custom_attribute('user_id', request.user.id)
        monitoring_utils.set_custom_attribute('is_staff',
                                              request.user.is_staff)

        course = get_course_with_access(request.user,
                                        'load',
                                        course_key,
                                        check_if_enrolled=False)

        _masquerade, request.user = setup_masquerade(
            request,
            course_key,
            staff_access=has_access(request.user, 'staff', course_key),
            reset_masquerade_data=True,
        )

        course_overview = CourseOverview.get_from_id(course_key)
        enrollment = CourseEnrollment.get_enrollment(request.user, course_key)
        allow_anonymous = COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.is_enabled(
            course_key)
        allow_public = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC
        allow_public_outline = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC_OUTLINE
        is_enrolled = enrollment and enrollment.is_active
        is_staff = bool(has_access(request.user, 'staff', course_key))
        show_enrolled = is_enrolled or is_staff

        show_handouts = show_enrolled or allow_public
        handouts_html = get_course_info_section(
            request, request.user, course, 'handouts') if show_handouts else ''

        # TODO: TNL-7185 Legacy: Refactor to return the offer & expired data and format the message in the MFE
        offer_html = show_enrolled and generate_offer_html(
            request.user, course_overview)
        course_expired_html = show_enrolled and generate_course_expired_message(
            request.user, course_overview)

        welcome_message_html = None
        if show_enrolled:
            if LATEST_UPDATE_FLAG.is_enabled(course_key):
                welcome_message_html = LatestUpdateFragmentView(
                ).latest_update_html(request, course)
            elif get_course_tag(request.user, course_key,
                                PREFERENCE_KEY) != 'False':
                welcome_message_html = WelcomeMessageFragmentView(
                ).welcome_message_html(request, course)

        enroll_alert = {
            'can_enroll': True,
            'extra_text': None,
        }
        if not show_enrolled:
            if CourseMode.is_masters_only(course_key):
                enroll_alert['can_enroll'] = False
                enroll_alert['extra_text'] = _(
                    'Please contact your degree administrator or '
                    'edX Support if you have questions.')
            elif course.invitation_only:
                enroll_alert['can_enroll'] = False

        course_tools = CourseToolsPluginManager.get_enabled_course_tools(
            request, course_key)
        date_blocks = get_course_date_blocks(course,
                                             request.user,
                                             request,
                                             num_assignments=1)

        # User locale settings
        user_timezone_locale = user_timezone_locale_prefs(request)
        user_timezone = user_timezone_locale['user_timezone']

        dates_tab_link = request.build_absolute_uri(
            reverse('dates', args=[course.id]))
        if course_home_mfe_dates_tab_is_active(course.id):
            dates_tab_link = get_microfrontend_url(course_key=course.id,
                                                   view_name='dates')

        course_blocks = None
        if show_enrolled or allow_public or allow_public_outline:
            outline_user = request.user if show_enrolled else None
            course_blocks = get_course_outline_block_tree(
                request, course_key_string, outline_user)

        resume_course = {
            'has_visited_course': False,
            'url': None,
        }
        if show_enrolled:
            try:
                resume_block = get_key_to_last_completed_block(
                    request.user, course.id)
                resume_course['has_visited_course'] = True
            except UnavailableCompletionData:
                resume_block = course_usage_key
            resume_path = reverse('jump_to',
                                  kwargs={
                                      'course_id': course_key_string,
                                      'location': str(resume_block)
                                  })
            resume_course['url'] = request.build_absolute_uri(resume_path)

        dates_widget = {
            'course_date_blocks': [
                block for block in date_blocks
                if not isinstance(block, TodaysDate)
            ],
            'dates_tab_link':
            dates_tab_link,
            'user_timezone':
            user_timezone,
        }

        # Only show the set course goal message for enrolled, unverified
        # users in a course that allows for verified statuses.
        is_already_verified = CourseEnrollment.is_enrolled_as_verified(
            request.user, course_key)
        if (not is_already_verified and has_course_goal_permission(
                request, course_key_string, {'is_enrolled': is_enrolled})):
            course_goals = {
                'goal_options':
                valid_course_goals_ordered(include_unsure=True),
                'selected_goal': None
            }

            selected_goal = get_course_goal(request.user, course_key)
            if selected_goal:
                course_goals['selected_goal'] = {
                    'key': selected_goal.goal_key,
                    'text': get_course_goal_text(selected_goal.goal_key),
                }
        else:
            course_goals = {'goal_options': [], 'selected_goal': None}

        data = {
            'course_blocks': course_blocks,
            'course_expired_html': course_expired_html or None,
            'course_goals': course_goals,
            'course_tools': course_tools,
            'dates_widget': dates_widget,
            'enroll_alert': enroll_alert,
            'handouts_html': handouts_html or None,
            'has_ended': course.has_ended(),
            'offer_html': offer_html or None,
            'resume_course': resume_course,
            'welcome_message_html': welcome_message_html or None,
        }
        context = self.get_serializer_context()
        context['course_key'] = course_key
        context['enable_links'] = show_enrolled or allow_public
        serializer = self.get_serializer_class()(data, context=context)

        return Response(serializer.data)
コード例 #43
0
    def get(self, request, *args, **kwargs):
        course_key_string = kwargs.get('course_key_string')
        course_key = CourseKey.from_string(course_key_string)
        course_usage_key = modulestore().make_course_usage_key(course_key)

        if not course_home_mfe_outline_tab_is_active(course_key):
            raise Http404

        # Enable NR tracing for this view based on course
        monitoring_utils.set_custom_metric('course_id', course_key_string)
        monitoring_utils.set_custom_metric('user_id', request.user.id)
        monitoring_utils.set_custom_metric('is_staff', request.user.is_staff)

        course = get_course_with_access(request.user,
                                        'load',
                                        course_key,
                                        check_if_enrolled=False)

        _masquerade, request.user = setup_masquerade(
            request,
            course_key,
            staff_access=has_access(request.user, 'staff', course_key),
            reset_masquerade_data=True,
        )

        course_overview = CourseOverview.get_from_id(course_key)
        enrollment = CourseEnrollment.get_enrollment(request.user, course_key)
        allow_anonymous = COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.is_enabled(
            course_key)
        allow_public = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC
        is_enrolled = enrollment and enrollment.is_active
        is_staff = has_access(request.user, 'staff', course_key)
        show_enrolled = is_enrolled or is_staff

        show_handouts = show_enrolled or allow_public
        handouts_html = get_course_info_section(
            request, request.user, course, 'handouts') if show_handouts else ''

        # TODO: TNL-7185 Legacy: Refactor to return the offer & expired data and format the message in the MFE
        offer_html = generate_offer_html(request.user, course_overview)
        course_expired_html = generate_course_expired_message(
            request.user, course_overview)

        welcome_message_html = None
        if get_course_tag(request.user, course_key, PREFERENCE_KEY) != 'False':
            if LATEST_UPDATE_FLAG.is_enabled(course_key):
                welcome_message_html = LatestUpdateFragmentView(
                ).latest_update_html(request, course)
            else:
                welcome_message_html = WelcomeMessageFragmentView(
                ).welcome_message_html(request, course)

        enroll_alert = {
            'can_enroll': True,
            'extra_text': None,
        }
        if not show_enrolled:
            if CourseMode.is_masters_only(course_key):
                enroll_alert['can_enroll'] = False
                enroll_alert['extra_text'] = _(
                    'Please contact your degree administrator or '
                    'edX Support if you have questions.')
            elif course.invitation_only:
                enroll_alert['can_enroll'] = False

        course_tools = CourseToolsPluginManager.get_enabled_course_tools(
            request, course_key)
        date_blocks = get_course_date_blocks(course,
                                             request.user,
                                             request,
                                             num_assignments=1)

        # User locale settings
        user_timezone_locale = user_timezone_locale_prefs(request)
        user_timezone = user_timezone_locale['user_timezone']

        dates_tab_link = request.build_absolute_uri(
            reverse('dates', args=[course.id]))
        if course_home_mfe_dates_tab_is_active(course.id):
            dates_tab_link = get_microfrontend_url(course_key=course.id,
                                                   view_name='dates')

        transformers = BlockStructureTransformers()
        transformers += get_course_block_access_transformers(request.user)
        transformers += [
            BlocksAPITransformer(None, None, depth=3),
        ]

        course_blocks = get_course_blocks(request.user,
                                          course_usage_key,
                                          transformers,
                                          include_completion=True)

        dates_widget = {
            'course_date_blocks': [
                block for block in date_blocks
                if not isinstance(block, TodaysDate)
            ],
            'dates_tab_link':
            dates_tab_link,
            'user_timezone':
            user_timezone,
        }

        data = {
            'course_blocks': course_blocks,
            'course_expired_html': course_expired_html,
            'course_tools': course_tools,
            'dates_widget': dates_widget,
            'enroll_alert': enroll_alert,
            'handouts_html': handouts_html,
            'offer_html': offer_html,
            'welcome_message_html': welcome_message_html,
        }
        context = self.get_serializer_context()
        context['course_key'] = course_key
        serializer = self.get_serializer_class()(data, context=context)

        return Response(serializer.data)
コード例 #44
0
def search_certificates(request):
    """
    Search for certificates for a particular user OR along with the given course.

    Supports search by either username or email address along with course id.

    First filter the records for the given username/email and then filter against the given course id (if given).
    Show the 'Regenerate' button if a record found in 'generatedcertificate' model otherwise it will show the Generate
    button.

    Arguments:
        request (HttpRequest): The request object.

    Returns:
        JsonResponse

    Example Usage:
        GET /certificates/[email protected]
        GET /certificates/[email protected]&course_id=xyz

        Response: 200 OK
        Content-Type: application/json
        [
            {
                "username": "******",
                "course_key": "edX/DemoX/Demo_Course",
                "type": "verified",
                "status": "downloadable",
                "download_url": "http://www.example.com/cert.pdf",
                "grade": "0.98",
                "created": 2015-07-31T00:00:00Z,
                "modified": 2015-07-31T00:00:00Z
            }
        ]

    """
    unbleached_filter = urllib.parse.unquote(
        urllib.parse.quote_plus(request.GET.get("user", "")))
    user_filter = bleach.clean(unbleached_filter)
    if not user_filter:
        msg = _("user is not given.")
        return HttpResponseBadRequest(msg)

    try:
        user = User.objects.get(Q(email=user_filter) | Q(username=user_filter))
    except User.DoesNotExist:
        return HttpResponseBadRequest(
            _("user '{user}' does not exist").format(user=user_filter))

    certificates = get_certificates_for_user(user.username)
    for cert in certificates:
        cert["course_key"] = str(cert["course_key"])
        cert["created"] = cert["created"].isoformat()
        cert["modified"] = cert["modified"].isoformat()
        cert["regenerate"] = not cert['is_pdf_certificate']

    course_id = urllib.parse.quote_plus(request.GET.get("course_id", ""),
                                        safe=':/')
    if course_id:
        try:
            course_key = CourseKey.from_string(course_id)
        except InvalidKeyError:
            return HttpResponseBadRequest(
                _("Course id '{course_id}' is not valid").format(
                    course_id=course_id))
        else:
            try:
                if CourseOverview.get_from_id(course_key):
                    certificates = [
                        certificate for certificate in certificates
                        if certificate['course_key'] == course_id
                    ]
                    if not certificates:
                        return JsonResponse([{
                            'username': user.username,
                            'course_key': course_id,
                            'regenerate': False
                        }])
            except CourseOverview.DoesNotExist:
                msg = _(
                    "The course does not exist against the given key '{course_key}'"
                ).format(course_key=course_key)
                return HttpResponseBadRequest(msg)

    return JsonResponse(certificates)
コード例 #45
0
    def render_to_fragment(self, request, course_id=None, **kwargs):  # lint-amnesty, pylint: disable=arguments-differ, too-many-statements
        """
        Renders the course's home page as a fragment.
        """
        course_key = CourseKey.from_string(course_id)
        course = get_course_with_access(request.user, 'load', course_key)

        # Render the course dates as a fragment
        dates_fragment = CourseDatesFragmentView().render_to_fragment(
            request, course_id=course_id, **kwargs)

        # Render the full content to enrolled users, as well as to course and global staff.
        # Unenrolled users who are not course or global staff are given only a subset.
        enrollment = CourseEnrollment.get_enrollment(request.user, course_key)
        user_access = {
            'is_anonymous': request.user.is_anonymous,
            'is_enrolled': enrollment and enrollment.is_active,
            'is_staff': has_access(request.user, 'staff', course_key),
        }

        allow_anonymous = COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.is_enabled(
            course_key)
        allow_public = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC
        allow_public_outline = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC_OUTLINE

        # Set all the fragments
        outline_fragment = None
        update_message_fragment = None
        course_sock_fragment = None
        offer_banner_fragment = None
        course_expiration_fragment = None
        has_visited_course = None
        resume_course_url = None
        handouts_html = None

        course_overview = CourseOverview.get_from_id(course.id)
        if user_access['is_enrolled'] or user_access['is_staff']:
            outline_fragment = CourseOutlineFragmentView().render_to_fragment(
                request, course_id=course_id, **kwargs)
            if LATEST_UPDATE_FLAG.is_enabled(course_key):
                update_message_fragment = LatestUpdateFragmentView(
                ).render_to_fragment(request, course_id=course_id, **kwargs)
            else:
                update_message_fragment = WelcomeMessageFragmentView(
                ).render_to_fragment(request, course_id=course_id, **kwargs)
            course_sock_fragment = CourseSockFragmentView().render_to_fragment(
                request, course=course, **kwargs)
            has_visited_course, resume_course_url = self._get_resume_course_info(
                request, course_id)
            handouts_html = self._get_course_handouts(request, course)
            offer_banner_fragment = get_first_purchase_offer_banner_fragment(
                request.user, course_overview)
            course_expiration_fragment = generate_course_expired_fragment(
                request.user, course_overview)
        elif allow_public_outline or allow_public:
            outline_fragment = CourseOutlineFragmentView().render_to_fragment(
                request, course_id=course_id, user_is_enrolled=False, **kwargs)
            course_sock_fragment = CourseSockFragmentView().render_to_fragment(
                request, course=course, **kwargs)
            if allow_public:
                handouts_html = self._get_course_handouts(request, course)
        else:
            # Redirect the user to the dashboard if they are not enrolled and
            # this is a course that does not support direct enrollment.
            if not can_self_enroll_in_course(course_key):
                raise CourseAccessRedirect(reverse('dashboard'))

        # Get the course tools enabled for this user and course
        course_tools = CourseToolsPluginManager.get_enabled_course_tools(
            request, course_key)

        # Check if the user can access the course goal functionality
        has_goal_permission = has_course_goal_permission(
            request, course_id, user_access)

        # Grab the current course goal and the acceptable course goal keys mapped to translated values
        current_goal = get_course_goal(request.user, course_key)
        goal_options = get_course_goal_options()

        # Get the course goals api endpoint
        goal_api_url = get_goal_api_url(request)

        # Grab the course home messages fragment to render any relevant django messages
        course_home_message_fragment = CourseHomeMessageFragmentView(
        ).render_to_fragment(request,
                             course_id=course_id,
                             user_access=user_access,
                             **kwargs)

        # Get info for upgrade messaging
        upgrade_price = None
        upgrade_url = None
        has_discount = False

        # TODO Add switch to control deployment
        if SHOW_UPGRADE_MSG_ON_COURSE_HOME.is_enabled(
                course_key) and can_show_verified_upgrade(
                    request.user, enrollment, course):
            upgrade_url = verified_upgrade_deadline_link(request.user,
                                                         course_id=course_key)
            upgrade_price, has_discount = format_strikeout_price(
                request.user, course_overview)

        show_search = (
            settings.FEATURES.get('ENABLE_COURSEWARE_SEARCH') or
            (settings.FEATURES.get('ENABLE_COURSEWARE_SEARCH_FOR_COURSE_STAFF')
             and user_access['is_staff']))
        # Render the course home fragment
        context = {
            'request': request,
            'csrf': csrf(request)['csrf_token'],
            'course': course,
            'course_key': course_key,
            'outline_fragment': outline_fragment,
            'handouts_html': handouts_html,
            'course_home_message_fragment': course_home_message_fragment,
            'offer_banner_fragment': offer_banner_fragment,
            'course_expiration_fragment': course_expiration_fragment,
            'has_visited_course': has_visited_course,
            'resume_course_url': resume_course_url,
            'course_tools': course_tools,
            'dates_fragment': dates_fragment,
            'username': request.user.username,
            'goal_api_url': goal_api_url,
            'has_goal_permission': has_goal_permission,
            'goal_options': goal_options,
            'current_goal': current_goal,
            'update_message_fragment': update_message_fragment,
            'course_sock_fragment': course_sock_fragment,
            'disable_courseware_js': True,
            'uses_bootstrap': True,
            'upgrade_price': upgrade_price,
            'upgrade_url': upgrade_url,
            'has_discount': has_discount,
            'show_search': show_search,
        }
        html = render_to_string('course_experience/course-home-fragment.html',
                                context)
        return Fragment(html)
コード例 #46
0
ファイル: test_serializers.py プロジェクト: eliesmr4/myedx
 def _get_result(self, course):
     """
     Return the CourseSerializer for the specified course.
     """
     course_overview = CourseOverview.get_from_id(course.id)
     return self.serializer_class(course_overview, context={'request': self._get_request()}).data
コード例 #47
0
def _course_from_key(course_key):
    return CourseOverview.get_from_id(_safe_course_key(course_key))
コード例 #48
0
def ccx_students_enrolling_center(action, identifiers, email_students,
                                  course_key, email_params, coach):
    """
    Function to enroll or unenroll/revoke students.

    Arguments:
        action (str): type of action to perform (Enroll, Unenroll/revoke)
        identifiers (list): list of students username/email
        email_students (bool): Flag to send an email to students
        course_key (CCXLocator): a CCX course key
        email_params (dict): dictionary of settings for the email to be sent
        coach (User): ccx coach

    Returns:
        list: list of error
    """
    errors = []

    if action == 'Enroll':
        ccx_course_overview = CourseOverview.get_from_id(course_key)
        course_locator = course_key.to_course_locator()
        staff = CourseStaffRole(course_locator).users_with_role()
        admins = CourseInstructorRole(course_locator).users_with_role()

        for identifier in identifiers:
            must_enroll = False
            try:
                email, student = get_valid_student_with_email(identifier)
                if student:
                    must_enroll = student in staff or student in admins or student == coach
            except CCXUserValidationException as exp:
                log.info("%s", exp)
                errors.append(f"{exp}")
                continue

            if CourseEnrollment.objects.is_course_full(
                    ccx_course_overview) and not must_enroll:
                error = _(
                    'The course is full: the limit is {max_student_enrollments_allowed}'
                ).format(max_student_enrollments_allowed=ccx_course_overview.
                         max_student_enrollments_allowed)
                log.info("%s", error)
                errors.append(error)
                break
            enroll_email(course_key,
                         email,
                         auto_enroll=True,
                         email_students=email_students,
                         email_params=email_params)
    elif action == 'Unenroll' or action == 'revoke':  # lint-amnesty, pylint: disable=consider-using-in
        for identifier in identifiers:
            try:
                email, __ = get_valid_student_with_email(identifier)
            except CCXUserValidationException as exp:
                log.info("%s", exp)
                errors.append(f"{exp}")
                continue
            unenroll_email(course_key,
                           email,
                           email_students=email_students,
                           email_params=email_params)
    return errors
コード例 #49
0
    def get(self, request):
        """
        Return the if the course should be upsold in the mobile app, if the user has appropriate permissions.
        """
        if not MOBILE_UPSELL_FLAG.is_enabled():
            return Response({
                'show_upsell': False,
                'upsell_flag': False,
            })

        course_id = request.GET.get('course_id')
        try:
            course_key = CourseKey.from_string(course_id)
        except InvalidKeyError:
            return HttpResponseBadRequest("Missing or invalid course_id")

        course = CourseOverview.get_from_id(course_key)
        if not course.has_started() or course.has_ended():
            return Response({
                'show_upsell': False,
                'upsell_flag': MOBILE_UPSELL_FLAG.is_enabled(),
                'course_running': False,
            })

        user = request.user
        try:
            enrollment = CourseEnrollment.objects.select_related('course').get(
                user_id=user.id, course_id=course.id)
            user_upsell = can_show_verified_upgrade(user, enrollment)
        except CourseEnrollment.DoesNotExist:
            user_upsell = True

        basket_url = EcommerceService().upgrade_url(user, course.id)
        upgrade_price = six.text_type(
            get_cosmetic_verified_display_price(course))
        could_upsell = bool(user_upsell and basket_url)

        bucket = stable_bucketing_hash_group(MOBILE_UPSELL_EXPERIMENT, 2, user)

        if could_upsell and hasattr(
                request,
                'session') and MOBILE_UPSELL_EXPERIMENT not in request.session:
            properties = {
                'site': request.site.domain,
                'app_label': 'experiments',
                'bucket': bucket,
                'experiment': 'REV-934',
            }
            segment.track(
                user_id=user.id,
                event_name='edx.bi.experiment.user.bucketed',
                properties=properties,
            )

            # Mark that we've recorded this bucketing, so that we don't do it again this session
            request.session[MOBILE_UPSELL_EXPERIMENT] = True

        show_upsell = bool(bucket != 0 and could_upsell)
        if show_upsell:
            return Response({
                'show_upsell': show_upsell,
                'price': upgrade_price,
                'basket_url': basket_url,
            })
        else:
            return Response({
                'show_upsell': show_upsell,
                'upsell_flag': MOBILE_UPSELL_FLAG.is_enabled(),
                'experiment_bucket': bucket,
                'user_upsell': user_upsell,
                'basket_url': basket_url,
            })
コード例 #50
0
    def test_course_messaging(self):
        """
        Ensure that the following four use cases work as expected

        1) Anonymous users are shown a course message linking them to the login page
        2) Unenrolled users are shown a course message allowing them to enroll
        3) Enrolled users who show up on the course page after the course has begun
        are not shown a course message.
        4) Enrolled users who show up on the course page after the course has begun will
        see the course expiration banner if course duration limits are on for the course.
        5) Enrolled users who show up on the course page before the course begins
        are shown a message explaining when the course starts as well as a call to
        action button that allows them to add a calendar event.
        """
        # Verify that anonymous users are shown a login link in the course message
        url = course_home_url(self.course)
        response = self.client.get(url)
        self.assertContains(response, TEST_COURSE_HOME_MESSAGE)
        self.assertContains(response, TEST_COURSE_HOME_MESSAGE_ANONYMOUS)

        # Verify that unenrolled users are shown an enroll call to action message
        user = self.create_user_for_course(self.course,
                                           CourseUserType.UNENROLLED)
        url = course_home_url(self.course)
        response = self.client.get(url)
        self.assertContains(response, TEST_COURSE_HOME_MESSAGE)
        self.assertContains(response, TEST_COURSE_HOME_MESSAGE_UNENROLLED)

        # Verify that enrolled users are not shown any state warning message when enrolled and course has begun.
        CourseEnrollment.enroll(user, self.course.id)
        url = course_home_url(self.course)
        response = self.client.get(url)
        self.assertNotContains(response, TEST_COURSE_HOME_MESSAGE_ANONYMOUS)
        self.assertNotContains(response, TEST_COURSE_HOME_MESSAGE_UNENROLLED)
        self.assertNotContains(response, TEST_COURSE_HOME_MESSAGE_PRE_START)

        # Verify that enrolled users are shown the course expiration banner if content gating is enabled

        # We use .save() explicitly here (rather than .objects.create) in order to force the
        # cache to refresh.
        config = CourseDurationLimitConfig(course=CourseOverview.get_from_id(
            self.course.id),
                                           enabled=True,
                                           enabled_as_of=datetime(2018, 1, 1))
        config.save()

        url = course_home_url(self.course)
        response = self.client.get(url)
        bannerText = get_expiration_banner_text(user, self.course)
        self.assertContains(response, bannerText, html=True)

        # Verify that enrolled users are not shown the course expiration banner if content gating is disabled
        config.enabled = False
        config.save()
        url = course_home_url(self.course)
        response = self.client.get(url)
        bannerText = get_expiration_banner_text(user, self.course)
        self.assertNotContains(response, bannerText, html=True)

        # Verify that enrolled users are shown 'days until start' message before start date
        future_course = self.create_future_course()
        CourseEnrollment.enroll(user, future_course.id)
        url = course_home_url(future_course)
        response = self.client.get(url)
        self.assertContains(response, TEST_COURSE_HOME_MESSAGE)
        self.assertContains(response, TEST_COURSE_HOME_MESSAGE_PRE_START)
コード例 #51
0
def enable_self_generated_certs(sender, course_key, **kwargs):  # pylint: disable=unused-argument
    """
    Enable/disable the self-generated certificates according to course-pacing.
    """
    course = CourseOverview.get_from_id(course_key)
    toggle_self_generated_certs.delay(unicode(course_key), course.self_paced)
コード例 #52
0
ファイル: views.py プロジェクト: angelapper/edx-platform
    def post(self, request):
        """
        **Use Case**

            * Send favorite course through email to user for later learning.

        **Example Request for course**

            POST /api/v1/save/course/

        **Example POST Request for course**

            {
                "email": "*****@*****.**",
                "course_id": "course-v1:edX+DemoX+2021",
                "marketing_url": "https://test.com",
                "org_img_url": "https://test.com/logo.png",
                "weeks_to_complete": 7,
                "min_effort": 4,
                "max_effort": 5,

            }
        """
        user = request.user
        data = request.data
        course_id = data.get('course_id')
        email = data.get('email')
        org_img_url = data.get('org_img_url')
        marketing_url = data.get('marketing_url')
        weeks_to_complete = data.get('weeks_to_complete', 0)
        min_effort = data.get('min_effort', 0)
        max_effort = data.get('max_effort', 0)
        user_id = request.user.id
        pref_lang = request.COOKIES.get(settings.LANGUAGE_COOKIE_NAME, 'en')
        send_to_self = bool(not request.user.is_anonymous
                            and request.user.email == email)

        if getattr(request, 'limited', False):
            return Response({'error_code': 'rate-limited'}, status=403)

        if get_email_validation_error(email):
            return Response({'error_code': 'incorrect-email'}, status=400)

        try:
            course_key = CourseKey.from_string(course_id)
            course = CourseOverview.get_from_id(course_key)
        except InvalidKeyError:
            return Response({'error_code': 'invalid-course-key'}, status=400)
        except CourseOverview.DoesNotExist:
            return Response({'error_code': 'course-not-found'}, status=404)

        SavedCourse.objects.update_or_create(
            user_id=user.id,
            email=email,
            course_id=course_id,
            org_img_url=org_img_url,
            marketing_url=marketing_url,
            weeks_to_complete=weeks_to_complete,
            min_effort=min_effort,
            max_effort=max_effort,
            reminder_email_sent=False,
        )
        course_data = {
            'course': course,
            'send_to_self': send_to_self,
            'user_id': user_id,
            'pref-lang': pref_lang,
            'org_img_url': org_img_url,
            'marketing_url': marketing_url,
            'weeks_to_complete': weeks_to_complete,
            'min_effort': min_effort,
            'max_effort': max_effort,
            'type': 'course',
            'reminder': False,
            'braze_event': USER_SEND_SAVE_FOR_LATER_EMAIL,
        }
        if send_email(email, course_data):
            return Response({'result': 'success'}, status=200)
        else:
            return Response({'error_code': 'email-not-send'}, status=400)
コード例 #53
0
ファイル: plugins.py プロジェクト: sunbest/edx-platform
 def is_enabled(cls, course_key: CourseKey) -> bool:
     """
     Get calculator enabled status from course overview model.
     """
     return CourseOverview.get_from_id(course_key).show_calculator
コード例 #54
0
def award_course_certificate(self, username, course_run_key):
    """
    This task is designed to be called whenever a student GeneratedCertificate is updated.
    It can be called independently for a username and a course_run, but is invoked on each GeneratedCertificate.save.
    """
    LOGGER.info('Running task award_course_certificate for username %s',
                username)

    countdown = 2**self.request.retries

    # If the credentials config model is disabled for this
    # feature, it may indicate a condition where processing of such tasks
    # has been temporarily disabled.  Since this is a recoverable situation,
    # mark this task for retry instead of failing it altogether.

    if not CredentialsApiConfig.current().is_learner_issuance_enabled:
        LOGGER.warning(
            'Task award_course_certificate cannot be executed when credentials issuance is disabled in API config',
        )
        raise self.retry(countdown=countdown, max_retries=MAX_RETRIES)

    try:
        course_key = CourseKey.from_string(course_run_key)
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            LOGGER.exception(
                'Task award_course_certificate was called with invalid username %s',
                username)
            # Don't retry for this case - just conclude the task.
            return
        # Get the cert for the course key and username if it's both passing and available in professional/verified
        try:
            certificate = GeneratedCertificate.eligible_certificates.get(
                user=user.id, course_id=course_key)
        except GeneratedCertificate.DoesNotExist:
            LOGGER.exception(
                'Task award_course_certificate was called without Certificate found for %s to user %s',
                course_key, username)
            return
        if certificate.mode in CourseMode.CREDIT_ELIGIBLE_MODES + CourseMode.CREDIT_MODES:
            try:
                course_overview = CourseOverview.get_from_id(course_key)
            except (CourseOverview.DoesNotExist, IOError):
                LOGGER.exception(
                    'Task award_course_certificate was called without course overview data for course %s',
                    course_key)
                return
            credentials_client = get_credentials_api_client(
                User.objects.get(
                    username=settings.CREDENTIALS_SERVICE_USERNAME),
                org=course_key.org,
            )
            # FIXME This may result in visible dates that do not update alongside the Course Overview if that changes
            # This is a known limitation of this implementation and was chosen to reduce the amount of replication,
            # endpoints, celery tasks, and jenkins jobs that needed to be written for this functionality
            visible_date = available_date_for_certificate(
                course_overview, certificate)
            post_course_certificate(credentials_client, username, certificate,
                                    visible_date)

            LOGGER.info('Awarded certificate for course %s to user %s',
                        course_key, username)
    except Exception as exc:
        LOGGER.exception(
            'Failed to determine course certificates to be awarded for user %s',
            username)
        raise self.retry(exc=exc, countdown=countdown, max_retries=MAX_RETRIES)
コード例 #55
0
ファイル: plugins.py プロジェクト: sunbest/edx-platform
 def is_enabled(cls, course_key: CourseKey) -> bool:
     """
     The progress course status is stored in the course module.
     """
     return not CourseOverview.get_from_id(course_key).hide_progress_tab
コード例 #56
0
ファイル: tasks.py プロジェクト: sshyran/edx-platform
def award_course_certificate(self, username, course_run_key, certificate_available_date=None):
    """
    This task is designed to be called whenever a student GeneratedCertificate is updated.
    It can be called independently for a username and a course_run, but is invoked on each GeneratedCertificate.save.

    Arguments:
        username (str): The user to award the Credentials course cert to
        course_run_key (str): The course run key to award the certificate for
        certificate_available_date (str): A string representation of the datetime for when to make the certificate
            available to the user. If not provided, it will calculate the date.

    """
    def _retry_with_custom_exception(username, course_run_key, reason, countdown):
        exception = MaxRetriesExceededError(
            f"Failed to award course certificate for user {username} for course {course_run_key}. Reason: {reason}"
        )
        return self.retry(
            exc=exception,
            countdown=countdown,
            max_retries=MAX_RETRIES
        )

    LOGGER.info(f"Running task award_course_certificate for username {username}")

    countdown = 2 ** self.request.retries

    # If the credentials config model is disabled for this
    # feature, it may indicate a condition where processing of such tasks
    # has been temporarily disabled.  Since this is a recoverable situation,
    # mark this task for retry instead of failing it altogether.

    if not CredentialsApiConfig.current().is_learner_issuance_enabled:
        error_msg = (
            "Task award_course_certificate cannot be executed when credentials issuance is disabled in API config"
        )
        LOGGER.warning(error_msg)
        raise _retry_with_custom_exception(
            username=username,
            course_run_key=course_run_key,
            reason=error_msg,
            countdown=countdown
        )

    try:
        course_key = CourseKey.from_string(course_run_key)
        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            LOGGER.exception(f"Task award_course_certificate was called with invalid username {username}")
            # Don't retry for this case - just conclude the task.
            return
        # Get the cert for the course key and username if it's both passing and available in professional/verified
        try:
            certificate = GeneratedCertificate.eligible_certificates.get(
                user=user.id,
                course_id=course_key
            )
        except GeneratedCertificate.DoesNotExist:
            LOGGER.exception(
                "Task award_course_certificate was called without Certificate found "
                f"for {course_key} to user {username}"
            )
            return
        if certificate.mode in CourseMode.CERTIFICATE_RELEVANT_MODES:
            try:
                course_overview = CourseOverview.get_from_id(course_key)
            except (CourseOverview.DoesNotExist, IOError):
                LOGGER.exception(
                    f"Task award_course_certificate was called without course overview data for course {course_key}"
                )
                return
            credentials_client = get_credentials_api_client(User.objects.get(
                username=settings.CREDENTIALS_SERVICE_USERNAME),
                org=course_key.org,
            )

            # Date is being passed via JSON and is encoded in the EMCA date time string format. The rest of the code
            # expects a datetime.
            if certificate_available_date:
                certificate_available_date = datetime.strptime(certificate_available_date, VISIBLE_DATE_FORMAT)

            # Even in the cases where this task is called with a certificate_available_date, we still need to retrieve
            # the course overview because it's required to determine if we should use the certificate_available_date or
            # the certs modified date
            visible_date = available_date_for_certificate(
                course_overview,
                certificate,
                certificate_available_date=certificate_available_date
            )
            LOGGER.info(
                "Task award_course_certificate will award certificate for course "
                f"{course_key} with a visible date of {visible_date}"
            )
            post_course_certificate(credentials_client, username, certificate, visible_date)

            LOGGER.info(f"Awarded certificate for course {course_key} to user {username}")
    except Exception as exc:
        error_msg = f"Failed to determine course certificates to be awarded for user {username}."
        LOGGER.exception(error_msg)
        raise _retry_with_custom_exception(
            username=username,
            course_run_key=course_run_key,
            reason=error_msg,
            countdown=countdown
        ) from exc
コード例 #57
0
    def get(self, request, *args, **kwargs):
        course_key_string = kwargs.get('course_key_string')
        course_key = CourseKey.from_string(course_key_string)
        course_usage_key = modulestore().make_course_usage_key(course_key)

        if not course_home_mfe_outline_tab_is_active(course_key):
            raise Http404

        # Enable NR tracing for this view based on course
        monitoring_utils.set_custom_attribute('course_id', course_key_string)
        monitoring_utils.set_custom_attribute('user_id', request.user.id)
        monitoring_utils.set_custom_attribute('is_staff',
                                              request.user.is_staff)

        course = get_course_with_access(request.user,
                                        'load',
                                        course_key,
                                        check_if_enrolled=False)

        masquerade_object, request.user = setup_masquerade(
            request,
            course_key,
            staff_access=has_access(request.user, 'staff', course_key),
            reset_masquerade_data=True,
        )

        user_is_masquerading = is_masquerading(
            request.user, course_key, course_masquerade=masquerade_object)

        course_overview = CourseOverview.get_from_id(course_key)
        enrollment = CourseEnrollment.get_enrollment(request.user, course_key)
        allow_anonymous = COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.is_enabled(
            course_key)
        allow_public = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC
        allow_public_outline = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC_OUTLINE

        # User locale settings
        user_timezone_locale = user_timezone_locale_prefs(request)
        user_timezone = user_timezone_locale['user_timezone']

        dates_tab_link = request.build_absolute_uri(
            reverse('dates', args=[course.id]))
        if course_home_mfe_dates_tab_is_active(course.id):
            dates_tab_link = get_learning_mfe_home_url(course_key=course.id,
                                                       view_name='dates')

        # Set all of the defaults
        access_expiration = None
        course_blocks = None
        course_goals = {'goal_options': [], 'selected_goal': None}
        course_tools = CourseToolsPluginManager.get_enabled_course_tools(
            request, course_key)
        dates_widget = {
            'course_date_blocks': [],
            'dates_tab_link': dates_tab_link,
            'user_timezone': user_timezone,
        }
        enroll_alert = {
            'can_enroll': True,
            'extra_text': None,
        }
        handouts_html = None
        offer_data = None
        resume_course = {
            'has_visited_course': False,
            'url': None,
        }
        welcome_message_html = None

        is_enrolled = enrollment and enrollment.is_active
        is_staff = bool(has_access(request.user, 'staff', course_key))
        show_enrolled = is_enrolled or is_staff
        if show_enrolled:
            course_blocks = get_course_outline_block_tree(
                request, course_key_string, request.user)
            date_blocks = get_course_date_blocks(course,
                                                 request.user,
                                                 request,
                                                 num_assignments=1)
            dates_widget['course_date_blocks'] = [
                block for block in date_blocks
                if not isinstance(block, TodaysDate)
            ]

            handouts_html = get_course_info_section(request, request.user,
                                                    course, 'handouts')
            welcome_message_html = get_current_update_for_user(request, course)

            offer_data = generate_offer_data(request.user, course_overview)
            access_expiration = get_access_expiration_data(
                request.user, course_overview)

            # Only show the set course goal message for enrolled, unverified
            # users in a course that allows for verified statuses.
            is_already_verified = CourseEnrollment.is_enrolled_as_verified(
                request.user, course_key)
            if not is_already_verified and has_course_goal_permission(
                    request, course_key_string, {'is_enrolled': is_enrolled}):
                course_goals = {
                    'goal_options':
                    valid_course_goals_ordered(include_unsure=True),
                    'selected_goal': None
                }

                selected_goal = get_course_goal(request.user, course_key)
                if selected_goal:
                    course_goals['selected_goal'] = {
                        'key': selected_goal.goal_key,
                        'text': get_course_goal_text(selected_goal.goal_key),
                    }

            try:
                resume_block = get_key_to_last_completed_block(
                    request.user, course.id)
                resume_course['has_visited_course'] = True
                resume_path = reverse('jump_to',
                                      kwargs={
                                          'course_id': course_key_string,
                                          'location': str(resume_block)
                                      })
                resume_course['url'] = request.build_absolute_uri(resume_path)
            except UnavailableCompletionData:
                start_block = get_start_block(course_blocks)
                resume_course['url'] = start_block['lms_web_url']

        elif allow_public_outline or allow_public or user_is_masquerading:
            course_blocks = get_course_outline_block_tree(
                request, course_key_string, None)
            if allow_public or user_is_masquerading:
                handouts_html = get_course_info_section(
                    request, request.user, course, 'handouts')

        if not show_enrolled:
            if CourseMode.is_masters_only(course_key):
                enroll_alert['can_enroll'] = False
                enroll_alert['extra_text'] = _(
                    'Please contact your degree administrator or '
                    'edX Support if you have questions.')
            elif course.invitation_only:
                enroll_alert['can_enroll'] = False

        data = {
            'access_expiration': access_expiration,
            'course_blocks': course_blocks,
            'course_goals': course_goals,
            'course_tools': course_tools,
            'dates_widget': dates_widget,
            'enroll_alert': enroll_alert,
            'handouts_html': handouts_html,
            'has_ended': course.has_ended(),
            'offer': offer_data,
            'resume_course': resume_course,
            'welcome_message_html': welcome_message_html,
        }
        context = self.get_serializer_context()
        context['course_overview'] = course_overview
        context['enable_links'] = show_enrolled or allow_public
        context['enrollment'] = enrollment
        serializer = self.get_serializer_class()(data, context=context)

        return Response(serializer.data)
コード例 #58
0
ファイル: index.py プロジェクト: ririfat750/edx-platform
    def get(self,
            request,
            course_id,
            chapter=None,
            section=None,
            position=None):
        """
        Displays courseware accordion and associated content.  If course, chapter,
        and section are all specified, renders the page, or returns an error if they
        are invalid.

        If section is not specified, displays the accordion opened to the right
        chapter.

        If neither chapter or section are specified, displays the user's most
        recent chapter, or the first chapter if this is the user's first visit.

        Arguments:
            request: HTTP request
            course_id (unicode): course id
            chapter (unicode): chapter url_name
            section (unicode): section url_name
            position (unicode): position in module, eg of <sequential> module
        """
        self.course_key = CourseKey.from_string(course_id)

        if not (request.user.is_authenticated
                or self.enable_unenrolled_access):
            return redirect_to_login(request.get_full_path())

        self.original_chapter_url_name = chapter
        self.original_section_url_name = section
        self.chapter_url_name = chapter
        self.section_url_name = section
        self.position = position
        self.chapter, self.section = None, None
        self.course = None
        self.url = request.path

        try:
            set_custom_attributes_for_course_key(self.course_key)
            self._clean_position()
            with modulestore().bulk_operations(self.course_key):

                self.view = STUDENT_VIEW

                self.course = get_course_with_access(
                    request.user,
                    'load',
                    self.course_key,
                    depth=CONTENT_DEPTH,
                    check_if_enrolled=True,
                    check_if_authenticated=True)
                self.course_overview = CourseOverview.get_from_id(
                    self.course.id)
                self.is_staff = has_access(request.user, 'staff', self.course)

                # There's only one situation where we want to show the public view
                if (not self.is_staff and self.enable_unenrolled_access
                        and self.course.course_visibility
                        == COURSE_VISIBILITY_PUBLIC
                        and not CourseEnrollment.is_enrolled(
                            request.user, self.course_key)):
                    self.view = PUBLIC_VIEW

                self.can_masquerade = request.user.has_perm(
                    MASQUERADE_AS_STUDENT, self.course)
                self._setup_masquerade_for_effective_user()

                return self.render(request)
        except Exception as exception:  # pylint: disable=broad-except
            return CourseTabView.handle_exceptions(request, self.course_key,
                                                   self.course, exception)
コード例 #59
0
 def is_enabled(cls, course, user=None):
     """Returns true if this tab is enabled."""
     # We want to only limit this feature to instructor led courses for now (and limit to relative dates experiment)
     return not CourseOverview.get_from_id(
         course.id).self_paced and RELATIVE_DATES_FLAG.is_enabled(course.id)
コード例 #60
0
    def get(self, request, *args, **kwargs):
        course_key_string = kwargs.get('course_key_string')
        course_key = CourseKey.from_string(course_key_string)
        course_usage_key = modulestore().make_course_usage_key(course_key)

        if course_home_legacy_is_active(course_key):
            raise Http404

        # Enable NR tracing for this view based on course
        monitoring_utils.set_custom_attribute('course_id', course_key_string)
        monitoring_utils.set_custom_attribute('user_id', request.user.id)
        monitoring_utils.set_custom_attribute('is_staff',
                                              request.user.is_staff)

        course = get_course_with_access(request.user,
                                        'load',
                                        course_key,
                                        check_if_enrolled=False)

        masquerade_object, request.user = setup_masquerade(
            request,
            course_key,
            staff_access=has_access(request.user, 'staff', course_key),
            reset_masquerade_data=True,
        )

        user_is_masquerading = is_masquerading(
            request.user, course_key, course_masquerade=masquerade_object)

        course_overview = CourseOverview.get_from_id(course_key)
        enrollment = CourseEnrollment.get_enrollment(request.user, course_key)
        allow_anonymous = COURSE_ENABLE_UNENROLLED_ACCESS_FLAG.is_enabled(
            course_key)
        allow_public = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC
        allow_public_outline = allow_anonymous and course.course_visibility == COURSE_VISIBILITY_PUBLIC_OUTLINE

        # User locale settings
        user_timezone_locale = user_timezone_locale_prefs(request)
        user_timezone = user_timezone_locale['user_timezone']

        if course_home_legacy_is_active(course.id):
            dates_tab_link = request.build_absolute_uri(
                reverse('dates', args=[course.id]))
        else:
            dates_tab_link = get_learning_mfe_home_url(course_key=course.id,
                                                       view_name='dates')

        # Set all of the defaults
        access_expiration = None
        cert_data = None
        course_blocks = None
        course_goals = {'goal_options': [], 'selected_goal': None}
        course_tools = CourseToolsPluginManager.get_enabled_course_tools(
            request, course_key)
        dates_widget = {
            'course_date_blocks': [],
            'dates_tab_link': dates_tab_link,
            'user_timezone': user_timezone,
        }
        enroll_alert = {
            'can_enroll': True,
            'extra_text': None,
        }
        handouts_html = None
        offer_data = None
        resume_course = {
            'has_visited_course': False,
            'url': None,
        }
        welcome_message_html = None

        is_enrolled = enrollment and enrollment.is_active
        is_staff = bool(has_access(request.user, 'staff', course_key))
        show_enrolled = is_enrolled or is_staff
        if show_enrolled:
            course_blocks = get_course_outline_block_tree(
                request, course_key_string, request.user)
            date_blocks = get_course_date_blocks(course,
                                                 request.user,
                                                 request,
                                                 num_assignments=1)
            dates_widget['course_date_blocks'] = [
                block for block in date_blocks
                if not isinstance(block, TodaysDate)
            ]

            handouts_html = get_course_info_section(request, request.user,
                                                    course, 'handouts')
            welcome_message_html = get_current_update_for_user(request, course)

            offer_data = generate_offer_data(request.user, course_overview)
            access_expiration = get_access_expiration_data(
                request.user, course_overview)
            cert_data = get_cert_data(request.user, course,
                                      enrollment.mode) if is_enrolled else None

            # Only show the set course goal message for enrolled, unverified
            # users in a course that allows for verified statuses.
            is_already_verified = CourseEnrollment.is_enrolled_as_verified(
                request.user, course_key)
            if not is_already_verified and has_course_goal_permission(
                    request, course_key_string, {'is_enrolled': is_enrolled}):
                course_goals = {
                    'goal_options':
                    valid_course_goals_ordered(include_unsure=True),
                    'selected_goal': None
                }

                selected_goal = get_course_goal(request.user, course_key)
                if selected_goal:
                    course_goals['selected_goal'] = {
                        'key': selected_goal.goal_key,
                        'text': get_course_goal_text(selected_goal.goal_key),
                    }

            try:
                resume_block = get_key_to_last_completed_block(
                    request.user, course.id)
                resume_course['has_visited_course'] = True
                resume_path = reverse('jump_to',
                                      kwargs={
                                          'course_id': course_key_string,
                                          'location': str(resume_block)
                                      })
                resume_course['url'] = request.build_absolute_uri(resume_path)
            except UnavailableCompletionData:
                start_block = get_start_block(course_blocks)
                resume_course['url'] = start_block['lms_web_url']

        elif allow_public_outline or allow_public or user_is_masquerading:
            course_blocks = get_course_outline_block_tree(
                request, course_key_string, None)
            if allow_public or user_is_masquerading:
                handouts_html = get_course_info_section(
                    request, request.user, course, 'handouts')

        if not show_enrolled:
            if CourseMode.is_masters_only(course_key):
                enroll_alert['can_enroll'] = False
                enroll_alert['extra_text'] = _(
                    'Please contact your degree administrator or '
                    'edX Support if you have questions.')
            elif course.invitation_only:
                enroll_alert['can_enroll'] = False

        # Sometimes there are sequences returned by Course Blocks that we
        # don't actually want to show to the user, such as when a sequence is
        # composed entirely of units that the user can't access. The Learning
        # Sequences API knows how to roll this up, so we use it determine which
        # sequences we should remove from course_blocks.
        #
        # The long term goal is to remove the Course Blocks API call entirely,
        # so this is a tiny first step in that migration.
        if course_blocks and learning_sequences_api_available(
                course_key, request.user):
            user_course_outline = get_user_course_outline(
                course_key, request.user, datetime.now(tz=timezone.utc))
            available_seq_ids = {
                str(usage_key)
                for usage_key in user_course_outline.sequences
            }

            # course_blocks is a reference to the root of the course, so we go
            # through the chapters (sections) to look for sequences to remove.
            for chapter_data in course_blocks['children']:
                chapter_data['children'] = [
                    seq_data for seq_data in chapter_data['children'] if
                    (seq_data['id'] in available_seq_ids or
                     # Edge case: Sometimes we have weird course structures.
                     # We expect only sequentials here, but if there is
                     # another type, just skip it (don't filter it out).
                     seq_data['type'] != 'sequential')
                ] if 'children' in chapter_data else []

        data = {
            'access_expiration': access_expiration,
            'cert_data': cert_data,
            'course_blocks': course_blocks,
            'course_goals': course_goals,
            'course_tools': course_tools,
            'dates_widget': dates_widget,
            'enroll_alert': enroll_alert,
            'handouts_html': handouts_html,
            'has_ended': course.has_ended(),
            'offer': offer_data,
            'resume_course': resume_course,
            'welcome_message_html': welcome_message_html,
        }
        context = self.get_serializer_context()
        context['course_overview'] = course_overview
        context['enable_links'] = show_enrolled or allow_public
        context['enrollment'] = enrollment
        serializer = self.get_serializer_class()(data, context=context)

        return Response(serializer.data)