Browse Source

Add HadisCategoryXMindView API endpoint and Swagger documentation

- Implemented HadisCategoryXMindView to retrieve a mind-map JSON structure for specific hadis categories.
- Added Swagger documentation detailing the endpoint's features, usage, and response structure.
- Updated URL routing to include the new endpoint for XMind format retrieval.
- Enhanced the HadisCategoryXMindView to support multi-language content and bookmark status for authenticated users.
master
Mohsen Taba 4 months ago
parent
commit
bcc2cf1104
  1. 132
      apps/hadis/docs.py
  2. 3
      apps/hadis/urls.py
  3. 100
      apps/hadis/views/category.py

132
apps/hadis/docs.py

@ -2540,3 +2540,135 @@ content_release_sync_swagger = swagger_auto_schema(
) )
} }
) )
# Swagger documentation for HadisCategoryXMindView
hadis_category_xmind_swagger = swagger_auto_schema(
operation_description="""
Retrieve a mind-map JSON structure (XMind format) for a specific category and its hadiths.
**Key Features:**
- Returns XMind-compatible JSON structure for mind-map visualization
- Root node represents the category
- Child nodes represent all hadiths in that category
- Supports multi-language content based on query parameter or Accept-Language header
- Includes bookmark status for each hadith (if user is authenticated)
- Returns all hadiths without pagination (suitable for mind-map visualization)
**Structure:**
- `rootTopic`: The main category node
- `id`: Category identifier (format: "cat-{category_id}")
- `title`: Localized category title
- `structureClass`: XMind structure type (org.xmind.ui.map.unbalanced for right-branching)
- `children.attached`: Array of hadith nodes
- Each hadith node contains:
- `id`: Hadith ID
- `slug`: URL-friendly identifier
- `title`: Localized hadith title
- `title_narrator`: Narrator information
- `text`: Original Arabic text
- `translation`: Translated text in requested language
- `share_link`: Direct link to the hadith
- `is_bookmarked`: Boolean indicating if user has bookmarked this hadith
**Usage:**
- Use this endpoint to generate mind-map visualizations of hadith categories
- The response format is compatible with XMind and similar mind-mapping tools
- Language can be specified via `?lang=` query parameter or Accept-Language header
- Bookmark status is only available for authenticated users
**Note:**
- This endpoint returns ALL hadiths in the category (no pagination)
- For categories with 1000+ hadiths, consider client-side limiting
- Only active (status=True) hadiths are included
""",
operation_summary="Get Category Mind-Map Structure (XMind Format)",
tags=['Dobodbi - Hadis'],
manual_parameters=[
openapi.Parameter(
'category_slug',
openapi.IN_PATH,
description="Unique slug identifier of the Hadis category. Must be a valid category slug that exists in the system.",
type=openapi.TYPE_STRING,
required=True,
example='cat-l3-25-7f5bcb'
),
openapi.Parameter(
'lang',
openapi.IN_QUERY,
description="Language code for content localization. Supported codes: 'en' (English), 'fa' (Persian), 'ar' (Arabic), 'ur' (Urdu), 'ru' (Russian). Defaults to 'en' if not specified. Can also be set via Accept-Language header.",
type=openapi.TYPE_STRING,
required=False,
default='en',
enum=['en', 'fa', 'ar', 'ur', 'ru']
),
openapi.Parameter(
'Accept-Language',
openapi.IN_HEADER,
description="Alternative way to specify language code. If both query parameter and header are provided, query parameter takes precedence.",
type=openapi.TYPE_STRING,
required=False,
default='en',
enum=['en', 'fa', 'ar', 'ur', 'ru']
)
],
responses={
status.HTTP_200_OK: openapi.Response(
description="Successfully retrieved mind-map structure for the specified category",
examples={
"application/json": {
"rootTopic": {
"id": "cat-25",
"title": "Book of Faith",
"structureClass": "org.xmind.ui.map.unbalanced",
"children": {
"attached": [
{
"id": 1,
"slug": "hadis-intention-and-actions",
"title": "The Intention",
"title_narrator": "From Umar ibn al-Khattab",
"text": "إنما الأعمال بالنيات وإنما لكل امرئ ما نوى",
"translation": "Actions are but by intention, and every man shall have only what he intended",
"share_link": "http://example.com/hadis/hadis-intention-and-actions",
"is_bookmarked": False
},
{
"id": 2,
"slug": "hadis-gabriel-hadith",
"title": "The Hadith of Gabriel",
"title_narrator": "From Abu Hurairah",
"text": "بينما نحن عند رسول الله صلى الله عليه وسلم ذات يوم",
"translation": "While we were sitting with the Messenger of Allah (peace be upon him) one day",
"share_link": "http://example.com/hadis/hadis-gabriel-hadith",
"is_bookmarked": True
},
{
"id": 3,
"slug": "hadis-islam-faith-excellence",
"title": "Islam, Faith, and Excellence",
"title_narrator": "From Abu Hurairah",
"text": "عن أبي هريرة رضي الله عنه قال: قال رسول الله صلى الله عليه وسلم",
"translation": "Narrated Abu Hurairah: The Messenger of Allah (peace be upon him) said",
"share_link": "http://example.com/hadis/hadis-islam-faith-excellence",
"is_bookmarked": False
}
]
}
}
}
}
),
status.HTTP_404_NOT_FOUND: openapi.Response(
description="Category not found. The provided category_slug does not exist in the system.",
examples={
"application/json": {
"detail": "Not found."
}
}
),
status.HTTP_500_INTERNAL_SERVER_ERROR: openapi.Response(
description="Internal server error"
)
}
)

