from rest_framework import serializers from books.models import Book, FontStyle, BackgroundColor, ReadingProgress, ReadingSettings class BookListSerializer(serializers.ModelSerializer): """Lightweight serializer for the library listing — no file or progress data.""" filename = serializers.CharField(read_only=True) progress = serializers.SerializerMethodField() class Meta: model = Book fields = [ "id", "title", "author", "filename", "cover_image", "created_at", "progress", ] def get_progress(self, obj: Book) -> float | None: try: return obj.reading_progress.current_position except ReadingProgress.DoesNotExist: return None class BookDetailSerializer(serializers.ModelSerializer): """Full serializer for the reader view — includes file URL and progress.""" filename = serializers.CharField(read_only=True) file_url = serializers.SerializerMethodField() progress = serializers.SerializerMethodField() class Meta: model = Book fields = [ "id", "title", "author", "filename", "file_url", "cover_image", "created_at", "updated_at", "progress", ] def get_file_url(self, obj: Book) -> str: request = self.context.get("request") if request and obj.file: return request.build_absolute_uri(obj.file.url) return "" def get_progress(self, obj: Book) -> dict | None: try: rp = obj.reading_progress return { "current_position": rp.current_position, "last_page": rp.last_page, } except ReadingProgress.DoesNotExist: return None class BookUploadSerializer(serializers.ModelSerializer): """Serializer for uploading a new book.""" class Meta: model = Book fields = ["title", "author", "file", "cover_image"] extra_kwargs = { "title": {"required": True}, "file": {"required": True}, } def validate_file(self, value: object) -> object: import os if isinstance(value, type(None)): return value ext = os.path.splitext(str(getattr(value, 'name', '')))[1].lower() if ext not in (".epub", ".pdf"): raise serializers.ValidationError( "Only EPUB and PDF files are supported." ) return value def create(self, validated_data: dict) -> Book: validated_data["user"] = self.context["request"].user return super().create(validated_data) class ReadingProgressSerializer(serializers.ModelSerializer): class Meta: model = ReadingProgress fields = ["current_position", "last_page"] extra_kwargs = { "current_position": {"required": True, "min_value": 0.0, "max_value": 100.0}, } def validate_current_position(self, value: float) -> float: if value < 0.0 or value > 100.0: raise serializers.ValidationError( "Position must be between 0.0 and 100.0." ) return value class ReadingSettingsSerializer(serializers.ModelSerializer): class Meta: model = ReadingSettings fields = ["font_size", "font_style", "background_color"] def validate_font_size(self, value: int) -> int: if value < 12 or value > 36: raise serializers.ValidationError("Font size must be between 12 and 36.") return value def validate_font_style(self, value: str) -> str: valid = [s.value for s in FontStyle] if value not in valid: raise serializers.ValidationError( f"Font style must be one of: {', '.join(valid)}" ) return value def validate_background_color(self, value: str) -> str: valid = [c.value for c in BackgroundColor] if value not in valid: raise serializers.ValidationError( f"Background color must be one of: {', '.join(valid)}" ) return value