from rest_framework import serializers import json from django.utils.translation import gettext_lazy as _ from rest_framework.fields import SerializerMethodField from urllib3 import request from .category import LocalizedField, ArabicLocalizedField from .category import get_localized_text, get_arabic_localized_text from .category import get_localized_text from apps.bookmark.serializers.bookmark import BookmarkStatusSerializer from ..models import ( Hadis, HadisStatus, HadisTag, HadisTransmitter, HadisReference, ReferenceImage, Transmitters, HadisCollection, TransmitterOpinion, TransmitterOriginalText, BookReference, BookReferenceImage, BookAuthor, HadisCorrection,HadisInterpretation , CorrectionReferenceImage , InterpretationReferenceImage , OriginalTextReferenceImage,CorrectionReference,InterpretationReference,OriginalTextReference ) def get_localized_correction_text(value, request=None): """ Resolve HadisCorrection.text in the same multilingual style used across the hadis app. Supports: - a parsed JSON list: [{"language_code": "en", "text": "..."}] - a JSON string containing that list - plain text fallback """ if value in (None, ""): return value parsed_value = value if isinstance(value, str): stripped = value.strip() if stripped.startswith("[") or stripped.startswith("{"): try: parsed_value = json.loads(stripped) except (TypeError, ValueError, json.JSONDecodeError): return value if isinstance(parsed_value, list): return get_localized_text(parsed_value, request=request) return value class PinnedHadisCollectionSerializer(serializers.ModelSerializer): title = LocalizedField() summary = LocalizedField() thumbnail = serializers.SerializerMethodField() class Meta: model = HadisCollection # فیلدهایی که برای نمایش در بخش پین شده (مثل اسلایدر یا لیست بالا) نیاز هست fields = ['id', 'title', 'slug', 'summary', 'thumbnail', 'order', 'created_at'] def get_thumbnail(self, obj): if obj.thumbnail: request = self.context.get('request') if request: return request.build_absolute_uri(obj.thumbnail.url) return obj.thumbnail.url return None class HadisCollectionListSerializer(serializers.ModelSerializer): thumbnail = serializers.SerializerMethodField() title = LocalizedField() summary = LocalizedField() class Meta: model = HadisCollection fields = ['id', 'title', 'summary','slug', 'thumbnail'] def get_thumbnail(self, obj): if obj.thumbnail: request = self.context.get('request') if request: return request.build_absolute_uri(obj.thumbnail.url) return obj.thumbnail.url return None class HadisSyncSerializer(serializers.ModelSerializer): """Serializer for syncing all hadis data (grouped fields)""" detail = serializers.SerializerMethodField() narrators = serializers.SerializerMethodField() explanations = serializers.SerializerMethodField() corrections = serializers.SerializerMethodField() category_slug = serializers.CharField(source='category.slug', read_only=True) interpretations = serializers.SerializerMethodField() title =LocalizedField() title_narrator = LocalizedField() translation = LocalizedField() class Meta: model = Hadis fields = [ # header (no-extend) 'id', 'number', 'slug', 'category_id', 'category_slug', 'title', 'title_narrator', 'text', 'translation', # grouped sections 'detail', 'narrators', 'explanations', 'corrections', 'interpretations' ] def get_detail(self, obj): request = self.context.get('request') # status status_block = None if obj.hadis_status: status_block = { 'id': obj.hadis_status.id, 'title': get_localized_text(obj.hadis_status.title, request), 'color': obj.hadis_status.color, 'main_color_code': obj.hadis_status.main_color_code, } # tags (already prefetched) tags_block = [ {'id': tag.id, 'title': get_localized_text(tag.title, request)} for tag in obj.tags.all() ] # references and reference images (already prefetched) references_block = [] reference_images_block = [] for reference in obj.references.all(): book = reference.book_reference authors = [book.author] if book and book.author else [] references_block.append({ 'id': reference.id, 'title': get_localized_text(book.title, request) if book else None, 'authors': [ {'id': a.id, 'name': get_localized_text(a.name, request)} for a in authors ], 'description': book.description if book else None, 'share_link': book.share_link if book else None, }) for img in reference.images.all(): thumb_url = None if img.thumbnail: thumb_url = request.build_absolute_uri(img.thumbnail.url) if request else img.thumbnail.url reference_images_block.append({ 'id': img.id, 'thumbnail': thumb_url, 'priority': img.priority, }) # Process address_details address_details_list = [] if hasattr(obj, 'address_details') and obj.address_details and isinstance(obj.address_details, list): address_details_list = sorted( obj.address_details, key=lambda x: x.get('priority', 0) ) return { 'address': get_localized_text(obj.address, request), 'address_details': address_details_list, 'hadis_status': status_block, 'status_text': get_localized_text(obj.hadis_status_text, request), 'share_link': obj.share_link, 'links': obj.links, 'tags': tags_block, 'references': references_block, 'reference_images': reference_images_block, } def get_narrators(self, obj): request = self.context.get('request') transmitters_data = [] for transmitter_rel in obj.transmitters.all(): t = transmitter_rel.transmitter layer = transmitter_rel.narrator_layer reliability_block = None if t.reliability: reliability_block = { 'id': t.reliability.id, 'title': get_localized_text(t.reliability.title, request), 'color': t.reliability.color, 'main_color_code': t.reliability.main_color_code, } transmitters_data.append({ 'id': t.id, 'name': get_localized_text(t.full_name, request), 'slug': t.slug, 'known_as': get_localized_text(t.known_as, request), 'nickname': get_localized_text(t.nickname, request), 'reliability': reliability_block, 'layer_level': layer.number if layer else None, 'layer_name': get_localized_text(layer.name, request) if layer else None, 'is_gap': transmitter_rel.is_gap, 'birth_year_hijri': t.birth_year_hijri, 'death_year_hijri': t.death_year_hijri, 'order': transmitter_rel.order, }) return { 'description': get_localized_text(obj.description, request), 'transmitters': transmitters_data, } def get_explanations(self, obj): request = self.context.get('request') # Return structured explanations with title and description explanations_list = [] if hasattr(obj, 'explanations') and obj.explanations and isinstance(obj.explanations, list): for item in obj.explanations: if isinstance(item, dict): lang = item.get('language_code') # Check if matches request language request_lang = request.query_params.get('lang', 'en') if request else 'en' if lang == request_lang: explanations_list.append({ 'title': item.get('title', ''), 'description': item.get('description', '') }) # If no match, return first available or fallback to old explanation field if not explanations_list and obj.explanation: return get_localized_text(obj.explanation, request) return explanations_list if explanations_list else None def get_corrections(self, obj): """گرفتن لیست کامل تصحیحات با تمام جزئیات و رفرنس‌ها""" # استفاده از سریالایزر Detail که در مرحله قبل ساختیم return HadisCorrectionDetailSerializer( obj.hadiscorrection_set.all(), many=True, context=self.context ).data def get_interpretations(self, obj): """گرفتن لیست کامل تفاسیر متصل به دسته‌بندی این حدیث با جزئیات کامل""" if obj.category: return HadisInterpretationDetailSerializer( obj.category.interpretations.all(), many=True, context=self.context ).data return [] class HadisListSerializer(serializers.ModelSerializer): """Serializer for Hadis list""" category = serializers.SerializerMethodField() status = serializers.SerializerMethodField() bookmark = serializers.SerializerMethodField() translation = LocalizedField() title = LocalizedField() title_narrator = LocalizedField() class Meta: model = Hadis fields = ['id', 'number', 'slug', 'title', 'title_narrator', 'text', 'translation', 'category', 'status', 'bookmark', 'share_link'] def get_category(self, obj): """Get category id and title""" if not obj.category: return None request = self.context.get('request') # Use your helper method title = get_localized_text(obj.category.title, request) or "" # Safe access for sect_type to prevent crashes if sect is missing # Note: Ensure your view uses .select_related('category__sect') for speed! sect_type = obj.category.sect.sect_type if obj.category.sect else None return { 'id': obj.category.id, 'title': title, 'slug': obj.category.slug, 'source_type': obj.category.source_type, 'sect_type': sect_type } def get_status(self, obj): """Get status id and title""" if not obj.hadis_status: return None request = self.context.get('request') # Use your helper method title = get_localized_text(obj.hadis_status.title, request) return { 'id': obj.hadis_status.id, 'title': title, 'color': obj.hadis_status.color, 'main_color_code': obj.hadis_status.main_color_code, } def get_bookmark(self, obj): """Get bookmark information for this playlist.""" request = self.context.get('request') user = request.user if request else None book_mark = BookmarkStatusSerializer.get_bookmark_info( obj=obj, user=user, service='hadith' ) return book_mark.get('is_bookmarked', False) class HadisStatusSerializer(serializers.ModelSerializer): """Serializer for HadisStatus""" title = LocalizedField() description = LocalizedField() main_color_code = serializers.ReadOnlyField() class Meta: model = HadisStatus fields = ['id', 'title', 'color', 'main_color_code', 'description'] class HadisTagSerializer(serializers.ModelSerializer): """Serializer for HadisTag""" title = LocalizedField() class Meta: model = HadisTag fields = ['id', 'title'] class TransmitterSerializer(serializers.ModelSerializer): """Serializer for Transmitters""" full_name = LocalizedField() arabic_full_name = ArabicLocalizedField(source='full_name') known_as = LocalizedField() arabic_known_as = ArabicLocalizedField(source='known_as') nickname = LocalizedField() arabic_nickname = ArabicLocalizedField(source='nickname') reliability = serializers.SerializerMethodField() share_link = serializers.CharField(read_only=True) class Meta: model = Transmitters fields = [ 'id', 'full_name', 'arabic_full_name', 'slug','birth_year_hijri', 'death_year_hijri', "known_as", 'arabic_known_as', 'nickname', 'arabic_nickname', 'reliability','madhhab','generation','share_link' ] def get_reliability(self, obj): """Serialize the reliability foreign key""" if obj.reliability: return { 'id': obj.reliability.id, 'title': get_localized_text(obj.reliability.title, self.context.get('request')), 'color': obj.reliability.color, 'main_color_code': obj.reliability.main_color_code, } return None class TransmitterShortSerializer(serializers.ModelSerializer): """Serializer for Transmitters""" full_name = LocalizedField() arabic_full_name = ArabicLocalizedField(source='full_name') known_as = LocalizedField() arabic_known_as = ArabicLocalizedField(source='known_as') nickname = LocalizedField() arabic_nickname = ArabicLocalizedField(source='nickname') class Meta: model = Transmitters fields = [ 'id', 'full_name', 'arabic_full_name', 'slug','birth_year_hijri', 'death_year_hijri', "known_as", 'arabic_known_as', 'nickname', 'arabic_nickname', 'reliability' ] class TransmitterOpinionSerializer(serializers.ModelSerializer): """ Serializer for TransmitterOpinions """ scholar_name = LocalizedField() opinion_text = LocalizedField() arabic_opinion_text = ArabicLocalizedField(source='opinion_text') status = serializers.SerializerMethodField() scholar = serializers.SerializerMethodField() class Meta: model = TransmitterOpinion fields = ['id','transmitter','scholar_name','scholar','opinion_text','arabic_opinion_text','status'] def get_scholar(self, obj): request = self.context.get('request') if obj.scholar: return { 'id': obj.scholar.id, 'name': get_localized_text(obj.scholar.name, request), 'slug': obj.scholar.slug } return None def get_status(self, obj): """Serialize the opinion status foreign key""" if obj.status: request = self.context.get('request') return { 'id': obj.status.id, 'title': get_localized_text(obj.status.title, request), 'slug': obj.status.slug, 'color': obj.status.color, 'main_color_code': obj.status.main_color_code, } return None class TransmitterOriginalTextSerializer(serializers.ModelSerializer): """ Serializer for TransmitterOriginalText """ title = LocalizedField() text = LocalizedField() translation =LocalizedField() class Meta: model = TransmitterOriginalText fields = ['id', 'title', 'text', 'translation', 'share_link', 'slug'] class TransmitterDetailSerializer(serializers.ModelSerializer): """ Serializer for Details of Transmitters """ full_name = LocalizedField() arabic_full_name = ArabicLocalizedField(source='full_name') known_as = LocalizedField() arabic_known_as = ArabicLocalizedField(source='known_as') nickname = LocalizedField() arabic_nickname = ArabicLocalizedField(source='nickname') kunya = LocalizedField() arabic_kunya = ArabicLocalizedField(source='kunya') origin = LocalizedField() arabic_origin = ArabicLocalizedField(source='origin') mawla_status = LocalizedField(source='freed_slave_of') arabic_mawla_status = ArabicLocalizedField(source='freed_slave_of') profession = LocalizedField(source='occupation') arabic_profession = ArabicLocalizedField(source='occupation') physical_traits = LocalizedField(source='features') arabic_physical_traits = ArabicLocalizedField(source='features') lived_in = LocalizedField() died_in = LocalizedField() description = LocalizedField() reliability = serializers.SerializerMethodField() relatives = serializers.SerializerMethodField() share_link = serializers.CharField(read_only=True) class Meta: model = Transmitters fields = [ 'id','full_name', 'arabic_full_name', 'kunya', 'arabic_kunya', 'known_as', 'arabic_known_as', 'nickname', 'arabic_nickname', 'origin', 'arabic_origin', 'mawla_status', 'arabic_mawla_status', 'profession', 'arabic_profession', 'physical_traits', 'arabic_physical_traits', 'lived_in','died_in','birth_year_hijri', 'death_year_hijri','age_at_death','reliability', 'madhhab',"in_sahih_muslim","in_sahih_bukhari", "description",'generation','share_link', 'tadlis', 'ikhtilat', 'companion_type', 'relatives' ] def get_reliability(self, obj): """Serialize the reliability foreign key""" if obj.reliability: return { 'id': obj.reliability.id, 'title': get_localized_text(obj.reliability.title, self.context.get('request')), 'color': obj.reliability.color, 'main_color_code': obj.reliability.main_color_code, } return None def get_relatives(self, obj): request = self.context.get('request') relatives_qs = obj.relatives.all() grouped = {} for rel in relatives_qs: rel_type = rel.relation_type slug = rel.narrator.slug if rel.narrator else None # Use narrator's localized name if exists and name is empty, otherwise use the direct name name = rel.name if rel.narrator and not name: name = get_localized_text(rel.narrator.full_name, request) item = { "name": name, "slug": slug } if rel_type not in grouped: grouped[rel_type] = { "relation_value": get_localized_text(rel.relation_value, request) if rel.relation_value else None, "arabic_relation_value": get_arabic_localized_text(rel.relation_value) if rel.relation_value else None, "items": [] } grouped[rel_type]["items"].append(item) result = [] for rel_type, data in grouped.items(): result.append({ "relation_type": rel_type, "relation_value": data["relation_value"], "arabic_relation_value": data["arabic_relation_value"], "members": data["items"] }) return result class TransmittersFiltersSerializer(serializers.ModelSerializer): pass class TransmitterSyncSerializer(serializers.ModelSerializer): """Serializer for syncing all transmitter data for offline mode""" biographical = serializers.SerializerMethodField() scholars_opinions = serializers.SerializerMethodField() original_texts = serializers.SerializerMethodField() full_name = LocalizedField() arabic_full_name = ArabicLocalizedField(source='full_name') hadis = serializers.SerializerMethodField() relations = serializers.SerializerMethodField() class Meta: model = Transmitters fields = [ 'id', 'full_name', 'arabic_full_name', 'slug', 'biographical', 'scholars_opinions', 'original_texts','hadis','relations' ] def get_biographical(self, obj): """Get biographical information (flattened)""" request = self.context.get('request') # ← FIX: Define request if obj.reliability: r= { 'id': obj.reliability.id, 'title': get_localized_text(obj.reliability.title, self.context.get('request')), 'color': obj.reliability.color, 'main_color_code': obj.reliability.main_color_code, } else : r= None return { 'full_name': get_localized_text(obj.full_name, request), 'arabic_full_name': get_arabic_localized_text(obj.full_name), 'kunya': get_localized_text(obj.kunya, request), 'arabic_kunya': get_arabic_localized_text(obj.kunya), 'known_as': get_localized_text(obj.known_as, request), 'arabic_known_as': get_arabic_localized_text(obj.known_as), 'nickname': get_localized_text(obj.nickname, request), 'arabic_nickname': get_arabic_localized_text(obj.nickname), 'origin': get_localized_text(obj.origin, request), 'arabic_origin': get_arabic_localized_text(obj.origin), 'mawla_status': get_localized_text(obj.freed_slave_of, request), 'arabic_mawla_status': get_arabic_localized_text(obj.freed_slave_of), 'profession': get_localized_text(obj.occupation, request), 'arabic_profession': get_arabic_localized_text(obj.occupation), 'physical_traits': get_localized_text(obj.features, request), 'arabic_physical_traits': get_arabic_localized_text(obj.features), 'lived_in': get_localized_text(obj.lived_in, request), 'died_in': get_localized_text(obj.died_in, request), 'birth_year_hijri': obj.birth_year_hijri, 'death_year_hijri': obj.death_year_hijri, 'age_at_death': obj.age_at_death, 'generation': obj.generation, 'reliability': r, 'madhhab': obj.madhhab, 'in_sahih_muslim': obj.in_sahih_muslim, 'in_sahih_bukhari': obj.in_sahih_bukhari, 'description': get_localized_text(obj.description, request), 'thumbnail': obj.thumbnail.url if obj.thumbnail else None, 'share_link': obj.share_link, 'tadlis': obj.tadlis, 'ikhtilat': obj.ikhtilat, 'companion_type': obj.companion_type, 'relatives': self._get_relatives(obj, request), } def _get_relatives(self, obj, request): relatives_qs = obj.relatives.all() grouped = {} for rel in relatives_qs: rel_type = rel.relation_type slug = rel.narrator.slug if rel.narrator else None name = rel.name if rel.narrator and not name: name = get_localized_text(rel.narrator.full_name, request) item = { "name": name, "slug": slug } if rel_type not in grouped: grouped[rel_type] = { "relation_value": get_localized_text(rel.relation_value, request) if rel.relation_value else None, "arabic_relation_value": get_arabic_localized_text(rel.relation_value) if rel.relation_value else None, "items": [] } grouped[rel_type]["items"].append(item) result = [] for rel_type, data in grouped.items(): result.append({ "relation_type": rel_type, "relation_value": data["relation_value"], "arabic_relation_value": data["arabic_relation_value"], "members": data["items"] }) return result def get_scholars_opinions(self, obj): """Get all scholarly opinions about this transmitter""" request = self.context.get('request') # ← FIX: Define request return [ { 'id': opinion.id, 'scholar_name': get_localized_text(opinion.scholar_name, request), 'arabic_scholar_name': get_arabic_localized_text(opinion.scholar_name), 'scholar': { 'id': opinion.scholar.id, 'name': get_localized_text(opinion.scholar.name, request), 'slug': opinion.scholar.slug } if opinion.scholar else None, 'opinion_text': get_localized_text(opinion.opinion_text, request), 'arabic_opinion_text': get_arabic_localized_text(opinion.opinion_text), 'status': { 'id': opinion.status.id, 'title': get_localized_text(opinion.status.title, request), 'slug': opinion.status.slug, 'color': opinion.status.color, 'main_color_code': opinion.status.main_color_code, } if opinion.status else None, 'created_at': opinion.created_at.isoformat() if opinion.created_at else None, 'updated_at': opinion.updated_at.isoformat() if opinion.updated_at else None, } for opinion in obj.opinions.all() # Already prefetched ] def get_original_texts(self, obj): """گرفتن متون اصلی راوی با جزئیات کامل و رفرنس‌ها""" # استفاده از سریالایزر Detail که در مرحله قبل ساختیم return TransmitterOriginalTextDetailSerializer( obj.originaltexts.all(), many=True, context=self.context ).data def get_hadis(self, obj): categories_map = self.context.get('categories_map', {}) return list(categories_map.get(int(obj.id), set())) def get_relations(self, obj): """ استخراج آنی آیدی اساتید و شاگردان از کانتکست بدون اجرای کوئری اضافه """ teachers_map = self.context.get('teachers_map', {}) students_map = self.context.get('students_map', {}) # حتماً آیدی را به عدد صحیح تبدیل می‌کنیم تا از مچ شدن با دیکشنری مطمئن شویم obj_id = int(obj.id) return { "teachers": list(teachers_map.get(obj_id, set())), "students": list(students_map.get(obj_id, set())) } class HadisTransmitterSerializer(serializers.ModelSerializer): """Serializer for HadisTransmitter with transmitter details""" transmitter = TransmitterShortSerializer(read_only=True) layer = serializers.SerializerMethodField() status = serializers.SerializerMethodField() class Meta: model = HadisTransmitter fields = [ 'id', 'order', 'is_gap','layer', 'transmitter', 'status' ] def get_layer(self, obj): """Get narrator layer slug""" return obj.narrator_layer.slug if obj.narrator_layer else None def get_status(self, obj): """Serialize the status foreign key""" if obj.status: request = self.context.get('request') return { 'id': obj.status.id, 'title': get_localized_text(obj.status.title, request), 'slug': obj.status.slug, 'color': obj.status.color, 'main_color_code': obj.status.main_color_code, } return None # serializers.py class HadisTransmitterListSerializer(serializers.ModelSerializer): """ The 'Parent' Serializer. It takes a HADIS object and returns the count + the list of transmitters. """ layer_count = serializers.SerializerMethodField() layer_names = serializers.SerializerMethodField() results = HadisTransmitterSerializer( source='transmitters', # Access the 'transmitters' reverse relation many=True, read_only=True ) class Meta: model = Hadis fields = ['id', 'layer_count','layer_names', 'results'] def get_layer_count(self, obj): # Calculate distinct layers efficiently return obj.transmitters.values('narrator_layer').distinct().count() def get_layer_names(self, obj): """Get list of localized narrator layer names""" request = self.context.get('request') # Import here to get actual objects from apps.hadis.models import NarratorLayer # Get ALL distinct narrator layer IDs for this hadis (not filtered) # This ensures layer names are returned regardless of layer filtering all_layers_for_hadis = HadisTransmitter.objects.filter( hadis=obj ).values_list('narrator_layer', flat=True).distinct() layer_objects = NarratorLayer.objects.filter(id__in=all_layers_for_hadis) # Extract localized names layer_names = [] for layer in layer_objects: name =get_localized_text(layer.name, request=request) description = get_localized_text(layer.description, request=request) slug = layer.slug layer_names.append({ 'name': name, 'slug': slug, 'description': description }) return layer_names class ReferenceImageSerializer(serializers.ModelSerializer): """Serializer for ReferenceImage""" thumbnail = serializers.SerializerMethodField() class Meta: model = ReferenceImage fields = ['id', 'thumbnail', 'priority'] def get_thumbnail(self, obj): """Get thumbnail URL""" if obj.thumbnail: request = self.context.get('request') if request: return request.build_absolute_uri(obj.thumbnail.url) return obj.thumbnail.url return None class HadisReferenceSerializer(serializers.ModelSerializer): """Serializer for HadisReference with book and images""" book_title = serializers.SerializerMethodField() book_authors = serializers.SerializerMethodField() book_description = serializers.SerializerMethodField() share_link = serializers.CharField(source='book_reference.share_link', read_only=True) class Meta: model = HadisReference fields = [ 'id', 'book_title', 'book_description','book_authors','share_link' ] # def get_type_name(self, obj): # """Get localized type name from book_reference.type""" # if obj.book_reference and obj.book_reference.type: # field = LocalizedField() # field._context = self.context # return field.to_representation(obj.book_reference.type.title) # return None # def get_subject_area(self, obj): # """Get list of localized subject area names""" # if obj.book_reference and obj.book_reference.subject_area.exists(): # subject_areas = [] # field = LocalizedField() # field._context = self.context # for subject in obj.book_reference.subject_area.all(): # subject_areas.append(field.to_representation(subject.title)) # return subject_areas # return [] # def get_book_english_name(self,obj): # english_name_data = obj.book_reference.title # for item in english_name_data: # if item.get('language_code') == 'en': # return item.get('text') # return None # def get_book_language(self,obj): # language_data = obj.book_reference.language # field = LocalizedField() # field._context = self.context # return field.to_representation(language_data) def get_book_title(self, obj): title_data = obj.book_reference.title field = LocalizedField() field._context = self.context return field.to_representation(title_data) def get_book_authors(self, obj): """Get list of localized author names""" try: # 1. Fetch the BookAuthor objects authors = [obj.book_reference.author] if obj.book_reference and obj.book_reference.author else [] if not authors: return [] # 2. Prepare your localized field helper field = LocalizedField() field._context = self.context # 3. Loop through authors and convert each object to a string (name) # Result will be like: ["Al-Bukhari", "Muslim"] out = [] for author in authors: name = field.to_representation(author.name) # Format birth and death dates (Hijri/CE) birth_hijri = author.birth_year_hijri birth_miladi = author.birth_year_miladi death_hijri = author.death_year_hijri death_miladi = author.death_year_miladi birth_str = "" if birth_hijri and birth_miladi: birth_str = f"{birth_hijri}AH/{birth_miladi}CE" elif birth_hijri: birth_str = f"{birth_hijri}AH" elif birth_miladi: birth_str = f"{birth_miladi}CE" death_str = "" if death_hijri and death_miladi: death_str = f"{death_hijri}AH/{death_miladi}CE" elif death_hijri: death_str = f"{death_hijri}AH" elif death_miladi: death_str = f"{death_miladi}CE" out.append({ 'name': name, 'birth': birth_str, 'death': death_str, }) return out except Exception: return [] def get_book_description(self, obj): description_data = obj.book_reference.description field = LocalizedField() field._context = self.context return field.to_representation(description_data) class HadisCorrectionSerializer(serializers.ModelSerializer): """Serializer for HadisCorrection""" title = LocalizedField() description = serializers.SerializerMethodField() translation = LocalizedField() bookmark = serializers.SerializerMethodField() class Meta: model = HadisCorrection fields = ['id', 'title','slug','narrator','description', 'translation','share_link','bookmark'] def get_description(self, obj): request = self.context.get('request') return get_localized_correction_text(obj.text, request=request) def get_bookmark(self, obj): """Get bookmark information for this correction.""" request = self.context.get('request') user = request.user if request else None book_mark = BookmarkStatusSerializer.get_bookmark_info( obj=obj, user=user, service='hadith_correction' ) return book_mark.get('is_bookmarked', False) class HadisBasicSerializer(serializers.ModelSerializer): """Basic serializer for Hadis with minimal information""" translation = LocalizedField() category = serializers.SerializerMethodField() bookmark= serializers.SerializerMethodField() title = LocalizedField() title_narrator = LocalizedField() explanation = LocalizedField() # explanations = serializers.SerializerMethodField() class Meta: model = Hadis fields = [ 'id', 'slug', 'title','bookmark', 'title_narrator', 'text', 'translation', 'share_link','explanation','category' ] # def get_explanations(self, obj): # """Get structured explanations for the requested language""" # request = self.context.get('request') # request_lang = request.query_params.get('lang', 'en') if request else 'en' # explanations_list = [] # if obj.explanations and isinstance(obj.explanations, list): # for item in obj.explanations: # if isinstance(item, dict) and item.get('language_code') == request_lang: # explanations_list.append({ # 'title': item.get('title', ''), # 'description': item.get('description', '') # }) # return explanations_list if explanations_list else None def get_category(self, obj): """Get category id and title""" if obj.category: request = self.context.get('request') return { 'id': obj.category.id, 'title': get_localized_text(obj.title,request), 'slug':obj.category.slug, 'source_type':obj.category.source_type, 'sect_type':obj.category.sect.sect_type } return None def get_bookmark(self, obj): """Get bookmark information for this playlist.""" request = self.context.get('request') user = request.user if request else None book_mark = BookmarkStatusSerializer.get_bookmark_info( obj=obj, user=user, service='hadith' ) return book_mark.get('is_bookmarked', False) class HadisShortSerializer(serializers.ModelSerializer): """Basic serializer for Hadis with minimal information""" translation = LocalizedField() title = LocalizedField() title_narrator = LocalizedField() class Meta: model = Hadis fields = [ 'id', 'slug', 'title', 'title_narrator', 'text', 'translation', 'share_link'] class HadisDetailSerializer(serializers.ModelSerializer): """Detailed serializer for Hadis with core information (transmitters and corrections moved to separate endpoints)""" hadis_status = HadisStatusSerializer(read_only=True) tags = HadisTagSerializer(many=True, read_only=True) references = HadisReferenceSerializer( many=True, read_only=True ) reference_images = SerializerMethodField() address = LocalizedField() category_slug = serializers.CharField(source='category.slug', read_only=True) # address_details = serializers.SerializerMethodField() class Meta: model = Hadis fields = [ 'id', 'number', 'slug', 'category_id', 'category_slug', 'hadis_status','links','share_link', 'tags','references','reference_images','address' ] # def get_address_details(self, obj): # """Get address details sorted by priority""" # if obj.address_details and isinstance(obj.address_details, list): # return sorted(obj.address_details, key=lambda x: x.get('priority', 0)) # return [] def get_reference_images(self, obj): """Get all reference images from all references""" all_images = [] for reference in obj.references.all(): images = reference.images.all().order_by('priority') serializer = ReferenceImageSerializer(images, many=True, context=self.context) all_images.extend(serializer.data) return all_images # def get_category(self, obj): # """Get category details""" # if obj.category: # return { # 'id': obj.category.id, # 'title': obj.category.title, # 'category_type': obj.category.content_type # } # return None def get_translation(self, obj): """Get translation based on request language""" request = self.context.get('request') language_code = getattr(request, 'LANGUAGE_CODE', 'en') return obj.translation.get(language_code) class NarratorLayerSerializer(serializers.Serializer): """Serializer for narrator layers information""" name = serializers.SerializerMethodField() slug = serializers.CharField(read_only=True) description = serializers.SerializerMethodField() def get_name(self, obj): """Get localized name""" request = self.context.get('request') return get_localized_text(obj.name, request=request) def get_description(self, obj): """Get localized description""" request = self.context.get('request') return get_localized_text(obj.description, request=request) class NarratorRelationSerializer(serializers.ModelSerializer): """ سریالایزر سبک برای نمایش راویان به عنوان استاد یا شاگرد """ full_name = LocalizedField() known_as = LocalizedField() reliability = SerializerMethodField() class Meta: model = Transmitters fields = ['id', 'slug', 'full_name', 'known_as', 'birth_year_hijri', 'death_year_hijri','reliability'] def get_reliability(self, obj): """Serialize the reliability foreign key""" if obj.reliability: return { 'id': obj.reliability.id, 'title': get_localized_text(obj.reliability.title, self.context.get('request')), 'color': obj.reliability.color, 'main_color_code': obj.reliability.main_color_code, } # --- سریالایزرهای کمکی تصاویر رفرنس --- class CorrectionReferenceImageSerializer(serializers.ModelSerializer): class Meta: model = CorrectionReferenceImage fields = ['id', 'image', 'priority'] class InterpretationReferenceImageSerializer(serializers.ModelSerializer): class Meta: model = InterpretationReferenceImage fields = ['id', 'image', 'priority'] class OriginalTextReferenceImageSerializer(serializers.ModelSerializer): class Meta: model = OriginalTextReferenceImage fields = ['id', 'image', 'priority'] # --- سریالایزرهای جامع رفرنس‌ها (شامل اطلاعات کتاب، ادیشن و ولوم) --- class BaseDetailedReferenceSerializer(serializers.ModelSerializer): book_title = serializers.SerializerMethodField() book_authors = serializers.SerializerMethodField() edition_info = serializers.SerializerMethodField() volume_info = serializers.SerializerMethodField() def get_book_title(self, obj): if obj.book_reference: return get_localized_text(obj.book_reference.title, self.context.get('request')) return None def get_book_authors(self, obj): if obj.book_reference: request = self.context.get('request') authors = [obj.book_reference.author] if obj.book_reference.author else [] return [ {"id": author.id, "name": get_localized_text(author.name, request)} for author in authors ] return [] def get_edition_info(self, obj): if obj.edition: return { "id": obj.edition.id, "edition_number": obj.edition.edition_number, "publisher": obj.edition.publisher } return None def get_volume_info(self, obj): if obj.book_volume: return { "id": obj.book_volume.id, "title": obj.book_volume.title } return None class DetailedCorrectionReferenceSerializer(BaseDetailedReferenceSerializer): images = CorrectionReferenceImageSerializer(many=True, read_only=True) class Meta: model = CorrectionReference fields = ['id', 'book_title', 'book_authors', 'edition_info', 'volume_info', 'hadith_number', 'volume', 'pages', 'address', 'url', 'images'] class DetailedInterpretationReferenceSerializer(BaseDetailedReferenceSerializer): images = InterpretationReferenceImageSerializer(many=True, read_only=True) class Meta: model = InterpretationReference fields = ['id', 'book_title', 'book_authors', 'edition_info', 'volume_info', 'hadith_number', 'volume', 'pages', 'address', 'url', 'images'] class DetailedOriginalTextReferenceSerializer(BaseDetailedReferenceSerializer): images = OriginalTextReferenceImageSerializer(many=True, read_only=True) class Meta: model = OriginalTextReference fields = ['id', 'book_title', 'book_authors', 'edition_info', 'volume_info', 'volume', 'pages', 'address', 'url', 'images'] # --- سریالایزرهای اصلی جزئیات (Detail Serializers) --- class HadisCorrectionDetailSerializer(serializers.ModelSerializer): title = LocalizedField() description = serializers.SerializerMethodField() translation = LocalizedField() references = DetailedCorrectionReferenceSerializer(many=True, read_only=True) hadis_info = serializers.SerializerMethodField() class Meta: model = HadisCorrection fields = ['id', 'title', 'slug', 'narrator', 'description', 'translation', 'share_link', 'hadis_info', 'references','links'] def get_description(self, obj): return get_localized_correction_text(obj.text, request=self.context.get('request')) def get_hadis_info(self, obj): return {"id": obj.hadis.id, "number": obj.hadis.number, "slug": obj.hadis.slug} class HadisInterpretationDetailSerializer(serializers.ModelSerializer): title = LocalizedField() translation = LocalizedField() references = DetailedInterpretationReferenceSerializer(many=True, read_only=True) category_info = serializers.SerializerMethodField() class Meta: model = HadisInterpretation fields = ['id', 'title', 'slug', 'narrator', 'text', 'translation', 'category_info', 'references','links'] def get_category_info(self, obj): return { "id": obj.category.id, "slug": obj.category.slug, "title": get_localized_text(obj.category.title, self.context.get('request')) } class TransmitterOriginalTextDetailSerializer(serializers.ModelSerializer): title = LocalizedField() text = LocalizedField() translation = LocalizedField() references = DetailedOriginalTextReferenceSerializer(many=True, read_only=True) transmitter_info = serializers.SerializerMethodField() class Meta: model = TransmitterOriginalText fields = ['id', 'slug', 'title', 'text', 'translation', 'share_link', 'transmitter_info', 'references'] def get_transmitter_info(self, obj): return { "id": obj.transmitter.id, "slug": obj.transmitter.slug, "name": get_localized_text(obj.transmitter.full_name, self.context.get('request')) }