Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions nau_openedx_extensions/partner_integration/facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,7 +445,7 @@ def get_student_progress(self, course, student_id, nif, email, username, query_s
Fetches student progress based on the client's `query_security_scope` and request parameters.
"""
try:

logger.info("Executing student progress data extraction.")
base_security_scope = query_security_scope.get("base_security_scope")
courses_base_query = super().apply_base_security_scope(base_security_scope)
student = self._get_student_user(student_id, nif, email, username)
Expand All @@ -463,6 +463,7 @@ def get_student_progress(self, course, student_id, nif, email, username, query_s
if not course_to_extract:
raise PartnerIntegrationCourseOwnerException()

logger.info("Course to extract: %s", course_to_extract.id)
course_key = CourseKey.from_string(str(course_to_extract.id))
course_to_extract = get_course_or_403(student, 'load', course_key, check_if_enrolled=False)
collected_block_structure = get_block_structure_manager(course_key).get_collected()
Expand All @@ -484,7 +485,7 @@ def get_student_progress(self, course, student_id, nif, email, username, query_s
user_has_passing_grade = False
if not student.is_anonymous:
user_grade = course_grade.percent
user_has_passing_grade = user_grade >= course.lowest_passing_grade
user_has_passing_grade = user_grade >= course_grade.lowest_passing_grade

block = modulestore().get_course(course_key)
grading_policy = block.grading_policy
Expand Down
112 changes: 102 additions & 10 deletions nau_openedx_extensions/partner_integration/tests/test_api.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# tests/test_views_and_serializers.py
import base64
from datetime import datetime, timedelta
from unittest.mock import patch
from unittest.mock import MagicMock, patch

from common.djangoapps.student.tests.factories import CourseEnrollmentFactory
from django.test import TransactionTestCase, override_settings
from django.test import TransactionTestCase
from django.utils import timezone
from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFactory
from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory
Expand Down Expand Up @@ -1585,14 +1585,6 @@ def test_enroll_user_invalid_course_id_returns_400(self):
self.assertIn("The specified course ID does not exist or is not accessible by the partner.", str(response.data))


# TO DO: Create tests for the following view.
# This view must be tested using the Open Edx test apporach implemented. It is a complex view,
# which counts on other complex subsystems, e.g. CourseGradeFactory, get_course_blocks, etc.
# It also depends on modulestore and other Open edX internals. That is, the tests implementation
# would require a significant amount of setup and mocking to isolate the view's functionality, that
# is already implemented in the upstream subsystems. It is necessary to investigate the best approach
# to test this view properly, possibly involving integration tests or using a more comprehensive
# testing framework that can handle Open edX's complexity.
class TestStudentProgressRestExportView(TransactionTestCase, BaseStructure):
"""Tests for the StudentProgressRestExportView API endpoint."""
def setUp(self):
Expand All @@ -1618,3 +1610,103 @@ def authenticate_partner_client(self, partner_client):

assert response.status_code == 200, f"Auth failed: {response.data}"
return response.data["access_token"]

@patch("nau_openedx_extensions.partner_integration.facade.get_block_structure_manager")
@patch("nau_openedx_extensions.partner_integration.facade.get_course_blocks_completion_summary")
@patch("nau_openedx_extensions.partner_integration.facade.CourseGradeFactory")
@patch("nau_openedx_extensions.partner_integration.facade.modulestore")
@patch("nau_openedx_extensions.partner_integration.facade.get_course_or_403")
def test_student_progress_export_success(
self,
block_structure_manager_mock,
modulestore_mock,
course_grade_factory_mock,
completion_summary_mock,
get_course_or_403_mock
):
course_id = self.base_data["courses"][0].id
partner_client = self.base_data["partner_clients"][0]
user_ext = self.base_data["users"][0]
user = user_ext.user
get_course_or_403_mock.return_value = MagicMock(id=course_id)

mock_grade = MagicMock()
mock_grade.percent = 0.84
mock_grade.passed = True
mock_grade.lowest_passing_grade = 0.5
mock_grade.letter_grade = "Approved"

course_grade_factory_mock().read.return_value = mock_grade

mock_course = MagicMock()
grading_policy = {
"GRADER": [
{
"type": "Avaliação Módulo 1",
"min_count": 1,
"drop_count": 0,
"short_label": "AS1",
"weight": 0.33,
},
{
"type": "Avaliação Módulo 2",
"min_count": 1,
"drop_count": 0,
"short_label": "AS2",
"weight": 0.33,
},
{
"type": "Avaliação Módulo 3",
"min_count": 1,
"drop_count": 0,
"short_label": "AS3",
"weight": 0.34,
},
],
"GRADE_CUTOFFS": {
"Aprovado": 0.5
},
}
mock_course.grading_policy = grading_policy

modulestore_instance = MagicMock()
modulestore_instance.get_course.return_value = mock_course
modulestore_mock.return_value = modulestore_instance

completion_summary_mock.return_value = {
"complete_count": 28,
"incomplete_count": 51,
"locked_count": 0,
}

mock_block_manager = MagicMock()
mock_block_manager.get_collected.return_value = {}
block_structure_manager_mock.return_value = mock_block_manager

access_token = self.authenticate_partner_client(partner_client)
self.http_client.credentials(HTTP_AUTHORIZATION=f"Bearer {access_token}")
response = self.http_client.post(
self.endpoint,
data={
"course": str(course_id),
"email": user.email
},
format="json",
)

response_data = response.json()
self.assertEqual(response.status_code, status.HTTP_200_OK)

self.assertEqual(response_data["username"], user.username)
self.assertTrue(response_data["user_has_passing_grade"], "User should have passing grade")

expected_summary = {
"complete_count": 28,
"incomplete_count": 51,
"locked_count": 0
}

self.assertEqual(response_data["completion_summary"], expected_summary)
self.assertEqual(response_data["course_grade"]["percent"], 0.84)
self.assertEqual(response_data["course_grade"]["passed"], True)
self.assertEqual(response_data["course_grade"]["letter_grade"], "Approved")