Browse Source

feat(hadis, reference): filter empty editions & categories in client APIs and order admin transmitters descending

master
Mohsen Taba 3 weeks ago
parent
commit
44f06762c1
  1. 16
      apps/hadis/serializers/reference_v2.py
  2. 103
      apps/hadis/views/category.py
  3. 20
      apps/hadis/views/hadis.py
  4. 2
      apps/hadis/views_admin.py

16
apps/hadis/serializers/reference_v2.py

@ -172,7 +172,8 @@ class BookReferenceV2DetailSerializer(serializers.ModelSerializer):
def get_information(self, obj):
request = self.context.get("request")
summary = get_localized_text(obj.description, request) if obj.description else ""
editions = BookEditionV2Serializer(obj.editions.all(), many=True, context=self.context).data
valid_editions = [ed for ed in obj.editions.all() if ed.volumes.exists()]
editions = BookEditionV2Serializer(valid_editions, many=True, context=self.context).data
return {
"summary": summary,
@ -184,10 +185,13 @@ class BookReferenceV2DetailSerializer(serializers.ModelSerializer):
editions = obj.editions.all()
editions_data = []
for ed in editions:
vol_qs = ed.volumes.all()
if not vol_qs.exists():
continue
editions_data.append({
"edition_id": ed.id,
"edition_statement": ed.edition_number or f"Edition {ed.id}",
"volumes": BookVolumeV2Serializer(ed.volumes.all(), many=True, context=self.context).data
"volumes": BookVolumeV2Serializer(vol_qs, many=True, context=self.context).data
})
return {
"has_editions": True,
@ -226,7 +230,8 @@ class BookReferenceV2SyncSerializer(serializers.ModelSerializer):
def get_information(self, obj):
request = self.context.get("request")
summary = get_localized_text(obj.description, request) if obj.description else ""
editions = BookEditionV2Serializer(obj.editions.all(), many=True, context=self.context).data
valid_editions = [ed for ed in obj.editions.all() if ed.volumes.exists()]
editions = BookEditionV2Serializer(valid_editions, many=True, context=self.context).data
return {
"summary": summary,
"editions": editions,
@ -244,10 +249,13 @@ class BookReferenceV2SyncSerializer(serializers.ModelSerializer):
editions = obj.editions.all()
editions_data = []
for ed in editions:
vol_qs = ed.volumes.all()
if not vol_qs.exists():
continue
editions_data.append({
"edition_id": ed.id,
"edition_statement": ed.edition_number or f"Edition {ed.id}",
"volumes": BookVolumeV2Serializer(ed.volumes.all(), many=True, context=self.context).data
"volumes": BookVolumeV2Serializer(vol_qs, many=True, context=self.context).data
})
return {
"has_editions": True,

103
apps/hadis/views/category.py

@ -136,6 +136,18 @@ class HadisCategoryTreeView(ListAPIView):
for root in roots:
sect_type = root.sect.sect_type
# Build tree using mapping (returns None if the root branch is completely empty)
category_data = self._build_tree_recursive(
root,
request,
children_map,
recursive_counts,
hadis_ids_map,
quran_data_map,
)
if category_data is None:
continue
if sect_type not in grouped_data:
grouped_data[sect_type] = {
'sects': {},
@ -158,15 +170,6 @@ class HadisCategoryTreeView(ListAPIView):
'source_types': list(source_types)
}
# Build tree using mapping
category_data = self._build_tree_recursive(
root,
request,
children_map,
recursive_counts,
hadis_ids_map,
quran_data_map, # 👇 پاس دادن مپِ قرآن به تابع بازگشتی
)
grouped_data[sect_type]['categories'].append(category_data)
total_count = len(queryset)
@ -180,10 +183,28 @@ class HadisCategoryTreeView(ListAPIView):
def _build_tree_recursive(self, category, request, children_map, recursive_counts, hadis_ids_map, quran_data_map):
"""
Build tree from flat mapping
Build tree from flat mapping, pruning any node that has no active children and no active hadiths.
"""
children = children_map.get(category.id, [])
raw_children = children_map.get(category.id, [])
built_children = []
for child in raw_children:
child_node = self._build_tree_recursive(
child,
request,
children_map,
recursive_counts,
hadis_ids_map,
quran_data_map,
)
if child_node is not None:
built_children.append(child_node)
has_hadis = category.direct_hadis_count > 0
has_quran = bool(quran_data_map.get(category.id, None)) if category.source_type == HadisCategory.SourceType.QURAN else False
# If this category has no children and no hadiths (and no quran verse), prune it
if len(built_children) == 0 and not has_hadis and not has_quran:
return None
return {
'id': category.id,
@ -198,23 +219,12 @@ class HadisCategoryTreeView(ListAPIView):
'has_hadis': has_hadis,
'has_parent': category.parent_id is not None,
'hadis_ids': hadis_ids_map.get(category.id, []) if has_hadis else [],
# 👇 کلید جدید: تزریق دیتای قرآن دقیقاً داخل نودِ کتگوری
'quran_data': quran_data_map.get(category.id, None) if category.source_type == HadisCategory.SourceType.QURAN else None,
'order': category.order,
'thumbnail': self._get_thumbnail_url(category, request),
'xmind_file': self._get_xmind_url(category, request),
'has_xmind_file': bool(getattr(category, 'xmind_file', None)),
'children': [
self._build_tree_recursive(
child,
request,
children_map,
recursive_counts,
hadis_ids_map,
quran_data_map, # 👇
)
for child in children
]
'children': built_children
}
def _get_thumbnail_url(self, category, request):
@ -244,9 +254,15 @@ class HadisCategoryTreeNormalView(ListAPIView):
return self.list(request, *args, **kwargs)
def get_queryset(self):
from django.db.models import Count, Q
return HadisCategory.objects.filter(
parent__isnull=True,
sect__is_active=True
).annotate(
active_hadis_count=Count('hadis', filter=Q(hadis__status=True)),
children_total_count=Count('children')
).filter(
Q(active_hadis_count__gt=0) | Q(children_total_count__gt=0) | Q(source_type=HadisCategory.SourceType.QURAN)
).order_by('sect__order', 'order')
@ -293,8 +309,6 @@ class HadisCategorySelectBySectView(ListAPIView):
def get_queryset(self):
sect_type = self.kwargs.get('sect_type')
slug = self.kwargs.get('slug')
print(slug)
print(sect_type)
# Find the parent category by slug and sect_type
try:
@ -304,14 +318,19 @@ class HadisCategorySelectBySectView(ListAPIView):
sect__is_active=True
)
except HadisCategory.DoesNotExist:
print('not ok')
return HadisCategory.objects.none()
# Return children of this category, filtered as before
from django.db.models import Count, Q
# Return children of this category that have either active hadiths, children, or are quran
return HadisCategory.objects.filter(
parent=parent_category,
sect__sect_type=sect_type,
sect__is_active=True
).annotate(
active_hadis_count=Count('hadis', filter=Q(hadis__status=True)),
children_total_count=Count('children')
).filter(
Q(active_hadis_count__gt=0) | Q(children_total_count__gt=0) | Q(source_type=HadisCategory.SourceType.QURAN)
).order_by('order')
@ -369,39 +388,61 @@ class HadisCategorySelectBySectSourceView(ListAPIView):
except HadisCategory.DoesNotExist:
return HadisCategory.objects.none()
# Return children of this category, filtered by source_type
from django.db.models import Count, Q
# Return children of this category, filtered by source_type and excluding empty leaves
return HadisCategory.objects.filter(
parent=parent_category,
sect__sect_type=sect_type,
sect__is_active=True,
source_type=source_type
).annotate(
active_hadis_count=Count('hadis', filter=Q(hadis__status=True)),
children_total_count=Count('children')
).filter(
Q(active_hadis_count__gt=0) | Q(children_total_count__gt=0) | Q(source_type=HadisCategory.SourceType.QURAN)
).order_by('order')
class CategoriesView(ListAPIView):
"""
API view to list all HadisCategories
API view to list all HadisCategories (excluding empty leaf categories)
"""
queryset = HadisCategory.objects.all()
serializer_class = CategorySerializer
pagination_class = StandardResultsSetPagination
@categories_list_swagger
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def get_queryset(self):
from django.db.models import Count, Q
return HadisCategory.objects.filter(
sect__is_active=True
).annotate(
active_hadis_count=Count('hadis', filter=Q(hadis__status=True)),
children_total_count=Count('children')
).filter(
Q(active_hadis_count__gt=0) | Q(children_total_count__gt=0) | Q(source_type=HadisCategory.SourceType.QURAN)
).order_by('order', 'id')
class CategoriesBySectView(ListAPIView):
"""
API view to list HadisCategories filtered by sect_type
API view to list HadisCategories filtered by sect_type (excluding empty leaf categories)
"""
serializer_class = CategorySerializer
pagination_class = StandardResultsSetPagination
def get_queryset(self):
sect_type = self.kwargs.get('sect_type')
from django.db.models import Count, Q
queryset = HadisCategory.objects.filter(
sect__sect_type=sect_type,
sect__is_active=True
).annotate(
active_hadis_count=Count('hadis', filter=Q(hadis__status=True)),
children_total_count=Count('children')
).filter(
Q(active_hadis_count__gt=0) | Q(children_total_count__gt=0) | Q(source_type=HadisCategory.SourceType.QURAN)
)
is_root = self.request.query_params.get('is_root', 'true').lower()
if is_root in ['true', '1']:

20
apps/hadis/views/hadis.py

@ -701,9 +701,15 @@ class HadisFiltersView(ListAPIView):
'slug': status.slug
})
# Get categories from HadisCategory model
# Get categories from HadisCategory model (excluding empty leaf categories)
from django.db.models import Count, Q
categories = []
for category in HadisCategory.objects.all().order_by('order'):
for category in HadisCategory.objects.annotate(
active_hadis_count=Count('hadis', filter=Q(hadis__status=True)),
children_total_count=Count('children')
).filter(
Q(active_hadis_count__gt=0) | Q(children_total_count__gt=0) | Q(source_type=HadisCategory.SourceType.QURAN)
).order_by('order'):
title_text = get_localized_text(category.title, request)
if title_text and category.slug:
categories.append({
@ -814,7 +820,8 @@ class HadisFiltersSyncAPIView(APIView):
for status in HadisStatus.objects.all().order_by('order')
]
# ۲. دسته‌بندی‌ها (مرتب‌شده بر اساس فیلد order)
# ۲. دسته‌بندی‌ها (مرتب‌شده بر اساس فیلد order، به استثنای دسته‌بندی‌های خالی)
from django.db.models import Count, Q
categories = [
{
"id": cat.id,
@ -822,7 +829,12 @@ class HadisFiltersSyncAPIView(APIView):
"title": get_localized_text(cat.title, request=request, language_code=lang) or "",
"order": cat.order,
}
for cat in HadisCategory.objects.all().order_by('order')
for cat in HadisCategory.objects.annotate(
active_hadis_count=Count('hadis', filter=Q(hadis__status=True)),
children_total_count=Count('children')
).filter(
Q(active_hadis_count__gt=0) | Q(children_total_count__gt=0) | Q(source_type=HadisCategory.SourceType.QURAN)
).order_by('order')
]
# ۳. منابع / کتاب‌ها

2
apps/hadis/views_admin.py

@ -312,7 +312,7 @@ class AdminTransmitterViewSet(ModelViewSet):
if generation_filter and generation_filter != "all":
queryset = queryset.filter(generation=generation_filter)
return queryset.order_by("id")
return queryset.order_by("-created_at", "-id")

Loading…
Cancel
Save