Example #1
0
    def __init__(self, site, user, enrollments=None, uuid=None, mobile_only=False):
        self.site = site
        self.user = user
        self.mobile_only = mobile_only

        self.enrollments = enrollments or list(CourseEnrollment.enrollments_for_user(self.user))
        self.enrollments.sort(key=lambda e: e.created, reverse=True)

        self.enrolled_run_modes = {}
        self.course_run_ids = []
        for enrollment in self.enrollments:
            # enrollment.course_id is really a CourseKey (╯ಠ_ಠ)╯︵ ┻━┻
            enrollment_id = unicode(enrollment.course_id)
            mode = enrollment.mode
            if mode == CourseMode.NO_ID_PROFESSIONAL_MODE:
                mode = CourseMode.PROFESSIONAL
            self.enrolled_run_modes[enrollment_id] = mode
            # We can't use dict.keys() for this because the course run ids need to be ordered
            self.course_run_ids.append(enrollment_id)

        self.entitlements = list(CourseEntitlement.unexpired_entitlements_for_user(self.user))
        self.course_uuids = [str(entitlement.course_uuid) for entitlement in self.entitlements]

        self.course_grade_factory = CourseGradeFactory()

        if uuid:
            self.programs = [get_programs(uuid=uuid)]
        else:
            self.programs = attach_program_detail_url(get_programs(self.site), self.mobile_only)
    def test_get_programs_anonymous_user(self, _mock_cache, mock_get_catalog_data):
        programs = [factories.Program() for __ in range(3)]
        mock_get_catalog_data.return_value = programs

        anonymous_user = AnonymousUserFactory()

        # The user is an Anonymous user but the Catalog Service User has not been created yet.
        data = utils.get_programs(anonymous_user)
        # This should not return programs.
        self.assertEqual(data, [])

        UserFactory(username="******")
        # After creating the service user above,
        data = utils.get_programs(anonymous_user)
        # the programs should be returned successfully.
        self.assertEqual(data, programs)
Example #3
0
    def test_programs_unavailable(self, mock_get_edx_api_data):
        mock_get_edx_api_data.return_value = []

        data = get_programs()

        self.assert_contract(mock_get_edx_api_data.call_args)
        self.assertEqual(data, [])
Example #4
0
def program_details(request, program_uuid):
    """View details about a specific program."""
    programs_config = ProgramsApiConfig.current()
    if not programs_config.enabled:
        raise Http404

    program_data = get_programs(uuid=program_uuid)
    if not program_data:
        raise Http404

    program_data = ProgramDataExtender(program_data, request.user).extend()

    urls = {
        'program_listing_url': reverse('program_listing_view'),
        'track_selection_url': strip_course_id(
            reverse('course_modes_choose', kwargs={'course_id': FAKE_COURSE_KEY})
        ),
        'commerce_api_url': reverse('commerce_api:v0:baskets:create'),
    }

    context = {
        'program_data': program_data,
        'urls': urls,
        'show_program_listing': programs_config.enabled,
        'nav_hidden': True,
        'disable_courseware_js': True,
        'uses_pattern_library': True,
        'user_preferences': get_user_preferences(request.user)
    }

    return render_to_response('learner_dashboard/program_details.html', context)
    def test_programs_unavailable(self, _mock_cache, mock_get_catalog_data):
        mock_get_catalog_data.return_value = []

        data = utils.get_programs(self.user)

        self.assert_contract(mock_get_catalog_data.call_args)
        self.assertEqual(data, [])
Example #6
0
def get_user_by_program_id(external_user_id, program_uuid):
    """
    Returns a User model for an external_user_id with a social auth entry.

    Args:
        external_user_id: external user id used for social auth
        program_uuid: a program this user is/will be enrolled in

    Returns:
        A User object or None, if no user with the given external id for the given organization exists.

    Raises:
        ProgramDoesNotExistException if no such program exists.
        OrganizationDoesNotExistException if no organization exists.
        ProviderDoesNotExistException if there is no SAML provider configured for the related organization.
    """
    program = get_programs(uuid=program_uuid)
    if program is None:
        log.error(u'Unable to find catalog program matching uuid [%s]', program_uuid)
        raise ProgramDoesNotExistException

    try:
        org_key = program['authoring_organizations'][0]['key']
        organization = Organization.objects.get(short_name=org_key)
    except (KeyError, IndexError):
        log.error(u'Cannot determine authoring organization key for catalog program [%s]', program_uuid)
        raise OrganizationDoesNotExistException
    except Organization.DoesNotExist:
        log.error(u'Unable to find organization for short_name [%s]', org_key)
        raise OrganizationDoesNotExistException

    return get_user_by_organization(external_user_id, organization)
Example #7
0
    def test_get_many_with_missing(self, mock_cache, mock_warning, mock_info):
        programs = ProgramFactory.create_batch(3)

        all_programs = {
            PROGRAM_CACHE_KEY_TPL.format(uuid=program['uuid']): program for program in programs
        }

        partial_programs = {
            PROGRAM_CACHE_KEY_TPL.format(uuid=program['uuid']): program for program in programs[:2]
        }

        def fake_get_many(keys):
            if len(keys) == 1:
                return {PROGRAM_CACHE_KEY_TPL.format(uuid=programs[-1]['uuid']): programs[-1]}
            else:
                return partial_programs

        mock_cache.get.return_value = [program['uuid'] for program in programs]
        mock_cache.get_many.side_effect = fake_get_many

        actual_programs = get_programs(self.site)

        # All 3 cached programs should be returned. An info message should be
        # logged about the one that was initially missing, but the code should
        # be able to stitch together all the details.
        self.assertEqual(
            set(program['uuid'] for program in actual_programs),
            set(program['uuid'] for program in all_programs.values())
        )
        self.assertFalse(mock_warning.called)
        mock_info.assert_called_with('Failed to get details for 1 programs. Retrying.')

        for program in actual_programs:
            key = PROGRAM_CACHE_KEY_TPL.format(uuid=program['uuid'])
            self.assertEqual(program, all_programs[key])
Example #8
0
    def test_get_one_program(self, mock_get_edx_api_data):
        program = ProgramFactory()
        mock_get_edx_api_data.return_value = program

        data = get_programs(uuid=self.uuid)

        self.assert_contract(mock_get_edx_api_data.call_args, program_uuid=self.uuid)
        self.assertEqual(data, program)
Example #9
0
    def test_get_programs_by_types(self, mock_get_edx_api_data):
        programs = ProgramFactory.create_batch(2)
        mock_get_edx_api_data.return_value = programs

        data = get_programs(types=self.types)

        self.assert_contract(mock_get_edx_api_data.call_args, types=self.types)
        self.assertEqual(data, programs)
    def _load_course_runs(self):
        """Find all course runs which are part of a program."""
        programs = []
        for site in Site.objects.all():
            logger.info('Loading programs from the catalog for site %s.', site.domain)
            programs.extend(get_programs(site))

        self.course_runs = self._flatten(programs)
Example #11
0
    def test_get_programs(self, mock_get_edx_api_data):
        programs = [ProgramFactory() for __ in range(3)]
        mock_get_edx_api_data.return_value = programs

        data = get_programs()

        self.assert_contract(mock_get_edx_api_data.call_args)
        self.assertEqual(data, programs)
Example #12
0
    def test_get_one_program(self, _mock_cache, mock_get_catalog_data):
        program = factories.Program()
        mock_get_catalog_data.return_value = program

        data = utils.get_programs(self.user, uuid=self.uuid)

        self.assert_contract(mock_get_catalog_data.call_args, program_uuid=self.uuid)
        self.assertEqual(data, program)
Example #13
0
    def test_get_programs(self, _mock_cache, mock_get_catalog_data):
        programs = [factories.Program() for __ in range(3)]
        mock_get_catalog_data.return_value = programs

        data = utils.get_programs(self.user)

        self.assert_contract(mock_get_catalog_data.call_args)
        self.assertEqual(data, programs)
Example #14
0
 def program(self):
     """
     The program specified by the `program_uuid` URL parameter.
     """
     program = get_programs(uuid=self.kwargs['program_uuid'])
     if program is None:
         raise Http404()
     return program
Example #15
0
    def test_config_missing(self, _mock_get_edx_api_data):
        """
        Verify that no errors occur if this method is called when catalog config
        is missing.
        """
        CatalogIntegration.objects.all().delete()

        data = get_programs()
        self.assertEqual(data, [])
Example #16
0
    def __init__(self, user, enrollments=None):
        self.user = user

        self.enrollments = enrollments or list(CourseEnrollment.enrollments_for_user(self.user))
        self.enrollments.sort(key=lambda e: e.created, reverse=True)

        # enrollment.course_id is really a CourseKey (╯ಠ_ಠ)╯︵ ┻━┻
        self.course_run_ids = [unicode(e.course_id) for e in self.enrollments]

        self.programs = attach_program_detail_url(get_programs())
Example #17
0
    def test_get_one(self, mock_warning, _mock_info):
        expected_program = ProgramFactory()
        expected_uuid = expected_program['uuid']

        self.assertEqual(get_programs(self.site, uuid=expected_uuid), None)
        mock_warning.assert_called_once_with(
            'Failed to get details for program {uuid} from the cache.'.format(uuid=expected_uuid)
        )
        mock_warning.reset_mock()

        cache.set(
            PROGRAM_CACHE_KEY_TPL.format(uuid=expected_uuid),
            expected_program,
            None
        )

        actual_program = get_programs(self.site, uuid=expected_uuid)
        self.assertEqual(actual_program, expected_program)
        self.assertFalse(mock_warning.called)
