Exemplo n.º 1
0
 def register_alerts(self, request, course):
     """
     Registers an alert close to the certificate delivery date.
     """
     is_enrolled = CourseEnrollment.get_enrollment(request.user, course.id)
     if not is_enrolled or not self.is_enabled or course.end > self.current_time:
         return
     if self.date > self.current_time:
         CourseHomeMessages.register_info_message(
             request,
             Text(
                 _(u'If you have earned a certificate, you will be able to access it {time_remaining_string}'
                   u' from now. You will also be able to view your certificates on your {learner_profile_link}.'
                   )
             ).format(
                 time_remaining_string=self.time_remaining_string,
                 learner_profile_link=HTML(
                     u'<a href="{learner_profile_url}">{learner_profile_name}</a>'
                 ).format(
                     learner_profile_url=reverse(
                         'learner_profile',
                         kwargs={'username': request.user.username}),
                     learner_profile_name=_('Learner Profile'),
                 ),
             ),
             title=Text(
                 _('We are working on generating course certificates.')))
Exemplo n.º 2
0
 def post(self, request, username_or_email):
     """Allows support staff to alter a user's enrollment."""
     try:
         user = User.objects.get(
             Q(username=username_or_email) | Q(email=username_or_email))
         course_id = request.data['course_id']
         course_key = CourseKey.from_string(course_id)
         old_mode = request.data['old_mode']
         new_mode = request.data['new_mode']
         reason = request.data['reason']
         enrollment = CourseEnrollment.objects.get(user=user,
                                                   course_id=course_key)
         if enrollment.mode != old_mode:
             return HttpResponseBadRequest(
                 'User {username} is not enrolled with mode {old_mode}.'.
                 format(username=user.username, old_mode=old_mode))
     except KeyError as err:
         return HttpResponseBadRequest('The field {} is required.'.format(
             str(err)))
     except InvalidKeyError:
         return HttpResponseBadRequest('Could not parse course key.')
     except (CourseEnrollment.DoesNotExist, User.DoesNotExist):
         return HttpResponseBadRequest(
             'Could not find enrollment for user {username} in course {course}.'
             .format(username=username_or_email, course=str(course_key)))
     try:
         # Wrapped in a transaction so that we can be sure the
         # ManualEnrollmentAudit record is always created correctly.
         with transaction.atomic():
             update_enrollment(user.username,
                               course_id,
                               mode=new_mode,
                               include_expired=True)
             manual_enrollment = ManualEnrollmentAudit.create_manual_enrollment_audit(
                 request.user,
                 enrollment.user.email,
                 ENROLLED_TO_ENROLLED,
                 reason=reason,
                 enrollment=enrollment)
             if new_mode == CourseMode.CREDIT_MODE:
                 provider_ids = get_credit_provider_attribute_values(
                     course_key, 'id')
                 credit_provider_attr = {
                     'namespace': 'credit',
                     'name': 'provider_id',
                     'value': provider_ids[0],
                 }
                 CourseEnrollmentAttribute.add_enrollment_attr(
                     enrollment=enrollment,
                     data_list=[credit_provider_attr])
             entitlement = CourseEntitlement.get_fulfillable_entitlement_for_user_course_run(
                 user=user, course_run_key=course_id)
             if entitlement is not None and entitlement.mode == new_mode:
                 entitlement.set_enrollment(
                     CourseEnrollment.get_enrollment(user, course_id))
             return JsonResponse(
                 ManualEnrollmentSerializer(
                     instance=manual_enrollment).data)
     except CourseModeNotFoundError as err:
         return HttpResponseBadRequest(str(err))
