Browse Source

feat(hadis): support category ordering by most content and alphabetical title

master
Mohsen Taba 2 weeks ago
parent
commit
790e5492ce
  1. 54
      apps/hadis/serializers/category.py
  2. 75
      apps/hadis/views/category.py

54
apps/hadis/serializers/category.py

@ -225,20 +225,15 @@ class HadisCategorySelectSerializer(serializers.ModelSerializer):
return Hadis.objects.filter(category=obj, status=True).exists()
def get_children_count(self, obj):
# """Get count of active children categories that have children or hadis"""
# children = obj.get_children().filter(sect=obj.sect)
# return len(children)
"""
Calculates the total number of Hadiths in this category
and all its descendants (sub-categories).
"""
# 1. Get all descendants of this category (including itself)
if hasattr(obj, 'total_content_count'):
return obj.total_content_count
family_tree = obj.get_descendants(include_self=True)
return Hadis.objects.filter(category__in=family_tree, status=True).count()
# 2. Count all Hadiths that belong to any category in this tree
return Hadis.objects.filter(category__in=family_tree).count()
def get_hadis_count(self, obj):
return len(Hadis.objects.filter(category=obj))
if hasattr(obj, 'active_hadis_count'):
return obj.active_hadis_count
return Hadis.objects.filter(category=obj, status=True).count()
@ -274,20 +269,15 @@ class HadisCategorySelectSourceSerializer(serializers.ModelSerializer):
return Hadis.objects.filter(category=obj, status=True).exists()
def get_children_count(self, obj):
# """Get count of active children categories that have children or hadis"""
# children = obj.get_children().filter(sect=obj.sect)
# return len(children)
"""
Calculates the total number of Hadiths in this category
and all its descendants (sub-categories).
"""
# 1. Get all descendants of this category (including itself)
if hasattr(obj, 'total_content_count'):
return obj.total_content_count
family_tree = obj.get_descendants(include_self=True)
return Hadis.objects.filter(category__in=family_tree, status=True).count()
# 2. Count all Hadiths that belong to any category in this tree
return Hadis.objects.filter(category__in=family_tree).count()
def get_hadis_count(self, obj):
return len(Hadis.objects.filter(category=obj))
if hasattr(obj, 'active_hadis_count'):
return obj.active_hadis_count
return Hadis.objects.filter(category=obj, status=True).count()
class CategorySerializer(serializers.ModelSerializer):
sect_id = serializers.IntegerField(source='sect.id', read_only=True)
@ -310,22 +300,18 @@ class CategorySerializer(serializers.ModelSerializer):
return obj.parent_id is not None
def get_children_count(self, obj):
# """Get count of active children categories that have children or hadis"""
# children = obj.get_children().filter(sect=obj.sect)
# return len(children)
"""
Calculates the total number of Hadiths in this category
and all its descendants (sub-categories).
"""
# 1. Get all descendants of this category (including itself)
if hasattr(obj, 'total_content_count'):
return obj.total_content_count
family_tree = obj.get_descendants(include_self=True)
return Hadis.objects.filter(category__in=family_tree, status=True).count()
# 2. Count all Hadiths that belong to any category in this tree
return Hadis.objects.filter(category__in=family_tree).count()
def get_has_hadis(self, obj):
return Hadis.objects.filter(category=obj).exists()
return Hadis.objects.filter(category=obj, status=True).exists()
def get_hadis_count(self, obj):
return len(Hadis.objects.filter(category=obj))
if hasattr(obj, 'active_hadis_count'):
return obj.active_hadis_count
return Hadis.objects.filter(category=obj, status=True).count()
# def get_title(self,obj):
# # ✅ Get language from request

75
apps/hadis/views/category.py

