Skip to content
Open
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
63 changes: 63 additions & 0 deletions lms/djangoapps/grades/rest_api/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""
Helpers shared by the versions of the Grades REST API.

Grades v1 keeps its own copies of these, so that the deprecated version stays
unchanged until it is removed; removing it then leaves the newer versions
untouched.
"""

from django.conf import settings
from django.http import Http404

from common.djangoapps.student.roles import CourseCcxCoachRole, CourseInstructorRole, CourseStaffRole
from lms.djangoapps.ccx.permissions import VIEW_CCX_COACH_DASHBOARD
from openedx.core.lib.courses import get_course_by_id


def has_ccx_gradebook_access(user, course_key):
"""
Return True if ``user`` may use the gradebook of the CCX course ``course_key``.

Applies the same platform-level and course-level guards as
``lms.djangoapps.ccx.api.v2.permissions.IsCCXCoach``:

* ``CUSTOM_COURSES_EDX`` must be enabled.
* The master course must have ``enable_ccx = True``.
* The user must either have the staff permission on the master course
(``VIEW_CCX_COACH_DASHBOARD``), hold the CCX coach role on it, or be site
staff or hold a staff or instructor role on the CCX itself (which
``create_ccx_course`` grants to the coach).
"""
if not settings.CUSTOM_COURSES_EDX:
return False
master_course_key = course_key.to_course_locator()
try:
master_course = get_course_by_id(master_course_key)
except Http404:
return False
if not master_course.enable_ccx:
return False
return (
user.is_staff
or user.has_perm(VIEW_CCX_COACH_DASHBOARD, master_course)
or CourseCcxCoachRole(master_course_key).has_user(user)
or CourseStaffRole(course_key).has_user(user)
or CourseInstructorRole(course_key).has_user(user)
)


def get_problem_blocks(course):
"""
Return the scored problem blocks of ``course``, in course order.

Only problems placed directly in a unit are found: problems inside legacy
library content blocks or content libraries are not included.
"""
blocks = []
for section in course.get_children():
for subsection in section.get_children():
for vertical in subsection.get_children():
for block in vertical.get_children():
if block.category == 'problem' and getattr(block, 'has_score', False):
blocks.append(block)
return blocks
34 changes: 34 additions & 0 deletions lms/djangoapps/grades/rest_api/v1/gradebook_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,14 @@ class CourseGradingView(BaseCourseView):
* short_label - A short label for graded assignments (e.g. 'HW 01').
* assignment_type - The assignment type of this subsection (for graded assignments only).


**Deprecated**

Superseded by:

* ``GET /api/grade/v2/courses/{course_key}/grading_policy/``

This version is deprecated and will be removed; see https://github.com/openedx/openedx-platform/issues/DEPR_TBD
"""
@course_author_access_required
def get(self, request, course_key):
Expand Down Expand Up @@ -482,6 +490,15 @@ class GradebookView(GradeViewMixin, PaginatedAPIView):

Note: It's important that `GradeViewMixin` is the first inherited class here, so that
self.api_error returns error responses as expected.

**Deprecated**

Superseded by:

* ``GET /api/grade/v2/courses/{course_key}/gradebook_entries/``
* ``GET /api/grade/v2/courses/{course_key}/gradebook_entries/{username}/``

This version is deprecated and will be removed; see https://github.com/openedx/openedx-platform/issues/DEPR_TBD
"""

pagination_class = CourseEnrollmentPagination
Expand Down Expand Up @@ -860,6 +877,14 @@ class GradebookBulkUpdateView(GradeViewMixin, PaginatedAPIView):
"reason": "User matching query does not exist."
}
]

**Deprecated**

Superseded by:

* ``POST /api/grade/v2/courses/{course_key}/subsection_grade_overrides/``

This version is deprecated and will be removed; see https://github.com/openedx/openedx-platform/issues/DEPR_TBD
"""

@verify_course_exists("Requested grade for unknown course {course}")
Expand Down Expand Up @@ -1092,6 +1117,15 @@ class SubsectionGradeView(GradeViewMixin, APIView):
}
]
}