Exemplo n.º 3
0
def get_expiration_banner_text(user, course, language='en'):  # lint-amnesty, pylint: disable=unused-argument
    """
    Get text for banner that messages user course expiration date
    for different tests that depend on it.
    """
    upgrade_link = verified_upgrade_deadline_link(user=user, course=course)
    enrollment = CourseEnrollment.get_enrollment(user, course.id)
    expiration_date = enrollment.created + timedelta(weeks=4)
    upgrade_deadline = enrollment.upgrade_deadline
    if upgrade_deadline is None or now() < upgrade_deadline:
        upgrade_deadline = enrollment.course_upgrade_deadline

    formatted_expiration_date = strftime_localized_html(expiration_date, 'SHORT_DATE')
    if upgrade_deadline:
        formatted_upgrade_deadline = strftime_localized_html(upgrade_deadline, 'SHORT_DATE')

        bannerText = '<strong>Audit Access Expires {expiration_date}</strong><br>\
                     You lose all access to this course, including your progress, on {expiration_date}.\
                     <br>Upgrade by {upgrade_deadline} to get unlimited access to the course as long as it exists\
                     on the site. <a id="FBE_banner" href="{upgrade_link}">Upgrade now<span class="sr-only"> to retain access past\
                     {expiration_date}</span></a>'.format(
            expiration_date=formatted_expiration_date,
            upgrade_link=upgrade_link,
            upgrade_deadline=formatted_upgrade_deadline
        )
    else:
        bannerText = '<strong>Audit Access Expires {expiration_date}</strong><br>\
                     You lose all access to this course, including your progress, on {expiration_date}.\
                     '.format(
            expiration_date=formatted_expiration_date
        )
    return bannerText
Exemplo n.º 4
0
def delete_enrollment(*args, **kwargs):
    """
    Delete enrollment and enrollment attributes of given user in the course provided.

    Example:
        >>>delete_enrollment(
            {
            "user": user_object,
            "course_id": course-v1-edX-DemoX-1T2015"
        )
    """
    course_id = kwargs.pop('course_id', None)
    user = kwargs.get('user')
    try:
        course_key = get_valid_course_key(course_id)
    except InvalidKeyError:
        raise NotFound('No course found by course id `{}`'.format(course_id))

    username = user.username

    LOG.info('Deleting enrollment. User: `%s`  course: `%s`', username,
             course_id)
    enrollment = CourseEnrollment.get_enrollment(user, course_key)
    if not enrollment:
        raise NotFound(
            'No enrollment found for user: `{}` on course_id `{}`'.format(
                username, course_id))
    try:
        enrollment.delete()
    except Exception:
        raise NotFound(
            'Error: Enrollment could not be deleted for user: `{}` on course_id `{}`'
            .format(username, course_id))
Exemplo n.º 5
0
    def __init__(self, course_key, request, username=''):
        self.request = request
        self.overview = course_detail(
            self.request,
            username or self.request.user.username,
            course_key,
        )

        self.original_user_is_staff = has_access(self.request.user, 'staff',
                                                 self.overview).has_access
        self.original_user_is_global_staff = self.request.user.is_staff
        self.course_key = course_key
        self.course = get_course_by_id(self.course_key)
        self.course_masquerade, self.effective_user = setup_masquerade(
            self.request,
            course_key,
            staff_access=self.original_user_is_staff,
        )
        self.request.user = self.effective_user
        self.is_staff = has_access(self.effective_user, 'staff',
                                   self.overview).has_access
        self.enrollment_object = CourseEnrollment.get_enrollment(
            self.effective_user,
            self.course_key,
            select_related=['celebration', 'user__celebration'])
        self.can_view_legacy_courseware = courseware_legacy_is_visible(
            course_key=course_key,
            is_global_staff=self.original_user_is_global_staff,
        )
Exemplo n.º 6
0
    def post(self, request, course_key_string, *args, **kwargs):  # lint-amnesty, pylint: disable=unused-argument
        """
        Handle a POST request.
        """
        course_key = CourseKey.from_string(course_key_string)

        # Check if we're masquerading as someone else. If so, we should just ignore this request.
        _, user = setup_masquerade(
            request,
            course_key,
            staff_access=has_access(request.user, 'staff', course_key),
            reset_masquerade_data=True,
        )
        if user != request.user:
            return Response(status=202)  # "Accepted"

        data = dict(request.data)
        first_section = data.pop('first_section', None)
        if data:
            return Response(status=400)  # there were parameters we didn't recognize

        enrollment = CourseEnrollment.get_enrollment(request.user, course_key)
        if not enrollment:
            return Response(status=404)

        defaults = {}
        if first_section is not None:
            defaults['celebrate_first_section'] = first_section

        if defaults:
            _, created = CourseEnrollmentCelebration.objects.update_or_create(enrollment=enrollment, defaults=defaults)
            return Response(status=201 if created else 200)
        else:
            return Response(status=200)  # just silently allow it
