You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
301 lines
10 KiB
301 lines
10 KiB
from rest_framework.generics import ListAPIView
|
|
from rest_framework.response import Response
|
|
from django.shortcuts import get_object_or_404
|
|
from utils.pagination import NoPagination
|
|
from django.db.models import Q
|
|
from utils.pagination import StandardResultsSetPagination
|
|
from ..models import HadisSect, HadisCategory,Hadis
|
|
from ..serializers import (
|
|
HadisCategorySectListSerializer,
|
|
HadisCategoryTreeSerializer,
|
|
CategorySerializer ,
|
|
HadisCategorySelectSerializer ,
|
|
HadisCategorySelectSourceSerializer,
|
|
get_localized_text
|
|
)
|
|
from ..docs import (
|
|
hadis_sect_list_swagger,
|
|
hadis_category_tree_swagger,
|
|
categories_list_swagger,
|
|
categories_by_sect_swagger,
|
|
categories_tree_by_sect_swagger,
|
|
categories_tree_by_sect_source_swagger
|
|
)
|
|
|
|
|
|
class HadisCategorySectListView(ListAPIView):
|
|
"""
|
|
API view to list all HadisSects grouped by sect_type (shia/sunni)
|
|
"""
|
|
queryset = HadisSect.objects.filter(is_active=True).order_by('order')
|
|
serializer_class = HadisCategorySectListSerializer
|
|
pagination_class = NoPagination
|
|
|
|
@hadis_sect_list_swagger
|
|
def get(self, request, *args, **kwargs):
|
|
return self.list(request, *args, **kwargs)
|
|
|
|
|
|
class HadisCategoryTreeView(ListAPIView):
|
|
"""
|
|
API view to get all HadisCategory tree structure grouped by sect
|
|
"""
|
|
serializer_class = HadisCategoryTreeSerializer
|
|
pagination_class = NoPagination
|
|
|
|
@hadis_category_tree_swagger
|
|
def get(self, request, *args, **kwargs):
|
|
return self.list(request, *args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
"""
|
|
Prefetch ALL data at once to avoid N+1
|
|
"""
|
|
from django.db.models import Prefetch, Count, Q
|
|
|
|
# Create a queryset for children that also has annotations
|
|
children_queryset = (
|
|
HadisCategory.objects
|
|
.select_related('sect')
|
|
.prefetch_related('children')
|
|
.annotate(
|
|
hadis_count=Count('hadis', filter=Q(hadis__status=True)),
|
|
children_count=Count('children', distinct=True)
|
|
)
|
|
.order_by('order')
|
|
)
|
|
|
|
return (
|
|
HadisCategory.objects
|
|
.filter(parent__isnull=True, sect__is_active=True)
|
|
.select_related('sect')
|
|
.prefetch_related(
|
|
Prefetch(
|
|
'children',
|
|
queryset=children_queryset
|
|
),
|
|
)
|
|
.annotate(
|
|
hadis_count=Count('hadis', filter=Q(hadis__status=True)),
|
|
children_count=Count('children', distinct=True)
|
|
)
|
|
.order_by('sect__order', 'order')
|
|
)
|
|
|
|
|
|
def list(self, request, *args, **kwargs):
|
|
queryset = self.get_queryset()
|
|
grouped_data = {}
|
|
|
|
# Single pass through prefetched data (NO QUERIES)
|
|
for category in queryset:
|
|
sect_type = category.sect.sect_type
|
|
|
|
if sect_type not in grouped_data:
|
|
grouped_data[sect_type] = {
|
|
'sects': {},
|
|
'categories': []
|
|
}
|
|
|
|
# Add sect info
|
|
sect_id = str(category.sect.id)
|
|
if sect_id not in grouped_data[sect_type]['sects']:
|
|
source_types = HadisCategory.objects.filter(
|
|
sect=category.sect
|
|
).values_list('source_type', flat=True)
|
|
grouped_data[sect_type]['sects'][sect_id] = {
|
|
'id': category.sect.id,
|
|
'sect_type': category.sect.sect_type,
|
|
'title': get_localized_text(category.sect.title, request),
|
|
'description': get_localized_text(category.sect.description,request),
|
|
'order': category.sect.order,
|
|
'source_types':list(set(source_types))
|
|
}
|
|
|
|
# Build tree using prefetched data
|
|
category_data = self._build_tree(category, request)
|
|
grouped_data[sect_type]['categories'].append(category_data)
|
|
|
|
# Count total categories (simple loop, no queries)
|
|
total_count = self._count_tree_items(grouped_data)
|
|
|
|
response_data = {
|
|
'count': total_count,
|
|
'results': grouped_data
|
|
}
|
|
|
|
return Response(response_data)
|
|
|
|
def _build_tree(self, category, request):
|
|
"""
|
|
Build tree from ALREADY PREFETCHED data
|
|
No database queries here!
|
|
"""
|
|
return {
|
|
'id': category.id,
|
|
'title': get_localized_text(category.title, request),
|
|
'description': get_localized_text(category.description, request),
|
|
'slug': category.slug,
|
|
'source_type': category.source_type,
|
|
'hadis_count': category.hadis_count, # ← Use annotated value
|
|
'children_count': category.children_count, # ← Use annotated value
|
|
'has_hadis': category.hadis_count > 0, # ← Simple calculation
|
|
'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(child, request)
|
|
for child in category.children.all() # Already prefetched!
|
|
]
|
|
}
|
|
|
|
def _count_tree_items(self, grouped_data):
|
|
"""Count total items in tree"""
|
|
total = 0
|
|
for sect_type_data in grouped_data.values():
|
|
for item in sect_type_data['categories']:
|
|
total += 1
|
|
total += self._count_children(item.get('children', []))
|
|
return total
|
|
|
|
def _count_children(self, children_list):
|
|
"""Recursively count children"""
|
|
count = 0
|
|
for item in children_list:
|
|
count += 1
|
|
if item.get('children'):
|
|
count += self._count_children(item['children'])
|
|
return count
|
|
|
|
def _get_thumbnail_url(self, category, request):
|
|
"""Get absolute thumbnail URL"""
|
|
if hasattr(category, 'thumbnail') and category.thumbnail:
|
|
return request.build_absolute_uri(category.thumbnail.url) if request else category.thumbnail.url
|
|
return None
|
|
|
|
def _get_xmind_url(self, category, request):
|
|
"""Get absolute xmind URL"""
|
|
if getattr(category, 'xmind_file', None):
|
|
return request.build_absolute_uri(category.xmind_file.url) if request else category.xmind_file.url
|
|
return None
|
|
|
|
|
|
class HadisCategoryTreeNormalView(ListAPIView):
|
|
"""
|
|
Normal (paginated) tree view for HadisCategory.
|
|
Unlike the sync view, this simply returns the root categories (filtered to active sects)
|
|
with their nested children, and uses the project's default pagination.
|
|
"""
|
|
serializer_class = HadisCategoryTreeSerializer
|
|
|
|
@hadis_category_tree_swagger
|
|
def get(self, request, *args, **kwargs):
|
|
return self.list(request, *args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
return HadisCategory.objects.filter(
|
|
parent__isnull=True,
|
|
sect__is_active=True
|
|
).order_by('sect__order', 'order')
|
|
|
|
|
|
class HadisCategorySelectBySectView(ListAPIView):
|
|
"""
|
|
Tree view for HadisCategory filtered by sect_type and category slug.
|
|
Returns the children categories of the specified category (by slug) within the sect_type.
|
|
"""
|
|
serializer_class = HadisCategorySelectSerializer
|
|
pagination_class = StandardResultsSetPagination
|
|
|
|
@categories_tree_by_sect_swagger
|
|
def get(self, request, *args, **kwargs):
|
|
return self.list(request, *args, **kwargs)
|
|
|
|
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:
|
|
parent_category = HadisCategory.objects.get(
|
|
slug=slug,
|
|
sect__sect_type=sect_type,
|
|
sect__is_active=True
|
|
)
|
|
except HadisCategory.DoesNotExist:
|
|
print('not ok')
|
|
return HadisCategory.objects.none()
|
|
|
|
# Return children of this category, filtered as before
|
|
return HadisCategory.objects.filter(
|
|
parent=parent_category,
|
|
sect__sect_type=sect_type,
|
|
sect__is_active=True
|
|
).order_by('order')
|
|
|
|
|
|
class HadisCategorySelectBySectSourceView(ListAPIView):
|
|
"""
|
|
Tree view for HadisCategory filtered by sect_type, category slug and source_type.
|
|
Returns the children categories of the specified category (by slug) within the sect_type, filtered by source_type.
|
|
"""
|
|
serializer_class = HadisCategorySelectSourceSerializer
|
|
pagination_class = StandardResultsSetPagination
|
|
@categories_tree_by_sect_source_swagger
|
|
def get(self, request, *args, **kwargs):
|
|
return self.list(request, *args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
sect_type = self.kwargs.get('sect_type')
|
|
slug = self.kwargs.get('slug')
|
|
source_type = self.kwargs.get('source_type')
|
|
|
|
# Find the parent category by slug and sect_type
|
|
try:
|
|
parent_category = HadisCategory.objects.get(
|
|
slug=slug,
|
|
sect__sect_type=sect_type,
|
|
sect__is_active=True
|
|
)
|
|
except HadisCategory.DoesNotExist:
|
|
return HadisCategory.objects.none()
|
|
|
|
# Return children of this category, filtered by source_type
|
|
return HadisCategory.objects.filter(
|
|
parent=parent_category,
|
|
sect__sect_type=sect_type,
|
|
sect__is_active=True,
|
|
source_type=source_type
|
|
).order_by('order')
|
|
|
|
class CategoriesView(ListAPIView):
|
|
"""
|
|
API view to list all HadisCategories
|
|
"""
|
|
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 list(self, request, *args, **kwargs):
|
|
return super().list(request, *args, **kwargs)
|
|
|
|
|
|
class CategoriesBySectView(ListAPIView):
|
|
"""
|
|
API view to list HadisCategories filtered by sect_type
|
|
"""
|
|
serializer_class = CategorySerializer
|
|
|
|
def get_queryset(self):
|
|
sect_type = self.kwargs.get('sect_type')
|
|
return HadisCategory.objects.filter(sect__sect_type=sect_type)
|
|
|
|
@categories_by_sect_swagger
|
|
def get(self, request, *args, **kwargs):
|
|
return self.list(request, *args, **kwargs)
|