Example #18
0
    def test_service_user_missing(self, _mock_get_edx_api_data):
        """
        Verify that no errors occur if this method is called when the catalog
        service user is missing.
        """
        # Note: Deleting the service user would be ideal, but causes mysterious
        # errors on Jenkins.
        self.create_catalog_integration(service_username='******')

        data = get_programs()
        self.assertEqual(data, [])
Example #19
0
    def test_get_from_course(self, mock_warning, _mock_info):
        expected_program = ProgramFactory()
        expected_course = expected_program['courses'][0]['course_runs'][0]['key']

        self.assertEqual(get_programs(course=expected_course), [])

        cache.set(
            COURSE_PROGRAMS_CACHE_KEY_TPL.format(course_run_id=expected_course),
            [expected_program['uuid']],
            None
        )
        cache.set(
            PROGRAM_CACHE_KEY_TPL.format(uuid=expected_program['uuid']),
            expected_program,
            None
        )

        actual_program = get_programs(course=expected_course)
        self.assertEqual(actual_program, [expected_program])
        self.assertFalse(mock_warning.called)
Example #20
0
def is_course_run_in_a_program(course_run_key):
    """ Returns true if the given course key is in any program at all. """

    # We don't have an easy way to go from course_run_key to a specific site that owns it. So just search each site.
    sites = Site.objects.all()
    str_key = str(course_run_key)
    for site in sites:
        for program in get_programs(site):
            for course in program['courses']:
                for course_run in course['course_runs']:
                    if str_key == course_run['key']:
                        return True
    return False
Example #21
0
 def wrapped_function(self, request, **kwargs):
     """
     Wraps the given view_function.
     """
     program_uuid = kwargs['program_uuid']
     program = get_programs(uuid=program_uuid)
     if not program:
         raise self.api_error(
             status_code=status.HTTP_404_NOT_FOUND,
             developer_message='no program exists with given key',
             error_code='program_does_not_exist'
         )
     return view_func(self, request, **kwargs)
Example #22
0
    def __init__(self, user, enrollments=None, uuid=None):
        self.user = user

        self.enrollments = enrollments or list(CourseEnrollment.enrollments_for_user(self.user))
        self.enrollments.sort(key=lambda e: e.created, reverse=True)

        self.enrolled_run_modes = {}
        self.course_run_ids = []
        for enrollment in self.enrollments:
            # enrollment.course_id is really a CourseKey (╯ಠ_ಠ)╯︵ ┻━┻
            enrollment_id = unicode(enrollment.course_id)
            mode = enrollment.mode
            if mode == CourseMode.NO_ID_PROFESSIONAL_MODE:
                mode = CourseMode.PROFESSIONAL
            self.enrolled_run_modes[enrollment_id] = mode
            # We can't use dict.keys() for this because the course run ids need to be ordered
            self.course_run_ids.append(enrollment_id)

        if uuid:
            self.programs = [get_programs(uuid=uuid)]
        else:
            self.programs = attach_program_detail_url(get_programs())
Example #23
0
    def test_get_programs_with_status_filtering(self, mock_get_edx_api_data):
        """ The function should request active and retired programs when the Waffle switch is enabled. """
        programs = ProgramFactory.create_batch(3)
        mock_get_edx_api_data.return_value = programs

        Switch.objects.get_or_create(name='display_retired_programs_on_learner_dashboard', defaults={'active': True})
        data = get_programs()

        expected_querystring = {
            'exclude_utm': 1,
            'status': ('active', 'retired',)
        }
        self.assert_contract(mock_get_edx_api_data.call_args, expected_querystring=expected_querystring)
        self.assertEqual(data, programs)
Example #24
0
def get_program_context(course, user_enrollments, audit_enrollments):
    """
    Return a context dictionary with program information.
    """
    program_key = None
    if PROGRAM_INFO_FLAG.is_enabled():
        programs = get_programs(course=course.id)
        if programs:
            # A course can be in multiple programs, but we're just grabbing the first one
            program = programs[0]
            complete_enrollment = False
            has_courses_left_to_purchase = False
            total_courses = None
            courses = program.get('courses')
            courses_left_to_purchase_price = None
            courses_left_to_purchase_url = None
            program_uuid = program.get('uuid')
            is_eligible_for_one_click_purchase = program.get('is_program_eligible_for_one_click_purchase')
            if courses is not None:
                total_courses = len(courses)
                complete_enrollment = is_enrolled_in_all_courses(courses, user_enrollments)

                # Get the price and purchase URL of the program courses the user has yet to purchase. Say a
                # program has 3 courses (A, B and C), and the user previously purchased a certificate for A.
                # The user is enrolled in audit mode for B. The "left to purchase price" should be the price of
                # B+C.
                non_audit_enrollments = [en for en in user_enrollments if en not in audit_enrollments]
                courses_left_to_purchase = get_unenrolled_courses(courses, non_audit_enrollments)
                if courses_left_to_purchase:
                    has_courses_left_to_purchase = True
                if courses_left_to_purchase and is_eligible_for_one_click_purchase:
                    courses_left_to_purchase_price, courses_left_to_purchase_skus = \
                        get_program_price_and_skus(courses_left_to_purchase)
                    if courses_left_to_purchase_skus:
                        courses_left_to_purchase_url = EcommerceService().get_checkout_page_url(
                            *courses_left_to_purchase_skus, program_uuid=program_uuid)

            program_key = {
                'uuid': program_uuid,
                'title': program.get('title'),
                'marketing_url': program.get('marketing_url'),
                'status': program.get('status'),
                'is_eligible_for_one_click_purchase': is_eligible_for_one_click_purchase,
                'total_courses': total_courses,
                'complete_enrollment': complete_enrollment,
                'has_courses_left_to_purchase': has_courses_left_to_purchase,
                'courses_left_to_purchase_price': courses_left_to_purchase_price,
                'courses_left_to_purchase_url': courses_left_to_purchase_url,
            }
    return program_key
Example #25
0
    def get(self, request):
        """
        How to respond to a GET request to this endpoint
        """
        program_enrollments = ProgramEnrollment.objects.filter(
            user=request.user,
            status__in=('enrolled', 'pending')
        )

        uuids = [enrollment.program_uuid for enrollment in program_enrollments]

        catalog_data_of_programs = get_programs(uuids=uuids) or []
        programs_in_which_learner_is_enrolled = [{'uuid': program['uuid'], 'slug': program['marketing_slug']}
                                                 for program
                                                 in catalog_data_of_programs]

        return Response(programs_in_which_learner_is_enrolled, status.HTTP_200_OK)
Example #26
0
def get_programs_for_credentials(programs_credentials):
    """ Given a user and an iterable of credentials, get corresponding programs
    data and return it as a list of dictionaries.

    Arguments:
        programs_credentials (list): List of credentials awarded to the user
            for completion of a program.

    Returns:
        list, containing programs dictionaries.
    """
    certified_programs = []
    programs = get_programs()
    for program in programs:
        for credential in programs_credentials:
            if program['uuid'] == credential['credential']['program_uuid']:
                program['credential_url'] = credential['certificate_url']
                certified_programs.append(program)

    return certified_programs
Example #27
0
 def __init__(self, program_uuid, request):
     self.program_uuid = program_uuid
     self.program = get_programs(uuid=self.program_uuid)
     self.request = request
     self.configuration = ProgramDiscussionsConfiguration.get(
         self.program_uuid)
Example #28
0
    def test_get_many(self, mock_warning, mock_info):
        programs = ProgramFactory.create_batch(3)

        # Cache details for 2 of 3 programs.
        partial_programs = {
            PROGRAM_CACHE_KEY_TPL.format(uuid=program['uuid']): program
            for program in programs[:2]
        }
        cache.set_many(partial_programs, None)

        # When called before UUIDs are cached, the function should return an
        # empty list and log a warning.
        self.assertEqual(get_programs(self.site), [])
        mock_warning.assert_called_once_with(
            'Failed to get program UUIDs from the cache.')
        mock_warning.reset_mock()

        # Cache UUIDs for all 3 programs.
        cache.set(
            SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(domain=self.site.domain),
            [program['uuid'] for program in programs], None)

        actual_programs = get_programs(self.site)

        # The 2 cached programs should be returned while info and warning
        # messages should be logged for the missing one.
        self.assertEqual(
            set(program['uuid'] for program in actual_programs),
            set(program['uuid'] for program in partial_programs.values()))
        mock_info.assert_called_with(
            'Failed to get details for 1 programs. Retrying.')
        mock_warning.assert_called_with(
            'Failed to get details for program {uuid} from the cache.'.format(
                uuid=programs[2]['uuid']))
        mock_warning.reset_mock()

        # We can't use a set comparison here because these values are dictionaries
        # and aren't hashable. We've already verified that all programs came out
        # of the cache above, so all we need to do here is verify the accuracy of
        # the data itself.
        for program in actual_programs:
            key = PROGRAM_CACHE_KEY_TPL.format(uuid=program['uuid'])
            self.assertEqual(program, partial_programs[key])

        # Cache details for all 3 programs.
        all_programs = {
            PROGRAM_CACHE_KEY_TPL.format(uuid=program['uuid']): program
            for program in programs
        }
        cache.set_many(all_programs, None)

        actual_programs = get_programs(self.site)

        # All 3 programs should be returned.
        self.assertEqual(
            set(program['uuid'] for program in actual_programs),
            set(program['uuid'] for program in all_programs.values()))
        self.assertFalse(mock_warning.called)

        for program in actual_programs:
            key = PROGRAM_CACHE_KEY_TPL.format(uuid=program['uuid'])
            self.assertEqual(program, all_programs[key])
Example #29
0
 def test_cache_disabled(self, _mock_cache, mock_get_catalog_data):
     self.catalog_integration = self.create_catalog_integration(cache_ttl=0)
     utils.get_programs(self.user)
     self.assert_contract(mock_get_catalog_data.call_args)