Exemplo n.º 7
0
def add_or_update_enrollment_attr(username, course_id, attributes):
    """Set enrollment attributes for the enrollment of given user in the
    course provided.

    Args:
        course_id (str): The Course to set enrollment attributes for.
        username: The User to set enrollment attributes for.
        attributes (list): Attributes to be set.

    Example:
        >>>add_or_update_enrollment_attr(
            "Bob",
            "course-v1-edX-DemoX-1T2015",
            [
                {
                    "namespace": "credit",
                    "name": "provider_id",
                    "value": "hogwarts",
                },
            ]
        )
    """
    course_key = CourseKey.from_string(course_id)
    user = _get_user(username)
    enrollment = CourseEnrollment.get_enrollment(user, course_key)
    if not _invalid_attribute(attributes) and enrollment is not None:
        CourseEnrollmentAttribute.add_enrollment_attr(enrollment, attributes)
Exemplo n.º 8
0
 def assert_enrollment(self, mode):
     """
     Assert that the student's enrollment has the correct mode.
     """
     enrollment = CourseEnrollment.get_enrollment(self.student,
                                                  self.course.id)
     assert enrollment.mode == mode
Exemplo n.º 9
0
 def __init__(self, course_key, request, username=''):
     self.request = request
     self.overview = course_detail(
         self.request,
         username or self.request.user.username,
         course_key,
     )
     # We must compute course load access *before* setting up masquerading,
     # else course staff (who are not enrolled) will not be able view
     # their course from the perspective of a learner.
     self.load_access = check_course_access(
         self.overview,
         self.request.user,
         'load',
         check_if_enrolled=True,
         check_if_authenticated=True,
     )
     self.original_user_is_staff = has_access(self.request.user, 'staff', self.overview).has_access
     self.original_user_is_global_staff = self.request.user.is_staff
     self.course_key = course_key
     self.course = get_course_by_id(self.course_key)
     self.course_masquerade, self.effective_user = setup_masquerade(
         self.request,
         course_key,
         staff_access=self.original_user_is_staff,
     )
     self.request.user = self.effective_user
     self.is_staff = has_access(self.effective_user, 'staff', self.overview).has_access
     self.enrollment_object = CourseEnrollment.get_enrollment(self.effective_user, self.course_key,
                                                              select_related=['celebration', 'user__celebration'])
     self.can_view_legacy_courseware = courseware_legacy_is_visible(
         course_key=course_key,
         is_global_staff=self.original_user_is_global_staff,
     )
Exemplo n.º 10
0
def enrollment_date_for_fbe(user, course_key=None, course=None):
    """
    Gets the enrollment date for the given user and course, if FBE is enabled.

    One of course_key or course must be provided.

    Returns:
        None if FBE is disabled for either this user or course
        The enrollment creation date if an enrollment exists
        now() if no enrollment.
    """
    if user is None or (course_key is None and course is None):
        raise ValueError('Both user and either course_key or course must be specified if no enrollment is provided')

    course_key = course_key or course.id

    full_access_masquerade = has_full_access_role_in_masquerade(user, course_key)
    if full_access_masquerade:
        return None
    elif full_access_masquerade is False:
        user = None  # we are masquerading as a generic user, not a specific one -- avoid all user checks below

    if user and user.id and has_staff_roles(user, course_key):
        return None

    enrollment = user and CourseEnrollment.get_enrollment(user, course_key, ['fbeenrollmentexclusion'])

    if is_in_holdback(enrollment):
        return None

    if not correct_modes_for_fbe(enrollment=enrollment, user=user, course_key=course_key, course=course):
        return None

    # If the user isn't enrolled, act as if the user enrolled today
    return enrollment.created if enrollment else timezone.now()
Exemplo n.º 11
0
    def is_enabled(cls, request, course_key):
        """
        Show this tool to all learners who are eligible to upgrade.
        """
        enrollment = CourseEnrollment.get_enrollment(request.user, course_key)
        if enrollment is None:
            return False

        if enrollment.dynamic_upgrade_deadline is None:
            return False

        if not enrollment.is_active:
            return False

        if enrollment.mode not in CourseMode.UPSELL_TO_VERIFIED_MODES:
            return False

        if enrollment.course_upgrade_deadline is None:
            return False

        if datetime.datetime.now(
                pytz.UTC) >= enrollment.course_upgrade_deadline:
            return False

        return True
