feat: grading rest api for authoring api (#34854)
* feat: grading rest api for authoring api * fix: doc string api path
This commit is contained in:
@@ -3,6 +3,7 @@ Serializers for v0 contentstore API.
|
||||
"""
|
||||
from .advanced_settings import AdvancedSettingsFieldSerializer, CourseAdvancedSettingsSerializer
|
||||
from .assets import AssetSerializer
|
||||
from .authoring_grading import CourseGradingModelSerializer
|
||||
from .tabs import CourseTabSerializer, CourseTabUpdateSerializer, TabIDLocatorSerializer
|
||||
from .transcripts import TranscriptSerializer, YoutubeTranscriptCheckSerializer, YoutubeTranscriptUploadSerializer
|
||||
from .xblock import XblockSerializer
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
"""
|
||||
API Serializers for course grading
|
||||
"""
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
|
||||
class GradersSerializer(serializers.Serializer):
|
||||
""" Serializer for graders """
|
||||
type = serializers.CharField()
|
||||
min_count = serializers.IntegerField()
|
||||
drop_count = serializers.IntegerField()
|
||||
short_label = serializers.CharField(required=False, allow_null=True, allow_blank=True)
|
||||
weight = serializers.IntegerField()
|
||||
id = serializers.IntegerField()
|
||||
|
||||
|
||||
class CourseGradingModelSerializer(serializers.Serializer):
|
||||
""" Serializer for course grading model data """
|
||||
graders = GradersSerializer(many=True, allow_null=True, allow_empty=True)
|
||||
@@ -7,6 +7,7 @@ from openedx.core.constants import COURSE_ID_PATTERN
|
||||
|
||||
from .views import (
|
||||
AdvancedCourseSettingsView,
|
||||
AuthoringGradingView,
|
||||
CourseTabSettingsView,
|
||||
CourseTabListView,
|
||||
CourseTabReorderView,
|
||||
@@ -46,8 +47,8 @@ urlpatterns = [
|
||||
),
|
||||
|
||||
# Authoring API
|
||||
re_path(
|
||||
r'^heartbeat$', APIHeartBeatView.as_view(), name='heartbeat'
|
||||
path(
|
||||
'heartbeat', APIHeartBeatView.as_view(), name='heartbeat'
|
||||
),
|
||||
re_path(
|
||||
fr'^file_assets/{settings.COURSE_ID_PATTERN}$',
|
||||
@@ -61,6 +62,10 @@ urlpatterns = [
|
||||
fr'^videos/encodings/{settings.COURSE_ID_PATTERN}$',
|
||||
authoring_videos.VideoEncodingsDownloadView.as_view(), name='cms_api_videos_encodings'
|
||||
),
|
||||
re_path(
|
||||
fr'grading/{settings.COURSE_ID_PATTERN}',
|
||||
AuthoringGradingView.as_view(), name='cms_api_update_grading'
|
||||
),
|
||||
path(
|
||||
'videos/features',
|
||||
authoring_videos.VideoFeaturesView.as_view(), name='cms_api_videos_features'
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
Views for v0 contentstore API.
|
||||
"""
|
||||
from .advanced_settings import AdvancedCourseSettingsView
|
||||
from .authoring_grading import AuthoringGradingView
|
||||
from .tabs import CourseTabSettingsView, CourseTabListView, CourseTabReorderView
|
||||
from .transcripts import TranscriptView, YoutubeTranscriptCheckView, YoutubeTranscriptUploadView
|
||||
from .api_heartbeat import APIHeartBeatView
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
""" API Views for course advanced settings """
|
||||
|
||||
import edx_api_doc_tools as apidocs
|
||||
from opaque_keys.edx.keys import CourseKey
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from cms.djangoapps.models.settings.course_grading import CourseGradingModel
|
||||
from common.djangoapps.student.auth import has_studio_read_access
|
||||
from openedx.core.djangoapps.credit.tasks import update_credit_course_requirements
|
||||
from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin, verify_course_exists, view_auth_classes
|
||||
from ..serializers import CourseGradingModelSerializer
|
||||
|
||||
|
||||
@view_auth_classes(is_authenticated=True)
|
||||
class AuthoringGradingView(DeveloperErrorViewMixin, APIView):
|
||||
"""
|
||||
View for getting and setting the advanced settings for a course.
|
||||
"""
|
||||
@apidocs.schema(
|
||||
body=CourseGradingModelSerializer,
|
||||
parameters=[
|
||||
apidocs.string_parameter("course_id", apidocs.ParameterLocation.PATH, description="Course ID"),
|
||||
],
|
||||
responses={
|
||||
200: CourseGradingModelSerializer,
|
||||
401: "The requester is not authenticated.",
|
||||
403: "The requester cannot access the specified course.",
|
||||
404: "The requested course does not exist.",
|
||||
},
|
||||
)
|
||||
@verify_course_exists()
|
||||
def post(self, request: Request, course_id: str):
|
||||
"""
|
||||
Update a course's grading.
|
||||
|
||||
**Example Request**
|
||||
|
||||
POST /api/contentstore/v0/course_grading/{course_id}
|
||||
|
||||
**POST Parameters**
|
||||
|
||||
The data sent for a post request should follow next object.
|
||||
Here is an example request data that updates the ``course_grading``
|
||||
|
||||
```json
|
||||
{
|
||||
"graders": [
|
||||
{
|
||||
"type": "Homework",
|
||||
"min_count": 1,
|
||||
"drop_count": 0,
|
||||
"short_label": "",
|
||||
"weight": 100,
|
||||
"id": 0
|
||||
}
|
||||
],
|
||||
"grade_cutoffs": {
|
||||
"A": 0.75,
|
||||
"B": 0.63,
|
||||
"C": 0.57,
|
||||
"D": 0.5
|
||||
},
|
||||
"grace_period": {
|
||||
"hours": 12,
|
||||
"minutes": 0
|
||||
},
|
||||
"minimum_grade_credit": 0.7,
|
||||
"is_credit_course": true
|
||||
}
|
||||
```
|
||||
|
||||
**Response Values**
|
||||
|
||||
If the request is successful, an HTTP 200 "OK" response is returned,
|
||||
"""
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
|
||||
if not has_studio_read_access(request.user, course_key):
|
||||
self.permission_denied(request)
|
||||
|
||||
if 'minimum_grade_credit' in request.data:
|
||||
update_credit_course_requirements.delay(str(course_key))
|
||||
|
||||
updated_data = CourseGradingModel.update_from_json(course_key, request.data, request.user)
|
||||
serializer = CourseGradingModelSerializer(updated_data)
|
||||
return Response(serializer.data)
|
||||
Reference in New Issue
Block a user