Example #30
0
 def test_cache_disabled(self, _mock_cache, mock_get_catalog_data):
     self.catalog_integration = self.create_catalog_integration(cache_ttl=0)
     utils.get_programs(self.user)
     self.assert_contract(mock_get_catalog_data.call_args)
Example #31
0
    def test_config_missing(self, _mock_cache, _mock_get_catalog_data):
        """Verify that no errors occur if this method is called when catalog config is missing."""
        CatalogIntegration.objects.all().delete()

        data = utils.get_programs(self.user)
        self.assertEqual(data, [])
Example #32
0
    def test_get_many(self, mock_warning, mock_info):
        programs = ProgramFactory.create_batch(3)

        # Cache details for 2 of 3 programs.
        partial_programs = {
            PROGRAM_CACHE_KEY_TPL.format(uuid=program['uuid']): program for program in programs[:2]
        }
        cache.set_many(partial_programs, None)

        # When called before UUIDs are cached, the function should return an
        # empty list and log a warning.
        self.assertEqual(get_programs(self.site), [])
        mock_warning.assert_called_once_with('Failed to get program UUIDs from the cache.')
        mock_warning.reset_mock()

        # Cache UUIDs for all 3 programs.
        cache.set(
            SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(domain=self.site.domain),
            [program['uuid'] for program in programs],
            None
        )

        actual_programs = get_programs(self.site)

        # The 2 cached programs should be returned while info and warning
        # messages should be logged for the missing one.
        self.assertEqual(
            set(program['uuid'] for program in actual_programs),
            set(program['uuid'] for program in partial_programs.values())
        )
        mock_info.assert_called_with('Failed to get details for 1 programs. Retrying.')
        mock_warning.assert_called_with(
            'Failed to get details for program {uuid} from the cache.'.format(uuid=programs[2]['uuid'])
        )
        mock_warning.reset_mock()

        # We can't use a set comparison here because these values are dictionaries
        # and aren't hashable. We've already verified that all programs came out
        # of the cache above, so all we need to do here is verify the accuracy of
        # the data itself.
        for program in actual_programs:
            key = PROGRAM_CACHE_KEY_TPL.format(uuid=program['uuid'])
            self.assertEqual(program, partial_programs[key])

        # Cache details for all 3 programs.
        all_programs = {
            PROGRAM_CACHE_KEY_TPL.format(uuid=program['uuid']): program for program in programs
        }
        cache.set_many(all_programs, None)

        actual_programs = get_programs(self.site)

        # All 3 programs should be returned.
        self.assertEqual(
            set(program['uuid'] for program in actual_programs),
            set(program['uuid'] for program in all_programs.values())
        )
        self.assertFalse(mock_warning.called)

        for program in actual_programs:
            key = PROGRAM_CACHE_KEY_TPL.format(uuid=program['uuid'])
            self.assertEqual(program, all_programs[key])
Example #33
0
 def _load_course_runs(self):
     """Find all course runs which are part of a program."""
     programs = get_programs()
     self.course_runs = self._flatten(programs)
Example #34
0
def get_experiment_user_metadata_context(course, user):
    """
    Return a context dictionary with the keys used by the user_metadata.html.
    """
    enrollment_mode = None
    enrollment_time = None
    enrollment = None
    # TODO: clean up as part of REVO-28 (START)
    has_non_audit_enrollments = None
    # TODO: clean up as part of REVO-28 (END)
    # TODO: clean up as part of REVEM-199 (START)
    program_key = None
    # TODO: clean up as part of REVEM-199 (END)
    try:
        # TODO: clean up as part of REVO-28 (START)
        user_enrollments = CourseEnrollment.objects.select_related(
            'course').filter(user_id=user.id)
        audit_enrollments = user_enrollments.filter(mode='audit')
        has_non_audit_enrollments = (len(audit_enrollments) !=
                                     len(user_enrollments))
        # TODO: clean up as part of REVO-28 (END)
        # TODO: clean up as part of REVEM-199 (START)
        if PROGRAM_INFO_FLAG.is_enabled():
            programs = get_programs(course=course.id)
            if programs:
                # A course can be in multiple programs, but we're just grabbing the first one
                program = programs[0]
                complete_enrollment = False
                total_courses = None
                courses = program.get('courses')
                if courses is not None:
                    total_courses = len(courses)
                    complete_enrollment = is_enrolled_in_all_courses_in_program(
                        courses, user_enrollments)

                program_key = {
                    'uuid': program.get('uuid'),
                    'title': program.get('title'),
                    'marketing_url': program.get('marketing_url'),
                    'total_courses': total_courses,
                    'complete_enrollment': complete_enrollment,
                }
        # TODO: clean up as part of REVEM-199 (END)
        enrollment = CourseEnrollment.objects.select_related('course').get(
            user_id=user.id, course_id=course.id)
        if enrollment.is_active:
            enrollment_mode = enrollment.mode
            enrollment_time = enrollment.created
    except CourseEnrollment.DoesNotExist:
        pass  # Not enrolled, used the default None values

    # upgrade_link and upgrade_date should be None if user has passed their dynamic pacing deadline.
    upgrade_link, upgrade_date = check_and_get_upgrade_link_and_date(
        user, enrollment, course)
    has_staff_access = has_staff_access_to_preview_mode(user, course.id)
    forum_roles = []
    if user.is_authenticated:
        forum_roles = list(
            Role.objects.filter(
                users=user,
                course_id=course.id).values_list('name').distinct())

    # get user partition data
    if user.is_authenticated():
        partition_groups = get_all_partitions_for_course(course)
        user_partitions = get_user_partition_groups(course.id,
                                                    partition_groups, user,
                                                    'name')
    else:
        user_partitions = {}

    return {
        'upgrade_link': upgrade_link,
        'upgrade_price': unicode(get_cosmetic_verified_display_price(course)),
        'enrollment_mode': enrollment_mode,
        'enrollment_time': enrollment_time,
        'pacing_type':
        'self_paced' if course.self_paced else 'instructor_paced',
        'upgrade_deadline': upgrade_date,
        'course_key': course.id,
        'course_start': course.start,
        'course_end': course.end,
        'has_staff_access': has_staff_access,
        'forum_roles': forum_roles,
        'partition_groups': user_partitions,
        # TODO: clean up as part of REVO-28 (START)
        'has_non_audit_enrollments': has_non_audit_enrollments,
        # TODO: clean up as part of REVO-28 (END)
        # TODO: clean up as part of REVEM-199 (START)
        'program_key_fields': program_key,
        # TODO: clean up as part of REVEM-199 (END)
    }
Example #35
0
 def test_cache_disabled(self, mock_get_edx_api_data):
     self.catalog_integration = self.create_catalog_integration(cache_ttl=0)
     get_programs()
     self.assert_contract(mock_get_edx_api_data.call_args)
Example #36
0
def student_dashboard(request):
    """
    Provides the LMS dashboard view

    TODO: This is lms specific and does not belong in common code.
    Note:
        To load the all courses set course_limit=None as parameter in GET. If its not None then default course
        limit will be used  that is set in configuration
    Arguments:
        request: The request object.

    Returns:
        The dashboard response.

    """
    user = request.user
    if not UserProfile.objects.filter(user=user).exists():
        return redirect(reverse('account_settings'))

    platform_name = configuration_helpers.get_value("platform_name",
                                                    settings.PLATFORM_NAME)

    enable_verified_certificates = configuration_helpers.get_value(
        'ENABLE_VERIFIED_CERTIFICATES',
        settings.FEATURES.get('ENABLE_VERIFIED_CERTIFICATES'))
    display_course_modes_on_dashboard = configuration_helpers.get_value(
        'DISPLAY_COURSE_MODES_ON_DASHBOARD',
        settings.FEATURES.get('DISPLAY_COURSE_MODES_ON_DASHBOARD', True))
    activation_email_support_link = configuration_helpers.get_value(
        'ACTIVATION_EMAIL_SUPPORT_LINK',
        settings.ACTIVATION_EMAIL_SUPPORT_LINK) or settings.SUPPORT_SITE_LINK
    hide_dashboard_courses_until_activated = configuration_helpers.get_value(
        'HIDE_DASHBOARD_COURSES_UNTIL_ACTIVATED',
        settings.FEATURES.get('HIDE_DASHBOARD_COURSES_UNTIL_ACTIVATED', False))
    empty_dashboard_message = configuration_helpers.get_value(
        'EMPTY_DASHBOARD_MESSAGE', None)

    disable_course_limit = request and 'course_limit' in request.GET
    course_limit = get_dashboard_course_limit(
    ) if not disable_course_limit else None

    # Get the org whitelist or the org blacklist for the current site
    site_org_whitelist, site_org_blacklist = get_org_black_and_whitelist_for_site(
    )
    course_enrollments = list(
        get_course_enrollments(user, site_org_whitelist, site_org_blacklist,
                               course_limit))

    # Get the entitlements for the user and a mapping to all available sessions for that entitlement
    # If an entitlement has no available sessions, pass through a mock course overview object
    (course_entitlements, course_entitlement_available_sessions,
     unfulfilled_entitlement_pseudo_sessions
     ) = get_filtered_course_entitlements(user, site_org_whitelist,
                                          site_org_blacklist)

    # Record how many courses there are so that we can get a better
    # understanding of usage patterns on prod.
    monitoring_utils.accumulate('num_courses', len(course_enrollments))

    # Sort the enrollment pairs by the enrollment date
    course_enrollments.sort(key=lambda x: x.created, reverse=True)

    # Retrieve the course modes for each course
    enrolled_course_ids = [
        enrollment.course_id for enrollment in course_enrollments
    ]
    __, unexpired_course_modes = CourseMode.all_and_unexpired_modes_for_courses(
        enrolled_course_ids)
    course_modes_by_course = {
        course_id: {mode.slug: mode
                    for mode in modes}
        for course_id, modes in iteritems(unexpired_course_modes)
    }

    # Check to see if the student has recently enrolled in a course.
    # If so, display a notification message confirming the enrollment.
    enrollment_message = _create_recent_enrollment_message(
        course_enrollments, course_modes_by_course)
    course_optouts = Optout.objects.filter(user=user).values_list('course_id',
                                                                  flat=True)

    # Display activation message
    activate_account_message = ''
    if not user.is_active:
        activate_account_message = Text(
            _("Check your {email_start}{email}{email_end} inbox for an account activation link from {platform_name}. "
              "If you need help, contact {link_start}{platform_name} Support{link_end}."
              )
        ).format(
            platform_name=platform_name,
            email_start=HTML("<strong>"),
            email_end=HTML("</strong>"),
            email=user.email,
            link_start=HTML(
                "<a target='_blank' href='{activation_email_support_link}'>").
            format(
                activation_email_support_link=activation_email_support_link, ),
            link_end=HTML("</a>"),
        )

    enterprise_message = get_dashboard_consent_notification(
        request, user, course_enrollments)

    recovery_email_message = recovery_email_activation_message = None
    if is_secondary_email_feature_enabled_for_user(user=user):
        try:
            pending_email = PendingSecondaryEmailChange.objects.get(user=user)
        except PendingSecondaryEmailChange.DoesNotExist:
            try:
                account_recovery_obj = AccountRecovery.objects.get(user=user)
            except AccountRecovery.DoesNotExist:
                recovery_email_message = Text(
                    _("Add a recovery email to retain access when single-sign on is not available. "
                      "Go to {link_start}your Account Settings{link_end}.")
                ).format(link_start=HTML(
                    "<a href='{account_setting_page}'>").format(
                        account_setting_page=reverse('account_settings'), ),
                         link_end=HTML("</a>"))
        else:
            recovery_email_activation_message = Text(
                _("Recovery email is not activated yet. "
                  "Kindly visit your email and follow the instructions to activate it."
                  ))