Exemplo n.º 12
0
    def get_upgradeable_enrollments_for_entitlement(self, entitlement):
        """
        Retrieve all the CourseEnrollments that are upgradeable for a given CourseEntitlement

        Arguments:
            entitlement: CourseEntitlement that we are requesting the CourseEnrollments for.

        Returns:
            list: List of upgradeable CourseEnrollments
        """
        # find all course_runs within the course
        course_runs = get_course_runs_for_course(entitlement.course_uuid)

        # check if the user has enrollments for any of the course_runs
        upgradeable_enrollments = []
        for course_run in course_runs:
            course_run_id = CourseKey.from_string(course_run.get('key'))
            enrollment = CourseEnrollment.get_enrollment(
                entitlement.user, course_run_id)

            if (enrollment and enrollment.is_active
                    and is_course_run_entitlement_fulfillable(
                        course_run_id, entitlement)):
                upgradeable_enrollments.append(enrollment)

        return upgradeable_enrollments
Exemplo n.º 13
0
 def register_alerts(self, request, course):
     """
     Registers an alert if the end date is approaching.
     """
     is_enrolled = CourseEnrollment.get_enrollment(request.user, course.id)
     if not course.start or self.current_time < course.start or not is_enrolled:
         return
     days_until_end = (course.end - self.current_time).days
     if course.end > self.current_time and days_until_end <= settings.COURSE_MESSAGE_ALERT_DURATION_IN_DAYS:
         if days_until_end > 0:
             CourseHomeMessages.register_info_message(
                 request,
                 Text(self.description),
                 title=Text(
                     _(u'This course is ending in {time_remaining_string} on {course_end_date}.'
                       )).format(
                           time_remaining_string=self.time_remaining_string,
                           course_end_date=self.long_date_html,
                       ))
         else:
             CourseHomeMessages.register_info_message(
                 request,
                 Text(self.description),
                 title=Text(
                     _(u'This course is ending in {time_remaining_string} at {course_end_time}.'
                       )).format(
                           time_remaining_string=self.time_remaining_string,
                           course_end_time=self.short_time_html,
                       ))
Exemplo n.º 14
0
def course_run_refund_status(request, course_id):
    """
    Get Refundable status for a course.

    Arguments:
        request: The request object.
        course_id (str): The unique identifier for the course.

    Returns:
        Json response.

    """

    try:
        course_key = CourseKey.from_string(course_id)
        course_enrollment = CourseEnrollment.get_enrollment(request.user, course_key)

    except InvalidKeyError:
        logging.exception("The course key used to get refund status caused InvalidKeyError during look up.")

        return JsonResponse({'course_refundable_status': ''}, status=406)

    refundable_status = course_enrollment.refundable()
    logging.info(f"Course refund status for course {course_id} is {refundable_status}")

    return JsonResponse({'course_refundable_status': refundable_status}, status=200)
    def test_change_enrollment_mode_fullfills_entitlement(self, search_string_type, mock_get_course_uuid):
        """
        Assert that changing student's enrollment fulfills it's respective entitlement if it exists.
        """
        assert ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email) is None
        enrollment = CourseEnrollment.get_enrollment(self.student, self.course.id)
        entitlement = CourseEntitlementFactory.create(
            user=self.user,
            mode=CourseMode.VERIFIED,
            enrollment_course_run=enrollment
        )
        mock_get_course_uuid.return_value = entitlement.course_uuid

        url = reverse(
            'support:enrollment_list',
            kwargs={'username_or_email': getattr(self.student, search_string_type)}
        )
        response = self.client.post(url, data={
            'course_id': str(self.course.id),
            'old_mode': CourseMode.AUDIT,
            'new_mode': CourseMode.VERIFIED,
            'reason': 'Financial Assistance'
        })
        entitlement.refresh_from_db()
        assert response.status_code == 200
        assert ManualEnrollmentAudit.get_manual_enrollment_by_email(self.student.email) is not None
        assert entitlement.enrollment_course_run is not None
        assert entitlement.is_entitlement_redeemable() is False
        self.assert_enrollment(CourseMode.VERIFIED)
