from rest_framework import serializers from rest_framework.pagination import PageNumberPagination from ..models import BookReference, BookAuthor, BookEdition, BookVolume, BookTag, BookResearcher, BookEditor from .category import get_localized_text from apps.hadis.serializers import HadisListSerializer, HadisShortSerializer from ..serializers.category import LocalizedField from ..serializers.reference import BookAttributeSerializer, BookReferenceImageSerializer from utils import absolute_https_url class BookTagV2Serializer(serializers.ModelSerializer): title = LocalizedField() class Meta: model = BookTag fields = ['id', 'title'] class BookAuthorV2Serializer(serializers.ModelSerializer): name = LocalizedField() class Meta: model = BookAuthor fields = ['id', 'name', 'slug', 'image'] class BookResearcherV2Serializer(serializers.ModelSerializer): name = LocalizedField() author = BookAuthorV2Serializer(read_only=True) author_id = serializers.SerializerMethodField() slug = serializers.SerializerMethodField() class Meta: model = BookResearcher fields = ['id', 'name', 'slug', 'author', 'author_id'] def get_author_id(self, obj): return obj.author_id def get_slug(self, obj): if obj.author and obj.author.slug: return obj.author.slug return obj.slug class BookEditorV2Serializer(serializers.ModelSerializer): name = LocalizedField() author = BookAuthorV2Serializer(read_only=True) author_id = serializers.SerializerMethodField() slug = serializers.SerializerMethodField() class Meta: model = BookEditor fields = ['id', 'name', 'slug', 'author', 'author_id'] def get_author_id(self, obj): return obj.author_id def get_slug(self, obj): if obj.author and obj.author.slug: return obj.author.slug return obj.slug class BookReferenceV2ListSerializer(serializers.ModelSerializer): title = LocalizedField() authors = serializers.SerializerMethodField() tag = BookTagV2Serializer(read_only=True) number_of_volumes = serializers.SerializerMethodField() image = serializers.SerializerMethodField() class Meta: model = BookReference fields = ['id', 'slug', 'title', 'authors', 'number_of_volumes', 'tag', 'image'] def get_authors(self, obj): if obj.author: return [BookAuthorV2Serializer(obj.author, context=self.context).data] return [] def get_number_of_volumes(self, obj): return obj.volumes.count() def get_image(self, obj): if hasattr(obj, 'thumbnail') and obj.thumbnail: request = self.context.get('request') if request: return absolute_https_url(obj.thumbnail.url, request) return absolute_https_url(obj.thumbnail.url) first_volume = obj.volumes.first() if first_volume: if first_volume.image: request = self.context.get('request') if request: return absolute_https_url(first_volume.image.url, request) return absolute_https_url(first_volume.image.url) first_image = first_volume.images.order_by('order').first() if first_image and first_image.image: request = self.context.get('request') if request: return absolute_https_url(first_image.image.url, request) return absolute_https_url(first_image.image.url) return None def _get_book_editions(obj): seen = set() result = [] for ed in obj.editions.all(): seen.add(ed.id) result.append(ed) if hasattr(obj, 'editions_list'): for ed in obj.editions_list.all(): if ed.id not in seen: seen.add(ed.id) result.append(ed) return result class BookEditionV2Serializer(serializers.ModelSerializer): title = LocalizedField() publisher = serializers.SerializerMethodField() notes = LocalizedField() tag = BookTagV2Serializer(read_only=True) publication_place = serializers.SerializerMethodField() researchers = BookResearcherV2Serializer(many=True, read_only=True) editors = BookEditorV2Serializer(many=True, read_only=True) number_of_volumes = serializers.SerializerMethodField() class Meta: model = BookEdition fields = [ 'id', 'title', 'publisher', 'publication_place', 'edition_number', 'year_of_publication', 'number_of_volumes', 'notes', 'source_url', 'tag', 'researchers', 'editors' ] def get_publisher(self, obj): request = self.context.get("request") if getattr(obj, "publisher_item_id", None) and obj.publisher_item and obj.publisher_item.title: val = get_localized_text(obj.publisher_item.title, request) if val: return val return get_localized_text(obj.publisher, request) if obj.publisher else None def get_number_of_volumes(self, obj): count = obj.volumes.count() if count > 0: return count return obj.number_of_volumes or 0 def get_publication_place(self, obj): request = self.context.get("request") city_src = ( obj.city_of_publication_city.title if (getattr(obj, "city_of_publication_city_id", None) and obj.city_of_publication_city and obj.city_of_publication_city.title) else obj.city_of_publication ) country_src = ( obj.country_of_publication_country.title if (getattr(obj, "country_of_publication_country_id", None) and obj.country_of_publication_country and obj.country_of_publication_country.title) else obj.country_of_publication ) city = get_localized_text(city_src, request) if city_src else "" country = get_localized_text(country_src, request) if country_src else "" return { "city": city, "country": country } class BookVolumeV2Serializer(serializers.ModelSerializer): image = serializers.SerializerMethodField() file = serializers.SerializerMethodField() class Meta: model = BookVolume fields = ['id', 'title', 'image', 'file'] def get_image(self, obj): request = self.context.get('request') if obj.image: if request: return absolute_https_url(obj.image.url, request) return absolute_https_url(obj.image.url) first_image = obj.images.order_by('order').first() if first_image and first_image.image: if request: return absolute_https_url(first_image.image.url, request) return absolute_https_url(first_image.image.url) return None def get_file(self, obj): request = self.context.get('request') if obj.file: if request: return absolute_https_url(obj.file.url, request) return absolute_https_url(obj.file.url) first_doc = obj.documents.order_by('order').first() if first_doc and first_doc.file: if request: return absolute_https_url(first_doc.file.url, request) return absolute_https_url(first_doc.file.url) return None class BookReferenceV2DetailSerializer(serializers.ModelSerializer): title = LocalizedField() authors = serializers.SerializerMethodField() image = serializers.SerializerMethodField() tag = BookTagV2Serializer(read_only=True) information = serializers.SerializerMethodField() volumes = serializers.SerializerMethodField() class Meta: model = BookReference fields = ['id', 'title', 'authors', 'image', 'tag', 'information', 'volumes'] def get_authors(self, obj): if obj.author: return [BookAuthorV2Serializer(obj.author, context=self.context).data] return [] def get_image(self, obj): first_image = obj.images.filter(book_volume__isnull=True).order_by('order').first() if not first_image: first_image = obj.images.order_by('order').first() if first_image and first_image.image: request = self.context.get('request') if request: return absolute_https_url(first_image.image.url, request) return absolute_https_url(first_image.image.url) return None def get_information(self, obj): request = self.context.get("request") summary = get_localized_text(obj.description, request) if obj.description else "" all_editions = _get_book_editions(obj) valid_editions = [ed for ed in all_editions if ed.volumes.exists()] or all_editions editions = BookEditionV2Serializer(valid_editions, many=True, context=self.context).data return { "summary": summary, "editions": editions } def get_volumes(self, obj): if obj.has_editions: editions = _get_book_editions(obj) editions_data = [] request = self.context.get("request") for ed in editions: vol_qs = ed.volumes.all() if not vol_qs.exists(): continue ed_title = get_localized_text(ed.title, request) if ed.title else None editions_data.append({ "edition_id": ed.id, "edition_title": ed_title, "edition_statement": ed.edition_number or ed_title or f"Edition {ed.id}", "volumes": BookVolumeV2Serializer(vol_qs, many=True, context=self.context).data }) return { "has_editions": True, "editions_volumes": editions_data, "all_volumes": [] } else: return { "has_editions": False, "editions_volumes": [], "all_volumes": BookVolumeV2Serializer(obj.volumes.all(), many=True, context=self.context).data } class BookReferenceV2SyncSerializer(serializers.ModelSerializer): title = LocalizedField() authors = serializers.SerializerMethodField() attributes = BookAttributeSerializer(many=True, read_only=True) images = BookReferenceImageSerializer(many=True, read_only=True) information = serializers.SerializerMethodField() hadises = serializers.SerializerMethodField() volumes = serializers.SerializerMethodField() class Meta: model = BookReference fields = [ 'id', 'title', 'slug', 'share_link', 'rate', 'has_editions', 'authors', 'attributes', 'images', 'information', 'hadises', 'volumes' ] def get_authors(self, obj): if obj.author: return [BookAuthorV2Serializer(obj.author, context=self.context).data] return [] def get_information(self, obj): request = self.context.get("request") summary = get_localized_text(obj.description, request) if obj.description else "" all_editions = _get_book_editions(obj) valid_editions = [ed for ed in all_editions if ed.volumes.exists()] or all_editions editions = BookEditionV2Serializer(valid_editions, many=True, context=self.context).data return { "summary": summary, "editions": editions, "language": get_localized_text(obj.language, request) if obj.language else "", "number_page": obj.number_page } def get_hadises(self, obj): references = obj.hadis_references.all() hadis_list = [ref.hadis for ref in references if ref.hadis] return HadisShortSerializer(hadis_list, many=True, context=self.context).data def get_volumes(self, obj): if obj.has_editions: editions = _get_book_editions(obj) editions_data = [] request = self.context.get("request") for ed in editions: vol_qs = ed.volumes.all() if not vol_qs.exists(): continue ed_title = get_localized_text(ed.title, request) if ed.title else None editions_data.append({ "edition_id": ed.id, "edition_title": ed_title, "edition_statement": ed.edition_number or ed_title or f"Edition {ed.id}", "volumes": BookVolumeV2Serializer(vol_qs, many=True, context=self.context).data }) return { "has_editions": True, "editions_volumes": editions_data, "all_volumes": [] } else: return { "has_editions": False, "editions_volumes": [], "all_volumes": BookVolumeV2Serializer(obj.volumes.filter(edition__isnull=True), many=True, context=self.context).data } from apps.hadis.models.reference import BookAuthor, BookReference from .category import LocalizedField, ArabicLocalizedField, get_arabic_localized_text def _get_request_lang_v2(serializer_instance) -> str: from django.utils.translation import get_language request = serializer_instance.context.get("request") if hasattr(serializer_instance, "context") and serializer_instance.context else None lang = None if request: query_params = getattr(request, "query_params", None) or getattr(request, "GET", None) if query_params: lang = query_params.get("language_code") or query_params.get("lang") if not lang and hasattr(request, "headers"): lang = request.headers.get("Accept-Language") if not lang: lang = getattr(request, "LANGUAGE_CODE", None) if not lang: lang = get_language() or "en" code = str(lang).split(",")[0].split(";")[0].split("-")[0].strip().lower() return code if code in ("ru", "en", "ar") else "en" def _resolve_author_city(city_obj, fallback_json, request, arabic=False): if city_obj and getattr(city_obj, 'title', None): val = get_arabic_localized_text(city_obj.title) if arabic else get_localized_text(city_obj.title, request) if val: return val if fallback_json: return get_arabic_localized_text(fallback_json) if arabic else get_localized_text(fallback_json, request) return None def _resolve_author_features(obj, request, arabic=False): parts = [] for item in obj.features_list.all(): txt = get_arabic_localized_text(item.title) if arabic else get_localized_text(item.title, request) if txt and str(txt).strip(): parts.append(str(txt).strip()) if parts: sep = '، ' if arabic else ', ' return sep.join(parts) if obj.features: return get_arabic_localized_text(obj.features) if arabic else get_localized_text(obj.features, request) return None def _serialize_author_features_detail(obj, request): return [ { 'id': item.id, 'slug': item.slug, 'title': get_localized_text(item.title, request), 'arabic_title': get_arabic_localized_text(item.title), } for item in obj.features_list.all() ] class _BaseBookAuthorV2Mixin(serializers.Serializer): name = LocalizedField() arabic_name = ArabicLocalizedField(source='name') known_as = LocalizedField() arabic_known_as = ArabicLocalizedField(source='known_as') scholarity = LocalizedField() arabic_scholarity = ArabicLocalizedField(source='scholarity') tabaqa = LocalizedField() arabic_tabaqa = ArabicLocalizedField(source='tabaqa') rutba = serializers.SerializerMethodField() rutba_detail = serializers.SerializerMethodField() features = serializers.SerializerMethodField() arabic_features = serializers.SerializerMethodField() features_detail = serializers.SerializerMethodField() origin = serializers.SerializerMethodField() arabic_origin = serializers.SerializerMethodField() lived_in = serializers.SerializerMethodField() arabic_lived_in = serializers.SerializerMethodField() died_in = serializers.SerializerMethodField() arabic_died_in = serializers.SerializerMethodField() related_narrator_slug = serializers.CharField(source='related_narrator.slug', read_only=True, allow_null=True) birth_year_hijri = serializers.SerializerMethodField() death_year_hijri = serializers.SerializerMethodField() age_at_death = serializers.SerializerMethodField() birth_year_canonical = serializers.CharField(source='birth_year_hijri', read_only=True) death_year_canonical = serializers.CharField(source='death_year_hijri', read_only=True) age_at_death_canonical = serializers.CharField(source='age_at_death', read_only=True) def get_birth_year_hijri(self, obj): return obj.get_formatted_birth_year(lang=_get_request_lang_v2(self)) def get_death_year_hijri(self, obj): return obj.get_formatted_death_year(lang=_get_request_lang_v2(self)) def get_age_at_death(self, obj): return obj.get_formatted_age(lang=_get_request_lang_v2(self)) def get_rutba(self, obj): request = self.context.get('request') if obj.rutba_status: return get_localized_text(obj.rutba_status.title, request) return get_localized_text(obj.rutba, request) if obj.rutba else None def get_rutba_detail(self, obj): if obj.rutba_status: request = self.context.get('request') return { 'id': obj.rutba_status.id, 'title': get_localized_text(obj.rutba_status.title, request), 'slug': obj.rutba_status.slug, 'color': obj.rutba_status.color, 'main_color_code': obj.rutba_status.main_color_code, 'description': get_localized_text(obj.rutba_status.description, request), } return None def get_features(self, obj): return _resolve_author_features(obj, self.context.get('request'), arabic=False) def get_arabic_features(self, obj): return _resolve_author_features(obj, self.context.get('request'), arabic=True) def get_features_detail(self, obj): return _serialize_author_features_detail(obj, self.context.get('request')) def get_origin(self, obj): return _resolve_author_city(obj.origin_city, obj.origin, self.context.get('request'), arabic=False) def get_arabic_origin(self, obj): return _resolve_author_city(obj.origin_city, obj.origin, self.context.get('request'), arabic=True) def get_lived_in(self, obj): return _resolve_author_city(obj.lived_in_city, obj.lived_in, self.context.get('request'), arabic=False) def get_arabic_lived_in(self, obj): return _resolve_author_city(obj.lived_in_city, obj.lived_in, self.context.get('request'), arabic=True) def get_died_in(self, obj): return _resolve_author_city(obj.died_in_city, obj.died_in, self.context.get('request'), arabic=False) def get_arabic_died_in(self, obj): return _resolve_author_city(obj.died_in_city, obj.died_in, self.context.get('request'), arabic=True) class BookAuthorListSerializer(_BaseBookAuthorV2Mixin, serializers.ModelSerializer): """سریالایزر برای لیست نویسنده‌ها""" class Meta: model = BookAuthor fields = [ 'id', 'slug', 'name', 'arabic_name', 'image', 'known_as', 'arabic_known_as', 'scholarity', 'arabic_scholarity', 'tabaqa', 'arabic_tabaqa', 'rutba', 'rutba_detail', 'features', 'arabic_features', 'features_detail', 'origin', 'arabic_origin', 'lived_in', 'arabic_lived_in', 'died_in', 'arabic_died_in', 'birth_year_hijri', 'death_year_hijri', 'age_at_death', 'birth_year_canonical', 'death_year_canonical', 'age_at_death_canonical', 'birth_year_miladi', 'death_year_miladi', 'related_narrator_slug', ] class BookAuthorDetailSerializer(_BaseBookAuthorV2Mixin, serializers.ModelSerializer): """سریالایزر برای دیتیل یک نویسنده""" class Meta: model = BookAuthor fields = [ 'id', 'slug', 'name', 'arabic_name', 'image', 'known_as', 'arabic_known_as', 'scholarity', 'arabic_scholarity', 'tabaqa', 'arabic_tabaqa', 'rutba', 'rutba_detail', 'features', 'arabic_features', 'features_detail', 'origin', 'arabic_origin', 'lived_in', 'arabic_lived_in', 'died_in', 'arabic_died_in', 'birth_year_hijri', 'death_year_hijri', 'age_at_death', 'birth_year_canonical', 'death_year_canonical', 'age_at_death_canonical', 'birth_year_miladi', 'death_year_miladi', 'related_narrator_slug', ] class AuthorBookReferenceSerializer(serializers.ModelSerializer): """سریالایزر برای لیست رفرنس‌های (کتاب‌های) یک نویسنده""" title = LocalizedField() authors = serializers.SerializerMethodField() thumbnail = serializers.SerializerMethodField() class Meta: model = BookReference fields = ['id', 'slug', 'title', 'thumbnail', 'authors'] def get_authors(self, obj): field = LocalizedField() field._context = self.context return [field.to_representation(obj.author.name)] if obj.author else [] def get_thumbnail(self, obj): # گرفتن اولین عکس آپلود شده برای این رفرنس first_image = obj.images.order_by('order').first() if first_image and first_image.image: request = self.context.get('request') if request: return absolute_https_url(first_image.image.url, request) return absolute_https_url(first_image.image.url) return None # یک سریالایزر مینیمال برای کتاب‌ها داخل سینک class BookAuthorSyncReferenceSerializer(serializers.ModelSerializer): title = LocalizedField() class Meta: model = BookReference fields = ['id', 'slug', 'title'] class BookAuthorSyncSerializer(_BaseBookAuthorV2Mixin, serializers.ModelSerializer): """ سریالایزر برای سینک آفلاین تمام نویسنده‌ها. شامل تمام اطلاعات و لیست کتاب‌های متصل به این نویسنده. """ # اضافه کردن لیست کتاب‌ها books = BookAuthorSyncReferenceSerializer(source='book_references', many=True, read_only=True) class Meta: model = BookAuthor fields = [ 'id', 'slug', 'name', 'arabic_name', 'known_as', 'arabic_known_as', 'scholarity', 'arabic_scholarity', 'tabaqa', 'arabic_tabaqa', 'rutba', 'rutba_detail', 'features', 'arabic_features', 'features_detail', 'origin', 'arabic_origin', 'lived_in', 'arabic_lived_in', 'died_in', 'arabic_died_in', 'birth_year_hijri', 'death_year_hijri', 'age_at_death', 'birth_year_canonical', 'death_year_canonical', 'age_at_death_canonical', 'birth_year_miladi', 'death_year_miladi', 'books', # 👈 لیست کتاب‌های هر نویسنده 'related_narrator_slug' ]