# Disable lookup of Enterprise consent_required_course due to ENT-727
# Will re-enable after fixing WL-1315
    consent_required_courses = set()
    enterprise_customer_name = None

    # Account activation message
    account_activation_messages = [
        message for message in messages.get_messages(request)
        if 'account-activation' in message.tags
    ]

    # Global staff can see what courses encountered an error on their dashboard
    staff_access = False
    errored_courses = {}
    if has_access(user, 'staff', 'global'):
        # Show any courses that encountered an error on load
        staff_access = True
        errored_courses = modulestore().get_errored_courses()

    show_courseware_links_for = {
        enrollment.course_id: has_access(request.user, 'load',
                                         enrollment.course_overview)
        for enrollment in course_enrollments
    }

    # Find programs associated with course runs being displayed. This information
    # is passed in the template context to allow rendering of program-related
    # information on the dashboard.
    meter = ProgramProgressMeter(request.site,
                                 user,
                                 enrollments=course_enrollments)
    ecommerce_service = EcommerceService()
    inverted_programs = meter.invert_programs()

    urls, programs_data = {}, {}
    bundles_on_dashboard_flag = WaffleFlag(experiments_namespace,
                                           u'bundles_on_dashboard')

    # TODO: Delete this code and the relevant HTML code after testing LEARNER-3072 is complete
    if bundles_on_dashboard_flag.is_enabled() and inverted_programs and list(
            inverted_programs.items()):
        if len(course_enrollments) < 4:
            for program in inverted_programs.values():
                try:
                    program_uuid = program[0]['uuid']
                    program_data = get_programs(uuid=program_uuid)
                    program_data = ProgramDataExtender(program_data,
                                                       request.user).extend()
                    skus = program_data.get('skus')
                    checkout_page_url = ecommerce_service.get_checkout_page_url(
                        *skus)
                    program_data[
                        'completeProgramURL'] = checkout_page_url + '&bundle=' + program_data.get(
                            'uuid')
                    programs_data[program_uuid] = program_data
                except:  # pylint: disable=bare-except
                    pass

    # Construct a dictionary of course mode information
    # used to render the course list.  We re-use the course modes dict
    # we loaded earlier to avoid hitting the database.
    course_mode_info = {
        enrollment.course_id: complete_course_mode_info(
            enrollment.course_id,
            enrollment,
            modes=course_modes_by_course[enrollment.course_id])
        for enrollment in course_enrollments
    }

    # Determine the per-course verification status
    # This is a dictionary in which the keys are course locators
    # and the values are one of:
    #
    # VERIFY_STATUS_NEED_TO_VERIFY
    # VERIFY_STATUS_SUBMITTED
    # VERIFY_STATUS_APPROVED
    # VERIFY_STATUS_MISSED_DEADLINE
    #
    # Each of which correspond to a particular message to display
    # next to the course on the dashboard.
    #
    # If a course is not included in this dictionary,
    # there is no verification messaging to display.
    verify_status_by_course = check_verify_status_by_course(
        user, course_enrollments)
    cert_statuses = {
        enrollment.course_id: cert_info(request.user,
                                        enrollment.course_overview)
        for enrollment in course_enrollments
    }

    # only show email settings for Mongo course and when bulk email is turned on
    show_email_settings_for = frozenset(
        enrollment.course_id for enrollment in course_enrollments
        if (is_bulk_email_feature_enabled(enrollment.course_id)))

    # Verification Attempts
    # Used to generate the "you must reverify for course x" banner
    verification_status = IDVerificationService.user_status(user)
    verification_errors = get_verification_error_reasons_for_display(
        verification_status['error'])

    # Gets data for midcourse reverifications, if any are necessary or have failed
    statuses = ["approved", "denied", "pending", "must_reverify"]
    reverifications = reverification_info(statuses)

    block_courses = frozenset(
        enrollment.course_id for enrollment in course_enrollments
        if is_course_blocked(
            request,
            CourseRegistrationCode.objects.filter(
                course_id=enrollment.course_id,
                registrationcoderedemption__redeemed_by=request.user),
            enrollment.course_id))

    enrolled_courses_either_paid = frozenset(
        enrollment.course_id for enrollment in course_enrollments
        if enrollment.is_paid_course())

    # If there are *any* denied reverifications that have not been toggled off,
    # we'll display the banner
    denied_banner = any(item.display for item in reverifications["denied"])

    # get list of courses having pre-requisites yet to be completed
    courses_having_prerequisites = frozenset(
        enrollment.course_id for enrollment in course_enrollments
        if enrollment.course_overview.pre_requisite_courses)
    courses_requirements_not_met = get_pre_requisite_courses_not_completed(
        user, courses_having_prerequisites)

    if 'notlive' in request.GET:
        redirect_message = _(
            "The course you are looking for does not start until {date}."
        ).format(date=request.GET['notlive'])
    elif 'course_closed' in request.GET:
        redirect_message = _(
            "The course you are looking for is closed for enrollment as of {date}."
        ).format(date=request.GET['course_closed'])
    elif 'access_response_error' in request.GET:
        # This can be populated in a generalized way with fields from access response errors
        redirect_message = request.GET['access_response_error']
    else:
        redirect_message = ''

    valid_verification_statuses = [
        'approved', 'must_reverify', 'pending', 'expired'
    ]
    display_sidebar_on_dashboard = verification_status['status'] in valid_verification_statuses and \
        verification_status['should_display']

    # Filter out any course enrollment course cards that are associated with fulfilled entitlements
    for entitlement in [
            e for e in course_entitlements
            if e.enrollment_course_run is not None
    ]:
        course_enrollments = [
            enr for enr in course_enrollments
            if entitlement.enrollment_course_run.course_id != enr.course_id
        ]

    context = {
        'urls':
        urls,
        'programs_data':
        programs_data,
        'enterprise_message':
        enterprise_message,
        'consent_required_courses':
        consent_required_courses,
        'enterprise_customer_name':
        enterprise_customer_name,
        'enrollment_message':
        enrollment_message,
        'redirect_message':
        Text(redirect_message),
        'account_activation_messages':
        account_activation_messages,
        'activate_account_message':
        activate_account_message,
        'course_enrollments':
        course_enrollments,
        'course_entitlements':
        course_entitlements,
        'course_entitlement_available_sessions':
        course_entitlement_available_sessions,
        'unfulfilled_entitlement_pseudo_sessions':
        unfulfilled_entitlement_pseudo_sessions,
        'course_optouts':
        course_optouts,
        'staff_access':
        staff_access,
        'errored_courses':
        errored_courses,
        'show_courseware_links_for':
        show_courseware_links_for,
        'all_course_modes':
        course_mode_info,
        'cert_statuses':
        cert_statuses,
        'credit_statuses':
        _credit_statuses(user, course_enrollments),
        'show_email_settings_for':
        show_email_settings_for,
        'reverifications':
        reverifications,
        'verification_display':
        verification_status['should_display'],
        'verification_status':
        verification_status['status'],
        'verification_expiry':
        verification_status['verification_expiry'],
        'verification_status_by_course':
        verify_status_by_course,
        'verification_errors':
        verification_errors,
        'block_courses':
        block_courses,
        'denied_banner':
        denied_banner,
        'billing_email':
        settings.PAYMENT_SUPPORT_EMAIL,
        'user':
        user,
        'logout_url':
        reverse('logout'),
        'platform_name':
        platform_name,
        'enrolled_courses_either_paid':
        enrolled_courses_either_paid,
        'provider_states': [],
        'courses_requirements_not_met':
        courses_requirements_not_met,
        'nav_hidden':
        True,
        'inverted_programs':
        inverted_programs,
        'show_program_listing':
        ProgramsApiConfig.is_enabled(),
        'show_dashboard_tabs':
        True,
        'disable_courseware_js':
        True,
        'display_course_modes_on_dashboard':
        enable_verified_certificates and display_course_modes_on_dashboard,
        'display_sidebar_on_dashboard':
        display_sidebar_on_dashboard,
        'display_sidebar_account_activation_message':
        not (user.is_active or hide_dashboard_courses_until_activated),
        'display_dashboard_courses':
        (user.is_active or not hide_dashboard_courses_until_activated),
        'empty_dashboard_message':
        empty_dashboard_message,
        'recovery_email_message':
        recovery_email_message,
        'recovery_email_activation_message':
        recovery_email_activation_message,
        'show_load_all_courses_link':
        show_load_all_courses_link(user, course_limit, course_enrollments),
        # TODO START: clean up as part of REVEM-199 (START)
        'course_info':
        get_dashboard_course_info(user, course_enrollments),
        # TODO START: clean up as part of REVEM-199 (END)
    }

    context_from_plugins = get_plugins_view_context(
        plugin_constants.ProjectType.LMS, COURSE_DASHBOARD_PLUGIN_VIEW_NAME,
        context)
    context.update(context_from_plugins)

    if ecommerce_service.is_enabled(request.user):
        context.update({
            'use_ecommerce_payment_flow':
            True,
            'ecommerce_payment_page':
            ecommerce_service.payment_page_url(),
        })

    # Gather urls for course card resume buttons.
    resume_button_urls = ['' for entitlement in course_entitlements]
    for url in get_resume_urls_for_enrollments(user,
                                               course_enrollments).values():
        resume_button_urls.append(url)
    # There must be enough urls for dashboard.html. Template creates course
    # cards for "enrollments + entitlements".
    context.update({'resume_button_urls': resume_button_urls})

    return render_to_response('dashboard.html', context)