Exemplo n.º 16
0
 def register_alerts(self, request, course):
     """
     Registers an alert if the course has not started yet.
     """
     is_enrolled = CourseEnrollment.get_enrollment(request.user, course.id)
     if not course.start or not is_enrolled:
         return
     days_until_start = (course.start - self.current_time).days
     if course.start > self.current_time:
         if days_until_start > 0:
             CourseHomeMessages.register_info_message(
                 request,
                 Text(_("Don't forget to add a calendar reminder!")),
                 title=Text(
                     _(u"Course starts in {time_remaining_string} on {course_start_date}."
                       )).format(
                           time_remaining_string=self.time_remaining_string,
                           course_start_date=self.long_date_html,
                       ))
         else:
             CourseHomeMessages.register_info_message(
                 request,
                 Text(
                     _(u"Course starts in {time_remaining_string} at {course_start_time}."
                       )).format(
                           time_remaining_string=self.time_remaining_string,
                           course_start_time=self.short_time_html,
                       ))
    def get_credit_state(self, user_id, course_key_or_id, return_course_info=False):
        """
        Return all information about the user's credit state inside of a given
        course.

        ARGS:
            - user_id: The PK of the User in question
            - course_key: The course ID (as string or CourseKey)

        RETURNS:
            NONE (user not found or is not enrolled or is not credit course)
            - or -
            {
                'enrollment_mode': the mode that the user is enrolled in the course
                'profile_fullname': the name that the student registered under, used for verification
                'is_credit_course': if the course has been marked as a credit bearing course
                'credit_requirement_status': the user's status in fulfilling those requirements
                'course_name': optional display name of the course
                'course_end_date': optional end date of the course
            }
        """

        # This seems to need to be here otherwise we get
        # circular references when starting up the app
        from openedx.core.djangoapps.credit.api.eligibility import (
            is_credit_course,
            get_credit_requirement_status,
        )

        # since we have to do name matching during various
        # verifications, User must have a UserProfile
        try:
            user = User.objects.select_related('profile').get(id=user_id)
        except ObjectDoesNotExist:
            # bad user_id
            return None

        course_key = _get_course_key(course_key_or_id)

        enrollment = CourseEnrollment.get_enrollment(user, course_key)
        if not enrollment or not enrollment.is_active:
            # not enrolled
            return None

        result = {
            'enrollment_mode': enrollment.mode,
            'profile_fullname': user.profile.name,
            'student_email': user.email,
            'is_credit_course': is_credit_course(course_key),
            'credit_requirement_status': get_credit_requirement_status(course_key, user.username)
        }

        if return_course_info:
            course = modulestore().get_course(course_key, depth=0)
            result.update({
                'course_name': course.display_name,
                'course_end_date': course.end,
            })
        return result
Exemplo n.º 18
0
 def date(self):
     if not self.course.self_paced:
         return self.course.start
     else:
         enrollment = CourseEnrollment.get_enrollment(
             self.user, self.course_id)
         return max(enrollment.created,
                    self.course.start) if enrollment else self.course.start
