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.
2345 lines
94 KiB
2345 lines
94 KiB
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 utils import absolute_https_url
|
|
|
|
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_request_language_code(request=None, fallback="ru") -> str:
|
|
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:
|
|
lang = getattr(request, "LANGUAGE_CODE", None)
|
|
if not lang and hasattr(request, "headers"):
|
|
lang = request.headers.get("Accept-Language")
|
|
elif not lang and hasattr(request, "META"):
|
|
lang = request.META.get("HTTP_ACCEPT_LANGUAGE")
|
|
if lang:
|
|
lang = lang.split(",")[0].split(";")[0].split("-")[0].strip().lower()
|
|
if not lang:
|
|
from django.utils.translation import get_language
|
|
lang = (get_language() or fallback).split("-")[0].lower()
|
|
if lang not in ("ru", "en", "ar"):
|
|
return "en"
|
|
return lang
|
|
|
|
|
|
def format_transmitter_date(transmitter_obj, date_type: str, request=None):
|
|
if not transmitter_obj:
|
|
return None
|
|
lang = get_request_language_code(request)
|
|
if hasattr(transmitter_obj, "get_formatted_date"):
|
|
return transmitter_obj.get_formatted_date(date_type, lang=lang)
|
|
return None
|
|
|
|
|
|
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()
|
|
hadis_ids = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = HadisCollection
|
|
# فیلدهایی که برای نمایش در بخش پین شده (مثل اسلایدر یا لیست بالا) نیاز هست
|
|
fields = ['id', 'title', 'slug', 'summary', 'thumbnail', 'order', 'created_at', 'hadis_ids']
|
|
|
|
def get_thumbnail(self, obj):
|
|
if obj.thumbnail:
|
|
request = self.context.get('request')
|
|
if request:
|
|
return absolute_https_url(obj.thumbnail.url, request)
|
|
return absolute_https_url(obj.thumbnail.url)
|
|
return None
|
|
|
|
def get_hadis_ids(self, obj):
|
|
# دریافت شناسه احادیث فعال متعلق به این کالکشن به ترتیب
|
|
if hasattr(obj, '_prefetched_objects_cache') and 'hadis_items' in obj._prefetched_objects_cache:
|
|
items = [
|
|
item.hadis_id for item in obj.hadis_items.all()
|
|
if getattr(getattr(item, 'hadis', None), 'status', True)
|
|
]
|
|
return items
|
|
return list(
|
|
obj.hadis_items.filter(hadis__status=True)
|
|
.order_by('order')
|
|
.values_list('hadis_id', flat=True)
|
|
)
|
|
|
|
class HadisCollectionListSerializer(serializers.ModelSerializer):
|
|
thumbnail = serializers.SerializerMethodField()
|
|
title = LocalizedField()
|
|
summary = LocalizedField()
|
|
hadis_ids = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = HadisCollection
|
|
fields = ['id', 'title', 'summary', 'slug', 'thumbnail', 'hadis_ids']
|
|
|
|
def get_thumbnail(self, obj):
|
|
if obj.thumbnail:
|
|
request = self.context.get('request')
|
|
if request:
|
|
return absolute_https_url(obj.thumbnail.url, request)
|
|
return absolute_https_url(obj.thumbnail.url)
|
|
return None
|
|
|
|
def get_hadis_ids(self, obj):
|
|
# دریافت شناسه احادیث فعال متعلق به این کالکشن به ترتیب
|
|
if hasattr(obj, '_prefetched_objects_cache') and 'hadis_items' in obj._prefetched_objects_cache:
|
|
items = [
|
|
item.hadis_id for item in obj.hadis_items.all()
|
|
if getattr(getattr(item, 'hadis', None), 'status', True)
|
|
]
|
|
return items
|
|
return list(
|
|
obj.hadis_items.filter(hadis__status=True)
|
|
.order_by('order')
|
|
.values_list('hadis_id', flat=True)
|
|
)
|
|
|
|
|
|
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 = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Hadis
|
|
fields = [
|
|
# header (no-extend)
|
|
'id', 'slug', 'category_id', 'category_slug', 'title', 'title_narrator', 'text', 'translation',
|
|
# grouped sections
|
|
'detail', 'narrators', 'explanations', 'corrections', 'interpretations'
|
|
]
|
|
|
|
def get_translation(self, obj):
|
|
return obj.translation or []
|
|
|
|
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),
|
|
'description': get_localized_text(obj.hadis_status.description, 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': None,
|
|
'share_link': book.share_link if book else None,
|
|
})
|
|
|
|
for img in reference.images.all():
|
|
thumb_url = None
|
|
if img.thumbnail:
|
|
thumb_url = absolute_https_url(img.thumbnail.url, request)
|
|
reference_images_block.append({
|
|
'id': img.id,
|
|
'thumbnail': thumb_url,
|
|
'priority': img.priority,
|
|
})
|
|
|
|
return {
|
|
'address': get_localized_text(obj.address, request),
|
|
'address_details': [],
|
|
'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 and t.reliability:
|
|
reliability_block = {
|
|
'id': t.reliability.id,
|
|
'title': get_localized_text(t.reliability.title, request),
|
|
'slug': t.reliability.slug,
|
|
'color': t.reliability.color,
|
|
'main_color_code': t.reliability.main_color_code,
|
|
'description': get_localized_text(t.reliability.description, request),
|
|
}
|
|
|
|
uncertain_list = []
|
|
if transmitter_rel.is_uncertain:
|
|
for ut in transmitter_rel.uncertain_transmitters.all():
|
|
ut_rel_block = None
|
|
if ut.reliability:
|
|
ut_rel_block = {
|
|
'id': ut.reliability.id,
|
|
'title': get_localized_text(ut.reliability.title, request),
|
|
'slug': ut.reliability.slug,
|
|
'color': ut.reliability.color,
|
|
'main_color_code': ut.reliability.main_color_code,
|
|
'description': get_localized_text(ut.reliability.description, request),
|
|
}
|
|
uncertain_list.append({
|
|
'id': ut.id,
|
|
'name': get_localized_text(ut.full_name, request),
|
|
'slug': ut.slug,
|
|
'gender': ut.gender,
|
|
'known_as': get_localized_text(ut.known_as, request),
|
|
'nickname': get_localized_text(ut.nickname, request),
|
|
'birth_year_hijri': format_transmitter_date(ut, 'birth', request),
|
|
'death_year_hijri': format_transmitter_date(ut, 'death', request),
|
|
'birth_year_raw': ut.birth_year_hijri,
|
|
'death_year_raw': ut.death_year_hijri,
|
|
'reliability': ut_rel_block,
|
|
})
|
|
|
|
transmitters_data.append({
|
|
'id': t.id if t else None,
|
|
'name': get_localized_text(t.full_name, request) if t else None,
|
|
'slug': t.slug if t else None,
|
|
'gender': t.gender if t else None,
|
|
'known_as': get_localized_text(t.known_as, request) if t else None,
|
|
'nickname': get_localized_text(t.nickname, request) if t else None,
|
|
'reliability': reliability_block,
|
|
'layer_id': layer.id if layer else None,
|
|
'narrator_layer_id': layer.id if layer else None,
|
|
'layer_level': layer.number if layer else None,
|
|
'layer_name': get_localized_text(layer.name, request) if layer else None,
|
|
'layer_description': get_localized_text(layer.description, request) if layer else None,
|
|
'is_gap': transmitter_rel.is_gap,
|
|
'is_uncertain': transmitter_rel.is_uncertain,
|
|
'uncertain_transmitters': uncertain_list,
|
|
'birth_year_hijri': format_transmitter_date(t, 'birth', request) if t else None,
|
|
'death_year_hijri': format_transmitter_date(t, 'death', request) if t else None,
|
|
'birth_year_raw': t.birth_year_hijri if t else None,
|
|
'death_year_raw': t.death_year_hijri if t else None,
|
|
'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):
|
|
"""گرفتن لیست تصحیحات بهینهشده مخصوص سینک"""
|
|
return HadisSyncCorrectionSerializer(
|
|
obj.hadiscorrection_set.all(),
|
|
many=True,
|
|
context=self.context
|
|
).data
|
|
|
|
def get_interpretations(self, obj):
|
|
"""گرفتن لیست تفاسیر بهینهشده مخصوص سینک"""
|
|
if obj.category:
|
|
return HadisSyncInterpretationSerializer(
|
|
obj.category.interpretations.all(),
|
|
many=True,
|
|
context=self.context
|
|
).data
|
|
return []
|
|
|
|
class HadisTagSerializer(serializers.ModelSerializer):
|
|
"""Serializer for HadisTag"""
|
|
title = LocalizedField()
|
|
|
|
class Meta:
|
|
model = HadisTag
|
|
fields = ['id', 'title']
|
|
|
|
|
|
class HadisListSerializer(serializers.ModelSerializer):
|
|
"""Serializer for Hadis list"""
|
|
category = serializers.SerializerMethodField()
|
|
status = serializers.SerializerMethodField()
|
|
bookmark = serializers.SerializerMethodField()
|
|
tags = HadisTagSerializer(many=True, read_only=True)
|
|
translation = LocalizedField()
|
|
title = LocalizedField()
|
|
title_narrator = LocalizedField()
|
|
address = serializers.SerializerMethodField()
|
|
links = serializers.SerializerMethodField()
|
|
references = serializers.SerializerMethodField()
|
|
reference_images = serializers.SerializerMethodField()
|
|
images = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Hadis
|
|
fields = ['id', 'number', 'slug', 'title', 'title_narrator', 'text',
|
|
'translation', 'category', 'status', 'bookmark', 'share_link',
|
|
'address', 'links', 'references', 'reference_images', 'images', 'tags']
|
|
|
|
def get_references(self, obj):
|
|
refs = obj.references.all()
|
|
return DetailedHadisReferenceSerializer(refs, many=True, context=self.context).data
|
|
|
|
def get_address(self, obj):
|
|
formatted = format_address_output(obj.address)
|
|
if formatted:
|
|
return formatted
|
|
refs = list(obj.references.all())
|
|
first_ref = refs[0] if refs else None
|
|
if first_ref and hasattr(first_ref, 'address') and first_ref.address:
|
|
return format_address_output(first_ref.address)
|
|
return []
|
|
|
|
def get_links(self, obj):
|
|
return format_links_output(obj.links)
|
|
|
|
def get_reference_images(self, obj):
|
|
request = self.context.get('request')
|
|
images_list = []
|
|
for ref in obj.references.all():
|
|
for img in sorted(ref.images.all(), key=lambda x: getattr(x, 'priority', 0)):
|
|
url = None
|
|
if img.thumbnail:
|
|
url = absolute_https_url(img.thumbnail.url, request) if request else absolute_https_url(img.thumbnail.url)
|
|
elif hasattr(img, 'image') and img.image:
|
|
url = absolute_https_url(img.image.url, request) if request else absolute_https_url(img.image.url)
|
|
if url:
|
|
images_list.append({
|
|
"id": img.id,
|
|
"image": url,
|
|
"thumbnail": url,
|
|
"priority": img.priority,
|
|
})
|
|
return images_list
|
|
|
|
def get_images(self, obj):
|
|
return self.get_reference_images(obj)
|
|
|
|
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)
|
|
description = get_localized_text(obj.hadis_status.description, request)
|
|
|
|
return {
|
|
'id': obj.hadis_status.id,
|
|
'title': title,
|
|
'description': description,
|
|
'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 TransmitterSerializer(serializers.ModelSerializer):
|
|
"""Serializer for Transmitters"""
|
|
full_name = LocalizedField()
|
|
arabic_full_name = ArabicLocalizedField(source='full_name')
|
|
kunya = LocalizedField()
|
|
arabic_kunya = ArabicLocalizedField(source='kunya')
|
|
known_as = LocalizedField()
|
|
arabic_known_as = ArabicLocalizedField(source='known_as')
|
|
nickname = LocalizedField()
|
|
arabic_nickname = ArabicLocalizedField(source='nickname')
|
|
birth_year_hijri = serializers.SerializerMethodField()
|
|
death_year_hijri = serializers.SerializerMethodField()
|
|
age_at_death = serializers.SerializerMethodField()
|
|
birth_year_raw = serializers.CharField(source='birth_year_hijri', read_only=True)
|
|
death_year_raw = serializers.CharField(source='death_year_hijri', read_only=True)
|
|
age_at_death_raw = serializers.CharField(source='age_at_death', read_only=True)
|
|
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', 'age_at_death',
|
|
'birth_year_raw', 'death_year_raw', 'age_at_death_raw',
|
|
'kunya', 'arabic_kunya', 'known_as', 'arabic_known_as', 'nickname', 'arabic_nickname',
|
|
'reliability', 'madhhab', 'generation', 'share_link', 'gender'
|
|
]
|
|
|
|
def get_birth_year_hijri(self, obj):
|
|
return format_transmitter_date(obj, 'birth', self.context.get('request'))
|
|
|
|
def get_death_year_hijri(self, obj):
|
|
return format_transmitter_date(obj, 'death', self.context.get('request'))
|
|
|
|
def get_age_at_death(self, obj):
|
|
return format_transmitter_date(obj, 'age', self.context.get('request'))
|
|
|
|
def get_reliability(self, obj):
|
|
"""Serialize the reliability foreign key"""
|
|
if obj.reliability:
|
|
request = self.context.get('request')
|
|
return {
|
|
'id': obj.reliability.id,
|
|
'title': get_localized_text(obj.reliability.title, request),
|
|
'slug': obj.reliability.slug,
|
|
'color': obj.reliability.color,
|
|
'main_color_code': obj.reliability.main_color_code,
|
|
'description': get_localized_text(obj.reliability.description, request),
|
|
}
|
|
return None
|
|
|
|
class TransmitterShortSerializer(serializers.ModelSerializer):
|
|
"""Serializer for Transmitters"""
|
|
full_name = LocalizedField()
|
|
arabic_full_name = ArabicLocalizedField(source='full_name')
|
|
kunya = LocalizedField()
|
|
arabic_kunya = ArabicLocalizedField(source='kunya')
|
|
known_as = LocalizedField()
|
|
arabic_known_as = ArabicLocalizedField(source='known_as')
|
|
nickname = LocalizedField()
|
|
arabic_nickname = ArabicLocalizedField(source='nickname')
|
|
birth_year_hijri = serializers.SerializerMethodField()
|
|
death_year_hijri = serializers.SerializerMethodField()
|
|
birth_year_raw = serializers.CharField(source='birth_year_hijri', read_only=True)
|
|
death_year_raw = serializers.CharField(source='death_year_hijri', read_only=True)
|
|
reliability = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Transmitters
|
|
fields = [
|
|
'id', 'full_name', 'arabic_full_name', 'slug',
|
|
'birth_year_hijri', 'death_year_hijri', 'birth_year_raw', 'death_year_raw',
|
|
'kunya', 'arabic_kunya', 'known_as', 'arabic_known_as', 'nickname', 'arabic_nickname', 'reliability', 'gender'
|
|
]
|
|
|
|
def get_birth_year_hijri(self, obj):
|
|
return format_transmitter_date(obj, 'birth', self.context.get('request'))
|
|
|
|
def get_death_year_hijri(self, obj):
|
|
return format_transmitter_date(obj, 'death', self.context.get('request'))
|
|
|
|
def get_reliability(self, obj):
|
|
if obj.reliability:
|
|
request = self.context.get('request')
|
|
return {
|
|
'id': obj.reliability.id,
|
|
'title': get_localized_text(obj.reliability.title, request),
|
|
'slug': obj.reliability.slug,
|
|
'color': obj.reliability.color,
|
|
'main_color_code': obj.reliability.main_color_code,
|
|
'description': get_localized_text(obj.reliability.description, request),
|
|
}
|
|
return None
|
|
class TransmitterOpinionSerializer(serializers.ModelSerializer):
|
|
""" Serializer for TransmitterOpinions """
|
|
scholar_name = LocalizedField()
|
|
opinion_text = serializers.SerializerMethodField()
|
|
arabic_opinion_text = serializers.SerializerMethodField()
|
|
status = serializers.SerializerMethodField()
|
|
scholar = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = TransmitterOpinion
|
|
fields = ['id','transmitter','scholar_name','scholar','opinion_text','arabic_opinion_text','status']
|
|
|
|
def get_opinion_text(self, obj):
|
|
request = self.context.get('request')
|
|
source_text = obj.opinion.opinion_text if (obj.opinion_id and obj.opinion) else obj.opinion_text
|
|
return get_localized_text(source_text, request)
|
|
|
|
def get_arabic_opinion_text(self, obj):
|
|
source_text = obj.opinion.opinion_text if (obj.opinion_id and obj.opinion) else obj.opinion_text
|
|
return get_arabic_localized_text(source_text)
|
|
|
|
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"""
|
|
status_obj = (obj.opinion.status if (obj.opinion_id and obj.opinion and obj.opinion.status) else obj.status)
|
|
if status_obj:
|
|
request = self.context.get('request')
|
|
return {
|
|
'id': status_obj.id,
|
|
'title': get_localized_text(status_obj.title, request),
|
|
'slug': status_obj.slug,
|
|
'color': status_obj.color,
|
|
'main_color_code': status_obj.main_color_code,
|
|
'description': get_localized_text(status_obj.description, request),
|
|
}
|
|
return None
|
|
|
|
def format_address_output(ref_or_data):
|
|
if not ref_or_data:
|
|
return []
|
|
|
|
if hasattr(ref_or_data, 'address'):
|
|
addr = ref_or_data.address
|
|
else:
|
|
addr = ref_or_data
|
|
|
|
if not addr:
|
|
return []
|
|
|
|
if isinstance(addr, str):
|
|
trimmed = addr.strip()
|
|
if trimmed.startswith("[") or trimmed.startswith("{"):
|
|
try:
|
|
import json
|
|
parsed = json.loads(trimmed)
|
|
return format_address_output(parsed)
|
|
except (ValueError, TypeError):
|
|
pass
|
|
return [trimmed] if trimmed else []
|
|
|
|
elif isinstance(addr, list):
|
|
result = []
|
|
for item in addr:
|
|
if isinstance(item, str):
|
|
s = item.strip()
|
|
if s.startswith("[") or s.startswith("{"):
|
|
try:
|
|
import json
|
|
parsed = json.loads(s)
|
|
result.extend(format_address_output(parsed))
|
|
continue
|
|
except (ValueError, TypeError):
|
|
pass
|
|
if s:
|
|
result.append(s)
|
|
elif isinstance(item, dict):
|
|
# could be {'title': ['pok'], 'language_code': 'fa'} or {'title': 'pok'} or {'address': '...'} or {'text': '...'}
|
|
val = item.get('title') or item.get('address') or item.get('text') or item.get('value')
|
|
if val:
|
|
result.extend(format_address_output(val))
|
|
elif isinstance(item, list):
|
|
result.extend(format_address_output(item))
|
|
return result
|
|
return [str(addr)]
|
|
|
|
|
|
from apps.hadis.utils.links import format_links_output, extract_domain_from_url
|
|
|
|
|
|
class TransmitterOriginalTextSerializer(serializers.ModelSerializer):
|
|
""" Serializer for TransmitterOriginalText """
|
|
title = LocalizedField()
|
|
text = serializers.CharField()
|
|
translation = LocalizedField()
|
|
address = serializers.SerializerMethodField()
|
|
images = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = TransmitterOriginalText
|
|
fields = ['id', 'title', 'text', 'translation', 'share_link', 'slug', 'address', 'images']
|
|
|
|
def get_address(self, obj):
|
|
refs = list(obj.references.all())
|
|
return format_address_output(refs[0] if refs else None)
|
|
|
|
def get_images(self, obj):
|
|
request = self.context.get('request')
|
|
images_list = []
|
|
for ref in obj.references.all():
|
|
for img in ref.images.all():
|
|
if img.image:
|
|
url = absolute_https_url(img.image.url, request) if request else absolute_https_url(img.image.url)
|
|
images_list.append({
|
|
"id": img.id,
|
|
"image": url,
|
|
"thumbnail": url,
|
|
})
|
|
return images_list
|
|
|
|
|
|
def _serialize_transmitter_m2m_items(items, request, include_description=False):
|
|
result = []
|
|
for item in items:
|
|
entry = {
|
|
'id': item.id,
|
|
'slug': getattr(item, 'slug', None),
|
|
'title': get_localized_text(item.title, request),
|
|
'arabic_title': get_arabic_localized_text(item.title),
|
|
}
|
|
if include_description:
|
|
entry['description'] = get_localized_text(getattr(item, 'description', None), request)
|
|
result.append(entry)
|
|
return result
|
|
|
|
|
|
def _resolve_transmitter_m2m_text(items, fallback_json, request, arabic=False, secondary_fallback_json=None):
|
|
parts = []
|
|
for item in items:
|
|
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 fallback_json:
|
|
val = get_arabic_localized_text(fallback_json) if arabic else get_localized_text(fallback_json, request)
|
|
if val:
|
|
return val
|
|
if secondary_fallback_json:
|
|
val = get_arabic_localized_text(secondary_fallback_json) if arabic else get_localized_text(secondary_fallback_json, request)
|
|
if val:
|
|
return val
|
|
return None
|
|
|
|
|
|
def _resolve_transmitter_city_text(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 _serialize_transmitter_relatives(obj, request):
|
|
from django.db.models import Q
|
|
from ..models.transmitter import RelationType
|
|
|
|
relatives_qs = obj.relatives.select_related('relation_type_ref', 'narrator').all()
|
|
|
|
grouped = {}
|
|
for rel in relatives_qs:
|
|
rt_obj = rel.relation_type_ref
|
|
if not rt_obj and rel.relation_type:
|
|
rt_obj = RelationType.objects.filter(
|
|
Q(code__iexact=rel.relation_type) | Q(slug__iexact=rel.relation_type)
|
|
).first()
|
|
|
|
rel_type = (rt_obj.code or rt_obj.slug or rel.relation_type) if rt_obj else rel.relation_type
|
|
rel_val_src = (rt_obj.title if (rt_obj and rt_obj.title) else rel.relation_value)
|
|
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)
|
|
arabic_name = (
|
|
get_arabic_localized_text(rel.narrator.full_name)
|
|
if rel.narrator
|
|
else rel.name
|
|
) or name
|
|
|
|
item = {
|
|
"id": rel.narrator_id,
|
|
"name": name,
|
|
"arabic_name": arabic_name,
|
|
"slug": slug,
|
|
}
|
|
|
|
if rel_type not in grouped:
|
|
grouped[rel_type] = {
|
|
"relation_value": get_localized_text(rel_val_src, request) if rel_val_src else None,
|
|
"arabic_relation_value": get_arabic_localized_text(rel_val_src) if rel_val_src 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 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 = serializers.SerializerMethodField()
|
|
arabic_kunya = serializers.SerializerMethodField()
|
|
kunyas = serializers.SerializerMethodField()
|
|
origin = serializers.SerializerMethodField()
|
|
arabic_origin = serializers.SerializerMethodField()
|
|
|
|
mawla_status = LocalizedField(source='freed_slave_of')
|
|
arabic_mawla_status = ArabicLocalizedField(source='freed_slave_of')
|
|
profession = serializers.SerializerMethodField()
|
|
arabic_profession = serializers.SerializerMethodField()
|
|
professions = serializers.SerializerMethodField()
|
|
physical_traits = serializers.SerializerMethodField()
|
|
arabic_physical_traits = serializers.SerializerMethodField()
|
|
features = serializers.SerializerMethodField()
|
|
|
|
lived_in = serializers.SerializerMethodField()
|
|
arabic_lived_in = serializers.SerializerMethodField()
|
|
died_in = serializers.SerializerMethodField()
|
|
arabic_died_in = serializers.SerializerMethodField()
|
|
description = LocalizedField()
|
|
companion_type = serializers.SerializerMethodField()
|
|
arabic_companion_type = serializers.SerializerMethodField()
|
|
sahabi_status = serializers.SerializerMethodField()
|
|
madhhab = serializers.SerializerMethodField()
|
|
madhhabs = serializers.SerializerMethodField()
|
|
birth_year_hijri = serializers.SerializerMethodField()
|
|
death_year_hijri = serializers.SerializerMethodField()
|
|
age_at_death = serializers.SerializerMethodField()
|
|
birth_year_raw = serializers.CharField(source='birth_year_hijri', read_only=True)
|
|
death_year_raw = serializers.CharField(source='death_year_hijri', read_only=True)
|
|
age_at_death_raw = serializers.CharField(source='age_at_death', read_only=True)
|
|
reliability = serializers.SerializerMethodField()
|
|
relatives = serializers.SerializerMethodField()
|
|
father = serializers.SerializerMethodField()
|
|
sons = serializers.SerializerMethodField()
|
|
share_link = serializers.CharField(read_only=True)
|
|
|
|
class Meta:
|
|
model = Transmitters
|
|
fields = [
|
|
'id', 'full_name', 'arabic_full_name', 'kunya', 'arabic_kunya', 'kunyas',
|
|
'known_as', 'arabic_known_as', 'nickname', 'arabic_nickname',
|
|
'origin', 'arabic_origin', 'mawla_status', 'arabic_mawla_status',
|
|
'profession', 'arabic_profession', 'professions',
|
|
'physical_traits', 'arabic_physical_traits', 'features',
|
|
'lived_in', 'arabic_lived_in', 'died_in', 'arabic_died_in',
|
|
'birth_year_hijri', 'death_year_hijri', 'age_at_death',
|
|
'birth_year_raw', 'death_year_raw', 'age_at_death_raw',
|
|
'reliability',
|
|
'madhhab', 'madhhabs', "in_sahih_muslim", "in_sahih_bukhari",
|
|
"description", 'generation', 'share_link',
|
|
'tadlis', 'ikhtilat', 'companion_type', 'arabic_companion_type', 'sahabi_status', 'relatives',
|
|
'father', 'sons', 'gender'
|
|
]
|
|
|
|
def get_kunya(self, obj):
|
|
return _resolve_transmitter_m2m_text(obj.kunyas.all(), obj.kunya, self.context.get('request'), arabic=False)
|
|
|
|
def get_arabic_kunya(self, obj):
|
|
return _resolve_transmitter_m2m_text(obj.kunyas.all(), obj.kunya, self.context.get('request'), arabic=True)
|
|
|
|
def get_kunyas(self, obj):
|
|
return _serialize_transmitter_m2m_items(obj.kunyas.all(), self.context.get('request'))
|
|
|
|
def get_profession(self, obj):
|
|
return _resolve_transmitter_m2m_text(
|
|
obj.professions_list.all(), obj.profession, self.context.get('request'),
|
|
arabic=False, secondary_fallback_json=obj.occupation
|
|
)
|
|
|
|
def get_arabic_profession(self, obj):
|
|
return _resolve_transmitter_m2m_text(
|
|
obj.professions_list.all(), obj.profession, self.context.get('request'),
|
|
arabic=True, secondary_fallback_json=obj.occupation
|
|
)
|
|
|
|
def get_professions(self, obj):
|
|
return _serialize_transmitter_m2m_items(obj.professions_list.all(), self.context.get('request'))
|
|
|
|
def get_physical_traits(self, obj):
|
|
return _resolve_transmitter_m2m_text(obj.features_list.all(), obj.features, self.context.get('request'), arabic=False)
|
|
|
|
def get_arabic_physical_traits(self, obj):
|
|
return _resolve_transmitter_m2m_text(obj.features_list.all(), obj.features, self.context.get('request'), arabic=True)
|
|
|
|
def get_features(self, obj):
|
|
return _serialize_transmitter_m2m_items(obj.features_list.all(), self.context.get('request'))
|
|
|
|
def get_origin(self, obj):
|
|
return _resolve_transmitter_city_text(obj.origin_city, obj.origin, self.context.get('request'), arabic=False)
|
|
|
|
def get_arabic_origin(self, obj):
|
|
return _resolve_transmitter_city_text(obj.origin_city, obj.origin, self.context.get('request'), arabic=True)
|
|
|
|
def get_lived_in(self, obj):
|
|
return _resolve_transmitter_city_text(obj.lived_in_city, obj.lived_in, self.context.get('request'), arabic=False)
|
|
|
|
def get_arabic_lived_in(self, obj):
|
|
return _resolve_transmitter_city_text(obj.lived_in_city, obj.lived_in, self.context.get('request'), arabic=True)
|
|
|
|
def get_died_in(self, obj):
|
|
return _resolve_transmitter_city_text(obj.died_in_city, obj.died_in, self.context.get('request'), arabic=False)
|
|
|
|
def get_arabic_died_in(self, obj):
|
|
return _resolve_transmitter_city_text(obj.died_in_city, obj.died_in, self.context.get('request'), arabic=True)
|
|
|
|
def get_companion_type(self, obj):
|
|
request = self.context.get('request')
|
|
if obj.sahabi_status and obj.sahabi_status.title:
|
|
val = get_localized_text(obj.sahabi_status.title, request)
|
|
if val:
|
|
return val
|
|
return get_localized_text(obj.companion_type, request)
|
|
|
|
def get_arabic_companion_type(self, obj):
|
|
if obj.sahabi_status and obj.sahabi_status.title:
|
|
val = get_arabic_localized_text(obj.sahabi_status.title)
|
|
if val:
|
|
return val
|
|
return get_arabic_localized_text(obj.companion_type)
|
|
|
|
def get_sahabi_status(self, obj):
|
|
if obj.sahabi_status:
|
|
request = self.context.get('request')
|
|
return {
|
|
'id': obj.sahabi_status.id,
|
|
'slug': obj.sahabi_status.slug,
|
|
'title': get_localized_text(obj.sahabi_status.title, request),
|
|
'arabic_title': get_arabic_localized_text(obj.sahabi_status.title),
|
|
'description': get_localized_text(obj.sahabi_status.description, request),
|
|
}
|
|
return None
|
|
|
|
def get_madhhab(self, obj):
|
|
if obj.madhhab:
|
|
return obj.madhhab
|
|
request = self.context.get('request')
|
|
m_items = list(obj.madhhabs.all())
|
|
if m_items:
|
|
return get_localized_text(m_items[0].title, request) or m_items[0].slug
|
|
return None
|
|
|
|
def get_madhhabs(self, obj):
|
|
return _serialize_transmitter_m2m_items(obj.madhhabs.all(), self.context.get('request'), include_description=True)
|
|
|
|
def get_birth_year_hijri(self, obj):
|
|
return format_transmitter_date(obj, 'birth', self.context.get('request'))
|
|
|
|
def get_death_year_hijri(self, obj):
|
|
return format_transmitter_date(obj, 'death', self.context.get('request'))
|
|
|
|
def get_age_at_death(self, obj):
|
|
return format_transmitter_date(obj, 'age', self.context.get('request'))
|
|
|
|
def get_father(self, obj):
|
|
"""Serialize the father foreign key or relative"""
|
|
father_obj = obj.father
|
|
request = self.context.get('request')
|
|
if not father_obj:
|
|
parent_rel = obj.relatives.filter(relation_type__in=['parents', 'stepfather']).first()
|
|
if parent_rel:
|
|
if parent_rel.narrator:
|
|
father_obj = parent_rel.narrator
|
|
elif parent_rel.name:
|
|
return {
|
|
'id': None,
|
|
'name': parent_rel.name,
|
|
'arabic_name': parent_rel.name,
|
|
'slug': None,
|
|
}
|
|
if father_obj:
|
|
return {
|
|
'id': father_obj.id,
|
|
'name': get_localized_text(father_obj.full_name, request) or (father_obj.get_name('en') if hasattr(father_obj, 'get_name') else None) or '—',
|
|
'arabic_name': get_arabic_localized_text(father_obj.full_name) or (father_obj.get_name('ar') if hasattr(father_obj, 'get_name') else None),
|
|
'slug': father_obj.slug,
|
|
}
|
|
return None
|
|
|
|
def get_sons(self, obj):
|
|
"""Serialize all sons (from children_as_father FK and TransmitterRelative)"""
|
|
request = self.context.get('request')
|
|
result = []
|
|
seen_ids = set()
|
|
|
|
for child in obj.children_as_father.all():
|
|
seen_ids.add(child.id)
|
|
result.append({
|
|
'id': child.id,
|
|
'name': get_localized_text(child.full_name, request) or (child.get_name('en') if hasattr(child, 'get_name') else None) or '—',
|
|
'arabic_name': get_arabic_localized_text(child.full_name) or (child.get_name('ar') if hasattr(child, 'get_name') else None),
|
|
'slug': child.slug,
|
|
})
|
|
|
|
for rel in obj.relatives.filter(relation_type='children'):
|
|
if rel.narrator and rel.narrator.id not in seen_ids:
|
|
seen_ids.add(rel.narrator.id)
|
|
result.append({
|
|
'id': rel.narrator.id,
|
|
'name': get_localized_text(rel.narrator.full_name, request) or (rel.narrator.get_name('en') if hasattr(rel.narrator, 'get_name') else None) or '—',
|
|
'arabic_name': get_arabic_localized_text(rel.narrator.full_name) or (rel.narrator.get_name('ar') if hasattr(rel.narrator, 'get_name') else None),
|
|
'slug': rel.narrator.slug,
|
|
})
|
|
elif not rel.narrator and rel.name:
|
|
result.append({
|
|
'id': None,
|
|
'name': rel.name,
|
|
'arabic_name': rel.name,
|
|
'slug': None,
|
|
})
|
|
|
|
return result
|
|
|
|
def get_reliability(self, obj):
|
|
"""Serialize the reliability foreign key"""
|
|
if obj.reliability:
|
|
request = self.context.get('request')
|
|
return {
|
|
'id': obj.reliability.id,
|
|
'title': get_localized_text(obj.reliability.title, request),
|
|
'slug': obj.reliability.slug,
|
|
'color': obj.reliability.color,
|
|
'main_color_code': obj.reliability.main_color_code,
|
|
'description': get_localized_text(obj.reliability.description, request),
|
|
}
|
|
return None
|
|
|
|
def get_relatives(self, obj):
|
|
return _serialize_transmitter_relatives(obj, self.context.get('request'))
|
|
|
|
|
|
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', 'gender', '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, request),
|
|
'slug': obj.reliability.slug,
|
|
'color': obj.reliability.color,
|
|
'main_color_code': obj.reliability.main_color_code,
|
|
'description': get_localized_text(obj.reliability.description, request),
|
|
}
|
|
else:
|
|
r = None
|
|
|
|
sahabi_status_data = None
|
|
if obj.sahabi_status:
|
|
sahabi_status_data = {
|
|
'id': obj.sahabi_status.id,
|
|
'slug': obj.sahabi_status.slug,
|
|
'title': get_localized_text(obj.sahabi_status.title, request),
|
|
'arabic_title': get_arabic_localized_text(obj.sahabi_status.title),
|
|
'description': get_localized_text(obj.sahabi_status.description, request),
|
|
}
|
|
|
|
companion_val = (
|
|
get_localized_text(obj.sahabi_status.title, request)
|
|
if (obj.sahabi_status and obj.sahabi_status.title)
|
|
else get_localized_text(obj.companion_type, request)
|
|
)
|
|
arabic_companion_val = (
|
|
get_arabic_localized_text(obj.sahabi_status.title)
|
|
if (obj.sahabi_status and obj.sahabi_status.title)
|
|
else get_arabic_localized_text(obj.companion_type)
|
|
)
|
|
|
|
madhhab_val = obj.madhhab
|
|
madhhabs_objs = list(obj.madhhabs.all())
|
|
if not madhhab_val and madhhabs_objs:
|
|
madhhab_val = get_localized_text(madhhabs_objs[0].title, request) or madhhabs_objs[0].slug
|
|
|
|
return {
|
|
'full_name': get_localized_text(obj.full_name, request),
|
|
'arabic_full_name': get_arabic_localized_text(obj.full_name),
|
|
'gender': obj.gender,
|
|
'kunya': _resolve_transmitter_m2m_text(obj.kunyas.all(), obj.kunya, request, arabic=False),
|
|
'arabic_kunya': _resolve_transmitter_m2m_text(obj.kunyas.all(), obj.kunya, request, arabic=True),
|
|
'kunyas': _serialize_transmitter_m2m_items(obj.kunyas.all(), request),
|
|
'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': _resolve_transmitter_city_text(obj.origin_city, obj.origin, request, arabic=False),
|
|
'arabic_origin': _resolve_transmitter_city_text(obj.origin_city, obj.origin, request, arabic=True),
|
|
'mawla_status': get_localized_text(obj.freed_slave_of, request),
|
|
'arabic_mawla_status': get_arabic_localized_text(obj.freed_slave_of),
|
|
'profession': _resolve_transmitter_m2m_text(obj.professions_list.all(), obj.profession, request, arabic=False, secondary_fallback_json=obj.occupation),
|
|
'arabic_profession': _resolve_transmitter_m2m_text(obj.professions_list.all(), obj.profession, request, arabic=True, secondary_fallback_json=obj.occupation),
|
|
'professions': _serialize_transmitter_m2m_items(obj.professions_list.all(), request),
|
|
'physical_traits': _resolve_transmitter_m2m_text(obj.features_list.all(), obj.features, request, arabic=False),
|
|
'arabic_physical_traits': _resolve_transmitter_m2m_text(obj.features_list.all(), obj.features, request, arabic=True),
|
|
'features': _serialize_transmitter_m2m_items(obj.features_list.all(), request),
|
|
'lived_in': _resolve_transmitter_city_text(obj.lived_in_city, obj.lived_in, request, arabic=False),
|
|
'arabic_lived_in': _resolve_transmitter_city_text(obj.lived_in_city, obj.lived_in, request, arabic=True),
|
|
'died_in': _resolve_transmitter_city_text(obj.died_in_city, obj.died_in, request, arabic=False),
|
|
'arabic_died_in': _resolve_transmitter_city_text(obj.died_in_city, obj.died_in, request, arabic=True),
|
|
'birth_year_hijri': format_transmitter_date(obj, 'birth', request),
|
|
'death_year_hijri': format_transmitter_date(obj, 'death', request),
|
|
'age_at_death': format_transmitter_date(obj, 'age', request),
|
|
'birth_year_raw': obj.birth_year_hijri,
|
|
'death_year_raw': obj.death_year_hijri,
|
|
'age_at_death_raw': obj.age_at_death,
|
|
'generation': obj.generation,
|
|
'reliability': r,
|
|
'madhhab': madhhab_val,
|
|
'madhhabs': _serialize_transmitter_m2m_items(madhhabs_objs, request, include_description=True),
|
|
'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': companion_val,
|
|
'arabic_companion_type': arabic_companion_val,
|
|
'sahabi_status': sahabi_status_data,
|
|
'relatives': self._get_relatives(obj, request),
|
|
}
|
|
|
|
def _get_relatives(self, obj, request):
|
|
return _serialize_transmitter_relatives(obj, request)
|
|
|
|
def get_scholars_opinions(self, obj):
|
|
"""Get all scholarly opinions about this transmitter"""
|
|
request = self.context.get('request') # ← FIX: Define request
|
|
|
|
results = []
|
|
for opinion in obj.opinions.all():
|
|
source_text = opinion.opinion.opinion_text if (opinion.opinion_id and opinion.opinion) else opinion.opinion_text
|
|
status_obj = (opinion.opinion.status if (opinion.opinion_id and opinion.opinion and opinion.opinion.status) else opinion.status)
|
|
scholar_name_src = opinion.scholar.name if (opinion.scholar and opinion.scholar.name) else opinion.scholar_name
|
|
results.append({
|
|
'id': opinion.id,
|
|
'scholar_name': get_localized_text(scholar_name_src, request),
|
|
'arabic_scholar_name': get_arabic_localized_text(scholar_name_src),
|
|
'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(source_text, request),
|
|
'arabic_opinion_text': get_arabic_localized_text(source_text),
|
|
'status': {
|
|
'id': status_obj.id,
|
|
'title': get_localized_text(status_obj.title, request),
|
|
'slug': status_obj.slug,
|
|
'color': status_obj.color,
|
|
'main_color_code': status_obj.main_color_code,
|
|
'description': get_localized_text(status_obj.description, request),
|
|
} if status_obj 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,
|
|
})
|
|
return results
|
|
|
|
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)
|
|
uncertain_transmitters = TransmitterShortSerializer(many=True, read_only=True)
|
|
layer = serializers.SerializerMethodField()
|
|
status = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = HadisTransmitter
|
|
fields = [
|
|
'id', 'order', 'is_gap', 'is_uncertain', 'layer', 'transmitter', 'uncertain_transmitters', '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,
|
|
'description': get_localized_text(obj.status.description, request),
|
|
}
|
|
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):
|
|
return obj.narrator_layers.count()
|
|
|
|
def get_layer_names(self, obj):
|
|
"""Get list of localized narrator layer names"""
|
|
request = self.context.get('request')
|
|
layer_objects = obj.narrator_layers.all().order_by('number')
|
|
|
|
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({
|
|
'id': layer.id,
|
|
'name': name,
|
|
'slug': slug,
|
|
'description': description
|
|
})
|
|
|
|
return layer_names
|
|
|
|
class ReferenceImageSerializer(serializers.ModelSerializer):
|
|
"""Serializer for ReferenceImage"""
|
|
thumbnail = serializers.SerializerMethodField()
|
|
image = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = ReferenceImage
|
|
fields = ['id', 'thumbnail', 'image', 'priority']
|
|
|
|
def get_thumbnail(self, obj):
|
|
"""Get thumbnail URL"""
|
|
img = getattr(obj, 'thumbnail', None) or getattr(obj, 'image', None)
|
|
if img:
|
|
request = self.context.get('request')
|
|
if request:
|
|
return absolute_https_url(img.url, request)
|
|
return absolute_https_url(img.url)
|
|
return None
|
|
|
|
def get_image(self, obj):
|
|
return self.get_thumbnail(obj)
|
|
|
|
|
|
class HadisReferenceSerializer(serializers.ModelSerializer):
|
|
"""Serializer for HadisReference with book, author, edition, volume, 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)
|
|
edition = serializers.SerializerMethodField()
|
|
edition_info = serializers.SerializerMethodField()
|
|
volume_info = serializers.SerializerMethodField()
|
|
volume = serializers.SerializerMethodField()
|
|
pages = serializers.SerializerMethodField()
|
|
hadith_number = serializers.SerializerMethodField()
|
|
citation_summary = serializers.SerializerMethodField()
|
|
open_book_url = serializers.SerializerMethodField()
|
|
book = serializers.SerializerMethodField()
|
|
author = serializers.SerializerMethodField()
|
|
images = ReferenceImageSerializer(many=True, read_only=True)
|
|
images_count = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = HadisReference
|
|
fields = [
|
|
'id', 'book', 'author', 'book_title', 'book_description', 'book_authors', 'share_link',
|
|
'edition', 'edition_info', 'volume_info', 'volume', 'pages', 'hadith_number',
|
|
'address', 'url', 'open_book_url', 'citation_summary', 'images', 'images_count'
|
|
]
|
|
|
|
def get_edition_info(self, obj):
|
|
edition = obj.edition
|
|
if not edition and getattr(obj, "book_volume", None) and obj.book_volume.edition:
|
|
edition = obj.book_volume.edition
|
|
if not edition and getattr(obj, "book_reference", None) and hasattr(obj.book_reference, "editions"):
|
|
edition = obj.book_reference.editions.first()
|
|
if edition:
|
|
request = self.context.get("request")
|
|
pub_src = edition.publisher_item.title if (getattr(edition, "publisher_item_id", None) and edition.publisher_item and edition.publisher_item.title) else edition.publisher
|
|
pub = get_localized_text(pub_src, request) if isinstance(pub_src, list) else pub_src
|
|
ed_title = get_localized_text(edition.title, request) if isinstance(getattr(edition, "title", None), list) else getattr(edition, "title", None)
|
|
return {
|
|
"id": edition.id,
|
|
"title": ed_title or "",
|
|
"edition_number": edition.edition_number,
|
|
"publisher": pub or "",
|
|
"year_of_publication": edition.year_of_publication or "",
|
|
"number_of_volumes": edition.number_of_volumes,
|
|
}
|
|
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
|
|
|
|
def get_edition(self, obj):
|
|
return self.get_edition_info(obj)
|
|
|
|
def get_volume(self, obj):
|
|
if obj.volume and str(obj.volume).strip().lower() != "none":
|
|
return obj.volume
|
|
if obj.book_volume and obj.book_volume.title:
|
|
return obj.book_volume.title
|
|
return None
|
|
|
|
def get_pages(self, obj):
|
|
if obj.pages and str(obj.pages).strip().lower() != "none":
|
|
return obj.pages
|
|
return None
|
|
|
|
def get_hadith_number(self, obj):
|
|
if obj.hadith_number and str(obj.hadith_number).strip().lower() != "none":
|
|
return obj.hadith_number
|
|
return None
|
|
|
|
def get_citation_summary(self, obj):
|
|
parts = []
|
|
vol = self.get_volume(obj)
|
|
pg = self.get_pages(obj)
|
|
num = self.get_hadith_number(obj)
|
|
if vol:
|
|
vol_str = str(vol).strip()
|
|
if vol_str.startswith("جلد"):
|
|
parts.append(vol_str)
|
|
else:
|
|
parts.append(f"جلد {vol_str}")
|
|
if pg:
|
|
pg_str = str(pg).strip()
|
|
if pg_str.startswith("ص") or pg_str.startswith("صفحه"):
|
|
parts.append(pg_str)
|
|
else:
|
|
parts.append(f"صفحه {pg_str}")
|
|
if num:
|
|
num_str = str(num).strip()
|
|
if num_str.startswith("ح") or num_str.startswith("حدیث") or num_str.startswith("شماره"):
|
|
parts.append(num_str)
|
|
else:
|
|
parts.append(f"شماره حدیث {num_str}")
|
|
return " • ".join(parts) if parts else ""
|
|
|
|
def get_open_book_url(self, obj):
|
|
request = self.context.get('request')
|
|
if obj.book_volume and obj.book_volume.file:
|
|
return absolute_https_url(obj.book_volume.file.url, request) if request else absolute_https_url(obj.book_volume.file.url)
|
|
return obj.url or None
|
|
|
|
def get_book(self, obj):
|
|
if obj.book_reference:
|
|
request = self.context.get('request')
|
|
first_ed = obj.edition
|
|
if not first_ed and getattr(obj, "book_volume", None) and obj.book_volume.edition:
|
|
first_ed = obj.book_volume.edition
|
|
if not first_ed and hasattr(obj.book_reference, "editions"):
|
|
first_ed = obj.book_reference.editions.first()
|
|
|
|
pub = getattr(obj.book_reference, "publisher", None)
|
|
if pub is None and first_ed:
|
|
pub = first_ed.publisher_item.title if (getattr(first_ed, "publisher_item_id", None) and first_ed.publisher_item and first_ed.publisher_item.title) else first_ed.publisher
|
|
if isinstance(pub, list):
|
|
pub = get_localized_text(pub, request)
|
|
|
|
year = getattr(obj.book_reference, "year_of_publication", None)
|
|
if not year and first_ed:
|
|
year = first_ed.year_of_publication
|
|
|
|
num_vols = getattr(obj.book_reference, "number_of_volumes", None)
|
|
if num_vols is None and first_ed:
|
|
num_vols = first_ed.number_of_volumes
|
|
|
|
return {
|
|
'id': obj.book_reference.id,
|
|
'slug': obj.book_reference.slug,
|
|
'title': get_localized_text(obj.book_reference.title, request) if isinstance(obj.book_reference.title, list) else obj.book_reference.title,
|
|
'publisher': pub or '',
|
|
'year_of_publication': year or '',
|
|
'number_of_volumes': num_vols,
|
|
}
|
|
return None
|
|
|
|
def get_author(self, obj):
|
|
if obj.book_reference and obj.book_reference.author:
|
|
author = obj.book_reference.author
|
|
request = self.context.get('request')
|
|
return {
|
|
'id': author.id,
|
|
'slug': author.slug,
|
|
'name': get_localized_text(author.name, request) if isinstance(author.name, list) else author.name
|
|
}
|
|
return None
|
|
|
|
def get_images_count(self, obj):
|
|
if hasattr(obj, '_prefetched_objects_cache') and 'images' in obj._prefetched_objects_cache:
|
|
return len(obj.images.all())
|
|
return obj.images.count()
|
|
|
|
# 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 = []
|
|
lang = _extract_request_lang(self.context.get('request'))
|
|
for author in authors:
|
|
name = field.to_representation(author.name)
|
|
|
|
# Format birth and death dates (Hijri/CE)
|
|
birth_hijri = author.get_formatted_birth_year(lang=lang) if hasattr(author, 'get_formatted_birth_year') else author.birth_year_hijri
|
|
birth_miladi = author.birth_year_miladi
|
|
death_hijri = author.get_formatted_death_year(lang=lang) if hasattr(author, 'get_formatted_death_year') else author.death_year_hijri
|
|
death_miladi = author.death_year_miladi
|
|
|
|
birth_str = ""
|
|
if birth_hijri and birth_miladi:
|
|
birth_str = f"{birth_hijri} / {birth_miladi}CE"
|
|
elif birth_hijri:
|
|
birth_str = f"{birth_hijri}"
|
|
elif birth_miladi:
|
|
birth_str = f"{birth_miladi}CE"
|
|
|
|
death_str = ""
|
|
if death_hijri and death_miladi:
|
|
death_str = f"{death_hijri} / {death_miladi}CE"
|
|
elif death_hijri:
|
|
death_str = f"{death_hijri}"
|
|
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()
|
|
address = serializers.SerializerMethodField()
|
|
images = serializers.SerializerMethodField()
|
|
links = serializers.SerializerMethodField()
|
|
hadis_info = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = HadisCorrection
|
|
fields = ['id', 'title', 'slug', 'narrator', 'description', 'translation', 'share_link', 'bookmark', 'address', 'images', 'links', 'hadis_info']
|
|
|
|
def get_links(self, obj):
|
|
return format_links_output(obj.links)
|
|
|
|
def get_hadis_info(self, obj):
|
|
if obj.hadis:
|
|
request = self.context.get('request')
|
|
return {
|
|
"id": obj.hadis.id,
|
|
"number": obj.hadis.number,
|
|
"slug": obj.hadis.slug,
|
|
"title": get_localized_text(obj.hadis.title, request) if hasattr(obj.hadis, 'title') and obj.hadis.title else f"Hadith {obj.hadis.number}",
|
|
}
|
|
return None
|
|
|
|
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)
|
|
|
|
def get_address(self, obj):
|
|
refs = list(obj.references.all())
|
|
return format_address_output(refs[0] if refs else None)
|
|
|
|
def get_images(self, obj):
|
|
request = self.context.get('request')
|
|
images_list = []
|
|
for ref in obj.references.all():
|
|
for img in ref.images.all():
|
|
if img.image:
|
|
url = absolute_https_url(img.image.url, request) if request else absolute_https_url(img.image.url)
|
|
images_list.append({
|
|
"id": img.id,
|
|
"image": url,
|
|
"thumbnail": url,
|
|
})
|
|
return images_list
|
|
|
|
|
|
def serialize_hadis_category_item(cat, request):
|
|
if not cat:
|
|
return None
|
|
return {
|
|
'id': cat.id,
|
|
'title': get_localized_text(cat.title, request),
|
|
'slug': cat.slug,
|
|
'source_type': cat.source_type,
|
|
'sect_type': cat.sect.sect_type if hasattr(cat, 'sect') and cat.sect else 'shia',
|
|
}
|
|
|
|
|
|
class HadisBasicSerializer(serializers.ModelSerializer):
|
|
"""Basic serializer for Hadis with minimal information"""
|
|
translation = LocalizedField()
|
|
category = serializers.SerializerMethodField()
|
|
categories = 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', 'categories'
|
|
]
|
|
|
|
def get_category(self, obj):
|
|
cat = obj.category
|
|
if not cat and hasattr(obj, 'categories'):
|
|
cat = obj.categories.first()
|
|
if not cat:
|
|
return None
|
|
return serialize_hadis_category_item(cat, self.context.get('request'))
|
|
|
|
def get_categories(self, obj):
|
|
request = self.context.get('request')
|
|
cats = list(obj.categories.all().select_related('sect'))
|
|
if not cats and obj.category:
|
|
cats = [obj.category]
|
|
return [serialize_hadis_category_item(c, request) for c in cats]
|
|
|
|
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()
|
|
bookmark = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Hadis
|
|
fields = [
|
|
'id', 'slug', 'title', 'title_narrator', 'text',
|
|
'translation', 'share_link', 'bookmark']
|
|
|
|
def get_bookmark(self, obj):
|
|
"""Get bookmark status for this Hadis."""
|
|
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 HadisDetailSerializer(serializers.ModelSerializer):
|
|
"""Detailed serializer for Hadis with core information (transmitters and corrections moved to separate endpoints)"""
|
|
hadis_status = HadisStatusSerializer(read_only=True)
|
|
status_text = LocalizedField(source='hadis_status_text')
|
|
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)
|
|
category = serializers.SerializerMethodField()
|
|
categories = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Hadis
|
|
fields = [
|
|
'id', 'number', 'slug', 'category_id', 'category_slug', 'category', 'categories', 'hadis_status', 'status_text', 'links', 'share_link',
|
|
'tags', 'references', 'reference_images', 'address'
|
|
]
|
|
|
|
def get_category(self, obj):
|
|
cat = obj.category
|
|
if not cat and hasattr(obj, 'categories'):
|
|
cat = obj.categories.first()
|
|
if not cat:
|
|
return None
|
|
return serialize_hadis_category_item(cat, self.context.get('request'))
|
|
|
|
def get_categories(self, obj):
|
|
request = self.context.get('request')
|
|
cats = list(obj.categories.all().select_related('sect'))
|
|
if not cats and obj.category:
|
|
cats = [obj.category]
|
|
return [serialize_hadis_category_item(c, request) for c in cats]
|
|
|
|
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()
|
|
arabic_full_name = ArabicLocalizedField(source='full_name')
|
|
kunya = LocalizedField()
|
|
arabic_kunya = ArabicLocalizedField(source='kunya')
|
|
known_as = LocalizedField()
|
|
arabic_known_as = ArabicLocalizedField(source='known_as')
|
|
nickname = LocalizedField()
|
|
arabic_nickname = ArabicLocalizedField(source='nickname')
|
|
birth_year_hijri = serializers.SerializerMethodField()
|
|
death_year_hijri = serializers.SerializerMethodField()
|
|
birth_year_raw = serializers.CharField(source='birth_year_hijri', read_only=True)
|
|
death_year_raw = serializers.CharField(source='death_year_hijri', read_only=True)
|
|
reliability = SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Transmitters
|
|
fields = [
|
|
'id', 'slug', 'full_name', 'arabic_full_name',
|
|
'kunya', 'arabic_kunya', 'known_as', 'arabic_known_as', 'nickname', 'arabic_nickname',
|
|
'birth_year_hijri', 'death_year_hijri', 'birth_year_raw', 'death_year_raw',
|
|
'reliability', 'gender'
|
|
]
|
|
|
|
def get_birth_year_hijri(self, obj):
|
|
return format_transmitter_date(obj, 'birth', self.context.get('request'))
|
|
|
|
def get_death_year_hijri(self, obj):
|
|
return format_transmitter_date(obj, 'death', self.context.get('request'))
|
|
|
|
def get_reliability(self, obj):
|
|
"""Serialize the reliability foreign key"""
|
|
if obj.reliability:
|
|
request = self.context.get('request')
|
|
return {
|
|
'id': obj.reliability.id,
|
|
'title': get_localized_text(obj.reliability.title, request),
|
|
'slug': obj.reliability.slug,
|
|
'color': obj.reliability.color,
|
|
'main_color_code': obj.reliability.main_color_code,
|
|
'description': get_localized_text(obj.reliability.description, request),
|
|
}
|
|
return None
|
|
|
|
|
|
# --- سریالایزرهای کمکی تصاویر رفرنس ---
|
|
class CorrectionReferenceImageSerializer(serializers.ModelSerializer):
|
|
image = serializers.SerializerMethodField()
|
|
thumbnail = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = CorrectionReferenceImage
|
|
fields = ['id', 'image', 'thumbnail', 'priority']
|
|
|
|
def get_image(self, obj):
|
|
if obj.image:
|
|
request = self.context.get('request')
|
|
return absolute_https_url(obj.image.url, request) if request else absolute_https_url(obj.image.url)
|
|
return None
|
|
|
|
def get_thumbnail(self, obj):
|
|
return self.get_image(obj)
|
|
|
|
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):
|
|
edition = obj.edition
|
|
if not edition and getattr(obj, "book_volume", None) and obj.book_volume.edition:
|
|
edition = obj.book_volume.edition
|
|
if not edition and getattr(obj, "book_reference", None) and hasattr(obj.book_reference, "editions"):
|
|
edition = obj.book_reference.editions.first()
|
|
if edition:
|
|
request = self.context.get("request")
|
|
pub_src = edition.publisher_item.title if (getattr(edition, "publisher_item_id", None) and edition.publisher_item and edition.publisher_item.title) else edition.publisher
|
|
pub = get_localized_text(pub_src, request) if isinstance(pub_src, list) else pub_src
|
|
ed_title = get_localized_text(edition.title, request) if isinstance(getattr(edition, "title", None), list) else getattr(edition, "title", None)
|
|
return {
|
|
"id": edition.id,
|
|
"title": ed_title or "",
|
|
"edition_number": edition.edition_number,
|
|
"publisher": pub or "",
|
|
"year_of_publication": edition.year_of_publication or "",
|
|
"number_of_volumes": edition.number_of_volumes,
|
|
}
|
|
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):
|
|
book = serializers.SerializerMethodField()
|
|
author = serializers.SerializerMethodField()
|
|
edition = serializers.SerializerMethodField()
|
|
open_book_url = serializers.SerializerMethodField()
|
|
citation_summary = serializers.SerializerMethodField()
|
|
images = CorrectionReferenceImageSerializer(many=True, read_only=True)
|
|
images_count = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = CorrectionReference
|
|
fields = [
|
|
'id',
|
|
'book',
|
|
'author',
|
|
'edition',
|
|
'volume_info',
|
|
'hadith_number',
|
|
'volume',
|
|
'pages',
|
|
'address',
|
|
'url',
|
|
'open_book_url',
|
|
'citation_summary',
|
|
'images',
|
|
'images_count',
|
|
'book_title',
|
|
'book_authors',
|
|
'edition_info',
|
|
]
|
|
|
|
def get_book(self, obj):
|
|
if obj.book_reference:
|
|
request = self.context.get('request')
|
|
first_ed = obj.edition
|
|
if not first_ed and getattr(obj, "book_volume", None) and obj.book_volume.edition:
|
|
first_ed = obj.book_volume.edition
|
|
if not first_ed and hasattr(obj.book_reference, "editions"):
|
|
first_ed = obj.book_reference.editions.first()
|
|
|
|
pub = getattr(obj.book_reference, "publisher", None)
|
|
if pub is None and first_ed:
|
|
pub = first_ed.publisher_item.title if (getattr(first_ed, "publisher_item_id", None) and first_ed.publisher_item and first_ed.publisher_item.title) else first_ed.publisher
|
|
if isinstance(pub, list):
|
|
pub = get_localized_text(pub, request)
|
|
|
|
year = getattr(obj.book_reference, "year_of_publication", None)
|
|
if not year and first_ed:
|
|
year = first_ed.year_of_publication
|
|
|
|
num_vols = getattr(obj.book_reference, "number_of_volumes", None)
|
|
if num_vols is None and first_ed:
|
|
num_vols = first_ed.number_of_volumes
|
|
if num_vols is None and hasattr(obj.book_reference, "volumes"):
|
|
num_vols = obj.book_reference.volumes.count() or None
|
|
|
|
return {
|
|
'id': obj.book_reference.id,
|
|
'slug': obj.book_reference.slug,
|
|
'title': get_localized_text(obj.book_reference.title, request),
|
|
'publisher': pub or '',
|
|
'year_of_publication': year or '',
|
|
'number_of_volumes': num_vols,
|
|
}
|
|
return None
|
|
|
|
def get_author(self, obj):
|
|
if obj.book_reference and obj.book_reference.author:
|
|
author = obj.book_reference.author
|
|
request = self.context.get('request')
|
|
return {
|
|
'id': author.id,
|
|
'slug': author.slug,
|
|
'name': get_localized_text(author.name, request)
|
|
}
|
|
return None
|
|
|
|
def get_edition(self, obj):
|
|
return self.get_edition_info(obj)
|
|
|
|
def get_open_book_url(self, obj):
|
|
request = self.context.get('request')
|
|
if obj.book_volume and obj.book_volume.file:
|
|
return absolute_https_url(obj.book_volume.file.url, request) if request else absolute_https_url(obj.book_volume.file.url)
|
|
return None
|
|
|
|
volume = serializers.SerializerMethodField()
|
|
pages = serializers.SerializerMethodField()
|
|
hadith_number = serializers.SerializerMethodField()
|
|
|
|
def get_volume(self, obj):
|
|
if obj.volume and str(obj.volume).strip().lower() != "none":
|
|
return obj.volume
|
|
if obj.book_volume and obj.book_volume.title:
|
|
return obj.book_volume.title
|
|
return None
|
|
|
|
def get_pages(self, obj):
|
|
if obj.pages and str(obj.pages).strip().lower() != "none":
|
|
return obj.pages
|
|
return None
|
|
|
|
def get_hadith_number(self, obj):
|
|
if obj.hadith_number and str(obj.hadith_number).strip().lower() != "none":
|
|
return obj.hadith_number
|
|
return None
|
|
|
|
def get_citation_summary(self, obj):
|
|
parts = []
|
|
vol = self.get_volume(obj)
|
|
pg = self.get_pages(obj)
|
|
num = self.get_hadith_number(obj)
|
|
if vol:
|
|
vol_str = str(vol).strip()
|
|
if vol_str.startswith("جلد"):
|
|
parts.append(vol_str)
|
|
else:
|
|
parts.append(f"جلد {vol_str}")
|
|
if pg:
|
|
pg_str = str(pg).strip()
|
|
if pg_str.startswith("ص") or pg_str.startswith("صفحه"):
|
|
parts.append(pg_str)
|
|
else:
|
|
parts.append(f"صفحه {pg_str}")
|
|
if num:
|
|
num_str = str(num).strip()
|
|
if num_str.startswith("ح") or num_str.startswith("حدیث") or num_str.startswith("شماره"):
|
|
parts.append(num_str)
|
|
else:
|
|
parts.append(f"شماره حدیث {num_str}")
|
|
return " • ".join(parts) if parts else ""
|
|
|
|
def get_images_count(self, obj):
|
|
if hasattr(obj, '_prefetched_objects_cache') and 'images' in obj._prefetched_objects_cache:
|
|
return len(obj.images.all())
|
|
return obj.images.count()
|
|
|
|
class DetailedInterpretationReferenceSerializer(DetailedCorrectionReferenceSerializer):
|
|
images = InterpretationReferenceImageSerializer(many=True, read_only=True)
|
|
|
|
class Meta:
|
|
model = InterpretationReference
|
|
fields = [
|
|
'id',
|
|
'book',
|
|
'author',
|
|
'edition',
|
|
'volume_info',
|
|
'hadith_number',
|
|
'volume',
|
|
'pages',
|
|
'address',
|
|
'url',
|
|
'open_book_url',
|
|
'citation_summary',
|
|
'images',
|
|
'images_count',
|
|
'book_title',
|
|
'book_authors',
|
|
'edition_info',
|
|
]
|
|
|
|
class DetailedOriginalTextReferenceSerializer(DetailedCorrectionReferenceSerializer):
|
|
images = OriginalTextReferenceImageSerializer(many=True, read_only=True)
|
|
|
|
class Meta:
|
|
model = OriginalTextReference
|
|
fields = [
|
|
'id',
|
|
'book',
|
|
'author',
|
|
'edition',
|
|
'volume_info',
|
|
'hadith_number',
|
|
'volume',
|
|
'pages',
|
|
'address',
|
|
'url',
|
|
'open_book_url',
|
|
'citation_summary',
|
|
'images',
|
|
'images_count',
|
|
'book_title',
|
|
'book_authors',
|
|
'edition_info',
|
|
]
|
|
|
|
|
|
class DetailedHadisReferenceSerializer(DetailedCorrectionReferenceSerializer):
|
|
images = ReferenceImageSerializer(many=True, read_only=True)
|
|
|
|
class Meta:
|
|
model = HadisReference
|
|
fields = [
|
|
'id',
|
|
'book',
|
|
'author',
|
|
'edition',
|
|
'volume_info',
|
|
'hadith_number',
|
|
'volume',
|
|
'pages',
|
|
'address',
|
|
'url',
|
|
'open_book_url',
|
|
'citation_summary',
|
|
'images',
|
|
'images_count',
|
|
'book_title',
|
|
'book_authors',
|
|
'edition_info',
|
|
]
|
|
|
|
|
|
# --- سریالایزرهای اصلی جزئیات (Detail Serializers) ---
|
|
|
|
class HadisCorrectionDetailSerializer(serializers.ModelSerializer):
|
|
title = LocalizedField()
|
|
description = serializers.SerializerMethodField()
|
|
translation = LocalizedField()
|
|
references = DetailedCorrectionReferenceSerializer(many=True, read_only=True)
|
|
hadis_info = serializers.SerializerMethodField()
|
|
address = serializers.SerializerMethodField()
|
|
images = serializers.SerializerMethodField()
|
|
links = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = HadisCorrection
|
|
fields = ['id', 'title', 'slug', 'narrator', 'description', 'translation', 'share_link', 'hadis_info', 'address', 'images', '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}
|
|
|
|
def get_address(self, obj):
|
|
refs = list(obj.references.all())
|
|
return format_address_output(refs[0] if refs else None)
|
|
|
|
def get_images(self, obj):
|
|
request = self.context.get('request')
|
|
images_list = []
|
|
for ref in obj.references.all():
|
|
for img in ref.images.all():
|
|
if img.image:
|
|
url = absolute_https_url(img.image.url, request) if request else absolute_https_url(img.image.url)
|
|
images_list.append({
|
|
"id": img.id,
|
|
"image": url,
|
|
"thumbnail": url,
|
|
})
|
|
return images_list
|
|
|
|
def get_links(self, obj):
|
|
return format_links_output(obj.links)
|
|
|
|
|
|
class HadisInterpretationDetailSerializer(serializers.ModelSerializer):
|
|
title = LocalizedField()
|
|
translation = LocalizedField()
|
|
references = DetailedInterpretationReferenceSerializer(many=True, read_only=True)
|
|
category_info = serializers.SerializerMethodField()
|
|
address = serializers.SerializerMethodField()
|
|
images = serializers.SerializerMethodField()
|
|
links = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = HadisInterpretation
|
|
fields = ['id', 'title', 'slug', 'narrator', 'text', 'translation', 'category_info', 'address', 'images', 'links', 'references']
|
|
|
|
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'))
|
|
}
|
|
|
|
def get_address(self, obj):
|
|
refs = list(obj.references.all())
|
|
return format_address_output(refs[0] if refs else None)
|
|
|
|
def get_images(self, obj):
|
|
request = self.context.get('request')
|
|
images_list = []
|
|
for ref in obj.references.all():
|
|
for img in ref.images.all():
|
|
if img.image:
|
|
url = absolute_https_url(img.image.url, request) if request else absolute_https_url(img.image.url)
|
|
images_list.append({
|
|
"id": img.id,
|
|
"image": url,
|
|
"thumbnail": url,
|
|
})
|
|
return images_list
|
|
|
|
|
|
def get_links(self, obj):
|
|
return format_links_output(obj.links)
|
|
|
|
|
|
class HadisSyncCorrectionSerializer(HadisCorrectionDetailSerializer):
|
|
"""نسخه سبک و بهینه تصحیحات مخصوص سینک بدون hadis_info"""
|
|
class Meta(HadisCorrectionDetailSerializer.Meta):
|
|
fields = ['id', 'title', 'slug', 'narrator', 'description', 'translation', 'share_link', 'address', 'images', 'references', 'links']
|
|
|
|
|
|
class HadisSyncInterpretationSerializer(HadisInterpretationDetailSerializer):
|
|
"""نسخه سبک و بهینه تفاسیر مخصوص سینک بدون category_info"""
|
|
class Meta(HadisInterpretationDetailSerializer.Meta):
|
|
fields = ['id', 'title', 'slug', 'narrator', 'text', 'translation', 'address', 'images', 'links', 'references']
|
|
|
|
|
|
class TransmitterOriginalTextDetailSerializer(serializers.ModelSerializer):
|
|
title = LocalizedField()
|
|
text = serializers.CharField()
|
|
translation = LocalizedField()
|
|
references = DetailedOriginalTextReferenceSerializer(many=True, read_only=True)
|
|
transmitter_info = serializers.SerializerMethodField()
|
|
address = serializers.SerializerMethodField()
|
|
images = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = TransmitterOriginalText
|
|
fields = ['id', 'slug', 'title', 'text', 'translation', 'share_link', 'transmitter_info', 'address', 'images', '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'))
|
|
}
|
|
|
|
def get_address(self, obj):
|
|
refs = list(obj.references.all())
|
|
return format_address_output(refs[0] if refs else None)
|
|
|
|
def get_images(self, obj):
|
|
request = self.context.get('request')
|
|
images_list = []
|
|
for ref in obj.references.all():
|
|
for img in ref.images.all():
|
|
if img.image:
|
|
url = absolute_https_url(img.image.url, request) if request else absolute_https_url(img.image.url)
|
|
images_list.append({
|
|
"id": img.id,
|
|
"image": url,
|
|
"thumbnail": url,
|
|
})
|
|
return images_list
|
|
|
|
|
|
class HadisSourceDetailsSerializer(serializers.ModelSerializer):
|
|
"""
|
|
Lightweight serializer for Hadis Source Details (Address, Links, Images, Reference).
|
|
Used on-demand when opening source detail modals in web and mobile clients.
|
|
"""
|
|
title = LocalizedField()
|
|
narrator = LocalizedField(source='title_narrator')
|
|
source_type = serializers.SerializerMethodField()
|
|
address = serializers.SerializerMethodField()
|
|
links = serializers.SerializerMethodField()
|
|
images = serializers.SerializerMethodField()
|
|
reference = serializers.SerializerMethodField()
|
|
references = serializers.SerializerMethodField()
|
|
tags = HadisTagSerializer(many=True, read_only=True)
|
|
|
|
class Meta:
|
|
model = Hadis
|
|
fields = [
|
|
'id', 'slug', 'title', 'narrator', 'source_type',
|
|
'address', 'links', 'images', 'share_link',
|
|
'reference', 'references', 'tags'
|
|
]
|
|
|
|
def get_source_type(self, obj):
|
|
if obj.category and obj.category.source_type:
|
|
return obj.category.source_type
|
|
return 'hadith'
|
|
|
|
def get_address(self, obj):
|
|
# 1. Primary: obj.address
|
|
formatted = format_address_output(obj.address)
|
|
if formatted:
|
|
return formatted
|
|
# 2. Fallback: references address
|
|
refs = list(obj.references.all())
|
|
first_ref = refs[0] if refs else None
|
|
if first_ref and hasattr(first_ref, 'address') and first_ref.address:
|
|
return format_address_output(first_ref.address)
|
|
return []
|
|
|
|
def get_links(self, obj):
|
|
return format_links_output(obj.links)
|
|
|
|
def get_images(self, obj):
|
|
request = self.context.get('request')
|
|
images_list = []
|
|
for ref in obj.references.all():
|
|
for img in sorted(ref.images.all(), key=lambda x: getattr(x, 'priority', 0)):
|
|
url = None
|
|
if img.thumbnail:
|
|
url = absolute_https_url(img.thumbnail.url, request) if request else absolute_https_url(img.thumbnail.url)
|
|
elif hasattr(img, 'image') and img.image:
|
|
url = absolute_https_url(img.image.url, request) if request else absolute_https_url(img.image.url)
|
|
if url:
|
|
images_list.append({
|
|
"id": img.id,
|
|
"image": url,
|
|
"thumbnail": url,
|
|
"priority": img.priority,
|
|
})
|
|
return images_list
|
|
|
|
def get_reference(self, obj):
|
|
ref = obj.references.first()
|
|
if ref:
|
|
return DetailedHadisReferenceSerializer(ref, context=self.context).data
|
|
return None
|
|
|
|
def get_references(self, obj):
|
|
refs = obj.references.all()
|
|
return DetailedHadisReferenceSerializer(refs, many=True, context=self.context).data
|
|
|