""" Сериализаторы для учебных материалов. """ from rest_framework import serializers from django.db import models from .models import Material, MaterialFolder, MaterialTag, MaterialAccess, StorageQuota from apps.users.serializers import UserSerializer class MaterialTagSerializer(serializers.ModelSerializer): """Сериализатор тега.""" class Meta: model = MaterialTag fields = [ 'id', 'name', 'slug', 'color', 'materials_count' ] read_only_fields = ['materials_count'] class MaterialFolderSerializer(serializers.ModelSerializer): """Сериализатор папки.""" owner = UserSerializer(read_only=True) path = serializers.SerializerMethodField() class Meta: model = MaterialFolder fields = [ 'id', 'name', 'description', 'owner', 'parent', 'path', 'is_public', 'materials_count', 'created_at', 'updated_at' ] read_only_fields = ['owner', 'materials_count', 'created_at', 'updated_at'] def get_path(self, obj): """Получить путь папки.""" return obj.get_path() class MaterialSerializer(serializers.ModelSerializer): """Сериализатор материала.""" owner = UserSerializer(read_only=True) folder = MaterialFolderSerializer(read_only=True) tags = MaterialTagSerializer(many=True, read_only=True) shared_with = UserSerializer(many=True, read_only=True) class Meta: model = Material fields = [ 'id', 'title', 'description', 'file', 'file_name', 'file_size', 'file_type', 'url', 'material_type', 'owner', 'folder', 'tags', 'lesson', 'homework', 'access_type', 'allow_download', 'is_featured', 'shared_with', 'views_count', 'downloads_count', 'created_at', 'updated_at' ] read_only_fields = [ 'owner', 'file_name', 'file_size', 'file_type', 'views_count', 'downloads_count', 'created_at', 'updated_at' ] class MaterialListSerializer(serializers.ModelSerializer): """Сериализатор списка материалов (упрощенный).""" owner = UserSerializer(read_only=True) tags = MaterialTagSerializer(many=True, read_only=True) file_url = serializers.SerializerMethodField() class Meta: model = Material fields = [ 'id', 'title', 'description', 'file', 'file_url', 'file_name', 'file_size', 'file_type', 'url', 'material_type', 'owner', 'folder', 'tags', 'access_type', 'is_featured', 'views_count', 'created_at' ] def get_file_url(self, obj): """Полный URL файла для превью (изображения, видео).""" if obj.file: request = self.context.get('request') if request: return request.build_absolute_uri(obj.file.url) return obj.file.url return None class MaterialCreateSerializer(serializers.ModelSerializer): """Сериализатор создания материала.""" folder_id = serializers.IntegerField(required=False, allow_null=True) tag_ids = serializers.ListField( child=serializers.IntegerField(), required=False, allow_empty=True ) class Meta: model = Material fields = [ 'title', 'description', 'file', 'url', 'material_type', 'folder_id', 'tag_ids', 'lesson', 'homework', 'access_type', 'allow_download', 'is_featured' ] def validate(self, attrs): """Валидация.""" from .services import StorageService # Проверяем что указан file или url if not attrs.get('file') and not attrs.get('url'): raise serializers.ValidationError({ 'file': 'Необходимо указать файл или ссылку' }) # Проверяем квоту если загружается файл if attrs.get('file'): user = self.context['request'].user file_size = attrs['file'].size # Используем сервис для проверки лимита can_upload, error_message, warning_message = StorageService.check_storage_limit(user, file_size) if not can_upload: raise serializers.ValidationError({ 'file': error_message }) # Сохраняем предупреждение в контексте для использования в create if warning_message: self.context['storage_warning'] = warning_message return attrs def create(self, validated_data): """Создание материала.""" folder_id = validated_data.pop('folder_id', None) tag_ids = validated_data.pop('tag_ids', []) user = self.context['request'].user # Получаем информацию о файле file = validated_data.get('file') if file: validated_data['file_name'] = file.name validated_data['file_size'] = file.size validated_data['file_type'] = file.content_type # Создаем материал с owner (owner будет переопределен в perform_create, но нужен для создания) material = Material.objects.create( owner=user, folder_id=folder_id, **validated_data ) # Добавляем теги if tag_ids: tags = MaterialTag.objects.filter(id__in=tag_ids) material.tags.set(tags) # Обновляем квоту если загружен файл if file: from .services import StorageService StorageService.add_file_usage(user, file.size) return material class StorageQuotaSerializer(serializers.ModelSerializer): """Сериализатор квоты хранилища.""" user = UserSerializer(read_only=True) used_percentage = serializers.SerializerMethodField() available_space = serializers.SerializerMethodField() total_quota_mb = serializers.SerializerMethodField() used_space_mb = serializers.SerializerMethodField() available_space_mb = serializers.SerializerMethodField() is_warning = serializers.SerializerMethodField() is_critical = serializers.SerializerMethodField() class Meta: model = StorageQuota fields = [ 'id', 'user', 'total_quota', 'used_space', 'used_percentage', 'available_space', 'total_quota_mb', 'used_space_mb', 'available_space_mb', 'is_warning', 'is_critical', 'created_at', 'updated_at' ] read_only_fields = ['user', 'used_space', 'created_at', 'updated_at'] def get_used_percentage(self, obj): """Процент использования.""" return round(obj.get_used_percentage(), 2) def get_available_space(self, obj): """Доступное пространство.""" return obj.get_available_space() def get_total_quota_mb(self, obj): """Лимит в МБ.""" return round(obj.total_quota / (1024 * 1024), 2) def get_used_space_mb(self, obj): """Использовано в МБ.""" return round(obj.used_space / (1024 * 1024), 2) def get_available_space_mb(self, obj): """Доступно в МБ.""" return round(obj.get_available_space() / (1024 * 1024), 2) def get_is_warning(self, obj): """Предупреждение (80% и выше).""" return obj.get_used_percentage() >= 80 def get_is_critical(self, obj): """Критическое состояние (90% и выше).""" return obj.get_used_percentage() >= 90 class MaterialAccessSerializer(serializers.ModelSerializer): """Сериализатор лога доступа.""" user = UserSerializer(read_only=True) material = MaterialListSerializer(read_only=True) class Meta: model = MaterialAccess fields = [ 'id', 'material', 'user', 'action', 'ip_address', 'created_at' ] read_only_fields = ['user', 'created_at']