Exemplo n.º 19
0
    def test_course_home_for_global_staff(self):
        """
        Tests that staff user can access the course home without being enrolled
        in the course.
        """
        course = self.course
        self.user.is_staff = True
        self.user.save()

        self.override_waffle_switch(True)
        CourseEnrollment.get_enrollment(self.user, course.id).delete()
        response = self.visit_course_home(course,
                                          start_count=1,
                                          resume_count=0)
        content = pq(response.content)
        vertical = course.children[0].children[0].children[0]
        assert content('.action-resume-course').attr('href').endswith(
            '/vertical/' + vertical.url_name)
    def handle(self, *args, **options):
        """
        Handle the send save for later reminder emails.
        """

        reminder_email_threshold_date = datetime.now() - timedelta(
            days=settings.SAVE_FOR_LATER_REMINDER_EMAIL_THRESHOLD)
        saved_courses_ids = SavedCourse.objects.filter(
            reminder_email_sent=False,
            modified__lt=reminder_email_threshold_date).values_list('id',
                                                                    flat=True)
        total = saved_courses_ids.count()
        batch_size = max(1, options.get('batch_size'))
        num_batches = ((total - 1) / batch_size + 1) if total > 0 else 0

        for batch_num in range(int(num_batches)):
            reminder_email_sent_ids = []
            start = batch_num * batch_size
            end = min(start + batch_size, total) - 1
            saved_courses_batch_ids = list(saved_courses_ids)[start:end + 1]

            query = SavedCourse.objects.filter(
                id__in=saved_courses_batch_ids).order_by('-modified')
            saved_courses = use_read_replica_if_available(query)
            for saved_course in saved_courses:
                user = User.objects.filter(email=saved_course.email).first()
                course_overview = CourseOverview.get_from_id(
                    saved_course.course_id)
                course_data = {
                    'course': course_overview,
                    'send_to_self': None,
                    'user_id': saved_course.user_id,
                    'org_img_url': saved_course.org_img_url,
                    'marketing_url': saved_course.marketing_url,
                    'weeks_to_complete': saved_course.weeks_to_complete,
                    'min_effort': saved_course.min_effort,
                    'max_effort': saved_course.max_effort,
                    'type': 'course',
                    'reminder': True,
                    'braze_event': USER_SEND_SAVE_FOR_LATER_REMINDER_EMAIL,
                }
                if user:
                    enrollment = CourseEnrollment.get_enrollment(
                        user, saved_course.course_id)
                    if enrollment:
                        continue
                email_sent = send_email(saved_course.email, course_data)
                if email_sent:
                    reminder_email_sent_ids.append(saved_course.id)
                else:
                    logging.info(
                        "Unable to send reminder email to {user} for {course} course"
                        .format(user=str(saved_course.email),
                                course=str(saved_course.course_id)))
            SavedCourse.objects.filter(id__in=reminder_email_sent_ids).update(
                reminder_email_sent=True)
Exemplo n.º 21
0
def check_and_get_upgrade_link_and_date(user, enrollment=None, course=None):
    """
    For an authenticated user, return a link to allow them to upgrade
    in the specified course.

    Returns the upgrade link and upgrade deadline for a user in a given course given
    that the user is within the window to upgrade defined by our dynamic pacing feature;
    otherwise, returns None for both the link and date.
    """
    if enrollment is None and course is None:
        logger.warning(u'Must specify either an enrollment or a course')
        return (None, None, None)

    if enrollment:
        if course and enrollment.course_id != course.id:
            logger.warning(u'{} refers to a different course than {} which was supplied. Enrollment course id={}, '
                           u'repr={!r}, deprecated={}. Course id={}, repr={!r}, deprecated={}.'
                           .format(enrollment,
                                   course,
                                   enrollment.course_id,
                                   enrollment.course_id,
                                   enrollment.course_id.deprecated,
                                   course.id,
                                   course.id,
                                   course.id.deprecated
                                   )
                           )
            return (None, None, None)

        if enrollment.user_id != user.id:
            logger.warning(u'{} refers to a different user than {} which was supplied. '
                           u'Enrollment user id={}, repr={!r}. '
                           u'User id={}, repr={!r}.'.format(enrollment,
                                                            user,
                                                            enrollment.user_id,
                                                            enrollment.user_id,
                                                            user.id,
                                                            user.id,
                                                            )
                           )
            return (None, None, None)

    if enrollment is None:
        enrollment = CourseEnrollment.get_enrollment(user, course.id)
        if enrollment is None:
            return (None, None, None)

    if user.is_authenticated and can_show_verified_upgrade(user, enrollment, course):
        return (
            verified_upgrade_deadline_link(user, enrollment.course),
            enrollment.upgrade_deadline,
            enrollment.course_upgrade_deadline,
        )

    return (None, None, enrollment.course_upgrade_deadline)
Exemplo n.º 22
0
    def test_create_user__course_staff(self):
        """
        Create a user and set them as course staff
        """
        username = '******'
        self.call_command([username], course=self.course_id, course_staff=True)

        user = self.user_model.objects.get(username=username)
        enrollment = CourseEnrollment.get_enrollment(user, self.course.id)
        assert enrollment.mode == 'audit'
        assert CourseStaffRole(self.course.id).has_user(user)
