diff --git a/lms/djangoapps/instructor/docs/references/instructor-v2-ora-api-spec.yaml b/lms/djangoapps/instructor/docs/references/instructor-v2-ora-api-spec.yaml index f88c4fe4b933..c73d1e6ed244 100644 --- a/lms/djangoapps/instructor/docs/references/instructor-v2-ora-api-spec.yaml +++ b/lms/djangoapps/instructor/docs/references/instructor-v2-ora-api-spec.yaml @@ -220,6 +220,7 @@ definitions: - waiting - staff - final_grade_received + - staff_ora_grading_url properties: block_id: type: string @@ -268,7 +269,11 @@ definitions: minimum: 0 description: Responses with final grade assigned example: 20 - + staff_ora_grading_url: + type: string + format: uri + description: URL to the staff grading interface for this ORA assessment + example: "http://apps.local.openedx.io:1993/ora-grading/block-v1:WGU+CS002+2025_T1+type@openassessment+block@ff4f5fddf42d4b9787e69c1a8cbeb058" Error: type: object description: Error response diff --git a/lms/djangoapps/instructor/ora.py b/lms/djangoapps/instructor/ora.py index 5a78a1503443..6198aa4c3b64 100644 --- a/lms/djangoapps/instructor/ora.py +++ b/lms/djangoapps/instructor/ora.py @@ -1,6 +1,8 @@ """Utilities for retrieving Open Response Assessments (ORAs) data for instructor dashboards.""" from django.utils.translation import gettext as _ +from django.conf import settings + from openassessment.data import OraAggregateData from xmodule.modulestore.django import modulestore # lint-amnesty, pylint: disable=wrong-import-order @@ -37,6 +39,7 @@ def get_open_response_assessment_list(course): parents_cache = {} ora_items = [] + ora_grading_base_url = getattr(settings, 'ORA_GRADING_MICROFRONTEND_URL', None) for block in openassessment_blocks: block_id = str(block.location) @@ -54,10 +57,28 @@ def get_open_response_assessment_list(course): else block.display_name ) + staff_ora_grading_url = None + + has_staff_assessment = 'staff-assessment' in block.assessment_steps + is_team_enabled = block.teams_enabled + if ora_grading_base_url and has_staff_assessment and not is_team_enabled: + # Always generate a URL that points to the ORA Grading Microfrontend (MFE). + # + # During the migration to the ORA microfrontend, + # only provide the grading URL for non-team assignments with staff assessment. + # This logic was based on the original implementation in instructor_dashboard: + # - lms/djangoapps/instructor/views/instructor_dashboard.py + # (_section_open_response_assessment) + # - edx-ora2: + # https://github.com/openedx/edx-ora2/blob/801fbd14ebb059ab8c5ee8d5a39c260c7f87ab81/ + # openassessment/xblock/static/js/src/lms/oa_course_items_listing.js#L73 + staff_ora_grading_url = f"{ora_grading_base_url}/{block_id}" + ora_assessment_data = { 'id': block_id, 'name': assessment_name, 'parent_name': parent_block.display_name, + 'staff_ora_grading_url': staff_ora_grading_url, **DEFAULT_ORA_METRICS, } diff --git a/lms/djangoapps/instructor/tests/test_api_v2.py b/lms/djangoapps/instructor/tests/test_api_v2.py index 3821ac0e05aa..2d2d1889b45e 100644 --- a/lms/djangoapps/instructor/tests/test_api_v2.py +++ b/lms/djangoapps/instructor/tests/test_api_v2.py @@ -958,6 +958,72 @@ def test_get_assessment_list(self): assert ora_data['waiting'] == 0 assert ora_data['staff'] == 0 assert ora_data['final_grade_received'] == 0 + assert ora_data['staff_ora_grading_url'] is None + + @patch("lms.djangoapps.instructor.ora.modulestore") + def test_get_assessment_list_includes_staff_ora_grading_url_for_non_team_assignment( + self, mock_modulestore + ): + """ + Retrieve ORA assessments and ensure staff grading URL is included + for non-team assignments with staff assessment enabled. + """ + mock_store = Mock() + + mock_assessment_block = Mock( + location=self.ora_block.location, + parent=Mock(), + teams_enabled=False, + assessment_steps=["staff-assessment"], + ) + + mock_store.get_items.return_value = [mock_assessment_block] + mock_modulestore.return_value = mock_store + + response = self.client.get(self._get_url()) + + assert response.status_code == 200 + + results = response.data["results"] + assert len(results) == 1 + + ora_data = results[0] + + assert "staff_ora_grading_url" in ora_data + assert ora_data["staff_ora_grading_url"] + + @patch("lms.djangoapps.instructor.ora.modulestore") + def test_get_assessment_list_includes_staff_ora_grading_url_for_team_assignment( + self, mock_modulestore + ): + """ + Retrieve ORA assessments and ensure staff grading URL is included + for team assignments with staff assessment enabled. + """ + mock_store = Mock() + + mock_assessment_block = Mock( + location=self.ora_block.location, + parent=Mock(), + teams_enabled=True, + display_name="Team Assignment", + assessment_steps=["staff-assessment"], + ) + + mock_store.get_items.return_value = [mock_assessment_block] + mock_modulestore.return_value = mock_store + + response = self.client.get(self._get_url()) + + assert response.status_code == 200 + + results = response.data["results"] + assert len(results) == 1 + + ora_data = results[0] + + assert "staff_ora_grading_url" in ora_data + assert ora_data["staff_ora_grading_url"] is None def test_invalid_course_id(self): """Test error handling for invalid course ID.""" diff --git a/lms/djangoapps/instructor/views/serializers_v2.py b/lms/djangoapps/instructor/views/serializers_v2.py index 414079007291..c83c42d43b6e 100644 --- a/lms/djangoapps/instructor/views/serializers_v2.py +++ b/lms/djangoapps/instructor/views/serializers_v2.py @@ -433,6 +433,7 @@ class ORASerializer(serializers.Serializer): waiting = serializers.IntegerField() staff = serializers.IntegerField() final_grade_received = serializers.IntegerField(source="done") + staff_ora_grading_url = serializers.URLField(allow_null=True) class ORASummarySerializer(serializers.Serializer):