diff --git a/lms/djangoapps/mobile_api/download_courses/views.py b/lms/djangoapps/mobile_api/download_courses/views.py index 1ecc1d6d321b..0310f17d6a6a 100644 --- a/lms/djangoapps/mobile_api/download_courses/views.py +++ b/lms/djangoapps/mobile_api/download_courses/views.py @@ -2,12 +2,10 @@ Views for the download courses API. """ -from django.conf import settings from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user from django.shortcuts import get_object_or_404 from rest_framework import views from rest_framework.response import Response -from urllib.parse import urljoin from common.djangoapps.student.models import CourseEnrollment, User # lint-amnesty, pylint: disable=reimported from lms.djangoapps.courseware.access import is_mobile_available_for_user @@ -74,18 +72,19 @@ def get(self, request, *args, **kwargs) -> Response: for enrollment in user_enrollments if is_mobile_available_for_user(user, enrollment.course_overview) ] - response_data = [] - - for user_enrollment in mobile_available: - course_id = user_enrollment.course_overview.id - offline_course_size = OfflineCourseSize.objects.filter(course_id=course_id).first() - response_data.append( - { - "course_id": str(course_id), - "course_name": user_enrollment.course_overview.display_name, - "course_image": user_enrollment.course_overview.course_image_url, - "total_size": getattr(offline_course_size, "size", 0), - } - ) + course_ids = [enrollment.course_overview.id for enrollment in mobile_available] + offline_course_sizes = OfflineCourseSize.objects.filter(course_id__in=course_ids, size__gt=0) + size_map = {size.course_id: size.size for size in offline_course_sizes} + + response_data = [ + { + "course_id": str(user_enrollment.course_overview.id), + "course_name": user_enrollment.course_overview.display_name, + "course_image": user_enrollment.course_overview.course_image_url, + "total_size": size_map.get(user_enrollment.course_overview.id) + } + for user_enrollment in mobile_available + if user_enrollment.course_overview.id in size_map + ] return Response(response_data) diff --git a/lms/djangoapps/mobile_api/tests/test_download_courses_views.py b/lms/djangoapps/mobile_api/tests/test_download_courses_views.py new file mode 100644 index 000000000000..4f33cc689ae8 --- /dev/null +++ b/lms/djangoapps/mobile_api/tests/test_download_courses_views.py @@ -0,0 +1,48 @@ +""" +Tests for download courses API view. +""" + +from unittest.mock import patch + +from django.urls import reverse +from rest_framework import status + +from common.djangoapps.student.tests.factories import CourseEnrollmentFactory +from lms.djangoapps.mobile_api.testutils import MobileAPITestCase +from openedx.features.offline_mode.models import OfflineCourseSize + + +class DownloadCoursesAPIViewTest(MobileAPITestCase): + """ + Download courses API view tests. + """ + + def setUp(self): + super().setUp() + self.enrollment = CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id, is_active=True) + self.url = reverse("download-courses", kwargs={"api_version": "v1", "username": self.user.username}) + self.login_and_enroll() + + @patch("lms.djangoapps.mobile_api.download_courses.views.is_mobile_available_for_user", return_value=True) + def test_get_download_courses_success(self, mock_mobile_available): + """ + Test that the API returns the expected response. + """ + OfflineCourseSize.objects.create(course_id=self.course.id, size=123456) + + response = self.client.get(self.url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data), 1) + self.assertEqual(response.data[0]["course_id"], str(self.course.id)) + self.assertEqual(response.data[0]["course_name"], self.course.display_name) + self.assertEqual(response.data[0]["course_image"], self.enrollment.course_overview.course_image_url) + self.assertEqual(response.data[0]["total_size"], 123456) + + @patch("lms.djangoapps.mobile_api.download_courses.views.is_mobile_available_for_user", return_value=True) + def test_excludes_courses_with_no_offline_content(self, mock_mobile_available): + """ + Test that courses with no offline content are not returned in the API response. + """ + response = self.client.get(self.url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data), 0)