Archived
feat: uv config other feats
- add uv configuration for the backend - update frontend to make auth work - add new auth endpoints - add bookmars feat - add reader feat
This commit is contained in:
@@ -1,62 +1,86 @@
|
||||
import re
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from apps.annotations.models import Bookmark, Note
|
||||
|
||||
_HEX_COLOR_RE = re.compile(r"^#[0-9A-Fa-f]{6}$")
|
||||
|
||||
|
||||
def validate_highlight_color(value: str) -> str:
|
||||
stripped = (value or "").strip()
|
||||
if not _HEX_COLOR_RE.match(stripped):
|
||||
raise serializers.ValidationError("highlight_color must be a hex color like #fde047.")
|
||||
return stripped.lower()
|
||||
|
||||
|
||||
class BookmarkSerializer(serializers.ModelSerializer):
|
||||
"""Serialize Bookmark data with full details."""
|
||||
|
||||
book_title = serializers.CharField(source="book.title", read_only=True)
|
||||
ebook_title = serializers.CharField(source="ebook.title", read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Bookmark
|
||||
fields = [
|
||||
"id",
|
||||
"book",
|
||||
"book_title",
|
||||
"ebook",
|
||||
"ebook_title",
|
||||
"epub_cfi",
|
||||
"chapter_index",
|
||||
"chapter_title",
|
||||
"page",
|
||||
"location_text",
|
||||
"content",
|
||||
"highlight_color",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
]
|
||||
read_only_fields = ["id", "created_at", "updated_at", "book_title"]
|
||||
|
||||
def validate_page(self, value: int) -> int:
|
||||
if value < 1:
|
||||
raise serializers.ValidationError("Page must be a positive integer.")
|
||||
return value
|
||||
read_only_fields = ["id", "created_at", "updated_at", "ebook_title", "page"]
|
||||
|
||||
|
||||
class BookmarkCreateSerializer(serializers.ModelSerializer):
|
||||
"""Serializer used for creating bookmarks. Sets user from request context."""
|
||||
|
||||
class Meta:
|
||||
model = Bookmark
|
||||
fields = ["book", "page", "location_text"]
|
||||
fields = [
|
||||
"ebook",
|
||||
"epub_cfi",
|
||||
"chapter_index",
|
||||
"chapter_title",
|
||||
"location_text",
|
||||
"content",
|
||||
"highlight_color",
|
||||
]
|
||||
|
||||
def validate_page(self, value: int) -> int:
|
||||
if value < 1:
|
||||
raise serializers.ValidationError("Page must be a positive integer.")
|
||||
def validate_highlight_color(self, value: str) -> str:
|
||||
return validate_highlight_color(value)
|
||||
|
||||
def validate_epub_cfi(self, value: str) -> str:
|
||||
stripped = (value or "").strip()
|
||||
if not stripped:
|
||||
raise serializers.ValidationError("epub_cfi is required.")
|
||||
return stripped
|
||||
|
||||
def validate_chapter_index(self, value: int) -> int:
|
||||
if value < 0:
|
||||
raise serializers.ValidationError("chapter_index must be non-negative.")
|
||||
return value
|
||||
|
||||
def validate(self, attrs):
|
||||
user = self.context["request"].user
|
||||
if Bookmark.objects.filter(
|
||||
user=user, book=attrs["book"], page=attrs["page"]
|
||||
).exists():
|
||||
ebook = attrs["ebook"]
|
||||
epub_cfi = attrs["epub_cfi"]
|
||||
if Bookmark.objects.filter(user=user, ebook=ebook, epub_cfi=epub_cfi).exists():
|
||||
raise serializers.ValidationError(
|
||||
{"page": "A bookmark already exists at this page for this book."}
|
||||
{"epub_cfi": "A marker already exists for this passage."}
|
||||
)
|
||||
return attrs
|
||||
|
||||
def create(self, validated_data):
|
||||
validated_data["user"] = self.context["request"].user
|
||||
validated_data["page"] = validated_data.get("chapter_index", 0) + 1
|
||||
validated_data["content"] = (validated_data.get("content") or "").strip()
|
||||
return super().create(validated_data)
|
||||
|
||||
|
||||
class NoteSerializer(serializers.ModelSerializer):
|
||||
"""Serialize Note data with full details."""
|
||||
|
||||
book_title = serializers.CharField(source="book.title", read_only=True)
|
||||
|
||||
class Meta:
|
||||
@@ -78,16 +102,8 @@ class NoteSerializer(serializers.ModelSerializer):
|
||||
raise serializers.ValidationError("Page must be a positive integer.")
|
||||
return value
|
||||
|
||||
def validate_content(self, value: str) -> str:
|
||||
stripped = value.strip()
|
||||
if not stripped:
|
||||
raise serializers.ValidationError("Note content cannot be empty.")
|
||||
return stripped
|
||||
|
||||
|
||||
class NoteCreateSerializer(serializers.ModelSerializer):
|
||||
"""Serializer used for creating notes. Sets user from request context."""
|
||||
|
||||
class Meta:
|
||||
model = Note
|
||||
fields = ["book", "page", "location_text", "content"]
|
||||
@@ -105,4 +121,4 @@ class NoteCreateSerializer(serializers.ModelSerializer):
|
||||
|
||||
def create(self, validated_data):
|
||||
validated_data["user"] = self.context["request"].user
|
||||
return super().create(validated_data)
|
||||
return super().create(validated_data)
|
||||
|
||||
Reference in New Issue
Block a user