3
apps/hadis/urls.py

@ -1,5 +1,5 @@
from django.urls import path from django.urls import path
from .views.category import HadisCategorySectListView, HadisCategoryTreeView, CategoriesView, CategoriesBySectView, HadisCategorySelectBySectView, HadisCategorySelectBySectSourceView , HadisCategoryTreeNormalView ,test_deploy,debug_headers
from .views.category import HadisCategorySectListView, HadisCategoryTreeView, CategoriesView, CategoriesBySectView, HadisCategorySelectBySectView, HadisCategorySelectBySectSourceView , HadisCategoryTreeNormalView ,test_deploy,debug_headers,HadisCategoryXMindView
from .views.hadis import HadisCollectionListView, HadisListView, HadisBasicView, HadisDetailView, HadisSyncView, HadisTransmittersView, HadisCorrectionsView,HadisMainListView, HadisFiltersView, HadisLayersView from .views.hadis import HadisCollectionListView, HadisListView, HadisBasicView, HadisDetailView, HadisSyncView, HadisTransmittersView, HadisCorrectionsView,HadisMainListView, HadisFiltersView, HadisLayersView
from .views.transmitter import TransmitterView ,TransmitterDetailView, TransmitterSyncView,TransmitterOpinionView, TransmitterOriginalTextView, TransmitterFiltersView from .views.transmitter import TransmitterView ,TransmitterDetailView, TransmitterSyncView,TransmitterOpinionView, TransmitterOriginalTextView, TransmitterFiltersView
from .views.reference import BookDetailView, BookReferencesView, BookReferenceSyncView, BookAttributeView from .views.reference import BookDetailView, BookReferencesView, BookReferenceSyncView, BookAttributeView
@ -32,6 +32,7 @@ urlpatterns = [
path('categories/', CategoriesView.as_view(), name='categories'), # ← Least specific LAST path('categories/', CategoriesView.as_view(), name='categories'), # ← Least specific LAST
# Hadis paths # 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('category/<str:category_slug>/', HadisListView.as_view(), name='hadis-list'),
path('arguments/', cached_view(HadisMainListView.as_view()), name='hadis-main-list'), path('arguments/', cached_view(HadisMainListView.as_view()), name='hadis-main-list'),
path('arguments/filters/', cached_view(HadisFiltersView.as_view()), name='hadis-filters'), path('arguments/filters/', cached_view(HadisFiltersView.as_view()), name='hadis-filters'),

100
apps/hadis/views/category.py

@ -5,6 +5,7 @@ from utils.pagination import NoPagination
from django.db.models import Q from django.db.models import Q
from utils.pagination import StandardResultsSetPagination from utils.pagination import StandardResultsSetPagination
from ..models import HadisSect, HadisCategory,Hadis from ..models import HadisSect, HadisCategory,Hadis
from apps.bookmark.serializers.bookmark import BookmarkStatusSerializer
from ..serializers import ( from ..serializers import (
HadisCategorySectListSerializer, HadisCategorySectListSerializer,
HadisCategoryTreeSerializer, HadisCategoryTreeSerializer,
@ -20,7 +21,8 @@ from ..docs import (
categories_list_swagger, categories_list_swagger,
categories_by_sect_swagger, categories_by_sect_swagger,
categories_tree_by_sect_swagger, categories_tree_by_sect_swagger,
categories_tree_by_sect_source_swagger
categories_tree_by_sect_source_swagger,
hadis_category_xmind_swagger
) )
@ -403,3 +405,99 @@ def debug_headers(request):
} }
return JsonResponse({'headers': headers, 'debug': scheme_debug}) return JsonResponse({'headers': headers, 'debug': scheme_debug})
from rest_framework.views import APIView
class HadisCategoryXMindView(APIView):
"""
Returns a mind-map JSON structure for a specific category and its hadiths.
Root -> Category
Children -> Hadiths
"""
def get_localized_text(self, json_field, lang):
"""Helper to extract text from your JSON structure"""
if not json_field or not isinstance(json_field, list):
return "Unknown"
# 1. Try specific language
for item in json_field:
if item.get('language_code') == lang:
return item.get('text', '')
# 2. Fallback to English
for item in json_field:
if item.get('language_code') == 'en':
return item.get('text', '')
# 3. Fallback to first available
if len(json_field) > 0:
return json_field[0].get('text', '')
return "Unknown"
@hadis_category_xmind_swagger
def get(self, request, category_slug):
# 1. Determine Language (support ?lang=ru or Accept-Language header)
lang = request.query_params.get('lang','en')
# 2. Get the Category (Root Node)
category = get_object_or_404(HadisCategory, slug=category_slug)
root_title = self.get_localized_text(category.title, lang)
# 3. Get the Hadiths (Child Nodes)
# Note: Mind maps generally show ALL nodes, so we avoid pagination here.
# If you have 1000+ hadiths, consider limiting this query (e.g., [:50]).
hadiths = Hadis.objects.filter(
category=category,
status=True
).order_by('number').only('id', 'number', 'title', 'title_narrator', 'translation', 'text', 'slug', 'share_link')
# 4. Get user for bookmark check
user = request.user if request and hasattr(request, 'user') else None
if user and user.is_anonymous:
user = None
# 5. Build Child Nodes List
children_nodes = []
for hadis in hadiths:
# Get Title
hadis_title = self.get_localized_text(hadis.title, lang)
hadis_title_narrator = self.get_localized_text(hadis.title_narrator, lang)
hadis_translation = self.get_localized_text(hadis.translation, lang)
# Get bookmark status
bookmark_info = BookmarkStatusSerializer.get_bookmark_info(
obj=hadis,
user=user,
service='hadith'
)
is_bookmarked = bookmark_info.get('is_bookmarked', False)
children_nodes.append({
"id": hadis.id,
"slug": hadis.slug,
"title": hadis_title,
"title_narrator": hadis_title_narrator,
"text": hadis.text,
"translation": hadis_translation,
"share_link": hadis.share_link,
"is_bookmarked": is_bookmarked,
# Optional: Add 'href' if you want XMind to handle links,
# but usually the frontend handles the 'click' event based on ID.
})
# 6. Construct XMind JSON Structure
data = {
"rootTopic": {
"id": f"cat-{category.id}",
"title": root_title,
"structureClass": "org.xmind.ui.map.unbalanced", # Standard right-branching map
"children": {
"attached": children_nodes
}
}
}
return Response(data)
Loading…
Cancel
Save