Example #37
0
def get_deprecated_experiment_user_metadata_context(course, user):
    """
    Return a context dictionary with the keys used by the user_metadata.html. This is deprecated and will be removed
    once we have confirmed that its replacement functions as intended.
    """
    enrollment_mode = None
    enrollment_time = None
    enrollment = None
    # TODO: clean up as part of REVO-28 (START)
    has_non_audit_enrollments = None
    # TODO: clean up as part of REVO-28 (END)
    # TODO: clean up as part of REVEM-199 (START)
    program_key = None
    # TODO: clean up as part of REVEM-199 (END)
    try:
        # TODO: clean up as part of REVO-28 (START)
        user_enrollments = CourseEnrollment.objects.select_related('course').filter(user_id=user.id)
        audit_enrollments = user_enrollments.filter(mode='audit')
        has_non_audit_enrollments = (len(audit_enrollments) != len(user_enrollments))
        # TODO: clean up as part of REVO-28 (END)
        # TODO: clean up as part of REVEM-199 (START)
        if PROGRAM_INFO_FLAG.is_enabled():
            programs = get_programs(course=course.id)
            if programs:
                # A course can be in multiple programs, but we're just grabbing the first one
                program = programs[0]
                complete_enrollment = False
                has_courses_left_to_purchase = False
                total_courses = None
                courses = program.get('courses')
                courses_left_to_purchase_price = None
                courses_left_to_purchase_url = None
                program_uuid = program.get('uuid')
                status = None
                is_eligible_for_one_click_purchase = None
                if courses is not None:
                    total_courses = len(courses)
                    complete_enrollment = is_enrolled_in_all_courses(courses, user_enrollments)
                    status = program.get('status')
                    is_eligible_for_one_click_purchase = program.get('is_program_eligible_for_one_click_purchase')
                    # Get the price and purchase URL of the program courses the user has yet to purchase. Say a
                    # program has 3 courses (A, B and C), and the user previously purchased a certificate for A.
                    # The user is enrolled in audit mode for B. The "left to purchase price" should be the price of
                    # B+C.
                    non_audit_enrollments = [en for en in user_enrollments if en not in
                                             audit_enrollments]
                    courses_left_to_purchase = get_unenrolled_courses(courses, non_audit_enrollments)
                    if courses_left_to_purchase:
                        has_courses_left_to_purchase = True
                        if is_eligible_for_one_click_purchase:
                            courses_left_to_purchase_price, courses_left_to_purchase_skus = \
                                get_program_price_and_skus(courses_left_to_purchase)
                            if courses_left_to_purchase_skus:
                                courses_left_to_purchase_url = EcommerceService().get_checkout_page_url(
                                    *courses_left_to_purchase_skus, program_uuid=program_uuid)

                program_key = {
                    'uuid': program_uuid,
                    'title': program.get('title'),
                    'marketing_url': program.get('marketing_url'),
                    'status': status,
                    'is_eligible_for_one_click_purchase': is_eligible_for_one_click_purchase,
                    'total_courses': total_courses,
                    'complete_enrollment': complete_enrollment,
                    'has_courses_left_to_purchase': has_courses_left_to_purchase,
                    'courses_left_to_purchase_price': courses_left_to_purchase_price,
                    'courses_left_to_purchase_url': courses_left_to_purchase_url,
                }
        # TODO: clean up as part of REVEM-199 (END)
        enrollment = CourseEnrollment.objects.select_related(
            'course'
        ).get(user_id=user.id, course_id=course.id)
        if enrollment.is_active:
            enrollment_mode = enrollment.mode
            enrollment_time = enrollment.created
    except CourseEnrollment.DoesNotExist:
        pass  # Not enrolled, used the default None values

    # upgrade_link and upgrade_date should be None if user has passed their dynamic pacing deadline.
    upgrade_link, upgrade_date = check_and_get_upgrade_link_and_date(user, enrollment, course)
    has_staff_access = has_staff_access_to_preview_mode(user, course.id)
    forum_roles = []
    if user.is_authenticated:
        forum_roles = list(Role.objects.filter(users=user, course_id=course.id).values_list('name').distinct())

    # get user partition data
    if user.is_authenticated():
        partition_groups = get_all_partitions_for_course(course)
        user_partitions = get_user_partition_groups(course.id, partition_groups, user, 'name')
    else:
        user_partitions = {}

    return {
        'upgrade_link': upgrade_link,
        'upgrade_price': six.text_type(get_cosmetic_verified_display_price(course)),
        'enrollment_mode': enrollment_mode,
        'enrollment_time': enrollment_time,
        'pacing_type': 'self_paced' if course.self_paced else 'instructor_paced',
        'upgrade_deadline': upgrade_date,
        'audit_access_deadline': get_audit_access_expiration(user, course),
        'course_key': course.id,
        'course_start': course.start,
        'course_end': course.end,
        'has_staff_access': has_staff_access,
        'forum_roles': forum_roles,
        'partition_groups': user_partitions,
        # TODO: clean up as part of REVO-28 (START)
        'has_non_audit_enrollments': has_non_audit_enrollments,
        # TODO: clean up as part of REVO-28 (END)
        # TODO: clean up as part of REVEM-199 (START)
        'program_key_fields': program_key,
        # TODO: clean up as part of REVEM-199 (END)
    }
