Skip to content

Commit

Permalink
feat: add GET endpoint to retrieve whether Learning Assistant is enab…
Browse files Browse the repository at this point in the history
…led (#63)

This commit adds a new GET endpoint to retrieve whether the Learning Assistant feature is enabled in a course, given a course ID. This endpoint will introspect all data that is relevant to determining whether the feature is enabled (i.e. the courseware.learning_assistant CourseWaffleFlag and the LearningAssistantCourseEnabled model).

This endpoint was added so that the frontend can determine whether or not to show the Learning Assistant feature without requiring that the edx-platform read from the LearningAssistantCourseEnabled model in this plugin.
  • Loading branch information
MichaelRoytman authored Feb 1, 2024
1 parent 8a25061 commit ab8b969
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 4 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ Change Log
Unreleased
**********

3.4.0 - 2024-01-30
******************
* Add new GET endpoint to retrieve whether Learning Assistant is enabled in a given course.

3.3.0 - 2024-01-30
******************
* Fix release version
Expand Down
2 changes: 1 addition & 1 deletion learning_assistant/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
Plugin for a learning assistant backend, intended for use within edx-platform.
"""

__version__ = '3.3.0'
__version__ = '3.4.0'

default_app_config = 'learning_assistant.apps.LearningAssistantConfig' # pylint: disable=invalid-name
9 changes: 7 additions & 2 deletions learning_assistant/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@
from django.urls import re_path

from learning_assistant.constants import COURSE_ID_PATTERN
from learning_assistant.views import CourseChatView
from learning_assistant.views import CourseChatView, LearningAssistantEnabledView

app_name = 'learning_assistant'

urlpatterns = [
re_path(
fr'learning_assistant/v1/course_id/{COURSE_ID_PATTERN}',
fr'learning_assistant/v1/course_id/{COURSE_ID_PATTERN}$',
CourseChatView.as_view(),
name='chat'
),
re_path(
fr'learning_assistant/v1/course_id/{COURSE_ID_PATTERN}/enabled',
LearningAssistantEnabledView.as_view(),
name='enabled',
),
]
54 changes: 53 additions & 1 deletion learning_assistant/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import logging

from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
from opaque_keys import InvalidKeyError
from opaque_keys.edx.keys import CourseKey
from rest_framework import status as http_status
from rest_framework.authentication import SessionAuthentication
Expand Down Expand Up @@ -44,7 +45,13 @@ def post(self, request, course_id):
]
}
"""
courserun_key = CourseKey.from_string(course_id)
try:
courserun_key = CourseKey.from_string(course_id)
except InvalidKeyError:
return Response(
status=http_status.HTTP_400_BAD_REQUEST,
data={'detail': 'Course ID is not a valid course ID.'}
)

if not learning_assistant_enabled(courserun_key):
return Response(
Expand Down Expand Up @@ -91,3 +98,48 @@ def post(self, request, course_id):
status_code, message = get_chat_response(prompt_template, message_list, course_id)

return Response(status=status_code, data=message)


class LearningAssistantEnabledView(APIView):
"""
View to retrieve whether the Learning Assistant is enabled for a course.
This endpoint returns a boolean representing whether the Learning Assistant feature is enabled in a course
represented by the course_key, which is provided in the URL.
Accepts: [GET]
Path: /learning_assistant/v1/course_id/{course_key}/enabled
Parameters:
* course_key: the ID of the course
Responses:
* 200: OK
* 400: Malformed Request - Course ID is not a valid course ID.
"""

authentication_classes = (SessionAuthentication, JwtAuthentication,)
permission_classes = (IsAuthenticated,)

def get(self, request, course_id):
"""
Given a course ID, retrieve whether the Learning Assistant is enabled for the corresponding course.
The response will be in the following format.
{'enabled': <bool>}
"""
try:
courserun_key = CourseKey.from_string(course_id)
except InvalidKeyError:
return Response(
status=http_status.HTTP_400_BAD_REQUEST,
data={'detail': 'Course ID is not a valid course ID.'}
)

data = {
'enabled': learning_assistant_enabled(courserun_key),
}

return Response(status=http_status.HTTP_200_OK, data=data)
43 changes: 43 additions & 0 deletions tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from importlib import import_module
from unittest.mock import MagicMock, patch

import ddt
from django.conf import settings
from django.contrib.auth import get_user_model, login
from django.http import HttpRequest
Expand Down Expand Up @@ -81,6 +82,13 @@ def setUp(self):
super().setUp()
self.course_id = 'course-v1:edx+test+23'

@patch('learning_assistant.views.learning_assistant_enabled')
def test_invalid_course_id(self, mock_learning_assistant_enabled):
mock_learning_assistant_enabled.return_value = True
response = self.client.get(reverse('enabled', kwargs={'course_id': self.course_id+'+invalid'}))

self.assertEqual(response.status_code, 400)

@patch('learning_assistant.views.learning_assistant_enabled')
def test_course_waffle_inactive(self, mock_waffle):
mock_waffle.return_value = False
Expand Down Expand Up @@ -164,3 +172,38 @@ def test_chat_response(self, mock_mode, mock_enrollment, mock_role, mock_waffle,

render_args = mock_render.call_args.args
self.assertIn(test_unit_id, render_args)


@ddt.ddt
class LearningAssistantEnabledViewTests(LoggedInTestCase):
"""
Test for the LearningAssistantEnabledView
"""
sys.modules['lms.djangoapps.courseware.access'] = MagicMock()
sys.modules['lms.djangoapps.courseware.toggles'] = MagicMock()
sys.modules['common.djangoapps.course_modes.models'] = MagicMock()
sys.modules['common.djangoapps.student.models'] = MagicMock()

def setUp(self):
super().setUp()
self.course_id = 'course-v1:edx+test+23'

@ddt.data(
(True, True),
(False, False),
)
@ddt.unpack
@patch('learning_assistant.views.learning_assistant_enabled')
def test_learning_assistant_enabled(self, mock_value, expected_value, mock_learning_assistant_enabled):
mock_learning_assistant_enabled.return_value = mock_value
response = self.client.get(reverse('enabled', kwargs={'course_id': self.course_id}))

self.assertEqual(response.status_code, 200)
self.assertEqual(response.data, {'enabled': expected_value})

@patch('learning_assistant.views.learning_assistant_enabled')
def test_invalid_course_id(self, mock_learning_assistant_enabled):
mock_learning_assistant_enabled.return_value = True
response = self.client.get(reverse('enabled', kwargs={'course_id': self.course_id+'+invalid'}))

self.assertEqual(response.status_code, 400)

0 comments on commit ab8b969

Please sign in to comment.