def test_update_verification_deadline_left_alone(self): """ When the course's verification deadline is set and an update request doesn't include it, we should take no action on it. """ verification_deadline = datetime(year=1915, month=5, day=7, tzinfo=pytz.utc) response, __ = self._get_update_response_and_expected_data(None, verification_deadline) self.assertEqual(VerificationDeadline.deadline_for_course(self.course.id), verification_deadline) verified_mode = CourseMode( mode_slug=u'verified', min_price=200, currency=u'USD', sku=u'ABC123', bulk_sku=u'BULK-ABC123', expiration_datetime=None ) updated_data = self._serialize_course(self.course, [verified_mode], None) # don't include the verification_deadline key in the PUT request updated_data.pop('verification_deadline', None) response = self.client.put(self.path, json.dumps(updated_data), content_type=JSON_CONTENT_TYPE) self.assertEqual(response.status_code, 200) self.assertEqual(VerificationDeadline.deadline_for_course(self.course.id), verification_deadline)
def test_deadline(self): """ Verify deadline is set to course end date by signal when changed. """ deadline = datetime.now(tz=UTC) - timedelta(days=7) VerificationDeadline.set_deadline(self.course.id, deadline) _listen_for_course_publish('store', self.course.id) self.assertEqual(VerificationDeadline.deadline_for_course(self.course.id), self.course.end)
def test_deadline(self): """ Verify deadline is set to course end date by signal when changed. """ deadline = now() - timedelta(days=7) VerificationDeadline.set_deadline(self.course.id, deadline) _listen_for_course_publish('store', self.course.id) assert VerificationDeadline.deadline_for_course( self.course.id) == self.course.end
def test_deadline(self): """ Verify deadline is set to course end date by signal when changed. """ deadline = datetime.now(tz=UTC) - timedelta(days=7) VerificationDeadline.set_deadline(self.course.id, deadline) _listen_for_course_publish('store', self.course.id) self.assertEqual( VerificationDeadline.deadline_for_course(self.course.id), self.course.end)
def test_update_remove_verification_deadline(self): """ Verify that verification deadlines can be removed through the API. """ verification_deadline = datetime(year=1915, month=5, day=7, tzinfo=pytz.utc) response, __ = self._get_update_response_and_expected_data(None, verification_deadline) self.assertEqual(VerificationDeadline.deadline_for_course(self.course.id), verification_deadline) verified_mode = CourseMode( mode_slug=u"verified", min_price=200, currency=u"USD", sku=u"ABC123", expiration_datetime=None ) updated_data = self._serialize_course(self.course, [verified_mode], None) updated_data["verification_deadline"] = None response = self.client.put(self.path, json.dumps(updated_data), content_type=JSON_CONTENT_TYPE) self.assertEqual(response.status_code, 200) self.assertIsNone(VerificationDeadline.deadline_for_course(self.course.id))
def test_update(self): """ Verify the view supports updating a course. """ # Sanity check: Ensure no verification deadline is set self.assertIsNone(VerificationDeadline.deadline_for_course(self.course.id)) # Generate the expected data verification_deadline = datetime(year=2020, month=12, day=31, tzinfo=pytz.utc) expiration_datetime = datetime.now(pytz.utc) response, expected = self._get_update_response_and_expected_data(expiration_datetime, verification_deadline) # Sanity check: The API should return HTTP status 200 for updates self.assertEqual(response.status_code, 200) # Verify the course and modes are returned as JSON actual = json.loads(response.content) self.assertEqual(actual, expected) # Verify the verification deadline is updated self.assertEqual(VerificationDeadline.deadline_for_course(self.course.id), verification_deadline)
def test_deadline_explicit(self): """ Verify deadline is unchanged by signal when explicitly set. """ deadline = now() - timedelta(days=7) VerificationDeadline.set_deadline(self.course.id, deadline, is_explicit=True) _listen_for_course_publish('store', self.course.id) actual_deadline = VerificationDeadline.deadline_for_course(self.course.id) self.assertNotEqual(actual_deadline, self.course.end) self.assertEqual(actual_deadline, deadline)
def test_update_verification_deadline_without_expiring_modes(self): """ Verify verification deadline can be set if no course modes expire. This accounts for the verified professional mode, which requires verification but should never expire. """ verification_deadline = datetime(year=1915, month=5, day=7, tzinfo=pytz.utc) response, __ = self._get_update_response_and_expected_data(None, verification_deadline) self.assertEqual(response.status_code, 200) self.assertEqual(VerificationDeadline.deadline_for_course(self.course.id), verification_deadline)
def test_update(self): """ Verify the view supports updating a course. """ # Sanity check: Ensure no verification deadline is set self.assertIsNone(VerificationDeadline.deadline_for_course(self.course.id)) # Generate the expected data verification_deadline = datetime(year=2020, month=12, day=31, tzinfo=pytz.utc) expiration_datetime = datetime.now(pytz.utc) response, expected = self._get_update_response_and_expected_data(expiration_datetime, verification_deadline) # Sanity check: The API should return HTTP status 200 for updates self.assertEqual(response.status_code, 200) # Verify the course and modes are returned as JSON actual = json.loads(response.content) self.assertEqual(actual, expected) # Verify the verification deadline is updated self.assertEqual(VerificationDeadline.deadline_for_course(self.course.id), verification_deadline)
def test_update_verification_deadline_without_expiring_modes(self): """ Verify verification deadline can be set if no course modes expire. This accounts for the verified professional mode, which requires verification but should never expire. """ verification_deadline = datetime(year=1915, month=5, day=7, tzinfo=pytz.utc) response, __ = self._get_update_response_and_expected_data(None, verification_deadline) self.assertEqual(response.status_code, 200) self.assertEqual(VerificationDeadline.deadline_for_course(self.course.id), verification_deadline)
def test_deadline_explicit(self): """ Verify deadline is unchanged by signal when explicitly set. """ deadline = datetime.now(tz=UTC) - timedelta(days=7) VerificationDeadline.set_deadline(self.course.id, deadline, is_explicit=True) _listen_for_course_publish('store', self.course.id) actual_deadline = VerificationDeadline.deadline_for_course(self.course.id) self.assertNotEqual(actual_deadline, self.course.end) self.assertEqual(actual_deadline, deadline)
def _serialize_course(cls, course, modes=None, verification_deadline=None): """ Serializes a course to a Python dict. """ modes = modes or [] verification_deadline = verification_deadline or VerificationDeadline.deadline_for_course(course.id) return { u'id': unicode(course.id), u'name': unicode(course.display_name), u'verification_deadline': cls._serialize_datetime(verification_deadline), u'modes': [cls._serialize_course_mode(mode) for mode in modes] }
def test_update(self): """ Verify the view supports updating a course. """ # Sanity check: Ensure no verification deadline is set assert VerificationDeadline.deadline_for_course(self.course.id) is None # Generate the expected data now = datetime.now(pytz.utc) verification_deadline = now + timedelta(days=1) expiration_datetime = now response, expected = self._get_update_response_and_expected_data(expiration_datetime, verification_deadline) # Sanity check: The API should return HTTP status 200 for updates assert response.status_code == 200 # Verify the course and modes are returned as JSON actual = json.loads(response.content.decode('utf-8')) assert actual == expected # Verify the verification deadline is updated assert VerificationDeadline.deadline_for_course(self.course.id) == verification_deadline
def _serialize_course(cls, course, modes=None, verification_deadline=None): """ Serializes a course to a Python dict. """ modes = modes or [] verification_deadline = verification_deadline or VerificationDeadline.deadline_for_course(course.id) return { u'id': six.text_type(course.id), u'name': six.text_type(course.display_name), u'verification_deadline': cls._serialize_datetime(verification_deadline), u'modes': [cls._serialize_course_mode(mode) for mode in modes] }
def test_disable_verification_deadline(self): # Configure a verification deadline for the course VerificationDeadline.set_deadline(self.course.id, self.VERIFICATION_DEADLINE) # Create the course mode Django admin form form = self._admin_form("verified", upgrade_deadline=self.UPGRADE_DEADLINE) # Use the form to disable the verification deadline self._set_form_verification_deadline(form, None) form.save() # Check that the deadline was disabled self.assertIs(VerificationDeadline.deadline_for_course(self.course.id), None)
def test_disable_verification_deadline(self): # Configure a verification deadline for the course VerificationDeadline.set_deadline(self.course.id, self.VERIFICATION_DEADLINE) # Create the course mode Django admin form form = self._admin_form("verified", upgrade_deadline=self.UPGRADE_DEADLINE) # Use the form to disable the verification deadline self._set_form_verification_deadline(form, None) form.save() # Check that the deadline was disabled self.assertIs(VerificationDeadline.deadline_for_course(self.course.id), None)
def test_update_remove_verification_deadline(self): """ Verify that verification deadlines can be removed through the API. """ verification_deadline = datetime(year=1915, month=5, day=7, tzinfo=pytz.utc) response, __ = self._get_update_response_and_expected_data(None, verification_deadline) self.assertEqual(VerificationDeadline.deadline_for_course(self.course.id), verification_deadline) verified_mode = CourseMode( mode_slug=u'verified', min_price=200, currency=u'USD', sku=u'ABC123', bulk_sku=u'BULK-ABC123', expiration_datetime=None ) updated_data = self._serialize_course(self.course, [verified_mode], None) updated_data['verification_deadline'] = None response = self.client.put(self.path, json.dumps(updated_data), content_type=JSON_CONTENT_TYPE) self.assertEqual(response.status_code, 200) self.assertIsNone(VerificationDeadline.deadline_for_course(self.course.id))
def get(cls, course_id): """ Retrieve a single course. """ try: course_id = CourseKey.from_string(str(course_id)) except InvalidKeyError: log.debug('[%s] is not a valid course key.', course_id) raise ValueError # lint-amnesty, pylint: disable=raise-missing-from course_modes = CourseMode.objects.filter(course_id=course_id) if course_modes: verification_deadline = VerificationDeadline.deadline_for_course(course_id) return cls(course_id, list(course_modes), verification_deadline=verification_deadline) return None
def get(cls, course_id): """ Retrieve a single course. """ try: course_id = CourseKey.from_string(unicode(course_id)) except InvalidKeyError: log.debug('[%s] is not a valid course key.', course_id) raise ValueError course_modes = CourseMode.objects.filter(course_id=course_id) if course_modes: verification_deadline = VerificationDeadline.deadline_for_course(course_id) return cls(course_id, list(course_modes), verification_deadline=verification_deadline) return None
def get(cls, course_id): """ Retrieve a single course. """ try: course_id = CourseKey.from_string(six.text_type(course_id)) except InvalidKeyError: log.debug(u'[%s] is not a valid course key.', course_id) raise ValueError course_modes = CourseMode.objects.filter(course_id=course_id) if course_modes: verification_deadline = VerificationDeadline.deadline_for_course(course_id) return cls(course_id, list(course_modes), verification_deadline=verification_deadline) return None
def test_set_verification_deadline(self, course_mode): # Configure a verification deadline for the course VerificationDeadline.set_deadline(self.course.id, self.VERIFICATION_DEADLINE) # Create the course mode Django admin form form = self._admin_form(course_mode) # Update the verification deadline form data # We need to set the date and time fields separately, since they're # displayed as separate widgets in the form. new_deadline = (self.VERIFICATION_DEADLINE + timedelta(days=1)).replace(microsecond=0) self._set_form_verification_deadline(form, new_deadline) form.save() # Check that the deadline was updated updated_deadline = VerificationDeadline.deadline_for_course(self.course.id) self.assertEqual(updated_deadline, new_deadline)
def test_set_verification_deadline(self, course_mode): # Configure a verification deadline for the course VerificationDeadline.set_deadline(self.course.id, self.VERIFICATION_DEADLINE) # Create the course mode Django admin form form = self._admin_form(course_mode) # Update the verification deadline form data # We need to set the date and time fields separately, since they're # displayed as separate widgets in the form. new_deadline = (self.VERIFICATION_DEADLINE + timedelta(days=1)).replace(microsecond=0) self._set_form_verification_deadline(form, new_deadline) form.save() # Check that the deadline was updated updated_deadline = VerificationDeadline.deadline_for_course(self.course.id) self.assertEqual(updated_deadline, new_deadline)
def include_verified_mode_info(enrollment_data, course_key): """ Add information about the verified mode for the given `course_key`, if that course has a verified mode. Args: enrollment_data (dict): Dictionary representing a single enrollment. course_key (CourseKey): The course which this enrollment belongs to. Returns: None """ course_modes = enrollment_data['course_modes'] for mode in course_modes: if mode['slug'] == CourseMode.VERIFIED: enrollment_data['verified_price'] = mode['min_price'] enrollment_data['verified_upgrade_deadline'] = mode['expiration_datetime'] enrollment_data['verification_deadline'] = VerificationDeadline.deadline_for_course(course_key)
def include_verified_mode_info(enrollment_data, course_key): """ Add information about the verified mode for the given `course_key`, if that course has a verified mode. Args: enrollment_data (dict): Dictionary representing a single enrollment. course_key (CourseKey): The course which this enrollment belongs to. Returns: None """ course_modes = enrollment_data['course_modes'] for mode in course_modes: if mode['slug'] == CourseMode.VERIFIED: enrollment_data['verified_price'] = mode['min_price'] enrollment_data['verified_upgrade_deadline'] = mode['expiration_datetime'] enrollment_data['verification_deadline'] = VerificationDeadline.deadline_for_course(course_key)
def date(self): return VerificationDeadline.deadline_for_course(self.course_id)
def get(self, request, course_id, always_show_payment=False, current_step=None, message=FIRST_TIME_VERIFY_MSG): """ Render the payment and verification flow. Arguments: request (HttpRequest): The request object. course_id (unicode): The ID of the course the user is trying to enroll in. Keyword Arguments: always_show_payment (bool): If True, show the payment steps even if the user has already paid. This is useful for users returning to the flow after paying. current_step (string): The current step in the flow. message (string): The messaging to display. Returns: HttpResponse Raises: Http404: The course does not exist or does not have a verified mode. """ # Parse the course key # The URL regex should guarantee that the key format is valid. course_key = CourseKey.from_string(course_id) course = modulestore().get_course(course_key) # Verify that the course exists if course is None: log.warn(u"Could not find course with ID %s.", course_id) raise Http404 # Check whether the user has access to this course # based on country access rules. redirect_url = embargo_api.redirect_if_blocked( course_key, user=request.user, ip_address=get_ip(request), url=request.path) if redirect_url: return redirect(redirect_url) # If the verification deadline has passed # then show the user a message that he/she can't verify. # # We're making the assumptions (enforced in Django admin) that: # # 1) Only verified modes have verification deadlines. # # 2) If set, verification deadlines are always AFTER upgrade deadlines, because why would you # let someone upgrade into a verified track if they can't complete verification? # verification_deadline = VerificationDeadline.deadline_for_course( course.id) response = self._response_if_deadline_passed( course, self.VERIFICATION_DEADLINE, verification_deadline) if response is not None: log.info(u"Verification deadline for '%s' has passed.", course.id) return response # Retrieve the relevant course mode for the payment/verification flow. # # WARNING: this is technical debt! A much better way to do this would be to # separate out the payment flow and use the product SKU to figure out what # the user is trying to purchase. # # Nonetheless, for the time being we continue to make the really ugly assumption # that at some point there was a paid course mode we can query for the price. relevant_course_mode = self._get_paid_mode(course_key) # If we can find a relevant course mode, then log that we're entering the flow # Otherwise, this course does not support payment/verification, so respond with a 404. if relevant_course_mode is not None: if CourseMode.is_verified_mode(relevant_course_mode): log.info( u"Entering payment and verification flow for user '%s', course '%s', with current step '%s'.", request.user.id, course_id, current_step) else: log.info( u"Entering payment flow for user '%s', course '%s', with current step '%s'", request.user.id, course_id, current_step) else: # Otherwise, there has never been a verified/paid mode, # so return a page not found response. log.warn( u"No paid/verified course mode found for course '%s' for verification/payment flow request", course_id) raise Http404 # If the user is trying to *pay* and the upgrade deadline has passed, # then they shouldn't be able to enter the flow. # # NOTE: This should match the availability dates used by the E-Commerce service # to determine whether a user can purchase a product. The idea is that if the service # won't fulfill the order, we shouldn't even let the user get into the payment flow. # user_is_trying_to_pay = message in [ self.FIRST_TIME_VERIFY_MSG, self.UPGRADE_MSG ] if user_is_trying_to_pay: upgrade_deadline = relevant_course_mode.expiration_datetime response = self._response_if_deadline_passed( course, self.UPGRADE_DEADLINE, upgrade_deadline) if response is not None: log.info(u"Upgrade deadline for '%s' has passed.", course.id) return response # Check whether the user has verified, paid, and enrolled. # A user is considered "paid" if he or she has an enrollment # with a paid course mode (such as "verified"). # For this reason, every paid user is enrolled, but not # every enrolled user is paid. # If the course mode is not verified(i.e only paid) then already_verified is always True already_verified = (self._check_already_verified( request.user) if CourseMode.is_verified_mode(relevant_course_mode) else True) already_paid, is_enrolled = self._check_enrollment( request.user, course_key) # Redirect the user to a more appropriate page if the # messaging won't make sense based on the user's # enrollment / payment / verification status. sku_to_use = relevant_course_mode.sku purchase_workflow = request.GET.get('purchase_workflow', 'single') if purchase_workflow == 'bulk' and relevant_course_mode.bulk_sku: sku_to_use = relevant_course_mode.bulk_sku redirect_response = self._redirect_if_necessary( message, already_verified, already_paid, is_enrolled, course_key, user_is_trying_to_pay, request.user, sku_to_use) if redirect_response is not None: return redirect_response display_steps = self._display_steps(always_show_payment, already_verified, already_paid, relevant_course_mode) # Override the actual value if account activation has been disabled # Also see the reference to this parameter in context dictionary further down user_is_active = self._get_user_active_status(request.user) requirements = self._requirements(display_steps, user_is_active) if current_step is None: current_step = display_steps[0]['name'] # Allow the caller to skip the first page # This is useful if we want the user to be able to # use the "back" button to return to the previous step. # This parameter should only work for known skip-able steps if request.GET.get( 'skip-first-step') and current_step in self.SKIP_STEPS: display_step_names = [step['name'] for step in display_steps] current_step_idx = display_step_names.index(current_step) if (current_step_idx + 1) < len(display_steps): current_step = display_steps[current_step_idx + 1]['name'] courseware_url = "" if not course.start or course.start < datetime.datetime.today( ).replace(tzinfo=UTC): courseware_url = reverse('course_root', kwargs={'course_id': unicode(course_key)}) full_name = (request.user.profile.name if request.user.profile.name else "") # If the user set a contribution amount on another page, # use that amount to pre-fill the price selection form. contribution_amount = request.session.get('donation_for_course', {}).get( unicode(course_key), '') # Remember whether the user is upgrading # so we can fire an analytics event upon payment. request.session['attempting_upgrade'] = (message == self.UPGRADE_MSG) # Determine the photo verification status verification_good_until = self._verification_valid_until(request.user) # get available payment processors if relevant_course_mode.sku: # transaction will be conducted via ecommerce service processors = ecommerce_api_client( request.user).payment.processors.get() else: # transaction will be conducted using legacy shopping cart processors = [settings.CC_PROCESSOR_NAME] # Render the top-level page context = { 'contribution_amount': contribution_amount, 'course': course, 'course_key': unicode(course_key), 'checkpoint_location': request.GET.get('checkpoint'), 'course_mode': relevant_course_mode, 'courseware_url': courseware_url, 'current_step': current_step, 'disable_courseware_js': True, 'display_steps': display_steps, 'is_active': json.dumps(user_is_active), 'user_email': request.user.email, 'message_key': message, 'platform_name': configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME), 'processors': processors, 'requirements': requirements, 'user_full_name': full_name, 'verification_deadline': verification_deadline or "", 'already_verified': already_verified, 'verification_good_until': verification_good_until, 'capture_sound': staticfiles_storage.url("audio/camera_capture.wav"), 'nav_hidden': True, 'is_ab_testing': 'begin-flow' in request.path, } return render_to_response("verify_student/pay_and_verify.html", context)
def test_no_deadline(self): """ Verify the signal sets deadline to course end when no deadline exists.""" _listen_for_course_publish('store', self.course.id) assert VerificationDeadline.deadline_for_course( self.course.id) == self.course.end
def get( self, request, course_id, always_show_payment=False, current_step=None, message=FIRST_TIME_VERIFY_MSG ): """ Render the payment and verification flow. Arguments: request (HttpRequest): The request object. course_id (unicode): The ID of the course the user is trying to enroll in. Keyword Arguments: always_show_payment (bool): If True, show the payment steps even if the user has already paid. This is useful for users returning to the flow after paying. current_step (string): The current step in the flow. message (string): The messaging to display. Returns: HttpResponse Raises: Http404: The course does not exist or does not have a verified mode. """ # Parse the course key # The URL regex should guarantee that the key format is valid. course_key = CourseKey.from_string(course_id) course = modulestore().get_course(course_key) # Verify that the course exists if course is None: log.warn(u"Could not find course with ID %s.", course_id) raise Http404 # Check whether the user has access to this course # based on country access rules. redirect_url = embargo_api.redirect_if_blocked( course_key, user=request.user, ip_address=get_ip(request), url=request.path ) if redirect_url: return redirect(redirect_url) # If the verification deadline has passed # then show the user a message that he/she can't verify. # # We're making the assumptions (enforced in Django admin) that: # # 1) Only verified modes have verification deadlines. # # 2) If set, verification deadlines are always AFTER upgrade deadlines, because why would you # let someone upgrade into a verified track if they can't complete verification? # verification_deadline = VerificationDeadline.deadline_for_course(course.id) response = self._response_if_deadline_passed(course, self.VERIFICATION_DEADLINE, verification_deadline) if response is not None: log.info(u"Verification deadline for '%s' has passed.", course.id) return response # Retrieve the relevant course mode for the payment/verification flow. # # WARNING: this is technical debt! A much better way to do this would be to # separate out the payment flow and use the product SKU to figure out what # the user is trying to purchase. # # Nonetheless, for the time being we continue to make the really ugly assumption # that at some point there was a paid course mode we can query for the price. relevant_course_mode = self._get_paid_mode(course_key) # If we can find a relevant course mode, then log that we're entering the flow # Otherwise, this course does not support payment/verification, so respond with a 404. if relevant_course_mode is not None: if CourseMode.is_verified_mode(relevant_course_mode): log.info( u"Entering payment and verification flow for user '%s', course '%s', with current step '%s'.", request.user.id, course_id, current_step ) else: log.info( u"Entering payment flow for user '%s', course '%s', with current step '%s'", request.user.id, course_id, current_step ) else: # Otherwise, there has never been a verified/paid mode, # so return a page not found response. log.warn( u"No paid/verified course mode found for course '%s' for verification/payment flow request", course_id ) raise Http404 # If the user is trying to *pay* and the upgrade deadline has passed, # then they shouldn't be able to enter the flow. # # NOTE: This should match the availability dates used by the E-Commerce service # to determine whether a user can purchase a product. The idea is that if the service # won't fulfill the order, we shouldn't even let the user get into the payment flow. # user_is_trying_to_pay = message in [self.FIRST_TIME_VERIFY_MSG, self.UPGRADE_MSG] if user_is_trying_to_pay: upgrade_deadline = relevant_course_mode.expiration_datetime response = self._response_if_deadline_passed(course, self.UPGRADE_DEADLINE, upgrade_deadline) if response is not None: log.info(u"Upgrade deadline for '%s' has passed.", course.id) return response # Check whether the user has verified, paid, and enrolled. # A user is considered "paid" if he or she has an enrollment # with a paid course mode (such as "verified"). # For this reason, every paid user is enrolled, but not # every enrolled user is paid. # If the course mode is not verified(i.e only paid) then already_verified is always True already_verified = ( self._check_already_verified(request.user) if CourseMode.is_verified_mode(relevant_course_mode) else True ) already_paid, is_enrolled = self._check_enrollment(request.user, course_key) # Redirect the user to a more appropriate page if the # messaging won't make sense based on the user's # enrollment / payment / verification status. sku_to_use = relevant_course_mode.sku purchase_workflow = request.GET.get('purchase_workflow', 'single') if purchase_workflow == 'bulk' and relevant_course_mode.bulk_sku: sku_to_use = relevant_course_mode.bulk_sku redirect_response = self._redirect_if_necessary( message, already_verified, already_paid, is_enrolled, course_key, user_is_trying_to_pay, request.user, sku_to_use ) if redirect_response is not None: return redirect_response display_steps = self._display_steps( always_show_payment, already_verified, already_paid, relevant_course_mode ) # Override the actual value if account activation has been disabled # Also see the reference to this parameter in context dictionary further down user_is_active = self._get_user_active_status(request.user) requirements = self._requirements(display_steps, user_is_active) if current_step is None: current_step = display_steps[0]['name'] # Allow the caller to skip the first page # This is useful if we want the user to be able to # use the "back" button to return to the previous step. # This parameter should only work for known skip-able steps if request.GET.get('skip-first-step') and current_step in self.SKIP_STEPS: display_step_names = [step['name'] for step in display_steps] current_step_idx = display_step_names.index(current_step) if (current_step_idx + 1) < len(display_steps): current_step = display_steps[current_step_idx + 1]['name'] courseware_url = "" if not course.start or course.start < datetime.datetime.today().replace(tzinfo=UTC): courseware_url = reverse( 'course_root', kwargs={'course_id': unicode(course_key)} ) full_name = ( request.user.profile.name if request.user.profile.name else "" ) # If the user set a contribution amount on another page, # use that amount to pre-fill the price selection form. contribution_amount = request.session.get( 'donation_for_course', {} ).get(unicode(course_key), '') # Remember whether the user is upgrading # so we can fire an analytics event upon payment. request.session['attempting_upgrade'] = (message == self.UPGRADE_MSG) # Determine the photo verification status verification_good_until = self._verification_valid_until(request.user) # get available payment processors if relevant_course_mode.sku: # transaction will be conducted via ecommerce service processors = ecommerce_api_client(request.user).payment.processors.get() else: # transaction will be conducted using legacy shopping cart processors = [settings.CC_PROCESSOR_NAME] # Render the top-level page context = { 'contribution_amount': contribution_amount, 'course': course, 'course_key': unicode(course_key), 'checkpoint_location': request.GET.get('checkpoint'), 'course_mode': relevant_course_mode, 'courseware_url': courseware_url, 'current_step': current_step, 'disable_courseware_js': True, 'display_steps': display_steps, 'is_active': json.dumps(user_is_active), 'user_email': request.user.email, 'message_key': message, 'platform_name': configuration_helpers.get_value('PLATFORM_NAME', settings.PLATFORM_NAME), 'processors': processors, 'requirements': requirements, 'user_full_name': full_name, 'verification_deadline': verification_deadline or "", 'already_verified': already_verified, 'verification_good_until': verification_good_until, 'capture_sound': staticfiles_storage.url("audio/camera_capture.wav"), 'nav_hidden': True, 'is_ab_testing': 'begin-flow' in request.path, } return render_to_response("verify_student/pay_and_verify.html", context)
def date(self): # lint-amnesty, pylint: disable=invalid-overridden-method return VerificationDeadline.deadline_for_course(self.course_id)
def test_no_deadline(self): """ Verify the signal sets deadline to course end when no deadline exists.""" _listen_for_course_publish('store', self.course.id) self.assertEqual(VerificationDeadline.deadline_for_course(self.course.id), self.course.end)
def date(self): return VerificationDeadline.deadline_for_course(self.course.id)