Exemplo n.º 23
0
    def test_create_user__course_staff__ignore_mode(self):
        """
        Test that mode is ignored when --course_staff is specified
        """
        username = '******'
        self.call_command([username], course=self.course_id, course_staff=True, mode='verified')

        user = self.user_model.objects.get(username=username)
        enrollment = CourseEnrollment.get_enrollment(user, self.course.id)
        assert enrollment.mode == 'audit'
        assert CourseStaffRole(self.course.id).has_user(user)
Exemplo n.º 24
0
    def test_create_user__mode(self):
        """
        Create a test for a user in verified mode.
        """
        # Create a user in verified rather than default audit
        username = '******'
        self.call_command([username], course=self.course_id, mode='verified')

        # Verify enrollment
        user = self.user_model.objects.get(username='******')
        enrollment = CourseEnrollment.get_enrollment(user, self.course.id)
        assert enrollment.mode == 'verified'
Exemplo n.º 25
0
    def handle_goal(goal, today, sunday_date, monday_date):
        """Sends an email reminder for a single CourseGoal, if it passes all our checks"""
        if not COURSE_GOALS_NUMBER_OF_DAYS_GOALS.is_enabled(goal.course_key):
            return False

        enrollment = CourseEnrollment.get_enrollment(goal.user,
                                                     goal.course_key,
                                                     select_related=['course'])
        # If you're not actively enrolled in the course or your enrollment was this week
        if not enrollment or not enrollment.is_active or enrollment.created.date(
        ) >= monday_date:
            return False

        audit_access_expiration_date = get_user_course_expiration_date(
            goal.user, enrollment.course_overview)
        # If an audit user's access expires this week, exclude them from the email since they may not
        # be able to hit their goal anyway
        if audit_access_expiration_date and audit_access_expiration_date.date(
        ) <= sunday_date:
            return False

        cert = get_certificate_for_user_id(goal.user, goal.course_key)
        # If a user has a downloadable certificate, we will consider them as having completed
        # the course and opt them out of receiving emails
        if cert and cert.status == CertificateStatuses.downloadable:
            return False

        # Check the number of days left to successfully hit their goal
        week_activity_count = UserActivity.objects.filter(
            user=goal.user,
            course_key=goal.course_key,
            date__gte=monday_date,
        ).count()
        required_days_left = goal.days_per_week - week_activity_count
        # The weekdays are 0 indexed, but we want this to be 1 to match required_days_left.
        # Essentially, if today is Sunday, days_left_in_week should be 1 since they have Sunday to hit their goal.
        days_left_in_week = SUNDAY_WEEKDAY - today.weekday() + 1

        # We want to email users in the morning of their timezone
        user_timezone = get_user_timezone_or_last_seen_timezone_or_utc(
            goal.user)
        now_in_users_timezone = datetime.now(user_timezone)
        if not 9 <= now_in_users_timezone.hour < 12:
            return False

        if required_days_left == days_left_in_week:
            send_ace_message(goal)
            CourseGoalReminderStatus.objects.update_or_create(
                goal=goal, defaults={'email_reminder_sent': True})
            return True

        return False
    def post(self, request, username_or_email):
        """
        Allows support staff to create a user's enrollment.
        """
        try:
            course_id = request.data['course_id']
            course_key = CourseKey.from_string(course_id)
            mode = request.data['mode']
            reason = request.data['reason']
            user = User.objects.get(
                Q(username=username_or_email) | Q(email=username_or_email))
        except KeyError as err:
            return HttpResponseBadRequest(f'The field {str(err)} is required.')
        except InvalidKeyError:
            return HttpResponseBadRequest('Could not parse course key.')
        except User.DoesNotExist:
            return HttpResponseBadRequest(
                'Could not find user {username}.'.format(
                    username=username_or_email))

        enrollment = CourseEnrollment.get_enrollment(user=user,
                                                     course_key=course_key)
        if enrollment is not None:
            return HttpResponseBadRequest(
                f'The user {str(username_or_email)} is already enrolled in {str(course_id)}.'
            )

        enrollment_modes = [
            enrollment_mode['slug']
            for enrollment_mode in self.get_course_modes(course_key)
        ]
        if mode not in enrollment_modes:
            return HttpResponseBadRequest(
                f'{str(mode)} is not a valid mode for {str(course_id)}. '
                f'Possible valid modes are {str(enrollment_modes)}')

        enrollment = CourseEnrollment.enroll(user=user,
                                             course_key=course_key,
                                             mode=mode)

        # Wrapped in a transaction so that we can be sure the
        # ManualEnrollmentAudit record is always created correctly.
        with transaction.atomic():
            manual_enrollment = ManualEnrollmentAudit.create_manual_enrollment_audit(
                request.user,
                enrollment.user.email,
                UNENROLLED_TO_ENROLLED,
                reason=reason,
                enrollment=enrollment)
            return JsonResponse(
                ManualEnrollmentSerializer(instance=manual_enrollment).data)
    def test_bulk_enrollment_from_config_model(self):
        """ Test all users are enrolled using the config model."""
        lines = "course_id,user,mode\n"
        for enrollment in self.enrollments:
            lines += str(enrollment.course.id) + "," + str(enrollment.user.username) + ",verified\n"

        csv_file = SimpleUploadedFile(name='test.csv', content=lines.encode('utf-8'), content_type='text/csv')
        BulkChangeEnrollmentConfiguration.objects.create(enabled=True, csv_file=csv_file)
        call_command("bulk_change_enrollment_csv", "--file_from_database")

        for enrollment in self.enrollments:
            new_enrollment = CourseEnrollment.get_enrollment(user=enrollment.user, course_key=enrollment.course)
            assert new_enrollment.is_active is True
            assert new_enrollment.mode == CourseMode.VERIFIED
