def test_default_state(self, _mock_cache): """ Ensure the config stores empty values when no data has been inserted, and is completely disabled. """ self.assertFalse(ProgramsApiConfig.is_enabled()) api_config = ProgramsApiConfig.current() self.assertEqual(api_config.internal_service_url, '') self.assertEqual(api_config.public_service_url, '') self.assertEqual(api_config.api_version_number, None) self.assertFalse(api_config.is_student_dashboard_enabled)
def test_created_state(self, _mock_cache): """ Ensure the config stores correct values when created with them, but remains disabled. """ self.create_config() self.assertFalse(ProgramsApiConfig.is_enabled()) api_config = ProgramsApiConfig.current() self.assertEqual(api_config.internal_service_url, self.INTERNAL_URL) self.assertEqual(api_config.public_service_url, self.PUBLIC_URL) self.assertEqual(api_config.api_version_number, 1) self.assertFalse(api_config.is_student_dashboard_enabled)
def text_me_the_app(request): """ Text me the app view. """ text_me_fragment = TextMeTheAppFragmentView().render_to_fragment(request) context = { 'nav_hidden': True, 'show_dashboard_tabs': True, 'show_program_listing': ProgramsApiConfig.is_enabled(), 'fragment': text_me_fragment } return render_to_response('text-me-the-app.html', context)
def journal_listing(request): """ View a list of journals which the user has or had access to""" user = request.user if not journals_enabled() or not user.is_authenticated(): raise Http404 journals = fetch_journal_access( site=request.site, user=request.user ) context = { 'journals': journals, 'show_dashboard_tabs': True, 'show_program_listing': ProgramsApiConfig.is_enabled(), 'show_journal_listing': journals_enabled() } return render_to_response('journals/learner_dashboard/journal_dashboard.html', context)
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
def account_settings_context(request): """ Context for the account settings page. Args: request: The request object. Returns: dict """ user = request.user year_of_birth_options = [(unicode(year), unicode(year)) for year in UserProfile.VALID_YEARS] try: user_orders = get_user_orders(user) except: # pylint: disable=bare-except log.exception('Error fetching order history from Otto.') # Return empty order list as account settings page expect a list and # it will be broken if exception raised user_orders = [] beta_language = {} dark_lang_config = DarkLangConfig.current() if dark_lang_config.enable_beta_languages: user_preferences = get_user_preferences(user) pref_language = user_preferences.get('pref-lang') if pref_language in dark_lang_config.beta_languages_list: beta_language['code'] = pref_language beta_language['name'] = settings.LANGUAGE_DICT.get(pref_language) context = { 'auth': {}, 'duplicate_provider': None, 'nav_hidden': True, 'fields': { 'country': { 'options': list(countries), }, 'gender': { 'options': [(choice[0], _(choice[1])) for choice in UserProfile.GENDER_CHOICES], }, 'language': { 'options': released_languages(), }, 'level_of_education': { 'options': [(choice[0], _(choice[1])) for choice in UserProfile.LEVEL_OF_EDUCATION_CHOICES], }, 'password': { 'url': reverse('password_reset'), }, 'year_of_birth': { 'options': year_of_birth_options, }, 'preferred_language': { 'options': all_languages(), }, 'time_zone': { 'options': TIME_ZONE_CHOICES, } }, 'platform_name': configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME), 'password_reset_support_link': configuration_helpers.get_value( 'PASSWORD_RESET_SUPPORT_LINK', settings.PASSWORD_RESET_SUPPORT_LINK ) or settings.SUPPORT_SITE_LINK, 'user_accounts_api_url': reverse("accounts_api", kwargs={'username': user.username}), 'user_preferences_api_url': reverse('preferences_api', kwargs={'username': user.username}), 'disable_courseware_js': True, 'show_program_listing': ProgramsApiConfig.is_enabled(), 'show_dashboard_tabs': True, 'order_history': user_orders, 'disable_order_history_tab': should_redirect_to_order_history_microfrontend(), 'enable_account_deletion': configuration_helpers.get_value( 'ENABLE_ACCOUNT_DELETION', settings.FEATURES.get('ENABLE_ACCOUNT_DELETION', False) ), 'extended_profile_fields': _get_extended_profile_fields(), 'beta_language': beta_language, } enterprise_customer = get_enterprise_customer_for_learner(user=request.user) update_account_settings_context_for_enterprise(context, enterprise_customer) if third_party_auth.is_enabled(): # If the account on the third party provider is already connected with another edX account, # we display a message to the user. context['duplicate_provider'] = pipeline.get_duplicate_provider(messages.get_messages(request)) auth_states = pipeline.get_provider_user_states(user) context['auth']['providers'] = [{ 'id': state.provider.provider_id, 'name': state.provider.name, # The name of the provider e.g. Facebook 'connected': state.has_account, # Whether the user's edX account is connected with the provider. # If the user is not connected, they should be directed to this page to authenticate # with the particular provider, as long as the provider supports initiating a login. 'connect_url': pipeline.get_login_url( state.provider.provider_id, pipeline.AUTH_ENTRY_ACCOUNT_SETTINGS, # The url the user should be directed to after the auth process has completed. redirect_url=reverse('account_settings'), ), 'accepts_logins': state.provider.accepts_logins, # If the user is connected, sending a POST request to this url removes the connection # information for this provider from their edX account. 'disconnect_url': pipeline.get_disconnect_url(state.provider.provider_id, state.association_id), # We only want to include providers if they are either currently available to be logged # in with, or if the user is already authenticated with them. } for state in auth_states if state.provider.display_for_login or state.has_account] return context
def learner_profile_context(request, profile_username, user_is_staff): """Context for the learner profile page. Args: logged_in_user (object): Logged In user. profile_username (str): username of user whose profile is requested. user_is_staff (bool): Logged In user has staff access. build_absolute_uri_func (): Returns: dict Raises: ObjectDoesNotExist: the specified profile_username does not exist. """ profile_user = User.objects.get(username=profile_username) logged_in_user = request.user own_profile = (logged_in_user.username == profile_username) account_settings_data = get_account_settings(request, [profile_username])[0] preferences_data = get_user_preferences(profile_user, profile_username) context = { 'own_profile': own_profile, 'platform_name': configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME), 'data': { 'profile_user_id': profile_user.id, 'default_public_account_fields': settings.ACCOUNT_VISIBILITY_CONFIGURATION['public_fields'], 'default_visibility': settings.ACCOUNT_VISIBILITY_CONFIGURATION['default_visibility'], 'accounts_api_url': reverse("accounts_api", kwargs={'username': profile_username}), 'preferences_api_url': reverse('preferences_api', kwargs={'username': profile_username}), 'preferences_data': preferences_data, 'account_settings_data': account_settings_data, 'profile_image_upload_url': reverse('profile_image_upload', kwargs={'username': profile_username}), 'profile_image_remove_url': reverse('profile_image_remove', kwargs={'username': profile_username}), 'profile_image_max_bytes': settings.PROFILE_IMAGE_MAX_BYTES, 'profile_image_min_bytes': settings.PROFILE_IMAGE_MIN_BYTES, 'account_settings_page_url': reverse('account_settings'), 'has_preferences_access': (logged_in_user.username == profile_username or user_is_staff), 'own_profile': own_profile, 'country_options': list(countries), 'find_courses_url': marketing_link('COURSES'), 'language_options': settings.ALL_LANGUAGES, 'badges_logo': staticfiles_storage.url('certificates/images/backpack-logo.png'), 'badges_icon': staticfiles_storage.url('certificates/images/ico-mozillaopenbadges.png'), 'backpack_ui_img': staticfiles_storage.url('certificates/images/backpack-ui.png'), 'platform_name': configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME), 'social_platforms': settings.SOCIAL_PLATFORMS, }, 'show_program_listing': ProgramsApiConfig.is_enabled(), 'show_journal_listing': journals_enabled(), 'show_dashboard_tabs': True, 'disable_courseware_js': True, 'nav_hidden': True, 'records_url': get_credentials_records_url(), } if own_profile or user_is_staff: achievements_fragment = LearnerAchievementsFragmentView().render_to_fragment( request, username=profile_user.username, own_profile=own_profile, ) context['achievements_fragment'] = achievements_fragment if badges_enabled(): context['data']['badges_api_url'] = reverse("badges_api:user_assertions", kwargs={'username': profile_username}) return context
def account_settings_context(request): """ Context for the account settings page. Args: request: The request object. Returns: dict """ user = request.user year_of_birth_options = [(unicode(year), unicode(year)) for year in UserProfile.VALID_YEARS] try: user_orders = get_user_orders(user) except: # pylint: disable=bare-except log.exception('Error fetching order history from Otto.') # Return empty order list as account settings page expect a list and # it will be broken if exception raised user_orders = [] context = { 'auth': {}, 'duplicate_provider': None, 'nav_hidden': True, 'fields': { 'country': { 'options': list(countries), }, 'gender': { 'options': [(choice[0], _(choice[1])) for choice in UserProfile.GENDER_CHOICES], # pylint: disable=translation-of-non-string }, 'language': { 'options': released_languages(), }, 'level_of_education': { 'options': [(choice[0], _(choice[1])) for choice in UserProfile.LEVEL_OF_EDUCATION_CHOICES], # pylint: disable=translation-of-non-string }, 'password': { 'url': reverse('password_reset'), }, 'year_of_birth': { 'options': year_of_birth_options, }, 'preferred_language': { 'options': all_languages(), }, 'time_zone': { 'options': TIME_ZONE_CHOICES, } }, 'platform_name': configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME), 'password_reset_support_link': configuration_helpers.get_value('PASSWORD_RESET_SUPPORT_LINK', settings.PASSWORD_RESET_SUPPORT_LINK) or settings.SUPPORT_SITE_LINK, 'user_accounts_api_url': reverse("accounts_api", kwargs={'username': user.username}), 'user_preferences_api_url': reverse('preferences_api', kwargs={'username': user.username}), 'disable_courseware_js': True, 'show_program_listing': ProgramsApiConfig.is_enabled(), 'show_dashboard_tabs': True, 'order_history': user_orders, 'extended_profile_fields': _get_extended_profile_fields(), } enterprise_customer = get_enterprise_customer_for_learner( site=request.site, user=request.user) update_account_settings_context_for_enterprise(context, enterprise_customer) if third_party_auth.is_enabled(): # If the account on the third party provider is already connected with another edX account, # we display a message to the user. context['duplicate_provider'] = pipeline.get_duplicate_provider( messages.get_messages(request)) auth_states = pipeline.get_provider_user_states(user) context['auth']['providers'] = [ { 'id': state.provider.provider_id, 'name': state.provider.name, # The name of the provider e.g. Facebook 'connected': state. has_account, # Whether the user's edX account is connected with the provider. # If the user is not connected, they should be directed to this page to authenticate # with the particular provider, as long as the provider supports initiating a login. 'connect_url': pipeline.get_login_url( state.provider.provider_id, pipeline.AUTH_ENTRY_ACCOUNT_SETTINGS, # The url the user should be directed to after the auth process has completed. redirect_url=reverse('account_settings'), ), 'accepts_logins': state.provider.accepts_logins, # If the user is connected, sending a POST request to this url removes the connection # information for this provider from their edX account. 'disconnect_url': pipeline.get_disconnect_url(state.provider.provider_id, state.association_id), # We only want to include providers if they are either currently available to be logged # in with, or if the user is already authenticated with them. } for state in auth_states if state.provider.display_for_login or state.has_account ] return context
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 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) # 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) # 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) # 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( 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']) 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 = ( 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, '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_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_journal_listing': journals_enabled(), # TODO: Dashboard Plugin required '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, } 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_urls_for_resume_buttons(user, course_enrollments): resume_button_urls.append(url) # eliteu membership if settings.FEATURES.get('ENABLE_MEMBERSHIP_INTEGRATION', False): from membership.models import VIPCourseEnrollment, VIPInfo vip_info = VIPInfo.objects.filter(user=user).order_by('-id').first() vces = VIPCourseEnrollment.objects.filter(user=user, is_active=True) vip_course_enrollment_ids = [v.course_id.html_id() for v in vces] context.update({ 'is_vip': VIPInfo.is_vip(user), 'vip_expired_at': vip_info and vip_info.expired_at or None, 'vip_purchase_url': reverse('membership_card'), 'vip_course_enrollment_ids': vip_course_enrollment_ids, 'display_sidebar_on_dashboard': True }) # There must be enough urls for dashboard.html. Template creates course # cards for "enrollments + entitlements". context.update({'resume_button_urls': resume_button_urls}) response = render_to_response('dashboard.html', context) set_deprecated_user_info_cookie(response, request, user) # pylint: disable=protected-access return response
def student_dashboard(request): # lint-amnesty, pylint: disable=too-many-statements """ 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_unenrollment = configuration_helpers.get_value( 'DISABLE_UNENROLLMENT', settings.FEATURES.get('DISABLE_UNENROLLMENT')) 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 unexpired_course_modes.items() } # 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(): try: pending_email = PendingSecondaryEmailChange.objects.get(user=user) # lint-amnesty, pylint: disable=unused-variable except PendingSecondaryEmailChange.DoesNotExist: try: account_recovery_obj = AccountRecovery.objects.get(user=user) # lint-amnesty, pylint: disable=unused-variable 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() # 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( f'{EXPERIMENTS_NAMESPACE}.bundles_on_dashboard', __name__) # lint-amnesty, pylint: disable=toggle-missing-annotation # 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) 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) enrolled_courses_either_paid = frozenset( enrollment.course_id for enrollment in course_enrollments if enrollment.is_paid_course()) # Checks if a course enrollment redeemed using a voucher is refundable enrolled_courses_voucher_refundable = frozenset( enrollment.course_id for enrollment in course_enrollments if enrollment.is_order_voucher_refundable()) # 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 = '' # 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 ] show_account_activation_popup = request.COOKIES.get( settings.SHOW_ACTIVATE_CTA_POPUP_COOKIE_NAME, None) context = { 'urls': urls, 'programs_data': programs_data, 'enterprise_message': enterprise_message, 'consent_required_courses': consent_required_courses, '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, 'denied_banner': denied_banner, 'billing_email': settings.PAYMENT_SUPPORT_EMAIL, 'show_account_activation_popup': show_account_activation_popup, 'user': user, 'logout_url': reverse('logout'), 'platform_name': platform_name, 'enrolled_courses_either_paid': enrolled_courses_either_paid, 'enrolled_courses_voucher_refundable': enrolled_courses_voucher_refundable, '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_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) 'disable_unenrollment': disable_unenrollment, } # Include enterprise learner portal metadata and messaging enterprise_learner_portal_context = get_enterprise_learner_portal_context( request) context.update(enterprise_learner_portal_context) context_from_plugins = get_plugins_view_context( ProjectType.LMS, COURSE_DASHBOARD_PLUGIN_VIEW_NAME, context) context.update(context_from_plugins) notice_url = check_for_unacknowledged_notices(context) if notice_url: return redirect(notice_url) course = None context.update(get_experiment_user_metadata_context( course, user, )) 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}) dashboard_template = 'dashboard.html' try: # .. filter_implemented_name: DashboardRenderStarted # .. filter_type: org.openedx.learning.dashboard.render.started.v1 context, dashboard_template = DashboardRenderStarted.run_filter( context=context, template_name=dashboard_template, ) except DashboardRenderStarted.RenderInvalidDashboard as exc: response = render_to_response(exc.dashboard_template, exc.template_context) except DashboardRenderStarted.RedirectToPage as exc: response = HttpResponseRedirect(exc.redirect_to or reverse('account_settings')) except DashboardRenderStarted.RenderCustomResponse as exc: response = exc.response else: response = render_to_response(dashboard_template, context) if show_account_activation_popup: response.delete_cookie( settings.SHOW_ACTIVATE_CTA_POPUP_COOKIE_NAME, domain=settings.SESSION_COOKIE_DOMAIN, path='/', ) return response
def learner_profile_context(request, profile_username, user_is_staff): """Context for the learner profile page. Args: logged_in_user (object): Logged In user. profile_username (str): username of user whose profile is requested. user_is_staff (bool): Logged In user has staff access. build_absolute_uri_func (): Returns: dict Raises: ObjectDoesNotExist: the specified profile_username does not exist. """ profile_user = User.objects.get(username=profile_username) logged_in_user = request.user own_profile = (logged_in_user.username == profile_username) account_settings_data = get_account_settings(request, [profile_username])[0] preferences_data = get_user_preferences(profile_user, profile_username) context = { 'own_profile': own_profile, 'platform_name': configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME), 'data': { 'profile_user_id': profile_user.id, 'default_public_account_fields': settings.ACCOUNT_VISIBILITY_CONFIGURATION['public_fields'], 'default_visibility': settings.ACCOUNT_VISIBILITY_CONFIGURATION['default_visibility'], 'accounts_api_url': reverse("accounts_api", kwargs={'username': profile_username}), 'preferences_api_url': reverse('preferences_api', kwargs={'username': profile_username}), 'preferences_data': preferences_data, 'account_settings_data': account_settings_data, 'profile_image_upload_url': reverse('profile_image_upload', kwargs={'username': profile_username}), 'profile_image_remove_url': reverse('profile_image_remove', kwargs={'username': profile_username}), 'profile_image_max_bytes': settings.PROFILE_IMAGE_MAX_BYTES, 'profile_image_min_bytes': settings.PROFILE_IMAGE_MIN_BYTES, 'account_settings_page_url': reverse('account_settings'), 'has_preferences_access': (logged_in_user.username == profile_username or user_is_staff), 'own_profile': own_profile, 'country_options': list(countries), 'find_courses_url': marketing_link('COURSES'), 'language_options': settings.ALL_LANGUAGES, 'badges_logo': staticfiles_storage.url('certificates/images/backpack-logo.png'), 'badges_icon': staticfiles_storage.url( 'certificates/images/ico-mozillaopenbadges.png'), 'backpack_ui_img': staticfiles_storage.url('certificates/images/backpack-ui.png'), 'platform_name': configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME), 'social_platforms': settings.SOCIAL_PLATFORMS, }, 'show_program_listing': ProgramsApiConfig.is_enabled(), 'show_dashboard_tabs': True, 'disable_courseware_js': True, 'nav_hidden': True, 'records_url': get_credentials_records_url(), } if own_profile or user_is_staff: achievements_fragment = LearnerAchievementsFragmentView( ).render_to_fragment( request, username=profile_user.username, own_profile=own_profile, ) context['achievements_fragment'] = achievements_fragment if badges_enabled(): context['data']['badges_api_url'] = reverse( "badges_api:user_assertions", kwargs={'username': profile_username}) return context