**Deprecated**

Superseded by:

* ``GET /api/grade/v2/subsection_grades/{username},{usage_key}/``
* ``GET /api/grade/v2/subsection_grades/{username},{usage_key}/override_history_records/``

This version is deprecated and will be removed; see https://github.com/openedx/openedx-platform/issues/DEPR_TBD
"""

def get(self, request, subsection_id):
Expand Down
33 changes: 33 additions & 0 deletions lms/djangoapps/grades/rest_api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,15 @@ class CourseGradesView(GradeViewMixin, PaginatedAPIView):
"percent": 0.19,
"letter_grade": null,
}]

**Deprecated**

Superseded by:

* ``GET /api/grade/v2/course_grades/?course_key={course_key}``
* ``GET /api/grade/v2/course_grades/{username},{course_key}/``

This version is deprecated and will be removed; see https://github.com/openedx/openedx-platform/issues/DEPR_TBD
"""
authentication_classes = (
JwtAuthentication,
Expand Down Expand Up @@ -180,6 +189,14 @@ class CourseGradingPolicy(GradeViewMixin, ListAPIView):

* weight: The weight, or effect, of the assignment type on the learner's
final grade.

**Deprecated**

Superseded by:

* ``GET /api/grade/v2/courses/{course_key}/grading_policy/?view=minimal``

This version is deprecated and will be removed; see https://github.com/openedx/openedx-platform/issues/DEPR_TBD
"""
allow_empty = False

Expand Down Expand Up @@ -226,6 +243,14 @@ def get(self, request, course_id, *args, **kwargs): # pylint: disable=arguments
class SectionGradesBreakdown(GradeViewMixin, PaginatedAPIView):
""" Section grades breakdown gives out the overall grade for a user in a course
accompanied by grades for each section of the course for the user.

**Deprecated**

Superseded by:

* ``GET /api/grade/v2/course_grades/?view=full``

This version is deprecated and will be removed; see https://github.com/openedx/openedx-platform/issues/DEPR_TBD
"""
authentication_classes = (
JwtAuthentication,
Expand Down Expand Up @@ -365,6 +390,14 @@ def _serialize_section_grades(user, course_key, course_grade):
class SubmissionHistoryView(GradeViewMixin, PaginatedAPIView):
"""
Submission history corresponding to ProblemBlocks present in the course.

**Deprecated**

Superseded by:

* ``GET /api/grade/v2/courses/{course_key}/submission_histories/``

This version is deprecated and will be removed; see https://github.com/openedx/openedx-platform/issues/DEPR_TBD
"""
authentication_classes = (
JwtAuthentication,
Expand Down
1 change: 1 addition & 0 deletions lms/djangoapps/grades/rest_api/v2/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Grades API v2; `/api/grades/v1/` is unchanged and deprecated."""
34 changes: 34 additions & 0 deletions lms/djangoapps/grades/rest_api/v2/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""Error types the Grades API v2 can return."""

from edx_rest_framework_extensions.errors import register_error_type
from rest_framework.exceptions import NotFound, PermissionDenied


class WritableGradebookDisabled(PermissionDenied):
"""The writable gradebook is switched off for the course being addressed."""

default_detail = 'The writable gradebook is not enabled for this course.'
default_code = 'writable_gradebook_disabled'


class GradesFrozen(PermissionDenied):
"""The course ended long enough ago that its grades may no longer be changed."""

default_detail = 'Grades are frozen for this course.'
default_code = 'grades_frozen'


class SubsectionUnavailable(NotFound):
"""The subsection addressed is not available to the learner addressed."""

default_detail = 'The subsection is not available to this learner.'
default_code = 'subsection_unavailable'


register_error_type(
WritableGradebookDisabled,
'grades/writable-gradebook-disabled',
'Writable Gradebook Disabled',
)
register_error_type(GradesFrozen, 'grades/grades-frozen', 'Grades Frozen')
register_error_type(SubsectionUnavailable, 'grades/subsection-unavailable', 'Subsection Unavailable')
Loading
Loading