feat: use new flow with with financial assistance form

This commit is contained in:
AliAkbar
2022-04-15 16:25:41 +05:00
parent ce5f1bb343
commit 33bb739e50
5 changed files with 115 additions and 57 deletions

View File

@@ -127,21 +127,7 @@ class TestFinancialAssistanceViews(TestCase):
assert has_application is False
assert reason == response_data.get('content').get('message')
@ddt.data(
{
'status': status.HTTP_400_BAD_REQUEST,
'content': {'message': 'Invalid course id provided'},
'message': 'Invalid course id provided',
'created': False
},
{
'status': status.HTTP_200_OK,
'content': {'success': True},
'message': None,
'created': True
}
)
def test_create_financial_assistance_application(self, response_data):
def test_create_financial_assistance_application(self):
"""
Tests the functionality of create_financial_assistance_application which calls edx-financial-assistance backend
to create a new financial assistance application given a form data.
@@ -149,10 +135,26 @@ class TestFinancialAssistanceViews(TestCase):
test_form_data = {
'lms_user_id': self.user.id,
'course_id': self.test_course_id,
'income': '85K_TO_100K'
'income': '$85,000 - $100,000'
}
with patch.object(OAuthAPIClient, 'request') as oauth_mock:
oauth_mock.return_value = self._mock_response(response_data.get('status'), response_data.get('content'))
created, message = create_financial_assistance_application(form_data=test_form_data)
assert created is response_data.get('created')
assert message == response_data.get('message')
oauth_mock.return_value = self._mock_response(status.HTTP_200_OK, {'success': True})
response = create_financial_assistance_application(form_data=test_form_data)
assert response.status_code == status.HTTP_204_NO_CONTENT
def test_create_financial_assistance_application_bad_request(self):
"""
Tests the functionality of create_financial_assistance_application which calls edx-financial-assistance backend
to create a new financial assistance application given a form data.
"""
test_form_data = {
'lms_user_id': self.user.id,
'course_id': 'invalid_course_id',
'income': '$85,000 - $100,000'
}
error_response = {'message': 'Invalid course id provided'}
with patch.object(OAuthAPIClient, 'request') as oauth_mock:
oauth_mock.return_value = self._mock_response(status.HTTP_400_BAD_REQUEST, error_response)
response = create_financial_assistance_application(form_data=test_form_data)
assert response.status_code == status.HTTP_400_BAD_REQUEST
assert json.loads(response.content.decode('utf-8')) == error_response

View File

@@ -13,13 +13,12 @@ from urllib.parse import urlencode
from uuid import uuid4
import ddt
from capa.tests.response_xml_factory import \
MultipleChoiceResponseXMLFactory
from capa.tests.response_xml_factory import MultipleChoiceResponseXMLFactory
from completion.test_utils import CompletionWaffleTestMixin
from crum import set_current_request
from django.conf import settings
from django.contrib.auth.models import AnonymousUser
from django.http import Http404, HttpResponseBadRequest
from django.http import Http404, HttpResponse, HttpResponseBadRequest
from django.http.request import QueryDict
from django.test import RequestFactory, TestCase
from django.test.client import Client
@@ -31,24 +30,17 @@ from markupsafe import escape
from milestones.tests.utils import MilestonesTestCaseMixin
from opaque_keys.edx.keys import CourseKey, UsageKey
from pytz import UTC
from rest_framework import status
from web_fragments.fragment import Fragment
from xblock.core import XBlock
from xblock.fields import Scope, String
from xmodule.course_module import (
COURSE_VISIBILITY_PRIVATE,
COURSE_VISIBILITY_PUBLIC,
COURSE_VISIBILITY_PUBLIC_OUTLINE
)
from xmodule.course_module import COURSE_VISIBILITY_PRIVATE, COURSE_VISIBILITY_PUBLIC, COURSE_VISIBILITY_PUBLIC_OUTLINE
from xmodule.data import CertificatesDisplayBehaviors
from xmodule.graders import ShowCorrectness
from xmodule.modulestore import ModuleStoreEnum
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.tests.django_utils import CourseUserType, ModuleStoreTestCase, SharedModuleStoreTestCase
from xmodule.modulestore.tests.factories import (
CourseFactory,
ItemFactory,
check_mongo_calls
)
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory, check_mongo_calls
import lms.djangoapps.courseware.views.views as views
from common.djangoapps.course_modes.models import CourseMode
@@ -957,9 +949,9 @@ class ViewsTestCase(BaseViewsTestCase):
self.assertContains(response, str(course))
def _submit_financial_assistance_form(self, data):
def _submit_financial_assistance_form(self, data, submit_url='submit_financial_assistance_request'):
"""Submit a financial assistance request."""
url = reverse('submit_financial_assistance_request')
url = reverse(submit_url)
return self.client.post(url, json.dumps(data), content_type='application/json')
@patch.object(views, 'create_zendesk_ticket', return_value=200)
@@ -1020,15 +1012,33 @@ class ViewsTestCase(BaseViewsTestCase):
})
assert response.status_code == 500
@patch.object(
views, 'create_financial_assistance_application', return_value=HttpResponse(status=status.HTTP_204_NO_CONTENT)
)
def test_submit_financial_assistance_request_v2(self, create_application_mock):
form_data = {
'username': self.user.username,
'course': 'course-v1:test+TestX+Test_Course',
'income': '$25,000 - $40,000',
'reason_for_applying': "It's just basic chemistry, yo.",
'goals': "I don't know if it even matters, but... work with my hands, I guess.",
'effort': "I'm done, okay? You just give me my money, and you and I, we're done.",
'mktg-permission': False
}
response = self._submit_financial_assistance_form(
form_data, submit_url='submit_financial_assistance_request_v2'
)
assert response.status_code == status.HTTP_204_NO_CONTENT
@ddt.data(
({}, 400),
({'username': 'wwhite'}, 403),
({'username': 'dummy', 'course': 'bad course ID'}, 400)
)
@ddt.unpack
def test_submit_financial_assistance_errors(self, data, status):
def test_submit_financial_assistance_errors(self, data, response_status):
response = self._submit_financial_assistance_form(data)
assert response.status_code == status
assert response.status_code == response_status
def test_financial_assistance_login_required(self):
for url in (

View File

@@ -6,6 +6,7 @@ import hashlib
import logging
from django.conf import settings
from django.http import HttpResponse, HttpResponseBadRequest
from edx_rest_api_client.client import OAuthAPIClient
from oauth2_provider.models import Application
from pytz import utc # lint-amnesty, pylint: disable=wrong-import-order
@@ -180,20 +181,21 @@ def create_financial_assistance_application(form_data):
"income": <income_from_range>,
"learner_reasons": <TEST_LONG_STRING>,
"learner_goals": <TEST_LONG_STRING>,
"learner_plans": <TEST_LONG_STRING>
"learner_plans": <TEST_LONG_STRING>,
"allow_for_marketing": <Boolean>
}
TODO: marketing checkmark field will be added in the backend and needs to be updated here.
"""
response = _request_financial_assistance(
'POST', f"{settings.CREATE_FINANCIAL_ASSISTANCE_APPLICATION_URL}/", data=form_data
)
if response.status_code == status.HTTP_200_OK:
return True, None
return HttpResponse(status=status.HTTP_204_NO_CONTENT)
elif response.status_code == status.HTTP_400_BAD_REQUEST:
return False, response.json().get('message')
log.error(response.json().get('message'))
return HttpResponseBadRequest(response.content)
else:
log.error('%s %s', UNEXPECTED_ERROR_CREATE_APPLICATION, response.content)
return False, UNEXPECTED_ERROR_CREATE_APPLICATION
return HttpResponse(status=status.HTTP_500_INTERNAL_SERVER_ERROR)
def get_course_hash_value(course_key):