Exemplo n.º 28
0
    def test_create_user_with_course(self):
        """
        Create users and have them enroll in a course
        """
        usernames = ['username1', 'username2']
        self.call_command(usernames, course=self.course_id)

        # Check that the users exist and were enrolled in the course with the default settings
        users = self.user_model.objects.filter(username__in=usernames).all()
        assert len(users) == len(usernames)
        for user in users:
            enrollment = CourseEnrollment.get_enrollment(user, self.course.id)
            assert enrollment.mode == 'audit'
            assert not CourseStaffRole(self.course.id).has_user(user)
Exemplo n.º 29
0
    def get(self, request, *args, **kwargs):
        course_key_string = kwargs.get('course_key_string')
        course_key = CourseKey.from_string(course_key_string)
        original_user_is_staff = has_access(request.user, 'staff',
                                            course_key).has_access

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

        username = request.user.username if request.user.username else None
        course = course_detail(request, request.user.username, course_key)
        enrollment = CourseEnrollment.get_enrollment(request.user,
                                                     course_key_string)
        user_is_enrolled = bool(enrollment and enrollment.is_active)

        courseware_meta = CoursewareMeta(course_key, request,
                                         request.user.username)
        can_load_courseware = courseware_meta.is_microfrontend_enabled_for_user(
        )

        browser_timezone = self.request.query_params.get(
            'browser_timezone', None)
        celebrations = get_celebrations_dict(request.user, enrollment, course,
                                             browser_timezone)

        data = {
            'course_id': course.id,
            'username': username,
            'is_staff': has_access(request.user, 'staff',
                                   course_key).has_access,
            'original_user_is_staff': original_user_is_staff,
            'number': course.display_number_with_default,
            'org': course.display_org_with_default,
            'tabs': get_course_tab_list(request.user, course),
            'title': course.display_name_with_default,
            'is_self_paced': getattr(course, 'self_paced', False),
            'is_enrolled': user_is_enrolled,
            'can_load_courseware': can_load_courseware,
            'celebrations': celebrations,
        }
        context = self.get_serializer_context()
        context['course'] = course
        context['course_overview'] = course
        context['enrollment'] = enrollment
        serializer = self.get_serializer_class()(data, context=context)
        return Response(serializer.data)
    def test_bulk_enrollment(self):
        """ Test all users are enrolled using the command."""
        lines = [
            str(enrollment.course.id) + "," + str(enrollment.user.username) + ",verified\n"
            for enrollment in self.enrollments
        ]

        with NamedTemporaryFile() as csv:
            csv = self._write_test_csv(csv, lines=lines)
            call_command("bulk_change_enrollment_csv", f"--csv_file_path={csv.name}")

        for enrollment in self.enrollments:
            new_enrollment = CourseEnrollment.get_enrollment(user=enrollment.user, course_key=enrollment.course)
            assert new_enrollment.is_active is True
            assert new_enrollment.mode == CourseMode.VERIFIED