From 4ddf7004fc73d249c7902f07e4d442a9f78acef7 Mon Sep 17 00:00:00 2001 From: Marcos Date: Tue, 25 Jul 2023 16:32:49 -0300 Subject: [PATCH] feat: ACADEMIC-16210: Created a module for API methods --- CHANGELOG.rst | 6 + README.rst | 46 ++++ ai_aside/__init__.py | 2 +- ai_aside/api/api.py | 138 +++++++++++ ai_aside/api/views.py | 125 ++++------ ai_aside/block.py | 15 +- ai_aside/waffle.py | 2 +- tests/api/test_api.py | 294 +++++++++++++++++++++++ tests/{test_api.py => api/test_views.py} | 22 +- tests/test_block.py | 2 +- 10 files changed, 552 insertions(+), 100 deletions(-) create mode 100644 ai_aside/api/api.py create mode 100644 tests/api/test_api.py rename tests/{test_api.py => api/test_views.py} (94%) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 71e315f..e181942 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,6 +14,12 @@ Change Log Unreleased ********** +3.2.0 – 2023-07-26 +********************************************** + +Features +========= +* Added the checks for the module settings behind the waffle flag `summaryhook.summaryhook_summaries_configuration`. * Error suppression logs now include block ID * Missing video transcript is caught earlier in content fetch diff --git a/README.rst b/README.rst index cc7ac9f..488d74d 100644 --- a/README.rst +++ b/README.rst @@ -62,6 +62,52 @@ For the summary aside to work, you will have to make two changes in the LMS admi 2. You must enable a course waffle flag for each course you want to summarize. ``summaryhook.summaryhook_enabled`` is the main one, ``summaryhook_enabled.summaryhook_staff_only`` can be used if you only want staff to see it. +3. You must enable a course waffle flag if you want to use the feature for enabling/disabling the summary by its settings. ``summaryhook.summaryhook_summaries_configuration``. If this flag is enabled, you can enable/disable the courses and the blocks by its settings. + +Aside Settings API +~~~~~~~~~~~~~~~~~~ + +There are some endpoints that can be used to pinpoint units to be either enabled or disabled based on their configs. The new settings work as follows: +- If a course is enabled, the summary for all the blocks for that course are enabled by default. +- If a course is disabled or the setting does not exists, then the summary for all the blocks from that course are disabled by default. +- If a block has its own settings, it will override any other setting with the one that is saved. +- If a block does not have any settings saved, then the enabled state will fall back to the course's enabled state mentioned above. + +The new endpoints for updating these settings are: + +Fetch settings +.............. + ++--------+-------------------------------------+-------------------------------------------------------------------+ +| Method | Path | Responses | ++========+=====================================+===================================================================+ +| GET | ``ai_aside/v1/:course_id`` | - Code 200: ``{ "success": true }`` | ++--------+-------------------------------------+ - Code 400: ``{ "success": false, "message": "(description)" }`` | +| GET | ``ai_aside/v1/:course_id/:unit_id`` | - Code 404: ``{ "success": false }`` | ++--------+-------------------------------------+-------------------------------------------------------------------+ + +Update settings +............... + ++--------+-------------------------------------+-------------------------------+------------------------------------------------------------------+ +| Method | Path | Payload | Responses | ++========+=====================================+===============================+==================================================================+ +| POST | ``ai_aside/v1/:course_id`` | ``{ "enabled": true|false }`` | - Code 200: ``{ "success": true }`` | ++--------+-------------------------------------+-------------------------------+ - Code 400: ``{ "success": false, "message": "(description)" }`` | +| POST | ``ai_aside/v1/:course_id/:unit_id`` | ``{ "enabled": true|false }`` | | ++--------+-------------------------------------+-------------------------------+------------------------------------------------------------------+ + +Delete settings +............... + ++--------+-------------------------------------+-------------------------------------------------------------------+ +| Method | Path | Responses | ++========+=====================================+===================================================================+ +| DELETE | ``ai_aside/v1/:course_id`` | - Code 200: ``{ "success": true }`` | ++--------+-------------------------------------+ - Code 400: ``{ "success": false, "message": "(description)" }`` | +| DELETE | ``ai_aside/v1/:course_id/:unit_id`` | - Code 404: ``{ "success": false }`` | ++--------+-------------------------------------+-------------------------------------------------------------------+ + Every time you develop something in this repo --------------------------------------------- .. code-block:: diff --git a/ai_aside/__init__.py b/ai_aside/__init__.py index f4df085..ad142c4 100644 --- a/ai_aside/__init__.py +++ b/ai_aside/__init__.py @@ -2,6 +2,6 @@ A plugin containing xblocks and apps supporting GPT and other LLM use on edX. """ -__version__ = '3.1.0' +__version__ = '3.2.0' default_app_config = "ai_aside.apps.AiAsideConfig" diff --git a/ai_aside/api/api.py b/ai_aside/api/api.py new file mode 100644 index 0000000..5faf438 --- /dev/null +++ b/ai_aside/api/api.py @@ -0,0 +1,138 @@ +""" +Implements an API for updating unit and course settings. +""" +from ai_aside.models import AIAsideCourseEnabled, AIAsideUnitEnabled +from ai_aside.waffle import summaries_configuration_enabled + + +class NotFoundError(Exception): + "Raised when the course/unit is not found in the database" + + +def _get_course(course_key): + "Private method that gets a course based on an id" + try: + record = AIAsideCourseEnabled.objects.get( + course_key=course_key, + ) + except AIAsideCourseEnabled.DoesNotExist as exc: + raise NotFoundError from exc + + return record + + +def _get_unit(course_key, unit_key): + "Private method that gets a unit based on an id" + try: + record = AIAsideUnitEnabled.objects.get( + course_key=course_key, + unit_key=unit_key, + ) + except AIAsideUnitEnabled.DoesNotExist as exc: + raise NotFoundError from exc + + return record + + +def get_course_settings(course_key): + "Gets the settings of a course" + record = _get_course(course_key) + fields = { + 'enabled': record.enabled + } + + return fields + + +def set_course_settings(course_key, settings): + "Sets the settings of a course" + + enabled = settings['enabled'] + + if not isinstance(enabled, bool): + raise TypeError + + update = {'enabled': enabled} + + AIAsideCourseEnabled.objects.update_or_create( + course_key=course_key, + defaults=update, + ) + + return True + + +def delete_course_settings(course_key): + "Deletes the settings of a course" + record = _get_course(course_key) + record.delete() + + return True + + +def get_unit_settings(course_key, unit_key): + "Gets the settings of a course's unit" + record = _get_unit(course_key, unit_key) + + fields = { + 'enabled': record.enabled + } + + return fields + + +def set_unit_settings(course_key, unit_key, settings): + "Sets the settings of a course's unit" + + enabled = settings['enabled'] + + if not isinstance(enabled, bool): + raise TypeError + + update = {'enabled': enabled} + + AIAsideUnitEnabled.objects.update_or_create( + course_key=course_key, + unit_key=unit_key, + defaults=update, + ) + + return True + + +def delete_unit_settings(course_key, unit_key): + "Deletes the settings of a course's unit" + record = _get_unit(course_key, unit_key) + record.delete() + + return True + + +def is_summary_enabled(course_key, unit_key=None): + """ + Gets the enabled state of a course's unit. + It considers both the state of a unit's override and a course defaults. + """ + + # If the feature flag is disabled, do not restrict + if not summaries_configuration_enabled(course_key): + return True + + if unit_key is not None: + try: + unit = _get_unit(course_key, unit_key) + + if unit is not None: + return unit.enabled + except NotFoundError: + pass + + try: + course = _get_course(course_key) + except NotFoundError: + return False + + if course is not None: + return course.enabled + + return False diff --git a/ai_aside/api/views.py b/ai_aside/api/views.py index 1eaa72c..5604b56 100644 --- a/ai_aside/api/views.py +++ b/ai_aside/api/views.py @@ -21,7 +21,15 @@ from rest_framework.response import Response from rest_framework.views import APIView -from ai_aside.models import AIAsideCourseEnabled, AIAsideUnitEnabled +from ai_aside.api.api import ( + NotFoundError, + delete_course_settings, + delete_unit_settings, + get_course_settings, + get_unit_settings, + set_course_settings, + set_unit_settings, +) class APIResponse(Response): @@ -43,66 +51,43 @@ def get(self, request, course_id=None): return APIResponse(http_status=status.HTTP_404_NOT_FOUND) try: - course_key = CourseKey.from_string(course_id) + settings = get_course_settings(CourseKey.from_string(course_id)) except InvalidKeyError: data = {'message': 'Invalid Key'} return APIResponse(http_status=status.HTTP_400_BAD_REQUEST, data=data) - - try: - record = AIAsideCourseEnabled.objects.get( - course_key=course_key, - ) - except AIAsideCourseEnabled.DoesNotExist: + except NotFoundError: return APIResponse(http_status=status.HTTP_404_NOT_FOUND) - enabled = record.enabled - - data = {'enabled': enabled} - return APIResponse(success=True, data=data) + return APIResponse(success=True, data=settings) def post(self, request, course_id=None): """Sets the enabled state for a course""" enabled = request.data.get('enabled') - if not isinstance(enabled, bool): - data = {'message': 'Invalid parameters'} - return APIResponse(http_status=status.HTTP_400_BAD_REQUEST, data=data) - - if course_id is None: - return APIResponse(http_status=status.HTTP_404_NOT_FOUND) - try: - course_key = CourseKey.from_string(course_id) + set_course_settings(CourseKey.from_string(course_id), {'enabled': enabled}) except InvalidKeyError: data = {'message': 'Invalid Key'} return APIResponse(http_status=status.HTTP_400_BAD_REQUEST, data=data) + except TypeError: + data = {'message': 'Invalid parameters'} + return APIResponse(http_status=status.HTTP_400_BAD_REQUEST, data=data) - AIAsideCourseEnabled.objects.update_or_create( - course_key=course_key, - defaults={'enabled': enabled} - ) - - data = {'enabled': enabled} - - return APIResponse(success=True, data=data) + return APIResponse(success=True) def delete(self, request, course_id=None): """Deletes the settings for a module""" + if course_id is None: return APIResponse(http_status=status.HTTP_404_NOT_FOUND) try: - course_key = CourseKey.from_string(course_id) + delete_course_settings(CourseKey.from_string(course_id)) except InvalidKeyError: data = {'message': 'Invalid Key'} return APIResponse(http_status=status.HTTP_400_BAD_REQUEST, data=data) - - try: - AIAsideCourseEnabled.objects.get( - course_key=course_key, - ).delete() - except AIAsideCourseEnabled.DoesNotExist: + except NotFoundError: return APIResponse(http_status=status.HTTP_404_NOT_FOUND) return APIResponse(success=True) @@ -110,81 +95,59 @@ def delete(self, request, course_id=None): class UnitEnabledAPIView(APIView): """Handlers for module level settings""" + def get(self, request, course_id=None, unit_id=None): - """Gets the enabled state for a module""" + """Gets the enabled state for a unit""" if course_id is None or unit_id is None: return APIResponse(http_status=status.HTTP_404_NOT_FOUND) try: - course_key = CourseKey.from_string(course_id) - unit_key = UsageKey.from_string(unit_id) + settings = get_unit_settings( + CourseKey.from_string(course_id), + UsageKey.from_string(unit_id), + ) except InvalidKeyError: data = {'message': 'Invalid Key'} return APIResponse(http_status=status.HTTP_400_BAD_REQUEST, data=data) - - try: - record = AIAsideUnitEnabled.objects.get( - course_key=course_key, - unit_key=unit_key, - ) - except AIAsideUnitEnabled.DoesNotExist: - return APIResponse(http_status=status.HTTP_404_NOT_FOUND) - - if record is None: + except NotFoundError: return APIResponse(http_status=status.HTTP_404_NOT_FOUND) - enabled = record.enabled - - data = {'enabled': enabled} - return APIResponse(success=True, data=data) + return APIResponse(success=True, data=settings) def post(self, request, course_id=None, unit_id=None): - - """Sets the enabled state for a module""" - if course_id is None or unit_id is None: - return APIResponse(http_status=status.HTTP_404_NOT_FOUND) + """Sets the enabled state for a unit""" enabled = request.data.get('enabled') - if not isinstance(enabled, bool): - data = {'message': 'Invalid parameters'} - return APIResponse(http_status=status.HTTP_400_BAD_REQUEST, data=data) - try: - course_key = CourseKey.from_string(course_id) - unit_key = UsageKey.from_string(unit_id) + set_unit_settings( + CourseKey.from_string(course_id), + UsageKey.from_string(unit_id), + {'enabled': enabled}) except InvalidKeyError: data = {'message': 'Invalid Key'} return APIResponse(http_status=status.HTTP_400_BAD_REQUEST, data=data) + except TypeError: + data = {'message': 'Invalid parameters'} + return APIResponse(http_status=status.HTTP_400_BAD_REQUEST, data=data) - AIAsideUnitEnabled.objects.update_or_create( - course_key=course_key, - unit_key=unit_key, - defaults={'enabled': enabled} - ) - - data = {'enabled': enabled} - - return APIResponse(success=True, data=data) + return APIResponse(success=True) def delete(self, request, course_id=None, unit_id=None): - """Deletes the settings for a module""" + """Deletes the settings for a unit""" + if course_id is None or unit_id is None: return APIResponse(http_status=status.HTTP_404_NOT_FOUND) try: - course_key = CourseKey.from_string(course_id) - unit_key = UsageKey.from_string(unit_id) + delete_unit_settings( + CourseKey.from_string(course_id), + UsageKey.from_string(unit_id), + ) except InvalidKeyError: data = {'message': 'Invalid Key'} return APIResponse(http_status=status.HTTP_400_BAD_REQUEST, data=data) - - try: - AIAsideUnitEnabled.objects.get( - course_key=course_key, - unit_key=unit_key, - ).delete() - except AIAsideUnitEnabled.DoesNotExist: + except NotFoundError: return APIResponse(http_status=status.HTTP_404_NOT_FOUND) return APIResponse(success=True) diff --git a/ai_aside/block.py b/ai_aside/block.py index dcfefbc..5148fe6 100644 --- a/ai_aside/block.py +++ b/ai_aside/block.py @@ -9,9 +9,11 @@ from webob import Response from xblock.core import XBlock, XBlockAside +from ai_aside.api.api import is_summary_enabled from ai_aside.platform_imports import get_block, get_text_transcript from ai_aside.text_utils import html_to_text -from ai_aside.waffle import summary_enabled, summary_staff_only +from ai_aside.waffle import summary_enabled as ff_summary_enabled +from ai_aside.waffle import summary_staff_only as ff_summary_staff_only log = logging.getLogger(__name__) @@ -271,7 +273,14 @@ def _should_apply_can_throw(cls, block): """ if getattr(block, 'category', None) != 'vertical': return False + course_key = block.scope_ids.usage_id.course_key - if _staff_user(block) and summary_staff_only(course_key): + unit_key = block.scope_ids.usage_id + + if _staff_user(block) and ff_summary_staff_only(course_key): return True - return summary_enabled(course_key) + + if not ff_summary_enabled(course_key): + return False + + return is_summary_enabled(course_key, unit_key) diff --git a/ai_aside/waffle.py b/ai_aside/waffle.py index 9730dba..26cbf26 100644 --- a/ai_aside/waffle.py +++ b/ai_aside/waffle.py @@ -72,6 +72,6 @@ def summary_staff_only(course_key): def summaries_configuration_enabled(course_key): """ - Return whether the summaryhook.summaryhook_staff_only WaffleFlag is on. + Return whether the summaryhook.summaryhook_summaries_configuration WaffleFlag is on. """ return _is_get_summaryhook_waffle_flag_enabled(SUMMARYHOOK_SUMMARIES_CONFIGURATION, course_key) diff --git a/tests/api/test_api.py b/tests/api/test_api.py new file mode 100644 index 0000000..1f8f480 --- /dev/null +++ b/tests/api/test_api.py @@ -0,0 +1,294 @@ +""" +Tests for the API +""" +from unittest.mock import patch + +from django.test import TestCase +from opaque_keys.edx.keys import CourseKey, UsageKey + +from ai_aside.api.api import ( + NotFoundError, + delete_course_settings, + delete_unit_settings, + get_course_settings, + get_unit_settings, + is_summary_enabled, + set_course_settings, + set_unit_settings, +) +from ai_aside.models import AIAsideCourseEnabled, AIAsideUnitEnabled + +course_keys = [ + CourseKey.from_string('course-v1:edX+DemoX+Demo_Course'), + CourseKey.from_string('course-v1:edX+DemoX+Demo_Course-2'), + CourseKey.from_string('course-v1:edX+DemoX+Demo_Course-3'), +] + +unit_keys = [ + UsageKey.from_string('block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical_0270f6de40fc'), + UsageKey.from_string('block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical_321ac313f2de'), + UsageKey.from_string('block-v1:edX+DemoX+Demo_Course+type@vertical+block@vertical_12b22cfd23e2'), +] + + +class TestApiMethods(TestCase): + """API Endpoint Method tests""" + def test_set_course_settings(self): + course_key = course_keys[0] + response = set_course_settings(course_key, {'enabled': True}) + + self.assertEqual(response, True) + + res = AIAsideCourseEnabled.objects.filter(course_key=course_key) + + self.assertEqual(res.count(), 1) + self.assertTrue(res.get().enabled) + + def test_set_course_settings_duplicates(self): + course_key = course_keys[0] + + AIAsideCourseEnabled.objects.create( + course_key=course_key, + enabled=True + ) + + response = set_course_settings(course_key, {'enabled': False}) + + self.assertEqual(response, True) + + res = AIAsideCourseEnabled.objects.filter(course_key=course_key) + + self.assertEqual(res.count(), 1) + self.assertFalse(res.get().enabled) + + def test_set_course_settings_invalid_parameters(self): + course_key = course_keys[0] + + with self.assertRaises(TypeError): + set_course_settings(course_key, {'enabled': 'false'}) + + res = AIAsideCourseEnabled.objects.filter(course_key=course_key) + + self.assertEqual(res.count(), 0) + + def test_get_course_settings(self): + course_key = course_keys[0] + + AIAsideCourseEnabled.objects.create( + course_key=course_key, + enabled=True + ) + + settings = get_course_settings(course_key) + + self.assertTrue(settings['enabled']) + + def test_get_course_settings_not_found(self): + with self.assertRaises(NotFoundError): + get_course_settings(course_keys[1]) + + def test_course_delete(self): + course_key = course_keys[0] + + AIAsideCourseEnabled.objects.create( + course_key=course_key, + enabled=True + ) + + delete_course_settings(course_key) + + res = AIAsideCourseEnabled.objects.filter(course_key=course_key) + + self.assertEqual(res.count(), 0) + + def test_course_delete_not_found(self): + with self.assertRaises(NotFoundError): + delete_course_settings(course_keys[1]) + + def test_set_unit_settings(self): + course_key = course_keys[0] + unit_key = unit_keys[0] + + response = set_unit_settings(course_key, unit_key, {'enabled': True}) + + self.assertEqual(response, True) + + res = AIAsideUnitEnabled.objects.filter( + course_key=course_key, + unit_key=unit_key, + ) + + self.assertEqual(res.count(), 1) + self.assertTrue(res.get().enabled) + + def test_set_unit_settings_check_duplicates(self): + course_key = course_keys[0] + unit_key = unit_keys[0] + + AIAsideUnitEnabled.objects.create( + course_key=course_key, + unit_key=unit_key, + enabled=True, + ) + + set_unit_settings(course_key, unit_key, {'enabled': False}) + + res = AIAsideUnitEnabled.objects.filter( + course_key=course_key, + unit_key=unit_key, + ) + + self.assertEqual(res.count(), 1) + self.assertFalse(res.get().enabled) + + def test_set_unit_settings_invalid_parameters(self): + course_key = course_keys[0] + unit_key = unit_keys[0] + + with self.assertRaises(TypeError): + set_unit_settings(course_key, unit_key, {'enabled': 'false'}) + + res = AIAsideUnitEnabled.objects.filter( + course_key=course_key, + unit_key=unit_key, + ) + + self.assertEqual(res.count(), 0) + + def test_get_unit_settings(self): + course_key = course_keys[0] + unit_key = unit_keys[0] + + AIAsideUnitEnabled.objects.create( + course_key=course_key, + unit_key=unit_key, + enabled=True, + ) + + settings = get_unit_settings(course_key, unit_key) + + self.assertTrue(settings['enabled']) + + def test_get_unit_settings_not_found(self): + course_key = course_keys[1] + unit_key = unit_keys[1] + + with self.assertRaises(NotFoundError): + get_unit_settings(course_key, unit_key) + + def test_unit_delete(self): + course_key = course_keys[0] + unit_key = unit_keys[0] + + AIAsideUnitEnabled.objects.create( + course_key=course_key, + unit_key=unit_key, + enabled=True, + ) + + response = delete_unit_settings(course_key, unit_key) + + self.assertTrue(response) + + res = AIAsideUnitEnabled.objects.filter( + course_key=course_key, + unit_key=unit_key, + enabled=True, + ) + + self.assertEqual(res.count(), 0) + + def test_unit_delete_not_found(self): + course_key = course_keys[1] + unit_key = unit_keys[1] + + with self.assertRaises(NotFoundError): + delete_unit_settings(course_key, unit_key) + + @patch('ai_aside.api.api.summaries_configuration_enabled') + def test_is_summary_enabled_course(self, mock_enabled): + mock_enabled.return_value = True + course_key_true = course_keys[0] + course_key_false = course_keys[1] + + AIAsideCourseEnabled.objects.create( + course_key=course_key_true, + enabled=True, + ) + AIAsideCourseEnabled.objects.create( + course_key=course_key_false, + enabled=False, + ) + + self.assertTrue(is_summary_enabled(course_key_true)) + self.assertFalse(is_summary_enabled(course_key_false)) + + course_key_non_existent = course_keys[2] + self.assertFalse(is_summary_enabled(course_key_non_existent)) + + @patch('ai_aside.api.api.summaries_configuration_enabled') + def test_is_summary_enabled_unit(self, mock_enabled): + mock_enabled.return_value = True + course_key = course_keys[0] + unit_key_true = unit_keys[0] + unit_key_false = unit_keys[1] + + AIAsideUnitEnabled.objects.create( + course_key=course_key, + unit_key=unit_key_true, + enabled=True, + ) + AIAsideUnitEnabled.objects.create( + course_key=course_key, + unit_key=unit_key_false, + enabled=False, + ) + + self.assertTrue(is_summary_enabled(course_key, unit_key_true)) + self.assertFalse(is_summary_enabled(course_key, unit_key_false)) + + unit_key_non_existent = unit_keys[2] + self.assertFalse(is_summary_enabled(course_key, unit_key_non_existent)) + + @patch('ai_aside.api.api.summaries_configuration_enabled') + def test_is_summary_enabled_fallback(self, mock_enabled): + mock_enabled.return_value = True + course_key_true = course_keys[0] + course_key_false = course_keys[1] + course_key_non_existent = course_keys[2] + unit_key_non_existent = unit_keys[0] + + AIAsideCourseEnabled.objects.create( + course_key=course_key_true, + enabled=True, + ) + AIAsideCourseEnabled.objects.create( + course_key=course_key_false, + enabled=False, + ) + + self.assertTrue(is_summary_enabled(course_key_true, unit_key_non_existent)) + self.assertFalse(is_summary_enabled(course_key_false, unit_key_non_existent)) + self.assertFalse(is_summary_enabled(course_key_non_existent, unit_key_non_existent)) + + def test_is_summary_enabled_disabled_feature_flag(self): + course_key_true = course_keys[0] + course_key_false = course_keys[1] + course_key_non_existent = course_keys[2] + unit_key_non_existent = unit_keys[0] + + AIAsideCourseEnabled.objects.create( + course_key=course_key_true, + enabled=True, + ) + AIAsideCourseEnabled.objects.create( + course_key=course_key_false, + enabled=False, + ) + + self.assertTrue(is_summary_enabled(course_key_non_existent)) + self.assertTrue(is_summary_enabled(course_key_true)) + self.assertTrue(is_summary_enabled(course_key_false)) + self.assertTrue(is_summary_enabled(course_key_true, unit_key_non_existent)) + self.assertTrue(is_summary_enabled(course_key_false, unit_key_non_existent)) + self.assertTrue(is_summary_enabled(course_key_non_existent, unit_key_non_existent)) diff --git a/tests/test_api.py b/tests/api/test_views.py similarity index 94% rename from tests/test_api.py rename to tests/api/test_views.py index 07a504c..9b32419 100644 --- a/tests/test_api.py +++ b/tests/api/test_views.py @@ -18,8 +18,8 @@ ] -class TestApiEndpoints(APITestCase): - """API Endpoint tests""" +class TestApiViews(APITestCase): + """API Endpoint View tests""" def test_course_enabled_setter_enable_valid(self): course_id = course_keys[0] @@ -29,7 +29,6 @@ def test_course_enabled_setter_enable_valid(self): self.assertEqual(response.status_code, 200) self.assertEqual(response.data['response']['success'], True) - self.assertEqual(response.data['response']['enabled'], True) res = AIAsideCourseEnabled.objects.filter(course_key=course_id) @@ -41,7 +40,7 @@ def test_course_enabled_setter_disable_valid_and_check_duplicates(self): AIAsideCourseEnabled.objects.create( course_key=CourseKey.from_string(course_id), - enabled=True + enabled=True, ) api_url = reverse('api-course-settings', kwargs={'course_id': course_id}) @@ -49,7 +48,6 @@ def test_course_enabled_setter_disable_valid_and_check_duplicates(self): self.assertEqual(response.status_code, 200) self.assertEqual(response.data['response']['success'], True) - self.assertEqual(response.data['response']['enabled'], False) res = AIAsideCourseEnabled.objects.filter(course_key=course_id) @@ -74,7 +72,7 @@ def test_course_enabled_getter_valid(self): AIAsideCourseEnabled.objects.create( course_key=CourseKey.from_string(course_id), - enabled=True + enabled=True, ) api_url = reverse('api-course-settings', kwargs={'course_id': course_id}) @@ -97,7 +95,7 @@ def test_course_delete(self): AIAsideCourseEnabled.objects.create( course_key=CourseKey.from_string(course_id), - enabled=True + enabled=True, ) api_url = reverse('api-course-settings', kwargs={'course_id': course_id}) @@ -130,7 +128,6 @@ def test_unit_enabled_setter_enable_valid(self): self.assertEqual(response.status_code, 200) self.assertEqual(response.data['response']['success'], True) - self.assertEqual(response.data['response']['enabled'], True) res = AIAsideUnitEnabled.objects.filter(course_key=course_id) @@ -144,7 +141,7 @@ def test_unit_enabled_setter_disable_valid_and_check_duplicates(self): AIAsideUnitEnabled.objects.create( course_key=CourseKey.from_string(course_id), unit_key=UsageKey.from_string(unit_id), - enabled=True + enabled=True, ) api_url = reverse('api-unit-settings', kwargs={ @@ -155,7 +152,6 @@ def test_unit_enabled_setter_disable_valid_and_check_duplicates(self): self.assertEqual(response.status_code, 200) self.assertEqual(response.data['response']['success'], True) - self.assertEqual(response.data['response']['enabled'], False) res = AIAsideUnitEnabled.objects.filter(course_key=course_id) @@ -199,7 +195,7 @@ def test_unit_enabled_getter_valid(self): AIAsideUnitEnabled.objects.create( course_key=CourseKey.from_string(course_id), unit_key=UsageKey.from_string(unit_id), - enabled=True + enabled=True, ) api_url = reverse('api-unit-settings', kwargs={ @@ -244,7 +240,7 @@ def test_unit_delete(self): AIAsideUnitEnabled.objects.create( course_key=CourseKey.from_string(course_id), unit_key=UsageKey.from_string(unit_id), - enabled=True + enabled=True, ) api_url = reverse('api-unit-settings', kwargs={ @@ -259,7 +255,7 @@ def test_unit_delete(self): res = AIAsideUnitEnabled.objects.filter( course_key=CourseKey.from_string(course_id), unit_key=UsageKey.from_string(unit_id), - enabled=True + enabled=True, ) self.assertEqual(res.count(), 0) diff --git a/tests/test_block.py b/tests/test_block.py index 87c4fc0..51d5012 100644 --- a/tests/test_block.py +++ b/tests/test_block.py @@ -281,7 +281,7 @@ class FakeNotFoundError(BaseException): xmodule_exceptions_mock.NotFoundError = FakeNotFoundError def error_get_transcript(child, lang=None, output_format='SRT', - youtube_id=None): # pylint: disable=unused-argument + youtube_id=None): raise FakeNotFoundError() transcript_utils_mock = MagicMock()