Skip to content

Commit

Permalink
feat: golle/ACADEMIC-16209 - Update views to support reset and delete…
Browse files Browse the repository at this point in the history
… course settings (#49)

feat: golle/ACADEMIC-16209 - Update views to support reset and delete course settings

* Bump version to 3.3.0
* add comments for enabled and reset params
* rename method 'is_course_present' to 'is_course_settings_present'
  • Loading branch information
germanolleunlp authored Aug 16, 2023
1 parent a110d86 commit 8eaf4f2
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 11 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ Change Log
Unreleased
**********

3.3.0 – 2023-08-16
**********************************************

Features
=========
* Add xpert summaries configuration by default for units

3.2.0 – 2023-07-26
**********************************************

Expand Down
2 changes: 1 addition & 1 deletion ai_aside/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
A plugin containing xblocks and apps supporting GPT and other LLM use on edX.
"""

__version__ = '3.2.0'
__version__ = '3.3.0'

default_app_config = "ai_aside.apps.AiAsideConfig"
23 changes: 21 additions & 2 deletions ai_aside/config_api/api.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
Implements an API for updating unit and course settings.
"""
from ai_aside.config_api.internal import NotFoundError, _get_course, _get_unit
from ai_aside.config_api.internal import NotFoundError, _get_course, _get_course_units, _get_unit
from ai_aside.models import AIAsideCourseEnabled, AIAsideUnitEnabled
from ai_aside.waffle import summaries_configuration_enabled

Expand Down Expand Up @@ -49,6 +49,7 @@ def delete_course_settings(course_key):
Raises NotFoundError if the settings are not found.
"""
reset_course_unit_settings(course_key)
record = _get_course(course_key)
record.delete()

Expand All @@ -69,6 +70,13 @@ def get_unit_settings(course_key, unit_key):
return fields


def reset_course_unit_settings(course_key):
"""
Deletes the unit settings of a course.
"""
return _get_course_units(course_key).delete()


def set_unit_settings(course_key, unit_key, settings):
"""
Sets the settings of a course's unit.
Expand Down Expand Up @@ -112,13 +120,24 @@ def is_summary_config_enabled(course_key):
return summaries_configuration_enabled(course_key)


def is_course_settings_present(course_key):
"""
Exist a course for the given key?
"""
try:
course = _get_course(course_key)
return course is not None
except NotFoundError:
return False


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, always returns false.
# If the feature flag is disabled, always returns False.
if not summaries_configuration_enabled(course_key):
return False

Expand Down
7 changes: 7 additions & 0 deletions ai_aside/config_api/internal.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,10 @@ def _get_unit(course_key, unit_key):
raise NotFoundError from exc

return record


def _get_course_units(course_key):
"Private method that gets a unit based on a course_key"
return AIAsideUnitEnabled.objects.filter(
course_key=course_key,
)
12 changes: 10 additions & 2 deletions ai_aside/config_api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
get_course_settings,
get_unit_settings,
is_summary_config_enabled,
reset_course_unit_settings,
set_course_settings,
set_unit_settings,
)
Expand Down Expand Up @@ -80,12 +81,19 @@ def get(self, request, course_id=None):
return APIResponse(success=True, data=settings)

def post(self, request, course_id=None):
"""Sets the enabled state for a course"""
"""Update the course and reset if its necessary"""

# enabled: Updates the course enabled default state
enabled = request.data.get('enabled')

# reset: If it is present, it will delete all unit settings, resetting them back to the default
reset = request.data.get('reset')

try:
set_course_settings(CourseKey.from_string(course_id), {'enabled': enabled})
course_key = CourseKey.from_string(course_id)
set_course_settings(course_key, {'enabled': enabled})
if reset:
reset_course_unit_settings(course_key)
except InvalidKeyError:
data = {'message': 'Invalid Key'}
return APIResponse(http_status=status.HTTP_400_BAD_REQUEST, data=data)
Expand Down
Empty file added tests/__init__.py
Empty file.
64 changes: 62 additions & 2 deletions tests/api/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
delete_unit_settings,
get_course_settings,
get_unit_settings,
is_course_settings_present,
is_summary_enabled,
reset_course_unit_settings,
set_course_settings,
set_unit_settings,
)
Expand Down Expand Up @@ -86,22 +88,47 @@ def test_get_course_settings_not_found(self):

def test_course_delete(self):
course_key = course_keys[0]
unit_key = unit_keys[0]

