You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1279 lines
43 KiB
1279 lines
43 KiB
from rest_framework import serializers
|
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
|
|
|
from utils.image_compression import maybe_compress_uploaded_file
|
|
from ..models import (
|
|
Hadis, HadisCategory, HadisStatus, HadisTag, HadisCorrection, HadisInterpretation, HadisCollection, HadisSect, HadisInCollection, HadisTransmitter,
|
|
BookReference, BookReferenceImage, BookReferenceDocument, BookAuthor, BookAttribute, BookSubjectArea, BookType, BookEdition, BookVolume, BookResearcher,
|
|
Transmitters, TransmitterReliability, NarratorLayer, OpinionStatus, TransmitterOpinion, TransmitterOriginalText,
|
|
CorrectionReference, CorrectionReferenceImage, InterpretationReference, InterpretationReferenceImage,
|
|
OriginalTextReference, OriginalTextReferenceImage
|
|
)
|
|
from .category import LocalizedField
|
|
|
|
|
|
def safe_copy_data(data):
|
|
if hasattr(data, "lists"):
|
|
new_data = {}
|
|
for key, values in data.lists():
|
|
if key in ["delete_image_ids", "uploaded_images"]:
|
|
new_data[key] = values
|
|
else:
|
|
new_data[key] = values[0] if values else None
|
|
return new_data
|
|
elif hasattr(data, "copy"):
|
|
return data.copy()
|
|
else:
|
|
return dict(data)
|
|
|
|
|
|
class AbsoluteImageField(serializers.ImageField):
|
|
def to_representation(self, value):
|
|
if not value:
|
|
return None
|
|
request = self.context.get("request")
|
|
url = value.url if hasattr(value, "url") else str(value)
|
|
if request:
|
|
return request.build_absolute_uri(url)
|
|
return url
|
|
|
|
|
|
class AdminHadisCategorySerializer(serializers.ModelSerializer):
|
|
sect_type = serializers.CharField(source='sect.sect_type', read_only=True)
|
|
title = LocalizedField()
|
|
description = LocalizedField()
|
|
|
|
class Meta:
|
|
model = HadisCategory
|
|
fields = [
|
|
"id",
|
|
"title",
|
|
"slug",
|
|
"source_type",
|
|
"order",
|
|
"sect",
|
|
"sect_type",
|
|
"description",
|
|
]
|
|
|
|
|
|
class AdminHadisStatusSerializer(serializers.ModelSerializer):
|
|
title = serializers.JSONField()
|
|
description = serializers.JSONField()
|
|
|
|
class Meta:
|
|
model = HadisStatus
|
|
fields = ["id", "title", "slug", "color", "main_color_code", "order", "description"]
|
|
|
|
|
|
class AdminHadisTagSerializer(serializers.ModelSerializer):
|
|
title = serializers.JSONField()
|
|
|
|
class Meta:
|
|
model = HadisTag
|
|
fields = ["id", "title", "status"]
|
|
|
|
|
|
class AdminHadisListSerializer(serializers.ModelSerializer):
|
|
category_detail = AdminHadisCategorySerializer(source="category", read_only=True)
|
|
hadis_status_detail = AdminHadisStatusSerializer(source="hadis_status", read_only=True)
|
|
|
|
class Meta:
|
|
model = Hadis
|
|
fields = [
|
|
"id",
|
|
"number",
|
|
"slug",
|
|
"title",
|
|
"title_narrator",
|
|
"text",
|
|
"translation",
|
|
"status",
|
|
"category",
|
|
"category_detail",
|
|
"hadis_status",
|
|
"hadis_status_detail",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
read_only_fields = ["id", "slug", "created_at", "updated_at"]
|
|
|
|
|
|
class HadisCollectionsField(serializers.PrimaryKeyRelatedField):
|
|
def to_representation(self, value):
|
|
return value.collection_id
|
|
|
|
|
|
class AdminHadisCollectionSerializer(serializers.ModelSerializer):
|
|
title = serializers.JSONField()
|
|
summary = serializers.JSONField()
|
|
thumbnail = AbsoluteImageField(required=False, allow_null=True)
|
|
remove_thumbnail = serializers.BooleanField(write_only=True, required=False, default=False)
|
|
|
|
class Meta:
|
|
model = HadisCollection
|
|
fields = [
|
|
"id",
|
|
"title",
|
|
"slug",
|
|
"summary",
|
|
"status",
|
|
"display_position",
|
|
"order",
|
|
"thumbnail",
|
|
"remove_thumbnail",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
read_only_fields = ["id", "created_at", "updated_at"]
|
|
|
|
def to_internal_value(self, data):
|
|
import json
|
|
mutable_data = safe_copy_data(data)
|
|
for json_field in ["title", "summary"]:
|
|
val = mutable_data.get(json_field)
|
|
if isinstance(val, str):
|
|
try:
|
|
mutable_data[json_field] = json.loads(val)
|
|
except ValueError:
|
|
pass
|
|
thumbnail = mutable_data.get("thumbnail") if hasattr(mutable_data, "get") else None
|
|
if thumbnail and hasattr(thumbnail, "read"):
|
|
compressed_bytes = maybe_compress_uploaded_file(thumbnail)
|
|
if compressed_bytes is not None:
|
|
mutable_data = safe_copy_data(mutable_data)
|
|
mutable_data["thumbnail"] = SimpleUploadedFile(
|
|
name=getattr(thumbnail, "name", "image"),
|
|
content=compressed_bytes,
|
|
content_type=getattr(thumbnail, "content_type", None),
|
|
)
|
|
return super().to_internal_value(mutable_data)
|
|
|
|
def create(self, validated_data):
|
|
validated_data.pop("remove_thumbnail", False)
|
|
return super().create(validated_data)
|
|
|
|
def update(self, instance, validated_data):
|
|
remove_thumbnail = validated_data.pop("remove_thumbnail", False)
|
|
if remove_thumbnail and instance.thumbnail:
|
|
instance.thumbnail.delete(save=False)
|
|
instance.thumbnail = None
|
|
return super().update(instance, validated_data)
|
|
|
|
|
|
class AdminHadisDetailSerializer(serializers.ModelSerializer):
|
|
category_detail = AdminHadisCategorySerializer(source="category", read_only=True)
|
|
hadis_status_detail = AdminHadisStatusSerializer(source="hadis_status", read_only=True)
|
|
tags_detail = AdminHadisTagSerializer(source="tags", many=True, read_only=True)
|
|
|
|
category = serializers.PrimaryKeyRelatedField(
|
|
queryset=HadisCategory.objects.all(),
|
|
required=False,
|
|
allow_null=True,
|
|
)
|
|
hadis_status = serializers.PrimaryKeyRelatedField(
|
|
queryset=HadisStatus.objects.all(),
|
|
required=False,
|
|
allow_null=True,
|
|
)
|
|
tags = serializers.PrimaryKeyRelatedField(
|
|
queryset=HadisTag.objects.all(),
|
|
many=True,
|
|
required=False,
|
|
)
|
|
collections = HadisCollectionsField(
|
|
source="collection_items",
|
|
queryset=HadisCollection.objects.all(),
|
|
many=True,
|
|
required=False,
|
|
)
|
|
collections_detail = serializers.SerializerMethodField()
|
|
|
|
bookmark_count = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Hadis
|
|
fields = [
|
|
"id",
|
|
"number",
|
|
"slug",
|
|
"category",
|
|
"category_detail",
|
|
"title_narrator",
|
|
"title",
|
|
"description",
|
|
"text",
|
|
"translation",
|
|
"status",
|
|
"hadis_status",
|
|
"hadis_status_detail",
|
|
"hadis_status_text",
|
|
"address",
|
|
"links",
|
|
"tags",
|
|
"tags_detail",
|
|
"collections",
|
|
"collections_detail",
|
|
"share_link",
|
|
"explanation",
|
|
"bookmark_count",
|
|
"created_at",
|
|
"updated_at",
|
|
"embedded_in",
|
|
]
|
|
read_only_fields = ["id", "slug", "share_link", "bookmark_count", "created_at", "updated_at"]
|
|
|
|
def get_bookmark_count(self, obj):
|
|
from apps.bookmark.models.bookmark import Bookmark
|
|
return Bookmark.objects.filter(service=Bookmark.ServiceChoices.HADITH, content_id=obj.id, status=True).count()
|
|
|
|
def get_collections_detail(self, obj):
|
|
collections = [item.collection for item in obj.collection_items.all()]
|
|
return AdminHadisCollectionSerializer(collections, many=True).data
|
|
|
|
def create(self, validated_data):
|
|
tags = validated_data.pop("tags", [])
|
|
collections = validated_data.pop("collection_items", [])
|
|
hadis = super().create(validated_data)
|
|
if tags:
|
|
hadis.tags.set(tags)
|
|
if collections:
|
|
for idx, col in enumerate(collections):
|
|
HadisInCollection.objects.create(hadis=hadis, collection=col, order=idx)
|
|
return hadis
|
|
|
|
def update(self, instance, validated_data):
|
|
tags = validated_data.pop("tags", None)
|
|
collections = validated_data.pop("collection_items", None)
|
|
hadis = super().update(instance, validated_data)
|
|
if tags is not None:
|
|
hadis.tags.set(tags)
|
|
if collections is not None:
|
|
instance.collection_items.all().delete()
|
|
for idx, col in enumerate(collections):
|
|
HadisInCollection.objects.create(hadis=hadis, collection=col, order=idx)
|
|
return hadis
|
|
|
|
|
|
class AdminHadisBriefSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = Hadis
|
|
fields = ["id", "number", "title"]
|
|
|
|
|
|
class AdminHadisCategoryBriefSerializer(serializers.ModelSerializer):
|
|
title = LocalizedField()
|
|
|
|
class Meta:
|
|
model = HadisCategory
|
|
fields = ["id", "title"]
|
|
|
|
|
|
def save_relation_reference_data(instance, validated_data, request=None, is_correction=True, ref_type=None):
|
|
book_reference = validated_data.pop("book_reference_id", None)
|
|
address = validated_data.pop("address", None)
|
|
delete_image_ids = validated_data.pop("delete_image_ids", [])
|
|
|
|
new_images = request.FILES.getlist("uploaded_images") if request else []
|
|
|
|
ref = instance.references.first()
|
|
if not ref:
|
|
if book_reference or address or new_images:
|
|
if ref_type == "original_text":
|
|
ref = OriginalTextReference.objects.create(original_text=instance)
|
|
elif is_correction:
|
|
ref = CorrectionReference.objects.create(correction=instance)
|
|
else:
|
|
ref = InterpretationReference.objects.create(interpretation=instance)
|
|
else:
|
|
return
|
|
|
|
if book_reference is not None:
|
|
ref.book_reference = book_reference
|
|
if address is not None:
|
|
ref.address = address
|
|
ref.save()
|
|
|
|
if delete_image_ids:
|
|
ref.images.filter(id__in=delete_image_ids).delete()
|
|
|
|
for img in new_images:
|
|
if ref_type == "original_text":
|
|
OriginalTextReferenceImage.objects.create(reference=ref, image=img)
|
|
elif is_correction:
|
|
CorrectionReferenceImage.objects.create(reference=ref, image=img)
|
|
else:
|
|
InterpretationReferenceImage.objects.create(reference=ref, image=img)
|
|
|
|
|
|
class AdminHadisCorrectionSerializer(serializers.ModelSerializer):
|
|
hadis_detail = AdminHadisBriefSerializer(source="hadis", read_only=True)
|
|
bookmark_count = serializers.IntegerField(source="annotated_bookmark_count", read_only=True)
|
|
|
|
# Writable relation fields
|
|
book_reference_id = serializers.PrimaryKeyRelatedField(
|
|
queryset=BookReference.objects.all(),
|
|
required=False,
|
|
write_only=True,
|
|
allow_null=True
|
|
)
|
|
address = serializers.CharField(
|
|
required=False,
|
|
write_only=True,
|
|
allow_null=True,
|
|
allow_blank=True
|
|
)
|
|
delete_image_ids = serializers.ListField(
|
|
child=serializers.IntegerField(),
|
|
required=False,
|
|
write_only=True
|
|
)
|
|
|
|
class Meta:
|
|
model = HadisCorrection
|
|
fields = [
|
|
"id",
|
|
"hadis",
|
|
"hadis_detail",
|
|
"title",
|
|
"slug",
|
|
"text",
|
|
"translation",
|
|
"priority",
|
|
"bookmark_count",
|
|
"links",
|
|
"book_reference_id",
|
|
"address",
|
|
"delete_image_ids",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
read_only_fields = ["id", "slug", "bookmark_count", "created_at", "updated_at"]
|
|
|
|
def to_internal_value(self, data):
|
|
import json
|
|
data = safe_copy_data(data)
|
|
for json_field in ["title", "translation", "links"]:
|
|
val = data.get(json_field)
|
|
if isinstance(val, str):
|
|
try:
|
|
data[json_field] = json.loads(val)
|
|
except ValueError:
|
|
pass
|
|
delete_ids = data.get("delete_image_ids")
|
|
if delete_ids:
|
|
if isinstance(delete_ids, str):
|
|
try:
|
|
data["delete_image_ids"] = json.loads(delete_ids)
|
|
except ValueError:
|
|
data["delete_image_ids"] = [int(x) for x in delete_ids.split(",") if x.strip().isdigit()]
|
|
elif isinstance(delete_ids, list):
|
|
parsed = []
|
|
for x in delete_ids:
|
|
if isinstance(x, str) and x.strip().isdigit():
|
|
parsed.append(int(x))
|
|
elif isinstance(x, int):
|
|
parsed.append(x)
|
|
data["delete_image_ids"] = parsed
|
|
return super().to_internal_value(data)
|
|
|
|
def to_representation(self, instance):
|
|
ret = super().to_representation(instance)
|
|
ref = instance.references.first()
|
|
ret["book_reference"] = ref.book_reference_id if ref else None
|
|
ret["address"] = ref.address if ref else ""
|
|
|
|
request = self.context.get("request")
|
|
ret["images"] = [
|
|
{
|
|
"id": img.id,
|
|
"image": request.build_absolute_uri(img.image.url) if request and img.image else img.image.url
|
|
}
|
|
for img in (ref.images.all() if ref else [])
|
|
]
|
|
return ret
|
|
|
|
def create(self, validated_data):
|
|
request = self.context.get("request")
|
|
book_reference_id = validated_data.pop("book_reference_id", None)
|
|
address = validated_data.pop("address", None)
|
|
delete_image_ids = validated_data.pop("delete_image_ids", [])
|
|
|
|
instance = super().create(validated_data)
|
|
|
|
ref_data = {
|
|
"book_reference_id": book_reference_id,
|
|
"address": address,
|
|
"delete_image_ids": delete_image_ids,
|
|
}
|
|
save_relation_reference_data(instance, ref_data, request=request, is_correction=True)
|
|
return instance
|
|
|
|
def update(self, instance, validated_data):
|
|
request = self.context.get("request")
|
|
book_reference_id = validated_data.pop("book_reference_id", None)
|
|
address = validated_data.pop("address", None)
|
|
delete_image_ids = validated_data.pop("delete_image_ids", [])
|
|
|
|
instance = super().update(instance, validated_data)
|
|
|
|
ref_data = {
|
|
"book_reference_id": book_reference_id,
|
|
"address": address,
|
|
"delete_image_ids": delete_image_ids,
|
|
}
|
|
save_relation_reference_data(instance, ref_data, request=request, is_correction=True)
|
|
return instance
|
|
|
|
|
|
class AdminHadisInterpretationSerializer(serializers.ModelSerializer):
|
|
category_detail = AdminHadisCategoryBriefSerializer(source="category", read_only=True)
|
|
|
|
# Writable relation fields
|
|
book_reference_id = serializers.PrimaryKeyRelatedField(
|
|
queryset=BookReference.objects.all(),
|
|
required=False,
|
|
write_only=True,
|
|
allow_null=True
|
|
)
|
|
address = serializers.CharField(
|
|
required=False,
|
|
write_only=True,
|
|
allow_null=True,
|
|
allow_blank=True
|
|
)
|
|
delete_image_ids = serializers.ListField(
|
|
child=serializers.IntegerField(),
|
|
required=False,
|
|
write_only=True
|
|
)
|
|
|
|
class Meta:
|
|
model = HadisInterpretation
|
|
fields = [
|
|
"id",
|
|
"category",
|
|
"category_detail",
|
|
"title",
|
|
"text",
|
|
"translation",
|
|
"priority",
|
|
"links",
|
|
"book_reference_id",
|
|
"address",
|
|
"delete_image_ids",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
read_only_fields = ["id", "created_at", "updated_at"]
|
|
|
|
def to_internal_value(self, data):
|
|
import json
|
|
data = safe_copy_data(data)
|
|
for json_field in ["title", "translation", "links"]:
|
|
val = data.get(json_field)
|
|
if isinstance(val, str):
|
|
try:
|
|
data[json_field] = json.loads(val)
|
|
except ValueError:
|
|
pass
|
|
delete_ids = data.get("delete_image_ids")
|
|
if delete_ids:
|
|
if isinstance(delete_ids, str):
|
|
try:
|
|
data["delete_image_ids"] = json.loads(delete_ids)
|
|
except ValueError:
|
|
data["delete_image_ids"] = [int(x) for x in delete_ids.split(",") if x.strip().isdigit()]
|
|
elif isinstance(delete_ids, list):
|
|
parsed = []
|
|
for x in delete_ids:
|
|
if isinstance(x, str) and x.strip().isdigit():
|
|
parsed.append(int(x))
|
|
elif isinstance(x, int):
|
|
parsed.append(x)
|
|
data["delete_image_ids"] = parsed
|
|
return super().to_internal_value(data)
|
|
|
|
def to_representation(self, instance):
|
|
ret = super().to_representation(instance)
|
|
ref = instance.references.first()
|
|
ret["book_reference"] = ref.book_reference_id if ref else None
|
|
ret["address"] = ref.address if ref else ""
|
|
|
|
request = self.context.get("request")
|
|
ret["images"] = [
|
|
{
|
|
"id": img.id,
|
|
"image": request.build_absolute_uri(img.image.url) if request and img.image else img.image.url
|
|
}
|
|
for img in (ref.images.all() if ref else [])
|
|
]
|
|
return ret
|
|
|
|
def create(self, validated_data):
|
|
request = self.context.get("request")
|
|
book_reference_id = validated_data.pop("book_reference_id", None)
|
|
address = validated_data.pop("address", None)
|
|
delete_image_ids = validated_data.pop("delete_image_ids", [])
|
|
|
|
instance = super().create(validated_data)
|
|
|
|
ref_data = {
|
|
"book_reference_id": book_reference_id,
|
|
"address": address,
|
|
"delete_image_ids": delete_image_ids,
|
|
}
|
|
save_relation_reference_data(instance, ref_data, request=request, is_correction=False)
|
|
return instance
|
|
|
|
def update(self, instance, validated_data):
|
|
request = self.context.get("request")
|
|
book_reference_id = validated_data.pop("book_reference_id", None)
|
|
address = validated_data.pop("address", None)
|
|
delete_image_ids = validated_data.pop("delete_image_ids", [])
|
|
|
|
instance = super().update(instance, validated_data)
|
|
|
|
ref_data = {
|
|
"book_reference_id": book_reference_id,
|
|
"address": address,
|
|
"delete_image_ids": delete_image_ids,
|
|
}
|
|
save_relation_reference_data(instance, ref_data, request=request, is_correction=False)
|
|
return instance
|
|
|
|
class AbsoluteFileField(serializers.FileField):
|
|
def to_representation(self, value):
|
|
if not value:
|
|
return None
|
|
request = self.context.get("request")
|
|
url = value.url if hasattr(value, "url") else str(value)
|
|
if request:
|
|
return request.build_absolute_uri(url)
|
|
return url
|
|
|
|
|
|
class AdminHadisSectArchiveSerializer(serializers.ModelSerializer):
|
|
title = serializers.JSONField()
|
|
description = serializers.JSONField()
|
|
|
|
class Meta:
|
|
model = HadisSect
|
|
fields = ["id", "sect_type", "title", "description", "is_active", "order"]
|
|
|
|
|
|
class AdminHadisCategoryArchiveParentSerializer(serializers.ModelSerializer):
|
|
title = serializers.JSONField()
|
|
|
|
class Meta:
|
|
model = HadisCategory
|
|
fields = ["id", "title", "slug"]
|
|
|
|
|
|
class AdminHadisCategoryArchiveSerializer(serializers.ModelSerializer):
|
|
parent_detail = AdminHadisCategoryArchiveParentSerializer(source="parent", read_only=True)
|
|
sect_detail = AdminHadisSectArchiveSerializer(source="sect", read_only=True)
|
|
title = serializers.JSONField()
|
|
description = serializers.JSONField()
|
|
xmind_file = AbsoluteFileField(required=False, allow_null=True)
|
|
remove_xmind_file = serializers.BooleanField(write_only=True, required=False, default=False)
|
|
|
|
parent = serializers.PrimaryKeyRelatedField(
|
|
queryset=HadisCategory.objects.all(),
|
|
required=False,
|
|
allow_null=True,
|
|
)
|
|
sect = serializers.PrimaryKeyRelatedField(
|
|
queryset=HadisSect.objects.all(),
|
|
required=True,
|
|
)
|
|
|
|
class Meta:
|
|
model = HadisCategory
|
|
fields = [
|
|
"id",
|
|
"parent",
|
|
"parent_detail",
|
|
"sect",
|
|
"sect_detail",
|
|
"source_type",
|
|
"title",
|
|
"description",
|
|
"order",
|
|
"xmind_file",
|
|
"remove_xmind_file",
|
|
"slug",
|
|
"share_link",
|
|
"xmind_share_link",
|
|
]
|
|
read_only_fields = ["id", "share_link", "xmind_share_link"]
|
|
|
|
def to_internal_value(self, data):
|
|
import json
|
|
data = safe_copy_data(data)
|
|
for json_field in ["title", "description"]:
|
|
val = data.get(json_field)
|
|
if isinstance(val, str):
|
|
try:
|
|
data[json_field] = json.loads(val)
|
|
except ValueError:
|
|
pass
|
|
return super().to_internal_value(data)
|
|
|
|
def create(self, validated_data):
|
|
validated_data.pop("remove_xmind_file", False)
|
|
return super().create(validated_data)
|
|
|
|
def update(self, instance, validated_data):
|
|
remove_xmind_file = validated_data.pop("remove_xmind_file", False)
|
|
if remove_xmind_file and instance.xmind_file:
|
|
instance.xmind_file.delete(save=False)
|
|
instance.xmind_file = None
|
|
return super().update(instance, validated_data)
|
|
|
|
class AdminTransmitterReliabilitySerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = TransmitterReliability
|
|
fields = ["id", "title", "slug", "color", "main_color_code"]
|
|
|
|
|
|
class AdminNarratorLayerSerializer(serializers.ModelSerializer):
|
|
name = serializers.JSONField()
|
|
description = serializers.JSONField()
|
|
|
|
class Meta:
|
|
model = NarratorLayer
|
|
fields = ["id", "name", "number", "description", "slug", "created_at", "updated_at"]
|
|
read_only_fields = ["id", "created_at", "updated_at"]
|
|
|
|
|
|
class AdminOpinionStatusSerializer(serializers.ModelSerializer):
|
|
title = serializers.JSONField()
|
|
|
|
class Meta:
|
|
model = OpinionStatus
|
|
fields = ["id", "title", "slug", "color", "main_color_code"]
|
|
|
|
|
|
class AdminTransmitterListSerializer(serializers.ModelSerializer):
|
|
reliability_detail = AdminTransmitterReliabilitySerializer(source="reliability", read_only=True)
|
|
full_name = serializers.JSONField()
|
|
known_as = serializers.JSONField()
|
|
nickname = serializers.JSONField()
|
|
thumbnail = AbsoluteImageField(required=False, allow_null=True)
|
|
share_link = serializers.CharField(read_only=True)
|
|
|
|
class Meta:
|
|
model = Transmitters
|
|
fields = [
|
|
"id",
|
|
"full_name",
|
|
"slug",
|
|
"birth_year_hijri",
|
|
"death_year_hijri",
|
|
"known_as",
|
|
"nickname",
|
|
"reliability",
|
|
"reliability_detail",
|
|
"madhhab",
|
|
"generation",
|
|
"thumbnail",
|
|
"share_link",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
read_only_fields = ["id", "slug", "share_link", "created_at", "updated_at"]
|
|
|
|
|
|
class AdminTransmitterDetailSerializer(serializers.ModelSerializer):
|
|
reliability_detail = AdminTransmitterReliabilitySerializer(source="reliability", read_only=True)
|
|
full_name = serializers.JSONField()
|
|
kunya = serializers.JSONField()
|
|
known_as = serializers.JSONField()
|
|
nickname = serializers.JSONField()
|
|
origin = serializers.JSONField()
|
|
lived_in = serializers.JSONField()
|
|
died_in = serializers.JSONField()
|
|
description = serializers.JSONField()
|
|
thumbnail = AbsoluteImageField(required=False, allow_null=True)
|
|
remove_thumbnail = serializers.BooleanField(write_only=True, required=False, default=False)
|
|
reliability = serializers.PrimaryKeyRelatedField(
|
|
queryset=TransmitterReliability.objects.all(),
|
|
required=False,
|
|
)
|
|
share_link = serializers.CharField(read_only=True)
|
|
|
|
class Meta:
|
|
model = Transmitters
|
|
fields = [
|
|
"id",
|
|
"full_name",
|
|
"kunya",
|
|
"known_as",
|
|
"nickname",
|
|
"slug",
|
|
"legacy_number",
|
|
"legacy_id",
|
|
"birth_year_hijri",
|
|
"death_year_hijri",
|
|
"age_at_death",
|
|
"generation",
|
|
"reliability",
|
|
"reliability_detail",
|
|
"madhhab",
|
|
"in_sahih_muslim",
|
|
"in_sahih_bukhari",
|
|
"origin",
|
|
"lived_in",
|
|
"died_in",
|
|
"description",
|
|
"thumbnail",
|
|
"remove_thumbnail",
|
|
"share_link",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
read_only_fields = ["id", "slug", "share_link", "created_at", "updated_at", "reliability_detail"]
|
|
|
|
def to_internal_value(self, data):
|
|
import json
|
|
data = safe_copy_data(data)
|
|
for json_field in [
|
|
"full_name",
|
|
"kunya",
|
|
"known_as",
|
|
"nickname",
|
|
"origin",
|
|
"lived_in",
|
|
"died_in",
|
|
"description",
|
|
]:
|
|
val = data.get(json_field)
|
|
if isinstance(val, str):
|
|
try:
|
|
data[json_field] = json.loads(val)
|
|
except ValueError:
|
|
pass
|
|
return super().to_internal_value(data)
|
|
|
|
def update(self, instance, validated_data):
|
|
remove_thumbnail = validated_data.pop("remove_thumbnail", False)
|
|
if remove_thumbnail and instance.thumbnail:
|
|
instance.thumbnail.delete(save=False)
|
|
instance.thumbnail = None
|
|
return super().update(instance, validated_data)
|
|
|
|
|
|
class AdminBookSubjectAreaSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = BookSubjectArea
|
|
fields = ["id", "title", "created_at", "updated_at"]
|
|
read_only_fields = ["id", "created_at", "updated_at"]
|
|
|
|
|
|
class AdminBookTypeSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = BookType
|
|
fields = ["id", "title", "sect", "created_at", "updated_at"]
|
|
read_only_fields = ["id", "created_at", "updated_at"]
|
|
|
|
|
|
class AdminBookAuthorSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = BookAuthor
|
|
fields = [
|
|
"id",
|
|
"name",
|
|
"slug",
|
|
"birth_year_hijri",
|
|
"death_year_hijri",
|
|
"birth_year_miladi",
|
|
"death_year_miladi",
|
|
"related_narrator",
|
|
"known_as",
|
|
"scholarity",
|
|
"tabaqa",
|
|
"rutba",
|
|
"features",
|
|
"book_references",
|
|
]
|
|
read_only_fields = ["id", "slug"]
|
|
|
|
|
|
class AdminBookAttributeSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = BookAttribute
|
|
fields = ["id", "book_reference", "title", "value"]
|
|
|
|
|
|
class AdminBookReferenceImageSerializer(serializers.ModelSerializer):
|
|
image = AbsoluteImageField(required=True)
|
|
volume = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = BookReferenceImage
|
|
fields = ["id", "book_reference", "image", "volume", "order", "description"]
|
|
|
|
def get_volume(self, obj):
|
|
return obj.book_volume.title if getattr(obj, 'book_volume', None) else ""
|
|
|
|
|
|
class AdminBookReferenceDocumentSerializer(serializers.ModelSerializer):
|
|
file = AbsoluteFileField(required=True)
|
|
volume = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = BookReferenceDocument
|
|
fields = ["id", "book_reference", "file", "volume", "title", "order"]
|
|
|
|
def get_volume(self, obj):
|
|
return obj.book_volume.title if getattr(obj, 'book_volume', None) else ""
|
|
|
|
|
|
class AdminBookReferenceListSerializer(serializers.ModelSerializer):
|
|
volume = serializers.SerializerMethodField()
|
|
publisher = serializers.SerializerMethodField()
|
|
year_of_publication = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = BookReference
|
|
fields = [
|
|
"id",
|
|
"title",
|
|
"slug",
|
|
"volume",
|
|
"publisher",
|
|
"year_of_publication",
|
|
"rate",
|
|
"created_at",
|
|
]
|
|
|
|
def get_volume(self, obj):
|
|
vols = list(obj.volumes.all())
|
|
return vols[0].title if vols else ""
|
|
|
|
def get_publisher(self, obj):
|
|
editions = list(obj.editions.all())
|
|
return editions[0].publisher if editions else []
|
|
|
|
def get_year_of_publication(self, obj):
|
|
editions = list(obj.editions.all())
|
|
return editions[0].year_of_publication if editions else ""
|
|
|
|
|
|
class AdminBookReferenceDetailSerializer(serializers.ModelSerializer):
|
|
subject_area_detail = AdminBookSubjectAreaSerializer(source="subject_area", many=True, read_only=True)
|
|
type_detail = AdminBookTypeSerializer(source="type", read_only=True)
|
|
author_detail = AdminBookAuthorSerializer(source="author", read_only=True)
|
|
|
|
subject_area = serializers.PrimaryKeyRelatedField(
|
|
queryset=BookSubjectArea.objects.all(),
|
|
many=True,
|
|
required=False,
|
|
)
|
|
type = serializers.PrimaryKeyRelatedField(
|
|
queryset=BookType.objects.all(),
|
|
required=False,
|
|
allow_null=True,
|
|
)
|
|
author = serializers.PrimaryKeyRelatedField(
|
|
queryset=BookAuthor.objects.all(),
|
|
required=False,
|
|
allow_null=True,
|
|
)
|
|
|
|
source_url = serializers.SerializerMethodField()
|
|
isbn = serializers.SerializerMethodField()
|
|
volume = serializers.SerializerMethodField()
|
|
number_of_volumes = serializers.SerializerMethodField()
|
|
year_of_publication = serializers.SerializerMethodField()
|
|
publisher = serializers.SerializerMethodField()
|
|
city_of_publication = serializers.SerializerMethodField()
|
|
country_of_publication = serializers.SerializerMethodField()
|
|
edition_number = serializers.SerializerMethodField()
|
|
|
|
def get_source_url(self, obj):
|
|
ed = obj.editions.first()
|
|
return ed.source_url if ed else ""
|
|
|
|
def get_isbn(self, obj):
|
|
ed = obj.editions.first()
|
|
return ed.isbn if ed else ""
|
|
|
|
def get_volume(self, obj):
|
|
vol = obj.volumes.first()
|
|
return vol.title if vol else ""
|
|
|
|
def get_number_of_volumes(self, obj):
|
|
ed = obj.editions.first()
|
|
return ed.number_of_volumes if ed else None
|
|
|
|
def get_year_of_publication(self, obj):
|
|
ed = obj.editions.first()
|
|
return ed.year_of_publication if ed else ""
|
|
|
|
def get_publisher(self, obj):
|
|
ed = obj.editions.first()
|
|
return ed.publisher if ed else []
|
|
|
|
def get_city_of_publication(self, obj):
|
|
ed = obj.editions.first()
|
|
return ed.city_of_publication if ed else []
|
|
|
|
def get_country_of_publication(self, obj):
|
|
ed = obj.editions.first()
|
|
return ed.country_of_publication if ed else []
|
|
|
|
def get_edition_number(self, obj):
|
|
ed = obj.editions.first()
|
|
return ed.edition_number if ed else ""
|
|
|
|
class Meta:
|
|
model = BookReference
|
|
fields = [
|
|
"id",
|
|
"legacy_id",
|
|
"source_url",
|
|
"title",
|
|
"description",
|
|
"language",
|
|
"isbn",
|
|
"volume",
|
|
"number_of_volumes",
|
|
"year_of_publication",
|
|
"number_page",
|
|
"slug",
|
|
"publisher",
|
|
"subject_area",
|
|
"subject_area_detail",
|
|
"type",
|
|
"type_detail",
|
|
"order",
|
|
"city_of_publication",
|
|
"country_of_publication",
|
|
"edition_number",
|
|
"rate",
|
|
"author",
|
|
"author_detail",
|
|
"created_at",
|
|
"updated_at",
|
|
"share_link",
|
|
]
|
|
read_only_fields = ["id", "slug", "created_at", "updated_at", "share_link"]
|
|
|
|
def create(self, validated_data):
|
|
subject_area = validated_data.pop("subject_area", [])
|
|
book = super().create(validated_data)
|
|
if subject_area:
|
|
book.subject_area.set(subject_area)
|
|
return book
|
|
|
|
def update(self, instance, validated_data):
|
|
subject_area = validated_data.pop("subject_area", None)
|
|
book = super().update(instance, validated_data)
|
|
if subject_area is not None:
|
|
book.subject_area.set(subject_area)
|
|
return book
|
|
|
|
|
|
# Category Quran Verse Admin Serializers
|
|
from ..models.category import CategoryQuranVerse, QuranSecondaryTranslation
|
|
|
|
class AdminQuranSecondaryTranslationSerializer(serializers.ModelSerializer):
|
|
id = serializers.IntegerField(required=False)
|
|
translation_text = serializers.JSONField(default=list)
|
|
translator = serializers.JSONField(default=list)
|
|
|
|
class Meta:
|
|
model = QuranSecondaryTranslation
|
|
fields = ["id", "translation_text", "translator"]
|
|
|
|
class AdminCategoryQuranVerseSerializer(serializers.ModelSerializer):
|
|
surah_name = serializers.JSONField(default=list)
|
|
translation = serializers.JSONField(default=list)
|
|
translator = serializers.JSONField(default=list)
|
|
secondary_translations = AdminQuranSecondaryTranslationSerializer(many=True, required=False)
|
|
|
|
class Meta:
|
|
model = CategoryQuranVerse
|
|
fields = [
|
|
"id",
|
|
"category",
|
|
"surah_name",
|
|
"ayah_number",
|
|
"arabic_text",
|
|
"translation",
|
|
"translator",
|
|
"secondary_translations",
|
|
]
|
|
|
|
def create(self, validated_data):
|
|
secondary_data = validated_data.pop("secondary_translations", [])
|
|
verse = CategoryQuranVerse.objects.create(**validated_data)
|
|
for sec in secondary_data:
|
|
QuranSecondaryTranslation.objects.create(quran_verse=verse, **sec)
|
|
return verse
|
|
|
|
def update(self, instance, validated_data):
|
|
secondary_data = validated_data.pop("secondary_translations", None)
|
|
instance.surah_name = validated_data.get("surah_name", instance.surah_name)
|
|
instance.ayah_number = validated_data.get("ayah_number", instance.ayah_number)
|
|
instance.arabic_text = validated_data.get("arabic_text", instance.arabic_text)
|
|
instance.translation = validated_data.get("translation", instance.translation)
|
|
instance.translator = validated_data.get("translator", instance.translator)
|
|
instance.category = validated_data.get("category", instance.category)
|
|
instance.save()
|
|
|
|
if secondary_data is not None:
|
|
keep_ids = [item.get("id") for item in secondary_data if item.get("id")]
|
|
instance.secondary_translations.exclude(id__in=keep_ids).delete()
|
|
for sec in secondary_data:
|
|
sec_id = sec.get("id")
|
|
if sec_id:
|
|
QuranSecondaryTranslation.objects.filter(id=sec_id, quran_verse=instance).update(
|
|
translation_text=sec.get("translation_text", []),
|
|
translator=sec.get("translator", [])
|
|
)
|
|
else:
|
|
QuranSecondaryTranslation.objects.create(quran_verse=instance, **sec)
|
|
return instance
|
|
|
|
|
|
class AdminHadisTransmitterSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = HadisTransmitter
|
|
fields = [
|
|
"id",
|
|
"hadis",
|
|
"transmitter",
|
|
"chain_index",
|
|
"narrator_layer",
|
|
"status",
|
|
"order",
|
|
"is_gap",
|
|
]
|
|
|
|
|
|
class AdminTransmitterOpinionSerializer(serializers.ModelSerializer):
|
|
scholar_name = serializers.JSONField(required=False, default=list)
|
|
opinion_text = serializers.JSONField(required=False, default=list)
|
|
status_detail = AdminTransmitterReliabilitySerializer(source="status", read_only=True)
|
|
|
|
class Meta:
|
|
model = TransmitterOpinion
|
|
fields = [
|
|
"id",
|
|
"transmitter",
|
|
"scholar",
|
|
"scholar_name",
|
|
"opinion_text",
|
|
"status",
|
|
"status_detail",
|
|
"order",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
|
|
|
|
class AdminTransmitterOriginalTextSerializer(serializers.ModelSerializer):
|
|
title = serializers.JSONField(required=False, default=list)
|
|
text = serializers.JSONField(required=False, default=list)
|
|
translation = serializers.JSONField(required=False, default=list)
|
|
|
|
# Writable relation fields
|
|
book_reference_id = serializers.PrimaryKeyRelatedField(
|
|
queryset=BookReference.objects.all(),
|
|
required=False,
|
|
write_only=True,
|
|
allow_null=True
|
|
)
|
|
address = serializers.CharField(
|
|
required=False,
|
|
write_only=True,
|
|
allow_null=True,
|
|
allow_blank=True
|
|
)
|
|
delete_image_ids = serializers.ListField(
|
|
child=serializers.IntegerField(),
|
|
required=False,
|
|
write_only=True
|
|
)
|
|
|
|
class Meta:
|
|
model = TransmitterOriginalText
|
|
fields = [
|
|
"id",
|
|
"transmitter",
|
|
"slug",
|
|
"title",
|
|
"text",
|
|
"translation",
|
|
"share_link",
|
|
"embedded_in",
|
|
"order",
|
|
"book_reference_id",
|
|
"address",
|
|
"delete_image_ids",
|
|
]
|
|
|
|
def to_internal_value(self, data):
|
|
import json
|
|
data = safe_copy_data(data)
|
|
for json_field in ["title", "text", "translation"]:
|
|
val = data.get(json_field)
|
|
if isinstance(val, str):
|
|
try:
|
|
data[json_field] = json.loads(val)
|
|
except ValueError:
|
|
pass
|
|
delete_ids = data.get("delete_image_ids")
|
|
if delete_ids:
|
|
if isinstance(delete_ids, str):
|
|
try:
|
|
data["delete_image_ids"] = json.loads(delete_ids)
|
|
except ValueError:
|
|
data["delete_image_ids"] = [int(x) for x in delete_ids.split(",") if x.strip().isdigit()]
|
|
elif isinstance(delete_ids, list):
|
|
parsed = []
|
|
for x in delete_ids:
|
|
if isinstance(x, str) and x.strip().isdigit():
|
|
parsed.append(int(x))
|
|
elif isinstance(x, int):
|
|
parsed.append(x)
|
|
data["delete_image_ids"] = parsed
|
|
return super().to_internal_value(data)
|
|
|
|
def to_representation(self, instance):
|
|
ret = super().to_representation(instance)
|
|
ref = instance.references.first()
|
|
ret["book_reference"] = ref.book_reference_id if ref else None
|
|
ret["address"] = ref.address if ref else ""
|
|
|
|
request = self.context.get("request")
|
|
ret["images"] = [
|
|
{
|
|
"id": img.id,
|
|
"image": request.build_absolute_uri(img.image.url) if request and img.image else img.image.url
|
|
}
|
|
for img in (ref.images.all() if ref else [])
|
|
]
|
|
return ret
|
|
|
|
def create(self, validated_data):
|
|
request = self.context.get("request")
|
|
book_reference_id = validated_data.pop("book_reference_id", None)
|
|
address = validated_data.pop("address", None)
|
|
delete_image_ids = validated_data.pop("delete_image_ids", [])
|
|
|
|
instance = super().create(validated_data)
|
|
|
|
ref_data = {
|
|
"book_reference_id": book_reference_id,
|
|
"address": address,
|
|
"delete_image_ids": delete_image_ids,
|
|
}
|
|
save_relation_reference_data(instance, ref_data, request=request, ref_type="original_text")
|
|
return instance
|
|
|
|
def update(self, instance, validated_data):
|
|
request = self.context.get("request")
|
|
book_reference_id = validated_data.pop("book_reference_id", None)
|
|
address = validated_data.pop("address", None)
|
|
delete_image_ids = validated_data.pop("delete_image_ids", [])
|
|
|
|
instance = super().update(instance, validated_data)
|
|
|
|
ref_data = {
|
|
"book_reference_id": book_reference_id,
|
|
"address": address,
|
|
"delete_image_ids": delete_image_ids,
|
|
}
|
|
save_relation_reference_data(instance, ref_data, request=request, ref_type="original_text")
|
|
return instance
|
|
|
|
|
|
class AdminBookVolumeSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = BookVolume
|
|
fields = [
|
|
"id",
|
|
"book_reference",
|
|
"edition",
|
|
"title",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
|
|
|
|
class AdminBookResearcherSerializer(serializers.ModelSerializer):
|
|
id = serializers.IntegerField(required=False)
|
|
name = serializers.JSONField(required=False, default=list)
|
|
|
|
class Meta:
|
|
model = BookResearcher
|
|
fields = ["id", "name", "slug", "author"]
|
|
|
|
|
|
class AdminBookEditionSerializer(serializers.ModelSerializer):
|
|
publisher = serializers.JSONField(required=False, default=list)
|
|
city_of_publication = serializers.JSONField(required=False, default=list)
|
|
country_of_publication = serializers.JSONField(required=False, default=list)
|
|
notes = serializers.JSONField(required=False, default=list)
|
|
tags = serializers.JSONField(required=False, default=list)
|
|
volumes_detail = AdminBookVolumeSerializer(source="volumes", many=True, read_only=True)
|
|
number_of_volumes = serializers.SerializerMethodField()
|
|
researchers = AdminBookResearcherSerializer(many=True, required=False)
|
|
|
|
class Meta:
|
|
model = BookEdition
|
|
fields = [
|
|
"id",
|
|
"book_reference",
|
|
"publisher",
|
|
"city_of_publication",
|
|
"country_of_publication",
|
|
"edition_number",
|
|
"year_of_publication",
|
|
"number_of_volumes",
|
|
"source_url",
|
|
"isbn",
|
|
"notes",
|
|
"tags",
|
|
"volumes_detail",
|
|
"researchers",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
|
|
def get_number_of_volumes(self, obj):
|
|
return obj.volumes.count()
|
|
|
|
def create(self, validated_data):
|
|
researchers_data = validated_data.pop("researchers", [])
|
|
edition = super().create(validated_data)
|
|
for res_data in researchers_data:
|
|
BookResearcher.objects.create(book_edition=edition, **res_data)
|
|
return edition
|
|
|
|
def update(self, instance, validated_data):
|
|
researchers_data = validated_data.pop("researchers", None)
|
|
edition = super().update(instance, validated_data)
|
|
if researchers_data is not None:
|
|
# Delete researchers no longer in the request payload
|
|
keep_ids = [r.get("id") for r in researchers_data if r.get("id")]
|
|
instance.researchers.exclude(id__in=keep_ids).delete()
|
|
for res_data in researchers_data:
|
|
res_id = res_data.get("id")
|
|
if res_id:
|
|
# Update existing researcher
|
|
res_data_clean = {k: v for k, v in res_data.items() if k != "id"}
|
|
# Filter and update
|
|
BookResearcher.objects.filter(id=res_id, book_edition=instance).update(**res_data_clean)
|
|
# Trigger save to execute save() hook (for author update propagation if author is changed)
|
|
res_obj = BookResearcher.objects.filter(id=res_id, book_edition=instance).first()
|
|
if res_obj:
|
|
res_obj.save()
|
|
else:
|
|
# Create new researcher
|
|
BookResearcher.objects.create(book_edition=instance, **res_data)
|
|
return edition
|