Files
edx-platform/lms/djangoapps/instructor/views/serializer.py
Awais Qureshi 6680aecbbe Rescore problem to drf (#35627)
* feat!: upgrading api to DRF.
2025-04-15 15:24:05 +05:00

376 lines
12 KiB
Python

""" Instructor apis serializers. """
import re
from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user
from django.core.exceptions import ValidationError
from django.utils.translation import gettext as _
from rest_framework import serializers
from lms.djangoapps.certificates.models import CertificateStatuses
from lms.djangoapps.instructor.access import ROLES
from .tools import get_student_from_identifier
class RoleNameSerializer(serializers.Serializer): # pylint: disable=abstract-method
"""
Serializer that describes the response of the problem response report generation API.
"""
rolename = serializers.CharField(help_text=_("Role name"))
def validate_rolename(self, value):
"""
Check that the rolename is valid.
"""
if value not in ROLES:
raise ValidationError(_("Invalid role name."))
return value
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'email', 'first_name', 'last_name']
class UniqueStudentIdentifierSerializer(serializers.Serializer):
"""
Serializer for identifying unique_student.
"""
unique_student_identifier = serializers.CharField(
max_length=255,
help_text="Email or username of user to change access"
)
def validate_unique_student_identifier(self, value):
"""
Validate that the unique_student_identifier corresponds to an existing user.
"""
try:
user = get_student_from_identifier(value)
except User.DoesNotExist:
return None
return user
class AccessSerializer(UniqueStudentIdentifierSerializer):
"""
Serializer for managing user access changes.
This serializer validates and processes the data required to modify
user access within a system.
"""
rolename = serializers.CharField(
help_text="Role name to assign to the user"
)
action = serializers.ChoiceField(
choices=['allow', 'revoke'],
help_text="Action to perform on the user's access"
)
class ListInstructorTaskInputSerializer(serializers.Serializer): # pylint: disable=abstract-method
"""
Serializer for handling the input data for the problem response report generation API.
Attributes:
unique_student_identifier (str): The email or username of the student.
This field is optional, but if provided, the `problem_location_str`
must also be provided.
problem_location_str (str): The string representing the location of the problem within the course.
This field is optional, unless `unique_student_identifier` is provided.
"""
unique_student_identifier = serializers.CharField(
max_length=255,
help_text="Email or username of student",
required=False
)
problem_location_str = serializers.CharField(
help_text="Problem location",
required=False
)
def validate(self, data):
"""
Validate the data to ensure that if unique_student_identifier is provided,
problem_location_str must also be provided.
"""
unique_student_identifier = data.get('unique_student_identifier')
problem_location_str = data.get('problem_location_str')
if unique_student_identifier and not problem_location_str:
raise serializers.ValidationError(
"unique_student_identifier must accompany problem_location_str"
)
return data
class ShowStudentExtensionSerializer(serializers.Serializer):
"""
Serializer for validating and processing the student identifier.
"""
student = serializers.CharField(write_only=True, required=True)
def validate_student(self, value):
"""
Validate that the student corresponds to an existing user.
"""
try:
user = get_student_from_identifier(value)
except User.DoesNotExist:
return None
return user
class StudentAttemptsSerializer(serializers.Serializer):
"""
Serializer for resetting a students attempts counter or starts a task to reset all students
attempts counters.
"""
problem_to_reset = serializers.CharField(
help_text="The identifier or description of the problem that needs to be reset."
)
# following are optional params.
unique_student_identifier = serializers.CharField(
help_text="Email or username of student.", required=False
)
all_students = serializers.CharField(required=False)
delete_module = serializers.CharField(required=False)
def validate_all_students(self, value):
"""
converts the all_student params value to bool.
"""
return self.verify_bool(value)
def validate_delete_module(self, value):
"""
converts the all_student params value.
"""
return self.verify_bool(value)
def validate_unique_student_identifier(self, value):
"""
Validate that the student corresponds to an existing user.
"""
try:
user = get_student_from_identifier(value)
except User.DoesNotExist:
return None
return user
def verify_bool(self, value):
"""
Returns the value of the boolean parameter with the given
name in the POST request. Handles translation from string
values to boolean values.
"""
if value is not None:
return value in ['true', 'True', True]
return False
class SendEmailSerializer(serializers.Serializer):
"""
Serializer for sending an email with optional scheduling.
Fields:
send_to (str): The email address of the recipient. This field is required.
subject (str): The subject line of the email. This field is required.
message (str): The body of the email. This field is required.
schedule (str, optional):
An optional field to specify when the email should be sent.
If provided, this should be a string that can be parsed into a
datetime format or some other scheduling logic.
"""
send_to = serializers.CharField(write_only=True, required=True)
# set max length as per model field.
subject = serializers.CharField(max_length=128, write_only=True, required=True)
message = serializers.CharField(required=True)
schedule = serializers.CharField(required=False)
class BlockDueDateSerializer(serializers.Serializer):
"""
Serializer for handling block due date updates for a specific student.
Fields:
url (str): The URL related to the block that needs the due date update.
due_datetime (str): The new due date and time for the block.
student (str): The email or username of the student whose access is being modified.
reason (str): Reason why updating this.
"""
url = serializers.CharField()
due_datetime = serializers.CharField()
student = serializers.CharField(
max_length=255,
help_text="Email or username of user to change access"
)
reason = serializers.CharField(required=False)
def validate_student(self, value):
"""
Validate that the student corresponds to an existing user.
"""
try:
user = get_student_from_identifier(value)
except User.DoesNotExist:
return None
return user
def __init__(self, *args, **kwargs):
# Get context to check if `due_datetime` should be optional
disable_due_datetime = kwargs.get('context', {}).get('disable_due_datetime', False)
super().__init__(*args, **kwargs)
if disable_due_datetime:
self.fields['due_datetime'].required = False
class ProblemResetSerializer(UniqueStudentIdentifierSerializer):
"""
serializer for resetting problem.
"""
problem_to_reset = serializers.CharField(
help_text=_("The URL name of the problem to reset."),
error_messages={
'blank': _("Problem URL name cannot be blank."),
}
)
all_students = serializers.BooleanField(
default=False,
help_text=_("Whether to reset the problem for all students."),
)
only_if_higher = serializers.BooleanField(
default=False,
)
# Override the unique_student_identifier field to make it optional
unique_student_identifier = serializers.CharField(
required=False, # Make this field optional
allow_null=True,
help_text=_("unique student identifier.")
)
class ModifyAccessSerializer(serializers.Serializer):
"""
serializers for enroll or un-enroll users in beta testing program.
"""
identifiers = serializers.CharField(
help_text="A comma separated list of emails or usernames.",
required=True
)
action = serializers.ChoiceField(
choices=["add", "remove"],
help_text="Action to perform: add or remove.",
required=True
)
email_students = serializers.BooleanField(
default=False,
help_text="Boolean flag to indicate if students should be emailed."
)
auto_enroll = serializers.BooleanField(
default=False,
help_text="Boolean flag to indicate if the user should be auto-enrolled."
)
def validate_identifiers(self, value):
"""
Validate the 'identifiers' field which is now a list of strings.
"""
# Iterate over the list of identifiers and validate each one
validated_list = _split_input_list(value)
if not validated_list:
raise serializers.ValidationError("The identifiers list cannot be empty.")
return validated_list
def validate_email_students(self, value):
"""
handle string values like 'true' or 'false'.
"""
if isinstance(value, str):
return value.lower() == 'true'
return bool(value)
def validate_auto_enroll(self, value):
"""
handle string values like 'true' or 'false'.
"""
if isinstance(value, str):
return value.lower() == 'true'
return bool(value)
def _split_input_list(str_list):
"""
Separate out individual student email from the comma, or space separated string.
e.g.
in: "Lorem@ipsum.dolor, sit@amet.consectetur\nadipiscing@elit.Aenean\r convallis@at.lacus\r, ut@lacinia.Sed"
out: ['Lorem@ipsum.dolor', 'sit@amet.consectetur', 'adipiscing@elit.Aenean', 'convallis@at.lacus', 'ut@lacinia.Sed']
`str_list` is a string coming from an input text area
returns a list of separated values
"""
new_list = re.split(r'[,\s\n\r]+', str_list)
new_list = [s.strip() for s in new_list]
new_list = [s for s in new_list if s != '']
return new_list
class CertificateStatusesSerializer(serializers.Serializer):
"""
Serializer for validating and serializing certificate status inputs.
This serializer is used to ensure that the provided certificate statuses
conform to the predefined set of valid statuses defined in the
`CertificateStatuses` enumeration.
"""
certificate_statuses = serializers.ListField(
child=serializers.ChoiceField(choices=[
CertificateStatuses.downloadable,
CertificateStatuses.error,
CertificateStatuses.notpassing,
CertificateStatuses.audit_passing,
CertificateStatuses.audit_notpassing,
]),
allow_empty=False # Set to True if you want to allow empty lists
)
class CertificateSerializer(serializers.Serializer):
"""
Serializer for multiple operations related with certificates.
resetting a students attempts counter or starts a task to reset all students
attempts counters
Also Add/Remove students to/from the certificate allowlist.
Also For resetting a students attempts counter or starts a task to reset all students
attempts counters.
"""
user = serializers.CharField(
help_text="Email or username of student.", required=True
)
notes = serializers.CharField(required=False, allow_null=True, allow_blank=True)
def validate_user(self, value):
"""
Validate that the user corresponds to an existing user.
"""
try:
user = get_student_from_identifier(value)
except User.DoesNotExist:
return None
return user