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

This commit is contained in:
2025-10-05 02:37:33 +08:00
parent 2cbb6d5fa1
commit b3fff546e9
226 changed files with 97805 additions and 35 deletions

View 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