|
|
|
@ -2,7 +2,15 @@ from django.contrib.admin.utils import lookup_field |
|
|
|
from django.db.models import Prefetch, Q |
|
|
|
from rest_framework.generics import ListAPIView , RetrieveAPIView |
|
|
|
from rest_framework.response import Response |
|
|
|
from rest_framework.views import APIView |
|
|
|
from rest_framework.response import Response |
|
|
|
from rest_framework.permissions import AllowAny |
|
|
|
from django.shortcuts import get_object_or_404 |
|
|
|
from django.db.models import F |
|
|
|
from drf_yasg.utils import swagger_auto_schema |
|
|
|
|
|
|
|
from ..models import Transmitters, HadisTransmitter |
|
|
|
from ..serializers import NarratorRelationSerializer |
|
|
|
from utils.pagination import StandardResultsSetPagination |
|
|
|
from ..models import Transmitters , TransmitterOpinion, TransmitterOriginalText, Hadis |
|
|
|
from ..serializers import TransmitterSerializer , TransmitterDetailSerializer, TransmitterSyncSerializer,TransmitterOpinionSerializer, TransmitterOriginalTextSerializer, HadisListSerializer |
|
|
|
@ -114,7 +122,7 @@ class TransmitterOriginalTextView(ListAPIView): |
|
|
|
class TransmitterSyncView(ListAPIView): |
|
|
|
""" |
|
|
|
API view to sync all transmitter data for offline mode |
|
|
|
Returns all transmitters with biographical data and scholarly opinions |
|
|
|
Returns all transmitters with biographical data, opinions, and exact flat relation IDs. |
|
|
|
""" |
|
|
|
serializer_class = TransmitterSyncSerializer |
|
|
|
pagination_class = NoPagination |
|
|
|
@ -124,9 +132,6 @@ class TransmitterSyncView(ListAPIView): |
|
|
|
return self.list(request, *args, **kwargs) |
|
|
|
|
|
|
|
def get_queryset(self): |
|
|
|
""" |
|
|
|
Prefetch ALL related data at once |
|
|
|
""" |
|
|
|
return ( |
|
|
|
Transmitters.objects |
|
|
|
.prefetch_related( |
|
|
|
@ -138,11 +143,57 @@ class TransmitterSyncView(ListAPIView): |
|
|
|
) |
|
|
|
|
|
|
|
def list(self, request, *args, **kwargs): |
|
|
|
# 🧠 ۱. واکشی لینکها با values_list برای جلوگیری از هرگونه تداخل دیکشنری |
|
|
|
all_links = HadisTransmitter.objects.values_list( |
|
|
|
'hadis_id', 'chain_index', 'order', 'transmitter_id' |
|
|
|
) |
|
|
|
|
|
|
|
# 🧠 ۲. بازسازی کل زنجیرههای حدیثی به صورت کاملاً ایمن و عددی |
|
|
|
chains = {} |
|
|
|
for hadis_id, chain_index, order, transmitter_id in all_links: |
|
|
|
if transmitter_id is None: |
|
|
|
continue |
|
|
|
|
|
|
|
key = (int(hadis_id), int(chain_index)) |
|
|
|
if key not in chains: |
|
|
|
chains[key] = {} |
|
|
|
chains[key][int(order)] = int(transmitter_id) |
|
|
|
|
|
|
|
# دایرکتوریهای نگاشت روابط |
|
|
|
teachers_map = {} |
|
|
|
students_map = {} |
|
|
|
|
|
|
|
# 🧠 ۳. پردازش خطی ساختار درختواره |
|
|
|
for key, orders in chains.items(): |
|
|
|
for order, tx_id in orders.items(): |
|
|
|
if tx_id not in teachers_map: |
|
|
|
teachers_map[tx_id] = set() |
|
|
|
if tx_id not in students_map: |
|
|
|
students_map[tx_id] = set() |
|
|
|
|
|
|
|
# استاد (پوزیشن قبلی در ترتیب روایی / order + 1) |
|
|
|
if (order + 1) in orders: |
|
|
|
teachers_map[tx_id].add(orders[order + 1]) |
|
|
|
|
|
|
|
# شاگرد (پوزیشن بعدی در ترتیب روایی / order - 1) |
|
|
|
if (order - 1) in orders: |
|
|
|
students_map[tx_id].add(orders[order - 1]) |
|
|
|
|
|
|
|
queryset = self.get_queryset() |
|
|
|
serializer = self.get_serializer(queryset, many=True, context={'request': request}) |
|
|
|
|
|
|
|
# 🧠 ۴. تزریق اجباری کانتکست به سریالایزر (جلوگیری از اوررایت شدن توسط DRF) |
|
|
|
context = self.get_serializer_context() |
|
|
|
context['teachers_map'] = teachers_map |
|
|
|
context['students_map'] = students_map |
|
|
|
|
|
|
|
serializer = self.get_serializer_class()( |
|
|
|
queryset, |
|
|
|
many=True, |
|
|
|
context=context |
|
|
|
) |
|
|
|
|
|
|
|
response_data = { |
|
|
|
'count': len(serializer.data), # ← No extra query! |
|
|
|
'count': len(serializer.data), |
|
|
|
'filters': get_transmitter_filters(request), |
|
|
|
'results': serializer.data |
|
|
|
} |
|
|
|
@ -223,4 +274,124 @@ class NarratorArgumentsListView(ListAPIView): |
|
|
|
).select_related( |
|
|
|
'category__sect', |
|
|
|
'hadis_status' |
|
|
|
).distinct().order_by('id') |
|
|
|
).distinct().order_by('id') |
|
|
|
|
|
|
|
|
|
|
|
# class NarratorRelationsView(APIView): |
|
|
|
# """ |
|
|
|
# API view to get the Teachers and Students of a specific narrator |
|
|
|
# based on their position in Hadith transmission chains. |
|
|
|
# """ |
|
|
|
# permission_classes = [AllowAny] |
|
|
|
|
|
|
|
# @swagger_auto_schema( |
|
|
|
# operation_summary="Get Narrator Teachers and Students", |
|
|
|
# operation_description="Analyzes transmission chains to find the teachers (order + 1) and students (order - 1) of a narrator.", |
|
|
|
# tags=['Dobodbi - Hadis (V2)'], |
|
|
|
# ) |
|
|
|
# def get(self, request, narrator_slug, *args, **kwargs): |
|
|
|
# # 1. پیدا کردن راوی اصلی |
|
|
|
# narrator = get_object_or_404(Transmitters, slug=narrator_slug) |
|
|
|
|
|
|
|
# # 2. پیدا کردن تمام اتصالاتی که این راوی در آنها حضور دارد |
|
|
|
# narrator_links = HadisTransmitter.objects.filter(transmitter=narrator) |
|
|
|
|
|
|
|
# teacher_ids = set() |
|
|
|
# student_ids = set() |
|
|
|
|
|
|
|
# # 3. تحلیل زنجیرهها برای پیدا کردن اساتید و شاگردان |
|
|
|
# for link in narrator_links: |
|
|
|
# # استاد = کسی که در همان حدیث و همان زنجیره، Order او یکی بیشتر از راوی ماست |
|
|
|
# teacher_link = HadisTransmitter.objects.filter( |
|
|
|
# hadis_id=link.hadis_id, |
|
|
|
# chain_index=link.chain_index, |
|
|
|
# order=link.order + 1 |
|
|
|
# ).first() |
|
|
|
# if teacher_link: |
|
|
|
# teacher_ids.add(teacher_link.transmitter_id) |
|
|
|
|
|
|
|
# # شاگرد = کسی که در همان حدیث و همان زنجیره، Order او یکی کمتر از راوی ماست |
|
|
|
# student_link = HadisTransmitter.objects.filter( |
|
|
|
# hadis_id=link.hadis_id, |
|
|
|
# chain_index=link.chain_index, |
|
|
|
# order=link.order - 1 |
|
|
|
# ).first() |
|
|
|
# if student_link: |
|
|
|
# student_ids.add(student_link.transmitter_id) |
|
|
|
|
|
|
|
# # 4. واکشی اطلاعات اساتید و شاگردان از دیتابیس به صورت یکجا |
|
|
|
# teachers_qs = Transmitters.objects.filter(id__in=teacher_ids) |
|
|
|
# students_qs = Transmitters.objects.filter(id__in=student_ids) |
|
|
|
|
|
|
|
# # 5. سریالایز کردن دادهها با در نظر گرفتن زبان ریکوئست |
|
|
|
# teachers_data = NarratorRelationSerializer(teachers_qs, many=True, context={'request': request}).data |
|
|
|
# students_data = NarratorRelationSerializer(students_qs, many=True, context={'request': request}).data |
|
|
|
|
|
|
|
# # 6. بازگرداندن پاسخ |
|
|
|
# return Response({ |
|
|
|
# "narrator_slug": narrator.slug, |
|
|
|
# "teachers_count": len(teachers_data), |
|
|
|
# "students_count": len(students_data), |
|
|
|
# "teachers": teachers_data, |
|
|
|
# "students": students_data |
|
|
|
# }) |
|
|
|
|
|
|
|
from django.db.models import Exists, OuterRef # 👈 حتما این دو را ایمپورت کنید |
|
|
|
|
|
|
|
class NarratorRelationsView(APIView): |
|
|
|
""" |
|
|
|
API view to get the Teachers and Students of a specific narrator |
|
|
|
based on their position in Hadith transmission chains. |
|
|
|
Optimized to run fully inside the database. |
|
|
|
""" |
|
|
|
permission_classes = [AllowAny] |
|
|
|
|
|
|
|
@swagger_auto_schema( |
|
|
|
operation_summary="Get Narrator Teachers and Students", |
|
|
|
operation_description="Analyzes transmission chains to find the teachers (order + 1) and students (order - 1) of a narrator.", |
|
|
|
tags=['Dobodbi - Hadis (V2)'], |
|
|
|
) |
|
|
|
def get(self, request, narrator_slug, *args, **kwargs): |
|
|
|
# 1. پیدا کردن راوی اصلی |
|
|
|
narrator = get_object_or_404(Transmitters, slug=narrator_slug) |
|
|
|
|
|
|
|
# 2. سابکوئری برای بررسی اینکه آیا در این سند خاص، این شخص نقش «استاد» را برای راوی اصلی دارد؟ |
|
|
|
is_teacher_subquery = HadisTransmitter.objects.filter( |
|
|
|
transmitter=narrator, |
|
|
|
hadis_id=OuterRef('hadis_id'), |
|
|
|
chain_index=OuterRef('chain_index'), |
|
|
|
order=OuterRef('order') - 1 # استاد یعنی Order او یکی کمتر از راوی تارگت باشد (بالاتر در زنجیره) |
|
|
|
) |
|
|
|
|
|
|
|
# 3. سابکوئری برای بررسی نقش «شاگرد» |
|
|
|
is_student_subquery = HadisTransmitter.objects.filter( |
|
|
|
transmitter=narrator, |
|
|
|
hadis_id=OuterRef('hadis_id'), |
|
|
|
chain_index=OuterRef('chain_index'), |
|
|
|
order=OuterRef('order') + 1 # شاگرد یعنی Order او یکی بیشتر باشد (پایینتر در زنجیره) |
|
|
|
) |
|
|
|
|
|
|
|
# 4. واکشی سریع آیدیها فقط با اجرای ۲ کوئری داخل خود SQL بدون حلقه پایتون |
|
|
|
teacher_ids = HadisTransmitter.objects.annotate( |
|
|
|
is_teacher=Exists(is_teacher_subquery) |
|
|
|
).filter(is_teacher=True).values_list('transmitter_id', flat=True).distinct() |
|
|
|
|
|
|
|
student_ids = HadisTransmitter.objects.annotate( |
|
|
|
is_student=Exists(is_student_subquery) |
|
|
|
).filter(is_student=True).values_list('transmitter_id', flat=True).distinct() |
|
|
|
|
|
|
|
# 5. واکشی دیتای نهاییِ راویان با select_related برای جلوگیری از N+1 روی Reliability |
|
|
|
teachers_qs = Transmitters.objects.filter(id__in=teacher_ids).select_related('reliability') |
|
|
|
students_qs = Transmitters.objects.filter(id__in=student_ids).select_related('reliability') |
|
|
|
|
|
|
|
# 6. ساخت خروجی |
|
|
|
teachers_data = NarratorRelationSerializer(teachers_qs, many=True, context={'request': request}).data |
|
|
|
students_data = NarratorRelationSerializer(students_qs, many=True, context={'request': request}).data |
|
|
|
|
|
|
|
return Response({ |
|
|
|
"narrator_slug": narrator.slug, |
|
|
|
"teachers_count": len(teachers_data), |
|
|
|
"students_count": len(students_data), |
|
|
|
"teachers": teachers_data, |
|
|
|
"students": students_data |
|
|
|
}) |