Example #38
0
def student_dashboard(request):
    """
    Provides the LMS dashboard view

    TODO: This is lms specific and does not belong in common code.

    Arguments:
        request: The request object.

    Returns:
        The dashboard response.

    """
    user = request.user
    if not UserProfile.objects.filter(user=user).exists():
        return redirect(reverse('account_settings'))

    platform_name = configuration_helpers.get_value("platform_name", settings.PLATFORM_NAME)

    enable_verified_certificates = configuration_helpers.get_value(
        'ENABLE_VERIFIED_CERTIFICATES',
        settings.FEATURES.get('ENABLE_VERIFIED_CERTIFICATES')
    )
    display_course_modes_on_dashboard = configuration_helpers.get_value(
        'DISPLAY_COURSE_MODES_ON_DASHBOARD',
        settings.FEATURES.get('DISPLAY_COURSE_MODES_ON_DASHBOARD', True)
    )
    activation_email_support_link = configuration_helpers.get_value(
        'ACTIVATION_EMAIL_SUPPORT_LINK', settings.ACTIVATION_EMAIL_SUPPORT_LINK
    ) or settings.SUPPORT_SITE_LINK

    # Get the org whitelist or the org blacklist for the current site
    site_org_whitelist, site_org_blacklist = get_org_black_and_whitelist_for_site()
    course_enrollments = list(get_course_enrollments(user, site_org_whitelist, site_org_blacklist))

    # Get the entitlements for the user and a mapping to all available sessions for that entitlement
    # If an entitlement has no available sessions, pass through a mock course overview object
    (course_entitlements,
     course_entitlement_available_sessions,
     unfulfilled_entitlement_pseudo_sessions) = get_filtered_course_entitlements(
        user,
        site_org_whitelist,
        site_org_blacklist
    )

    # Record how many courses there are so that we can get a better
    # understanding of usage patterns on prod.
    monitoring_utils.accumulate('num_courses', len(course_enrollments))

    # Sort the enrollment pairs by the enrollment date
    course_enrollments.sort(key=lambda x: x.created, reverse=True)

    # Retrieve the course modes for each course
    enrolled_course_ids = [enrollment.course_id for enrollment in course_enrollments]
    __, unexpired_course_modes = CourseMode.all_and_unexpired_modes_for_courses(enrolled_course_ids)
    course_modes_by_course = {
        course_id: {
            mode.slug: mode
            for mode in modes
        }
        for course_id, modes in iteritems(unexpired_course_modes)
    }

    courses_count = len(course_enrollments)
    # Check to see if the student has recently enrolled in a course.
    # If so, display a notification message confirming the enrollment.
    enrollment_message = _create_recent_enrollment_message(
        course_enrollments, course_modes_by_course
    )


    course_optouts = Optout.objects.filter(user=user).values_list('course_id', flat=True)

    sidebar_account_activation_message = ''
    banner_account_activation_message = ''
    display_account_activation_message_on_sidebar = configuration_helpers.get_value(
        'DISPLAY_ACCOUNT_ACTIVATION_MESSAGE_ON_SIDEBAR',
        settings.FEATURES.get('DISPLAY_ACCOUNT_ACTIVATION_MESSAGE_ON_SIDEBAR', False)
    )

    # Display activation message in sidebar if DISPLAY_ACCOUNT_ACTIVATION_MESSAGE_ON_SIDEBAR
    # flag is active. Otherwise display existing message at the top.
    if display_account_activation_message_on_sidebar and not user.is_active:
        sidebar_account_activation_message = render_to_string(
            'registration/account_activation_sidebar_notice.html',
            {
                'email': user.email,
                'platform_name': platform_name,
                'activation_email_support_link': activation_email_support_link
            }
        )
    elif not user.is_active:
        banner_account_activation_message = render_to_string(
            'registration/activate_account_notice.html',
            {'email': user.email}
        )

    enterprise_message = get_dashboard_consent_notification(request, user, course_enrollments)

    # Disable lookup of Enterprise consent_required_course due to ENT-727
    # Will re-enable after fixing WL-1315
    consent_required_courses = set()
    enterprise_customer_name = None

    # Account activation message
    account_activation_messages = [
        message for message in messages.get_messages(request) if 'account-activation' in message.tags
    ]

    # Global staff can see what courses encountered an error on their dashboard
    staff_access = False
    errored_courses = {}
    if has_access(user, 'staff', 'global'):
        # Show any courses that encountered an error on load
        staff_access = True
        errored_courses = modulestore().get_errored_courses()

    show_courseware_links_for = frozenset(
        enrollment.course_id for enrollment in course_enrollments
        if has_access(request.user, 'load', enrollment.course_overview)
    )

    # Find programs associated with course runs being displayed. This information
    # is passed in the template context to allow rendering of program-related
    # information on the dashboard.
    meter = ProgramProgressMeter(request.site, user, enrollments=course_enrollments)
    ecommerce_service = EcommerceService()
    inverted_programs = meter.invert_programs()

    urls, programs_data = {}, {}
    bundles_on_dashboard_flag = WaffleFlag(WaffleFlagNamespace(name=u'student.experiments'), u'bundles_on_dashboard')

    # TODO: Delete this code and the relevant HTML code after testing LEARNER-3072 is complete
    if bundles_on_dashboard_flag.is_enabled() and inverted_programs and inverted_programs.items():
        if len(course_enrollments) < 4:
            for program in inverted_programs.values():
                try:
                    program_uuid = program[0]['uuid']
                    program_data = get_programs(request.site, uuid=program_uuid)
                    program_data = ProgramDataExtender(program_data, request.user).extend()
                    skus = program_data.get('skus')
                    checkout_page_url = ecommerce_service.get_checkout_page_url(*skus)
                    program_data['completeProgramURL'] = checkout_page_url + '&bundle=' + program_data.get('uuid')
                    programs_data[program_uuid] = program_data
                except:  # pylint: disable=bare-except
                    pass

    # Construct a dictionary of course mode information
    # used to render the course list.  We re-use the course modes dict
    # we loaded earlier to avoid hitting the database.
    course_mode_info = {
        enrollment.course_id: complete_course_mode_info(
            enrollment.course_id, enrollment,
            modes=course_modes_by_course[enrollment.course_id]
        )
        for enrollment in course_enrollments
    }

    # Determine the per-course verification status
    # This is a dictionary in which the keys are course locators
    # and the values are one of:
    #
    # VERIFY_STATUS_NEED_TO_VERIFY
    # VERIFY_STATUS_SUBMITTED
    # VERIFY_STATUS_APPROVED
    # VERIFY_STATUS_MISSED_DEADLINE
    #
    # Each of which correspond to a particular message to display
    # next to the course on the dashboard.
    #
    # If a course is not included in this dictionary,
    # there is no verification messaging to display.
    verify_status_by_course = check_verify_status_by_course(user, course_enrollments)
    cert_statuses = {
        enrollment.course_id: cert_info(request.user, enrollment.course_overview)
        for enrollment in course_enrollments
    }

    # only show email settings for Mongo course and when bulk email is turned on
    show_email_settings_for = frozenset(
        enrollment.course_id for enrollment in course_enrollments if (
            BulkEmailFlag.feature_enabled(enrollment.course_id)
        )
    )

    # Verification Attempts
    # Used to generate the "you must reverify for course x" banner
    verification_status = IDVerificationService.user_status(user)
    verification_errors = get_verification_error_reasons_for_display(verification_status['error'])

    # Gets data for midcourse reverifications, if any are necessary or have failed
    statuses = ["approved", "denied", "pending", "must_reverify"]
    reverifications = reverification_info(statuses)

    block_courses = frozenset(
        enrollment.course_id for enrollment in course_enrollments
        if is_course_blocked(
            request,
            CourseRegistrationCode.objects.filter(
                course_id=enrollment.course_id,
                registrationcoderedemption__redeemed_by=request.user
            ),
            enrollment.course_id
        )
    )

    enrolled_courses_either_paid = frozenset(
        enrollment.course_id for enrollment in course_enrollments
        if enrollment.is_paid_course()
    )

    # If there are *any* denied reverifications that have not been toggled off,
    # we'll display the banner
    denied_banner = any(item.display for item in reverifications["denied"])

    # Populate the Order History for the side-bar.
    order_history_list = order_history(
        user,
        course_org_filter=site_org_whitelist,
        org_filter_out_set=site_org_blacklist
    )

    # get list of courses having pre-requisites yet to be completed
    courses_having_prerequisites = frozenset(
        enrollment.course_id for enrollment in course_enrollments
        if enrollment.course_overview.pre_requisite_courses
    )
    courses_requirements_not_met = get_pre_requisite_courses_not_completed(user, courses_having_prerequisites)

    if 'notlive' in request.GET:
        redirect_message = _("The course you are looking for does not start until {date}.").format(
            date=request.GET['notlive']
        )
    elif 'course_closed' in request.GET:
        redirect_message = _("The course you are looking for is closed for enrollment as of {date}.").format(
            date=request.GET['course_closed']
        )
    else:
        redirect_message = ''

    valid_verification_statuses = ['approved', 'must_reverify', 'pending', 'expired']
    display_sidebar_on_dashboard = (len(order_history_list) or
                                    (verification_status['status'] in valid_verification_statuses and
                                    verification_status['should_display']))

    # Filter out any course enrollment course cards that are associated with fulfilled entitlements
    for entitlement in [e for e in course_entitlements if e.enrollment_course_run is not None]:
        course_enrollments = [
            enr for enr in course_enrollments if entitlement.enrollment_course_run.course_id != enr.course_id
        ]

    context = {
        'urls': urls,
        'programs_data': programs_data,
        'enterprise_message': enterprise_message,
        'consent_required_courses': consent_required_courses,
        'enterprise_customer_name': enterprise_customer_name,
        'prev_courses_count': prev_courses_count,
        'courses_count': courses_count,
        'prev_time': prev_time,
        'time': time,
        'enrollment_message': enrollment_message,
        'redirect_message': redirect_message,
        'account_activation_messages': account_activation_messages,
        'course_enrollments': course_enrollments,
        'course_entitlements': course_entitlements,
        'course_entitlement_available_sessions': course_entitlement_available_sessions,
        'unfulfilled_entitlement_pseudo_sessions': unfulfilled_entitlement_pseudo_sessions,
        'course_optouts': course_optouts,
        'banner_account_activation_message': banner_account_activation_message,
        'sidebar_account_activation_message': sidebar_account_activation_message,
        'staff_access': staff_access,
        'errored_courses': errored_courses,
        'show_courseware_links_for': show_courseware_links_for,
        'all_course_modes': course_mode_info,
        'cert_statuses': cert_statuses,
        'credit_statuses': _credit_statuses(user, course_enrollments),
        'show_email_settings_for': show_email_settings_for,
        'reverifications': reverifications,
        'verification_display': verification_status['should_display'],
        'verification_status': verification_status['status'],
        'verification_status_by_course': verify_status_by_course,
        'verification_errors': verification_errors,
        'block_courses': block_courses,
        'denied_banner': denied_banner,
        'billing_email': settings.PAYMENT_SUPPORT_EMAIL,
        'user': user,
        'logout_url': reverse('logout'),
        'platform_name': platform_name,
        'enrolled_courses_either_paid': enrolled_courses_either_paid,
        'provider_states': [],
        'order_history_list': order_history_list,
        'courses_requirements_not_met': courses_requirements_not_met,
        'nav_hidden': True,
        'inverted_programs': inverted_programs,
        'show_program_listing': ProgramsApiConfig.is_enabled(),
        'show_dashboard_tabs': True,
        'disable_courseware_js': True,
        'display_course_modes_on_dashboard': enable_verified_certificates and display_course_modes_on_dashboard,
        'display_sidebar_on_dashboard': display_sidebar_on_dashboard,
    }

    if ecommerce_service.is_enabled(request.user):
        context.update({
            'use_ecommerce_payment_flow': True,
            'ecommerce_payment_page': ecommerce_service.payment_page_url(),
        })

    # Gather urls for course card resume buttons.
    resume_button_urls = _get_urls_for_resume_buttons(user, course_enrollments)
    # There must be enough urls for dashboard.html. Template creates course
    # cards for "enrollments + entitlements".
    resume_button_urls += ['' for entitlement in course_entitlements]

    context.update({
        'resume_button_urls': resume_button_urls,
        'time': pytz.utc.localize(datetime.datetime.now()),
    })

    response = render_to_response('dashboard.html', context)
    set_user_info_cookie(response, request)
    return response
