Browse Source

feat(hadis): expose new narrator, author, edition fields and status descriptions in public serializers and views

master
Mohsen Taba 4 days ago
parent
commit
6fd1a45afb
  1. 8
      apps/hadis/models/reference.py
  2. 451
      apps/hadis/serializers/hadis.py
  3. 282
      apps/hadis/serializers/reference_v2.py
  4. 52
      apps/hadis/views/category.py
  5. 9
      apps/hadis/views/hadis.py
  6. 22
      apps/hadis/views/reference_v2.py
  7. 29
      apps/hadis/views/transmitter.py

8
apps/hadis/models/reference.py

@ -294,6 +294,10 @@ class BookPublisher(LowercaseSlugMixin, models.Model):
return self.title[0].get('text', '') or self.title[0].get('title', '') return self.title[0].get('text', '') or self.title[0].get('title', '')
return None return None
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
self.editions.all().update(publisher=self.title or [])
class BookEdition(models.Model): class BookEdition(models.Model):
""" """
@ -369,8 +373,12 @@ class BookEdition(models.Model):
return f"{self.book_reference} - {self.edition_number or 'Edition ' + str(self.id)}" return f"{self.book_reference} - {self.edition_number or 'Edition ' + str(self.id)}"
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if self.publisher_item_id and self.publisher_item:
self.publisher = self.publisher_item.title or []
if self.city_of_publication_city_id and self.city_of_publication_city: if self.city_of_publication_city_id and self.city_of_publication_city:
self.city_of_publication = self.city_of_publication_city.title or [] self.city_of_publication = self.city_of_publication_city.title or []
if self.country_of_publication_country_id and self.country_of_publication_country:
self.country_of_publication = self.country_of_publication_country.title or []
super().save(*args, **kwargs) super().save(*args, **kwargs)
if self.book_reference and not self.books.filter(id=self.book_reference_id).exists(): if self.book_reference and not self.books.filter(id=self.book_reference_id).exists():
self.books.add(self.book_reference) self.books.add(self.book_reference)

451
apps/hadis/serializers/hadis.py

