Commit d54f79f5 by Robert Raposa

Switch dashboard from GET to POST.

parent 58856964
......@@ -17,7 +17,7 @@ from django.conf import settings
from django.contrib.auth.models import User
from django.core import mail
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.urlresolvers import reverse
from django.core.urlresolvers import reverse as django_reverse
from django.http import HttpRequest, HttpResponse
from django.test import RequestFactory, TestCase
from django.test.utils import override_settings
......@@ -34,7 +34,9 @@ from xmodule.modulestore import ModuleStoreEnum
from bulk_email.models import BulkEmailFlag
from course_modes.models import CourseMode
from courseware.models import StudentModule
from courseware.tests.factories import StaffFactory, InstructorFactory, BetaTesterFactory, UserProfileFactory
from courseware.tests.factories import (
BetaTesterFactory, GlobalStaffFactory, InstructorFactory, StaffFactory, UserProfileFactory
)
from courseware.tests.helpers import LoginEnrollmentTestCase
from django_comment_common.models import FORUM_ROLE_COMMUNITY_TA
from django_comment_common.utils import seed_permissions_roles
......@@ -131,6 +133,82 @@ EXECUTIVE_SUMMARY_DATA = (
)
INSTRUCTOR_GET_ENDPOINTS = set([
'get_anon_ids',
'get_coupon_codes',
'get_issued_certificates',
'get_sale_order_records',
'get_sale_records',
])
INSTRUCTOR_POST_ENDPOINTS = set([
'active_registration_codes',
'add_users_to_cohorts',
'bulk_beta_modify_access',
'calculate_grades_csv',
'change_due_date',
'export_ora2_data',
'generate_registration_codes',
'get_enrollment_report',
'get_exec_summary_report',
'get_grading_config',
'get_problem_responses',
'get_proctored_exam_results',
'get_registration_codes',
'get_student_progress_url',
'get_students_features',
'get_students_who_may_enroll',
'get_user_invoice_preference',
'list_background_email_tasks',
'list_course_role_members',
'list_email_content',
'list_entrance_exam_instructor_tasks',
'list_financial_report_downloads',
'list_forum_members',
'list_instructor_tasks',
'list_report_downloads',
'mark_student_can_skip_entrance_exam',
'modify_access',
'register_and_enroll_students',
'rescore_entrance_exam',
'rescore_problem',
'reset_due_date',
'reset_student_attempts',
'reset_student_attempts_for_entrance_exam',
'sale_validation',
'show_student_extensions',
'show_unit_extensions',
'send_email',
'spent_registration_codes',
'students_update_enrollment',
'update_forum_role_membership',
])
def reverse(endpoint, args=None, kwargs=None, is_dashboard_endpoint=True):
"""
Simple wrapper of Django's reverse that first ensures that we have declared
each endpoint under test.
Arguments:
args: The args to be passed through to reverse.
endpoint: The endpoint to be passed through to reverse.
kwargs: The kwargs to be passed through to reverse.
is_dashboard_endpoint: True if this is an instructor dashboard endpoint
that must be declared in the INSTRUCTOR_GET_ENDPOINTS or
INSTRUCTOR_GET_ENDPOINTS sets, or false otherwise.
Returns:
The return of Django's reverse function
"""
is_endpoint_declared = endpoint in INSTRUCTOR_GET_ENDPOINTS or endpoint in INSTRUCTOR_POST_ENDPOINTS
if is_dashboard_endpoint and is_endpoint_declared is False:
# Verify that all endpoints are declared so we can ensure they are
# properly validated elsewhere.
raise ValueError("The endpoint {} must be declared in ENDPOINTS before use.".format(endpoint))
return django_reverse(endpoint, args=args, kwargs=kwargs)
@common_exceptions_400
def view_success(request): # pylint: disable=unused-argument
"A dummy view for testing that returns a simple HTTP response"
......@@ -192,6 +270,61 @@ class TestCommonExceptions400(TestCase):
@attr('shard_1')
@ddt.ddt
class TestEndpointHttpMethods(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
"""
Ensure that users can make GET requests against endpoints that allow GET,
and not against those that don't allow GET.
"""
@classmethod
def setUpClass(cls):
"""
Set up test course.
"""
super(TestEndpointHttpMethods, cls).setUpClass()
cls.course = CourseFactory.create()
def setUp(self):
"""
Set up global staff role so authorization will not fail.
"""
super(TestEndpointHttpMethods, self).setUp()
global_user = GlobalStaffFactory()
self.client.login(username=global_user.username, password='test')
@ddt.data(*INSTRUCTOR_POST_ENDPOINTS)
def test_endpoints_reject_get(self, data):
"""
Tests that POST endpoints are rejected with 405 when using GET.
"""
url = reverse(data, kwargs={'course_id': unicode(self.course.id)})
response = self.client.get(url)
self.assertEqual(
response.status_code, 405,
"Endpoint {} returned status code {} instead of a 405. It should not allow GET.".format(
data, response.status_code
)
)
@ddt.data(*INSTRUCTOR_GET_ENDPOINTS)
def test_endpoints_accept_get(self, data):
"""
Tests that GET endpoints are not rejected with 405 when using GET.
"""
url = reverse(data, kwargs={'course_id': unicode(self.course.id)})
response = self.client.get(url)
self.assertNotEqual(
response.status_code, 405,
"Endpoint {} returned status code 405 where it shouldn't, since it should allow GET.".format(
data
)
)
@attr('shard_1')
@patch('bulk_email.models.html_to_text', Mock(return_value='Mocking CourseEmail.text_message', autospec=True))
class TestInstructorAPIDenyLevels(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
"""
......@@ -271,10 +404,10 @@ class TestInstructorAPIDenyLevels(SharedModuleStoreTestCase, LoginEnrollmentTest
msg: message to display if assertion fails.
"""
url = reverse(endpoint, kwargs={'course_id': self.course.id.to_deprecated_string()})
if endpoint in ['send_email', 'students_update_enrollment', 'bulk_beta_modify_access']:
response = self.client.post(url, args)
else:
if endpoint in INSTRUCTOR_GET_ENDPOINTS:
response = self.client.get(url, args)
else:
response = self.client.post(url, args)
self.assertEqual(
response.status_code,
status_code,
......@@ -1919,13 +2052,13 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe
def test_modify_access_noparams(self):
""" Test missing all query parameters. """
url = reverse('modify_access', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url)
response = self.client.post(url)
self.assertEqual(response.status_code, 400)
def test_modify_access_bad_action(self):
""" Test with an invalid action parameter. """
url = reverse('modify_access', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.other_staff.email,
'rolename': 'staff',
'action': 'robot-not-an-action',
......@@ -1935,7 +2068,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe
def test_modify_access_bad_role(self):
""" Test with an invalid action parameter. """
url = reverse('modify_access', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.other_staff.email,
'rolename': 'robot-not-a-roll',
'action': 'revoke',
......@@ -1944,7 +2077,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe
def test_modify_access_allow(self):
url = reverse('modify_access', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.other_user.email,
'rolename': 'staff',
'action': 'allow',
......@@ -1953,7 +2086,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe
def test_modify_access_allow_with_uname(self):
url = reverse('modify_access', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.other_instructor.username,
'rolename': 'staff',
'action': 'allow',
......@@ -1962,7 +2095,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe
def test_modify_access_revoke(self):
url = reverse('modify_access', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.other_staff.email,
'rolename': 'staff',
'action': 'revoke',
......@@ -1971,7 +2104,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe
def test_modify_access_revoke_with_username(self):
url = reverse('modify_access', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.other_staff.username,
'rolename': 'staff',
'action': 'revoke',
......@@ -1980,7 +2113,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe
def test_modify_access_with_fake_user(self):
url = reverse('modify_access', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': 'GandalfTheGrey',
'rolename': 'staff',
'action': 'revoke',
......@@ -1997,7 +2130,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe
self.other_user.is_active = False
self.other_user.save() # pylint: disable=no-member
url = reverse('modify_access', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.other_user.username,
'rolename': 'beta',
'action': 'allow',
......@@ -2013,7 +2146,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe
def test_modify_access_revoke_not_allowed(self):
""" Test revoking access that a user does not have. """
url = reverse('modify_access', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.other_staff.email,
'rolename': 'instructor',
'action': 'revoke',
......@@ -2025,7 +2158,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe
Test that an instructor cannot remove instructor privelages from themself.
"""
url = reverse('modify_access', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.instructor.email,
'rolename': 'instructor',
'action': 'revoke',
......@@ -2044,20 +2177,20 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe
def test_list_course_role_members_noparams(self):
""" Test missing all query parameters. """
url = reverse('list_course_role_members', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url)
response = self.client.post(url)
self.assertEqual(response.status_code, 400)
def test_list_course_role_members_bad_rolename(self):
""" Test with an invalid rolename parameter. """
url = reverse('list_course_role_members', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'rolename': 'robot-not-a-rolename',
})
self.assertEqual(response.status_code, 400)
def test_list_course_role_members_staff(self):
url = reverse('list_course_role_members', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'rolename': 'staff',
})
self.assertEqual(response.status_code, 200)
......@@ -2079,7 +2212,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe
def test_list_course_role_members_beta(self):
url = reverse('list_course_role_members', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'rolename': 'beta',
})
self.assertEqual(response.status_code, 200)
......@@ -2112,7 +2245,7 @@ class TestInstructorAPILevelsAccess(SharedModuleStoreTestCase, LoginEnrollmentTe
Get unique_student_identifier, rolename and action and update forum role.
"""
url = reverse('update_forum_role_membership', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(
response = self.client.post(
url,
{
'unique_student_identifier': identifier,
......@@ -2185,7 +2318,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
"""
enroll user using a registration code
"""
redeem_url = reverse('register_code_redemption', args=[code])
redeem_url = reverse('shoppingcart.views.register_code_redemption', args=[code], is_dashboard_endpoint=False)
self.client.login(username=user.username, password='test')
response = self.client.get(redeem_url)
self.assertEquals(response.status_code, 200)
......@@ -2265,10 +2398,16 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
mode_slug=CourseMode.HONOR
)
# update the quantity of the cart item paid_course_reg_item
resp = self.client.post(reverse('shoppingcart.views.update_user_cart'), {'ItemId': paid_course_reg_item.id, 'qty': '4'})
resp = self.client.post(
reverse('shoppingcart.views.update_user_cart', is_dashboard_endpoint=False),
{'ItemId': paid_course_reg_item.id, 'qty': '4'}
)
self.assertEqual(resp.status_code, 200)
# apply the coupon code to the item in the cart
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': coupon.code})
resp = self.client.post(
reverse('shoppingcart.views.use_code', is_dashboard_endpoint=False),
{'code': coupon.code}
)
self.assertEqual(resp.status_code, 200)
self.cart.purchase()
# get the updated item
......@@ -2277,7 +2416,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
coupon_redemption = CouponRedemption.objects.select_related('coupon').filter(order=self.cart)
sale_order_url = reverse('get_sale_order_records', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(sale_order_url)
response = self.client.post(sale_order_url)
self.assertEqual(response['Content-Type'], 'text/csv')
self.assertIn('36', response.content.split('\r\n')[1])
self.assertIn(str(item.unit_cost), response.content.split('\r\n')[1],)
......@@ -2301,11 +2440,18 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
PaidCourseRegistration.add_to_order(self.cart, self.course.id)
# apply the coupon code to the item in the cart
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': coupon.code})
resp = self.client.post(
reverse('shoppingcart.views.use_code', is_dashboard_endpoint=False),
{'code': coupon.code}
)
self.assertEqual(resp.status_code, 200)
# URL for instructor dashboard
instructor_dashboard = reverse('instructor_dashboard', kwargs={'course_id': self.course.id.to_deprecated_string()})
instructor_dashboard = reverse(
'instructor_dashboard',
kwargs={'course_id': self.course.id.to_deprecated_string()},
is_dashboard_endpoint=False
)
# visit the instructor dashboard page and
# check that the coupon redeem count should be 0
resp = self.client.get(instructor_dashboard)
......@@ -2342,7 +2488,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
'get_sale_records',
kwargs={'course_id': self.course.id.to_deprecated_string()}
)
response = self.client.get(url + '/csv', {})
response = self.client.post(url + '/csv', {})
self.assertEqual(response['Content-Type'], 'text/csv')
def test_get_sale_records_features_json(self):
......@@ -2361,7 +2507,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
course_registration_code.save()
url = reverse('get_sale_records', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {})
response = self.client.post(url, {})
res_json = json.loads(response.content)
self.assertIn('sale', res_json)
......@@ -2411,7 +2557,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
course_registration_code.save()
url = reverse('get_sale_records', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {})
response = self.client.post(url, {})
res_json = json.loads(response.content)
self.assertIn('sale', res_json)
......@@ -2459,7 +2605,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
)
problem_location = ''
response = self.client.get(url, {'problem_location': problem_location})
response = self.client.post(url, {'problem_location': problem_location})
res_json = json.loads(response.content)
self.assertEqual(res_json, 'Could not find problem with this location.')
......@@ -2494,7 +2640,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
)
problem_location = ''
response = self.client.get(url, {'problem_location': problem_location})
response = self.client.post(url, {'problem_location': problem_location})
res_json = json.loads(response.content)
self.assertIn('status', res_json)
status = res_json['status']
......@@ -2515,7 +2661,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
with patch('instructor_task.api.submit_calculate_problem_responses_csv') as submit_task_function:
error = AlreadyRunningError()
submit_task_function.side_effect = error
response = self.client.get(url, {})
response = self.client.post(url, {})
res_json = json.loads(response.content)
self.assertIn('status', res_json)
self.assertIn('already in progress', res_json['status'])
......@@ -2529,7 +2675,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
student.profile.city = "Mos Eisley {}".format(student.id)
student.profile.save()
url = reverse('get_students_features', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {})
response = self.client.post(url, {})
res_json = json.loads(response.content)
self.assertIn('students', res_json)
for student in self.students:
......@@ -2551,7 +2697,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
url = reverse('get_students_features', kwargs={'course_id': unicode(self.course.id)})
set_course_cohort_settings(self.course.id, is_cohorted=is_cohorted)
response = self.client.get(url, {})
response = self.client.post(url, {})
res_json = json.loads(response.content)
self.assertEqual('cohort' in res_json['feature_names'], is_cohorted)
......@@ -2571,7 +2717,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
url = reverse('get_students_features', kwargs={'course_id': unicode(self.course.id)})
response = self.client.get(url, {})
response = self.client.post(url, {})
res_json = json.loads(response.content)
self.assertEqual('team' in res_json['feature_names'], has_teams)
......@@ -2587,7 +2733,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
kwargs={'course_id': unicode(self.course.id)}
)
# Successful case:
response = self.client.get(url, {})
response = self.client.post(url, {})
res_json = json.loads(response.content)
self.assertIn('status', res_json)
self.assertNotIn('currently being created', res_json['status'])
......@@ -2595,7 +2741,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
with patch('instructor_task.api.submit_calculate_may_enroll_csv') as submit_task_function:
error = AlreadyRunningError()
submit_task_function.side_effect = error
response = self.client.get(url, {})
response = self.client.post(url, {})
res_json = json.loads(response.content)
self.assertIn('status', res_json)
self.assertIn('currently being created', res_json['status'])
......@@ -2610,7 +2756,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
kwargs={'course_id': unicode(self.course.id)}
)
# Successful case:
response = self.client.get(url, {})
response = self.client.post(url, {})
res_json = json.loads(response.content)
self.assertIn('status', res_json)
self.assertNotIn('currently being created', res_json['status'])
......@@ -2618,7 +2764,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
with patch('instructor_task.api.submit_proctored_exam_results_report') as submit_task_function:
error = AlreadyRunningError()
submit_task_function.side_effect = error
response = self.client.get(url, {})
response = self.client.post(url, {})
res_json = json.loads(response.content)
self.assertIn('status', res_json)
self.assertIn('currently being created', res_json['status'])
......@@ -2704,7 +2850,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
self.client.login(username=self.instructor.username, password='test')
url = reverse('get_enrollment_report', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {})
response = self.client.post(url, {})
self.assertIn('The detailed enrollment report is being created.', response.content)
def test_bulk_purchase_detailed_report(self):
......@@ -2716,11 +2862,16 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
"""
paid_course_reg_item = PaidCourseRegistration.add_to_order(self.cart, self.course.id)
# update the quantity of the cart item paid_course_reg_item
resp = self.client.post(reverse('shoppingcart.views.update_user_cart'),
{'ItemId': paid_course_reg_item.id, 'qty': '4'})
resp = self.client.post(
reverse('shoppingcart.views.update_user_cart', is_dashboard_endpoint=False),
{'ItemId': paid_course_reg_item.id, 'qty': '4'}
)
self.assertEqual(resp.status_code, 200)
# apply the coupon code to the item in the cart
resp = self.client.post(reverse('shoppingcart.views.use_code'), {'code': self.coupon_code})
resp = self.client.post(
reverse('shoppingcart.views.use_code', is_dashboard_endpoint=False),
{'code': self.coupon_code}
)
self.assertEqual(resp.status_code, 200)
self.cart.purchase()
......@@ -2754,7 +2905,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
self.client.login(username=self.instructor.username, password='test')
url = reverse('get_enrollment_report', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {})
response = self.client.post(url, {})
self.assertIn('The detailed enrollment report is being created.', response.content)
def test_create_registration_code_without_invoice_and_order(self):
......@@ -2776,7 +2927,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
self.client.login(username=self.instructor.username, password='test')
url = reverse('get_enrollment_report', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {})
response = self.client.post(url, {})
self.assertIn('The detailed enrollment report is being created.', response.content)
def test_invoice_payment_is_still_pending_for_registration_codes(self):
......@@ -2801,7 +2952,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
self.client.login(username=self.instructor.username, password='test')
url = reverse('get_enrollment_report', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {})
response = self.client.post(url, {})
self.assertIn('The detailed enrollment report is being created.', response.content)
@patch.object(instructor.views.api, 'anonymous_id_for_user', Mock(return_value='42'))
......@@ -2811,7 +2962,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
Test the CSV output for the anonymized user ids.
"""
url = reverse('get_anon_ids', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {})
response = self.client.post(url, {})
self.assertEqual(response['Content-Type'], 'text/csv')
body = response.content.replace('\r', '')
self.assertTrue(body.startswith(
......@@ -2829,7 +2980,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
('mock_file_name_1', 'https://1.mock.url'),
('mock_file_name_2', 'https://2.mock.url'),
]
response = self.client.get(url, {})
response = self.client.post(url, {})
expected_response = {
"downloads": [
......@@ -2858,12 +3009,12 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
success_status = "The {report_type} report is being created.".format(report_type=report_type)
if report_type == 'problem responses':
with patch(task_api_endpoint):
response = self.client.get(url, {'problem_location': ''})
response = self.client.post(url, {'problem_location': ''})
self.assertIn(success_status, response.content)
else:
CourseFinanceAdminRole(self.course.id).add_users(self.instructor)
with patch(task_api_endpoint):
response = self.client.get(url, {})
response = self.client.post(url, {})
self.assertIn(success_status, response.content)
@ddt.data(*EXECUTIVE_SUMMARY_DATA)
......@@ -2881,7 +3032,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
CourseFinanceAdminRole(self.course.id).add_users(self.instructor)
with patch(task_api_endpoint):
response = self.client.get(url, {})
response = self.client.post(url, {})
success_status = "The {report_type} report is being created." \
" To view the status of the report, see Pending" \
" Tasks below".format(report_type=report_type)
......@@ -2903,7 +3054,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
CourseFinanceAdminRole(self.course.id).add_users(self.instructor)
with patch(task_api_endpoint) as mock:
mock.side_effect = AlreadyRunningError()
response = self.client.get(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." \
" You will be able to download the report" \
......@@ -2916,7 +3067,7 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
with patch('instructor_task.api.submit_export_ora2_data') as mock_submit_ora2_task:
mock_submit_ora2_task.return_value = True
response = self.client.get(url, {})
response = self.client.post(url, {})
success_status = "The ORA data report is being generated."
self.assertIn(success_status, response.content)
......@@ -2925,17 +3076,15 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
with patch('instructor_task.api.submit_export_ora2_data') as mock_submit_ora2_task:
mock_submit_ora2_task.side_effect = AlreadyRunningError()
response = self.client.get(url, {})
response = self.client.post(url, {})
already_running_status = "An ORA data report generation task is already in progress."
self.assertIn(already_running_status, response.content)
def test_get_student_progress_url(self):
""" Test that progress_url is in the successful response. """
url = reverse('get_student_progress_url', kwargs={'course_id': self.course.id.to_deprecated_string()})
url += "?unique_student_identifier={}".format(
quote(self.students[0].email.encode("utf-8"))
)
response = self.client.get(url)
data = {'unique_student_identifier': self.students[0].email.encode("utf-8")}
response = self.client.post(url, data)
self.assertEqual(response.status_code, 200)
res_json = json.loads(response.content)
self.assertIn('progress_url', res_json)
......@@ -2943,10 +3092,8 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
def test_get_student_progress_url_from_uname(self):
""" Test that progress_url is in the successful response. """
url = reverse('get_student_progress_url', kwargs={'course_id': self.course.id.to_deprecated_string()})
url += "?unique_student_identifier={}".format(
quote(self.students[0].username.encode("utf-8"))
)
response = self.client.get(url)
data = {'unique_student_identifier': self.students[0].username.encode("utf-8")}
response = self.client.post(url, data)
self.assertEqual(response.status_code, 200)
res_json = json.loads(response.content)
self.assertIn('progress_url', res_json)
......@@ -2954,13 +3101,13 @@ class TestInstructorAPILevelsDataDump(SharedModuleStoreTestCase, LoginEnrollment
def test_get_student_progress_url_noparams(self):
""" Test that the endpoint 404's without the required query params. """
url = reverse('get_student_progress_url', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url)
response = self.client.post(url)
self.assertEqual(response.status_code, 400)
def test_get_student_progress_url_nostudent(self):
""" Test that the endpoint 400's when requesting an unknown email. """
url = reverse('get_student_progress_url', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url)
response = self.client.post(url)
self.assertEqual(response.status_code, 400)
......@@ -3001,7 +3148,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes
def test_reset_student_attempts_deletall(self):
""" Make sure no one can delete all students state on a problem. """
url = reverse('reset_student_attempts', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'problem_to_reset': self.problem_urlname,
'all_students': True,
'delete_module': True,
......@@ -3011,7 +3158,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes
def test_reset_student_attempts_single(self):
""" Test reset single student attempts. """
url = reverse('reset_student_attempts', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'problem_to_reset': self.problem_urlname,
'unique_student_identifier': self.student.email,
})
......@@ -3028,7 +3175,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes
def test_reset_student_attempts_all(self, act):
""" Test reset all student attempts. """
url = reverse('reset_student_attempts', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'problem_to_reset': self.problem_urlname,
'all_students': True,
})
......@@ -3038,7 +3185,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes
def test_reset_student_attempts_missingmodule(self):
""" Test reset for non-existant problem. """
url = reverse('reset_student_attempts', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'problem_to_reset': 'robot-not-a-real-module',
'unique_student_identifier': self.student.email,
})
......@@ -3047,7 +3194,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes
def test_reset_student_attempts_delete(self):
""" Test delete single student state. """
url = reverse('reset_student_attempts', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'problem_to_reset': self.problem_urlname,
'unique_student_identifier': self.student.email,
'delete_module': True,
......@@ -3066,7 +3213,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes
def test_reset_student_attempts_nonsense(self):
""" Test failure with both unique_student_identifier and all_students. """
url = reverse('reset_student_attempts', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'problem_to_reset': self.problem_urlname,
'unique_student_identifier': self.student.email,
'all_students': True,
......@@ -3077,7 +3224,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes
def test_rescore_problem_single(self, act):
""" Test rescoring of a single student. """
url = reverse('rescore_problem', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'problem_to_reset': self.problem_urlname,
'unique_student_identifier': self.student.email,
})
......@@ -3088,7 +3235,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes
def test_rescore_problem_single_from_uname(self, act):
""" Test rescoring of a single student. """
url = reverse('rescore_problem', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'problem_to_reset': self.problem_urlname,
'unique_student_identifier': self.student.username,
})
......@@ -3099,7 +3246,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes
def test_rescore_problem_all(self, act):
""" Test rescoring for all students. """
url = reverse('rescore_problem', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'problem_to_reset': self.problem_urlname,
'all_students': True,
})
......@@ -3111,7 +3258,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes
""" Test course has entrance exam id set while resetting attempts"""
url = reverse('reset_student_attempts_for_entrance_exam',
kwargs={'course_id': unicode(self.course.id)})
response = self.client.get(url, {
response = self.client.post(url, {
'all_students': True,
'delete_module': False,
})
......@@ -3121,7 +3268,7 @@ class TestInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginEnrollmentTes
def test_rescore_entrance_exam_with_invalid_exam(self):
""" Test course has entrance exam id set while re-scoring. """
url = reverse('rescore_entrance_exam', kwargs={'course_id': unicode(self.course.id)})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.student.email,
})
self.assertEqual(response.status_code, 400)
......@@ -3225,7 +3372,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE
""" Make sure no one can delete all students state on entrance exam. """
url = reverse('reset_student_attempts_for_entrance_exam',
kwargs={'course_id': unicode(self.course.id)})
response = self.client.get(url, {
response = self.client.post(url, {
'all_students': True,
'delete_module': True,
})
......@@ -3235,7 +3382,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE
""" Test reset single student attempts for entrance exam. """
url = reverse('reset_student_attempts_for_entrance_exam',
kwargs={'course_id': unicode(self.course.id)})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.student.email,
})
self.assertEqual(response.status_code, 200)
......@@ -3253,7 +3400,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE
""" Test reset all student attempts for entrance exam. """
url = reverse('reset_student_attempts_for_entrance_exam',
kwargs={'course_id': unicode(self.course.id)})
response = self.client.get(url, {
response = self.client.post(url, {
'all_students': True,
})
self.assertEqual(response.status_code, 200)
......@@ -3263,7 +3410,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE
""" Test reset for invalid entrance exam. """
url = reverse('reset_student_attempts_for_entrance_exam',
kwargs={'course_id': unicode(self.course_with_invalid_ee.id)})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.student.email,
})
self.assertEqual(response.status_code, 400)
......@@ -3272,7 +3419,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE
""" Test delete single student entrance exam state. """
url = reverse('reset_student_attempts_for_entrance_exam',
kwargs={'course_id': unicode(self.course.id)})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.student.email,
'delete_module': True,
})
......@@ -3288,7 +3435,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE
self.client.login(username=staff_user.username, password='test')
url = reverse('reset_student_attempts_for_entrance_exam',
kwargs={'course_id': unicode(self.course.id)})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.student.email,
'delete_module': True,
})
......@@ -3298,7 +3445,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE
""" Test failure with both unique_student_identifier and all_students. """
url = reverse('reset_student_attempts_for_entrance_exam',
kwargs={'course_id': unicode(self.course.id)})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.student.email,
'all_students': True,
})
......@@ -3308,7 +3455,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE
def test_rescore_entrance_exam_single_student(self, act):
""" Test re-scoring of entrance exam for single student. """
url = reverse('rescore_entrance_exam', kwargs={'course_id': unicode(self.course.id)})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.student.email,
})
self.assertEqual(response.status_code, 200)
......@@ -3317,7 +3464,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE
def test_rescore_entrance_exam_all_student(self):
""" Test rescoring for all students. """
url = reverse('rescore_entrance_exam', kwargs={'course_id': unicode(self.course.id)})
response = self.client.get(url, {
response = self.client.post(url, {
'all_students': True,
})
self.assertEqual(response.status_code, 200)
......@@ -3325,7 +3472,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE
def test_rescore_entrance_exam_all_student_and_single(self):
""" Test re-scoring with both all students and single student parameters. """
url = reverse('rescore_entrance_exam', kwargs={'course_id': unicode(self.course.id)})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.student.email,
'all_students': True,
})
......@@ -3334,7 +3481,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE
def test_rescore_entrance_exam_with_invalid_exam(self):
""" Test re-scoring of entrance exam with invalid exam. """
url = reverse('rescore_entrance_exam', kwargs={'course_id': unicode(self.course_with_invalid_ee.id)})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.student.email,
})
self.assertEqual(response.status_code, 400)
......@@ -3343,13 +3490,13 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE
""" Test list task history for entrance exam AND student. """
# create a re-score entrance exam task
url = reverse('rescore_entrance_exam', kwargs={'course_id': unicode(self.course.id)})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.student.email,
})
self.assertEqual(response.status_code, 200)
url = reverse('list_entrance_exam_instructor_tasks', kwargs={'course_id': unicode(self.course.id)})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.student.email,
})
self.assertEqual(response.status_code, 200)
......@@ -3362,7 +3509,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE
def test_list_entrance_exam_instructor_tasks_all_student(self):
""" Test list task history for entrance exam AND all student. """
url = reverse('list_entrance_exam_instructor_tasks', kwargs={'course_id': unicode(self.course.id)})
response = self.client.get(url, {})
response = self.client.post(url, {})
self.assertEqual(response.status_code, 200)
# check response
......@@ -3373,7 +3520,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE
""" Test list task history for entrance exam failure if course has invalid exam. """
url = reverse('list_entrance_exam_instructor_tasks',
kwargs={'course_id': unicode(self.course_with_invalid_ee.id)})
response = self.client.get(url, {
response = self.client.post(url, {
'unique_student_identifier': self.student.email,
})
self.assertEqual(response.status_code, 400)
......@@ -3589,7 +3736,7 @@ class TestInstructorAPITaskLists(SharedModuleStoreTestCase, LoginEnrollmentTestC
mock_factory = MockCompletionInfo()
with patch('instructor.views.instructor_task_helpers.get_task_completion_info') as mock_completion_info:
mock_completion_info.side_effect = mock_factory.mock_get_task_completion_info
response = self.client.get(url, {})
response = self.client.post(url, {})
self.assertEqual(response.status_code, 200)
# check response
......@@ -3608,7 +3755,7 @@ class TestInstructorAPITaskLists(SharedModuleStoreTestCase, LoginEnrollmentTestC
mock_factory = MockCompletionInfo()
with patch('instructor.views.instructor_task_helpers.get_task_completion_info') as mock_completion_info:
mock_completion_info.side_effect = mock_factory.mock_get_task_completion_info
response = self.client.get(url, {})
response = self.client.post(url, {})
self.assertEqual(response.status_code, 200)
# check response
......@@ -3627,7 +3774,7 @@ class TestInstructorAPITaskLists(SharedModuleStoreTestCase, LoginEnrollmentTestC
mock_factory = MockCompletionInfo()
with patch('instructor.views.instructor_task_helpers.get_task_completion_info') as mock_completion_info:
mock_completion_info.side_effect = mock_factory.mock_get_task_completion_info
response = self.client.get(url, {
response = self.client.post(url, {
'problem_location_str': self.problem_urlname,
})
self.assertEqual(response.status_code, 200)
......@@ -3648,7 +3795,7 @@ class TestInstructorAPITaskLists(SharedModuleStoreTestCase, LoginEnrollmentTestC
mock_factory = MockCompletionInfo()
with patch('instructor.views.instructor_task_helpers.get_task_completion_info') as mock_completion_info:
mock_completion_info.side_effect = mock_factory.mock_get_task_completion_info
response = self.client.get(url, {
response = self.client.post(url, {
'problem_location_str': self.problem_urlname,
'unique_student_identifier': self.student.email,
})
......@@ -3709,7 +3856,7 @@ class TestInstructorEmailContentList(SharedModuleStoreTestCase, LoginEnrollmentT
url = reverse('list_email_content', kwargs={'course_id': self.course.id.to_deprecated_string()})
with patch('instructor.views.api.CourseEmail.objects.get') as mock_email_info:
mock_email_info.side_effect = self.get_matching_mock_email
response = self.client.get(url, {})
response = self.client.post(url, {})
self.assertEqual(response.status_code, 200)
return response
......@@ -3760,7 +3907,7 @@ class TestInstructorEmailContentList(SharedModuleStoreTestCase, LoginEnrollmentT
invalid_task.make_invalid_input()
task_history_request.return_value = [invalid_task]
url = reverse('list_email_content', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {})
response = self.client.post(url, {})
self.assertEqual(response.status_code, 200)
self.assertTrue(task_history_request.called)
......@@ -3786,7 +3933,7 @@ class TestInstructorEmailContentList(SharedModuleStoreTestCase, LoginEnrollmentT
url = reverse('list_email_content', kwargs={'course_id': self.course.id.to_deprecated_string()})
with patch('instructor.views.api.CourseEmail.objects.get') as mock_email_info:
mock_email_info.return_value = email
response = self.client.get(url, {})
response = self.client.post(url, {})
self.assertEqual(response.status_code, 200)
self.assertTrue(task_history_request.called)
......@@ -3937,7 +4084,7 @@ class TestDueDateExtensions(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
def test_change_due_date(self):
url = reverse('change_due_date', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'student': self.user1.username,
'url': self.week1.location.to_deprecated_string(),
'due_datetime': '12/30/2013 00:00'
......@@ -3948,7 +4095,7 @@ class TestDueDateExtensions(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
def test_change_to_invalid_due_date(self):
url = reverse('change_due_date', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'student': self.user1.username,
'url': self.week1.location.to_deprecated_string(),
'due_datetime': '01/01/2009 00:00'
......@@ -3961,7 +4108,7 @@ class TestDueDateExtensions(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
def test_change_nonexistent_due_date(self):
url = reverse('change_due_date', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'student': self.user1.username,
'url': self.week3.location.to_deprecated_string(),
'due_datetime': '12/30/2013 00:00'
......@@ -3975,7 +4122,7 @@ class TestDueDateExtensions(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
def test_reset_date(self):
self.test_change_due_date()
url = reverse('reset_due_date', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'student': self.user1.username,
'url': self.week1.location.to_deprecated_string(),
})
......@@ -3987,7 +4134,7 @@ class TestDueDateExtensions(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
def test_reset_nonexistent_extension(self):
url = reverse('reset_due_date', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'student': self.user1.username,
'url': self.week1.location.to_deprecated_string(),
})
......@@ -3997,7 +4144,7 @@ class TestDueDateExtensions(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
self.test_change_due_date()
url = reverse('show_unit_extensions',
kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {'url': self.week1.location.to_deprecated_string()})
response = self.client.post(url, {'url': self.week1.location.to_deprecated_string()})
self.assertEqual(response.status_code, 200, response.content)
self.assertEqual(json.loads(response.content), {
u'data': [{u'Extended Due Date': u'2013-12-30 00:00',
......@@ -4011,7 +4158,7 @@ class TestDueDateExtensions(SharedModuleStoreTestCase, LoginEnrollmentTestCase):
self.test_change_due_date()
url = reverse('show_student_extensions',
kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {'student': self.user1.username})
response = self.client.post(url, {'student': self.user1.username})
self.assertEqual(response.status_code, 200, response.content)
self.assertEqual(json.loads(response.content), {
u'data': [{u'Extended Due Date': u'2013-12-30 00:00',
......@@ -4105,7 +4252,7 @@ class TestDueDateExtensionsDeletedDate(ModuleStoreTestCase, LoginEnrollmentTestC
"""
url = reverse('change_due_date', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'student': self.user1.username,
'url': self.week1.location.to_deprecated_string(),
'due_datetime': '12/30/2013 00:00'
......@@ -4118,7 +4265,7 @@ class TestDueDateExtensionsDeletedDate(ModuleStoreTestCase, LoginEnrollmentTestC
self.week1 = self.store.update_item(self.week1, self.user1.id)
# Now, week1's normal due date is deleted but the extension still exists.
url = reverse('reset_due_date', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.get(url, {
response = self.client.post(url, {
'student': self.user1.username,
'url': self.week1.location.to_deprecated_string(),
})
......@@ -4166,14 +4313,14 @@ class TestCourseIssuedCertificatesData(SharedModuleStoreTestCase):
for __ in xrange(certificate_count):
self.generate_certificate(course_id=self.course.id, mode='honor', status=CertificateStatuses.generating)
response = self.client.get(url)
response = self.client.post(url)
res_json = json.loads(response.content)
self.assertIn('certificates', res_json)
self.assertEqual(len(res_json['certificates']), 0)
# Certificates with status 'downloadable' should be in response.
self.generate_certificate(course_id=self.course.id, mode='honor', status=CertificateStatuses.downloadable)
response = self.client.get(url)
response = self.client.post(url)
res_json = json.loads(response.content)
self.assertIn('certificates', res_json)
self.assertEqual(len(res_json['certificates']), 1)
......@@ -4188,7 +4335,7 @@ class TestCourseIssuedCertificatesData(SharedModuleStoreTestCase):
for __ in xrange(certificate_count):
self.generate_certificate(course_id=self.course.id, mode='honor', status=CertificateStatuses.downloadable)
response = self.client.get(url)
response = self.client.post(url)
res_json = json.loads(response.content)
self.assertIn('certificates', res_json)
self.assertEqual(len(res_json['certificates']), 1)
......@@ -4207,7 +4354,7 @@ class TestCourseIssuedCertificatesData(SharedModuleStoreTestCase):
status=CertificateStatuses.downloadable
)
response = self.client.get(url)
response = self.client.post(url)
res_json = json.loads(response.content)
self.assertIn('certificates', res_json)
......@@ -4224,14 +4371,13 @@ class TestCourseIssuedCertificatesData(SharedModuleStoreTestCase):
Test for certificate csv features.
"""
url = reverse('get_issued_certificates', kwargs={'course_id': unicode(self.course.id)})
url += '?csv=true'
# firstly generating downloadable certificates with 'honor' mode
certificate_count = 3
for __ in xrange(certificate_count):
self.generate_certificate(course_id=self.course.id, mode='honor', status=CertificateStatuses.downloadable)
current_date = datetime.date.today().strftime("%B %d, %Y")
response = self.client.get(url)
response = self.client.get(url, {'csv': 'true'})
self.assertEqual(response['Content-Type'], 'text/csv')
self.assertEqual(response['Content-Disposition'], 'attachment; filename={0}'.format('issued_certificates.csv'))
self.assertEqual(
......@@ -4680,7 +4826,7 @@ class TestCourseRegistrationCodes(SharedModuleStoreTestCase):
)
coupon.save()
response = self.client.get(get_coupon_code_url)
response = self.client.post(get_coupon_code_url)
self.assertEqual(response.status_code, 200, response.content)
# filter all the coupons
for coupon in Coupon.objects.all():
......@@ -4721,7 +4867,7 @@ class TestBulkCohorting(SharedModuleStoreTestCase):
self.tempdir = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, self.tempdir)
def call_add_users_to_cohorts(self, csv_data, suffix='.csv', method='POST'):
def call_add_users_to_cohorts(self, csv_data, suffix='.csv'):
"""
Call `add_users_to_cohorts` with a file generated from `csv_data`.
"""
......@@ -4731,10 +4877,7 @@ class TestBulkCohorting(SharedModuleStoreTestCase):
file_pointer.write(csv_data.encode('utf-8'))
with open(file_name, 'r') as file_pointer:
url = reverse('add_users_to_cohorts', kwargs={'course_id': unicode(self.course.id)})
if method == 'POST':
return self.client.post(url, {'uploaded-file': file_pointer})
elif method == 'GET':
return self.client.get(url, {'uploaded-file': file_pointer})
return self.client.post(url, {'uploaded-file': file_pointer})
def expect_error_on_file_content(self, file_content, error, file_suffix='.csv'):
"""
......@@ -4803,14 +4946,6 @@ class TestBulkCohorting(SharedModuleStoreTestCase):
response = self.call_add_users_to_cohorts('')
self.assertEqual(response.status_code, 403)
def test_post_only(self):
"""
Verify that we can't call the view when we aren't using POST.
"""
self.client.login(username=self.staff_user.username, password='test')
response = self.call_add_users_to_cohorts('', method='GET')
self.assertEqual(response.status_code, 405)
@patch('instructor.views.api.instructor_task.api.submit_cohort_students')
@patch('instructor.views.api.store_uploaded_file')
def test_success_username(self, mock_store_upload, mock_cohort_task):
......
......@@ -140,51 +140,14 @@ def common_exceptions_400(func):
return wrapped
def require_query_params(*args, **kwargs):
"""
Checks for required paremters or renders a 400 error.
(decorator with arguments)
`args` is a *list of required GET parameter names.
`kwargs` is a **dict of required GET parameter names
to string explanations of the parameter
"""
required_params = []
required_params += [(arg, None) for arg in args]
required_params += [(key, kwargs[key]) for key in kwargs]
# required_params = e.g. [('action', 'enroll or unenroll'), ['emails', None]]
def decorator(func): # pylint: disable=missing-docstring
def wrapped(*args, **kwargs): # pylint: disable=missing-docstring
request = args[0]
error_response_data = {
'error': 'Missing required query parameter(s)',
'parameters': [],
'info': {},
}
for (param, extra) in required_params:
default = object()
if request.GET.get(param, default) == default:
error_response_data['parameters'].append(param)
error_response_data['info'][param] = extra
if len(error_response_data['parameters']) > 0:
return JsonResponse(error_response_data, status=400)
else:
return func(*args, **kwargs)
return wrapped
return decorator
def require_post_params(*args, **kwargs):
"""
Checks for required parameters or renders a 400 error.
(decorator with arguments)
Functions like 'require_query_params', but checks for
POST parameters rather than GET parameters.
`args` is a *list of required POST parameter names.
`kwargs` is a **dict of required POST parameter names
to string explanations of the parameter
"""
required_params = []
required_params += [(arg, None) for arg in args]
......@@ -314,6 +277,7 @@ NAME_INDEX = 2
COUNTRY_INDEX = 3
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -604,6 +568,7 @@ def create_and_enroll_user(email, username, name, country, password, course_id,
return errors
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -768,6 +733,7 @@ def students_update_enrollment(request, course_id):
return JsonResponse(response_payload)
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('instructor')
......@@ -848,11 +814,12 @@ def bulk_beta_modify_access(request, course_id):
return JsonResponse(response_payload)
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('instructor')
@common_exceptions_400
@require_query_params(
@require_post_params(
unique_student_identifier="email or username of user to change access",
rolename="'instructor', 'staff', 'beta', or 'ccx_coach'",
action="'allow' or 'revoke'"
......@@ -874,10 +841,10 @@ def modify_access(request, course_id):
request.user, 'instructor', course_id, depth=None
)
try:
user = get_student_from_identifier(request.GET.get('unique_student_identifier'))
user = get_student_from_identifier(request.POST.get('unique_student_identifier'))
except User.DoesNotExist:
response_payload = {
'unique_student_identifier': request.GET.get('unique_student_identifier'),
'unique_student_identifier': request.POST.get('unique_student_identifier'),
'userDoesNotExist': True,
}
return JsonResponse(response_payload)
......@@ -892,8 +859,8 @@ def modify_access(request, course_id):
}
return JsonResponse(response_payload)
rolename = request.GET.get('rolename')
action = request.GET.get('action')
rolename = request.POST.get('rolename')
action = request.POST.get('action')
if rolename not in ROLES:
error = strip_tags("unknown rolename '{}'".format(rolename))
......@@ -928,10 +895,11 @@ def modify_access(request, course_id):
return JsonResponse(response_payload)
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('instructor')
@require_query_params(rolename="'instructor', 'staff', or 'beta'")
@require_post_params(rolename="'instructor', 'staff', or 'beta'")
def list_course_role_members(request, course_id):
"""
List instructors and staff.
......@@ -956,7 +924,7 @@ def list_course_role_members(request, course_id):
request.user, 'instructor', course_id, depth=None
)
rolename = request.GET.get('rolename')
rolename = request.POST.get('rolename')
if rolename not in ROLES:
return HttpResponseBadRequest()
......@@ -980,6 +948,7 @@ def list_course_role_members(request, course_id):
@transaction.non_atomic_requests
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -996,7 +965,7 @@ def get_problem_responses(request, course_id):
Responds with BadRequest if problem location is faulty.
"""
course_key = CourseKey.from_string(course_id)
problem_location = request.GET.get('problem_location', '')
problem_location = request.POST.get('problem_location', '')
try:
problem_key = UsageKey.from_string(problem_location)
......@@ -1025,6 +994,7 @@ def get_problem_responses(request, course_id):
return JsonResponse({"status": already_running_status})
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -1223,6 +1193,7 @@ def get_issued_certificates(request, course_id):
@transaction.non_atomic_requests
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -1315,6 +1286,7 @@ def get_students_features(request, course_id, csv=False): # pylint: disable=red
@transaction.non_atomic_requests
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -1422,6 +1394,7 @@ def get_coupon_codes(request, course_id): # pylint: disable=unused-argument
@transaction.non_atomic_requests
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -1446,6 +1419,7 @@ def get_enrollment_report(request, course_id):
@transaction.non_atomic_requests
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -1471,6 +1445,7 @@ def get_exec_summary_report(request, course_id):
@transaction.non_atomic_requests
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -1495,6 +1470,7 @@ def get_course_survey_results(request, course_id):
@transaction.non_atomic_requests
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -1901,11 +1877,12 @@ def get_anon_ids(request, course_id): # pylint: disable=unused-argument
return csv_response(course_id.to_deprecated_string().replace('/', '-') + '-anon-ids.csv', header, rows)
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@common_exceptions_400
@require_level('staff')
@require_query_params(
@require_post_params(
unique_student_identifier="email or username of student for whom to get progress url"
)
def get_student_progress_url(request, course_id):
......@@ -1913,13 +1890,13 @@ def get_student_progress_url(request, course_id):
Get the progress url of a student.
Limited to staff access.
Takes query paremeter unique_student_identifier and if the student exists
Takes query parameter unique_student_identifier and if the student exists
returns e.g. {
'progress_url': '/../...'
}
"""
course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
user = get_student_from_identifier(request.GET.get('unique_student_identifier'))
user = get_student_from_identifier(request.POST.get('unique_student_identifier'))
progress_url = reverse('student_progress', kwargs={'course_id': course_id.to_deprecated_string(), 'student_id': user.id})
......@@ -1931,10 +1908,11 @@ def get_student_progress_url(request, course_id):
@transaction.non_atomic_requests
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
@require_query_params(
@require_post_params(
problem_to_reset="problem urlname to reset"
)
@common_exceptions_400
......@@ -1961,13 +1939,13 @@ def reset_student_attempts(request, course_id):
request.user, 'staff', course_id, depth=None
)
problem_to_reset = strip_if_string(request.GET.get('problem_to_reset'))
student_identifier = request.GET.get('unique_student_identifier', None)
problem_to_reset = strip_if_string(request.POST.get('problem_to_reset'))
student_identifier = request.POST.get('unique_student_identifier', None)
student = None
if student_identifier is not None:
student = get_student_from_identifier(student_identifier)
all_students = request.GET.get('all_students', False) in ['true', 'True', True]
delete_module = request.GET.get('delete_module', False) in ['true', 'True', True]
all_students = request.POST.get('all_students', False) in ['true', 'True', True]
delete_module = request.POST.get('delete_module', False) in ['true', 'True', True]
# parameter combinations
if all_students and student:
......@@ -2019,6 +1997,7 @@ def reset_student_attempts(request, course_id):
@transaction.non_atomic_requests
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -2049,12 +2028,12 @@ def reset_student_attempts_for_entrance_exam(request, course_id): # pylint: dis
_("Course has no entrance exam section.")
)
student_identifier = request.GET.get('unique_student_identifier', None)
student_identifier = request.POST.get('unique_student_identifier', None)
student = None
if student_identifier is not None:
student = get_student_from_identifier(student_identifier)
all_students = request.GET.get('all_students', False) in ['true', 'True', True]
delete_module = request.GET.get('delete_module', False) in ['true', 'True', True]
all_students = request.POST.get('all_students', False) in ['true', 'True', True]
delete_module = request.POST.get('delete_module', False) in ['true', 'True', True]
# parameter combinations
if all_students and student:
......@@ -2085,10 +2064,11 @@ def reset_student_attempts_for_entrance_exam(request, course_id): # pylint: dis
@transaction.non_atomic_requests
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('instructor')
@require_query_params(problem_to_reset="problem urlname to reset")
@require_post_params(problem_to_reset="problem urlname to reset")
@common_exceptions_400
def rescore_problem(request, course_id):
"""
......@@ -2103,13 +2083,13 @@ def rescore_problem(request, course_id):
all_students and unique_student_identifier cannot both be present.
"""
course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
problem_to_reset = strip_if_string(request.GET.get('problem_to_reset'))
student_identifier = request.GET.get('unique_student_identifier', None)
problem_to_reset = strip_if_string(request.POST.get('problem_to_reset'))
student_identifier = request.POST.get('unique_student_identifier', None)
student = None
if student_identifier is not None:
student = get_student_from_identifier(student_identifier)
all_students = request.GET.get('all_students') in ['true', 'True', True]
all_students = request.POST.get('all_students') in ['true', 'True', True]
if not (problem_to_reset and (all_students or student)):
return HttpResponseBadRequest("Missing query parameters.")
......@@ -2141,6 +2121,7 @@ def rescore_problem(request, course_id):
@transaction.non_atomic_requests
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('instructor')
......@@ -2161,12 +2142,12 @@ def rescore_entrance_exam(request, course_id):
request.user, 'staff', course_id, depth=None
)
student_identifier = request.GET.get('unique_student_identifier', None)
student_identifier = request.POST.get('unique_student_identifier', None)
student = None
if student_identifier is not None:
student = get_student_from_identifier(student_identifier)
all_students = request.GET.get('all_students') in ['true', 'True', True]
all_students = request.POST.get('all_students') in ['true', 'True', True]
if not course.entrance_exam_id:
return HttpResponseBadRequest(
......@@ -2193,6 +2174,7 @@ def rescore_entrance_exam(request, course_id):
return JsonResponse(response_payload)
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -2211,6 +2193,7 @@ def list_background_email_tasks(request, course_id): # pylint: disable=unused-a
return JsonResponse(response_payload)
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -2229,6 +2212,7 @@ def list_email_content(request, course_id): # pylint: disable=unused-argument
return JsonResponse(response_payload)
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -2243,8 +2227,8 @@ def list_instructor_tasks(request, course_id):
history for problem AND student (intersection)
"""
course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
problem_location_str = strip_if_string(request.GET.get('problem_location_str', False))
student = request.GET.get('unique_student_identifier', None)
problem_location_str = strip_if_string(request.POST.get('problem_location_str', False))
student = request.POST.get('unique_student_identifier', None)
if student is not None:
student = get_student_from_identifier(student)
......@@ -2274,6 +2258,7 @@ def list_instructor_tasks(request, course_id):
return JsonResponse(response_payload)
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -2287,7 +2272,7 @@ def list_entrance_exam_instructor_tasks(request, course_id): # pylint: disable=
"""
course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id)
course = get_course_by_id(course_id)
student = request.GET.get('unique_student_identifier', None)
student = request.POST.get('unique_student_identifier', None)
if student is not None:
student = get_student_from_identifier(student)
......@@ -2308,6 +2293,7 @@ def list_entrance_exam_instructor_tasks(request, course_id): # pylint: disable=
return JsonResponse(response_payload)
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -2327,6 +2313,7 @@ def list_report_downloads(_request, course_id):
return JsonResponse(response_payload)
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -2348,6 +2335,7 @@ def list_financial_report_downloads(_request, course_id):
@transaction.non_atomic_requests
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -2373,6 +2361,7 @@ def export_ora2_data(request, course_id):
@transaction.non_atomic_requests
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -2394,6 +2383,7 @@ def calculate_grades_csv(request, course_id):
@transaction.non_atomic_requests
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -2420,10 +2410,11 @@ def problem_grade_report(request, course_id):
})
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
@require_query_params('rolename')
@require_post_params('rolename')
def list_forum_members(request, course_id):
"""
Lists forum members of a certain rolename.
......@@ -2442,7 +2433,7 @@ def list_forum_members(request, course_id):
request.user, course_id, FORUM_ROLE_ADMINISTRATOR
)
rolename = request.GET.get('rolename')
rolename = request.POST.get('rolename')
# default roles require either (staff & forum admin) or (instructor)
if not (has_forum_admin or has_instructor_access):
......@@ -2483,6 +2474,7 @@ def list_forum_members(request, course_id):
@transaction.non_atomic_requests
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
......@@ -2539,10 +2531,11 @@ def send_email(request, course_id):
return JsonResponse(response_payload)
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
@require_query_params(
@require_post_params(
unique_student_identifier="email or username of user to change access",
rolename="the forum role",
action="'allow' or 'revoke'",
......@@ -2569,9 +2562,9 @@ def update_forum_role_membership(request, course_id):
request.user, course_id, FORUM_ROLE_ADMINISTRATOR
)
unique_student_identifier = request.GET.get('unique_student_identifier')
rolename = request.GET.get('rolename')
action = request.GET.get('action')
unique_student_identifier = request.POST.get('unique_student_identifier')
rolename = request.POST.get('rolename')
action = request.POST.get('action')
# default roles require either (staff & forum admin) or (instructor)
if not (has_forum_admin or has_instructor_access):
......@@ -2629,18 +2622,19 @@ def _display_unit(unit):
@handle_dashboard_error
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
@require_query_params('student', 'url', 'due_datetime')
@require_post_params('student', 'url', 'due_datetime')
def change_due_date(request, course_id):
"""
Grants a due date extension to a student for a particular unit.
"""
course = get_course_by_id(SlashSeparatedCourseKey.from_deprecated_string(course_id))
student = require_student_from_identifier(request.GET.get('student'))
unit = find_unit(course, request.GET.get('url'))
due_date = parse_datetime(request.GET.get('due_datetime'))
student = require_student_from_identifier(request.POST.get('student'))
unit = find_unit(course, request.POST.get('url'))
due_date = parse_datetime(request.POST.get('due_datetime'))
set_due_date_extension(course, unit, student, due_date)
return JsonResponse(_(
......@@ -2650,17 +2644,18 @@ def change_due_date(request, course_id):
@handle_dashboard_error
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
@require_query_params('student', 'url')
@require_post_params('student', 'url')
def reset_due_date(request, course_id):
"""
Rescinds a due date extension for a student on a particular unit.
"""
course = get_course_by_id(SlashSeparatedCourseKey.from_deprecated_string(course_id))
student = require_student_from_identifier(request.GET.get('student'))
unit = find_unit(course, request.GET.get('url'))
student = require_student_from_identifier(request.POST.get('student'))
unit = find_unit(course, request.POST.get('url'))
set_due_date_extension(course, unit, student, None)
if not getattr(unit, "due", None):
# It's possible the normal due date was deleted after an extension was granted:
......@@ -2676,30 +2671,32 @@ def reset_due_date(request, course_id):
@handle_dashboard_error
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
@require_query_params('url')
@require_post_params('url')
def show_unit_extensions(request, course_id):
"""
Shows all of the students which have due date extensions for the given unit.
"""
course = get_course_by_id(SlashSeparatedCourseKey.from_deprecated_string(course_id))
unit = find_unit(course, request.GET.get('url'))
unit = find_unit(course, request.POST.get('url'))
return JsonResponse(dump_module_extensions(course, unit))
@handle_dashboard_error
@require_POST
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
@require_query_params('student')
@require_post_params('student')
def show_student_extensions(request, course_id):
"""
Shows all of the due date extensions granted to a particular student in a
particular course.
"""
student = require_student_from_identifier(request.GET.get('student'))
student = require_student_from_identifier(request.POST.get('student'))
course = get_course_by_id(SlashSeparatedCourseKey.from_deprecated_string(course_id))
return JsonResponse(dump_student_extensions(course, student))
......@@ -3076,7 +3073,6 @@ def generate_certificate_exceptions(request, course_id, generate_for=None):
return JsonResponse(response_payload)
@csrf_exempt
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_global_staff
@require_POST
......
......@@ -110,6 +110,7 @@ class DataDownload
url = @$list_proctored_exam_results_csv_btn.data 'endpoint'
# display html from proctored exam results config endpoint
$.ajax
type: 'POST'
dataType: 'json'
url: url
error: (std_ajax_err) =>
......@@ -129,6 +130,7 @@ class DataDownload
url = @$survey_results_csv_btn.data 'endpoint'
# display html from survey results config endpoint
$.ajax
type: 'POST'
dataType: 'json'
url: url
error: (std_ajax_err) =>
......@@ -153,6 +155,7 @@ class DataDownload
url += '/csv'
$.ajax
type: 'POST'
dataType: 'json'
url: url
error: (std_ajax_err) =>
......@@ -171,6 +174,7 @@ class DataDownload
# fetch user list
$.ajax
type: 'POST'
dataType: 'json'
url: url
error: (std_ajax_err) =>
......@@ -199,6 +203,7 @@ class DataDownload
url = @$list_problem_responses_csv_btn.data 'endpoint'
$.ajax
type: 'POST'
dataType: 'json'
url: url
data:
......@@ -215,6 +220,7 @@ class DataDownload
url = @$list_may_enroll_csv_btn.data 'endpoint'
$.ajax
type: 'POST'
dataType: 'json'
url: url
error: (std_ajax_err) =>
......@@ -228,6 +234,7 @@ class DataDownload
url = @$grade_config_btn.data 'endpoint'
# display html from grading config endpoint
$.ajax
type: 'POST'
dataType: 'json'
url: url
error: (std_ajax_err) =>
......@@ -244,6 +251,7 @@ class DataDownload
@clear_display()
url = $(e.target).data 'endpoint'
$.ajax
type: 'POST'
dataType: 'json'
url: url
error: std_ajax_err =>
......
......@@ -45,6 +45,7 @@ class Extensions
due_datetime: @$due_datetime_input.val()
$.ajax
type: 'POST'
dataType: 'json'
url: @$change_due_date.data 'endpoint'
data: send_data
......@@ -60,6 +61,7 @@ class Extensions
url: @$url_input.val()
$.ajax
type: 'POST'
dataType: 'json'
url: @$reset_due_date.data 'endpoint'
data: send_data
......@@ -75,6 +77,7 @@ class Extensions
send_data =
url: @$url_input.val()
$.ajax
type: 'POST'
dataType: 'json'
url: url
data: send_data
......@@ -90,6 +93,7 @@ class Extensions
send_data =
student: @$student_input.val()
$.ajax
type: 'POST'
dataType: 'json'
url: url
data: send_data
......
......@@ -137,6 +137,7 @@ class AuthListWidget extends MemberListWidget
# `cb` is called with cb(error, member_list)
get_member_list: (cb) ->
$.ajax
type: 'POST'
dataType: 'json'
url: @list_endpoint
data: rolename: @rolename
......@@ -151,6 +152,7 @@ class AuthListWidget extends MemberListWidget
# `cb` is called with cb(error, data)
modify_member_access: (unique_student_identifier, action, cb) ->
$.ajax
type: 'POST'
dataType: 'json'
url: @modify_endpoint
data:
......@@ -645,6 +647,7 @@ class AuthList
# the endpoint comes from data-endpoint of the table
$.ajax
dataType: 'json'
type: 'POST'
url: @$display_table.data 'endpoint'
data: rolename: @rolename
success: load_auth_list
......@@ -664,6 +667,7 @@ class AuthList
access_change: (email, action, cb) ->
$.ajax
dataType: 'json'
type: 'POST'
url: @$add_section.data 'endpoint'
data:
email: email
......
......@@ -99,6 +99,7 @@ class @SendEmail
@$btn_task_history_email.click =>
url = @$btn_task_history_email.data 'endpoint'
$.ajax
type: 'POST'
dataType: 'json'
url: url
success: (data) =>
......@@ -115,6 +116,7 @@ class @SendEmail
@$btn_task_history_email_content.click =>
url = @$btn_task_history_email_content.data 'endpoint'
$.ajax
type: 'POST'
dataType: 'json'
url : url
success: (data) =>
......
......@@ -76,6 +76,7 @@ class @StudentAdmin
full_error_message = _.template(error_message)({student_id: unique_student_identifier})
$.ajax
type: 'POST'
dataType: 'json'
url: @$progress_link.data 'endpoint'
data: unique_student_identifier: unique_student_identifier
......@@ -101,6 +102,7 @@ class @StudentAdmin
full_error_message = _.template(error_message)({problem_id: problem_to_reset, student_id: unique_student_identifier})
$.ajax
type: 'POST'
dataType: 'json'
url: @$btn_reset_attempts_single.data 'endpoint'
data: send_data
......@@ -127,6 +129,7 @@ class @StudentAdmin
full_error_message = _.template(error_message)({student_id: unique_student_identifier, problem_id: problem_to_reset})
$.ajax
type: 'POST'
dataType: 'json'
url: @$btn_delete_state_single.data 'endpoint'
data: send_data
......@@ -153,6 +156,7 @@ class @StudentAdmin
full_error_message = _.template(error_message)({student_id: unique_student_identifier, problem_id: problem_to_reset})
$.ajax
type: 'POST'
dataType: 'json'
url: @$btn_rescore_problem_single.data 'endpoint'
data: send_data
......@@ -174,6 +178,7 @@ class @StudentAdmin
full_error_message = _.template(error_message)({student_id: unique_student_identifier, problem_id: problem_to_reset})
$.ajax
type: 'POST'
dataType: 'json'
url: @$btn_task_history_single.data 'endpoint'
data: send_data
......@@ -191,6 +196,7 @@ class @StudentAdmin
delete_module: false
$.ajax
type: 'POST'
dataType: 'json'
url: @$btn_reset_entrance_exam_attempts.data 'endpoint'
data: send_data
......@@ -212,6 +218,7 @@ class @StudentAdmin
unique_student_identifier: unique_student_identifier
$.ajax
type: 'POST'
dataType: 'json'
url: @$btn_rescore_entrance_exam.data 'endpoint'
data: send_data
......@@ -256,6 +263,7 @@ class @StudentAdmin
delete_module: true
$.ajax
type: 'POST'
dataType: 'json'
url: @$btn_delete_entrance_exam_state.data 'endpoint'
data: send_data
......@@ -277,6 +285,7 @@ class @StudentAdmin
unique_student_identifier: unique_student_identifier
$.ajax
type: 'POST'
dataType: 'json'
url: @$btn_entrance_exam_task_history.data 'endpoint'
data: send_data
......@@ -304,6 +313,7 @@ class @StudentAdmin
full_error_message = _.template(error_message)({problem_id: problem_to_reset})
$.ajax
type: 'POST'
dataType: 'json'
url: @$btn_reset_attempts_all.data 'endpoint'
data: send_data
......@@ -330,6 +340,7 @@ class @StudentAdmin
full_error_message = _.template(error_message)({problem_id: problem_to_reset})
$.ajax
type: 'POST'
dataType: 'json'
url: @$btn_rescore_problem_all.data 'endpoint'
data: send_data
......@@ -348,6 +359,7 @@ class @StudentAdmin
return @$request_response_error_all.text gettext("Please enter a problem location.")
$.ajax
type: 'POST'
dataType: 'json'
url: @$btn_task_history_all.data 'endpoint'
data: send_data
......
......@@ -336,6 +336,7 @@ class @PendingInstructorTasks
reload_running_tasks_list: =>
list_endpoint = @$table_running_tasks.data 'endpoint'
$.ajax
type: 'POST'
dataType: 'json'
url: list_endpoint
success: (data) =>
......@@ -392,6 +393,7 @@ class ReportDownloads
reload_report_downloads: ->
endpoint = @$report_downloads_table.data 'endpoint'
$.ajax
type: 'POST'
dataType: 'json'
url: endpoint
success: (data) =>
......
......@@ -45,6 +45,7 @@ var edx = edx || {};
$('input[name="user-enrollment-report"]').click(function(){
var url = $(this).data('endpoint');
$.ajax({
type: 'POST',
dataType: "json",
url: url,
success: function (data) {
......@@ -64,6 +65,7 @@ var edx = edx || {};
$('input[name="exec-summary-report"]').click(function(){
var url = $(this).data('endpoint');
$.ajax({
type: 'POST',
dataType: "json",
url: url,
success: function (data) {
......
......@@ -36,15 +36,15 @@ define(['jquery', 'coffee/src/instructor_dashboard/student_admin', 'common/js/sp
// Spy on AJAX requests
var requests = AjaxHelpers.requests(this);
studentadmin.$field_entrance_exam_student_select_grade.val(unique_student_identifier)
studentadmin.$field_entrance_exam_student_select_grade.val(unique_student_identifier);
studentadmin.$btn_reset_entrance_exam_attempts.click();
// Verify that the client contacts the server to start instructor task
var params = $.param({
unique_student_identifier: unique_student_identifier,
delete_module: false
});
var url = dashboard_api_url + '/reset_student_attempts_for_entrance_exam?' + params;
AjaxHelpers.expectJsonRequest(requests, 'GET', url);
var url = dashboard_api_url + '/reset_student_attempts_for_entrance_exam';
AjaxHelpers.expectPostRequest(requests, url, params);
// Simulate a success response from the server
AjaxHelpers.respondWithJson(requests, {
......@@ -63,8 +63,8 @@ define(['jquery', 'coffee/src/instructor_dashboard/student_admin', 'common/js/sp
unique_student_identifier: unique_student_identifier,
delete_module: false
});
var url = dashboard_api_url + '/reset_student_attempts_for_entrance_exam?' + params;
AjaxHelpers.expectJsonRequest(requests, 'GET', url);
var url = dashboard_api_url + '/reset_student_attempts_for_entrance_exam';
AjaxHelpers.expectPostRequest(requests, url, params);
// Simulate an error response from the server
AjaxHelpers.respondWithError(requests, 400,{});
......@@ -96,8 +96,8 @@ define(['jquery', 'coffee/src/instructor_dashboard/student_admin', 'common/js/sp
var params = $.param({
unique_student_identifier: unique_student_identifier
});
var url = dashboard_api_url + '/rescore_entrance_exam?' + params;
AjaxHelpers.expectJsonRequest(requests, 'GET', url);
var url = dashboard_api_url + '/rescore_entrance_exam';
AjaxHelpers.expectPostRequest(requests, url, params);
// Simulate a success response from the server
AjaxHelpers.respondWithJson(requests, {
......@@ -115,8 +115,8 @@ define(['jquery', 'coffee/src/instructor_dashboard/student_admin', 'common/js/sp
var params = $.param({
unique_student_identifier: unique_student_identifier
});
var url = dashboard_api_url + '/rescore_entrance_exam?' + params;
AjaxHelpers.expectJsonRequest(requests, 'GET', url);
var url = dashboard_api_url + '/rescore_entrance_exam';
AjaxHelpers.expectPostRequest(requests, url, params);
// Simulate an error response from the server
AjaxHelpers.respondWithError(requests, 400,{});
......@@ -195,8 +195,8 @@ define(['jquery', 'coffee/src/instructor_dashboard/student_admin', 'common/js/sp
unique_student_identifier: unique_student_identifier,
delete_module: true
});
var url = dashboard_api_url + '/reset_student_attempts_for_entrance_exam?' + params;
AjaxHelpers.expectJsonRequest(requests, 'GET', url);
var url = dashboard_api_url + '/reset_student_attempts_for_entrance_exam';
AjaxHelpers.expectPostRequest(requests, url, params);
// Simulate a success response from the server
AjaxHelpers.respondWithJson(requests, {
......@@ -215,8 +215,8 @@ define(['jquery', 'coffee/src/instructor_dashboard/student_admin', 'common/js/sp
unique_student_identifier: unique_student_identifier,
delete_module: true
});
var url = dashboard_api_url + '/reset_student_attempts_for_entrance_exam?' + params;
AjaxHelpers.expectJsonRequest(requests, 'GET', url);
var url = dashboard_api_url + '/reset_student_attempts_for_entrance_exam';
AjaxHelpers.expectPostRequest(requests, url, params);
// Simulate an error response from the server
AjaxHelpers.respondWithError(requests, 400,{});
......@@ -248,8 +248,8 @@ define(['jquery', 'coffee/src/instructor_dashboard/student_admin', 'common/js/sp
var params = $.param({
unique_student_identifier: unique_student_identifier
});
var url = dashboard_api_url + '/list_entrance_exam_instructor_tasks?' + params;
AjaxHelpers.expectJsonRequest(requests, 'GET', url);
var url = dashboard_api_url + '/list_entrance_exam_instructor_tasks';
AjaxHelpers.expectPostRequest(requests, url, params);
// Simulate a success response from the server
AjaxHelpers.respondWithJson(requests, {
......@@ -279,8 +279,8 @@ define(['jquery', 'coffee/src/instructor_dashboard/student_admin', 'common/js/sp
var params = $.param({
unique_student_identifier: unique_student_identifier
});
var url = dashboard_api_url + '/list_entrance_exam_instructor_tasks?' + params;
AjaxHelpers.expectJsonRequest(requests, 'GET', url);
var url = dashboard_api_url + '/list_entrance_exam_instructor_tasks';
AjaxHelpers.expectPostRequest(requests, url, params);
// Simulate an error response from the server
AjaxHelpers.respondWithError(requests, 400,{});
......
......@@ -91,7 +91,7 @@ define([
spyOn($, 'ajax');
StaffDebug.reset(locationName, location);
expect($.ajax.calls.mostRecent().args[0].type).toEqual('GET');
expect($.ajax.calls.mostRecent().args[0].type).toEqual('POST');
expect($.ajax.calls.mostRecent().args[0].data).toEqual({
'problem_to_reset': location,
'unique_student_identifier': 'userman',
......@@ -110,7 +110,7 @@ define([
spyOn($, 'ajax');
StaffDebug.sdelete(locationName, location);
expect($.ajax.calls.mostRecent().args[0].type).toEqual('GET');
expect($.ajax.calls.mostRecent().args[0].type).toEqual('POST');
expect($.ajax.calls.mostRecent().args[0].data).toEqual({
'problem_to_reset': location,
'unique_student_identifier': 'userman',
......@@ -130,7 +130,7 @@ define([
spyOn($, 'ajax');
StaffDebug.rescore(locationName, location);
expect($.ajax.calls.mostRecent().args[0].type).toEqual('GET');
expect($.ajax.calls.mostRecent().args[0].type).toEqual('POST');
expect($.ajax.calls.mostRecent().args[0].data).toEqual({
'problem_to_reset': location,
'unique_student_identifier': 'userman',
......
......@@ -31,7 +31,7 @@ var StaffDebug = (function (){
'delete_module': action.delete_module
};
$.ajax({
type: "GET",
type: "POST",
url: get_url(action.method),
data: pdata,
success: function(data){
......
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