Example #39
0
def get_experiment_user_metadata_context(course, user):
    """
    Return a context dictionary with the keys used by the user_metadata.html.
    """
    # TODO: call get_base_experiment_metadata_context(), and then add in the bits that are only needed by user_metadata
    enrollment_mode = None
    enrollment_time = None
    enrollment = None
    # TODO: clean up as part of REVO-28 (START)
    has_non_audit_enrollments = None
    # TODO: clean up as part of REVO-28 (END)
    # TODO: clean up as part of REVEM-199 (START)
    program_key = None
    # TODO: clean up as part of REVEM-199 (END)
    try:
        # TODO: clean up as part of REVO-28 (START)
        user_enrollments = CourseEnrollment.objects.select_related('course').filter(user_id=user.id)
        audit_enrollments = user_enrollments.filter(mode='audit')
        has_non_audit_enrollments = (len(audit_enrollments) != len(user_enrollments))
        # TODO: clean up as part of REVO-28 (END)
        # TODO: clean up as part of REVEM-199 (START)
        if PROGRAM_INFO_FLAG.is_enabled():
            programs = get_programs(course=course.id)
            if programs:
                # A course can be in multiple programs, but we're just grabbing the first one
                program = programs[0]
                complete_enrollment = False
                has_courses_left_to_purchase = False
                total_courses = None
                courses = program.get('courses')
                courses_left_to_purchase_price = None
                courses_left_to_purchase_url = None
                program_uuid = program.get('uuid')
                status = None
                is_eligible_for_one_click_purchase = None
                if courses is not None:
                    total_courses = len(courses)
                    complete_enrollment = is_enrolled_in_all_courses(courses, user_enrollments)
                    status = program.get('status')
                    is_eligible_for_one_click_purchase = program.get('is_program_eligible_for_one_click_purchase')
                    # Get the price and purchase URL of the program courses the user has yet to purchase. Say a
                    # program has 3 courses (A, B and C), and the user previously purchased a certificate for A.
                    # The user is enrolled in audit mode for B. The "left to purchase price" should be the price of
                    # B+C.
                    non_audit_enrollments = [en for en in user_enrollments if en not in
                                             audit_enrollments]
                    courses_left_to_purchase = get_unenrolled_courses(courses, non_audit_enrollments)
                    if courses_left_to_purchase:
                        has_courses_left_to_purchase = True
                        if is_eligible_for_one_click_purchase:
                            courses_left_to_purchase_price, courses_left_to_purchase_skus = \
                                get_program_price_and_skus(courses_left_to_purchase)
                            if courses_left_to_purchase_skus:
                                courses_left_to_purchase_url = EcommerceService().get_checkout_page_url(
                                    *courses_left_to_purchase_skus, program_uuid=program_uuid)

                program_key = {
                    'uuid': program_uuid,
                    'title': program.get('title'),
                    'marketing_url': program.get('marketing_url'),
                    'status': status,
                    'is_eligible_for_one_click_purchase': is_eligible_for_one_click_purchase,
                    'total_courses': total_courses,
                    'complete_enrollment': complete_enrollment,
                    'has_courses_left_to_purchase': has_courses_left_to_purchase,
                    'courses_left_to_purchase_price': courses_left_to_purchase_price,
                    'courses_left_to_purchase_url': courses_left_to_purchase_url,
                }
        # TODO: clean up as part of REVEM-199 (END)
        enrollment = CourseEnrollment.objects.select_related(
            'course'
        ).get(user_id=user.id, course_id=course.id)
        if enrollment.is_active:
            enrollment_mode = enrollment.mode
            enrollment_time = enrollment.created
    except CourseEnrollment.DoesNotExist:
        pass  # Not enrolled, used the default None values

    # upgrade_link and upgrade_date should be None if user has passed their dynamic pacing deadline.
    upgrade_link, upgrade_date = check_and_get_upgrade_link_and_date(user, enrollment, course)
    has_staff_access = has_staff_access_to_preview_mode(user, course.id)
    forum_roles = []
    if user.is_authenticated:
        forum_roles = list(Role.objects.filter(users=user, course_id=course.id).values_list('name').distinct())

    # get user partition data
    if user.is_authenticated():
        partition_groups = get_all_partitions_for_course(course)
        user_partitions = get_user_partition_groups(course.id, partition_groups, user, 'name')
    else:
        user_partitions = {}

    return {
        'upgrade_link': upgrade_link,
        'upgrade_price': unicode(get_cosmetic_verified_display_price(course)),
        'enrollment_mode': enrollment_mode,
        'enrollment_time': enrollment_time,
        'pacing_type': 'self_paced' if course.self_paced else 'instructor_paced',
        'upgrade_deadline': upgrade_date,
        'course_key': course.id,
        'course_start': course.start,
        'course_end': course.end,
        'has_staff_access': has_staff_access,
        'forum_roles': forum_roles,
        'partition_groups': user_partitions,
        # TODO: clean up as part of REVO-28 (START)
        'has_non_audit_enrollments': has_non_audit_enrollments,
        # TODO: clean up as part of REVO-28 (END)
        # TODO: clean up as part of REVEM-199 (START)
        'program_key_fields': program_key,
        # TODO: clean up as part of REVEM-199 (END)
    }
Example #40
0
def student_dashboard(request):
    """
    Provides the LMS dashboard view

    TODO: This is lms specific and does not belong in common code.

    Arguments:
        request: The request object.

    Returns:
        The dashboard response.

    """
    user = request.user
    if not UserProfile.objects.filter(user=user).exists():
        return redirect(reverse('account_settings'))

    platform_name = configuration_helpers.get_value("platform_name", settings.PLATFORM_NAME)

    enable_verified_certificates = configuration_helpers.get_value(
        'ENABLE_VERIFIED_CERTIFICATES',
        settings.FEATURES.get('ENABLE_VERIFIED_CERTIFICATES')
    )
    display_course_modes_on_dashboard = configuration_helpers.get_value(
        'DISPLAY_COURSE_MODES_ON_DASHBOARD',
        settings.FEATURES.get('DISPLAY_COURSE_MODES_ON_DASHBOARD', True)
    )
    activation_email_support_link = configuration_helpers.get_value(
        'ACTIVATION_EMAIL_SUPPORT_LINK', settings.ACTIVATION_EMAIL_SUPPORT_LINK
    ) or settings.SUPPORT_SITE_LINK

    # Get the org whitelist or the org blacklist for the current site
    site_org_whitelist, site_org_blacklist = get_org_black_and_whitelist_for_site()
    course_enrollments = list(get_course_enrollments(user, site_org_whitelist, site_org_blacklist))

    # Get the entitlements for the user and a mapping to all available sessions for that entitlement
    # If an entitlement has no available sessions, pass through a mock course overview object
    (course_entitlements,
     course_entitlement_available_sessions,
     unfulfilled_entitlement_pseudo_sessions) = get_filtered_course_entitlements(
        user,
        site_org_whitelist,
        site_org_blacklist
    )

    # Record how many courses there are so that we can get a better
    # understanding of usage patterns on prod.
    monitoring_utils.accumulate('num_courses', len(course_enrollments))

    # Sort the enrollment pairs by the enrollment date
    course_enrollments.sort(key=lambda x: x.created, reverse=True)

    # Retrieve the course modes for each course
    enrolled_course_ids = [enrollment.course_id for enrollment in course_enrollments]
    __, unexpired_course_modes = CourseMode.all_and_unexpired_modes_for_courses(enrolled_course_ids)
    course_modes_by_course = {
        course_id: {
            mode.slug: mode
            for mode in modes
        }
        for course_id, modes in iteritems(unexpired_course_modes)
    }

    # Check to see if the student has recently enrolled in a course.
    # If so, display a notification message confirming the enrollment.
    enrollment_message = _create_recent_enrollment_message(
        course_enrollments, course_modes_by_course
    )
    course_optouts = Optout.objects.filter(user=user).values_list('course_id', flat=True)

    sidebar_account_activation_message = ''
    banner_account_activation_message = ''
    display_account_activation_message_on_sidebar = configuration_helpers.get_value(
        'DISPLAY_ACCOUNT_ACTIVATION_MESSAGE_ON_SIDEBAR',
        settings.FEATURES.get('DISPLAY_ACCOUNT_ACTIVATION_MESSAGE_ON_SIDEBAR', False)
    )

    # Display activation message in sidebar if DISPLAY_ACCOUNT_ACTIVATION_MESSAGE_ON_SIDEBAR
    # flag is active. Otherwise display existing message at the top.
    if display_account_activation_message_on_sidebar and not user.is_active:
        sidebar_account_activation_message = render_to_string(
            'registration/account_activation_sidebar_notice.html',
            {
                'email': user.email,
                'platform_name': platform_name,
                'activation_email_support_link': activation_email_support_link
            }
        )
    elif not user.is_active:
        banner_account_activation_message = render_to_string(
            'registration/activate_account_notice.html',
            {'email': user.email}
        )

    enterprise_message = get_dashboard_consent_notification(request, user, course_enrollments)

    # Disable lookup of Enterprise consent_required_course due to ENT-727
    # Will re-enable after fixing WL-1315
    consent_required_courses = set()
    enterprise_customer_name = None

    # Account activation message
    account_activation_messages = [
        message for message in messages.get_messages(request) if 'account-activation' in message.tags
    ]

    # Global staff can see what courses encountered an error on their dashboard
    staff_access = False
    errored_courses = {}
    if has_access(user, 'staff', 'global'):
        # Show any courses that encountered an error on load
        staff_access = True
        errored_courses = modulestore().get_errored_courses()

    show_courseware_links_for = frozenset(
        enrollment.course_id for enrollment in course_enrollments
        if has_access(request.user, 'load', enrollment.course_overview)
    )

    # Find programs associated with course runs being displayed. This information
    # is passed in the template context to allow rendering of program-related
    # information on the dashboard.
    meter = ProgramProgressMeter(request.site, user, enrollments=course_enrollments)
    ecommerce_service = EcommerceService()
    inverted_programs = meter.invert_programs()

    urls, programs_data = {}, {}
    bundles_on_dashboard_flag = WaffleFlag(WaffleFlagNamespace(name=u'student.experiments'), u'bundles_on_dashboard')

    # TODO: Delete this code and the relevant HTML code after testing LEARNER-3072 is complete
    if bundles_on_dashboard_flag.is_enabled() and inverted_programs and inverted_programs.items():
        if len(course_enrollments) < 4:
            for program in inverted_programs.values():
                try:
                    program_uuid = program[0]['uuid']
                    program_data = get_programs(request.site, uuid=program_uuid)
                    program_data = ProgramDataExtender(program_data, request.user).extend()
                    skus = program_data.get('skus')
                    checkout_page_url = ecommerce_service.get_checkout_page_url(*skus)
                    program_data['completeProgramURL'] = checkout_page_url + '&bundle=' + program_data.get('uuid')
                    programs_data[program_uuid] = program_data
                except:  # pylint: disable=bare-except
                    pass

    # Construct a dictionary of course mode information
    # used to render the course list.  We re-use the course modes dict
    # we loaded earlier to avoid hitting the database.
    course_mode_info = {
        enrollment.course_id: complete_course_mode_info(
            enrollment.course_id, enrollment,
            modes=course_modes_by_course[enrollment.course_id]
        )
        for enrollment in course_enrollments
    }

    # Determine the per-course verification status
    # This is a dictionary in which the keys are course locators
    # and the values are one of:
    #
    # VERIFY_STATUS_NEED_TO_VERIFY
    # VERIFY_STATUS_SUBMITTED
    # VERIFY_STATUS_APPROVED
    # VERIFY_STATUS_MISSED_DEADLINE
    #
    # Each of which correspond to a particular message to display
    # next to the course on the dashboard.
    #
    # If a course is not included in this dictionary,
    # there is no verification messaging to display.
    verify_status_by_course = check_verify_status_by_course(user, course_enrollments)
    cert_statuses = {
        enrollment.course_id: cert_info(request.user, enrollment.course_overview)
        for enrollment in course_enrollments
    }

    # only show email settings for Mongo course and when bulk email is turned on
    show_email_settings_for = frozenset(
        enrollment.course_id for enrollment in course_enrollments if (
            BulkEmailFlag.feature_enabled(enrollment.course_id)
        )
    )

    # Verification Attempts
    # Used to generate the "you must reverify for course x" banner
    verification_status = IDVerificationService.user_status(user)
    verification_errors = get_verification_error_reasons_for_display(verification_status['error'])

    # Gets data for midcourse reverifications, if any are necessary or have failed
    statuses = ["approved", "denied", "pending", "must_reverify"]
    reverifications = reverification_info(statuses)

    block_courses = frozenset(
        enrollment.course_id for enrollment in course_enrollments
        if is_course_blocked(
            request,
            CourseRegistrationCode.objects.filter(
                course_id=enrollment.course_id,
                registrationcoderedemption__redeemed_by=request.user
            ),
            enrollment.course_id
        )
    )

    enrolled_courses_either_paid = frozenset(
        enrollment.course_id for enrollment in course_enrollments
        if enrollment.is_paid_course()
    )

    # If there are *any* denied reverifications that have not been toggled off,
    # we'll display the banner
    denied_banner = any(item.display for item in reverifications["denied"])

    # Populate the Order History for the side-bar.
    order_history_list = order_history(
        user,
        course_org_filter=site_org_whitelist,
        org_filter_out_set=site_org_blacklist
    )

    # get list of courses having pre-requisites yet to be completed
    courses_having_prerequisites = frozenset(
        enrollment.course_id for enrollment in course_enrollments
        if enrollment.course_overview.pre_requisite_courses
    )
    courses_requirements_not_met = get_pre_requisite_courses_not_completed(user, courses_having_prerequisites)

    if 'notlive' in request.GET:
        redirect_message = _("The course you are looking for does not start until {date}.").format(
            date=request.GET['notlive']
        )
    elif 'course_closed' in request.GET:
        redirect_message = _("The course you are looking for is closed for enrollment as of {date}.").format(
            date=request.GET['course_closed']
        )
    else:
        redirect_message = ''

    valid_verification_statuses = ['approved', 'must_reverify', 'pending', 'expired']
    display_sidebar_on_dashboard = (len(order_history_list) or
                                    (verification_status['status'] in valid_verification_statuses and
                                    verification_status['should_display']))

    # Filter out any course enrollment course cards that are associated with fulfilled entitlements
    for entitlement in [e for e in course_entitlements if e.enrollment_course_run is not None]:
        course_enrollments = [
            enr for enr in course_enrollments if entitlement.enrollment_course_run.course_id != enr.course_id
        ]

    context = {
        'urls': urls,
        'programs_data': programs_data,
        'enterprise_message': enterprise_message,
        'consent_required_courses': consent_required_courses,
        'enterprise_customer_name': enterprise_customer_name,
        'enrollment_message': enrollment_message,
        'redirect_message': redirect_message,
        'account_activation_messages': account_activation_messages,
        'course_enrollments': course_enrollments,
        'course_entitlements': course_entitlements,
        'course_entitlement_available_sessions': course_entitlement_available_sessions,
        'unfulfilled_entitlement_pseudo_sessions': unfulfilled_entitlement_pseudo_sessions,
        'course_optouts': course_optouts,
        'banner_account_activation_message': banner_account_activation_message,
        'sidebar_account_activation_message': sidebar_account_activation_message,
        'staff_access': staff_access,
        'errored_courses': errored_courses,
        'show_courseware_links_for': show_courseware_links_for,
        'all_course_modes': course_mode_info,
        'cert_statuses': cert_statuses,
        'credit_statuses': _credit_statuses(user, course_enrollments),
        'show_email_settings_for': show_email_settings_for,
        'reverifications': reverifications,
        'verification_display': verification_status['should_display'],
        'verification_status': verification_status['status'],
        'verification_status_by_course': verify_status_by_course,
        'verification_errors': verification_errors,
        'block_courses': block_courses,
        'denied_banner': denied_banner,
        'billing_email': settings.PAYMENT_SUPPORT_EMAIL,
        'user': user,
        'logout_url': reverse('logout'),
        'platform_name': platform_name,
        'enrolled_courses_either_paid': enrolled_courses_either_paid,
        'provider_states': [],
        'order_history_list': order_history_list,
        'courses_requirements_not_met': courses_requirements_not_met,
        'nav_hidden': True,
        'inverted_programs': inverted_programs,
        'show_program_listing': ProgramsApiConfig.is_enabled(),
        'show_dashboard_tabs': True,
        'disable_courseware_js': True,
        'display_course_modes_on_dashboard': enable_verified_certificates and display_course_modes_on_dashboard,
        'display_sidebar_on_dashboard': display_sidebar_on_dashboard,
    }

    if ecommerce_service.is_enabled(request.user):
        context.update({
            'use_ecommerce_payment_flow': True,
            'ecommerce_payment_page': ecommerce_service.payment_page_url(),
        })

    # Gather urls for course card resume buttons.
    resume_button_urls = _get_urls_for_resume_buttons(user, course_enrollments)
    # There must be enough urls for dashboard.html. Template creates course
    # cards for "enrollments + entitlements".
    resume_button_urls += ['' for entitlement in course_entitlements]
    context.update({
        'resume_button_urls': resume_button_urls
    })

    response = render_to_response('dashboard.html', context)
    set_user_info_cookie(response, request)
    return response
Example #41
0
def matriculate_learner(user, uid):
    """
    Update any waiting program enrollments with a user,
    and enroll the user in any waiting course enrollments.

    In most cases this will just short-circuit. Enrollments will only be updated
    for a SAML provider with a linked organization.
    """
    try:
        provider_slug, external_user_key = uid.split(':')
        authorizing_org = SAMLProviderConfig.objects.current_set().get(
            slug=provider_slug).organization

        if not authorizing_org:
            return
    except (AttributeError, ValueError):
        logger.info('Ignoring non-saml social auth entry for user=%s', user.id)
        return
    except SAMLProviderConfig.DoesNotExist:
        logger.warning(
            'Got incoming social auth for provider=%s but no such provider exists',
            provider_slug)
        return
    except SAMLProviderConfig.MultipleObjectsReturned:
        logger.warning(
            'Unable to activate waiting enrollments for user=%s.'
            '  Multiple active SAML configurations found for slug=%s. Expected one.',
            user.id, provider_slug)
        return

    incomplete_enrollments = fetch_program_enrollments_by_student(
        external_user_key=external_user_key,
        waiting_only=True,
    ).prefetch_related('program_course_enrollments')

    for enrollment in incomplete_enrollments:
        try:
            enrollment_org = get_programs(
                uuid=enrollment.program_uuid)['authoring_organizations'][0]
            if enrollment_org['key'] != authorizing_org.short_name:
                continue
        except (KeyError, TypeError):
            logger.warning(
                'Failed to complete waiting enrollments for organization=%s.'
                ' No catalog programs with matching authoring_organization exist.',
                authorizing_org.short_name)
            continue

        enrollment.user = user
        enrollment.save()
        for program_course_enrollment in enrollment.program_course_enrollments.all(
        ):
            try:
                program_course_enrollment.enroll(user)
            except CourseEnrollmentException as e:
                logger.warning(
                    'Failed to enroll user=%s with waiting program_course_enrollment=%s: %s',
                    user.id,
                    program_course_enrollment.id,
                    e,
                )
                raise e