@ -244,6 +244,7 @@ class HadisSyncSerializer(serializers.ModelSerializer):
'slug': t.reliability.slug, 'slug': t.reliability.slug,
'color': t.reliability.color, 'color': t.reliability.color,
'main_color_code': t.reliability.main_color_code, 'main_color_code': t.reliability.main_color_code,
'description': get_localized_text(t.reliability.description, request),
} }
uncertain_list = [] uncertain_list = []
@ -257,6 +258,7 @@ class HadisSyncSerializer(serializers.ModelSerializer):
'slug': ut.reliability.slug, 'slug': ut.reliability.slug,
'color': ut.reliability.color, 'color': ut.reliability.color,
'main_color_code': ut.reliability.main_color_code, 'main_color_code': ut.reliability.main_color_code,
'description': get_localized_text(ut.reliability.description, request),
} }
uncertain_list.append({ uncertain_list.append({
'id': ut.id, 'id': ut.id,
@ -476,6 +478,8 @@ class TransmitterSerializer(serializers.ModelSerializer):
"""Serializer for Transmitters""" """Serializer for Transmitters"""
full_name = LocalizedField() full_name = LocalizedField()
arabic_full_name = ArabicLocalizedField(source='full_name') arabic_full_name = ArabicLocalizedField(source='full_name')
kunya = LocalizedField()
arabic_kunya = ArabicLocalizedField(source='kunya')
known_as = LocalizedField() known_as = LocalizedField()
arabic_known_as = ArabicLocalizedField(source='known_as') arabic_known_as = ArabicLocalizedField(source='known_as')
nickname = LocalizedField() nickname = LocalizedField()
@ -495,7 +499,7 @@ class TransmitterSerializer(serializers.ModelSerializer):
'id', 'full_name', 'arabic_full_name', 'slug', 'id', 'full_name', 'arabic_full_name', 'slug',
'birth_year_hijri', 'death_year_hijri', 'age_at_death', 'birth_year_hijri', 'death_year_hijri', 'age_at_death',
'birth_year_raw', 'death_year_raw', 'age_at_death_raw', 'birth_year_raw', 'death_year_raw', 'age_at_death_raw',
'known_as', 'arabic_known_as', 'nickname', 'arabic_nickname',
'kunya', 'arabic_kunya', 'known_as', 'arabic_known_as', 'nickname', 'arabic_nickname',
'reliability', 'madhhab', 'generation', 'share_link', 'gender' 'reliability', 'madhhab', 'generation', 'share_link', 'gender'
] ]
@ -526,6 +530,8 @@ class TransmitterShortSerializer(serializers.ModelSerializer):
"""Serializer for Transmitters""" """Serializer for Transmitters"""
full_name = LocalizedField() full_name = LocalizedField()
arabic_full_name = ArabicLocalizedField(source='full_name') arabic_full_name = ArabicLocalizedField(source='full_name')
kunya = LocalizedField()
arabic_kunya = ArabicLocalizedField(source='kunya')
known_as = LocalizedField() known_as = LocalizedField()
arabic_known_as = ArabicLocalizedField(source='known_as') arabic_known_as = ArabicLocalizedField(source='known_as')
nickname = LocalizedField() nickname = LocalizedField()
@ -541,7 +547,7 @@ class TransmitterShortSerializer(serializers.ModelSerializer):
fields = [ fields = [
'id', 'full_name', 'arabic_full_name', 'slug', 'id', 'full_name', 'arabic_full_name', 'slug',
'birth_year_hijri', 'death_year_hijri', 'birth_year_raw', 'death_year_raw', 'birth_year_hijri', 'death_year_hijri', 'birth_year_raw', 'death_year_raw',
'known_as', 'arabic_known_as', 'nickname', 'arabic_nickname', 'reliability', 'gender'
'kunya', 'arabic_kunya', 'known_as', 'arabic_known_as', 'nickname', 'arabic_nickname', 'reliability', 'gender'
] ]
def get_birth_year_hijri(self, obj): def get_birth_year_hijri(self, obj):
@ -691,6 +697,105 @@ class TransmitterOriginalTextSerializer(serializers.ModelSerializer):
return images_list 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): class TransmitterDetailSerializer(serializers.ModelSerializer):
""" Serializer for Details of Transmitters """ """ Serializer for Details of Transmitters """
@ -700,23 +805,31 @@ class TransmitterDetailSerializer(serializers.ModelSerializer):
arabic_known_as = ArabicLocalizedField(source='known_as') arabic_known_as = ArabicLocalizedField(source='known_as')
nickname = LocalizedField() nickname = LocalizedField()
arabic_nickname = ArabicLocalizedField(source='nickname') arabic_nickname = ArabicLocalizedField(source='nickname')
kunya = LocalizedField()
arabic_kunya = ArabicLocalizedField(source='kunya')
origin = LocalizedField()
arabic_origin = ArabicLocalizedField(source='origin')
kunya = serializers.SerializerMethodField()
arabic_kunya = serializers.SerializerMethodField()
kunyas = serializers.SerializerMethodField()
origin = serializers.SerializerMethodField()
arabic_origin = serializers.SerializerMethodField()
mawla_status = LocalizedField(source='freed_slave_of') mawla_status = LocalizedField(source='freed_slave_of')
arabic_mawla_status = ArabicLocalizedField(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()
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() description = LocalizedField()
companion_type = LocalizedField()
arabic_companion_type = ArabicLocalizedField(source='companion_type')
companion_type = serializers.SerializerMethodField()
arabic_companion_type = serializers.SerializerMethodField()
sahabi_status = serializers.SerializerMethodField()
madhhab = serializers.SerializerMethodField()
madhhabs = serializers.SerializerMethodField()
birth_year_hijri = serializers.SerializerMethodField() birth_year_hijri = serializers.SerializerMethodField()
death_year_hijri = serializers.SerializerMethodField() death_year_hijri = serializers.SerializerMethodField()
age_at_death = serializers.SerializerMethodField() age_at_death = serializers.SerializerMethodField()
@ -732,18 +845,111 @@ class TransmitterDetailSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Transmitters model = Transmitters
fields = [ 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',
'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', 'birth_year_raw', 'death_year_raw', 'age_at_death_raw',
'reliability', 'reliability',
'madhhab',"in_sahih_muslim","in_sahih_bukhari",
"description",'generation','share_link',
'tadlis', 'ikhtilat', 'companion_type', 'arabic_companion_type', 'relatives',
'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' '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): def get_birth_year_hijri(self, obj):
return format_transmitter_date(obj, 'birth', self.context.get('request')) return format_transmitter_date(obj, 'birth', self.context.get('request'))
@ -827,43 +1033,7 @@ class TransmitterDetailSerializer(serializers.ModelSerializer):
return None return None
def get_relatives(self, obj): def get_relatives(self, obj):
request = self.context.get('request')
relatives_qs = obj.relatives.select_related('relation_type_ref', 'narrator').all()
grouped = {}
for rel in relatives_qs:
rel_type = (rel.relation_type_ref.code or rel.relation_type_ref.slug or rel.relation_type) if rel.relation_type_ref else rel.relation_type
rel_val_src = (rel.relation_type_ref.title if (rel.relation_type_ref and rel.relation_type_ref.title) else rel.relation_value)
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_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
return _serialize_transmitter_relatives(obj, self.context.get('request'))
class TransmittersFiltersSerializer(serializers.ModelSerializer): class TransmittersFiltersSerializer(serializers.ModelSerializer):
@ -890,36 +1060,68 @@ class TransmitterSyncSerializer(serializers.ModelSerializer):
"""Get biographical information (flattened)""" """Get biographical information (flattened)"""
request = self.context.get('request') # ← FIX: Define request request = self.context.get('request') # ← FIX: Define request
if obj.reliability: if obj.reliability:
r= {
r = {
'id': obj.reliability.id, 'id': obj.reliability.id,
'title': get_localized_text(obj.reliability.title, request), 'title': get_localized_text(obj.reliability.title, request),
'slug': obj.reliability.slug,
'color': obj.reliability.color, 'color': obj.reliability.color,
'main_color_code': obj.reliability.main_color_code, 'main_color_code': obj.reliability.main_color_code,
'description': get_localized_text(obj.reliability.description, request), 'description': get_localized_text(obj.reliability.description, request),
} }
else :
r= None
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 { return {
'full_name': get_localized_text(obj.full_name, request), 'full_name': get_localized_text(obj.full_name, request),
'arabic_full_name': get_arabic_localized_text(obj.full_name), 'arabic_full_name': get_arabic_localized_text(obj.full_name),
'gender': obj.gender, 'gender': obj.gender,
'kunya': get_localized_text(obj.kunya, request),
'arabic_kunya': get_arabic_localized_text(obj.kunya),
'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), 'known_as': get_localized_text(obj.known_as, request),
'arabic_known_as': get_arabic_localized_text(obj.known_as), 'arabic_known_as': get_arabic_localized_text(obj.known_as),
'nickname': get_localized_text(obj.nickname, request), 'nickname': get_localized_text(obj.nickname, request),
'arabic_nickname': get_arabic_localized_text(obj.nickname), 'arabic_nickname': get_arabic_localized_text(obj.nickname),
'origin': get_localized_text(obj.origin, request),
'arabic_origin': get_arabic_localized_text(obj.origin),
'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), 'mawla_status': get_localized_text(obj.freed_slave_of, request),
'arabic_mawla_status': get_arabic_localized_text(obj.freed_slave_of), '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),
'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), 'birth_year_hijri': format_transmitter_date(obj, 'birth', request),
'death_year_hijri': format_transmitter_date(obj, 'death', request), 'death_year_hijri': format_transmitter_date(obj, 'death', request),
'age_at_death': format_transmitter_date(obj, 'age', request), 'age_at_death': format_transmitter_date(obj, 'age', request),
@ -928,7 +1130,8 @@ class TransmitterSyncSerializer(serializers.ModelSerializer):
'age_at_death_raw': obj.age_at_death, 'age_at_death_raw': obj.age_at_death,
'generation': obj.generation, 'generation': obj.generation,
'reliability': r, 'reliability': r,
'madhhab': obj.madhhab,
'madhhab': madhhab_val,
'madhhabs': _serialize_transmitter_m2m_items(madhhabs_objs, request, include_description=True),
'in_sahih_muslim': obj.in_sahih_muslim, 'in_sahih_muslim': obj.in_sahih_muslim,
'in_sahih_bukhari': obj.in_sahih_bukhari, 'in_sahih_bukhari': obj.in_sahih_bukhari,
'description': get_localized_text(obj.description, request), 'description': get_localized_text(obj.description, request),
@ -936,47 +1139,14 @@ class TransmitterSyncSerializer(serializers.ModelSerializer):
'share_link': obj.share_link, 'share_link': obj.share_link,
'tadlis': obj.tadlis, 'tadlis': obj.tadlis,
'ikhtilat': obj.ikhtilat, 'ikhtilat': obj.ikhtilat,
'companion_type': get_localized_text(obj.companion_type, request),
'arabic_companion_type': get_arabic_localized_text(obj.companion_type),
'companion_type': companion_val,
'arabic_companion_type': arabic_companion_val,
'sahabi_status': sahabi_status_data,
'relatives': self._get_relatives(obj, request), 'relatives': self._get_relatives(obj, request),
} }
def _get_relatives(self, obj, request): def _get_relatives(self, obj, request):
relatives_qs = obj.relatives.select_related('relation_type_ref', 'narrator').all()
grouped = {}
for rel in relatives_qs:
rel_type = (rel.relation_type_ref.code or rel.relation_type_ref.slug or rel.relation_type) if rel.relation_type_ref else rel.relation_type
rel_val_src = (rel.relation_type_ref.title if (rel.relation_type_ref and rel.relation_type_ref.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)
item = {
"name": 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
return _serialize_transmitter_relatives(obj, request)
def get_scholars_opinions(self, obj): def get_scholars_opinions(self, obj):
"""Get all scholarly opinions about this transmitter""" """Get all scholarly opinions about this transmitter"""
@ -1172,11 +1342,12 @@ class HadisReferenceSerializer(serializers.ModelSerializer):
edition = obj.book_reference.editions.first() edition = obj.book_reference.editions.first()
if edition: if edition:
request = self.context.get("request") request = self.context.get("request")
pub = edition.publisher
if isinstance(pub, list):
pub = get_localized_text(pub, 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 { return {
"id": edition.id, "id": edition.id,
"title": ed_title or "",
"edition_number": edition.edition_number, "edition_number": edition.edition_number,
"publisher": pub or "", "publisher": pub or "",
"year_of_publication": edition.year_of_publication or "", "year_of_publication": edition.year_of_publication or "",
@ -1254,7 +1425,7 @@ class HadisReferenceSerializer(serializers.ModelSerializer):
pub = getattr(obj.book_reference, "publisher", None) pub = getattr(obj.book_reference, "publisher", None)
if pub is None and first_ed: if pub is None and first_ed:
pub = first_ed.publisher
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): if isinstance(pub, list):
pub = get_localized_text(pub, request) pub = get_localized_text(pub, request)
@ -1350,28 +1521,29 @@ class HadisReferenceSerializer(serializers.ModelSerializer):
# 3. Loop through authors and convert each object to a string (name) # 3. Loop through authors and convert each object to a string (name)
# Result will be like: ["Al-Bukhari", "Muslim"] # Result will be like: ["Al-Bukhari", "Muslim"]
out = [] out = []
lang = _extract_request_lang(self.context.get('request'))
for author in authors: for author in authors:
name = field.to_representation(author.name) name = field.to_representation(author.name)
# Format birth and death dates (Hijri/CE) # Format birth and death dates (Hijri/CE)
birth_hijri = author.birth_year_hijri
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 birth_miladi = author.birth_year_miladi
death_hijri = author.death_year_hijri
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 death_miladi = author.death_year_miladi
birth_str = "" birth_str = ""
if birth_hijri and birth_miladi: if birth_hijri and birth_miladi:
birth_str = f"{birth_hijri}AH/{birth_miladi}CE"
birth_str = f"{birth_hijri} / {birth_miladi}CE"
elif birth_hijri: elif birth_hijri:
birth_str = f"{birth_hijri}AH"
birth_str = f"{birth_hijri}"
elif birth_miladi: elif birth_miladi:
birth_str = f"{birth_miladi}CE" birth_str = f"{birth_miladi}CE"
death_str = "" death_str = ""
if death_hijri and death_miladi: if death_hijri and death_miladi:
death_str = f"{death_hijri}AH/{death_miladi}CE"
death_str = f"{death_hijri} / {death_miladi}CE"
elif death_hijri: elif death_hijri:
death_str = f"{death_hijri}AH"
death_str = f"{death_hijri}"
elif death_miladi: elif death_miladi:
death_str = f"{death_miladi}CE" death_str = f"{death_miladi}CE"
@ -1624,7 +1796,13 @@ class NarratorRelationSerializer(serializers.ModelSerializer):
سریالایزر سبک برای نمایش راویان به عنوان استاد یا شاگرد سریالایزر سبک برای نمایش راویان به عنوان استاد یا شاگرد
""" """
full_name = LocalizedField() full_name = LocalizedField()
arabic_full_name = ArabicLocalizedField(source='full_name')
kunya = LocalizedField()
arabic_kunya = ArabicLocalizedField(source='kunya')
known_as = LocalizedField() known_as = LocalizedField()
arabic_known_as = ArabicLocalizedField(source='known_as')
nickname = LocalizedField()
arabic_nickname = ArabicLocalizedField(source='nickname')
birth_year_hijri = serializers.SerializerMethodField() birth_year_hijri = serializers.SerializerMethodField()
death_year_hijri = serializers.SerializerMethodField() death_year_hijri = serializers.SerializerMethodField()
birth_year_raw = serializers.CharField(source='birth_year_hijri', read_only=True) birth_year_raw = serializers.CharField(source='birth_year_hijri', read_only=True)
@ -1634,7 +1812,8 @@ class NarratorRelationSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Transmitters model = Transmitters
fields = [ fields = [
'id', 'slug', 'full_name', 'known_as',
'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', 'birth_year_hijri', 'death_year_hijri', 'birth_year_raw', 'death_year_raw',
'reliability', 'gender' 'reliability', 'gender'
] ]
@ -1652,10 +1831,12 @@ class NarratorRelationSerializer(serializers.ModelSerializer):
return { return {
'id': obj.reliability.id, 'id': obj.reliability.id,
'title': get_localized_text(obj.reliability.title, request), 'title': get_localized_text(obj.reliability.title, request),
'slug': obj.reliability.slug,
'color': obj.reliability.color, 'color': obj.reliability.color,
'main_color_code': obj.reliability.main_color_code, 'main_color_code': obj.reliability.main_color_code,
'description': get_localized_text(obj.reliability.description, request), 'description': get_localized_text(obj.reliability.description, request),
} }
return None
# --- سریالایزرهای کمکی تصاویر رفرنس --- # --- سریالایزرهای کمکی تصاویر رفرنس ---
@ -1717,11 +1898,12 @@ class BaseDetailedReferenceSerializer(serializers.ModelSerializer):
edition = obj.book_reference.editions.first() edition = obj.book_reference.editions.first()
if edition: if edition:
request = self.context.get("request") request = self.context.get("request")
pub = edition.publisher
if isinstance(pub, list):
pub = get_localized_text(pub, 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 { return {
"id": edition.id, "id": edition.id,
"title": ed_title or "",
"edition_number": edition.edition_number, "edition_number": edition.edition_number,
"publisher": pub or "", "publisher": pub or "",
"year_of_publication": edition.year_of_publication or "", "year_of_publication": edition.year_of_publication or "",
@ -1779,7 +1961,7 @@ class DetailedCorrectionReferenceSerializer(BaseDetailedReferenceSerializer):
pub = getattr(obj.book_reference, "publisher", None) pub = getattr(obj.book_reference, "publisher", None)
if pub is None and first_ed: if pub is None and first_ed:
pub = first_ed.publisher
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): if isinstance(pub, list):
pub = get_localized_text(pub, request) pub = get_localized_text(pub, request)
@ -1815,24 +1997,7 @@ class DetailedCorrectionReferenceSerializer(BaseDetailedReferenceSerializer):
return None return None
def get_edition(self, obj): def get_edition(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 = edition.publisher
if isinstance(pub, list):
pub = get_localized_text(pub, request)
return {
'id': edition.id,
'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
return self.get_edition_info(obj)
def get_open_book_url(self, obj): def get_open_book_url(self, obj):
request = self.context.get('request') request = self.context.get('request')

282
apps/hadis/serializers/reference_v2.py

@ -95,8 +95,23 @@ class BookReferenceV2ListSerializer(serializers.ModelSerializer):
return absolute_https_url(first_image.image.url) return absolute_https_url(first_image.image.url)
return None return None
def _get_book_editions(obj):
seen = set()
result = []
for ed in obj.editions.all():
seen.add(ed.id)
result.append(ed)
if hasattr(obj, 'editions_list'):
for ed in obj.editions_list.all():
if ed.id not in seen:
seen.add(ed.id)
result.append(ed)
return result
class BookEditionV2Serializer(serializers.ModelSerializer): class BookEditionV2Serializer(serializers.ModelSerializer):
publisher = LocalizedField()
title = LocalizedField()
publisher = serializers.SerializerMethodField()
notes = LocalizedField() notes = LocalizedField()
tag = BookTagV2Serializer(read_only=True) tag = BookTagV2Serializer(read_only=True)
publication_place = serializers.SerializerMethodField() publication_place = serializers.SerializerMethodField()
@ -107,11 +122,19 @@ class BookEditionV2Serializer(serializers.ModelSerializer):
class Meta: class Meta:
model = BookEdition model = BookEdition
fields = [ fields = [
'id', 'publisher', 'publication_place', 'edition_number',
'id', 'title', 'publisher', 'publication_place', 'edition_number',
'year_of_publication', 'number_of_volumes', 'notes', 'source_url', 'tag', 'year_of_publication', 'number_of_volumes', 'notes', 'source_url', 'tag',
'researchers', 'editors' 'researchers', 'editors'
] ]
def get_publisher(self, obj):
request = self.context.get("request")
if getattr(obj, "publisher_item_id", None) and obj.publisher_item and obj.publisher_item.title:
val = get_localized_text(obj.publisher_item.title, request)
if val:
return val
return get_localized_text(obj.publisher, request) if obj.publisher else None
def get_number_of_volumes(self, obj): def get_number_of_volumes(self, obj):
count = obj.volumes.count() count = obj.volumes.count()
if count > 0: if count > 0:
@ -120,8 +143,18 @@ class BookEditionV2Serializer(serializers.ModelSerializer):
def get_publication_place(self, obj): def get_publication_place(self, obj):
request = self.context.get("request") request = self.context.get("request")
city = get_localized_text(obj.city_of_publication, request) if obj.city_of_publication else ""
country = get_localized_text(obj.country_of_publication, request) if obj.country_of_publication else ""
city_src = (
obj.city_of_publication_city.title
if (getattr(obj, "city_of_publication_city_id", None) and obj.city_of_publication_city and obj.city_of_publication_city.title)
else obj.city_of_publication
)
country_src = (
obj.country_of_publication_country.title
if (getattr(obj, "country_of_publication_country_id", None) and obj.country_of_publication_country and obj.country_of_publication_country.title)
else obj.country_of_publication
)
city = get_localized_text(city_src, request) if city_src else ""
country = get_localized_text(country_src, request) if country_src else ""
return { return {
"city": city, "city": city,
"country": country "country": country
@ -193,7 +226,8 @@ class BookReferenceV2DetailSerializer(serializers.ModelSerializer):
def get_information(self, obj): def get_information(self, obj):
request = self.context.get("request") request = self.context.get("request")
summary = get_localized_text(obj.description, request) if obj.description else "" summary = get_localized_text(obj.description, request) if obj.description else ""
valid_editions = [ed for ed in obj.editions.all() if ed.volumes.exists()]
all_editions = _get_book_editions(obj)
valid_editions = [ed for ed in all_editions if ed.volumes.exists()] or all_editions
editions = BookEditionV2Serializer(valid_editions, many=True, context=self.context).data editions = BookEditionV2Serializer(valid_editions, many=True, context=self.context).data
return { return {
@ -203,15 +237,18 @@ class BookReferenceV2DetailSerializer(serializers.ModelSerializer):
def get_volumes(self, obj): def get_volumes(self, obj):
if obj.has_editions: if obj.has_editions:
editions = obj.editions.all()
editions = _get_book_editions(obj)
editions_data = [] editions_data = []
request = self.context.get("request")
for ed in editions: for ed in editions:
vol_qs = ed.volumes.all() vol_qs = ed.volumes.all()
if not vol_qs.exists(): if not vol_qs.exists():
continue continue
ed_title = get_localized_text(ed.title, request) if ed.title else None
editions_data.append({ editions_data.append({
"edition_id": ed.id, "edition_id": ed.id,
"edition_statement": ed.edition_number or f"Edition {ed.id}",
"edition_title": ed_title,
"edition_statement": ed.edition_number or ed_title or f"Edition {ed.id}",
"volumes": BookVolumeV2Serializer(vol_qs, many=True, context=self.context).data "volumes": BookVolumeV2Serializer(vol_qs, many=True, context=self.context).data
}) })
return { return {
@ -251,7 +288,8 @@ class BookReferenceV2SyncSerializer(serializers.ModelSerializer):
def get_information(self, obj): def get_information(self, obj):
request = self.context.get("request") request = self.context.get("request")
summary = get_localized_text(obj.description, request) if obj.description else "" summary = get_localized_text(obj.description, request) if obj.description else ""
valid_editions = [ed for ed in obj.editions.all() if ed.volumes.exists()]
all_editions = _get_book_editions(obj)
valid_editions = [ed for ed in all_editions if ed.volumes.exists()] or all_editions
editions = BookEditionV2Serializer(valid_editions, many=True, context=self.context).data editions = BookEditionV2Serializer(valid_editions, many=True, context=self.context).data
return { return {
"summary": summary, "summary": summary,
@ -267,15 +305,18 @@ class BookReferenceV2SyncSerializer(serializers.ModelSerializer):
def get_volumes(self, obj): def get_volumes(self, obj):
if obj.has_editions: if obj.has_editions:
editions = obj.editions.all()
editions = _get_book_editions(obj)
editions_data = [] editions_data = []
request = self.context.get("request")
for ed in editions: for ed in editions:
vol_qs = ed.volumes.all() vol_qs = ed.volumes.all()
if not vol_qs.exists(): if not vol_qs.exists():
continue continue
ed_title = get_localized_text(ed.title, request) if ed.title else None
editions_data.append({ editions_data.append({
"edition_id": ed.id, "edition_id": ed.id,
"edition_statement": ed.edition_number or f"Edition {ed.id}",
"edition_title": ed_title,
"edition_statement": ed.edition_number or ed_title or f"Edition {ed.id}",
"volumes": BookVolumeV2Serializer(vol_qs, many=True, context=self.context).data "volumes": BookVolumeV2Serializer(vol_qs, many=True, context=self.context).data
}) })
return { return {
@ -291,7 +332,7 @@ class BookReferenceV2SyncSerializer(serializers.ModelSerializer):
} }
from apps.hadis.models.reference import BookAuthor, BookReference from apps.hadis.models.reference import BookAuthor, BookReference
from .category import LocalizedField
from .category import LocalizedField, ArabicLocalizedField, get_arabic_localized_text
def _get_request_lang_v2(serializer_instance) -> str: def _get_request_lang_v2(serializer_instance) -> str:
from django.utils.translation import get_language from django.utils.translation import get_language
@ -304,58 +345,60 @@ def _get_request_lang_v2(serializer_instance) -> str:
return str(lang).split(",")[0].split("-")[0].strip().lower() or "ru" return str(lang).split(",")[0].split("-")[0].strip().lower() or "ru"
class BookAuthorListSerializer(serializers.ModelSerializer):
"""سریالایزر برای لیست نویسنده‌ها"""
name = LocalizedField()
known_as = LocalizedField()
scholarity = LocalizedField()
tabaqa = LocalizedField()
rutba = serializers.SerializerMethodField()
features = LocalizedField()
related_narrator_slug = serializers.CharField(source='related_narrator.slug', read_only=True, allow_null=True)
birth_year_hijri = serializers.SerializerMethodField()
death_year_hijri = serializers.SerializerMethodField()
age_at_death = serializers.SerializerMethodField()
birth_year_canonical = serializers.CharField(source='birth_year_hijri', read_only=True)
death_year_canonical = serializers.CharField(source='death_year_hijri', read_only=True)
age_at_death_canonical = serializers.CharField(source='age_at_death', read_only=True)
class Meta:
model = BookAuthor
fields = [
'id', 'slug', 'name', 'image',
'known_as', 'scholarity', 'tabaqa', 'rutba', 'features',
'birth_year_hijri', 'death_year_hijri', 'age_at_death',
'birth_year_canonical', 'death_year_canonical', 'age_at_death_canonical',
'birth_year_miladi', 'death_year_miladi',
'related_narrator_slug',
]
def get_rutba(self, obj):
request = self.context.get('request')
if obj.rutba_status:
return get_localized_text(obj.rutba_status.title, request)
return get_localized_text(obj.rutba, request) if obj.rutba else None
def get_birth_year_hijri(self, obj):
return obj.get_formatted_birth_year(lang=_get_request_lang_v2(self))
def get_death_year_hijri(self, obj):
return obj.get_formatted_death_year(lang=_get_request_lang_v2(self))
def get_age_at_death(self, obj):
return obj.get_formatted_age(lang=_get_request_lang_v2(self))
def _resolve_author_city(city_obj, fallback_json, request, arabic=False):
if city_obj and getattr(city_obj, 'title', None):
val = get_arabic_localized_text(city_obj.title) if arabic else get_localized_text(city_obj.title, request)
if val:
return val
if fallback_json:
return get_arabic_localized_text(fallback_json) if arabic else get_localized_text(fallback_json, request)
return None
def _resolve_author_features(obj, request, arabic=False):
parts = []
for item in obj.features_list.all():
txt = get_arabic_localized_text(item.title) if arabic else get_localized_text(item.title, request)
if txt and str(txt).strip():
parts.append(str(txt).strip())
if parts:
sep = '، ' if arabic else ', '
return sep.join(parts)
if obj.features:
return get_arabic_localized_text(obj.features) if arabic else get_localized_text(obj.features, request)
return None
def _serialize_author_features_detail(obj, request):
return [
{
'id': item.id,
'slug': item.slug,
'title': get_localized_text(item.title, request),
'arabic_title': get_arabic_localized_text(item.title),
}
for item in obj.features_list.all()
]
class BookAuthorDetailSerializer(serializers.ModelSerializer):
"""سریالایزر برای دیتیل یک نویسنده"""
class _BaseBookAuthorV2Mixin(serializers.Serializer):
name = LocalizedField() name = LocalizedField()
arabic_name = ArabicLocalizedField(source='name')
known_as = LocalizedField() known_as = LocalizedField()
arabic_known_as = ArabicLocalizedField(source='known_as')
scholarity = LocalizedField() scholarity = LocalizedField()
tabaqa = LocalizedField() tabaqa = LocalizedField()
rutba = serializers.SerializerMethodField() rutba = serializers.SerializerMethodField()
rutba_detail = serializers.SerializerMethodField() rutba_detail = serializers.SerializerMethodField()
features = LocalizedField()
features = serializers.SerializerMethodField()
arabic_features = serializers.SerializerMethodField()
features_detail = serializers.SerializerMethodField()
origin = serializers.SerializerMethodField()
arabic_origin = serializers.SerializerMethodField()
lived_in = serializers.SerializerMethodField()
arabic_lived_in = serializers.SerializerMethodField()
died_in = serializers.SerializerMethodField()
arabic_died_in = serializers.SerializerMethodField()
related_narrator_slug = serializers.CharField(source='related_narrator.slug', read_only=True, allow_null=True) related_narrator_slug = serializers.CharField(source='related_narrator.slug', read_only=True, allow_null=True)
birth_year_hijri = serializers.SerializerMethodField() birth_year_hijri = serializers.SerializerMethodField()
death_year_hijri = serializers.SerializerMethodField() death_year_hijri = serializers.SerializerMethodField()
@ -364,17 +407,6 @@ class BookAuthorDetailSerializer(serializers.ModelSerializer):
death_year_canonical = serializers.CharField(source='death_year_hijri', read_only=True) death_year_canonical = serializers.CharField(source='death_year_hijri', read_only=True)
age_at_death_canonical = serializers.CharField(source='age_at_death', read_only=True) age_at_death_canonical = serializers.CharField(source='age_at_death', read_only=True)
class Meta:
model = BookAuthor
fields = [
'id', 'slug', 'name', 'image', 'known_as', 'scholarity',
'tabaqa', 'rutba', 'rutba_detail', 'features',
'birth_year_hijri', 'death_year_hijri', 'age_at_death',
'birth_year_canonical', 'death_year_canonical', 'age_at_death_canonical',
'birth_year_miladi', 'death_year_miladi',
'related_narrator_slug'
]
def get_birth_year_hijri(self, obj): def get_birth_year_hijri(self, obj):
return obj.get_formatted_birth_year(lang=_get_request_lang_v2(self)) return obj.get_formatted_birth_year(lang=_get_request_lang_v2(self))
@ -403,6 +435,69 @@ class BookAuthorDetailSerializer(serializers.ModelSerializer):
} }
return None return None
def get_features(self, obj):
return _resolve_author_features(obj, self.context.get('request'), arabic=False)
def get_arabic_features(self, obj):
return _resolve_author_features(obj, self.context.get('request'), arabic=True)
def get_features_detail(self, obj):
return _serialize_author_features_detail(obj, self.context.get('request'))
def get_origin(self, obj):
return _resolve_author_city(obj.origin_city, obj.origin, self.context.get('request'), arabic=False)
def get_arabic_origin(self, obj):
return _resolve_author_city(obj.origin_city, obj.origin, self.context.get('request'), arabic=True)
def get_lived_in(self, obj):
return _resolve_author_city(obj.lived_in_city, obj.lived_in, self.context.get('request'), arabic=False)
def get_arabic_lived_in(self, obj):
return _resolve_author_city(obj.lived_in_city, obj.lived_in, self.context.get('request'), arabic=True)
def get_died_in(self, obj):
return _resolve_author_city(obj.died_in_city, obj.died_in, self.context.get('request'), arabic=False)
def get_arabic_died_in(self, obj):
return _resolve_author_city(obj.died_in_city, obj.died_in, self.context.get('request'), arabic=True)
class BookAuthorListSerializer(_BaseBookAuthorV2Mixin, serializers.ModelSerializer):
"""سریالایزر برای لیست نویسنده‌ها"""
class Meta:
model = BookAuthor
fields = [
'id', 'slug', 'name', 'arabic_name', 'image',
'known_as', 'arabic_known_as', 'scholarity', 'tabaqa',
'rutba', 'rutba_detail',
'features', 'arabic_features', 'features_detail',
'origin', 'arabic_origin', 'lived_in', 'arabic_lived_in', 'died_in', 'arabic_died_in',
'birth_year_hijri', 'death_year_hijri', 'age_at_death',
'birth_year_canonical', 'death_year_canonical', 'age_at_death_canonical',
'birth_year_miladi', 'death_year_miladi',
'related_narrator_slug',
]
class BookAuthorDetailSerializer(_BaseBookAuthorV2Mixin, serializers.ModelSerializer):
"""سریالایزر برای دیتیل یک نویسنده"""
class Meta:
model = BookAuthor
fields = [
'id', 'slug', 'name', 'arabic_name', 'image',
'known_as', 'arabic_known_as', 'scholarity', 'tabaqa',
'rutba', 'rutba_detail',
'features', 'arabic_features', 'features_detail',
'origin', 'arabic_origin', 'lived_in', 'arabic_lived_in', 'died_in', 'arabic_died_in',
'birth_year_hijri', 'death_year_hijri', 'age_at_death',
'birth_year_canonical', 'death_year_canonical', 'age_at_death_canonical',
'birth_year_miladi', 'death_year_miladi',
'related_narrator_slug',
]
class AuthorBookReferenceSerializer(serializers.ModelSerializer): class AuthorBookReferenceSerializer(serializers.ModelSerializer):
"""سریالایزر برای لیست رفرنس‌های (کتاب‌های) یک نویسنده""" """سریالایزر برای لیست رفرنس‌های (کتاب‌های) یک نویسنده"""
@ -437,26 +532,11 @@ class BookAuthorSyncReferenceSerializer(serializers.ModelSerializer):
model = BookReference model = BookReference
fields = ['id', 'slug', 'title'] fields = ['id', 'slug', 'title']
class BookAuthorSyncSerializer(serializers.ModelSerializer):
class BookAuthorSyncSerializer(_BaseBookAuthorV2Mixin, serializers.ModelSerializer):
""" """
سریالایزر برای سینک آفلاین تمام نویسنده‌ها. سریالایزر برای سینک آفلاین تمام نویسنده‌ها.
شامل تمام اطلاعات و لیست کتاب‌های متصل به این نویسنده. شامل تمام اطلاعات و لیست کتاب‌های متصل به این نویسنده.
""" """
name = LocalizedField()
known_as = LocalizedField()
scholarity = LocalizedField()
tabaqa = LocalizedField()
rutba = serializers.SerializerMethodField()
rutba_detail = serializers.SerializerMethodField()
features = LocalizedField()
related_narrator_slug = serializers.CharField(source='related_narrator.slug', read_only=True, allow_null=True)
birth_year_hijri = serializers.SerializerMethodField()
death_year_hijri = serializers.SerializerMethodField()
age_at_death = serializers.SerializerMethodField()
birth_year_canonical = serializers.CharField(source='birth_year_hijri', read_only=True)
death_year_canonical = serializers.CharField(source='death_year_hijri', read_only=True)
age_at_death_canonical = serializers.CharField(source='age_at_death', read_only=True)
# اضافه کردن لیست کتاب‌ها # اضافه کردن لیست کتاب‌ها
books = BookAuthorSyncReferenceSerializer(source='book_references', many=True, read_only=True) books = BookAuthorSyncReferenceSerializer(source='book_references', many=True, read_only=True)
@ -466,12 +546,22 @@ class BookAuthorSyncSerializer(serializers.ModelSerializer):
'id', 'id',
'slug', 'slug',
'name', 'name',
'arabic_name',
'known_as', 'known_as',
'arabic_known_as',
'scholarity', 'scholarity',
'tabaqa', 'tabaqa',
'rutba', 'rutba',
'rutba_detail', 'rutba_detail',
'features', 'features',
'arabic_features',
'features_detail',
'origin',
'arabic_origin',
'lived_in',
'arabic_lived_in',
'died_in',
'arabic_died_in',
'birth_year_hijri', 'birth_year_hijri',
'death_year_hijri', 'death_year_hijri',
'age_at_death', 'age_at_death',
@ -483,31 +573,3 @@ class BookAuthorSyncSerializer(serializers.ModelSerializer):
'books', # 👈 لیست کتاب‌های هر نویسنده 'books', # 👈 لیست کتاب‌های هر نویسنده
'related_narrator_slug' 'related_narrator_slug'
] ]
def get_birth_year_hijri(self, obj):
return obj.get_formatted_birth_year(lang=_get_request_lang_v2(self))
def get_death_year_hijri(self, obj):
return obj.get_formatted_death_year(lang=_get_request_lang_v2(self))
def get_age_at_death(self, obj):
return obj.get_formatted_age(lang=_get_request_lang_v2(self))
def get_rutba(self, obj):
request = self.context.get('request')
if obj.rutba_status:
return get_localized_text(obj.rutba_status.title, request)
return get_localized_text(obj.rutba, request) if obj.rutba else None
def get_rutba_detail(self, obj):
if obj.rutba_status:
request = self.context.get('request')
return {
'id': obj.rutba_status.id,
'title': get_localized_text(obj.rutba_status.title, request),
'slug': obj.rutba_status.slug,
'color': obj.rutba_status.color,
'main_color_code': obj.rutba_status.main_color_code,
'description': get_localized_text(obj.rutba_status.description, request),
}
return None

52
apps/hadis/views/category.py

@ -669,10 +669,16 @@ class HadisCategoryXMindView(APIView):
# Fetch active hadiths with their transmitters and corrections # Fetch active hadiths with their transmitters and corrections
transmitter_qs = HadisTransmitter.objects.select_related( transmitter_qs = HadisTransmitter.objects.select_related(
'transmitter__reliability', 'transmitter__reliability',
'transmitter__sahabi_status',
'transmitter__origin_city',
'transmitter__lived_in_city',
'transmitter__died_in_city',
'status', 'status',
'narrator_layer' 'narrator_layer'
).prefetch_related( ).prefetch_related(
'uncertain_transmitters__reliability'
'transmitter__narrator_dates__year_value',
'uncertain_transmitters__reliability',
'uncertain_transmitters__narrator_dates__year_value',
).order_by('order') ).order_by('order')
hadiths = Hadis.objects.filter( hadiths = Hadis.objects.filter(
@ -793,6 +799,7 @@ class HadisCategoryXMindView(APIView):
"color": getattr(rel_obj, 'color', None), "color": getattr(rel_obj, 'color', None),
"main_color_code": getattr(rel_obj, 'main_color_code', None), "main_color_code": getattr(rel_obj, 'main_color_code', None),
"slug": getattr(rel_obj, 'slug', None), "slug": getattr(rel_obj, 'slug', None),
"description": self.get_localized(getattr(rel_obj, 'description', None), lang),
} }
layer_obj = ht.narrator_layer layer_obj = ht.narrator_layer
@ -818,6 +825,7 @@ class HadisCategoryXMindView(APIView):
"color": getattr(ut_rel, 'color', None), "color": getattr(ut_rel, 'color', None),
"main_color_code": getattr(ut_rel, 'main_color_code', None), "main_color_code": getattr(ut_rel, 'main_color_code', None),
"slug": getattr(ut_rel, 'slug', None), "slug": getattr(ut_rel, 'slug', None),
"description": self.get_localized(getattr(ut_rel, 'description', None), lang),
} }
uncertain_candidates.append({ uncertain_candidates.append({
"id": ut.id, "id": ut.id,
@ -834,8 +842,12 @@ class HadisCategoryXMindView(APIView):
"arabic_known_as": self.get_arabic(ut.known_as), "arabic_known_as": self.get_arabic(ut.known_as),
"nickname": self.get_localized(ut.nickname, lang), "nickname": self.get_localized(ut.nickname, lang),
"arabic_nickname": self.get_arabic(ut.nickname), "arabic_nickname": self.get_arabic(ut.nickname),
"birth_year_hijri": ut.birth_year_hijri,
"death_year_hijri": ut.death_year_hijri,
"birth_year_hijri": ut.get_formatted_birth_year(lang=lang),
"death_year_hijri": ut.get_formatted_death_year(lang=lang),
"age_at_death": ut.get_formatted_age(lang=lang),
"birth_year_raw": ut.birth_year_hijri,
"death_year_raw": ut.death_year_hijri,
"age_at_death_raw": ut.age_at_death,
"reliability": ut_rel_data, "reliability": ut_rel_data,
"thumbnail": self._get_thumbnail_url(ut.thumbnail, request), "thumbnail": self._get_thumbnail_url(ut.thumbnail, request),
"share_link": ut.share_link, "share_link": ut.share_link,
@ -844,6 +856,16 @@ class HadisCategoryXMindView(APIView):
node_type_str = "uncertain" if is_node_uncertain else "narrator" node_type_str = "uncertain" if is_node_uncertain else "narrator"
node_id_str = f"uncertain-{'-'.join(map(str, cand_ids))}" if is_node_uncertain else f"narrator-{tr.id if tr else ht.id}" node_id_str = f"uncertain-{'-'.join(map(str, cand_ids))}" if is_node_uncertain else f"narrator-{tr.id if tr else ht.id}"
tr_companion = None
tr_arabic_companion = None
if tr:
if tr.sahabi_status and tr.sahabi_status.title:
tr_companion = self.get_localized(tr.sahabi_status.title, lang)
tr_arabic_companion = self.get_arabic(tr.sahabi_status.title)
else:
tr_companion = self.get_localized(tr.companion_type, lang)
tr_arabic_companion = self.get_arabic(tr.companion_type)
tr_node_data = { tr_node_data = {
"id": node_id_str, "id": node_id_str,
"transmitter_id": tr.id if tr else (uncertain_candidates[0]["id"] if uncertain_candidates else None), "transmitter_id": tr.id if tr else (uncertain_candidates[0]["id"] if uncertain_candidates else None),
@ -860,13 +882,29 @@ class HadisCategoryXMindView(APIView):
"arabic_known_as": self.get_arabic(tr.known_as) if tr else None, "arabic_known_as": self.get_arabic(tr.known_as) if tr else None,
"nickname": self.get_localized(tr.nickname, lang) if tr else None, "nickname": self.get_localized(tr.nickname, lang) if tr else None,
"arabic_nickname": self.get_arabic(tr.nickname) if tr else None, "arabic_nickname": self.get_arabic(tr.nickname) if tr else None,
"birth_year_hijri": tr.birth_year_hijri if tr else None,
"death_year_hijri": tr.death_year_hijri if tr else None,
"age_at_death": tr.age_at_death if tr else None,
"origin": self.get_localized(tr.origin_city.title if (tr.origin_city_id and tr.origin_city and tr.origin_city.title) else tr.origin, lang) if tr else None,
"arabic_origin": self.get_arabic(tr.origin_city.title if (tr.origin_city_id and tr.origin_city and tr.origin_city.title) else tr.origin) if tr else None,
"lived_in": self.get_localized(tr.lived_in_city.title if (tr.lived_in_city_id and tr.lived_in_city and tr.lived_in_city.title) else tr.lived_in, lang) if tr else None,
"arabic_lived_in": self.get_arabic(tr.lived_in_city.title if (tr.lived_in_city_id and tr.lived_in_city and tr.lived_in_city.title) else tr.lived_in) if tr else None,
"died_in": self.get_localized(tr.died_in_city.title if (tr.died_in_city_id and tr.died_in_city and tr.died_in_city.title) else tr.died_in, lang) if tr else None,
"arabic_died_in": self.get_arabic(tr.died_in_city.title if (tr.died_in_city_id and tr.died_in_city and tr.died_in_city.title) else tr.died_in) if tr else None,
"mawla_status": self.get_localized(tr.freed_slave_of, lang) if tr else None,
"arabic_mawla_status": self.get_arabic(tr.freed_slave_of) if tr else None,
"profession": (self.get_localized(tr.profession, lang) or self.get_localized(tr.occupation, lang)) if tr else None,
"arabic_profession": (self.get_arabic(tr.profession) or self.get_arabic(tr.occupation)) if tr else None,
"physical_traits": self.get_localized(tr.features, lang) if tr else None,
"arabic_physical_traits": self.get_arabic(tr.features) if tr else None,
"birth_year_hijri": tr.get_formatted_birth_year(lang=lang) if tr else None,
"death_year_hijri": tr.get_formatted_death_year(lang=lang) if tr else None,
"age_at_death": tr.get_formatted_age(lang=lang) if tr else None,
"birth_year_raw": tr.birth_year_hijri if tr else None,
"death_year_raw": tr.death_year_hijri if tr else None,
"age_at_death_raw": tr.age_at_death if tr else None,
"generation": tr.generation if tr else None, "generation": tr.generation if tr else None,
"tadlis": tr.tadlis if tr else None, "tadlis": tr.tadlis if tr else None,
"ikhtilat": tr.ikhtilat if tr else None, "ikhtilat": tr.ikhtilat if tr else None,
"companion_type": tr.companion_type if tr else None,
"companion_type": tr_companion,
"arabic_companion_type": tr_arabic_companion,
"madhhab": tr.madhhab if tr else None, "madhhab": tr.madhhab if tr else None,
"in_sahih_muslim": tr.in_sahih_muslim if tr else None, "in_sahih_muslim": tr.in_sahih_muslim if tr else None,
"in_sahih_bukhari": tr.in_sahih_bukhari if tr else None, "in_sahih_bukhari": tr.in_sahih_bukhari if tr else None,

9
apps/hadis/views/hadis.py

@ -299,6 +299,7 @@ class HadisListView(ListAPIView):
"title": get_localized_text(status.title, request=request, language_code=lang) or "", "title": get_localized_text(status.title, request=request, language_code=lang) or "",
"color": status.color, "color": status.color,
"main_color_code": status.main_color_code, "main_color_code": status.main_color_code,
"description": get_localized_text(status.description, request=request, language_code=lang),
} }
for status in HadisStatus.objects.all().order_by('order') for status in HadisStatus.objects.all().order_by('order')
] ]
@ -1099,8 +1100,13 @@ class HadisFiltersView(ListAPIView):
title_text = get_localized_text(status.title, request) title_text = get_localized_text(status.title, request)
if title_text and status.slug: if title_text and status.slug:
statuses.append({ statuses.append({
'id': status.id,
'text': title_text, 'text': title_text,
'slug': status.slug
'title': title_text,
'slug': status.slug,
'color': status.color,
'main_color_code': status.main_color_code,
'description': get_localized_text(status.description, request),
}) })
# Get categories from HadisCategory model (excluding empty leaf categories) # Get categories from HadisCategory model (excluding empty leaf categories)
@ -1230,6 +1236,7 @@ class HadisFiltersSyncAPIView(APIView):
"order": status.order, "order": status.order,
"color": status.color, "color": status.color,
"main_color_code": status.main_color_code, "main_color_code": status.main_color_code,
"description": get_localized_text(status.description, request=request, language_code=lang),
} }
for status in HadisStatus.objects.all().order_by('order') for status in HadisStatus.objects.all().order_by('order')
] ]

22
apps/hadis/views/reference_v2.py

@ -19,9 +19,19 @@ class BookReferenceV2DetailView(generics.RetrieveAPIView):
.select_related('author', 'tag') .select_related('author', 'tag')
.prefetch_related( .prefetch_related(
'editions__tag', 'editions__tag',
'editions__publisher_item',
'editions__city_of_publication_city',
'editions__country_of_publication_country',
'editions__researchers__author', 'editions__researchers__author',
'editions__editors__author', 'editions__editors__author',
'editions__volumes__images', 'editions__volumes__images',
'editions_list__tag',
'editions_list__publisher_item',
'editions_list__city_of_publication_city',
'editions_list__country_of_publication_country',
'editions_list__researchers__author',
'editions_list__editors__author',
'editions_list__volumes__images',
'volumes__images', 'volumes__images',
'images', 'images',
) )
@ -282,8 +292,8 @@ class BookAuthorListView(ListAPIView):
""" """
queryset = ( queryset = (
BookAuthor.objects BookAuthor.objects
.select_related('related_narrator', 'rutba_status')
.prefetch_related('author_dates__year_value')
.select_related('related_narrator', 'rutba_status', 'origin_city', 'lived_in_city', 'died_in_city')
.prefetch_related('author_dates__year_value', 'features_list')
.order_by('-created_at') .order_by('-created_at')
) )
serializer_class = BookAuthorListSerializer serializer_class = BookAuthorListSerializer
@ -307,8 +317,8 @@ class BookAuthorDetailView(RetrieveAPIView):
""" """
queryset = ( queryset = (
BookAuthor.objects BookAuthor.objects
.select_related('related_narrator', 'rutba_status')
.prefetch_related('author_dates__year_value')
.select_related('related_narrator', 'rutba_status', 'origin_city', 'lived_in_city', 'died_in_city')
.prefetch_related('author_dates__year_value', 'features_list')
.all() .all()
) )
serializer_class = BookAuthorDetailSerializer serializer_class = BookAuthorDetailSerializer
@ -357,8 +367,8 @@ class BookAuthorSyncView(generics.ListAPIView):
# 👇 استفاده از prefetch_related برای جلوگیری از کرش کردن دیتابیس به خاطر N+1 Query # 👇 استفاده از prefetch_related برای جلوگیری از کرش کردن دیتابیس به خاطر N+1 Query
queryset = ( queryset = (
BookAuthor.objects BookAuthor.objects
.select_related('related_narrator', 'rutba_status')
.prefetch_related('book_references', 'author_dates__year_value')
.select_related('related_narrator', 'rutba_status', 'origin_city', 'lived_in_city', 'died_in_city')
.prefetch_related('book_references', 'author_dates__year_value', 'features_list')
.order_by('id') .order_by('id')
) )
serializer_class = BookAuthorSyncSerializer serializer_class = BookAuthorSyncSerializer

29
apps/hadis/views/transmitter.py

@ -99,10 +99,22 @@ class TransmitterDetailView(RetrieveAPIView):
def get_queryset(self): def get_queryset(self):
input = self.kwargs.get('narrator_slug') input = self.kwargs.get('narrator_slug')
return Transmitters.objects.filter(slug=input).select_related('reliability', 'father').prefetch_related(
return Transmitters.objects.filter(slug=input).select_related(
'reliability',
'father',
'sahabi_status',
'origin_city',
'lived_in_city',
'died_in_city',
).prefetch_related(
'narrator_dates__year_value', 'narrator_dates__year_value',
'children_as_father', 'children_as_father',
'relatives__narrator', 'relatives__narrator',
'relatives__relation_type_ref',
'kunyas',
'features_list',
'professions_list',
'madhhabs',
) )
class TransmitterOpinionView(ListAPIView): class TransmitterOpinionView(ListAPIView):
@ -181,9 +193,19 @@ class TransmitterSyncView(ListAPIView):
return ( return (
Transmitters.objects Transmitters.objects
# 👇 ۱. واکشی اطلاعات reliability راوی در همان کوئری اصلی # 👇 ۱. واکشی اطلاعات reliability راوی در همان کوئری اصلی
.select_related('reliability')
.select_related(
'reliability',
'sahabi_status',
'origin_city',
'lived_in_city',
'died_in_city',
)
.prefetch_related( .prefetch_related(
'narrator_dates__year_value', 'narrator_dates__year_value',
'kunyas',
'features_list',
'professions_list',
'madhhabs',
# 👇 ۲. اضافه کردن 'scholar' برای جلوگیری از کوئری اضافه در نظرات دانشمندان # 👇 ۲. اضافه کردن 'scholar' برای جلوگیری از کوئری اضافه در نظرات دانشمندان
Prefetch('opinions', queryset=TransmitterOpinion.objects.select_related('status', 'scholar', 'opinion', 'opinion__status')), Prefetch('opinions', queryset=TransmitterOpinion.objects.select_related('status', 'scholar', 'opinion', 'opinion__status')),
@ -197,7 +219,7 @@ class TransmitterSyncView(ListAPIView):
'references__images' 'references__images'
) )
), ),
Prefetch('relatives', queryset=TransmitterRelative.objects.select_related('narrator'))
Prefetch('relatives', queryset=TransmitterRelative.objects.select_related('narrator', 'relation_type_ref'))
) )
.order_by('id') .order_by('id')
) )
@ -291,6 +313,7 @@ def get_transmitter_filters(request):
'slug': reliability.slug, 'slug': reliability.slug,
'color': reliability.color, 'color': reliability.color,
'main_color_code': reliability.main_color_code, 'main_color_code': reliability.main_color_code,
'description': get_localized_text(reliability.description, request),
}) })
return { return {

Loading…
Cancel
Save