@ -266,6 +266,57 @@ class HadisCategoryTreeNormalView(ListAPIView):
Q(active_hadis_count__gt=0) | Q(children_total_count__gt=0) | Q(source_type=HadisCategory.SourceType.QURAN)
).order_by('sect__order', 'order')
def apply_category_ordering_and_counts(queryset, request):
from django.db.models import Subquery, OuterRef, Count, IntegerField
from django.db.models.functions import Coalesce
from django.db.models.expressions import RawSQL
from django.utils.translation import get_language
descendant_hadis_subquery = Subquery(
Hadis.objects.filter(
category__tree_id=OuterRef('tree_id'),
category__lft__gte=OuterRef('lft'),
category__rght__lte=OuterRef('rght'),
status=True
).values('category__tree_id').annotate(cnt=Count('id')).values('cnt')[:1],
output_field=IntegerField()
)
queryset = queryset.annotate(total_content_count=Coalesce(descendant_hadis_subquery, 0))
ordering_param = (
request.query_params.get('ordering') or
request.query_params.get('sort') or
''
).strip().lower()
if ordering_param in ['content', 'most_content', '-total_content_count', '-content']:
return queryset.order_by('-total_content_count', 'order', 'id')
elif ordering_param in ['alphabetical', 'title', 'slug', 'a-z']:
lang = (
request.query_params.get('language_code') or
request.query_params.get('lang') or
getattr(request, 'LANGUAGE_CODE', None)
)
if not lang and hasattr(request, 'headers'):
lang = request.headers.get('Accept-Language')
if lang:
lang = lang.split(',')[0].split(';')[0].split('-')[0].strip().lower()
if not lang:
lang = get_language() or 'en'
queryset = queryset.annotate(
loc_title=RawSQL("""
COALESCE(
NULLIF((SELECT COALESCE(elem->>'title', elem->>'text') FROM jsonb_array_elements(CASE WHEN jsonb_typeof(hadis_hadiscategory.title::jsonb) = 'array' THEN hadis_hadiscategory.title::jsonb ELSE '[]'::jsonb END) elem WHERE elem->>'language_code' = %s LIMIT 1), ''),
NULLIF((SELECT COALESCE(elem->>'title', elem->>'text') FROM jsonb_array_elements(CASE WHEN jsonb_typeof(hadis_hadiscategory.title::jsonb) = 'array' THEN hadis_hadiscategory.title::jsonb ELSE '[]'::jsonb END) elem WHERE elem->>'language_code' = 'en' LIMIT 1), ''),
hadis_hadiscategory.slug
)
""", [lang])
)
return queryset.order_by('loc_title', 'slug', 'id')
return queryset.order_by('order', 'id')
class HadisCategorySelectBySectView(ListAPIView):
"""
@ -274,7 +325,6 @@ class HadisCategorySelectBySectView(ListAPIView):
"""
serializer_class = HadisCategorySelectSerializer
pagination_class = StandardResultsSetPagination
@categories_tree_by_sect_swagger
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
@ -323,7 +373,7 @@ class HadisCategorySelectBySectView(ListAPIView):
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(
queryset = HadisCategory.objects.filter(
parent=parent_category,
sect__sect_type=sect_type,
sect__is_active=True
@ -332,7 +382,8 @@ class HadisCategorySelectBySectView(ListAPIView):
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')
)
return apply_category_ordering_and_counts(queryset, self.request)
class HadisCategorySelectBySectSourceView(ListAPIView):
@ -391,7 +442,7 @@ class HadisCategorySelectBySectSourceView(ListAPIView):
from django.db.models import Count, Q
# Return children of this category, filtered by source_type and excluding empty leaves
return HadisCategory.objects.filter(
queryset = HadisCategory.objects.filter(
parent=parent_category,
sect__sect_type=sect_type,
sect__is_active=True,
@ -401,7 +452,8 @@ class HadisCategorySelectBySectSourceView(ListAPIView):
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')
)
return apply_category_ordering_and_counts(queryset, self.request)
class CategoriesView(ListAPIView):
"""
@ -415,14 +467,21 @@ class CategoriesView(ListAPIView):
def get_queryset(self):
from django.db.models import Count, Q
return HadisCategory.objects.filter(
queryset = 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')
)
is_root = self.request.query_params.get('is_root', '').lower()
if is_root in ['true', '1']:
queryset = queryset.filter(parent__isnull=True)
elif is_root in ['false', '0']:
queryset = queryset.filter(parent__isnull=False)
return apply_category_ordering_and_counts(queryset, self.request)
@ -451,7 +510,7 @@ class CategoriesBySectView(ListAPIView):
elif is_root in ['false', '0']:
queryset = queryset.filter(parent__isnull=False)
return queryset.order_by('order', 'id')
return apply_category_ordering_and_counts(queryset, self.request)
@categories_by_sect_swagger
def get(self, request, *args, **kwargs):

Loading…
Cancel
Save