Browse Source

sources filters fixed

master
Mohsen Taba 3 hours ago
parent
commit
af6d1d9a2c
  1. 8
      apps/hadis/urls.py
  2. 77
      apps/hadis/views/reference.py
  3. 85
      apps/hadis/views/reference_v2.py

8
apps/hadis/urls.py

@ -67,9 +67,9 @@ for prefix, viewset, basename in admin_router.registry:
urlpatterns = [
path('sync/authors/', BookAuthorSyncView.as_view(), name='sync-author-list'),
path('authors/', BookAuthorListView.as_view(), name='author-list'),
path('authors/<str:author_slug>/', BookAuthorDetailView.as_view(), name='author-detail'),
path('authors/<str:author_slug>/references/', AuthorReferencesListView.as_view(), name='author-references'),
path('authors/<str:author_slug>/', BookAuthorDetailView.as_view(), name='author-detail'),
path('authors/', BookAuthorListView.as_view(), name='author-list'),
# Admin endpoints
@ -98,17 +98,17 @@ urlpatterns = [
# Hadis paths
path('category/<str:category_slug>/xmind/', HadisCategoryXMindView.as_view(), name='hadis-category-xmind'), # ← Must be before other category paths
path('category/<str:category_slug>/', HadisListView.as_view(), name='hadis-list'),
path('arguments/', HadisMainListView.as_view(), name='hadis-main-list'),
path('arguments/filters/', HadisFiltersView.as_view(), name='hadis-filters'),
path('arguments/', HadisMainListView.as_view(), name='hadis-main-list'),
# Narrator paths
path('narrators/filters/', TransmitterFiltersView.as_view(), name='narrator-filters'),
path('narrators/<str:narrator_slug>/teachers/', NarratorTeachersView.as_view(), name='narrator-teachers'),
path('narrators/<str:narrator_slug>/students/', NarratorStudentsView.as_view(), name='narrator-students'),
path('narrators/<str:narrator_slug>/arguments/', NarratorArgumentsListView.as_view(), name='narrator-arguments'),
path('narrators/<str:narrator_slug>/opinions/', TransmitterOpinionView.as_view(), name='narrator-opinions'),
path('narrators/<str:narrator_slug>/original_texts/', TransmitterOriginalTextView.as_view(), name='narrator-original-texts'),
path('narrators/<str:narrator_slug>/', TransmitterDetailView.as_view(), name='narrator-detail'),
path('narrators/filters/', TransmitterFiltersView.as_view(), name='narrator-filters'),
path('narrators/', TransmitterView.as_view(), name='narrators'),
# Reference paths

77
apps/hadis/views/reference.py

@ -95,41 +95,74 @@ class BookReferenceSyncView(ListAPIView):
def get(self, request, *args, **kwargs):
return super().get(request, *args, **kwargs)
def _get_multi_param(self, *param_names):
values = []
for name in param_names:
for item in self.request.query_params.getlist(name):
for val in str(item).split(','):
val = val.strip()
if val:
values.append(val)
return values
def get_queryset(self):
"""
Prefetch ALL related data to avoid N+1 queries
"""
qs = BookReference.objects.select_related().prefetch_related(
'authors', 'attributes', 'images', 'hadis_references__hadis'
qs = BookReference.objects.select_related('author', 'type').prefetch_related(
'attributes', 'images', 'hadis_references__hadis'
).distinct().order_by('id')
category = self.request.query_params.get('category')
if category:
qs = qs.filter(subject_area__id=category)
author = self.request.query_params.get('author')
if author:
qs = qs.filter(authors__id=author)
sect = self.request.query_params.get('sect')
if sect:
qs = qs.filter(type__id=sect)
# Category (Multi-select)
categories = self._get_multi_param('category', 'categories', 'category[]', 'categories[]')
category_ids = [int(c) for c in categories if c.isdigit()]
if category_ids:
qs = qs.filter(subject_area__id__in=category_ids).distinct()
# Author (Multi-select)
authors = self._get_multi_param('author', 'authors', 'author[]', 'authors[]')
author_ids = [int(a) for a in authors if a.isdigit()]
if author_ids:
qs = qs.filter(author_id__in=author_ids)
# Sect / Madhab (Multi-select)
sects = self._get_multi_param('sect', 'sects', 'sect[]', 'sects[]', 'madhab', 'madhabs', 'madhab[]', 'madhabs[]')
if sects:
from django.db.models import Q
sect_q = Q()
type_ids = []
for s in sects:
if s.lower() in ['shia', 'sunni']:
sect_q |= Q(type__sect=s.lower())
elif s.isdigit():
type_ids.append(int(s))
if type_ids:
sect_q |= Q(type__id__in=type_ids)
if sect_q:
qs = qs.filter(sect_q)
# PDF availability
pdf_available = self.request.query_params.get('pdf_available')
if pdf_available == 'true':
qs = qs.filter(documents__isnull=False)
qs = qs.filter(documents__isnull=False).distinct()
elif pdf_available == 'false':
qs = qs.filter(documents__isnull=True)
century = self.request.query_params.get('century')
if century:
if century == 'before_islam':
qs = qs.filter(authors__death_year_hijri__lt=0)
elif century.isdigit():
century_num = int(century)
# Century (Multi-select)
centuries = self._get_multi_param('century', 'centuries', 'century[]', 'centuries[]')
if centuries:
from django.db.models import Q
century_q = Q()
for c in centuries:
if c == 'before_islam':
century_q |= Q(author__death_year_hijri__lt=0)
elif c.isdigit():
century_num = int(c)
start_year = (century_num - 1) * 100 + 1
end_year = century_num * 100
qs = qs.filter(authors__death_year_hijri__gte=start_year, authors__death_year_hijri__lte=end_year)
century_q |= Q(author__death_year_hijri__gte=start_year, author__death_year_hijri__lte=end_year)
if century_q:
qs = qs.filter(century_q)
return qs

85
apps/hadis/views/reference_v2.py

@ -133,17 +133,28 @@ class BookReferenceV2ListView(generics.ListAPIView):
"Use query parameters to filter the results.",
tags=['Dobodbi - Hadis (V2)'],
manual_parameters=[
openapi.Parameter('category', openapi.IN_QUERY, description="Filter by Category/Subject Area ID", type=openapi.TYPE_INTEGER),
openapi.Parameter('author', openapi.IN_QUERY, description="Filter by Author ID", type=openapi.TYPE_INTEGER),
openapi.Parameter('madhab', openapi.IN_QUERY, description="Filter by Madhab ('shia' or 'sunni') or Type ID", type=openapi.TYPE_STRING, enum=['shia', 'sunni']),
openapi.Parameter('category', openapi.IN_QUERY, description="Filter by Category/Subject Area ID(s). Supports comma-separated (e.g. '1,2,3') or multiple params", type=openapi.TYPE_STRING),
openapi.Parameter('author', openapi.IN_QUERY, description="Filter by Author ID(s). Supports comma-separated (e.g. '1,2,3') or multiple params", type=openapi.TYPE_STRING),
openapi.Parameter('madhab', openapi.IN_QUERY, description="Filter by Madhab ('shia' or 'sunni') or Type ID(s). Supports comma-separated", type=openapi.TYPE_STRING),
openapi.Parameter('pdf_available', openapi.IN_QUERY, description="Filter by PDF availability ('true' or 'false')", type=openapi.TYPE_STRING),
openapi.Parameter('century', openapi.IN_QUERY, description="Filter by Century (e.g. '1', '2', 'before_islam')", type=openapi.TYPE_STRING),
openapi.Parameter('century', openapi.IN_QUERY, description="Filter by Century (e.g. '1,2', 'before_islam'). Supports comma-separated", type=openapi.TYPE_STRING),
openapi.Parameter('search', openapi.IN_QUERY, description="Search term for book title, description, or author name", type=openapi.TYPE_STRING),
]
)
def get(self, request, *args, **kwargs):
return super().get(request, *args, **kwargs)
def _get_multi_param(self, *param_names):
"""Extract multi-value params supporting comma-separated, repeated keys, and [] suffix."""
values = []
for name in param_names:
for item in self.request.query_params.getlist(name):
for val in str(item).split(','):
val = val.strip()
if val:
values.append(val)
return values
def get_queryset(self):
qs = BookReference.objects.select_related('author').prefetch_related('tags', 'volumes__images').order_by('-created_at')
@ -156,36 +167,56 @@ class BookReferenceV2ListView(generics.ListAPIView):
Q(author__name__icontains=search)
)
category = self.request.query_params.get('category')
if category:
qs = qs.filter(subject_area__id=category)
author = self.request.query_params.get('author')
if author:
qs = qs.filter(author_id=author)
madhab = self.request.query_params.get('madhab')
if madhab:
if madhab in ['shia', 'sunni']:
qs = qs.filter(type__sect=madhab)
else:
qs = qs.filter(type__id=madhab)
# Category (Multi-select)
categories = self._get_multi_param('category', 'categories', 'category[]', 'categories[]')
category_ids = [int(c) for c in categories if c.isdigit()]
if category_ids:
qs = qs.filter(subject_area__id__in=category_ids).distinct()
# Author (Multi-select)
authors = self._get_multi_param('author', 'authors', 'author[]', 'authors[]')
author_ids = [int(a) for a in authors if a.isdigit()]
if author_ids:
qs = qs.filter(author_id__in=author_ids)
# Madhab / Sect (Multi-select)
madhabs = self._get_multi_param('madhab', 'madhabs', 'madhab[]', 'madhabs[]', 'sect', 'sects', 'sect[]', 'sects[]')
if madhabs:
from django.db.models import Q
madhab_q = Q()
type_ids = []
for m in madhabs:
if m.lower() in ['shia', 'sunni']:
madhab_q |= Q(type__sect=m.lower())
elif m.isdigit():
type_ids.append(int(m))
if type_ids:
madhab_q |= Q(type__id__in=type_ids)
if madhab_q:
qs = qs.filter(madhab_q)
# PDF availability
pdf_available = self.request.query_params.get('pdf_available')
if pdf_available == 'true':
qs = qs.filter(documents__isnull=False)
qs = qs.filter(documents__isnull=False).distinct()
elif pdf_available == 'false':
qs = qs.filter(documents__isnull=True)
century = self.request.query_params.get('century')
if century:
if century == 'before_islam':
qs = qs.filter(authors__death_year_hijri__lt=0)
elif century.isdigit():
century_num = int(century)
# Century (Multi-select)
centuries = self._get_multi_param('century', 'centuries', 'century[]', 'centuries[]')
if centuries:
from django.db.models import Q
century_q = Q()
for c in centuries:
if c == 'before_islam':
century_q |= Q(author__death_year_hijri__lt=0)
elif c.isdigit():
century_num = int(c)
start_year = (century_num - 1) * 100 + 1
end_year = century_num * 100
qs = qs.filter(authors__death_year_hijri__gte=start_year, authors__death_year_hijri__lte=end_year)
century_q |= Q(author__death_year_hijri__gte=start_year, author__death_year_hijri__lte=end_year)
if century_q:
qs = qs.filter(century_q)
return qs

Loading…
Cancel
Save