feat: added support for new topics for mobile (#31441)

Co-authored-by: adeel.tajamul <adeel.tajamul@arbisoft.com>
This commit is contained in:
Muhammad Adeel Tajamul
2022-12-14 13:21:31 +05:00
committed by GitHub
parent 6ef0aba48d
commit e04d53a9a1
4 changed files with 175 additions and 5 deletions

View File

@@ -33,6 +33,7 @@ from common.djangoapps.student.roles import (
CourseStaffRole,
)
from lms.djangoapps.course_api.blocks.api import get_blocks
from lms.djangoapps.course_blocks.api import get_course_blocks
from lms.djangoapps.courseware.courses import get_course_with_access
from lms.djangoapps.courseware.exceptions import CourseAccessRedirect
@@ -116,13 +117,16 @@ from .serializers import (
get_context
)
from .utils import (
AttributeDict,
add_stats_for_users_with_no_discussion_content,
create_blocks_params,
discussion_open_for_user,
get_usernames_for_course,
get_usernames_from_search_string,
set_attribute
)
User = get_user_model()
ThreadType = Literal["discussion", "question"]
@@ -533,6 +537,108 @@ def get_course_topics(request: Request, course_key: CourseKey, topic_ids: Option
}
def get_v2_non_courseware_topics_as_v1(request, course_key, topics):
"""
Takes v2 topics list and returns v1 list of non courseware topics
"""
non_courseware_topics = []
for topic in topics:
if topic.get('usage_key', '') is None:
for key in ['usage_key', 'enabled_in_context']:
topic.pop(key)
topic.update({
'children': [],
'thread_list_url': get_thread_list_url(
request,
course_key,
topic.get('id'),
)
})
non_courseware_topics.append(topic)
return non_courseware_topics
def get_v2_courseware_topics_as_v1(request, course_key, sequentials, topics):
"""
Returns v2 courseware topics list as v1 structure
"""
courseware_topics = []
for sequential in sequentials:
children = []
for child in sequential.get('children', []):
for topic in topics:
if child == topic.get('usage_key'):
topic.update({
'children': [],
'thread_list_url': get_thread_list_url(
request,
course_key,
[topic.get('id')],
)
})
topic.pop('enabled_in_context')
children.append(AttributeDict(topic))
discussion_topic = DiscussionTopic(
None,
sequential.get('display_name'),
get_thread_list_url(
request,
course_key,
[child.id for child in children],
),
children,
None,
)
courseware_topics.append(DiscussionTopicSerializer(discussion_topic).data)
return courseware_topics
def get_v2_course_topics_as_v1(
request: Request,
course_key: CourseKey,
topic_ids: Optional[Iterable[str]] = None,
):
"""
Returns v2 topics in v1 structure
"""
course_usage_key = modulestore().make_course_usage_key(course_key)
blocks_params = create_blocks_params(course_usage_key, request.user)
blocks = get_blocks(
request,
blocks_params['usage_key'],
blocks_params['user'],
blocks_params['depth'],
blocks_params['nav_depth'],
blocks_params['requested_fields'],
blocks_params['block_counts'],
blocks_params['student_view_data'],
blocks_params['return_type'],
blocks_params['block_types_filter'],
hide_access_denials=False,
)['blocks']
sequentials = [value for _, value in blocks.items()
if value.get('type') == "sequential"]
topics = get_course_topics_v2(course_key, request.user, topic_ids)
non_courseware_topics = get_v2_non_courseware_topics_as_v1(
request,
course_key,
topics,
)
courseware_topics = get_v2_courseware_topics_as_v1(
request,
course_key,
sequentials,
topics,
)
return {
"courseware_topics": courseware_topics,
"non_courseware_topics": non_courseware_topics,
}
def get_course_topics_v2(
course_key: CourseKey,
user: User,

View File

@@ -876,6 +876,47 @@ class CourseTopicsViewTest(DiscussionAPIViewTestMixin, CommentsServiceMockMixin,
}
)
@override_waffle_flag(ENABLE_NEW_STRUCTURE_DISCUSSIONS, True)
def test_new_course_structure_response(self):
"""
Tests whether the new structure is available on old topics API
(For mobile compatibility)
"""
chapter = ItemFactory.create(
parent_location=self.course.location,
category='chapter',
display_name="Week 1",
start=datetime(2015, 3, 1, tzinfo=UTC),
)
sequential = ItemFactory.create(
parent_location=chapter.location,
category='sequential',
display_name="Lesson 1",
start=datetime(2015, 3, 1, tzinfo=UTC),
)
ItemFactory.create(
parent_location=sequential.location,
category='vertical',
display_name='vertical',
start=datetime(2015, 4, 1, tzinfo=UTC),
)
DiscussionsConfiguration.objects.create(
context_key=self.course.id,
provider_type=Provider.OPEN_EDX
)
update_discussions_settings_from_course_task(str(self.course.id))
response = json.loads(self.client.get(self.url).content.decode())
keys = ['children', 'id', 'name', 'thread_counts', 'thread_list_url']
assert list(response.keys()) == ['courseware_topics', 'non_courseware_topics']
assert len(response['courseware_topics']) == 1
courseware_keys = list(response['courseware_topics'][0].keys())
courseware_keys.sort()
assert courseware_keys == keys
assert len(response['non_courseware_topics']) == 1
non_courseware_keys = list(response['non_courseware_topics'][0].keys())
non_courseware_keys.sort()
assert non_courseware_keys == keys
@ddt.ddt
@mock.patch('lms.djangoapps.discussion.rest_api.api._get_course', mock.Mock())

View File

@@ -17,6 +17,15 @@ from openedx.core.djangoapps.django_comment_common.models import (
)
class AttributeDict(dict):
"""
Converts Dict Keys into Attributes
"""
__getattr__ = dict.__getitem__
__setattr__ = dict.__setitem__
__delattr__ = dict.__delitem__
def discussion_open_for_user(course, user):
"""
Check if course discussion are open or not for user.

View File

@@ -28,6 +28,8 @@ from lms.djangoapps.course_goals.models import UserActivity
from lms.djangoapps.discussion.django_comment_client import settings as cc_settings
from lms.djangoapps.discussion.django_comment_client.utils import get_group_id_for_comments_service
from lms.djangoapps.instructor.access import update_forum_role
from openedx.core.djangoapps.discussions.config.waffle import ENABLE_NEW_STRUCTURE_DISCUSSIONS
from openedx.core.djangoapps.discussions.models import DiscussionsConfiguration, Provider
from openedx.core.djangoapps.discussions.serializers import DiscussionSettingsSerializer
from openedx.core.djangoapps.django_comment_common import comment_client
from openedx.core.djangoapps.django_comment_common.models import CourseDiscussionSettings, Role
@@ -52,6 +54,7 @@ from ..rest_api.api import (
get_thread_list,
get_learner_active_thread_list,
get_user_comments,
get_v2_course_topics_as_v1,
update_comment,
update_thread,
)
@@ -227,11 +230,22 @@ class CourseTopicsView(DeveloperErrorViewMixin, APIView):
topic_ids = self.request.GET.get('topic_id')
topic_ids = set(topic_ids.strip(',').split(',')) if topic_ids else None
with modulestore().bulk_operations(course_key):
response = get_course_topics(
request,
course_key,
topic_ids,
)
configuration = DiscussionsConfiguration.get(context_key=course_key)
provider = configuration.provider_type
# This will be removed when mobile app will support new topic structure
new_structure_enabled = ENABLE_NEW_STRUCTURE_DISCUSSIONS.is_enabled(course_key)
if provider == Provider.OPEN_EDX and new_structure_enabled:
response = get_v2_course_topics_as_v1(
request,
course_key,
topic_ids
)
else:
response = get_course_topics(
request,
course_key,
topic_ids,
)
# Record user activity for tracking progress towards a user's course goals (for mobile app)
UserActivity.record_user_activity(request.user, course_key, request=request, only_if_mobile_app=True)
return Response(response)