diff --git a/.coveragerc b/.coveragerc index ed25b4ed5d8..d6074827637 100644 --- a/.coveragerc +++ b/.coveragerc @@ -4,4 +4,5 @@ parallel = True source = edx_proctoring omit = */migrations/* + edx_proctoring/settings/* edx_proctoring/admin.py diff --git a/Makefile b/Makefile index 8185b00c6cb..4afcb6b8569 100644 --- a/Makefile +++ b/Makefile @@ -22,12 +22,8 @@ help: ## display this help message @perl -nle'print $& if m{^[a-zA-Z_-]+:.*?## .*$$}' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m %-25s\033[0m %s\n", $$1, $$2}' clean: ## remove generated byte code, coverage reports, and build artifacts - find . -name '*.pyc' -exec rm -f {} + - find . -name '*.pyo' -exec rm -f {} + - find . -name '*~' -exec rm -f {} + - rm -fr build/ - rm -fr dist/ - rm -fr *.egg-info + find . -name '*.pyc' -o -name '*.pyo' -o -name '*~' -delete + rm -fr build/ dist/ *.egg-info upgrade: ## update the requirements/*.txt files with the latest packages satisfying requirements/*.in pip install -q pip-tools diff --git a/edx_proctoring/__init__.py b/edx_proctoring/__init__.py index da288ef47d1..45d1a5810be 100644 --- a/edx_proctoring/__init__.py +++ b/edx_proctoring/__init__.py @@ -5,6 +5,6 @@ from __future__ import absolute_import # Be sure to update the version number in edx_proctoring/package.json -__version__ = '1.5.0rc2' +__version__ = '1.5.0rc3' default_app_config = 'edx_proctoring.apps.EdxProctoringConfig' # pylint: disable=invalid-name diff --git a/edx_proctoring/api.py b/edx_proctoring/api.py index 80617d67899..ccaf2b50a30 100644 --- a/edx_proctoring/api.py +++ b/edx_proctoring/api.py @@ -1684,9 +1684,9 @@ def _get_timed_exam_view(exam, context, exam_id, user_id, course_id): 'progress_page_url': progress_page_url, 'does_time_remain': _does_time_remain(attempt), 'has_time_expired': has_time_expired, - 'enter_exam_endpoint': reverse('edx_proctoring.proctored_exam.attempt.collection'), + 'enter_exam_endpoint': reverse('edx_proctoring:proctored_exam.attempt.collection'), 'change_state_url': reverse( - 'edx_proctoring.proctored_exam.attempt', + 'edx_proctoring:proctored_exam.attempt', args=[attempt['id']] ) if attempt else '', }) @@ -1741,17 +1741,17 @@ def _get_proctored_exam_context(exam, attempt, user_id, course_id, is_practice_e 'has_due_date': has_due_date, 'has_due_date_passed': has_due_date_passed(exam['due_date']), 'does_time_remain': _does_time_remain(attempt), - 'enter_exam_endpoint': reverse('edx_proctoring.proctored_exam.attempt.collection'), + 'enter_exam_endpoint': reverse('edx_proctoring:proctored_exam.attempt.collection'), 'exam_started_poll_url': reverse( - 'edx_proctoring.proctored_exam.attempt', + 'edx_proctoring:proctored_exam.attempt', args=[attempt['id']] ) if attempt else '', 'change_state_url': reverse( - 'edx_proctoring.proctored_exam.attempt', + 'edx_proctoring:proctored_exam.attempt', args=[attempt['id']] ) if attempt else '', 'update_is_status_acknowledge_url': reverse( - 'edx_proctoring.proctored_exam.attempt.review_status', + 'edx_proctoring:proctored_exam.attempt.review_status', args=[attempt['id']] ) if attempt else '', 'link_urls': settings.PROCTORING_SETTINGS.get('LINK_URLS', {}), diff --git a/edx_proctoring/apps.py b/edx_proctoring/apps.py index 810087bd99d..b0cd2890499 100644 --- a/edx_proctoring/apps.py +++ b/edx_proctoring/apps.py @@ -18,7 +18,27 @@ class EdxProctoringConfig(AppConfig): Configuration for the edx_proctoring Django application. """ - name = 'edx_proctoring' + name = u'edx_proctoring' + plugin_app = { + u'url_config': { + u'lms.djangoapp': { + u'namespace': u'edx_proctoring', + u'regex': u'^api/', + u'relative_path': u'urls', + } + }, + u'settings_config': { + u'lms.djangoapp': { + u'common': {'relative_path': u'settings.common'}, + u'aws': {'relative_path': u'settings.aws'}, + }, + u'cms.djangoapp': { + u'common': {'relative_path': u'settings.common'}, + u'aws': {'relative_path': u'settings.aws'}, + } + + }, + } def get_backend_choices(self): """ diff --git a/edx_proctoring/backends/rest.py b/edx_proctoring/backends/rest.py index 1d4b6f44759..87087f36f64 100644 --- a/edx_proctoring/backends/rest.py +++ b/edx_proctoring/backends/rest.py @@ -11,6 +11,7 @@ from webpack_loader.exceptions import BaseWebpackLoaderException, WebpackBundleLookupError from edx_proctoring.backends.backend import ProctoringBackendProvider +from edx_proctoring.exceptions import BackendProviderCannotRegisterAttempt from edx_proctoring.statuses import ProctoredExamStudentAttemptStatus from edx_rest_api_client.client import OAuthAPIClient @@ -161,7 +162,10 @@ def register_exam_attempt(self, exam, context): payload.pop('attempt_code', False) log.debug('Creating exam attempt for %r at %r', exam['external_id'], url) response = self.session.post(url, json=payload) + if response.status_code != 200: + raise BackendProviderCannotRegisterAttempt(response.content) response = response.json() + log.debug(response) return response['id'] def start_exam_attempt(self, exam, attempt): diff --git a/edx_proctoring/backends/software_secure.py b/edx_proctoring/backends/software_secure.py index c92d289b20a..c06f05f1e32 100644 --- a/edx_proctoring/backends/software_secure.py +++ b/edx_proctoring/backends/software_secure.py @@ -24,7 +24,7 @@ from edx_proctoring.backends.backend import ProctoringBackendProvider from edx_proctoring import constants from edx_proctoring.exceptions import ( - BackendProvideCannotRegisterAttempt, + BackendProviderCannotRegisterAttempt, ProctoredExamSuspiciousLookup, ) from edx_proctoring.statuses import SoftwareSecureReviewStatus @@ -91,7 +91,7 @@ def register_exam_attempt(self, exam, context): ) ) log.error(err_msg) - raise BackendProvideCannotRegisterAttempt(err_msg) + raise BackendProviderCannotRegisterAttempt(err_msg) # get the external ID that Software Secure has defined # for this attempt @@ -223,7 +223,7 @@ def _get_payload(self, exam, context): scheme=scheme, hostname=settings.SITE_NAME, path=reverse( - 'edx_proctoring.anonymous.proctoring_launch_callback.start_exam', + 'edx_proctoring:anonymous.proctoring_launch_callback.start_exam', args=[attempt_code] ) ) diff --git a/edx_proctoring/backends/tests/test_rest.py b/edx_proctoring/backends/tests/test_rest.py index 617ad3ee825..f1f3eac843b 100644 --- a/edx_proctoring/backends/tests/test_rest.py +++ b/edx_proctoring/backends/tests/test_rest.py @@ -12,6 +12,7 @@ from django.utils import translation from edx_proctoring.backends.rest import BaseRestProctoringProvider +from edx_proctoring.exceptions import BackendProviderCannotRegisterAttempt class RESTBackendTests(TestCase): @@ -178,6 +179,23 @@ def test_register_exam_attempt(self): self.assertIn('lms_host', request) self.assertIn('full_name', request) + @responses.activate + def test_register_exam_attempt_failure(self): + context = { + 'attempt_code': '2', + 'obs_user_id': 'abcdefghij', + 'full_name': 'user name', + 'lms_host': 'http://lms.com' + } + responses.add( + responses.POST, + url=self.provider.create_exam_attempt_url.format(exam_id=self.backend_exam['external_id']), + json={'error': 'something'}, + status=400 + ) + with self.assertRaises(BackendProviderCannotRegisterAttempt): + self.provider.register_exam_attempt(self.backend_exam, context) + @responses.activate def test_start_exam_attempt(self): attempt_id = 2 diff --git a/edx_proctoring/backends/tests/test_software_secure.py b/edx_proctoring/backends/tests/test_software_secure.py index a23d48e516f..11df5832915 100644 --- a/edx_proctoring/backends/tests/test_software_secure.py +++ b/edx_proctoring/backends/tests/test_software_secure.py @@ -17,7 +17,7 @@ from edx_proctoring.runtime import set_runtime_service from edx_proctoring.backends import get_backend_provider -from edx_proctoring.exceptions import BackendProvideCannotRegisterAttempt +from edx_proctoring.exceptions import BackendProviderCannotRegisterAttempt from edx_proctoring import constants from edx_proctoring.api import ( @@ -183,7 +183,7 @@ def test_allow_simulated_callbacks(self): external_id='bogus' ) response = self.client.post( - reverse('edx_proctoring.anonymous.proctoring_review_callback'), + reverse('edx_proctoring:anonymous.proctoring_review_callback'), data=test_payload, content_type='application/json' ) @@ -212,7 +212,7 @@ def test_missing_attempt_code(self): external_id='bogus' ) response = self.client.post( - reverse('edx_proctoring.anonymous.proctoring_review_callback'), + reverse('edx_proctoring:anonymous.proctoring_review_callback'), data=test_payload, content_type='application/json' ) @@ -501,7 +501,7 @@ def test_failing_register_attempt(self): # now try a failing request with HTTMock(mock_response_error): - with self.assertRaises(BackendProvideCannotRegisterAttempt): + with self.assertRaises(BackendProviderCannotRegisterAttempt): create_exam_attempt(exam_id, self.user.id, taking_as_proctored=True) def test_payload_construction(self): diff --git a/edx_proctoring/exceptions.py b/edx_proctoring/exceptions.py index ca0c1007b61..85acb123164 100644 --- a/edx_proctoring/exceptions.py +++ b/edx_proctoring/exceptions.py @@ -71,7 +71,7 @@ class AllowanceValueNotAllowedException(ProctoredBaseException): """ -class BackendProvideCannotRegisterAttempt(ProctoredBaseException): +class BackendProviderCannotRegisterAttempt(ProctoredBaseException): """ Raised when a back-end provider cannot register an attempt """ diff --git a/edx_proctoring/settings/__init__.py b/edx_proctoring/settings/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/edx_proctoring/settings/aws.py b/edx_proctoring/settings/aws.py new file mode 100644 index 00000000000..87d69da8880 --- /dev/null +++ b/edx_proctoring/settings/aws.py @@ -0,0 +1,13 @@ +"Pluggable Django App settings for production" + + +def plugin_settings(settings): + "Injects local settings into django settings" + auth_tokens = getattr(settings, 'AUTH_TOKENS', {}) + env_tokens = getattr(settings, 'ENV_TOKENS', {}) + if env_tokens.get('PROCTORING_SETTINGS'): + settings.PROCTORING_SETTINGS = env_tokens['PROCTORING_SETTINGS'] + if auth_tokens.get('PROCTORING_BACKENDS'): + settings.PROCTORING_BACKENDS = auth_tokens['PROCTORING_BACKENDS'] + elif env_tokens.get('PROCTORING_BACKENDS'): + settings.PROCTORING_BACKENDS = env_tokens['PROCTORING_BACKENDS'] diff --git a/edx_proctoring/settings/common.py b/edx_proctoring/settings/common.py new file mode 100644 index 00000000000..b8f3140908c --- /dev/null +++ b/edx_proctoring/settings/common.py @@ -0,0 +1,32 @@ +"Common Pluggable Django App settings" + + +def plugin_settings(settings): + "Injects local settings into django settings" + settings.PROCTORING_SETTINGS = {} + settings.PROCTORING_BACKENDS = { + 'DEFAULT': 'null', + 'null': {}, + } + proctoring_js = ( + [ + 'proctoring/js/models/proctored_exam_allowance_model.js', + 'proctoring/js/models/proctored_exam_attempt_model.js', + 'proctoring/js/models/proctored_exam_model.js', + 'proctoring/js/collections/proctored_exam_allowance_collection.js', + 'proctoring/js/collections/proctored_exam_attempt_collection.js', + 'proctoring/js/collections/proctored_exam_collection.js', + 'proctoring/js/views/Backbone.ModalDialog.js', + 'proctoring/js/views/proctored_exam_add_allowance_view.js', + 'proctoring/js/views/proctored_exam_allowance_view.js', + 'proctoring/js/views/proctored_exam_attempt_view.js', + 'proctoring/js/views/proctored_exam_view.js', + 'proctoring/js/views/proctored_exam_instructor_launch.js', + 'proctoring/js/proctored_app.js', + 'proctoring/js/exam_action_handler.js' + ] + ) + settings.PIPELINE_JS['proctoring'] = { + 'source_filenames': proctoring_js, + 'output_filename': 'js/lms-proctoring.js', + } diff --git a/edx_proctoring/settings/test.py b/edx_proctoring/settings/test.py new file mode 100644 index 00000000000..e1a22361e87 --- /dev/null +++ b/edx_proctoring/settings/test.py @@ -0,0 +1,11 @@ +"Pluggable Django App settings for test" + + +def plugin_settings(settings): + "Injects local settings into django settings" + settings.PROCTORING_SETTINGS = {} + settings.PROCTORING_BACKENDS = { + 'DEFAULT': 'mock', + 'mock': {}, + 'mock_proctoring_without_rules': {} + } diff --git a/edx_proctoring/tests/test_reviews.py b/edx_proctoring/tests/test_reviews.py index 6e52f28dc19..21093f29e18 100644 --- a/edx_proctoring/tests/test_reviews.py +++ b/edx_proctoring/tests/test_reviews.py @@ -177,7 +177,7 @@ def test_post_review(self, external_id, status): if not external_id: external_id = self.attempt['external_id'] response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.callback', + reverse('edx_proctoring:proctored_exam.attempt.callback', kwargs={'external_id': external_id}), json.dumps(review), content_type='application/json' diff --git a/edx_proctoring/tests/test_views.py b/edx_proctoring/tests/test_views.py index 36c7ed6f319..f47c160cfff 100644 --- a/edx_proctoring/tests/test_views.py +++ b/edx_proctoring/tests/test_views.py @@ -62,19 +62,20 @@ def test_no_anonymous_access(self): """ self.client = Client() # use AnonymousUser on the API calls for urlpattern in urlpatterns: - if hasattr(urlpattern, 'name') and '.anonymous.' not in urlpattern.name: + if hasattr(urlpattern, 'name') and 'anonymous.' not in urlpattern.name: + name = 'edx_proctoring:%s' % urlpattern.name try: - response = self.client.get(reverse(urlpattern.name)) + response = self.client.get(reverse(name)) except NoReverseMatch: # some of our URL mappings may require a argument substitution try: - response = self.client.get(reverse(urlpattern.name, args=[0])) + response = self.client.get(reverse(name, args=[0])) except NoReverseMatch: try: - response = self.client.get(reverse(urlpattern.name, args=["0/0/0"])) + response = self.client.get(reverse(name, args=["0/0/0"])) except NoReverseMatch: # some require 2 args. - response = self.client.get(reverse(urlpattern.name, args=["0/0/0", 0])) + response = self.client.get(reverse(name, args=["0/0/0", 0])) self.assertEqual(response.status_code, 403) @@ -106,7 +107,7 @@ def test_create_exam(self): 'hide_after_due': False, } response = self.client.post( - reverse('edx_proctoring.proctored_exam.exam'), + reverse('edx_proctoring:proctored_exam.exam'), exam_data ) @@ -116,7 +117,7 @@ def test_create_exam(self): # Now lookup the exam by giving the exam_id returned and match the data. response = self.client.get( - reverse('edx_proctoring.proctored_exam.exam_by_id', kwargs={'exam_id': response_data['exam_id']}) + reverse('edx_proctoring:proctored_exam.exam_by_id', kwargs={'exam_id': response_data['exam_id']}) ) self.assertEqual(response.status_code, 200) response_data = json.loads(response.content) @@ -142,7 +143,7 @@ def test_create_duplicate_exam(self): 'hide_after_due': False, } response = self.client.post( - reverse('edx_proctoring.proctored_exam.exam'), + reverse('edx_proctoring:proctored_exam.exam'), exam_data ) @@ -151,7 +152,7 @@ def test_create_duplicate_exam(self): self.assertGreater(response_data['exam_id'], 0) response = self.client.post( - reverse('edx_proctoring.proctored_exam.exam'), + reverse('edx_proctoring:proctored_exam.exam'), exam_data ) @@ -180,7 +181,7 @@ def test_update_existing_exam(self): 'is_active': True } response = self.client.put( - reverse('edx_proctoring.proctored_exam.exam'), + reverse('edx_proctoring:proctored_exam.exam'), json.dumps(updated_exam_data), content_type='application/json' ) @@ -191,7 +192,7 @@ def test_update_existing_exam(self): # Now lookup the exam by giving the exam_id returned and match the data. response = self.client.get( - reverse('edx_proctoring.proctored_exam.exam_by_id', kwargs={'exam_id': response_data['exam_id']}) + reverse('edx_proctoring:proctored_exam.exam_by_id', kwargs={'exam_id': response_data['exam_id']}) ) self.assertEqual(response.status_code, 200) response_data = json.loads(response.content) @@ -250,7 +251,7 @@ def test_update_non_existing_exam(self): 'is_active': True } response = self.client.put( - reverse('edx_proctoring.proctored_exam.exam'), + reverse('edx_proctoring:proctored_exam.exam'), json.dumps(updated_exam_data), content_type='application/json' ) @@ -273,7 +274,7 @@ def test_get_exam_by_id(self): ) response = self.client.get( - reverse('edx_proctoring.proctored_exam.exam_by_id', kwargs={'exam_id': proctored_exam.id}) + reverse('edx_proctoring:proctored_exam.exam_by_id', kwargs={'exam_id': proctored_exam.id}) ) self.assertEqual(response.status_code, 200) response_data = json.loads(response.content) @@ -289,7 +290,7 @@ def test_get_exam_by_bad_id(self): """ # Create an exam. response = self.client.get( - reverse('edx_proctoring.proctored_exam.exam_by_id', kwargs={'exam_id': 99999}) + reverse('edx_proctoring:proctored_exam.exam_by_id', kwargs={'exam_id': 99999}) ) self.assertEqual(response.status_code, 400) response_data = json.loads(response.content) @@ -309,7 +310,7 @@ def test_get_exam_by_content_id(self): ) response = self.client.get( - reverse('edx_proctoring.proctored_exam.exam_by_content_id', kwargs={ + reverse('edx_proctoring:proctored_exam.exam_by_content_id', kwargs={ 'course_id': proctored_exam.course_id, 'content_id': proctored_exam.content_id }) @@ -337,7 +338,7 @@ def test_get_exam_by_course_id(self): ) response = self.client.get( - reverse('edx_proctoring.proctored_exam.exams_by_course_id', kwargs={ + reverse('edx_proctoring:proctored_exam.exams_by_course_id', kwargs={ 'course_id': proctored_exam.course_id }) ) @@ -363,7 +364,7 @@ def test_get_exam_by_bad_content_id(self): ) response = self.client.get( - reverse('edx_proctoring.proctored_exam.exam_by_content_id', kwargs={ + reverse('edx_proctoring:proctored_exam.exam_by_content_id', kwargs={ 'course_id': 'c/d/e', 'content_id': proctored_exam.content_id }) @@ -387,7 +388,7 @@ def test_get_exam_insufficient_args(self): ) response = self.client.get( - reverse('edx_proctoring.proctored_exam.exam_by_content_id', kwargs={ + reverse('edx_proctoring:proctored_exam.exam_by_content_id', kwargs={ 'course_id': proctored_exam.course_id, 'content_id': proctored_exam.content_id }) @@ -440,7 +441,7 @@ def _create_exam_attempt(self): # Starting exam attempt response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) self.assertEqual(response.status_code, 200) @@ -478,7 +479,7 @@ def _test_repeated_start_exam_callbacks(self, attempt): attempt_id = attempt['id'] code = attempt['attempt_code'] self.client.get( - reverse('edx_proctoring.anonymous.proctoring_launch_callback.start_exam', kwargs={'attempt_code': code}) + reverse('edx_proctoring:anonymous.proctoring_launch_callback.start_exam', kwargs={'attempt_code': code}) ) attempt = get_exam_attempt_by_id(attempt_id) self.assertEqual(attempt['status'], "ready_to_start") @@ -492,7 +493,7 @@ def _test_repeated_start_exam_callbacks(self, attempt): # hit callback again and verify that status is still 'started' and not 'ready to start' self.client.get( - reverse('edx_proctoring.anonymous.proctoring_launch_callback.start_exam', kwargs={'attempt_code': code}) + reverse('edx_proctoring:anonymous.proctoring_launch_callback.start_exam', kwargs={'attempt_code': code}) ) attempt = get_exam_attempt_by_id(attempt_id) self.assertEqual(attempt['status'], "started") @@ -508,7 +509,7 @@ def test_authenticated_start_callback(self, ext_id, http_status, status): attempt_id = attempt['id'] response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.ready_callback', kwargs={'external_id': ext_id}) + reverse('edx_proctoring:proctored_exam.attempt.ready_callback', kwargs={'external_id': ext_id}) ) self.assertEqual(response.status_code, http_status) attempt = get_exam_attempt_by_id(attempt_id) @@ -532,7 +533,7 @@ def test_start_exam_create(self): 'start_clock': True, } response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) @@ -558,7 +559,7 @@ def test_start_exam(self): 'start_clock': False, } response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) @@ -573,7 +574,7 @@ def test_start_exam(self): self.assertIsNone(attempt['started_at']) response = self.client.put( - reverse('edx_proctoring.proctored_exam.attempt', args=[old_attempt_id]), + reverse('edx_proctoring:proctored_exam.attempt', args=[old_attempt_id]), json.dumps({ 'action': 'start', }), @@ -629,7 +630,7 @@ def test_attempt_readback(self): 'start_clock': True, } response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) @@ -639,7 +640,7 @@ def test_attempt_readback(self): self.assertGreater(attempt_id, 0) response = self.client.get( - reverse('edx_proctoring.proctored_exam.attempt', args=[attempt_id]) + reverse('edx_proctoring:proctored_exam.attempt', args=[attempt_id]) ) self.assertEqual(response.status_code, 200) response_data = json.loads(response.content) @@ -654,7 +655,7 @@ def test_attempt_readback(self): reset_time = datetime.now(pytz.UTC) + timedelta(minutes=90, seconds=30) with freeze_time(reset_time): response = self.client.get( - reverse('edx_proctoring.proctored_exam.attempt', args=[attempt_id]) + reverse('edx_proctoring:proctored_exam.attempt', args=[attempt_id]) ) self.assertEqual(response.status_code, 200) response_data = json.loads(response.content) @@ -679,7 +680,7 @@ def test_timer_remaining_time(self): 'start_clock': True, } response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) @@ -689,7 +690,7 @@ def test_timer_remaining_time(self): self.assertGreater(attempt_id, 0) response = self.client.get( - reverse('edx_proctoring.proctored_exam.attempt', args=[attempt_id]) + reverse('edx_proctoring:proctored_exam.attempt', args=[attempt_id]) ) self.assertEqual(response.status_code, 200) response_data = json.loads(response.content) @@ -751,7 +752,7 @@ def test_attempt_ready_to_start(self): attempt.save() response = self.client.get( - reverse('edx_proctoring.proctored_exam.attempt', args=[attempt.id]) + reverse('edx_proctoring:proctored_exam.attempt', args=[attempt.id]) ) self.assertEqual(response.status_code, 200) response_data = json.loads(response.content) @@ -774,7 +775,7 @@ def test_attempt_status_for_exception(self): with patch('edx_proctoring.api.update_attempt_status', Mock(side_effect=ProctoredExamIllegalStatusTransition)): with freeze_time(reset_time): response = self.client.get( - reverse('edx_proctoring.proctored_exam.attempt', args=[exam_attempt.id]) + reverse('edx_proctoring:proctored_exam.attempt', args=[exam_attempt.id]) ) self.assertEqual(response.status_code, 200) @@ -796,7 +797,7 @@ def test_attempt_status_stickiness(self): 'start_clock': True, } response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) @@ -806,7 +807,7 @@ def test_attempt_status_stickiness(self): self.assertEqual(attempt_id, 1) response = self.client.get( - reverse('edx_proctoring.proctored_exam.attempt', args=[attempt_id]) + reverse('edx_proctoring:proctored_exam.attempt', args=[attempt_id]) ) self.assertEqual(response.status_code, 200) response_data = json.loads(response.content) @@ -823,7 +824,7 @@ def test_attempt_status_stickiness(self): reset_time = datetime.now(pytz.UTC) + timedelta(minutes=2) with freeze_time(reset_time): response = self.client.get( - reverse('edx_proctoring.proctored_exam.attempt', args=[attempt_id]) + reverse('edx_proctoring:proctored_exam.attempt', args=[attempt_id]) ) self.assertEqual(response.status_code, 200) response_data = json.loads(response.content) @@ -854,7 +855,7 @@ def test_attempt_with_duedate_expired(self): # Starting exam attempt response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) self.assertEqual(response.status_code, 400) @@ -874,7 +875,7 @@ def test_remove_attempt(self): ) response = self.client.delete( - reverse('edx_proctoring.proctored_exam.attempt', args=[1]) + reverse('edx_proctoring:proctored_exam.attempt', args=[1]) ) self.assertEqual(response.status_code, 400) @@ -885,7 +886,7 @@ def test_remove_attempt(self): 'start_clock': True, } response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) @@ -897,7 +898,7 @@ def test_remove_attempt(self): self.user.is_staff = False self.user.save() response = self.client.delete( - reverse('edx_proctoring.proctored_exam.attempt', args=[attempt_id]) + reverse('edx_proctoring:proctored_exam.attempt', args=[attempt_id]) ) self.assertEqual(response.status_code, 200) @@ -922,7 +923,7 @@ def test_remove_attempt_non_staff(self): 'start_clock': True, } response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) @@ -938,7 +939,7 @@ def test_remove_attempt_non_staff(self): set_runtime_service('instructor', MockInstructorService(is_user_course_staff=False)) response = self.client.delete( - reverse('edx_proctoring.proctored_exam.attempt', args=[attempt_id]) + reverse('edx_proctoring:proctored_exam.attempt', args=[attempt_id]) ) self.assertEqual(response.status_code, 403) @@ -963,7 +964,7 @@ def test_read_others_attempt(self): 'start_clock': True, } response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) @@ -974,7 +975,7 @@ def test_read_others_attempt(self): self.client.login_user(self.second_user) response = self.client.get( - reverse('edx_proctoring.proctored_exam.attempt', args=[attempt_id]) + reverse('edx_proctoring:proctored_exam.attempt', args=[attempt_id]) ) self.assertEqual(response.status_code, 400) @@ -983,7 +984,7 @@ def test_read_non_existing_attempt(self): Confirms that we cannot read a non-existing attempt """ response = self.client.get( - reverse('edx_proctoring.proctored_exam.attempt', args=[0]) + reverse('edx_proctoring:proctored_exam.attempt', args=[0]) ) self.assertEqual(response.status_code, 400) @@ -1004,7 +1005,7 @@ def test_restart_exam_attempt(self): 'external_id': proctored_exam.external_id } response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) @@ -1013,7 +1014,7 @@ def test_restart_exam_attempt(self): self.assertGreater(response_data['exam_attempt_id'], 0) response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) self.assertEqual(response.status_code, 400) @@ -1041,7 +1042,7 @@ def test_stop_exam_attempt(self): 'external_id': proctored_exam.external_id } response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) @@ -1051,7 +1052,7 @@ def test_stop_exam_attempt(self): old_attempt_id = response_data['exam_attempt_id'] response = self.client.put( - reverse('edx_proctoring.proctored_exam.attempt', args=[old_attempt_id]), + reverse('edx_proctoring:proctored_exam.attempt', args=[old_attempt_id]), json.dumps({ 'action': 'stop', }), @@ -1080,7 +1081,7 @@ def test_download_software_clicked_action(self): 'external_id': proctored_exam.external_id } response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), json.dumps(attempt_data), content_type='application/json' ) @@ -1091,7 +1092,7 @@ def test_download_software_clicked_action(self): old_attempt_id = response_data['exam_attempt_id'] response = self.client.put( - reverse('edx_proctoring.proctored_exam.attempt', args=[old_attempt_id]), + reverse('edx_proctoring:proctored_exam.attempt', args=[old_attempt_id]), json.dumps({ 'action': 'click_download_software', }), @@ -1129,7 +1130,7 @@ def test_submit_exam_attempt(self, action, expected_status): 'external_id': proctored_exam.external_id } response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) @@ -1139,7 +1140,7 @@ def test_submit_exam_attempt(self, action, expected_status): old_attempt_id = response_data['exam_attempt_id'] response = self.client.put( - reverse('edx_proctoring.proctored_exam.attempt', args=[old_attempt_id]), + reverse('edx_proctoring:proctored_exam.attempt', args=[old_attempt_id]), json.dumps({ 'action': action, }), @@ -1156,7 +1157,7 @@ def test_submit_exam_attempt(self, action, expected_status): # we should not be able to restart it response = self.client.put( - reverse('edx_proctoring.proctored_exam.attempt', args=[old_attempt_id]), + reverse('edx_proctoring:proctored_exam.attempt', args=[old_attempt_id]), json.dumps({ 'action': 'start', }), @@ -1166,7 +1167,7 @@ def test_submit_exam_attempt(self, action, expected_status): self.assertEqual(response.status_code, 400) response = self.client.put( - reverse('edx_proctoring.proctored_exam.attempt', args=[old_attempt_id]), + reverse('edx_proctoring:proctored_exam.attempt', args=[old_attempt_id]), json.dumps({ 'action': 'stop', }), @@ -1192,10 +1193,10 @@ def test_get_exam_attempts(self): 'external_id': proctored_exam.external_id } response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) - url = reverse('edx_proctoring.proctored_exam.attempts.course', kwargs={'course_id': proctored_exam.course_id}) + url = reverse('edx_proctoring:proctored_exam.attempts.course', kwargs={'course_id': proctored_exam.course_id}) self.assertEqual(response.status_code, 200) response = self.client.get(url) self.assertEqual(response.status_code, 200) @@ -1233,7 +1234,7 @@ def test_exam_attempts_not_global_staff(self): 'external_id': timed_exam.external_id } self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) @@ -1252,10 +1253,10 @@ def test_exam_attempts_not_global_staff(self): 'external_id': proctored_exam.external_id } self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) - url = reverse('edx_proctoring.proctored_exam.attempts.course', kwargs={'course_id': proctored_exam.course_id}) + url = reverse('edx_proctoring:proctored_exam.attempts.course', kwargs={'course_id': proctored_exam.course_id}) self.user.is_staff = False self.user.save() @@ -1294,7 +1295,7 @@ def test_get_filtered_exam_attempts(self): } # create a exam attempt response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) self.assertEqual(response.status_code, 200) @@ -1302,7 +1303,7 @@ def test_get_filtered_exam_attempts(self): self.client.login_user(self.second_user) # create a new exam attempt for second student response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) self.assertEqual(response.status_code, 200) @@ -1310,7 +1311,7 @@ def test_get_filtered_exam_attempts(self): self.client.login_user(self.user) response = self.client.get( reverse( - 'edx_proctoring.proctored_exam.attempts.search', + 'edx_proctoring:proctored_exam.attempts.search', kwargs={ 'course_id': proctored_exam.course_id, 'search_by': 'tester' @@ -1349,7 +1350,7 @@ def test_get_filtered_timed_exam_attempts(self): # pylint: disable=invalid-name } # create a exam attempt response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) self.assertEqual(response.status_code, 200) @@ -1357,7 +1358,7 @@ def test_get_filtered_timed_exam_attempts(self): # pylint: disable=invalid-name self.client.login_user(self.second_user) # create a new exam attempt for second student response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) self.assertEqual(response.status_code, 200) @@ -1367,7 +1368,7 @@ def test_get_filtered_timed_exam_attempts(self): # pylint: disable=invalid-name self.client.login_user(self.user) response = self.client.get( reverse( - 'edx_proctoring.proctored_exam.attempts.search', + 'edx_proctoring:proctored_exam.attempts.search', kwargs={ 'course_id': timed_exm.course_id, 'search_by': 'tester' @@ -1409,7 +1410,7 @@ def test_paginated_exam_attempts(self): self.client.login_user(self.user) response = self.client.get( reverse( - 'edx_proctoring.proctored_exam.attempts.course', + 'edx_proctoring:proctored_exam.attempts.course', kwargs={ 'course_id': proctored_exam.course_id } @@ -1441,7 +1442,7 @@ def test_stop_others_attempt(self): 'external_id': proctored_exam.external_id } response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) @@ -1452,7 +1453,7 @@ def test_stop_others_attempt(self): self.client.login_user(self.second_user) response = self.client.put( - reverse('edx_proctoring.proctored_exam.attempt', args=[old_attempt_id]), + reverse('edx_proctoring:proctored_exam.attempt', args=[old_attempt_id]), {} ) @@ -1464,7 +1465,7 @@ def test_stop_unstarted_attempt(self): """ response = self.client.put( - reverse('edx_proctoring.proctored_exam.attempt', args=[0]), + reverse('edx_proctoring:proctored_exam.attempt', args=[0]), {} ) @@ -1485,7 +1486,7 @@ def test_get_exam_attempt(self): time_limit_mins=90 ) response = self.client.get( - reverse('edx_proctoring.proctored_exam.attempt.collection') + reverse('edx_proctoring:proctored_exam.attempt.collection') ) self.assertEqual(response.status_code, 200) data = json.loads(response.content) @@ -1498,13 +1499,13 @@ def test_get_exam_attempt(self): 'start_clock': True } response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) self.assertEqual(response.status_code, 200) response = self.client.get( - reverse('edx_proctoring.proctored_exam.attempt.collection') + reverse('edx_proctoring:proctored_exam.attempt.collection') ) self.assertEqual(response.status_code, 200) data = json.loads(response.content) @@ -1527,7 +1528,7 @@ def test_get_exam_attempt_with_non_staff_user(self): # pylint: disable=invalid- time_limit_mins=90 ) response = self.client.get( - reverse('edx_proctoring.proctored_exam.attempt.collection') + reverse('edx_proctoring:proctored_exam.attempt.collection') ) self.assertEqual(response.status_code, 200) data = json.loads(response.content) @@ -1540,13 +1541,13 @@ def test_get_exam_attempt_with_non_staff_user(self): # pylint: disable=invalid- 'start_clock': True } response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) self.assertEqual(response.status_code, 200) response = self.client.get( - reverse('edx_proctoring.proctored_exam.attempt.collection') + reverse('edx_proctoring:proctored_exam.attempt.collection') ) self.assertEqual(response.status_code, 200) data = json.loads(response.content) @@ -1576,7 +1577,7 @@ def test_get_expired_attempt(self): 'start_clock': True } response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) self.assertEqual(response.status_code, 200) @@ -1586,7 +1587,7 @@ def test_get_expired_attempt(self): self.assertEqual(attempt['status'], ProctoredExamStudentAttemptStatus.submitted) response = self.client.get( - reverse('edx_proctoring.proctored_exam.attempt.collection') + reverse('edx_proctoring:proctored_exam.attempt.collection') ) self.assertEqual(response.status_code, 200) data = json.loads(response.content) @@ -1606,7 +1607,7 @@ def test_get_expired_exam_attempt(self): time_limit_mins=90 ) response = self.client.get( - reverse('edx_proctoring.proctored_exam.attempt.collection') + reverse('edx_proctoring:proctored_exam.attempt.collection') ) self.assertEqual(response.status_code, 200) @@ -1616,7 +1617,7 @@ def test_get_expired_exam_attempt(self): 'external_id': proctored_exam.external_id } response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) self.assertEqual(response.status_code, 200) @@ -1630,7 +1631,7 @@ def test_get_expired_exam_attempt(self): ) response = self.client.get( - reverse('edx_proctoring.proctored_exam.attempt.collection') + reverse('edx_proctoring:proctored_exam.attempt.collection') ) self.assertEqual(response.status_code, 200) @@ -1654,7 +1655,7 @@ def test_declined_attempt(self): } # create a exam attempt response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) self.assertEqual(response.status_code, 200) @@ -1690,7 +1691,7 @@ def test_exam_callback(self): 'start_clock': False, } response = self.client.post( - reverse('edx_proctoring.proctored_exam.attempt.collection'), + reverse('edx_proctoring:proctored_exam.attempt.collection'), attempt_data ) @@ -1708,7 +1709,7 @@ def test_exam_callback(self): response = self.client.get( reverse( - 'edx_proctoring.anonymous.proctoring_launch_callback.start_exam', + 'edx_proctoring:anonymous.proctoring_launch_callback.start_exam', args=[attempt_code] ) ) @@ -1723,7 +1724,7 @@ def test_bad_exam_code_callback(self): """ response = self.client.get( reverse( - 'edx_proctoring.anonymous.proctoring_launch_callback.start_exam', + 'edx_proctoring:anonymous.proctoring_launch_callback.start_exam', args=['foo'] ) ) @@ -1759,7 +1760,7 @@ def test_review_callback(self): external_id=attempt['external_id'] ) response = self.client.post( - reverse('edx_proctoring.anonymous.proctoring_review_callback'), + reverse('edx_proctoring:anonymous.proctoring_review_callback'), data=test_payload, content_type='application/json' ) @@ -1797,7 +1798,7 @@ def test_review_caseinsensitive(self): ) response = self.client.post( - reverse('edx_proctoring.anonymous.proctoring_review_callback'), + reverse('edx_proctoring:anonymous.proctoring_review_callback'), data=test_payload, content_type='application/json' ) @@ -1834,7 +1835,7 @@ def test_review_bad_contenttype(self): ) response = self.client.post( - reverse('edx_proctoring.anonymous.proctoring_review_callback'), + reverse('edx_proctoring:anonymous.proctoring_review_callback'), data=test_payload, content_type='foo' ) @@ -1870,7 +1871,7 @@ def test_review_mismatch(self): external_id='mismatch' ) response = self.client.post( - reverse('edx_proctoring.anonymous.proctoring_review_callback'), + reverse('edx_proctoring:anonymous.proctoring_review_callback'), data=test_payload, content_type='application/json' ) @@ -1882,7 +1883,7 @@ def test_review_callback_get(self): """ response = self.client.get( - reverse('edx_proctoring.anonymous.proctoring_review_callback'), + reverse('edx_proctoring:anonymous.proctoring_review_callback'), ) self.assertEqual(response.status_code, 405) @@ -1918,7 +1919,7 @@ def test_exam_type(self, is_proctored, is_practice, expected_exam_type): ) response = self.client.get( - reverse('edx_proctoring.proctored_exam.attempt.collection') + reverse('edx_proctoring:proctored_exam.attempt.collection') ) self.assertEqual(response.status_code, 200) data = json.loads(response.content) @@ -1956,7 +1957,7 @@ def test_attempt_review_status_callback(self): response = self.client.put( reverse( - 'edx_proctoring.proctored_exam.attempt.review_status', + 'edx_proctoring:proctored_exam.attempt.review_status', args=[attempt.id] ), {}, @@ -1974,7 +1975,7 @@ def test_attempt_review_status_callback_with_doesnotexit_exception(self): response = self.client.put( reverse( - 'edx_proctoring.proctored_exam.attempt.review_status', + 'edx_proctoring:proctored_exam.attempt.review_status', args=['5'] ), {}, @@ -1999,7 +2000,7 @@ def test_attempt_review_status_callback_with_permission_exception(self): response = self.client.put( reverse( - 'edx_proctoring.proctored_exam.attempt.review_status', + 'edx_proctoring:proctored_exam.attempt.review_status', args=[attempt.id] ), {}, @@ -2043,7 +2044,7 @@ def test_add_allowance_for_user(self): 'value': '30' } response = self.client.put( - reverse('edx_proctoring.proctored_exam.allowance'), + reverse('edx_proctoring:proctored_exam.allowance'), json.dumps(allowance_data), content_type='application/json' ) @@ -2069,7 +2070,7 @@ def test_add_invalid_allowance(self): 'value': '30' } response = self.client.put( - reverse('edx_proctoring.proctored_exam.allowance'), + reverse('edx_proctoring:proctored_exam.allowance'), json.dumps(allowance_data), content_type='application/json' ) @@ -2098,7 +2099,7 @@ def test_add_invalid_allowance_value(self): 'value': 'invalid_value' } response = self.client.put( - reverse('edx_proctoring.proctored_exam.allowance'), + reverse('edx_proctoring:proctored_exam.allowance'), json.dumps(allowance_data), content_type='application/json' ) @@ -2131,7 +2132,7 @@ def test_add_allowance_for_inactive_exam(self): } # Try to add an allowance response = self.client.put( - reverse('edx_proctoring.proctored_exam.allowance'), + reverse('edx_proctoring:proctored_exam.allowance'), json.dumps(allowance_data), content_type='application/json' ) @@ -2158,7 +2159,7 @@ def test_remove_allowance_for_user(self): 'value': '30' } response = self.client.put( - reverse('edx_proctoring.proctored_exam.allowance'), + reverse('edx_proctoring:proctored_exam.allowance'), json.dumps(allowance_data), content_type='application/json' ) @@ -2167,7 +2168,7 @@ def test_remove_allowance_for_user(self): allowance_data.pop('value') response = self.client.delete( - reverse('edx_proctoring.proctored_exam.allowance'), + reverse('edx_proctoring:proctored_exam.allowance'), json.dumps(allowance_data), content_type='application/json' ) @@ -2197,7 +2198,7 @@ def test_add_allowance_non_staff_user(self): # pylint: disable=invalid-name 'value': '30' } response = self.client.put( - reverse('edx_proctoring.proctored_exam.allowance'), + reverse('edx_proctoring:proctored_exam.allowance'), json.dumps(allowance_data), content_type='application/json' ) @@ -2225,14 +2226,14 @@ def test_get_allowances_for_course(self): 'value': '30' } response = self.client.put( - reverse('edx_proctoring.proctored_exam.allowance'), + reverse('edx_proctoring:proctored_exam.allowance'), json.dumps(allowance_data), content_type='application/json' ) self.assertEqual(response.status_code, 200) response = self.client.get( - reverse('edx_proctoring.proctored_exam.allowance', kwargs={'course_id': proctored_exam.course_id}) + reverse('edx_proctoring:proctored_exam.allowance', kwargs={'course_id': proctored_exam.course_id}) ) self.assertEqual(response.status_code, 200) @@ -2265,7 +2266,7 @@ def test_get_allowance_non_staff_user(self): # pylint: disable=invalid-name 'value': '30' } response = self.client.put( - reverse('edx_proctoring.proctored_exam.allowance'), + reverse('edx_proctoring:proctored_exam.allowance'), json.dumps(allowance_data), content_type='application/json' ) @@ -2275,7 +2276,7 @@ def test_get_allowance_non_staff_user(self): # pylint: disable=invalid-name # which will return in the Forbidden request. set_runtime_service('instructor', MockInstructorService(is_user_course_staff=False)) response = self.client.get( - reverse('edx_proctoring.proctored_exam.allowance', kwargs={'course_id': proctored_exam.course_id}) + reverse('edx_proctoring:proctored_exam.allowance', kwargs={'course_id': proctored_exam.course_id}) ) self.assertEqual(response.status_code, 403) @@ -2306,7 +2307,7 @@ def test_get_timed_exam_allowances_for_course(self): # pylint: disable=invalid- 'value': '30' } response = self.client.put( - reverse('edx_proctoring.proctored_exam.allowance'), + reverse('edx_proctoring:proctored_exam.allowance'), json.dumps(allowance_data), content_type='application/json' ) @@ -2329,14 +2330,14 @@ def test_get_timed_exam_allowances_for_course(self): # pylint: disable=invalid- 'value': '30' } response = self.client.put( - reverse('edx_proctoring.proctored_exam.allowance'), + reverse('edx_proctoring:proctored_exam.allowance'), json.dumps(allowance_data), content_type='application/json' ) self.assertEqual(response.status_code, 200) response = self.client.get( - reverse('edx_proctoring.proctored_exam.allowance', kwargs={'course_id': proctored_exam.course_id}) + reverse('edx_proctoring:proctored_exam.allowance', kwargs={'course_id': proctored_exam.course_id}) ) self.assertEqual(response.status_code, 200) @@ -2368,7 +2369,7 @@ def test_get_allowances_for_inactive_exam(self): 'value': '30' } response = self.client.put( - reverse('edx_proctoring.proctored_exam.allowance'), + reverse('edx_proctoring:proctored_exam.allowance'), json.dumps(allowance_data), content_type='application/json' ) @@ -2379,7 +2380,7 @@ def test_get_allowances_for_inactive_exam(self): proctored_exam.save() response = self.client.get( - reverse('edx_proctoring.proctored_exam.allowance', kwargs={'course_id': proctored_exam.course_id}) + reverse('edx_proctoring:proctored_exam.allowance', kwargs={'course_id': proctored_exam.course_id}) ) self.assertEqual(response.status_code, 200) @@ -2410,7 +2411,7 @@ def test_remove_allowance_for_inactive_exam(self): # Add allowance response = self.client.put( - reverse('edx_proctoring.proctored_exam.allowance'), + reverse('edx_proctoring:proctored_exam.allowance'), json.dumps(allowance_data), content_type='application/json' ) @@ -2423,7 +2424,7 @@ def test_remove_allowance_for_inactive_exam(self): proctored_exam.save() response = self.client.delete( - reverse('edx_proctoring.proctored_exam.allowance'), + reverse('edx_proctoring:proctored_exam.allowance'), json.dumps(allowance_data), content_type='application/json' ) @@ -2474,7 +2475,7 @@ def test_get_active_exams_for_user(self): 'course_id': proctored_exam.course_id } response = self.client.get( - reverse('edx_proctoring.proctored_exam.active_exams_for_user'), + reverse('edx_proctoring:proctored_exam.active_exams_for_user'), exams_query_data ) self.assertEqual(response.status_code, 200) @@ -2509,7 +2510,7 @@ def test_launch_for_course(self): expected_url = '/instructor/%s/' % course_id response = self.client.get( - reverse('edx_proctoring.instructor_dashboard_course', args=[course_id]) + reverse('edx_proctoring:instructor_dashboard_course', args=[course_id]) ) self.assertRedirects(response, expected_url, fetch_redirect_response=False) @@ -2529,7 +2530,7 @@ def test_launch_for_exam(self): expected_url = '/instructor/%s/?exam=%s' % (course_id, exam_id) response = self.client.get( - reverse('edx_proctoring.instructor_dashboard_exam', kwargs={'course_id': course_id, 'exam_id': exam_id}) + reverse('edx_proctoring:instructor_dashboard_exam', kwargs={'course_id': course_id, 'exam_id': exam_id}) ) self.assertRedirects(response, expected_url, fetch_redirect_response=False) @@ -2557,7 +2558,7 @@ def test_error_with_multiple_backends(self): backend='null', ) response = self.client.get( - reverse('edx_proctoring.instructor_dashboard_course', + reverse('edx_proctoring:instructor_dashboard_course', kwargs={'course_id': course_id}) ) self.assertEqual(response.status_code, 400) @@ -2566,7 +2567,7 @@ def test_error_with_multiple_backends(self): def test_error_with_no_exams(self): course_id = 'a/b/c' response = self.client.get( - reverse('edx_proctoring.instructor_dashboard_course', + reverse('edx_proctoring:instructor_dashboard_course', kwargs={'course_id': course_id}) ) self.assertEqual(response.status_code, 404) @@ -2583,7 +2584,7 @@ def test_error_with_no_exams(self): backend='software_secure', ) response = self.client.get( - reverse('edx_proctoring.instructor_dashboard_course', + reverse('edx_proctoring:instructor_dashboard_course', kwargs={'course_id': course_id}) ) self.assertEqual(response.status_code, 404) @@ -2602,7 +2603,7 @@ def test_error_with_no_dashboard(self): backend='software_secure', ) response = self.client.get( - reverse('edx_proctoring.instructor_dashboard_course', + reverse('edx_proctoring:instructor_dashboard_course', kwargs={'course_id': course_id}) ) self.assertEqual(response.status_code, 404) diff --git a/edx_proctoring/urls.py b/edx_proctoring/urls.py index b0ad0333682..b81fef07eab 100644 --- a/edx_proctoring/urls.py +++ b/edx_proctoring/urls.py @@ -9,89 +9,91 @@ from edx_proctoring import views, callbacks +app_name = u'edx_proctoring' + urlpatterns = [ url( r'edx_proctoring/v1/proctored_exam/exam$', views.ProctoredExamView.as_view(), - name='edx_proctoring.proctored_exam.exam' + name='proctored_exam.exam' ), url( r'edx_proctoring/v1/proctored_exam/exam/exam_id/(?P\d+)$', views.ProctoredExamView.as_view(), - name='edx_proctoring.proctored_exam.exam_by_id' + name='proctored_exam.exam_by_id' ), url( r'edx_proctoring/v1/proctored_exam/exam/course_id/{}/content_id/(?P[A-z0-9]+)$'.format( settings.COURSE_ID_PATTERN), views.ProctoredExamView.as_view(), - name='edx_proctoring.proctored_exam.exam_by_content_id' + name='proctored_exam.exam_by_content_id' ), url( r'edx_proctoring/v1/proctored_exam/exam/course_id/{}$'.format( settings.COURSE_ID_PATTERN), views.ProctoredExamView.as_view(), - name='edx_proctoring.proctored_exam.exams_by_course_id' + name='proctored_exam.exams_by_course_id' ), url( r'edx_proctoring/v1/proctored_exam/attempt/(?P\d+)$', views.StudentProctoredExamAttempt.as_view(), - name='edx_proctoring.proctored_exam.attempt' + name='proctored_exam.attempt' ), url( r'edx_proctoring/v1/proctored_exam/attempt/course_id/{}$'.format(settings.COURSE_ID_PATTERN), views.StudentProctoredExamAttemptsByCourse.as_view(), - name='edx_proctoring.proctored_exam.attempts.course' + name='proctored_exam.attempts.course' ), url( r'edx_proctoring/v1/proctored_exam/attempt/course_id/{}/search/(?P.+)$'.format( settings.COURSE_ID_PATTERN), views.StudentProctoredExamAttemptsByCourse.as_view(), - name='edx_proctoring.proctored_exam.attempts.search' + name='proctored_exam.attempts.search' ), url( r'edx_proctoring/v1/proctored_exam/attempt$', views.StudentProctoredExamAttemptCollection.as_view(), - name='edx_proctoring.proctored_exam.attempt.collection' + name='proctored_exam.attempt.collection' ), url( r'edx_proctoring/v1/proctored_exam/attempt/(?P\d+)/review_status$', views.ProctoredExamAttemptReviewStatus.as_view(), - name='edx_proctoring.proctored_exam.attempt.review_status' + name='proctored_exam.attempt.review_status' ), url( r'edx_proctoring/v1/proctored_exam/attempt/(?P[-\w]+)/ready$', views.ExamReadyCallback.as_view(), - name='edx_proctoring.proctored_exam.attempt.ready_callback' + name='proctored_exam.attempt.ready_callback' ), url( r'edx_proctoring/v1/proctored_exam/attempt/(?P[-\w]+)/reviewed$', views.ProctoredExamReviewCallback.as_view(), - name='edx_proctoring.proctored_exam.attempt.callback' + name='proctored_exam.attempt.callback' ), url( r'edx_proctoring/v1/proctored_exam/{}/allowance$'.format(settings.COURSE_ID_PATTERN), views.ExamAllowanceView.as_view(), - name='edx_proctoring.proctored_exam.allowance' + name='proctored_exam.allowance' ), url( r'edx_proctoring/v1/proctored_exam/allowance$', views.ExamAllowanceView.as_view(), - name='edx_proctoring.proctored_exam.allowance' + name='proctored_exam.allowance' ), url( r'edx_proctoring/v1/proctored_exam/active_exams_for_user$', views.ActiveExamsForUserView.as_view(), - name='edx_proctoring.proctored_exam.active_exams_for_user' + name='proctored_exam.active_exams_for_user' ), url( r'edx_proctoring/v1/instructor/{}$'.format(settings.COURSE_ID_PATTERN), views.InstructorDashboard.as_view(), - name='edx_proctoring.instructor_dashboard_course' + name='instructor_dashboard_course' ), url( r'edx_proctoring/v1/instructor/{}/(?P\d+)$'.format(settings.COURSE_ID_PATTERN), views.InstructorDashboard.as_view(), - name='edx_proctoring.instructor_dashboard_exam' + name='instructor_dashboard_exam' ), # # Unauthenticated callbacks from SoftwareSecure. Note we use other @@ -100,12 +102,12 @@ url( r'edx_proctoring/proctoring_launch_callback/start_exam/(?P[-\w]+)$', callbacks.start_exam_callback, - name='edx_proctoring.anonymous.proctoring_launch_callback.start_exam' + name='anonymous.proctoring_launch_callback.start_exam' ), url( r'edx_proctoring/proctoring_review_callback/$', views.AnonymousReviewCallback.as_view(), - name='edx_proctoring.anonymous.proctoring_review_callback' + name='anonymous.proctoring_review_callback' ), url(r'^', include('rest_framework.urls', namespace='rest_framework')) ] diff --git a/edx_proctoring/views.py b/edx_proctoring/views.py index 4094be5088d..7dc388a9db6 100644 --- a/edx_proctoring/views.py +++ b/edx_proctoring/views.py @@ -599,12 +599,12 @@ def get(self, request, course_id, search_by=None): # pylint: disable=unused-arg exam_attempts = ProctoredExamStudentAttempt.objects.get_filtered_exam_attempts( course_id, search_by ) - attempt_url = reverse('edx_proctoring.proctored_exam.attempts.search', args=[course_id, search_by]) + attempt_url = reverse('edx_proctoring:proctored_exam.attempts.search', args=[course_id, search_by]) else: exam_attempts = ProctoredExamStudentAttempt.objects.get_all_exam_attempts( course_id ) - attempt_url = reverse('edx_proctoring.proctored_exam.attempts.course', args=[course_id]) + attempt_url = reverse('edx_proctoring:proctored_exam.attempts.course', args=[course_id]) paginator = Paginator(exam_attempts, ATTEMPTS_PER_PAGE) page = request.GET.get('page') diff --git a/package.json b/package.json index 122850b2e29..dc3bbed56eb 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@edx/edx-proctoring", "//": "Be sure to update the version number in edx_proctoring/__init__.py", "//": "Note that the version format is slightly different than that of the Python version when using prereleases.", - "version": "1.5.0-rc.2", + "version": "1.5.0-rc.3", "main": "edx_proctoring/static/index.js", "repository": { "type": "git", diff --git a/pylintrc b/pylintrc index 25cde5d9694..7e0fa5c3706 100644 --- a/pylintrc +++ b/pylintrc @@ -91,7 +91,7 @@ evaluation = 10.0 - ((float(5 * error + warning + refactor + convention) / state [BASIC] bad-functions = map,filter,apply,input module-rgx = (([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ -const-rgx = (([A-Z_][A-Z0-9_]*)|(__.*__)|log|urlpatterns)$ +const-rgx = (([A-Z_][A-Z0-9_]*)|(__.*__)|log|urlpatterns|app_name)$ class-rgx = [A-Z_][a-zA-Z0-9]+$ function-rgx = ([a-z_][a-z0-9_]{2,40}|test_[a-z0-9_]+)$ method-rgx = ([a-z_][a-z0-9_]{2,40}|setUp|set[Uu]pClass|tearDown|tear[Dd]ownClass|assert[A-Z]\w*|maxDiff|test_[a-z0-9_]+)$ diff --git a/setup.py b/setup.py index 6bc89297dd9..879e91c0c66 100755 --- a/setup.py +++ b/setup.py @@ -97,5 +97,12 @@ def is_requirement(line): 'null = edx_proctoring.backends.null:NullBackendProvider', 'software_secure = edx_proctoring.backends.software_secure:SoftwareSecureBackendProvider', ], + 'lms.djangoapp': [ + "edx_proctoring = edx_proctoring.apps:EdxProctoringConfig", + ], + 'cms.djangoapp': [ + "edx_proctoring = edx_proctoring.apps:EdxProctoringConfig", + ], + }, ) diff --git a/test_settings.py b/test_settings.py index a007ab91638..e0f4bd61572 100644 --- a/test_settings.py +++ b/test_settings.py @@ -73,7 +73,7 @@ 'django.middleware.csrf.CsrfViewMiddleware', ) -ROOT_URLCONF = 'edx_proctoring.urls' +ROOT_URLCONF = 'test_urls' COURSE_ID_REGEX = r'[^/+]+(/|\+)[^/+]+(/|\+)[^/]+' COURSE_ID_PATTERN = r'(?P%s)' % COURSE_ID_REGEX diff --git a/test_urls.py b/test_urls.py new file mode 100644 index 00000000000..b1677b66090 --- /dev/null +++ b/test_urls.py @@ -0,0 +1,3 @@ +from django.conf.urls import include, url + +urlpatterns = [url(r'^', include('edx_proctoring.urls', namespace='edx_proctoring'))]