View File

@@ -44,15 +44,9 @@ from rest_framework.decorators import api_view, throttle_classes
from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle
from web_fragments.fragment import Fragment
from xmodule.course_module import (
COURSE_VISIBILITY_PUBLIC,
COURSE_VISIBILITY_PUBLIC_OUTLINE
)
from xmodule.course_module import COURSE_VISIBILITY_PUBLIC, COURSE_VISIBILITY_PUBLIC_OUTLINE
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import (
ItemNotFoundError,
NoPathToItem
)
from xmodule.modulestore.exceptions import ItemNotFoundError, NoPathToItem
from xmodule.tabs import CourseTabList
from xmodule.x_module import STUDENT_VIEW
@@ -72,6 +66,7 @@ from lms.djangoapps.course_goals.models import UserActivity
from lms.djangoapps.course_home_api.toggles import course_home_mfe_progress_tab_is_active
from lms.djangoapps.courseware.access import has_access, has_ccx_coach_role
from lms.djangoapps.courseware.access_utils import check_course_open_for_learner, check_public_access
from lms.djangoapps.courseware.config import ENABLE_NEW_FINANCIAL_ASSISTANCE_FLOW
from lms.djangoapps.courseware.courses import (
can_self_enroll_in_course,
course_open_for_self_enrollment,
@@ -90,13 +85,10 @@ from lms.djangoapps.courseware.exceptions import CourseAccessRedirect, Redirect
from lms.djangoapps.courseware.masquerade import is_masquerading_as_specific_student, setup_masquerade
from lms.djangoapps.courseware.model_data import FieldDataCache
from lms.djangoapps.courseware.models import BaseStudentModuleHistory, StudentModule
from lms.djangoapps.courseware.permissions import (
MASQUERADE_AS_STUDENT,
VIEW_COURSE_HOME,
VIEW_COURSEWARE,
)
from lms.djangoapps.courseware.permissions import MASQUERADE_AS_STUDENT, VIEW_COURSE_HOME, VIEW_COURSEWARE
from lms.djangoapps.courseware.toggles import course_is_invitation_only
from lms.djangoapps.courseware.user_state_client import DjangoXBlockUserStateClient
from lms.djangoapps.courseware.utils import create_financial_assistance_application
from lms.djangoapps.edxnotes.helpers import is_feature_enabled
from lms.djangoapps.experiments.utils import get_experiment_user_metadata_context
from lms.djangoapps.grades.api import CourseGradeFactory
@@ -111,7 +103,6 @@ from openedx.core.djangoapps.credit.api import (
is_credit_course,
is_user_eligible_for_credit
)
from openedx.core.lib.courses import get_course_by_id
from openedx.core.djangoapps.enrollments.api import add_enrollment
from openedx.core.djangoapps.enrollments.permissions import ENROLL_IN_COURSE
from openedx.core.djangoapps.models.course_details import CourseDetails
@@ -122,6 +113,7 @@ from openedx.core.djangoapps.site_configuration import helpers as configuration_
from openedx.core.djangoapps.util.user_messages import PageLevelMessages
from openedx.core.djangoapps.zendesk_proxy.utils import create_zendesk_ticket
from openedx.core.djangolib.markup import HTML, Text
from openedx.core.lib.courses import get_course_by_id
from openedx.core.lib.mobile_utils import is_request_from_mobile_app
from openedx.features.course_duration_limits.access import generate_course_expired_fragment
from openedx.features.course_experience import DISABLE_UNIFIED_COURSE_TAB_FLAG, course_home_url
@@ -1970,6 +1962,48 @@ def financial_assistance_request(request):
return HttpResponse(status=status.HTTP_204_NO_CONTENT)
@login_required
@require_POST
def financial_assistance_request_v2(request):
"""
Uses the new financial assistance application flow.
Creates a post request to edx-financial-assistance backend.
"""
try:
data = json.loads(request.body.decode('utf8'))
username = data['username']
# Simple sanity check that the session belongs to the user
# submitting an FA request
if request.user.username != username:
return HttpResponseForbidden()
lms_user_id = request.user.id
course_id = data['course']
income = data['income']
learner_reasons = data['reason_for_applying']
learner_goals = data['goals']
learner_plans = data['effort']
allowed_for_marketing = data['mktg-permission']
except ValueError:
# Thrown if JSON parsing fails
return HttpResponseBadRequest('Could not parse request JSON.')
except KeyError as err:
# Thrown if fields are missing
return HttpResponseBadRequest(f'The field {str(err)} is required.')
form_data = {
'lms_user_id': lms_user_id,
'course_id': course_id,
'income': income,
'learner_reasons': learner_reasons,
'learner_goals': learner_goals,
'learner_plans': learner_plans,
'allowed_for_marketing': allowed_for_marketing
}
return create_financial_assistance_application(form_data)
@login_required
def financial_assistance_form(request):
"""Render the financial assistance application form page."""
@@ -1982,6 +2016,11 @@ def financial_assistance_form(request):
annual_incomes = [
{'name': _(income), 'value': income} for income in incomes # lint-amnesty, pylint: disable=translation-of-non-string
]
if ENABLE_NEW_FINANCIAL_ASSISTANCE_FLOW.is_enabled():
submit_url = 'submit_financial_assistance_request_v2'
else:
submit_url = 'submit_financial_assistance_request'
return render_to_response('financial-assistance/apply.html', {
'header_text': _get_fa_header(FINANCIAL_ASSISTANCE_HEADER),
'student_faq_url': marketing_link('FAQ'),
@@ -1994,7 +2033,7 @@ def financial_assistance_form(request):
'name': user.profile.name,
'country': str(user.profile.country.name),
},
'submit_url': reverse('submit_financial_assistance_request'),
'submit_url': reverse(submit_url),
'fields': [
{
'name': 'course',

View File

@@ -943,6 +943,11 @@ if settings.FEATURES.get('ENABLE_FINANCIAL_ASSISTANCE_FORM'):
'financial-assistance/submit/',
courseware_views.financial_assistance_request,
name='submit_financial_assistance_request'
),
path(
'financial-assistance_v2/submit/',
courseware_views.financial_assistance_request_v2,
name='submit_financial_assistance_request_v2'
)
]