project initialization
Some checks failed
System Monitoring / Health Checks (push) Has been cancelled
System Monitoring / Performance Monitoring (push) Has been cancelled
System Monitoring / Database Monitoring (push) Has been cancelled
System Monitoring / Cache Monitoring (push) Has been cancelled
System Monitoring / Log Monitoring (push) Has been cancelled
System Monitoring / Resource Monitoring (push) Has been cancelled
System Monitoring / Uptime Monitoring (push) Has been cancelled
System Monitoring / Backup Monitoring (push) Has been cancelled
System Monitoring / Security Monitoring (push) Has been cancelled
System Monitoring / Monitoring Dashboard (push) Has been cancelled
System Monitoring / Alerting (push) Has been cancelled
Security Scanning / Dependency Scanning (push) Has been cancelled
Security Scanning / Code Security Scanning (push) Has been cancelled
Security Scanning / Secrets Scanning (push) Has been cancelled
Security Scanning / Container Security Scanning (push) Has been cancelled
Security Scanning / Compliance Checking (push) Has been cancelled
Security Scanning / Security Dashboard (push) Has been cancelled
Security Scanning / Security Remediation (push) Has been cancelled
Some checks failed
System Monitoring / Health Checks (push) Has been cancelled
System Monitoring / Performance Monitoring (push) Has been cancelled
System Monitoring / Database Monitoring (push) Has been cancelled
System Monitoring / Cache Monitoring (push) Has been cancelled
System Monitoring / Log Monitoring (push) Has been cancelled
System Monitoring / Resource Monitoring (push) Has been cancelled
System Monitoring / Uptime Monitoring (push) Has been cancelled
System Monitoring / Backup Monitoring (push) Has been cancelled
System Monitoring / Security Monitoring (push) Has been cancelled
System Monitoring / Monitoring Dashboard (push) Has been cancelled
System Monitoring / Alerting (push) Has been cancelled
Security Scanning / Dependency Scanning (push) Has been cancelled
Security Scanning / Code Security Scanning (push) Has been cancelled
Security Scanning / Secrets Scanning (push) Has been cancelled
Security Scanning / Container Security Scanning (push) Has been cancelled
Security Scanning / Compliance Checking (push) Has been cancelled
Security Scanning / Security Dashboard (push) Has been cancelled
Security Scanning / Security Remediation (push) Has been cancelled
This commit is contained in:
327
backend/src/modules/education/serializers/class_serializers.py
Normal file
327
backend/src/modules/education/serializers/class_serializers.py
Normal file
@@ -0,0 +1,327 @@
|
||||
"""
|
||||
Class Serializers
|
||||
Serializers for class-related models in the education module
|
||||
"""
|
||||
|
||||
from rest_framework import serializers
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.utils import timezone
|
||||
|
||||
from ..models.class import Class
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class ClassSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for Class model"""
|
||||
|
||||
display_name = serializers.SerializerMethodField()
|
||||
available_seats = serializers.SerializerMethodField()
|
||||
enrollment_percentage = serializers.SerializerMethodField()
|
||||
is_enrollment_period = serializers.SerializerMethodField()
|
||||
is_active_currently = serializers.SerializerMethodField()
|
||||
duration_in_days = serializers.SerializerMethodField()
|
||||
schedule_summary = serializers.SerializerMethodField()
|
||||
capacity_stats = serializers.SerializerMethodField()
|
||||
class_teacher_name = serializers.CharField(source='class_teacher.name', read_only=True)
|
||||
assistant_teacher_name = serializers.CharField(source='assistant_teacher.name', read_only=True)
|
||||
grade_level_display = serializers.CharField(source='get_grade_level_display', read_only=True)
|
||||
stream_display = serializers.CharField(source='get_stream_display', read_only=True)
|
||||
status_display = serializers.CharField(source='get_status_display', read_only=True)
|
||||
shift_display = serializers.CharField(source='get_shift_display', read_only=True)
|
||||
medium_display = serializers.CharField(source='get_medium_display', read_only=True)
|
||||
created_by_name = serializers.CharField(source='created_by.name', read_only=True)
|
||||
updated_by_name = serializers.CharField(source='updated_by.name', read_only=True)
|
||||
subject_teachers_info = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Class
|
||||
fields = [
|
||||
'id', 'tenant', 'name', 'class_code', 'grade_level', 'grade_level_display',
|
||||
'stream', 'stream_display', 'shift', 'shift_display', 'medium', 'medium_display',
|
||||
'max_students', 'current_students', 'min_students', 'waitlist_count',
|
||||
'available_seats', 'enrollment_percentage', 'is_full', 'has_waitlist',
|
||||
'class_teacher', 'class_teacher_name', 'assistant_teacher', 'assistant_teacher_name',
|
||||
'subject_teachers', 'subject_teachers_info', 'classroom', 'building', 'floor',
|
||||
'academic_year', 'semester', 'term', 'schedule', 'schedule_summary', 'meeting_days',
|
||||
'start_time', 'end_time', 'break_times', 'curriculum', 'subjects_offered',
|
||||
'elective_subjects', 'assessment_methods', 'grading_scale', 'passing_grade',
|
||||
'special_programs', 'support_services', 'class_rules', 'attendance_policy',
|
||||
'homework_policy', 'discipline_policy', 'classroom_equipment', 'learning_materials',
|
||||
'digital_resources', 'parent_group_id', 'communication_preferences', 'status',
|
||||
'status_display', 'start_date', 'end_date', 'enrollment_start_date',
|
||||
'enrollment_end_date', 'is_enrollment_open', 'is_enrollment_period',
|
||||
'is_active_currently', 'duration_in_days', 'description', 'notes', 'tags',
|
||||
'requirements', 'created_by', 'created_by_name', 'updated_by', 'updated_by_name',
|
||||
'created_at', 'updated_at', 'display_name', 'capacity_stats',
|
||||
]
|
||||
read_only_fields = [
|
||||
'tenant', 'id', 'class_code', 'created_at', 'updated_at', 'created_by',
|
||||
'updated_by', 'display_name', 'available_seats', 'enrollment_percentage',
|
||||
'is_enrollment_period', 'is_active_currently', 'duration_in_days',
|
||||
'schedule_summary', 'capacity_stats',
|
||||
]
|
||||
|
||||
def get_display_name(self, obj):
|
||||
"""Get display name for the class"""
|
||||
return obj.display_name
|
||||
|
||||
def get_available_seats(self, obj):
|
||||
"""Get number of available seats"""
|
||||
return obj.available_seats
|
||||
|
||||
def get_enrollment_percentage(self, obj):
|
||||
"""Get enrollment percentage"""
|
||||
return round(obj.enrollment_percentage, 2)
|
||||
|
||||
def get_is_enrollment_period(self, obj):
|
||||
"""Check if currently in enrollment period"""
|
||||
return obj.is_enrollment_period
|
||||
|
||||
def get_is_active_currently(self, obj):
|
||||
"""Check if class is currently active"""
|
||||
return obj.is_active_currently
|
||||
|
||||
def get_duration_in_days(self, obj):
|
||||
"""Get class duration in days"""
|
||||
return obj.duration_in_days
|
||||
|
||||
def get_schedule_summary(self, obj):
|
||||
"""Get schedule summary"""
|
||||
return obj.get_schedule_summary()
|
||||
|
||||
def get_capacity_stats(self, obj):
|
||||
"""Get capacity statistics"""
|
||||
return obj.get_capacity_stats()
|
||||
|
||||
def get_subject_teachers_info(self, obj):
|
||||
"""Get subject teachers information"""
|
||||
return [
|
||||
{
|
||||
'id': str(teacher.id),
|
||||
'name': teacher.get_full_name(),
|
||||
'email': teacher.email,
|
||||
}
|
||||
for teacher in obj.subject_teachers.all()
|
||||
]
|
||||
|
||||
def validate(self, data):
|
||||
"""Validate class data"""
|
||||
# Validate dates
|
||||
if data.get('start_date') and data.get('end_date'):
|
||||
if data['end_date'] <= data['start_date']:
|
||||
raise serializers.ValidationError(
|
||||
"End date must be after start date"
|
||||
)
|
||||
|
||||
# Validate enrollment dates
|
||||
if data.get('enrollment_start_date') and data.get('enrollment_end_date'):
|
||||
if data['enrollment_end_date'] <= data['enrollment_start_date']:
|
||||
raise serializers.ValidationError(
|
||||
"Enrollment end date must be after start date"
|
||||
)
|
||||
|
||||
# Validate capacity
|
||||
if data.get('min_students') and data.get('max_students'):
|
||||
if data['min_students'] > data['max_students']:
|
||||
raise serializers.ValidationError(
|
||||
"Minimum students cannot exceed maximum students"
|
||||
)
|
||||
|
||||
# Validate class times
|
||||
if data.get('start_time') and data.get('end_time'):
|
||||
if data['end_time'] <= data['start_time']:
|
||||
raise serializers.ValidationError(
|
||||
"End time must be after start time"
|
||||
)
|
||||
|
||||
# Validate schedule if provided
|
||||
if data.get('schedule'):
|
||||
self._validate_schedule(data['schedule'])
|
||||
|
||||
# Validate teacher assignments
|
||||
if data.get('class_teacher') and data.get('assistant_teacher'):
|
||||
if data['class_teacher'].id == data['assistant_teacher'].id:
|
||||
raise serializers.ValidationError(
|
||||
"Assistant teacher cannot be the same as class teacher"
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
def _validate_schedule(self, schedule):
|
||||
"""Validate schedule format"""
|
||||
if not isinstance(schedule, dict):
|
||||
raise serializers.ValidationError("Schedule must be a dictionary")
|
||||
|
||||
valid_days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
|
||||
|
||||
for day, periods in schedule.items():
|
||||
if day not in valid_days:
|
||||
raise serializers.ValidationError(f"Invalid day: {day}")
|
||||
|
||||
if not isinstance(periods, list):
|
||||
raise serializers.ValidationError(f"Schedule for {day} must be a list")
|
||||
|
||||
for period in periods:
|
||||
if not isinstance(period, dict):
|
||||
raise serializers.ValidationError(f"Each period in {day} must be a dictionary")
|
||||
|
||||
required_fields = ['subject', 'time', 'teacher']
|
||||
for field in required_fields:
|
||||
if field not in period:
|
||||
raise serializers.ValidationError(f"Missing required field '{field}' in period for {day}")
|
||||
|
||||
def create(self, validated_data):
|
||||
"""Create class with tenant context"""
|
||||
validated_data['tenant'] = self.context['tenant']
|
||||
return super().create(validated_data)
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
"""Update class with proper validation"""
|
||||
# Remove read-only fields from validated data
|
||||
validated_data.pop('tenant', None)
|
||||
validated_data.pop('class_code', None)
|
||||
|
||||
return super().update(instance, validated_data)
|
||||
|
||||
|
||||
class ClassCreateSerializer(ClassSerializer):
|
||||
"""Serializer for creating classes with less restrictions"""
|
||||
|
||||
class Meta(ClassSerializer.Meta):
|
||||
read_only_fields = ['id', 'created_at', 'updated_at', 'created_by', 'updated_by']
|
||||
|
||||
|
||||
class ClassUpdateSerializer(ClassSerializer):
|
||||
"""Serializer for updating classes"""
|
||||
|
||||
class Meta(ClassSerializer.Meta):
|
||||
read_only_fields = ClassSerializer.Meta.read_only_fields + ['class_code']
|
||||
|
||||
|
||||
class ClassListSerializer(serializers.ModelSerializer):
|
||||
"""Simplified serializer for class lists"""
|
||||
|
||||
display_name = serializers.SerializerMethodField()
|
||||
grade_level_display = serializers.CharField(source='get_grade_level_display', read_only=True)
|
||||
stream_display = serializers.CharField(source='get_stream_display', read_only=True)
|
||||
status_display = serializers.CharField(source='get_status_display', read_only=True)
|
||||
class_teacher_name = serializers.CharField(source='class_teacher.name', read_only=True)
|
||||
available_seats = serializers.SerializerMethodField()
|
||||
enrollment_percentage = serializers.SerializerMethodField()
|
||||
is_enrollment_open = serializers.BooleanField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Class
|
||||
fields = [
|
||||
'id', 'class_code', 'name', 'display_name', 'grade_level', 'grade_level_display',
|
||||
'stream', 'stream_display', 'classroom', 'max_students', 'current_students',
|
||||
'available_seats', 'enrollment_percentage', 'academic_year', 'status',
|
||||
'status_display', 'class_teacher_name', 'is_enrollment_open', 'start_date', 'end_date',
|
||||
]
|
||||
|
||||
def get_display_name(self, obj):
|
||||
return obj.display_name
|
||||
|
||||
def get_available_seats(self, obj):
|
||||
return obj.available_seats
|
||||
|
||||
def get_enrollment_percentage(self, obj):
|
||||
return round(obj.enrollment_percentage, 2)
|
||||
|
||||
|
||||
class TeacherAssignmentSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for teacher assignments"""
|
||||
|
||||
role = serializers.CharField(max_length=20)
|
||||
teacher_name = serializers.CharField(source='teacher.name', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Class
|
||||
fields = ['id', 'name', 'role', 'teacher_name', 'academic_year', 'grade_level']
|
||||
|
||||
|
||||
class ClassStatisticsSerializer(serializers.Serializer):
|
||||
"""Serializer for class statistics"""
|
||||
|
||||
total_classes = serializers.IntegerField()
|
||||
active_classes = serializers.IntegerField()
|
||||
classes_with_open_enrollment = serializers.IntegerField()
|
||||
grade_level_distribution = serializers.DictField()
|
||||
stream_distribution = serializers.DictField()
|
||||
total_capacity = serializers.IntegerField()
|
||||
total_enrollment = serializers.IntegerField()
|
||||
overall_enrollment_rate = serializers.FloatField()
|
||||
|
||||
|
||||
class ClassImportSerializer(serializers.Serializer):
|
||||
"""Serializer for bulk class import"""
|
||||
|
||||
file = serializers.FileField()
|
||||
update_existing = serializers.BooleanField(default=False)
|
||||
format = serializers.ChoiceField(choices=['csv', 'excel'], default='csv')
|
||||
|
||||
|
||||
class ClassExportSerializer(serializers.Serializer):
|
||||
"""Serializer for class export options"""
|
||||
|
||||
format = serializers.ChoiceField(choices=['csv', 'excel', 'json'], default='csv')
|
||||
include_schedule = serializers.BooleanField(default=False)
|
||||
include_teachers = serializers.BooleanField(default=True)
|
||||
include_capacity = serializers.BooleanField(default=True)
|
||||
filters = serializers.DictField(required=False)
|
||||
|
||||
|
||||
class ScheduleUpdateSerializer(serializers.Serializer):
|
||||
"""Serializer for updating class schedule"""
|
||||
|
||||
schedule = serializers.DictField()
|
||||
check_conflicts = serializers.BooleanField(default=True)
|
||||
|
||||
def validate_schedule(self, schedule):
|
||||
"""Validate schedule format"""
|
||||
if not isinstance(schedule, dict):
|
||||
raise serializers.ValidationError("Schedule must be a dictionary")
|
||||
|
||||
valid_days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
|
||||
|
||||
for day, periods in schedule.items():
|
||||
if day not in valid_days:
|
||||
raise serializers.ValidationError(f"Invalid day: {day}")
|
||||
|
||||
if not isinstance(periods, list):
|
||||
raise serializers.ValidationError(f"Schedule for {day} must be a list")
|
||||
|
||||
for period in periods:
|
||||
if not isinstance(period, dict):
|
||||
raise serializers.ValidationError(f"Each period in {day} must be a dictionary")
|
||||
|
||||
required_fields = ['subject', 'time', 'teacher']
|
||||
for field in required_fields:
|
||||
if field not in period:
|
||||
raise serializers.ValidationError(f"Missing required field '{field}' in period for {day}")
|
||||
|
||||
return schedule
|
||||
|
||||
|
||||
class EnrollmentControlSerializer(serializers.Serializer):
|
||||
"""Serializer for controlling enrollment"""
|
||||
|
||||
is_enrollment_open = serializers.BooleanField()
|
||||
enrollment_start_date = serializers.DateField(required=False)
|
||||
enrollment_end_date = serializers.DateField(required=False)
|
||||
|
||||
def validate(self, data):
|
||||
"""Validate enrollment control data"""
|
||||
if data.get('is_enrollment_open'):
|
||||
if not data.get('enrollment_start_date') or not data.get('enrollment_end_date'):
|
||||
raise serializers.ValidationError(
|
||||
"Enrollment start and end dates are required when opening enrollment"
|
||||
)
|
||||
|
||||
if data['enrollment_end_date'] <= data['enrollment_start_date']:
|
||||
raise serializers.ValidationError(
|
||||
"Enrollment end date must be after start date"
|
||||
)
|
||||
|
||||
return data
|
||||
Reference in New Issue
Block a user