def verify_identity_url(self): """ Returns a String to the location to verify a learner's identity Note: This might return an absolute URL (if the verification MFE is enabled) or a relative URL. The serializer will make the relative URL absolute so any consumers can treat this as a full URL. """ if self.enrollment_object and self.enrollment_object.mode in CourseMode.VERIFIED_MODES: verification_status = IDVerificationService.user_status(self.effective_user)['status'] if verification_status == 'must_reverify': return IDVerificationService.get_verify_location() else: return IDVerificationService.get_verify_location(self.course_key)
def link_table(self): """Maps verification state to a tuple of link text and location.""" return { 'verification-deadline-passed': (_('Learn More'), ''), 'verification-deadline-retry': ( _('Retry Verification'), IDVerificationService.get_verify_location(), ), 'verification-deadline-upcoming': ( _('Verify My Identity'), IDVerificationService.get_verify_location(self.course_id), ) }
def test_professional_enrollment(self, mode): # The only course mode is professional ed CourseModeFactory.create(mode_slug=mode, course_id=self.course.id, min_price=1) # Go to the "choose your track" page choose_track_url = reverse('course_modes_choose', args=[str(self.course.id)]) response = self.client.get(choose_track_url) # Since the only available track is professional ed, expect that # we're redirected immediately to the start of the payment flow. start_flow_url = IDVerificationService.get_verify_location( course_id=self.course.id) self.assertRedirects(response, start_flow_url, fetch_redirect_response=False) # Now enroll in the course CourseEnrollmentFactory( user=self.user, is_active=True, mode=mode, course_id=str(self.course.id), ) # Expect that this time we're redirected to the dashboard (since we're already registered) response = self.client.get(choose_track_url) self.assertRedirects(response, reverse('dashboard'))
def test_get_verify_location_no_course_key(self): """ Test for the path to the IDV flow with no course key given """ path = IDVerificationService.get_verify_location() expected_path = f'{settings.ACCOUNT_MICROFRONTEND_URL}/id-verification' assert path == expected_path
def _get_fragments(self): """ Provide a tuple of string[]s that should be (in, not_in) the email """ course_module = modulestore().get_course(self.course.id) proctoring_provider = capwords( course_module.proctoring_provider.replace('_', ' ')) id_verification_url = IDVerificationService.get_verify_location() fragments = [ ("You are enrolled in {} at {}. This course contains proctored exams." .format(self.course.display_name, settings.PLATFORM_NAME)), ("Proctored exams are timed exams that you take while proctoring software monitors " "your computer's desktop, webcam video, and audio."), proctoring_provider, escape( "Carefully review the system requirements as well as the steps to take a proctored " "exam in order to ensure that you are prepared."), settings.PROCTORING_SETTINGS.get('LINK_URLS', {}).get('faq', ''), ] idv_fragments = [ escape( "Before taking a graded proctored exam, you must have approved ID verification photos." ), id_verification_url, ] if not is_integrity_signature_enabled(self.course.id): fragments.extend(idv_fragments) return (fragments, []) return (fragments, idv_fragments)
def test_get_verify_location_from_string(self): """ Test for the path to the IDV flow with a course key string """ path = IDVerificationService.get_verify_location('course-v1:edX+DemoX+Demo_Course') expected_path = f'{settings.ACCOUNT_MICROFRONTEND_URL}/id-verification' assert path == (expected_path + '?course_id=course-v1%3AedX%2BDemoX%2BDemo_Course')
def test_choose_mode_redirect(self, course_mode, expected_redirect): # Create the course modes for mode in ('audit', 'honor', 'verified'): min_price = 0 if mode in ["honor", "audit"] else 1 CourseModeFactory.create(mode_slug=mode, course_id=self.course.id, min_price=min_price) # Choose the mode (POST request) choose_track_url = reverse('course_modes_choose', args=[str(self.course.id)]) response = self.client.post( choose_track_url, self.POST_PARAMS_FOR_COURSE_MODE[course_mode]) # Verify the redirect if expected_redirect == 'dashboard': redirect_url = reverse('dashboard') elif expected_redirect == 'start-flow': redirect_url = IDVerificationService.get_verify_location( course_id=self.course.id) else: self.fail("Must provide a valid redirect URL name") with mock_payment_processors(expect_called=None): self.assertRedirects( response, redirect_url, fetch_redirect_response=False, )
def test_get_verify_location_no_course_key(self): """ Test for the path to the IDV flow with no course key given """ path = IDVerificationService.get_verify_location() expected_path = '{}/id-verification'.format( settings.ACCOUNT_MICROFRONTEND_URL) self.assertEqual(path, expected_path)
def test_get_verify_location_from_course_id(self): """ Test for the path to the IDV flow with a course ID """ course = CourseFactory.create(org='Robot', number='999', display_name='Test Course') path = IDVerificationService.get_verify_location(course.id) expected_path = f'{settings.ACCOUNT_MICROFRONTEND_URL}/id-verification' assert path == (expected_path + '?course_id=course-v1%3ARobot%2B999%2BTest_Course')
def test_get_verify_location_from_course_id(self): """ Test for the path to the IDV flow with a course ID """ course = CourseFactory.create(org='Robot', number='999', display_name='Test Course') path = IDVerificationService.get_verify_location(course.id) expected_path = '{}/id-verification'.format( settings.ACCOUNT_MICROFRONTEND_URL) self.assertEqual(path, expected_path + '?course_id=Robot/999/Test_Course')
def test_verification_deadline_date_retry(self): with freeze_time('2015-01-02'): course = create_course_run(days_till_start=-1) user = create_user(verification_status='denied') CourseEnrollmentFactory(course_id=course.id, user=user, mode=CourseMode.VERIFIED) block = VerificationDeadlineDate(course, user) assert block.css_class == 'verification-deadline-retry' assert block.title == 'Verification Deadline' assert block.date == (datetime.now(utc) + timedelta(days=14)) assert block.description ==\ 'You must successfully complete verification before this date to qualify for a Verified Certificate.' assert block.link_text == 'Retry Verification' assert block.link == IDVerificationService.get_verify_location()
def generate_proctoring_requirements_email_context(user, course_id): """ Constructs a dictionary for use in proctoring requirements email context Arguments: user: Currently logged-in user course_id: ID of the proctoring-enabled course the user is enrolled in """ course_module = modulestore().get_course(course_id) return { 'user': user, 'course_name': course_module.display_name, 'proctoring_provider': capwords(course_module.proctoring_provider.replace('_', ' ')), 'proctoring_requirements_url': settings.PROCTORING_SETTINGS.get('LINK_URLS', {}).get('faq', ''), 'id_verification_url': IDVerificationService.get_verify_location(), }
def _get_fragments(self): course_module = modulestore().get_course(self.course.id) proctoring_provider = capwords( course_module.proctoring_provider.replace('_', ' ')) id_verification_url = IDVerificationService.get_verify_location() return [ ("You are enrolled in {} at {}. This course contains proctored exams." .format(self.course.display_name, settings.PLATFORM_NAME)), ("Proctored exams are timed exams that you take while proctoring software monitors " "your computer's desktop, webcam video, and audio."), proctoring_provider, ("Carefully review the system requirements as well as the steps to take a proctored " "exam in order to ensure that you are prepared."), settings.PROCTORING_SETTINGS.get('LINK_URLS', {}).get('faq', ''), ("Before taking a graded proctored exam, you must have approved ID verification photos." ), id_verification_url ]
def test_no_id_redirect(self): # Create the course modes CourseModeFactory.create(mode_slug=CourseMode.NO_ID_PROFESSIONAL_MODE, course_id=self.course.id, min_price=100) # Enroll the user in the test course CourseEnrollmentFactory( is_active=False, mode=CourseMode.NO_ID_PROFESSIONAL_MODE, course_id=self.course.id, user=self.user ) # Configure whether we're upgrading or not url = reverse('course_modes_choose', args=[str(self.course.id)]) response = self.client.get(url) start_flow_url = IDVerificationService.get_verify_location(course_id=self.course.id) # Check whether we were correctly redirected self.assertRedirects(response, start_flow_url, fetch_redirect_response=False)
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) student_id = kwargs.get('student_id') if not course_home_mfe_progress_tab_is_active(course_key): raise Http404 # Enable NR tracing for this view based on course monitoring_utils.set_custom_attribute('course_id', course_key_string) monitoring_utils.set_custom_attribute('user_id', request.user.id) monitoring_utils.set_custom_attribute('is_staff', request.user.is_staff) is_staff = bool(has_access(request.user, 'staff', course_key)) student = self._get_student_user(request, course_key, student_id, is_staff) username = get_enterprise_learner_generic_name( request) or student.username course = get_course_with_access(student, 'load', course_key, check_if_enrolled=False) course_overview = CourseOverview.get_from_id(course_key) enrollment = CourseEnrollment.get_enrollment(student, course_key) enrollment_mode = getattr(enrollment, 'mode', None) if not (enrollment and enrollment.is_active) and not is_staff: return Response('User not enrolled.', status=401) # The block structure is used for both the course_grade and has_scheduled content fields # So it is called upfront and reused for optimization purposes collected_block_structure = get_block_structure_manager( course_key).get_collected() course_grade = CourseGradeFactory().read( student, collected_block_structure=collected_block_structure) # recalculate course grade from visible grades (stored grade was calculated over all grades, visible or not) course_grade.update(visible_grades_only=True, has_staff_access=is_staff) # Get has_scheduled_content data transformers = BlockStructureTransformers() transformers += [ start_date.StartDateTransformer(), ContentTypeGateTransformer() ] usage_key = collected_block_structure.root_block_usage_key course_blocks = get_course_blocks( student, usage_key, transformers=transformers, collected_block_structure=collected_block_structure, include_has_scheduled_content=True) has_scheduled_content = course_blocks.get_xblock_field( usage_key, 'has_scheduled_content') # Get user_has_passing_grade data user_has_passing_grade = False if not student.is_anonymous: user_grade = course_grade.percent user_has_passing_grade = user_grade >= course.lowest_passing_grade descriptor = modulestore().get_course(course_key) grading_policy = descriptor.grading_policy verification_status = IDVerificationService.user_status(student) verification_link = None if verification_status['status'] is None or verification_status[ 'status'] == 'expired': verification_link = IDVerificationService.get_verify_location( course_id=course_key) elif verification_status['status'] == 'must_reverify': verification_link = IDVerificationService.get_verify_location( course_id=course_key) verification_data = { 'link': verification_link, 'status': verification_status['status'], 'status_date': verification_status['status_date'], } access_expiration = get_access_expiration_data(request.user, course_overview) data = { 'access_expiration': access_expiration, 'certificate_data': get_cert_data(student, course, enrollment_mode, course_grade), 'completion_summary': get_course_blocks_completion_summary(course_key, student), 'course_grade': course_grade, 'credit_course_requirements': credit_course_requirements(course_key, student), 'end': course.end, 'enrollment_mode': enrollment_mode, 'grading_policy': grading_policy, 'has_scheduled_content': has_scheduled_content, 'section_scores': list(course_grade.chapter_grades.values()), 'studio_url': get_studio_url(course, 'settings/grading'), 'username': username, 'user_has_passing_grade': user_has_passing_grade, 'verification_data': verification_data, } context = self.get_serializer_context() context['staff_access'] = is_staff context['course_blocks'] = course_blocks context['course_key'] = course_key # course_overview and enrollment will be used by VerifiedModeSerializer context['course_overview'] = course_overview context['enrollment'] = enrollment serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def test_enrolled_course_metadata(self, logged_in, enrollment_mode): check_public_access = mock.Mock() check_public_access.return_value = ACCESS_DENIED with mock.patch('lms.djangoapps.courseware.access_utils.check_public_access', check_public_access): if not logged_in: self.client.logout() if enrollment_mode == 'verified': cert = GeneratedCertificateFactory.create( user=self.user, course_id=self.course.id, status='downloadable', mode='verified', ) if enrollment_mode: CourseEnrollment.enroll(self.user, self.course.id, enrollment_mode) response = self.client.get(self.url) assert response.status_code == 200 enrollment = response.data['enrollment'] assert enrollment_mode == enrollment['mode'] assert enrollment['is_active'] assert not response.data['user_has_passing_grade'] assert response.data['celebrations']['first_section'] assert not response.data['celebrations']['weekly_goal'] # This import errors in cms if it is imported at the top level from lms.djangoapps.course_goals.api import get_course_goal selected_goal = get_course_goal(self.user, self.course.id) if selected_goal: assert response.data['course_goals']['selected_goal'] == { 'days_per_week': selected_goal.days_per_week, 'subscribed_to_reminders': selected_goal.subscribed_to_reminders, } if enrollment_mode == 'audit': assert response.data['verify_identity_url'] is None assert response.data['verification_status'] == 'none' assert response.data['linkedin_add_to_profile_url'] is None else: assert response.data['certificate_data']['cert_status'] == 'earned_but_not_available' expected_verify_identity_url = IDVerificationService.get_verify_location( course_id=self.course.id ) # The response contains an absolute URL so this is only checking the path of the final assert expected_verify_identity_url in response.data['verify_identity_url'] assert response.data['verification_status'] == 'none' request = RequestFactory().request() cert_url = get_certificate_url(course_id=self.course.id, uuid=cert.verify_uuid) linkedin_url_params = { 'name': '{platform_name} Verified Certificate for {course_name}'.format( platform_name=settings.PLATFORM_NAME, course_name=self.course.display_name, ), 'certUrl': request.build_absolute_uri(cert_url), # default value from the LinkedInAddToProfileConfigurationFactory company_identifier 'organizationId': 1337, 'certId': cert.verify_uuid, 'issueYear': cert.created_date.year, 'issueMonth': cert.created_date.month, } expected_linkedin_url = ( 'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&{params}'.format( params=urlencode(linkedin_url_params) ) ) assert response.data['linkedin_add_to_profile_url'] == expected_linkedin_url
def post(self, request, course_id): """Takes the form submission from the page and parses it. Args: request (`Request`): The Django Request object. course_id (unicode): The slash-separated course key. Returns: Status code 400 when the requested mode is unsupported. When the honor mode is selected, redirects to the dashboard. When the verified mode is selected, returns error messages if the indicated contribution amount is invalid or below the minimum, otherwise redirects to the verification flow. """ course_key = CourseKey.from_string(course_id) user = request.user # This is a bit redundant with logic in student.views.change_enrollment, # but I don't really have the time to refactor it more nicely and test. course = modulestore().get_course(course_key) if not user.has_perm(ENROLL_IN_COURSE, course): error_msg = _("Enrollment is closed") return self.get(request, course_id, error=error_msg) requested_mode = self._get_requested_mode(request.POST) allowed_modes = CourseMode.modes_for_course_dict(course_key) if requested_mode not in allowed_modes: return HttpResponseBadRequest(_("Enrollment mode not supported")) if requested_mode == 'audit': # If the learner has arrived at this screen via the traditional enrollment workflow, # then they should already be enrolled in an audit mode for the course, assuming one has # been configured. However, alternative enrollment workflows have been introduced into the # system, such as third-party discovery. These workflows result in learners arriving # directly at this screen, and they will not necessarily be pre-enrolled in the audit mode. CourseEnrollment.enroll(request.user, course_key, CourseMode.AUDIT) return self._redirect_to_course_or_dashboard( course, course_key, user) if requested_mode == 'honor': CourseEnrollment.enroll(user, course_key, mode=requested_mode) return self._redirect_to_course_or_dashboard( course, course_key, user) mode_info = allowed_modes[requested_mode] if requested_mode == 'verified': amount = request.POST.get("contribution") or \ request.POST.get("contribution-other-amt") or 0 try: # Validate the amount passed in and force it into two digits amount_value = decimal.Decimal(amount).quantize( decimal.Decimal('.01'), rounding=decimal.ROUND_DOWN) except decimal.InvalidOperation: error_msg = _("Invalid amount selected.") return self.get(request, course_id, error=error_msg) # Check for minimum pricing if amount_value < mode_info.min_price: error_msg = _( "No selected price or selected price is too low.") return self.get(request, course_id, error=error_msg) donation_for_course = request.session.get("donation_for_course", {}) donation_for_course[str(course_key)] = amount_value request.session["donation_for_course"] = donation_for_course verify_url = IDVerificationService.get_verify_location( course_id=course_key) return redirect(verify_url)
def get(self, request, course_id, error=None): # lint-amnesty, pylint: disable=too-many-statements """Displays the course mode choice page. Args: request (`Request`): The Django Request object. course_id (unicode): The slash-separated course key. Keyword Args: error (unicode): If provided, display this error message on the page. Returns: Response """ course_key = CourseKey.from_string(course_id) # Check whether the user has access to this course # based on country access rules. embargo_redirect = embargo_api.redirect_if_blocked( course_key, user=request.user, ip_address=get_client_ip(request)[0], url=request.path) if embargo_redirect: return redirect(embargo_redirect) enrollment_mode, is_active = CourseEnrollment.enrollment_mode_for_user( request.user, course_key) increment('track-selection.{}.{}'.format( enrollment_mode, 'active' if is_active else 'inactive')) increment('track-selection.views') if enrollment_mode is None: LOG.info( 'Rendering track selection for unenrolled user, referred by %s', request.META.get('HTTP_REFERER')) modes = CourseMode.modes_for_course_dict(course_key) ecommerce_service = EcommerceService() # We assume that, if 'professional' is one of the modes, it should be the *only* mode. # If there are both modes, default to 'no-id-professional'. has_enrolled_professional = ( CourseMode.is_professional_slug(enrollment_mode) and is_active) if CourseMode.has_professional_mode( modes) and not has_enrolled_professional: purchase_workflow = request.GET.get("purchase_workflow", "single") redirect_url = IDVerificationService.get_verify_location( course_id=course_key) if ecommerce_service.is_enabled(request.user): professional_mode = modes.get( CourseMode.NO_ID_PROFESSIONAL_MODE) or modes.get( CourseMode.PROFESSIONAL) if purchase_workflow == "single" and professional_mode.sku: redirect_url = ecommerce_service.get_checkout_page_url( professional_mode.sku) if purchase_workflow == "bulk" and professional_mode.bulk_sku: redirect_url = ecommerce_service.get_checkout_page_url( professional_mode.bulk_sku) return redirect(redirect_url) course = modulestore().get_course(course_key) # If there isn't a verified mode available, then there's nothing # to do on this page. Send the user to the dashboard. if not CourseMode.has_verified_mode(modes): return self._redirect_to_course_or_dashboard( course, course_key, request.user) # If a user has already paid, redirect them to the dashboard. if is_active and (enrollment_mode in CourseMode.VERIFIED_MODES + [CourseMode.NO_ID_PROFESSIONAL_MODE]): return self._redirect_to_course_or_dashboard( course, course_key, request.user) donation_for_course = request.session.get("donation_for_course", {}) chosen_price = donation_for_course.get(str(course_key), None) if CourseEnrollment.is_enrollment_closed(request.user, course): locale = to_locale(get_language()) enrollment_end_date = format_datetime(course.enrollment_end, 'short', locale=locale) params = six.moves.urllib.parse.urlencode( {'course_closed': enrollment_end_date}) return redirect('{}?{}'.format(reverse('dashboard'), params)) # When a credit mode is available, students will be given the option # to upgrade from a verified mode to a credit mode at the end of the course. # This allows students who have completed photo verification to be eligible # for university credit. # Since credit isn't one of the selectable options on the track selection page, # we need to check *all* available course modes in order to determine whether # a credit mode is available. If so, then we show slightly different messaging # for the verified track. has_credit_upsell = any( CourseMode.is_credit_mode(mode) for mode in CourseMode.modes_for_course(course_key, only_selectable=False)) course_id = str(course_key) gated_content = ContentTypeGatingConfig.enabled_for_enrollment( user=request.user, course_key=course_key) context = { "course_modes_choose_url": reverse("course_modes_choose", kwargs={'course_id': course_id}), "modes": modes, "has_credit_upsell": has_credit_upsell, "course_name": course.display_name_with_default, "course_org": course.display_org_with_default, "course_num": course.display_number_with_default, "chosen_price": chosen_price, "error": error, "responsive": True, "nav_hidden": True, "content_gating_enabled": gated_content, "course_duration_limit_enabled": CourseDurationLimitConfig.enabled_for_enrollment( request.user, course), } context.update( get_experiment_user_metadata_context( course, request.user, )) title_content = '' if enrollment_mode: title_content = _( "Congratulations! You are now enrolled in {course_name}" ).format(course_name=course.display_name_with_default) context["title_content"] = title_content if "verified" in modes: verified_mode = modes["verified"] context["suggested_prices"] = [ decimal.Decimal(x.strip()) for x in verified_mode.suggested_prices.split(",") if x.strip() ] price_before_discount = verified_mode.min_price course_price = price_before_discount enterprise_customer = enterprise_customer_for_request(request) LOG.info( '[e-commerce calculate API] Going to hit the API for user [%s] linked to [%s] enterprise', request.user.username, enterprise_customer.get('name') if isinstance( enterprise_customer, dict) else None # Test Purpose ) if enterprise_customer and verified_mode.sku: course_price = get_course_final_price(request.user, verified_mode.sku, price_before_discount) context["currency"] = verified_mode.currency.upper() context["currency_symbol"] = get_currency_symbol( verified_mode.currency.upper()) context["min_price"] = course_price context["verified_name"] = verified_mode.name context["verified_description"] = verified_mode.description # if course_price is equal to price_before_discount then user doesn't entitle to any discount. if course_price != price_before_discount: context["price_before_discount"] = price_before_discount if verified_mode.sku: context[ "use_ecommerce_payment_flow"] = ecommerce_service.is_enabled( request.user) context[ "ecommerce_payment_page"] = ecommerce_service.payment_page_url( ) context["sku"] = verified_mode.sku context["bulk_sku"] = verified_mode.bulk_sku context['currency_data'] = [] if waffle.switch_is_active('local_currency'): if 'edx-price-l10n' not in request.COOKIES: currency_data = get_currency_data() try: context['currency_data'] = json.dumps(currency_data) except TypeError: pass language = get_language() context['track_links'] = get_verified_track_links(language) duration = get_user_course_duration(request.user, course) deadline = duration and get_user_course_expiration_date( request.user, course) if deadline: formatted_audit_access_date = strftime_localized_html( deadline, 'SHORT_DATE') context['audit_access_deadline'] = formatted_audit_access_date fbe_is_on = deadline and gated_content # Route to correct Track Selection page. # REV-2133 TODO Value Prop: remove waffle flag after testing is completed # and happy path version is ready to be rolled out to all users. if VALUE_PROP_TRACK_SELECTION_FLAG.is_enabled(): if not error: # TODO: Remove by executing REV-2355 if not enterprise_customer_for_request( request): # TODO: Remove by executing REV-2342 if fbe_is_on: return render_to_response("course_modes/fbe.html", context) else: return render_to_response("course_modes/unfbe.html", context) # If error or enterprise_customer, failover to old choose.html page return render_to_response("course_modes/choose.html", context)
def test_course_metadata(self, logged_in, enrollment_mode, enable_anonymous, is_microfrontend_enabled_for_user): is_microfrontend_enabled_for_user.return_value = True check_public_access = mock.Mock() check_public_access.return_value = enable_anonymous with mock.patch( 'lms.djangoapps.courseware.access_utils.check_public_access', check_public_access): if not logged_in: self.client.logout() if enrollment_mode == 'verified': cert = GeneratedCertificateFactory.create( user=self.user, course_id=self.course.id, status='downloadable', mode='verified', ) if enrollment_mode: CourseEnrollment.enroll(self.user, self.course.id, enrollment_mode) response = self.client.get(self.url) assert response.status_code == 200 if enrollment_mode: enrollment = response.data['enrollment'] assert enrollment_mode == enrollment['mode'] assert enrollment['is_active'] assert len(response.data['tabs']) == 6 found = False for tab in response.data['tabs']: if tab['type'] == 'external_link': assert tab[ 'url'] != 'http://hidden.com', "Hidden tab is not hidden" if tab['url'] == 'http://zombo.com': found = True assert found, 'external link not in course tabs' assert not response.data['user_has_passing_grade'] if enrollment_mode == 'audit': assert response.data['verify_identity_url'] is None assert response.data['verification_status'] == 'none' # lint-amnesty, pylint: disable=literal-comparison assert response.data['linkedin_add_to_profile_url'] is None else: assert response.data['certificate_data'][ 'cert_status'] == 'earned_but_not_available' expected_verify_identity_url = IDVerificationService.get_verify_location( course_id=self.course.id) # The response contains an absolute URL so this is only checking the path of the final assert expected_verify_identity_url in response.data[ 'verify_identity_url'] assert response.data['verification_status'] == 'none' # lint-amnesty, pylint: disable=literal-comparison request = RequestFactory().request() cert_url = get_certificate_url(course_id=self.course.id, uuid=cert.verify_uuid) linkedin_url_params = { 'name': '{platform_name} Verified Certificate for {course_name}' .format( platform_name=settings.PLATFORM_NAME, course_name=self.course.display_name, ), 'certUrl': request.build_absolute_uri(cert_url), # default value from the LinkedInAddToProfileConfigurationFactory company_identifier 'organizationId': 1337, 'certId': cert.verify_uuid, 'issueYear': cert.created_date.year, 'issueMonth': cert.created_date.month, } expected_linkedin_url = ( 'https://www.linkedin.com/profile/add?startTask=CERTIFICATION_NAME&{params}' .format(params=urlencode(linkedin_url_params))) assert response.data[ 'linkedin_add_to_profile_url'] == expected_linkedin_url elif enable_anonymous and not logged_in: # multiple checks use this handler check_public_access.assert_called() assert response.data['enrollment']['mode'] is None assert response.data['course_access']['has_access'] else: assert not response.data['course_access']['has_access']
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) if not course_home_mfe_progress_tab_is_active(course_key): raise Http404 # Enable NR tracing for this view based on course monitoring_utils.set_custom_attribute('course_id', course_key_string) monitoring_utils.set_custom_attribute('user_id', request.user.id) monitoring_utils.set_custom_attribute('is_staff', request.user.is_staff) _, request.user = setup_masquerade(request, course_key, staff_access=has_access( request.user, 'staff', course_key), reset_masquerade_data=True) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True) enrollment_mode, _ = CourseEnrollment.enrollment_mode_for_user( request.user, course_key) # The block structure is used for both the course_grade and has_scheduled content fields # So it is called upfront and reused for optimization purposes collected_block_structure = get_block_structure_manager( course_key).get_collected() course_grade = CourseGradeFactory().read( request.user, collected_block_structure=collected_block_structure) # Get has_scheduled_content data transformers = BlockStructureTransformers() transformers += [start_date.StartDateTransformer()] usage_key = collected_block_structure.root_block_usage_key course_blocks = get_course_blocks( request.user, usage_key, transformers=transformers, collected_block_structure=collected_block_structure, include_has_scheduled_content=True) has_scheduled_content = course_blocks.get_xblock_field( usage_key, 'has_scheduled_content') # Get user_has_passing_grade data user_has_passing_grade = False if not request.user.is_anonymous: user_grade = course_grade.percent user_has_passing_grade = user_grade >= course.lowest_passing_grade descriptor = modulestore().get_course(course_key) grading_policy = descriptor.grading_policy verification_status = IDVerificationService.user_status(request.user) verification_link = None if verification_status['status'] is None or verification_status[ 'status'] == 'expired': verification_link = IDVerificationService.get_verify_location( course_id=course_key) elif verification_status['status'] == 'must_reverify': verification_link = IDVerificationService.get_verify_location( course_id=course_key) verification_data = { 'link': verification_link, 'status': verification_status['status'], 'status_date': verification_status['status_date'], } data = { 'end': course.end, 'user_has_passing_grade': user_has_passing_grade, 'certificate_data': get_cert_data(request.user, course, enrollment_mode, course_grade), 'completion_summary': get_course_blocks_completion_summary(course_key, request.user), 'course_grade': course_grade, 'has_scheduled_content': has_scheduled_content, 'section_scores': course_grade.chapter_grades.values(), 'enrollment_mode': enrollment_mode, 'grading_policy': grading_policy, 'studio_url': get_studio_url(course, 'settings/grading'), 'verification_data': verification_data, } context = self.get_serializer_context() context['staff_access'] = bool( has_access(request.user, 'staff', course)) context['course_key'] = course_key serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) # Enable NR tracing for this view based on course monitoring_utils.set_custom_attribute('course_id', course_key_string) monitoring_utils.set_custom_attribute('user_id', request.user.id) monitoring_utils.set_custom_attribute('is_staff', request.user.is_staff) _, request.user = setup_masquerade(request, course_key, staff_access=has_access( request.user, 'staff', course_key), reset_masquerade_data=True) user_timezone_locale = user_timezone_locale_prefs(request) user_timezone = user_timezone_locale['user_timezone'] transformers = BlockStructureTransformers() transformers += course_blocks_api.get_course_block_access_transformers( request.user) transformers += [ BlocksAPITransformer(None, None, depth=3), ] course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True) enrollment_mode, _ = CourseEnrollment.enrollment_mode_for_user( request.user, course_key) course_grade = CourseGradeFactory().read(request.user, course) courseware_summary = course_grade.chapter_grades.values() verification_status = IDVerificationService.user_status(request.user) verification_link = None if verification_status['status'] is None or verification_status[ 'status'] == 'expired': verification_link = IDVerificationService.get_verify_location( course_id=course_key) elif verification_status['status'] == 'must_reverify': verification_link = IDVerificationService.get_verify_location( course_id=course_key) verification_data = { 'link': verification_link, 'status': verification_status['status'], 'status_date': verification_status['status_date'], } data = { 'certificate_data': get_cert_data(request.user, course, enrollment_mode, course_grade), 'courseware_summary': courseware_summary, 'credit_course_requirements': credit_course_requirements(course_key, request.user), 'credit_support_url': CREDIT_SUPPORT_URL, 'enrollment_mode': enrollment_mode, 'studio_url': get_studio_url(course, 'settings/grading'), 'user_timezone': user_timezone, 'verification_data': verification_data, } context = self.get_serializer_context() context['staff_access'] = bool( has_access(request.user, 'staff', course)) context['course_key'] = course_key serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) if not course_home_mfe_progress_tab_is_active(course_key): raise Http404 # Enable NR tracing for this view based on course monitoring_utils.set_custom_attribute('course_id', course_key_string) monitoring_utils.set_custom_attribute('user_id', request.user.id) monitoring_utils.set_custom_attribute('is_staff', request.user.is_staff) _, request.user = setup_masquerade(request, course_key, staff_access=has_access( request.user, 'staff', course_key), reset_masquerade_data=True) course = get_course_with_access(request.user, 'load', course_key, check_if_enrolled=True) enrollment_mode, _ = CourseEnrollment.enrollment_mode_for_user( request.user, course_key) course_grade = CourseGradeFactory().read(request.user, course)\ descriptor = modulestore().get_course(course_key) grading_policy = descriptor.grading_policy verification_status = IDVerificationService.user_status(request.user) verification_link = None if verification_status['status'] is None or verification_status[ 'status'] == 'expired': verification_link = IDVerificationService.get_verify_location( course_id=course_key) elif verification_status['status'] == 'must_reverify': verification_link = IDVerificationService.get_verify_location( course_id=course_key) verification_data = { 'link': verification_link, 'status': verification_status['status'], 'status_date': verification_status['status_date'], } data = { 'certificate_data': get_cert_data(request.user, course, enrollment_mode, course_grade), 'completion_summary': get_course_blocks_completion_summary(course_key, request.user), 'course_grade': course_grade, 'section_scores': course_grade.chapter_grades.values(), 'enrollment_mode': enrollment_mode, 'grading_policy': grading_policy, 'studio_url': get_studio_url(course, 'settings/grading'), 'verification_data': verification_data, } context = self.get_serializer_context() context['staff_access'] = bool( has_access(request.user, 'staff', course)) context['course_key'] = course_key serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)
def get(self, request, *args, **kwargs): course_key_string = kwargs.get('course_key_string') course_key = CourseKey.from_string(course_key_string) student_id = kwargs.get('student_id') if student_id: try: student_id = int(student_id) except ValueError: raise Http404 if not course_home_mfe_progress_tab_is_active(course_key): raise Http404 # Enable NR tracing for this view based on course monitoring_utils.set_custom_attribute('course_id', course_key_string) monitoring_utils.set_custom_attribute('user_id', request.user.id) monitoring_utils.set_custom_attribute('is_staff', request.user.is_staff) is_staff = bool(has_access(request.user, 'staff', course_key)) if student_id is None or student_id == request.user.id: _, student = setup_masquerade(request, course_key, staff_access=is_staff, reset_masquerade_data=True) else: # When a student_id is passed in, we display the progress page for the user # with the provided user id, rather than the requesting user try: coach_access = has_ccx_coach_role(request.user, course_key) except CCXLocatorValidationException: coach_access = False has_access_on_students_profiles = is_staff or coach_access # Requesting access to a different student's profile if not has_access_on_students_profiles: raise Http404 try: student = User.objects.get(id=student_id) except User.DoesNotExist as exc: raise Http404 from exc username = get_enterprise_learner_generic_name( request) or student.username course = get_course_with_access(student, 'load', course_key, check_if_enrolled=False) course_overview = CourseOverview.get_from_id(course_key) enrollment = CourseEnrollment.get_enrollment(student, course_key) enrollment_mode = getattr(enrollment, 'mode', None) if not (enrollment and enrollment.is_active) and not is_staff: return Response('User not enrolled.', status=401) # The block structure is used for both the course_grade and has_scheduled content fields # So it is called upfront and reused for optimization purposes collected_block_structure = get_block_structure_manager( course_key).get_collected() course_grade = CourseGradeFactory().read( student, collected_block_structure=collected_block_structure) # Get has_scheduled_content data transformers = BlockStructureTransformers() transformers += [ start_date.StartDateTransformer(), ContentTypeGateTransformer() ] usage_key = collected_block_structure.root_block_usage_key course_blocks = get_course_blocks( student, usage_key, transformers=transformers, collected_block_structure=collected_block_structure, include_has_scheduled_content=True) has_scheduled_content = course_blocks.get_xblock_field( usage_key, 'has_scheduled_content') # Get user_has_passing_grade data user_has_passing_grade = False if not student.is_anonymous: user_grade = course_grade.percent user_has_passing_grade = user_grade >= course.lowest_passing_grade descriptor = modulestore().get_course(course_key) grading_policy = descriptor.grading_policy verification_status = IDVerificationService.user_status(student) verification_link = None if verification_status['status'] is None or verification_status[ 'status'] == 'expired': verification_link = IDVerificationService.get_verify_location( course_id=course_key) elif verification_status['status'] == 'must_reverify': verification_link = IDVerificationService.get_verify_location( course_id=course_key) verification_data = { 'link': verification_link, 'status': verification_status['status'], 'status_date': verification_status['status_date'], } data = { 'username': username, 'end': course.end, 'user_has_passing_grade': user_has_passing_grade, 'certificate_data': get_cert_data(student, course, enrollment_mode, course_grade), 'completion_summary': get_course_blocks_completion_summary(course_key, student), 'course_grade': course_grade, 'has_scheduled_content': has_scheduled_content, 'section_scores': course_grade.chapter_grades.values(), 'enrollment_mode': enrollment_mode, 'grading_policy': grading_policy, 'studio_url': get_studio_url(course, 'settings/grading'), 'verification_data': verification_data, } context = self.get_serializer_context() context['staff_access'] = is_staff context['course_blocks'] = course_blocks context['course_key'] = course_key # course_overview and enrollment will be used by VerifiedModeSerializerMixin context['course_overview'] = course_overview context['enrollment'] = enrollment serializer = self.get_serializer_class()(data, context=context) return Response(serializer.data)