Commit d1da0445 by noraiz-anwar

Throw error if messaging queue is not available

parent 3e7243ea
...@@ -52,7 +52,11 @@ from lms.djangoapps.instructor.views.api import ( ...@@ -52,7 +52,11 @@ from lms.djangoapps.instructor.views.api import (
generate_unique_password, generate_unique_password,
require_finance_admin require_finance_admin
) )
from lms.djangoapps.instructor_task.api_helper import AlreadyRunningError from lms.djangoapps.instructor_task.api_helper import (
AlreadyRunningError,
QueueConnectionError,
generate_already_running_error_message
)
from openedx.core.djangoapps.course_groups.cohorts import set_course_cohorted from openedx.core.djangoapps.course_groups.cohorts import set_course_cohorted
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin
...@@ -143,6 +147,7 @@ REPORTS_DATA = ( ...@@ -143,6 +147,7 @@ REPORTS_DATA = (
EXECUTIVE_SUMMARY_DATA = ( EXECUTIVE_SUMMARY_DATA = (
{ {
'report_type': 'executive summary', 'report_type': 'executive summary',
'task_type': 'exec_summary_report',
'instructor_api_endpoint': 'get_exec_summary_report', 'instructor_api_endpoint': 'get_exec_summary_report',
'task_api_endpoint': 'lms.djangoapps.instructor_task.api.submit_executive_summary_report', 'task_api_endpoint': 'lms.djangoapps.instructor_task.api.submit_executive_summary_report',
'extra_instructor_api_kwargs': {} 'extra_instructor_api_kwargs': {}
...@@ -244,7 +249,16 @@ def view_alreadyrunningerror(request): # pylint: disable=unused-argument ...@@ -244,7 +249,16 @@ def view_alreadyrunningerror(request): # pylint: disable=unused-argument
raise AlreadyRunningError() raise AlreadyRunningError()
@common_exceptions_400
def view_queue_connection_error(request): # pylint: disable=unused-argument
"""
A dummy view that raises a QueueConnectionError exception.
"""
raise QueueConnectionError()
@attr(shard=1) @attr(shard=1)
@ddt.ddt
class TestCommonExceptions400(TestCase): class TestCommonExceptions400(TestCase):
""" """
Testing the common_exceptions_400 decorator. Testing the common_exceptions_400 decorator.
...@@ -269,21 +283,29 @@ class TestCommonExceptions400(TestCase): ...@@ -269,21 +283,29 @@ class TestCommonExceptions400(TestCase):
self.request.is_ajax.return_value = True self.request.is_ajax.return_value = True
resp = view_user_doesnotexist(self.request) # pylint: disable=assignment-from-no-return resp = view_user_doesnotexist(self.request) # pylint: disable=assignment-from-no-return
self.assertEqual(resp.status_code, 400) self.assertEqual(resp.status_code, 400)
result = json.loads(resp.content) self.assertIn("User does not exist", resp.content)
self.assertIn("User does not exist", result["error"])
def test_alreadyrunningerror(self): def test_alreadyrunningerror(self):
self.request.is_ajax.return_value = False self.request.is_ajax.return_value = False
resp = view_alreadyrunningerror(self.request) # pylint: disable=assignment-from-no-return resp = view_alreadyrunningerror(self.request) # pylint: disable=assignment-from-no-return
self.assertEqual(resp.status_code, 400) self.assertEqual(resp.status_code, 400)
self.assertIn("Task is already running", resp.content) self.assertIn("Requested task is already running", resp.content)
def test_alreadyrunningerror_ajax(self): def test_alreadyrunningerror_ajax(self):
self.request.is_ajax.return_value = True self.request.is_ajax.return_value = True
resp = view_alreadyrunningerror(self.request) # pylint: disable=assignment-from-no-return resp = view_alreadyrunningerror(self.request) # pylint: disable=assignment-from-no-return
self.assertEqual(resp.status_code, 400) self.assertEqual(resp.status_code, 400)
result = json.loads(resp.content) self.assertIn("Requested task is already running", resp.content)
self.assertIn("Task is already running", result["error"])
@ddt.data(True, False)
def test_queue_connection_error(self, is_ajax):
"""
Tests that QueueConnectionError exception is handled in common_exception_400.
"""
self.request.is_ajax.return_value = is_ajax
resp = view_queue_connection_error(self.request) # pylint: disable=assignment-from-no-return
self.assertEqual(resp.status_code, 400)
self.assertIn('Error occured. Please try again later', resp.content)
@attr(shard=1) @attr(shard=1)
...@@ -401,6 +423,7 @@ class TestInstructorAPIDenyLevels(SharedModuleStoreTestCase, LoginEnrollmentTest ...@@ -401,6 +423,7 @@ class TestInstructorAPIDenyLevels(SharedModuleStoreTestCase, LoginEnrollmentTest
('get_proctored_exam_results', {}), ('get_proctored_exam_results', {}),
('get_problem_responses', {}), ('get_problem_responses', {}),
('export_ora2_data', {}), ('export_ora2_data', {}),
] ]
# Endpoints that only Instructors can access # Endpoints that only Instructors can access
self.instructor_level_endpoints = [ self.instructor_level_endpoints = [
...@@ -2680,14 +2703,15 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment ...@@ -2680,14 +2703,15 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
'get_problem_responses', 'get_problem_responses',
kwargs={'course_id': unicode(self.course.id)} kwargs={'course_id': unicode(self.course.id)}
) )
task_type = 'problem_responses_csv'
already_running_status = generate_already_running_error_message(task_type)
with patch('lms.djangoapps.instructor_task.api.submit_calculate_problem_responses_csv') as submit_task_function: with patch('lms.djangoapps.instructor_task.api.submit_calculate_problem_responses_csv') as submit_task_function:
error = AlreadyRunningError() error = AlreadyRunningError(already_running_status)
submit_task_function.side_effect = error submit_task_function.side_effect = error
response = self.client.post(url, {}) response = self.client.post(url, {})
res_json = json.loads(response.content)
self.assertIn('status', res_json) self.assertEqual(response.status_code, 400)
self.assertIn('already in progress', res_json['status']) self.assertIn(already_running_status, response.content)
def test_get_students_features(self): def test_get_students_features(self):
""" """
...@@ -2757,17 +2781,16 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment ...@@ -2757,17 +2781,16 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
) )
# Successful case: # Successful case:
response = self.client.post(url, {}) response = self.client.post(url, {})
res_json = json.loads(response.content) self.assertEqual(response.status_code, 200)
self.assertIn('status', res_json)
self.assertNotIn('currently being created', res_json['status'])
# CSV generation already in progress: # CSV generation already in progress:
task_type = 'may_enroll_info_csv'
already_running_status = generate_already_running_error_message(task_type)
with patch('lms.djangoapps.instructor_task.api.submit_calculate_may_enroll_csv') as submit_task_function: with patch('lms.djangoapps.instructor_task.api.submit_calculate_may_enroll_csv') as submit_task_function:
error = AlreadyRunningError() error = AlreadyRunningError(already_running_status)
submit_task_function.side_effect = error submit_task_function.side_effect = error
response = self.client.post(url, {}) response = self.client.post(url, {})
res_json = json.loads(response.content) self.assertEqual(response.status_code, 400)
self.assertIn('status', res_json) self.assertIn(already_running_status, response.content)
self.assertIn('currently being created', res_json['status'])
def test_get_student_exam_results(self): def test_get_student_exam_results(self):
""" """
...@@ -2778,19 +2801,19 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment ...@@ -2778,19 +2801,19 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
'get_proctored_exam_results', 'get_proctored_exam_results',
kwargs={'course_id': unicode(self.course.id)} kwargs={'course_id': unicode(self.course.id)}
) )
# Successful case: # Successful case:
response = self.client.post(url, {}) response = self.client.post(url, {})
res_json = json.loads(response.content) self.assertEqual(response.status_code, 200)
self.assertIn('status', res_json)
self.assertNotIn('currently being created', res_json['status'])
# CSV generation already in progress: # CSV generation already in progress:
task_type = 'proctored_exam_results_report'
already_running_status = generate_already_running_error_message(task_type)
with patch('lms.djangoapps.instructor_task.api.submit_proctored_exam_results_report') as submit_task_function: with patch('lms.djangoapps.instructor_task.api.submit_proctored_exam_results_report') as submit_task_function:
error = AlreadyRunningError() error = AlreadyRunningError(already_running_status)
submit_task_function.side_effect = error submit_task_function.side_effect = error
response = self.client.post(url, {}) response = self.client.post(url, {})
res_json = json.loads(response.content) self.assertEqual(response.status_code, 400)
self.assertIn('status', res_json) self.assertIn(already_running_status, response.content)
self.assertIn('currently being created', res_json['status'])
def test_access_course_finance_admin_with_invalid_course_key(self): def test_access_course_finance_admin_with_invalid_course_key(self):
""" """
...@@ -3045,10 +3068,11 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment ...@@ -3045,10 +3068,11 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
def test_executive_summary_report_success( def test_executive_summary_report_success(
self, self,
report_type, report_type,
task_type,
instructor_api_endpoint, instructor_api_endpoint,
task_api_endpoint, task_api_endpoint,
extra_instructor_api_kwargs extra_instructor_api_kwargs
): ): # pylint: disable=unused-argument
kwargs = {'course_id': unicode(self.course.id)} kwargs = {'course_id': unicode(self.course.id)}
kwargs.update(extra_instructor_api_kwargs) kwargs.update(extra_instructor_api_kwargs)
url = reverse(instructor_api_endpoint, kwargs=kwargs) url = reverse(instructor_api_endpoint, kwargs=kwargs)
...@@ -3066,6 +3090,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment ...@@ -3066,6 +3090,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
def test_executive_summary_report_already_running( def test_executive_summary_report_already_running(
self, self,
report_type, report_type,
task_type,
instructor_api_endpoint, instructor_api_endpoint,
task_api_endpoint, task_api_endpoint,
extra_instructor_api_kwargs extra_instructor_api_kwargs
...@@ -3075,14 +3100,12 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment ...@@ -3075,14 +3100,12 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
url = reverse(instructor_api_endpoint, kwargs=kwargs) url = reverse(instructor_api_endpoint, kwargs=kwargs)
CourseFinanceAdminRole(self.course.id).add_users(self.instructor) CourseFinanceAdminRole(self.course.id).add_users(self.instructor)
already_running_status = generate_already_running_error_message(task_type)
with patch(task_api_endpoint) as mock: with patch(task_api_endpoint) as mock:
mock.side_effect = AlreadyRunningError() mock.side_effect = AlreadyRunningError(already_running_status)
response = self.client.post(url, {}) response = self.client.post(url, {})
already_running_status = "The {report_type} report is currently being created." \
" To view the status of the report, see Pending Tasks below." \ self.assertEqual(response.status_code, 400)
" You will be able to download the report" \
" when it is" \
" complete.".format(report_type=report_type)
self.assertIn(already_running_status, response.content) self.assertIn(already_running_status, response.content)
def test_get_ora2_responses_success(self): def test_get_ora2_responses_success(self):
...@@ -3091,16 +3114,19 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment ...@@ -3091,16 +3114,19 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
with patch('lms.djangoapps.instructor_task.api.submit_export_ora2_data') as mock_submit_ora2_task: with patch('lms.djangoapps.instructor_task.api.submit_export_ora2_data') as mock_submit_ora2_task:
mock_submit_ora2_task.return_value = True mock_submit_ora2_task.return_value = True
response = self.client.post(url, {}) response = self.client.post(url, {})
success_status = "The ORA data report is being generated." success_status = "The ORA data report is being created."
self.assertIn(success_status, response.content) self.assertIn(success_status, response.content)
def test_get_ora2_responses_already_running(self): def test_get_ora2_responses_already_running(self):
url = reverse('export_ora2_data', kwargs={'course_id': unicode(self.course.id)}) url = reverse('export_ora2_data', kwargs={'course_id': unicode(self.course.id)})
task_type = 'export_ora2_data'
already_running_status = generate_already_running_error_message(task_type)
with patch('lms.djangoapps.instructor_task.api.submit_export_ora2_data') as mock_submit_ora2_task: with patch('lms.djangoapps.instructor_task.api.submit_export_ora2_data') as mock_submit_ora2_task:
mock_submit_ora2_task.side_effect = AlreadyRunningError() mock_submit_ora2_task.side_effect = AlreadyRunningError(already_running_status)
response = self.client.post(url, {}) response = self.client.post(url, {})
already_running_status = "An ORA data report generation task is already in progress."
self.assertEqual(response.status_code, 400)
self.assertIn(already_running_status, response.content) self.assertIn(already_running_status, response.content)
def test_get_student_progress_url(self): def test_get_student_progress_url(self):
......
...@@ -71,7 +71,7 @@ from lms.djangoapps.instructor.enrollment import ( ...@@ -71,7 +71,7 @@ from lms.djangoapps.instructor.enrollment import (
from lms.djangoapps.instructor.views import INVOICE_KEY from lms.djangoapps.instructor.views import INVOICE_KEY
from lms.djangoapps.instructor.views.instructor_task_helpers import extract_email_features, extract_task_features from lms.djangoapps.instructor.views.instructor_task_helpers import extract_email_features, extract_task_features
from lms.djangoapps.instructor_task.api import submit_override_score from lms.djangoapps.instructor_task.api import submit_override_score
from lms.djangoapps.instructor_task.api_helper import AlreadyRunningError from lms.djangoapps.instructor_task.api_helper import AlreadyRunningError, QueueConnectionError
from lms.djangoapps.instructor_task.models import ReportStore from lms.djangoapps.instructor_task.models import ReportStore
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.course_groups.cohorts import is_course_cohorted from openedx.core.djangoapps.course_groups.cohorts import is_course_cohorted
...@@ -133,29 +133,31 @@ log = logging.getLogger(__name__) ...@@ -133,29 +133,31 @@ log = logging.getLogger(__name__)
TASK_SUBMISSION_OK = 'created' TASK_SUBMISSION_OK = 'created'
SUCCESS_MESSAGE_TEMPLATE = _("The {report_type} report is being created. "
"To view the status of the report, see Pending Tasks below.")
def common_exceptions_400(func): def common_exceptions_400(func):
""" """
Catches common exceptions and renders matching 400 errors. Catches common exceptions and renders matching 400 errors.
(decorator without arguments) (decorator without arguments)
""" """
def wrapped(request, *args, **kwargs): # pylint: disable=missing-docstring def wrapped(request, *args, **kwargs): # pylint: disable=missing-docstring
use_json = (request.is_ajax() or use_json = (request.is_ajax() or
request.META.get("HTTP_ACCEPT", "").startswith("application/json")) request.META.get("HTTP_ACCEPT", "").startswith("application/json"))
try: try:
return func(request, *args, **kwargs) return func(request, *args, **kwargs)
except User.DoesNotExist: except User.DoesNotExist:
message = _("User does not exist.") message = _('User does not exist.')
if use_json: except (AlreadyRunningError, QueueConnectionError) as err:
return JsonResponse({"error": message}, 400) message = str(err)
else:
return HttpResponseBadRequest(message)
except AlreadyRunningError:
message = _("Task is already running.")
if use_json: if use_json:
return JsonResponse({"error": message}, 400) return JsonResponseBadRequest(message)
else: else:
return HttpResponseBadRequest(message) return HttpResponseBadRequest(message)
return wrapped return wrapped
...@@ -829,12 +831,12 @@ def bulk_beta_modify_access(request, course_id): ...@@ -829,12 +831,12 @@ def bulk_beta_modify_access(request, course_id):
@ensure_csrf_cookie @ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('instructor') @require_level('instructor')
@common_exceptions_400
@require_post_params( @require_post_params(
unique_student_identifier="email or username of user to change access", unique_student_identifier="email or username of user to change access",
rolename="'instructor', 'staff', 'beta', or 'ccx_coach'", rolename="'instructor', 'staff', 'beta', or 'ccx_coach'",
action="'allow' or 'revoke'" action="'allow' or 'revoke'"
) )
@common_exceptions_400
def modify_access(request, course_id): def modify_access(request, course_id):
""" """
Modify staff/instructor access of other user. Modify staff/instructor access of other user.
...@@ -964,6 +966,7 @@ def list_course_role_members(request, course_id): ...@@ -964,6 +966,7 @@ def list_course_role_members(request, course_id):
@ensure_csrf_cookie @ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff') @require_level('staff')
@common_exceptions_400
def get_problem_responses(request, course_id): def get_problem_responses(request, course_id):
""" """
Initiate generation of a CSV file containing all student answers Initiate generation of a CSV file containing all student answers
...@@ -978,6 +981,7 @@ def get_problem_responses(request, course_id): ...@@ -978,6 +981,7 @@ def get_problem_responses(request, course_id):
""" """
course_key = CourseKey.from_string(course_id) course_key = CourseKey.from_string(course_id)
problem_location = request.POST.get('problem_location', '') problem_location = request.POST.get('problem_location', '')
report_type = _('problem responses')
try: try:
problem_key = UsageKey.from_string(problem_location) problem_key = UsageKey.from_string(problem_location)
...@@ -990,20 +994,10 @@ def get_problem_responses(request, course_id): ...@@ -990,20 +994,10 @@ def get_problem_responses(request, course_id):
except InvalidKeyError: except InvalidKeyError:
return JsonResponseBadRequest(_("Could not find problem with this location.")) return JsonResponseBadRequest(_("Could not find problem with this location."))
try:
lms.djangoapps.instructor_task.api.submit_calculate_problem_responses_csv(request, course_key, problem_location) lms.djangoapps.instructor_task.api.submit_calculate_problem_responses_csv(request, course_key, problem_location)
success_status = _( success_status = SUCCESS_MESSAGE_TEMPLATE.format(report_type=report_type)
"The problem responses report is being created."
" To view the status of the report, see Pending Tasks below."
)
return JsonResponse({"status": success_status}) return JsonResponse({"status": success_status})
except AlreadyRunningError:
already_running_status = _(
"A problem responses report generation task is already in progress. "
"Check the 'Pending Tasks' table for the status of the task. "
"When completed, the report will be available for download in the table below."
)
return JsonResponse({"status": already_running_status})
@require_POST @require_POST
...@@ -1209,6 +1203,7 @@ def get_issued_certificates(request, course_id): ...@@ -1209,6 +1203,7 @@ def get_issued_certificates(request, course_id):
@ensure_csrf_cookie @ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff') @require_level('staff')
@common_exceptions_400
def get_students_features(request, course_id, csv=False): # pylint: disable=redefined-outer-name def get_students_features(request, course_id, csv=False): # pylint: disable=redefined-outer-name
""" """
Respond with json which contains a summary of all enrolled students profile information. Respond with json which contains a summary of all enrolled students profile information.
...@@ -1220,7 +1215,7 @@ def get_students_features(request, course_id, csv=False): # pylint: disable=red ...@@ -1220,7 +1215,7 @@ def get_students_features(request, course_id, csv=False): # pylint: disable=red
""" """
course_key = CourseKey.from_string(course_id) course_key = CourseKey.from_string(course_id)
course = get_course_by_id(course_key) course = get_course_by_id(course_key)
report_type = _('enrolled learner profile')
available_features = instructor_analytics.basic.AVAILABLE_FEATURES available_features = instructor_analytics.basic.AVAILABLE_FEATURES
# Allow for sites to be able to define additional columns. # Allow for sites to be able to define additional columns.
...@@ -1285,22 +1280,16 @@ def get_students_features(request, course_id, csv=False): # pylint: disable=red ...@@ -1285,22 +1280,16 @@ def get_students_features(request, course_id, csv=False): # pylint: disable=red
'available_features': available_features, 'available_features': available_features,
} }
return JsonResponse(response_payload) return JsonResponse(response_payload)
else: else:
try:
lms.djangoapps.instructor_task.api.submit_calculate_students_features_csv( lms.djangoapps.instructor_task.api.submit_calculate_students_features_csv(
request, request,
course_key, course_key,
query_features query_features
) )
success_status = _("The enrolled learner profile report is being created." success_status = SUCCESS_MESSAGE_TEMPLATE.format(report_type=report_type)
" To view the status of the report, see Pending Tasks below.")
return JsonResponse({"status": success_status}) return JsonResponse({"status": success_status})
except AlreadyRunningError:
already_running_status = _(
"This enrollment report is currently being created."
" To view the status of the report, see Pending Tasks below."
" You will be able to download the report when it is complete.")
return JsonResponse({"status": already_running_status})
@transaction.non_atomic_requests @transaction.non_atomic_requests
...@@ -1308,6 +1297,7 @@ def get_students_features(request, course_id, csv=False): # pylint: disable=red ...@@ -1308,6 +1297,7 @@ def get_students_features(request, course_id, csv=False): # pylint: disable=red
@ensure_csrf_cookie @ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff') @require_level('staff')
@common_exceptions_400
def get_students_who_may_enroll(request, course_id): def get_students_who_may_enroll(request, course_id):
""" """
Initiate generation of a CSV file containing information about Initiate generation of a CSV file containing information about
...@@ -1319,21 +1309,11 @@ def get_students_who_may_enroll(request, course_id): ...@@ -1319,21 +1309,11 @@ def get_students_who_may_enroll(request, course_id):
""" """
course_key = CourseKey.from_string(course_id) course_key = CourseKey.from_string(course_id)
query_features = ['email'] query_features = ['email']
try: report_type = _('enrollment')
lms.djangoapps.instructor_task.api.submit_calculate_may_enroll_csv(request, course_key, query_features) lms.djangoapps.instructor_task.api.submit_calculate_may_enroll_csv(request, course_key, query_features)
success_status = _( success_status = SUCCESS_MESSAGE_TEMPLATE.format(report_type=report_type)
"The enrollment report is being created. This report contains"
" information about learners who can enroll in the course."
" To view the status of the report, see Pending Tasks below."
)
return JsonResponse({"status": success_status}) return JsonResponse({"status": success_status})
except AlreadyRunningError:
already_running_status = _(
"This enrollment report is currently being created."
" To view the status of the report, see Pending Tasks below."
" You will be able to download the report when it is complete."
)
return JsonResponse({"status": already_running_status})
@transaction.non_atomic_requests @transaction.non_atomic_requests
...@@ -1341,6 +1321,7 @@ def get_students_who_may_enroll(request, course_id): ...@@ -1341,6 +1321,7 @@ def get_students_who_may_enroll(request, course_id):
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_POST @require_POST
@require_level('staff') @require_level('staff')
@common_exceptions_400
def add_users_to_cohorts(request, course_id): def add_users_to_cohorts(request, course_id):
""" """
View method that accepts an uploaded file (using key "uploaded-file") View method that accepts an uploaded file (using key "uploaded-file")
...@@ -1417,23 +1398,17 @@ def get_coupon_codes(request, course_id): # pylint: disable=unused-argument ...@@ -1417,23 +1398,17 @@ def get_coupon_codes(request, course_id): # pylint: disable=unused-argument
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff') @require_level('staff')
@require_finance_admin @require_finance_admin
@common_exceptions_400
def get_enrollment_report(request, course_id): def get_enrollment_report(request, course_id):
""" """
get the enrollment report for the particular course. get the enrollment report for the particular course.
""" """
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
try: report_type = _('detailed enrollment')
lms.djangoapps.instructor_task.api.submit_detailed_enrollment_features_csv(request, course_key) lms.djangoapps.instructor_task.api.submit_detailed_enrollment_features_csv(request, course_key)
success_status = _("The detailed enrollment report is being created." success_status = SUCCESS_MESSAGE_TEMPLATE.format(report_type=report_type)
" To view the status of the report, see Pending Tasks below.")
return JsonResponse({"status": success_status}) return JsonResponse({"status": success_status})
except AlreadyRunningError:
already_running_status = _("The detailed enrollment report is being created."
" To view the status of the report, see Pending Tasks below."
" You will be able to download the report when it is complete.")
return JsonResponse({
"status": already_running_status
})
@transaction.non_atomic_requests @transaction.non_atomic_requests
...@@ -1442,24 +1417,17 @@ def get_enrollment_report(request, course_id): ...@@ -1442,24 +1417,17 @@ def get_enrollment_report(request, course_id):
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff') @require_level('staff')
@require_finance_admin @require_finance_admin
@common_exceptions_400
def get_exec_summary_report(request, course_id): def get_exec_summary_report(request, course_id):
""" """
get the executive summary report for the particular course. get the executive summary report for the particular course.
""" """
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
try: report_type = _('executive summary')
lms.djangoapps.instructor_task.api.submit_executive_summary_report(request, course_key) lms.djangoapps.instructor_task.api.submit_executive_summary_report(request, course_key)
status_response = _("The executive summary report is being created." success_status = SUCCESS_MESSAGE_TEMPLATE.format(report_type=report_type)
" To view the status of the report, see Pending Tasks below.")
except AlreadyRunningError: return JsonResponse({"status": success_status})
status_response = _(
"The executive summary report is currently being created."
" To view the status of the report, see Pending Tasks below."
" You will be able to download the report when it is complete."
)
return JsonResponse({
"status": status_response
})
@transaction.non_atomic_requests @transaction.non_atomic_requests
...@@ -1467,24 +1435,17 @@ def get_exec_summary_report(request, course_id): ...@@ -1467,24 +1435,17 @@ def get_exec_summary_report(request, course_id):
@ensure_csrf_cookie @ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff') @require_level('staff')
@common_exceptions_400
def get_course_survey_results(request, course_id): def get_course_survey_results(request, course_id):
""" """
get the survey results report for the particular course. get the survey results report for the particular course.
""" """
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
try: report_type = _('survey')
lms.djangoapps.instructor_task.api.submit_course_survey_report(request, course_key) lms.djangoapps.instructor_task.api.submit_course_survey_report(request, course_key)
status_response = _("The survey report is being created." success_status = SUCCESS_MESSAGE_TEMPLATE.format(report_type=report_type)
" To view the status of the report, see Pending Tasks below.")
except AlreadyRunningError: return JsonResponse({"status": success_status})
status_response = _(
"The survey report is currently being created."
" To view the status of the report, see Pending Tasks below."
" You will be able to download the report when it is complete."
)
return JsonResponse({
"status": status_response
})
@transaction.non_atomic_requests @transaction.non_atomic_requests
...@@ -1492,6 +1453,7 @@ def get_course_survey_results(request, course_id): ...@@ -1492,6 +1453,7 @@ def get_course_survey_results(request, course_id):
@ensure_csrf_cookie @ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff') @require_level('staff')
@common_exceptions_400
def get_proctored_exam_results(request, course_id): def get_proctored_exam_results(request, course_id):
""" """
get the proctored exam resultsreport for the particular course. get the proctored exam resultsreport for the particular course.
...@@ -1508,19 +1470,11 @@ def get_proctored_exam_results(request, course_id): ...@@ -1508,19 +1470,11 @@ def get_proctored_exam_results(request, course_id):
] ]
course_key = CourseKey.from_string(course_id) course_key = CourseKey.from_string(course_id)
try: report_type = _('proctored exam results')
lms.djangoapps.instructor_task.api.submit_proctored_exam_results_report(request, course_key, query_features) lms.djangoapps.instructor_task.api.submit_proctored_exam_results_report(request, course_key, query_features)
status_response = _("The proctored exam results report is being created." success_status = SUCCESS_MESSAGE_TEMPLATE.format(report_type=report_type)
" To view the status of the report, see Pending Tasks below.")
except AlreadyRunningError: return JsonResponse({"status": success_status})
status_response = _(
"The proctored exam results report is currently being created."
" To view the status of the report, see Pending Tasks below."
" You will be able to download the report when it is complete."
)
return JsonResponse({
"status": status_response
})
def save_registration_code(user, course_id, mode_slug, invoice=None, order=None, invoice_item=None): def save_registration_code(user, course_id, mode_slug, invoice=None, order=None, invoice_item=None):
...@@ -1901,11 +1855,11 @@ def get_anon_ids(request, course_id): # pylint: disable=unused-argument ...@@ -1901,11 +1855,11 @@ def get_anon_ids(request, course_id): # pylint: disable=unused-argument
@require_POST @require_POST
@ensure_csrf_cookie @ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
@common_exceptions_400
@require_level('staff') @require_level('staff')
@require_post_params( @require_post_params(
unique_student_identifier="email or username of student for whom to get progress url" unique_student_identifier="email or username of student for whom to get progress url"
) )
@common_exceptions_400
def get_student_progress_url(request, course_id): def get_student_progress_url(request, course_id):
""" """
Get the progress url of a student. Get the progress url of a student.
...@@ -2147,6 +2101,7 @@ def rescore_problem(request, course_id): ...@@ -2147,6 +2101,7 @@ def rescore_problem(request, course_id):
) )
except NotImplementedError as exc: except NotImplementedError as exc:
return HttpResponseBadRequest(exc.message) return HttpResponseBadRequest(exc.message)
elif all_students: elif all_students:
try: try:
lms.djangoapps.instructor_task.api.submit_rescore_problem_for_all_students( lms.djangoapps.instructor_task.api.submit_rescore_problem_for_all_students(
...@@ -2453,25 +2408,17 @@ def list_financial_report_downloads(_request, course_id): ...@@ -2453,25 +2408,17 @@ def list_financial_report_downloads(_request, course_id):
@ensure_csrf_cookie @ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff') @require_level('staff')
@common_exceptions_400
def export_ora2_data(request, course_id): def export_ora2_data(request, course_id):
""" """
Pushes a Celery task which will aggregate ora2 responses for a course into a .csv Pushes a Celery task which will aggregate ora2 responses for a course into a .csv
""" """
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
try: report_type = _('ORA data')
lms.djangoapps.instructor_task.api.submit_export_ora2_data(request, course_key) lms.djangoapps.instructor_task.api.submit_export_ora2_data(request, course_key)
success_status = _("The ORA data report is being generated.") success_status = SUCCESS_MESSAGE_TEMPLATE.format(report_type=report_type)
return JsonResponse({"status": success_status}) return JsonResponse({"status": success_status})
except AlreadyRunningError:
already_running_status = _(
"An ORA data report generation task is already in "
"progress. Check the 'Pending Tasks' table "
"for the status of the task. When completed, the report "
"will be available for download in the table below."
)
return JsonResponse({"status": already_running_status})
@transaction.non_atomic_requests @transaction.non_atomic_requests
...@@ -2479,21 +2426,17 @@ def export_ora2_data(request, course_id): ...@@ -2479,21 +2426,17 @@ def export_ora2_data(request, course_id):
@ensure_csrf_cookie @ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff') @require_level('staff')
@common_exceptions_400
def calculate_grades_csv(request, course_id): def calculate_grades_csv(request, course_id):
""" """
AlreadyRunningError is raised if the course's grades are already being updated. AlreadyRunningError is raised if the course's grades are already being updated.
""" """
report_type = _('grade')
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
try:
lms.djangoapps.instructor_task.api.submit_calculate_grades_csv(request, course_key) lms.djangoapps.instructor_task.api.submit_calculate_grades_csv(request, course_key)
success_status = _("The grade report is being created." success_status = SUCCESS_MESSAGE_TEMPLATE.format(report_type=report_type)
" To view the status of the report, see Pending Tasks below.")
return JsonResponse({"status": success_status}) return JsonResponse({"status": success_status})
except AlreadyRunningError:
already_running_status = _("The grade report is currently being created."
" To view the status of the report, see Pending Tasks below."
" You will be able to download the report when it is complete.")
return JsonResponse({"status": already_running_status})
@transaction.non_atomic_requests @transaction.non_atomic_requests
...@@ -2501,6 +2444,7 @@ def calculate_grades_csv(request, course_id): ...@@ -2501,6 +2444,7 @@ def calculate_grades_csv(request, course_id):
@ensure_csrf_cookie @ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff') @require_level('staff')
@common_exceptions_400
def problem_grade_report(request, course_id): def problem_grade_report(request, course_id):
""" """
Request a CSV showing students' grades for all problems in the Request a CSV showing students' grades for all problems in the
...@@ -2510,18 +2454,11 @@ def problem_grade_report(request, course_id): ...@@ -2510,18 +2454,11 @@ def problem_grade_report(request, course_id):
updated. updated.
""" """
course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id) course_key = SlashSeparatedCourseKey.from_deprecated_string(course_id)
try: report_type = _('problem grade')
lms.djangoapps.instructor_task.api.submit_problem_grade_report(request, course_key) lms.djangoapps.instructor_task.api.submit_problem_grade_report(request, course_key)
success_status = _("The problem grade report is being created." success_status = SUCCESS_MESSAGE_TEMPLATE.format(report_type=report_type)
" To view the status of the report, see Pending Tasks below.")
return JsonResponse({"status": success_status}) return JsonResponse({"status": success_status})
except AlreadyRunningError:
already_running_status = _("A problem grade report is already being generated."
" To view the status of the report, see Pending Tasks below."
" You will be able to download the report when it is complete.")
return JsonResponse({
"status": already_running_status
})
@require_POST @require_POST
...@@ -2601,6 +2538,7 @@ def list_forum_members(request, course_id): ...@@ -2601,6 +2538,7 @@ def list_forum_members(request, course_id):
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff') @require_level('staff')
@require_post_params(send_to="sending to whom", subject="subject line", message="message text") @require_post_params(send_to="sending to whom", subject="subject line", message="message text")
@common_exceptions_400
def send_email(request, course_id): def send_email(request, course_id):
""" """
Send an email to self, staff, cohorts, or everyone involved in a course. Send an email to self, staff, cohorts, or everyone involved in a course.
...@@ -2667,6 +2605,7 @@ def send_email(request, course_id): ...@@ -2667,6 +2605,7 @@ def send_email(request, course_id):
'course_id': course_id.to_deprecated_string(), 'course_id': course_id.to_deprecated_string(),
'success': True, 'success': True,
} }
return JsonResponse(response_payload) return JsonResponse(response_payload)
...@@ -2944,6 +2883,7 @@ def mark_student_can_skip_entrance_exam(request, course_id): # pylint: disable= ...@@ -2944,6 +2883,7 @@ def mark_student_can_skip_entrance_exam(request, course_id): # pylint: disable=
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_global_staff @require_global_staff
@require_POST @require_POST
@common_exceptions_400
def start_certificate_generation(request, course_id): def start_certificate_generation(request, course_id):
""" """
Start generating certificates for all students enrolled in given course. Start generating certificates for all students enrolled in given course.
...@@ -2956,6 +2896,7 @@ def start_certificate_generation(request, course_id): ...@@ -2956,6 +2896,7 @@ def start_certificate_generation(request, course_id):
'message': message, 'message': message,
'task_id': task.task_id 'task_id': task.task_id
} }
return JsonResponse(response_payload) return JsonResponse(response_payload)
...@@ -2964,6 +2905,7 @@ def start_certificate_generation(request, course_id): ...@@ -2964,6 +2905,7 @@ def start_certificate_generation(request, course_id):
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_global_staff @require_global_staff
@require_POST @require_POST
@common_exceptions_400
def start_certificate_regeneration(request, course_id): def start_certificate_regeneration(request, course_id):
""" """
Start regenerating certificates for students whose certificate statuses lie with in 'certificate_statuses' Start regenerating certificates for students whose certificate statuses lie with in 'certificate_statuses'
...@@ -2990,11 +2932,8 @@ def start_certificate_regeneration(request, course_id): ...@@ -2990,11 +2932,8 @@ def start_certificate_regeneration(request, course_id):
{'message': _('Please select certificate statuses from the list only.')}, {'message': _('Please select certificate statuses from the list only.')},
status=400 status=400
) )
try:
lms.djangoapps.instructor_task.api.regenerate_certificates(request, course_key, certificates_statuses)
except AlreadyRunningError as error:
return JsonResponse({'message': error.message}, status=400)
lms.djangoapps.instructor_task.api.regenerate_certificates(request, course_key, certificates_statuses)
response_payload = { response_payload = {
'message': _('Certificate regeneration task has been started. ' 'message': _('Certificate regeneration task has been started. '
'You can view the status of the generation task in the "Pending Tasks" section.'), 'You can view the status of the generation task in the "Pending Tasks" section.'),
...@@ -3183,6 +3122,7 @@ def get_student(username_or_email, course_key): ...@@ -3183,6 +3122,7 @@ def get_student(username_or_email, course_key):
@cache_control(no_cache=True, no_store=True, must_revalidate=True) @cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_global_staff @require_global_staff
@require_POST @require_POST
@common_exceptions_400
def generate_certificate_exceptions(request, course_id, generate_for=None): def generate_certificate_exceptions(request, course_id, generate_for=None):
""" """
Generate Certificate for students in the Certificate White List. Generate Certificate for students in the Certificate White List.
...@@ -3213,7 +3153,6 @@ def generate_certificate_exceptions(request, course_id, generate_for=None): ...@@ -3213,7 +3153,6 @@ def generate_certificate_exceptions(request, course_id, generate_for=None):
) )
lms.djangoapps.instructor_task.api.generate_certificates_for_students(request, course_key, student_set=students) lms.djangoapps.instructor_task.api.generate_certificates_for_students(request, course_key, student_set=students)
response_payload = { response_payload = {
'success': True, 'success': True,
'message': _('Certificate generation started for white listed students.'), 'message': _('Certificate generation started for white listed students.'),
...@@ -3401,6 +3340,7 @@ def invalidate_certificate(request, generated_certificate, certificate_invalidat ...@@ -3401,6 +3340,7 @@ def invalidate_certificate(request, generated_certificate, certificate_invalidat
} }
@common_exceptions_400
def re_validate_certificate(request, course_key, generated_certificate): def re_validate_certificate(request, course_key, generated_certificate):
""" """
Remove certificate invalidation from db and start certificate generation task for this student. Remove certificate invalidation from db and start certificate generation task for this student.
...@@ -3421,6 +3361,7 @@ def re_validate_certificate(request, course_key, generated_certificate): ...@@ -3421,6 +3361,7 @@ def re_validate_certificate(request, course_key, generated_certificate):
# We need to generate certificate only for a single student here # We need to generate certificate only for a single student here
student = certificate_invalidation.generated_certificate.user student = certificate_invalidation.generated_certificate.user
lms.djangoapps.instructor_task.api.generate_certificates_for_students( lms.djangoapps.instructor_task.api.generate_certificates_for_students(
request, course_key, student_set="specific_student", specific_student_id=student.id request, course_key, student_set="specific_student", specific_student_id=student.id
) )
......
...@@ -25,7 +25,26 @@ log = logging.getLogger(__name__) ...@@ -25,7 +25,26 @@ log = logging.getLogger(__name__)
class AlreadyRunningError(Exception): class AlreadyRunningError(Exception):
"""Exception indicating that a background task is already running""" """Exception indicating that a background task is already running"""
pass
message = _('Requested task is already running')
def __init__(self, message=None):
if not message:
message = self.message
super(AlreadyRunningError, self).__init__(message)
class QueueConnectionError(Exception):
"""
Exception indicating that celery task was not created successfully.
"""
message = _('Error occured. Please try again later.')
def __init__(self, message=None):
if not message:
message = self.message
super(QueueConnectionError, self).__init__(message)
def _task_is_running(course_id, task_type, task_key): def _task_is_running(course_id, task_type, task_key):
...@@ -57,7 +76,8 @@ def _reserve_task(course_id, task_type, task_key, task_input, requester): ...@@ -57,7 +76,8 @@ def _reserve_task(course_id, task_type, task_key, task_input, requester):
if _task_is_running(course_id, task_type, task_key): if _task_is_running(course_id, task_type, task_key):
log.warning("Duplicate task found for task_type %s and task_key %s", task_type, task_key) log.warning("Duplicate task found for task_type %s and task_key %s", task_type, task_key)
raise AlreadyRunningError("requested task is already running") error_message = generate_already_running_error_message(task_type)
raise AlreadyRunningError(error_message)
try: try:
most_recent_id = InstructorTask.objects.latest('id').id most_recent_id = InstructorTask.objects.latest('id').id
...@@ -75,6 +95,37 @@ def _reserve_task(course_id, task_type, task_key, task_input, requester): ...@@ -75,6 +95,37 @@ def _reserve_task(course_id, task_type, task_key, task_input, requester):
return InstructorTask.create(course_id, task_type, task_key, task_input, requester) return InstructorTask.create(course_id, task_type, task_key, task_input, requester)
def generate_already_running_error_message(task_type):
"""
Returns already running error message for given task type.
"""
message = ''
report_types = {
'grade_problems': _('problem grade'),
'problem_responses_csv': _('problem responses'),
'profile_info_csv': _('enrolled learner profile'),
'may_enroll_info_csv': _('enrollment'),
'detailed_enrollment_report': _('detailed enrollment'),
'exec_summary_report': _('executive summary'),
'course_survey_report': _('survey'),
'proctored_exam_results_report': _('proctored exam results'),
'export_ora2_data': _('ORA data'),
'grade_course': _('grade'),
}
if report_types.get(task_type):
message = _(
"The {report_type} report is being created. "
"To view the status of the report, see Pending Tasks below. "
"You will be able to download the report when it is complete."
).format(report_type=report_types.get(task_type))
return message
def _get_xmodule_instance_args(request, task_id): def _get_xmodule_instance_args(request, task_id):
""" """
Calculate parameters needed for instantiating xmodule instances. Calculate parameters needed for instantiating xmodule instances.
...@@ -190,6 +241,27 @@ def _update_instructor_task(instructor_task, task_result): ...@@ -190,6 +241,27 @@ def _update_instructor_task(instructor_task, task_result):
instructor_task.save() instructor_task.save()
def _update_instructor_task_state(instructor_task, task_state, message=None):
"""
Update state and output of InstructorTask object.
"""
instructor_task.task_state = task_state
if message:
instructor_task.task_output = message
instructor_task.save()
def _handle_instructor_task_failure(instructor_task, error):
"""
Do required operations if task creation was not complete.
"""
log.info("instructor task (%s) failed, result: %s", instructor_task.task_id, error.message)
_update_instructor_task_state(instructor_task, FAILURE, error.message)
raise QueueConnectionError()
def get_updated_instructor_task(task_id): def get_updated_instructor_task(task_id):
""" """
Returns InstructorTask object corresponding to a given `task_id`. Returns InstructorTask object corresponding to a given `task_id`.
...@@ -365,6 +437,10 @@ def submit_task(request, task_type, task_class, course_key, task_input, task_key ...@@ -365,6 +437,10 @@ def submit_task(request, task_type, task_class, course_key, task_input, task_key
task_id = instructor_task.task_id task_id = instructor_task.task_id
task_args = [instructor_task.id, _get_xmodule_instance_args(request, task_id)] task_args = [instructor_task.id, _get_xmodule_instance_args(request, task_id)]
try:
task_class.apply_async(task_args, task_id=task_id) task_class.apply_async(task_args, task_id=task_id)
except Exception as error:
_handle_instructor_task_failure(instructor_task, error)
return instructor_task return instructor_task
...@@ -32,7 +32,7 @@ from lms.djangoapps.instructor_task.api import ( ...@@ -32,7 +32,7 @@ from lms.djangoapps.instructor_task.api import (
submit_reset_problem_attempts_for_all_students, submit_reset_problem_attempts_for_all_students,
submit_reset_problem_attempts_in_entrance_exam submit_reset_problem_attempts_in_entrance_exam
) )
from lms.djangoapps.instructor_task.api_helper import AlreadyRunningError from lms.djangoapps.instructor_task.api_helper import AlreadyRunningError, QueueConnectionError
from lms.djangoapps.instructor_task.models import PROGRESS, InstructorTask from lms.djangoapps.instructor_task.models import PROGRESS, InstructorTask
from lms.djangoapps.instructor_task.tasks import export_ora2_data from lms.djangoapps.instructor_task.tasks import export_ora2_data
from lms.djangoapps.instructor_task.tests.test_base import ( from lms.djangoapps.instructor_task.tests.test_base import (
...@@ -43,6 +43,7 @@ from lms.djangoapps.instructor_task.tests.test_base import ( ...@@ -43,6 +43,7 @@ from lms.djangoapps.instructor_task.tests.test_base import (
TestReportMixin TestReportMixin
) )
from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.modulestore.exceptions import ItemNotFoundError
from celery.states import FAILURE
class InstructorTaskReportTest(InstructorTaskTestCase): class InstructorTaskReportTest(InstructorTaskTestCase):
...@@ -164,15 +165,31 @@ class InstructorTaskModuleSubmitTest(InstructorTaskModuleTestCase): ...@@ -164,15 +165,31 @@ class InstructorTaskModuleSubmitTest(InstructorTaskModuleTestCase):
) )
@ddt.unpack @ddt.unpack
def test_submit_task(self, task_function, expected_task_type, params=None): def test_submit_task(self, task_function, expected_task_type, params=None):
"""
Tests submission of instructor task.
"""
if params is None: if params is None:
params = {} params = {}
if params.get('student'): if params.get('student'):
params['student'] = self.student params['student'] = self.student
# tests submit, and then tests a second identical submission.
problem_url_name = 'H1P1' problem_url_name = 'H1P1'
self.define_option_problem(problem_url_name) self.define_option_problem(problem_url_name)
location = InstructorTaskModuleTestCase.problem_location(problem_url_name) location = InstructorTaskModuleTestCase.problem_location(problem_url_name)
# unsuccessful submission, exception raised while submitting.
with patch('lms.djangoapps.instructor_task.tasks_base.BaseInstructorTask.apply_async') as apply_async:
error = Exception()
apply_async.side_effect = error
with self.assertRaises(QueueConnectionError):
instructor_task = task_function(self.create_task_request(self.instructor), location, **params)
most_recent_task = InstructorTask.objects.latest('id')
self.assertEquals(most_recent_task.task_state, FAILURE)
# successful submission
instructor_task = task_function(self.create_task_request(self.instructor), location, **params) instructor_task = task_function(self.create_task_request(self.instructor), location, **params)
self.assertEquals(instructor_task.task_type, expected_task_type) self.assertEquals(instructor_task.task_type, expected_task_type)
......
...@@ -122,16 +122,18 @@ ...@@ -122,16 +122,18 @@
}); });
this.$proctored_exam_csv_btn.click(function() { this.$proctored_exam_csv_btn.click(function() {
var url = dataDownloadObj.$proctored_exam_csv_btn.data('endpoint'); var url = dataDownloadObj.$proctored_exam_csv_btn.data('endpoint');
var errorMessage = gettext('Error generating proctored exam results. Please try again.');
return $.ajax({ return $.ajax({
type: 'POST', type: 'POST',
dataType: 'json', dataType: 'json',
url: url, url: url,
error: function() { error: function(error) {
if (error.responseText) {
errorMessage = JSON.parse(error.responseText);
}
dataDownloadObj.clear_display(); dataDownloadObj.clear_display();
dataDownloadObj.$reports_request_response_error.text( dataDownloadObj.$reports_request_response_error.text(errorMessage);
gettext('Error generating proctored exam results. Please try again.') return dataDownloadObj.$reports_request_response_error.css({
);
return $('.msg-error').css({
display: 'block' display: 'block'
}); });
}, },
...@@ -146,16 +148,18 @@ ...@@ -146,16 +148,18 @@
}); });
this.$survey_results_csv_btn.click(function() { this.$survey_results_csv_btn.click(function() {
var url = dataDownloadObj.$survey_results_csv_btn.data('endpoint'); var url = dataDownloadObj.$survey_results_csv_btn.data('endpoint');
var errorMessage = gettext('Error generating survey results. Please try again.');
return $.ajax({ return $.ajax({
type: 'POST', type: 'POST',
dataType: 'json', dataType: 'json',
url: url, url: url,
error: function() { error: function(error) {
if (error.responseText) {
errorMessage = JSON.parse(error.responseText);
}
dataDownloadObj.clear_display(); dataDownloadObj.clear_display();
dataDownloadObj.$reports_request_response_error.text( dataDownloadObj.$reports_request_response_error.text(errorMessage);
gettext('Error generating survey results. Please try again.') return dataDownloadObj.$reports_request_response_error.css({
);
return $('.msg-error').css({
display: 'block' display: 'block'
}); });
}, },
...@@ -170,16 +174,18 @@ ...@@ -170,16 +174,18 @@
}); });
this.$list_studs_csv_btn.click(function() { this.$list_studs_csv_btn.click(function() {
var url = dataDownloadObj.$list_studs_csv_btn.data('endpoint') + '/csv'; var url = dataDownloadObj.$list_studs_csv_btn.data('endpoint') + '/csv';
var errorMessage = gettext('Error generating student profile information. Please try again.');
dataDownloadObj.clear_display(); dataDownloadObj.clear_display();
return $.ajax({ return $.ajax({
type: 'POST', type: 'POST',
dataType: 'json', dataType: 'json',
url: url, url: url,
error: function() { error: function(error) {
dataDownloadObj.$reports_request_response_error.text( if (error.responseText) {
gettext('Error generating student profile information. Please try again.') errorMessage = JSON.parse(error.responseText);
); }
return $('.msg-error').css({ dataDownloadObj.$reports_request_response_error.text(errorMessage);
return dataDownloadObj.$reports_request_response_error.css({
display: 'block' display: 'block'
}); });
}, },
...@@ -201,9 +207,13 @@ ...@@ -201,9 +207,13 @@
url: url, url: url,
error: function() { error: function() {
dataDownloadObj.clear_display(); dataDownloadObj.clear_display();
return dataDownloadObj.$download_request_response_error.text( dataDownloadObj.$download_request_response_error.text(
gettext('Error getting student list.') gettext('Error getting student list.')
); );
return dataDownloadObj.$download_request_response_error.css({
display: 'block'
});
}, },
success: function(data) { success: function(data) {
var $tablePlaceholder, columns, feature, gridData, options; var $tablePlaceholder, columns, feature, gridData, options;
...@@ -251,7 +261,7 @@ ...@@ -251,7 +261,7 @@
dataDownloadObj.$reports_request_response_error.text( dataDownloadObj.$reports_request_response_error.text(
JSON.parse(error.responseText) JSON.parse(error.responseText)
); );
return $('.msg-error').css({ return dataDownloadObj.$reports_request_response_error.css({
display: 'block' display: 'block'
}); });
}, },
...@@ -265,16 +275,18 @@ ...@@ -265,16 +275,18 @@
}); });
this.$list_may_enroll_csv_btn.click(function() { this.$list_may_enroll_csv_btn.click(function() {
var url = dataDownloadObj.$list_may_enroll_csv_btn.data('endpoint'); var url = dataDownloadObj.$list_may_enroll_csv_btn.data('endpoint');
var errorMessage = gettext('Error generating list of students who may enroll. Please try again.');
dataDownloadObj.clear_display(); dataDownloadObj.clear_display();
return $.ajax({ return $.ajax({
type: 'POST', type: 'POST',
dataType: 'json', dataType: 'json',
url: url, url: url,
error: function() { error: function(error) {
dataDownloadObj.$reports_request_response_error.text( if (error.responseText) {
gettext('Error generating list of students who may enroll. Please try again.') errorMessage = JSON.parse(error.responseText);
); }
return $('.msg-error').css({ dataDownloadObj.$reports_request_response_error.text(errorMessage);
return dataDownloadObj.$reports_request_response_error.css({
display: 'block' display: 'block'
}); });
}, },
...@@ -294,9 +306,12 @@ ...@@ -294,9 +306,12 @@
url: url, url: url,
error: function() { error: function() {
dataDownloadObj.clear_display(); dataDownloadObj.clear_display();
return dataDownloadObj.$download_request_response_error.text( dataDownloadObj.$download_request_response_error.text(
gettext('Error retrieving grading configuration.') gettext('Error retrieving grading configuration.')
); );
return dataDownloadObj.$download_request_response_error.css({
display: 'block'
});
}, },
success: function(data) { success: function(data) {
dataDownloadObj.clear_display(); dataDownloadObj.clear_display();
...@@ -307,29 +322,27 @@ ...@@ -307,29 +322,27 @@
}); });
this.$async_report_btn.click(function(e) { this.$async_report_btn.click(function(e) {
var url = $(e.target).data('endpoint'); var url = $(e.target).data('endpoint');
var errorMessage = '';
dataDownloadObj.clear_display(); dataDownloadObj.clear_display();
return $.ajax({ return $.ajax({
type: 'POST', type: 'POST',
dataType: 'json', dataType: 'json',
url: url, url: url,
error: statusAjaxError(function() { error: function(error) {
if (e.target.name === 'calculate-grades-csv') { if (error.responseText) {
dataDownloadObj.$grades_request_response_error.text( errorMessage = JSON.parse(error.responseText);
gettext('Error generating grades. Please try again.') } else if (e.target.name === 'calculate-grades-csv') {
); errorMessage = gettext('Error generating grades. Please try again.');
} else if (e.target.name === 'problem-grade-report') { } else if (e.target.name === 'problem-grade-report') {
dataDownloadObj.$grades_request_response_error.text( errorMessage = gettext('Error generating problem grade report. Please try again.');
gettext('Error generating problem grade report. Please try again.')
);
} else if (e.target.name === 'export-ora2-data') { } else if (e.target.name === 'export-ora2-data') {
dataDownloadObj.$grades_request_response_error.text( errorMessage = gettext('Error generating ORA data report. Please try again.');
gettext('Error generating ORA data report. Please try again.')
);
} }
return $('.msg-error').css({ dataDownloadObj.$reports_request_response_error.text(errorMessage);
return dataDownloadObj.$reports_request_response_error.css({
display: 'block' display: 'block'
}); });
}), },
success: function(data) { success: function(data) {
dataDownloadObj.$reports_request_response.text(data.status); dataDownloadObj.$reports_request_response.text(data.status);
return $('.msg-confirm').css({ return $('.msg-confirm').css({
......
...@@ -96,7 +96,7 @@ define([ ...@@ -96,7 +96,7 @@ define([
error_msg: 'Failed to reset attempts for user.' error_msg: 'Failed to reset attempts for user.'
}; };
StaffDebug.doInstructorDashAction(action); StaffDebug.doInstructorDashAction(action);
AjaxHelpers.respondWithError(requests); AjaxHelpers.respondWithTextError(requests);
expect($('#idash_msg').text()).toBe('Failed to reset attempts for user. '); expect($('#idash_msg').text()).toBe('Failed to reset attempts for user. ');
$('#result_' + locationName).remove(); $('#result_' + locationName).remove();
}); });
......
...@@ -54,12 +54,12 @@ var StaffDebug = (function() { ...@@ -54,12 +54,12 @@ var StaffDebug = (function() {
try { try {
responseJSON = $.parseJSON(request.responseText); responseJSON = $.parseJSON(request.responseText);
} catch (e) { } catch (e) {
responseJSON = {error: gettext('Unknown Error Occurred.')}; responseJSON = 'Unknown Error Occurred.';
} }
var text = _.template('{error_msg} {error}', {interpolate: /\{(.+?)\}/g})( var text = _.template('{error_msg} {error}', {interpolate: /\{(.+?)\}/g})(
{ {
error_msg: action.error_msg, error_msg: action.error_msg,
error: responseJSON.error error: gettext(responseJSON)
} }
); );
var html = _.template('<p id="idash_msg" class="error">{text}</p>', {interpolate: /\{(.+?)\}/g})( var html = _.template('<p id="idash_msg" class="error">{text}</p>', {interpolate: /\{(.+?)\}/g})(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment