Browse Source

sync apis updated

master
Mohsen Taba 5 months ago
parent
commit
01ee93b0d0
  1. 360
      apps/hadis/docs.py
  2. 2
      apps/hadis/models/hadis.py
  3. 22
      apps/hadis/serializers/category.py
  4. 255
      apps/hadis/serializers/hadis.py
  5. 41
      apps/hadis/serializers/reference.py
  6. 60
      apps/hadis/views/category.py
  7. 15
      apps/hadis/views/hadis.py
  8. 3
      apps/hadis/views/reference.py
  9. 12
      apps/hadis/views/transmitter.py

360
apps/hadis/docs.py

@ -52,99 +52,95 @@ hadis_sect_list_swagger = swagger_auto_schema(
# Swagger documentation for HadisCategoryTreeView
hadis_category_tree_swagger = swagger_auto_schema(
operation_description="Get complete hierarchical tree structure of Hadis categories grouped by sect type (shia/sunni), with enhanced child information including father category details and hadis information",
operation_description=(
"Get complete hierarchical tree of Hadis categories grouped by sect type (shia/sunni). "
"Categories are not grouped by source_type in the response (mobile filters source_type client-side). "
"This sync endpoint returns only category metadata (no hadis payload) for fast offline building of navigation trees."
),
operation_summary="Get Complete Hadis Category Tree",
tags=['Hadis'],
responses={
status.HTTP_200_OK: openapi.Response(
description="Complete hierarchical tree structure of categories grouped by sect type with enhanced child information",
description="Complete hierarchical tree grouped by sect with only category metadata (no hadis payloads)",
examples={
"application/json": {
"count": 12,
"count": 6,
"results": {
"shia": {
"sects": {
"1": {
"id": 1,
"sect_type": "shia",
"title": "Shi'a Hadith Collections",
"description": "Collections of Shi'a hadith",
"title": "Shi'a Collections",
"description": "Shia roots",
"order": 1
}
},
"categories": {
"quran": [
{
"id": 1,
"name": "Tafsir",
"hadis_count": 150,
"has_hadis": False,
"order": 1,
"xmind_file": "http://example.com/media/xmind/tafsir.xmind",
"has_xmind_file": True,
"children": [
{
"id": 2,
"name": "Surah Al-Fatiha",
"hadis_count": 25,
"has_hadis": True,
"order": 1,
"father_category": {
"id": 1,
"name": "Tafsir",
"sect_id": 1,
"sect_type": "shia",
"source_type": "quran"
},
"hadis_details": [
{
"id": 1,
"title": "The Opening",
"title_narrator": "From Abu Hurairah",
"text": "Actions are but by intention...",
"translation": "Actions are but by intention...",
"share_link": "http://example.com/hadis/1"
}
],
"children": []
}
]
}
],
"hadith": []
}
"categories": [
{
"id": 10,
"name": "Tafsir",
"description": "Quran commentary",
"source_type": "quran",
"hadis_count": 2,
"has_hadis": False,
"hadis_index": [],
"order": 1,
"thumbnail": None,
"xmind_file": None,
"has_xmind_file": False,
"children": [
{
"id": 11,
"name": "Surah Al-Fatiha",
"description": "Opening chapter",
"source_type": "quran",
"hadis_count": 2,
"has_hadis": True,
"hadis_index": [1, 2],
"order": 1,
"thumbnail": None,
"xmind_file": None,
"has_xmind_file": False,
"father_category": {
"id": 10,
"name": "Tafsir",
"sect_id": 1,
"sect_type": "shia",
"source_type": "quran"
},
"children": []
}
]
}
]
},
"sunni": {
"sects": {
"2": {
"id": 2,
"sect_type": "sunni",
"title": "Sunni Hadith Collections",
"description": "Collections of Sunni hadith",
"title": "Sunni Collections",
"description": "Sunni roots",
"order": 2
}
},
"categories": {
"hadith": [
{
"id": 10,
"name": "Sahih al-Bukhari",
"hadis_count": 2500,
"has_hadis": True,
"hadis_details": [
{
"id": 100,
"title": "The Beginning of Revelation",
"title_narrator": "From Aisha",
"text": "The first revelation...",
"translation": "The first revelation...",
"share_link": "http://example.com/hadis/100"
}
],
"children": []
}
]
}
"categories": [
{
"id": 20,
"name": "Book of Faith",
"description": "Iman topics",
"source_type": "hadith",
"hadis_count": 1,
"has_hadis": True,
"hadis_index": [50],
"order": 1,
"thumbnail": None,
"xmind_file": None,
"has_xmind_file": False,
"children": []
}
]
}
}
}
@ -159,6 +155,126 @@ hadis_category_tree_swagger = swagger_auto_schema(
# Swagger documentation for HadisSyncView
hadis_sync_swagger = swagger_auto_schema(
operation_description="Get all Hadis data for offline sync. Returns a dictionary keyed by Hadis ID.",
operation_summary="Sync Hadis Data",
operation_id="syncHadisData",
tags=['Hadis'],
manual_parameters=[
openapi.Parameter(
'last_updated',
openapi.IN_QUERY,
description="Timestamp (ISO 8601) to fetch only modified records",
type=openapi.TYPE_STRING,
required=False
)
],
responses={
status.HTTP_200_OK: openapi.Response(
description="Successful Sync Response",
examples={
"application/json": {
"count": 1,
"results":[
{
"id": 1001,
"number": 45,
"category_id": 205,
"title": "The Reward of Intentions",
"title_narrator": "Imam Sadiq (as)",
"text": "إنما الأعمال بالنیات...",
"translations": {
"en": "Actions are but by intentions..."
},
"detail":
{"address": 'null',
"hadis_status": {
"id": 130,
"title": "Прерванный",
"color": "orange"
},
"status_text": 'null',
"share_link": "https://imamjavad.nwhco.ir/hadis/None",
"links": [
{
"link": "https://example.com/source1",
"title": "Source 1"
},
{
"link": "https://example.com/source2",
"title": "Source 2"
}
],
"tags": [
{
"id": 520,
"title": "Постановления"
},
{
"id": 514,
"title": "Терпение"
},
],
"references": [
{
"id": 2193,
"title": 'null',
"authors": [],
"description": 'null'
}
],
"reference_images": [
{
"id": 1768,
"thumbnail": "http://127.0.0.1:8000/media/hadis/reference_images/ref_2193.png",
"priority": 0
}
]
},
"narrators": {
"description": 'null',
"transmitters": [
{
"id": 53,
"name": "Мухаммад ибн аль-Хасан ат-Туси",
"reliability": "unknown",
"layer_level": 'null',
"layer_name": 'null',
"is_gap": 'false',
"birth_year_hijri": 385,
"death_year_hijri": 460,
"order": 1
},
{
"id": 60,
"name": "Мухаммад ибн Муслим",
"reliability": "unknown",
"layer_level": 'null',
"layer_name": 'null',
"is_gap": 'false',
"birth_year_hijri": 70,
"death_year_hijri": 150,
"order": 2
},
]
},
"explanations": "Example explanation...",
"corrections": [
{
"id":"id",
'title':'title',
'description':'description',
'translation':'translation',
'share_link':'share_link'
},
]
}
]
}
}
)
}
)
# Swagger documentation for HadisListView
hadis_list_swagger = swagger_auto_schema(
@ -633,112 +749,6 @@ hadis_info_swagger = swagger_auto_schema(
}
)
# Swagger documentation for HadisSyncView
hadis_sync_swagger = swagger_auto_schema(
operation_description="Get complete hadis data for offline synchronization including all categories, hadis, and related information",
operation_summary="Sync Hadis Data",
operation_id="syncHadisData",
tags=['Hadis'],
responses={
status.HTTP_200_OK: openapi.Response(
description="Complete hadis data for synchronization with enhanced information",
examples={
"application/json": {
"count": 1500,
"results": {
"1": {
"id": 1,
"number": 1,
"category_id": 2,
"title": "The Opening",
"title_narrator": "From Abu Hurairah",
"text": "Actions are but by intention...",
"description": "This hadith emphasizes the importance of intention in all actions...",
"translations": {
"en": "Actions are but by intention...",
"ar": "إنما الأعمال بالنيات...",
"fa": "اعمال به نیت است..."
},
"explanation": "This hadith emphasizes the importance of intention in all actions...",
"address": "Sahih al-Bukhari, Book of Revelation",
"hadis_status": {
"id": 1,
"title": "Sahih",
"color": "green"
},
"hadis_status_text": "Authentic",
"share_link": "http://example.com/hadis/1",
"tags": [
{"id": 1, "title": "Intention"},
{"id": 2, "title": "Actions"}
],
"links": {
"audio": "http://example.com/audio/hadis1.mp3",
"video": "http://example.com/video/hadis1.mp4"
},
"transmitters": [
{
"id": 1,
"order": 1,
"is_gap": False,
"narrator_layer": "sahaba",
"transmitter": {
"id": 1,
"full_name": "Abu Hurairah",
"birth_year_hijri": 18,
"death_year_hijri": 59,
"madhhab": "sunni",
"description": "One of the most prolific narrators of hadith",
"reliability": "very_reliable"
}
}
],
"references": [
{
"id": 1,
"title": "Sahih al-Bukhari",
"images": [
{
"id": 1,
"image": "http://example.com/media/books/bukhari_cover.jpg",
"order": 1,
"description": "Front cover of Sahih al-Bukhari"
}
],
"authors": [
{
"id": 1,
"name": "Muhammad ibn Isma'il al-Bukhari"
}
],
"description": "The most authentic collection of hadith compiled by Imam Bukhari"
}
],
"corrections": [
{
"id": 1,
"title": "Translation Correction",
"description": "Corrected translation for better accuracy",
"translation": {
"en": "Actions are judged by intentions...",
"ar": "إنما الأعمال بالنيات...",
"fa": "اعمال به نیت ها قضاوت می شود..."
}
}
]
}
}
}
}
),
status.HTTP_500_INTERNAL_SERVER_ERROR: openapi.Response(
description="Internal server error"
)
}
)
# Swagger documentation for TransmitterView
transmitter_list_swagger = swagger_auto_schema(
operation_description="Get list of transmitters (narrators) with optional filtering by status, madhhab, and generation",

2
apps/hadis/models/hadis.py

@ -180,7 +180,7 @@ class HadisReference(models.Model):
return f'{self.hadis.number}-{self.book_reference.title if self.book_reference else "No Book Reference"}'
class ReferenceImage(models.Model):
reference = models.ForeignKey(HadisReference, verbose_name="Hadis Reference", on_delete=models.CASCADE)
reference = models.ForeignKey(HadisReference,related_name = 'images', verbose_name="Hadis Reference", on_delete=models.CASCADE)
thumbnail = models.ImageField(upload_to='hadis/reference_images/', null=True, blank=True, verbose_name=_('thumbnail'))
priority = models.IntegerField(
default=0,

22
apps/hadis/serializers/category.py

@ -40,16 +40,9 @@ class HadisCategoryTreeSerializer(serializers.ModelSerializer):
return obj.get_translation(language_code) if hasattr(obj, 'get_translation') else obj.title
def get_children(self, obj):
"""Get active children categories that have children or hadis"""
"""Get all active children categories (no filtering by hadis/children)"""
children = obj.get_children().filter(sect=obj.sect).order_by('order')
# Filter children that have either children or hadis
filtered_children = []
for child in children:
has_children = child.get_children().filter(sect=obj.sect).exists()
has_hadis = Hadis.objects.filter(category=child, status=True).exists()
if has_children or has_hadis:
filtered_children.append(child)
return [self.to_dict(cat) for cat in filtered_children]
return [self.to_dict(cat) for cat in children]
def get_hadis_count(self, obj):
"""Get total hadis count including children categories"""
@ -110,25 +103,20 @@ class HadisCategoryTreeSerializer(serializers.ModelSerializer):
def to_dict(self, c):
"""Convert category to dictionary"""
children = c.get_children().filter(sect=c.sect).order_by('order')
filtered_children = []
for child in children:
has_children = child.get_children().filter(sect=c.sect).exists()
has_hadis = Hadis.objects.filter(category=child, status=True).exists()
if has_children or has_hadis:
filtered_children.append(child)
return {
'id': c.id,
'name': self.get_name(c),
'description': c.description,
'source_type': c.source_type,
'hadis_count': self.get_hadis_count(c),
'has_hadis': self.get_has_hadis(c),
'hadis_index': self.get_hadis_index(c) if self.get_has_hadis(c) else [],
'order': c.order,
'thumbnail': self.get_thumbnail(c),
'xmind_file': self.get_xmind_file(c),
'has_xmind_file': self.get_has_xmind_file(c),
'children': [] if not filtered_children else [self.to_dict(i) for i in filtered_children],
'children_count': 0 if not children else len([self.to_dict(i) for i in children]),
'children': [] if not children else [self.to_dict(i) for i in children],
}
class HadisCategorySelectSerializer(serializers.ModelSerializer):

255
apps/hadis/serializers/hadis.py

@ -1,5 +1,6 @@
from rest_framework import serializers
from django.utils.translation import gettext_lazy as _
from rest_framework.fields import SerializerMethodField
from urllib3 import fields
from ..models import (
@ -27,26 +28,25 @@ class HadisCollectionListSerializer(serializers.ModelSerializer):
class HadisSyncSerializer(serializers.ModelSerializer):
"""Serializer for syncing all hadis data"""
"""Serializer for syncing all hadis data (grouped fields)"""
translations = serializers.SerializerMethodField()
hadis_status = serializers.SerializerMethodField()
tags = serializers.SerializerMethodField()
transmitters = serializers.SerializerMethodField()
description = serializers.SerializerMethodField()
references = serializers.SerializerMethodField()
detail = serializers.SerializerMethodField()
narrators = serializers.SerializerMethodField()
explanations = serializers.SerializerMethodField()
corrections = serializers.SerializerMethodField()
class Meta:
model = Hadis
fields = [
'id', 'number', 'category_id', 'title', 'title_narrator',
'text', 'translations', 'explanation', 'address', 'description',
'hadis_status', 'hadis_status_text', 'share_link', 'tags', 'links',
'transmitters', 'references', 'corrections'
# header (no-extend)
'id', 'number', 'category_id', 'title', 'title_narrator', 'text', 'translations',
# grouped sections
'detail', 'narrators', 'explanations', 'corrections',
]
def get_translations(self, obj):
"""Get all translations"""
"""Get all translations as {lang: text}"""
translations_dict = {}
if obj.translation and isinstance(obj.translation, list):
for tr in obj.translation:
@ -56,88 +56,92 @@ class HadisSyncSerializer(serializers.ModelSerializer):
if lang_code:
translations_dict[lang_code] = title
return translations_dict
def get_hadis_status(self, obj):
"""Get hadis status info"""
def get_detail(self, obj):
"""Detail group: address, status, share, links, tags, references, reference_images"""
request = self.context.get('request')
# status
status_block = None
if obj.hadis_status:
return {
status_block = {
'id': obj.hadis_status.id,
'title': obj.hadis_status.title,
'color': obj.hadis_status.color
}
return None
def get_tags(self, obj):
"""Get tags"""
return [{'id': tag.id, 'title': tag.title} for tag in obj.tags.all()]
def get_transmitters(self, obj):
"""Get transmitters with their details"""
# tags
tags_block = [{'id': tag.id, 'title': tag.title} for tag in obj.tags.all()]
# references and reference images
references_block = []
reference_images_block = []
for reference in obj.references.select_related('book_reference').prefetch_related('book_reference__authors', 'images'):
book = reference.book_reference
references_block.append({
'id': reference.id,
'title': book.title if book else None,
'authors': [{'id': a.id, 'name': a.name} for a in book.authors.all()] if book else [],
'description': book.description if book else None,
})
for img in reference.images.all().order_by('priority'):
thumb_url = None
if img.thumbnail:
thumb_url = request.build_absolute_uri(img.thumbnail.url) if request else img.thumbnail.url
reference_images_block.append({
'id': img.id,
'thumbnail': thumb_url,
'priority': img.priority,
})
return {
'address': obj.address,
'hadis_status': status_block,
'status_text': obj.hadis_status_text,
'share_link': obj.share_link,
'links': obj.links,
'tags': tags_block,
'references': references_block,
'reference_images': reference_images_block,
}
def get_narrators(self, obj):
"""Narrators group: description + transmitters"""
transmitters_data = []
for transmitter_rel in obj.transmitters.all().order_by('order'):
transmitter_info = {
'id': transmitter_rel.id,
'order': transmitter_rel.order,
for transmitter_rel in obj.transmitters.select_related('transmitter', 'narrator_layer').order_by('order'):
t = transmitter_rel.transmitter
transmitters_data.append({
'id': t.id,
'name': t.full_name,
'reliability': t.reliability,
'layer_level': transmitter_rel.narrator_layer.number if transmitter_rel.narrator_layer else None,
'layer_name': transmitter_rel.narrator_layer.name if transmitter_rel.narrator_layer else None,
'is_gap': transmitter_rel.is_gap,
'narrator_layer': transmitter_rel.narrator_layer,
'transmitter': {
'id': transmitter_rel.transmitter.id,
'full_name': transmitter_rel.transmitter.full_name,
'birth_year_hijri': transmitter_rel.transmitter.birth_year_hijri,
'death_year_hijri': transmitter_rel.transmitter.death_year_hijri,
'madhhab': transmitter_rel.transmitter.madhhab,
'description': transmitter_rel.transmitter.description,
'reliability': transmitter_rel.transmitter.reliability
}
}
transmitters_data.append(transmitter_info)
return transmitters_data
'birth_year_hijri': t.birth_year_hijri,
'death_year_hijri': t.death_year_hijri,
'order': transmitter_rel.order,
})
def get_description(self, obj):
"""Get hadis description"""
return getattr(obj, 'description', None)
return {
'description': getattr(obj, 'description', None),
'transmitters': transmitters_data
}
def get_references(self, obj):
"""Get references with book information"""
references_data = []
for reference in obj.references.all():
try:
book = reference.book_reference
reference_info = {
'id': reference.id,
'title': book.title if book else None,
'images': [
{
'id': img.id,
'image': img.image.url if img.image else None,
'order': img.order,
'description': img.description
} for img in book.images.all() if book
] if book else [],
'authors': [
{
'id': author.id,
'name': author.name
} for author in book.authors.all() if book
] if book else [],
'description': book.description if book else None
}
references_data.append(reference_info)
except:
continue
return references_data
def get_explanations(self, obj):
"""Explanations group"""
return obj.explanation
def get_corrections(self, obj):
"""Get hadis corrections"""
"""Corrections group"""
corrections_data = []
for correction in obj.hadiscorrection_set.all():
correction_info = {
corrections_data.append({
'id': correction.id,
'title': correction.title,
'description': correction.description,
'translation': correction.translation
}
corrections_data.append(correction_info)
'translation': correction.translation,
'share_link': correction.share_link,
})
return corrections_data
@ -232,43 +236,39 @@ class TransmitterDetailSerializer(serializers.ModelSerializer):
class TransmitterSyncSerializer(serializers.ModelSerializer):
"""Serializer for syncing all transmitter data for offline mode"""
# Biographical data group
# Biographical data group (flattened)
biographical = serializers.SerializerMethodField()
# Scholar's opinions group
scholars_opinions = serializers.SerializerMethodField()
# Original texts group
original_texts = serializers.SerializerMethodField()
class Meta:
model = Transmitters
fields = [
'id', 'full_name', 'biographical', 'scholars_opinions'
'id', 'full_name', 'biographical', 'scholars_opinions', 'original_texts'
]
def get_biographical(self, obj):
"""Get biographical information"""
"""Get biographical information (flattened)"""
return {
'personal_info': {
'full_name': obj.full_name,
'kunya': obj.kunya,
'known_as': obj.known_as,
'nickname': obj.nickname,
},
'dates': {
'birth_year_hijri': obj.birth_year_hijri,
'death_year_hijri': obj.death_year_hijri,
'age_at_death': obj.age_at_death,
},
'locations': {
'origin': obj.origin,
'lived_in': obj.lived_in,
'died_in': obj.died_in,
},
'religious_profile': {
'reliability': obj.reliability,
'madhhab': obj.madhhab,
'in_sahih_muslim': obj.in_sahih_muslim,
'in_sahih_bukhari': obj.in_sahih_bukhari,
},
'full_name': obj.full_name,
'kunya': obj.kunya,
'known_as': obj.known_as,
'nickname': obj.nickname,
'origin': obj.origin,
'lived_in': obj.lived_in,
'died_in': obj.died_in,
'birth_year_hijri': obj.birth_year_hijri,
'death_year_hijri': obj.death_year_hijri,
'age_at_death': obj.age_at_death,
'generation': obj.generation,
'reliability': obj.reliability,
'madhhab': obj.madhhab,
'in_sahih_muslim': obj.in_sahih_muslim,
'in_sahih_bukhari': obj.in_sahih_bukhari,
'description': obj.description,
'thumbnail': obj.thumbnail.url if obj.thumbnail else None,
}
@ -285,7 +285,20 @@ class TransmitterSyncSerializer(serializers.ModelSerializer):
'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 opinions
return opinions
def get_original_texts(self, obj):
"""Get original texts of the transmitter"""
texts = []
for t in obj.originaltexts.all():
texts.append({
'id': t.id,
'title': t.title,
'text': t.text,
'translation': t.translation,
'share_link': t.share_link,
})
return texts
@ -310,24 +323,23 @@ class ReferenceImageSerializer(serializers.ModelSerializer):
def get_thumbnail(self, obj):
"""Get thumbnail URL"""
if obj.image:
if obj.thumbnail:
request = self.context.get('request')
if request:
return request.build_absolute_uri(obj.image.url)
return obj.image.url
return request.build_absolute_uri(obj.thumbnail.url)
return obj.thumbnail.url
return None
class HadisReferenceSerializer(serializers.ModelSerializer):
"""Serializer for HadisReference with book and images"""
book_title = serializers.SerializerMethodField()
book_images = serializers.SerializerMethodField()
book_authors = serializers.SerializerMethodField()
book_description = serializers.SerializerMethodField()
class Meta:
model = HadisReference
fields = [
'id', 'book_title', 'book_images', 'book_authors', 'book_description'
'id', 'book_title','book_authors', 'book_description'
]
def get_book_title(self, obj):
@ -338,13 +350,13 @@ class HadisReferenceSerializer(serializers.ModelSerializer):
except:
return None
def get_book_images(self, obj):
"""Get book images"""
try :
images = obj.book_reference.images.all()
return images
except:
return None
# def get_book_images(self, obj):
# """Get book images"""
# try :
# images = obj.book_reference.images.all()
# return images
# except:
# return None
def get_book_authors(self, obj):
"""Get book authors"""
@ -414,16 +426,25 @@ class HadisDetailSerializer(serializers.ModelSerializer):
many=True,
read_only=True
)
# category = serializers.SerializerMethodField()
reference_images = SerializerMethodField()
class Meta:
model = Hadis
fields = [
'id', 'number',
'hadis_status_text','hadis_status', 'links','share_link',
'tags', 'references','address'
'tags', 'references','reference_images','address'
]
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:

41
apps/hadis/serializers/reference.py

@ -19,17 +19,13 @@ class BookReferenceSerializer(serializers.ModelSerializer):
read_only = True ,
source = 'bookreference_set'
)
volume_count = serializers.SerializerMethodField()
author = serializers.SerializerMethodField()
class Meta:
model = BookReference
fields = ['id','title','rate','author','description','image','volume_count']
fields = ['id','title','rate','author','description','image','volume']
def get_author (self,obj):
author = obj.bookauthor_set
return author.name
def get_volume_count(self,obj):
request = self.context.get('request')
return BookReference.objects.filter(title=obj.title).count()
class BookAttributeSerializer(serializers.ModelSerializer):
class Meta:
@ -53,7 +49,6 @@ class BookDetailSerializer(serializers.ModelSerializer):
read_only = True ,
source = 'bookreference_set'
)
volume_count = serializers.SerializerMethodField()
hadis = HadisListSerializer(
many=True,
@ -64,9 +59,6 @@ class BookDetailSerializer(serializers.ModelSerializer):
class Meta:
model = BookReference
fields = '__all__'
def get_volume_count(self,obj):
request = self.context.get('request')
return BookReference.objects.filter(title=obj.title).count()
# def create(self , validated_data):
# author = validated_data.pop('author')
@ -78,22 +70,19 @@ class BookReferenceSyncSerializer(serializers.ModelSerializer):
"""Serializer for syncing all book reference data for offline mode"""
# Basic information
basic_info = serializers.SerializerMethodField()
# Information group (detailed publication info)
information = serializers.SerializerMethodField()
detail = serializers.SerializerMethodField()
# Hadis group (related hadises)
hadis = serializers.SerializerMethodField()
hadises = serializers.SerializerMethodField()
authors = serializers.SerializerMethodField()
class Meta:
model = BookReference
fields = [
'id', 'title', 'basic_info', 'information', 'hadis'
'id', 'title','rate' , 'authors' ,'detail', 'hadises'
]
def get_basic_info(self, obj):
"""Get basic book information including authors and rating"""
def get_authors(self,obj):
authors = []
try:
for author in obj.authors.all():
@ -103,18 +92,14 @@ class BookReferenceSyncSerializer(serializers.ModelSerializer):
})
except:
authors = []
return authors
return {
'title': obj.title,
'authors': authors,
'rating': obj.rate,
'description': obj.description,
'volume': obj.volume
}
def get_information(self, obj):
"""Get detailed publication information"""
def get_detail(self, obj):
"""Get basic book information including authors and rating"""
return {
'description': obj.description,
'volume': obj.volume,
'language': obj.language,
'isbn': obj.isbn,
'year_of_publication': obj.year_of_publication,
@ -123,7 +108,7 @@ class BookReferenceSyncSerializer(serializers.ModelSerializer):
'rating': obj.rate
}
def get_hadis(self, obj):
def get_hadises(self, obj):
"""Get all hadises related to this book reference"""
hadis_list = []
try:

60
apps/hadis/views/category.py

@ -60,8 +60,8 @@ class HadisCategorySectListView(ListAPIView):
class HadisCategoryTreeView(ListAPIView):
"""
API view to get all HadisCategory tree structure grouped by sect and source_type
Returns all categories in a tree structure
API view to get all HadisCategory tree structure grouped by sect
Returns all categories in a tree structure (source_type grouping removed for mobile filtering)
"""
serializer_class = HadisCategoryTreeSerializer
pagination_class = NoPagination
@ -74,6 +74,9 @@ class HadisCategoryTreeView(ListAPIView):
return HadisCategory.objects.filter(
parent__isnull=True,
sect__is_active=True
).select_related('sect').prefetch_related(
'children',
'children__children'
).order_by('sect__order', 'order')
def list(self, request, *args, **kwargs):
@ -91,7 +94,7 @@ class HadisCategoryTreeView(ListAPIView):
# ایجاد گروه برای هر sect_type
grouped_data[sect_type] = {
'sects': {},
'categories': {}
'categories': []
}
# اضافه کردن اطلاعات sect به گروه sect_type
@ -105,12 +108,8 @@ class HadisCategoryTreeView(ListAPIView):
'order': category.sect.order
}
# گروه‌بندی categories بر اساس source_type
if category.source_type not in grouped_data[sect_type]['categories']:
grouped_data[sect_type]['categories'][category.source_type] = []
category_data = self.build_enhanced_category_tree(category, serializer_instance)
grouped_data[sect_type]['categories'][category.source_type].append(category_data)
grouped_data[sect_type]['categories'].append(category_data)
def count_children(children_list):
count = 0
@ -122,11 +121,10 @@ class HadisCategoryTreeView(ListAPIView):
total_count = 0
for sect_type_data in grouped_data.values():
for source_categories in sect_type_data['categories'].values():
for item in source_categories:
total_count += 1
if 'children' in item and item['children']:
total_count += count_children(item['children'])
for item in sect_type_data['categories']:
total_count += 1
if 'children' in item and item['children']:
total_count += count_children(item['children'])
response_data = {
'count': total_count,
@ -149,8 +147,7 @@ class HadisCategoryTreeView(ListAPIView):
return base_data
def enhance_child_data(self, child_data, parent_category, serializer_instance):
"""Enhance child data with father category info and hadis details"""
from ..models import Hadis
"""Enhance child data with father category info (no hadis payload for sync tree)"""
# Add father category information
child_data['father_category'] = {
@ -161,38 +158,7 @@ class HadisCategoryTreeView(ListAPIView):
'source_type': parent_category.source_type
}
# If this child has no children but has hadis, add hadis details
if not child_data.get('children', []) and child_data.get('has_hadis', False):
try:
# Get the category object
from ..models import HadisCategory
child_category = HadisCategory.objects.get(id=child_data['id'])
# Get hadis for this category
hadis_list = Hadis.objects.filter(
category=child_category,
status=True
).order_by('number')
hadis_details = []
for hadis in hadis_list:
hadis_detail = {
'id': hadis.id,
'title': hadis.title,
'title_narrator': hadis.title_narrator,
'text': hadis.text,
'translation': hadis.get_translation(
self.request.LANGUAGE_CODE if hasattr(self, 'request') and self.request else 'en'
),
'share_link': hadis.share_link
}
hadis_details.append(hadis_detail)
child_data['hadis_details'] = hadis_details
except Exception as e:
# If there's any error, just continue without hadis details
pass
# Note: we intentionally DO NOT load or attach hadis details here for performance.
# Recursively enhance children's children
if child_data.get('children', []):

15
apps/hadis/views/hadis.py

@ -28,8 +28,6 @@ class HadisSyncView(ListAPIView):
serializer_class = HadisSyncSerializer
pagination_class = NoPagination
@hadis_sync_swagger
def get_queryset(self):
return Hadis.objects.filter(status=True).select_related(
'category', 'hadis_status'
@ -41,18 +39,17 @@ class HadisSyncView(ListAPIView):
'hadiscorrection_set'
).order_by('id')
@hadis_sync_swagger
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def list(self, request, *args, **kwargs):
queryset = self.get_queryset()
serializer = self.get_serializer(queryset, many=True)
grouped_data = {}
for hadis_data in serializer.data:
hadis_id = str(hadis_data['id'])
grouped_data[hadis_id] = hadis_data
response_data = {
'count': queryset.count(),
'results': grouped_data
'results': serializer.data
}
return Response(response_data)

3
apps/hadis/views/reference.py

@ -49,6 +49,9 @@ class BookReferenceSyncView(ListAPIView):
pagination_class = NoPagination
@reference_sync_swagger
def get(self, request, *args, **kwargs):
return super().get(request, *args, **kwargs)
def get_queryset(self):
return BookReference.objects.prefetch_related(
'authors',

12
apps/hadis/views/transmitter.py

@ -83,23 +83,19 @@ class TransmitterSyncView(ListAPIView):
pagination_class = NoPagination
@transmitter_sync_swagger
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def get_queryset(self):
return Transmitters.objects.prefetch_related('opinions').order_by('id')
return Transmitters.objects.prefetch_related('opinions', 'originaltexts').order_by('id')
def list(self, request, *args, **kwargs):
queryset = self.get_queryset()
serializer = self.get_serializer(queryset, many=True, context={'request': request})
# Group transmitters by ID for easy lookup
grouped_data = {}
for transmitter_data in serializer.data:
transmitter_id = str(transmitter_data['id'])
grouped_data[transmitter_id] = transmitter_data
response_data = {
'count': queryset.count(),
'results': grouped_data
'results': serializer.data
}
return Response(response_data)
Loading…
Cancel
Save