AIAsideCourseEnabled.objects.create(
course_key=course_key,
enabled=True
)

AIAsideUnitEnabled.objects.create(
course_key=course_key,
unit_key=unit_key,
enabled=True
)

delete_course_settings(course_key)

res = AIAsideCourseEnabled.objects.filter(course_key=course_key)
courses = AIAsideCourseEnabled.objects.filter(course_key=course_key)
units = AIAsideUnitEnabled.objects.filter(course_key=course_key)

self.assertEqual(res.count(), 0)
self.assertEqual(courses.count(), 0)
self.assertEqual(units.count(), 0)

def test_course_delete_not_found(self):
with self.assertRaises(NotFoundError):
delete_course_settings(course_keys[1])

def test_course_delete_not_found_reset_all_units(self):
course_key = course_keys[1]
unit_key = unit_keys[1]

AIAsideUnitEnabled.objects.create(
course_key=course_key,
unit_key=unit_key,
)

units = AIAsideUnitEnabled.objects.filter(course_key=course_key)

with self.assertRaises(NotFoundError):
self.assertEqual(units.count(), 1)
delete_course_settings(course_keys[1])
self.assertEqual(units.count(), 0)

def test_set_unit_settings(self):
course_key = course_keys[0]
unit_key = unit_keys[0]
Expand Down Expand Up @@ -285,3 +312,36 @@ def test_is_summary_enabled_disabled_feature_flag(self):
self.assertFalse(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_reset_course_unit_settings(self):
course_key = course_keys[0]

AIAsideUnitEnabled.objects.create(
course_key=course_key,
unit_key=unit_keys[0],
enabled=True
)

AIAsideUnitEnabled.objects.create(
course_key=course_key,
unit_key=unit_keys[1],
enabled=False
)

units = AIAsideUnitEnabled.objects.filter(course_key=course_key)
self.assertEqual(units.count(), 2)

reset_course_unit_settings(course_key)
self.assertEqual(units.count(), 0)

def test_is_course_settings_present(self):
course_key = course_keys[0]

self.assertFalse(is_course_settings_present(course_key))

AIAsideCourseEnabled.objects.create(
course_key=course_key,
enabled=True,
)

self.assertTrue(is_course_settings_present(course_key))
39 changes: 35 additions & 4 deletions tests/api/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,21 +109,31 @@ def test_course_enabled_getter_404(self):

def test_course_delete(self):
course_id = course_keys[0]
unit_id = unit_keys[0]

AIAsideCourseEnabled.objects.create(
course_key=CourseKey.from_string(course_id),
enabled=True,
)
AIAsideUnitEnabled.objects.create(
course_key=CourseKey.from_string(course_id),
unit_key=UsageKey.from_string(unit_id),
enabled=True,
)

courses = AIAsideCourseEnabled.objects.filter(course_key=course_id)
units = AIAsideUnitEnabled.objects.filter(course_key=course_id)

self.assertEqual(courses.count(), 1)
self.assertEqual(units.count(), 1)

api_url = reverse('api-course-settings', kwargs={'course_id': course_id})
response = self.client.delete(api_url)

self.assertEqual(response.status_code, 200)
self.assertEqual(response.data['response']['success'], True)

res = AIAsideCourseEnabled.objects.filter(course_key=course_id)

self.assertEqual(res.count(), 0)
self.assertEqual(courses.count(), 0)
self.assertEqual(units.count(), 0)

def test_course_delete_404(self):
course_id = course_keys[1]
Expand Down Expand Up @@ -288,3 +298,24 @@ def test_unit_delete_404(self):
response = self.client.delete(api_url)

self.assertEqual(response.status_code, 404)

def test_course_enabled_setter_enable_valid_and_reset(self):
course_id = course_keys[0]
unit_id = unit_keys[0]

units = AIAsideUnitEnabled.objects.filter(course_key=course_id)
AIAsideUnitEnabled.objects.create(
course_key=CourseKey.from_string(course_id),
unit_key=UsageKey.from_string(unit_id),
enabled=True,
)

self.assertEqual(units.count(), 1)

api_url = reverse('api-course-settings', kwargs={'course_id': course_id})

self.client.post(api_url, {'enabled': True, 'reset': False}, format='json')
self.assertEqual(units.count(), 1)

self.client.post(api_url, {'enabled': False, 'reset': True}, format='json')
self.assertEqual(units.count(), 0)

0 comments on commit 8eaf4f2

Please sign in to comment.