diff --git a/apps/hadis/docs.py b/apps/hadis/docs.py index ff29b7f..97e07ed 100644 --- a/apps/hadis/docs.py +++ b/apps/hadis/docs.py @@ -84,11 +84,11 @@ hadis_category_tree_swagger = swagger_auto_schema( "source_type": "quran", "hadis_count": 2, "has_hadis": False, - "hadis_index": [], "order": 1, "thumbnail": None, "xmind_file": None, "has_xmind_file": False, + "children_count": 3, "children": [ { "id": 11, @@ -97,7 +97,6 @@ hadis_category_tree_swagger = swagger_auto_schema( "source_type": "quran", "hadis_count": 2, "has_hadis": True, - "hadis_index": [1, 2], "order": 1, "thumbnail": None, "xmind_file": None, @@ -109,6 +108,7 @@ hadis_category_tree_swagger = swagger_auto_schema( "sect_type": "shia", "source_type": "quran" }, + "children_count": 3, "children": [] } ] @@ -133,11 +133,11 @@ hadis_category_tree_swagger = swagger_auto_schema( "source_type": "hadith", "hadis_count": 1, "has_hadis": True, - "hadis_index": [50], "order": 1, "thumbnail": None, "xmind_file": None, "has_xmind_file": False, + "children_count": 3, "children": [] } ] @@ -414,6 +414,13 @@ hadis_basic_swagger = swagger_auto_schema( "translation": "Actions are but by intention...", "share_link": "http://example.com/hadis/1", 'explanation': "This hadith emphasizes the importance of intention in all actions...", + "category": { + "id": 330, + "title": "Толкование суры Аль-Фатиха", + "slug": "cat-slug", + "source_type": "quran", + "sect_type": "shia" + } } } ), @@ -427,173 +434,159 @@ hadis_basic_swagger = swagger_auto_schema( ) hadis_detail_swagger = swagger_auto_schema( - operation_summary="Get Hadis Full Details", - operation_description="Retrieve detailed information about a specific hadis including status, tags, and references (transmitters and corrections moved to separate endpoints)", + operation_summary="Get Hadis Detail Metadata", + operation_description="Retrieve metadata for a specific hadis including Status, Tags, External Links, and Book References. (Note: Text and Narrators are in separate endpoints).", tags=['Hadis'], + manual_parameters=[ + openapi.Parameter( + 'hadis_id', + openapi.IN_PATH, + description="ID of the hadis", + type=openapi.TYPE_INTEGER, + required=True + ) + ], responses={ - 200: openapi.Response( - description="Hadis details retrieved successfully", - schema=openapi.Schema( - type=openapi.TYPE_OBJECT, - properties={ - 'id': openapi.Schema(type=openapi.TYPE_INTEGER, description='Hadis ID'), - 'number': openapi.Schema(type=openapi.TYPE_INTEGER, description='Hadis number'), - 'title': openapi.Schema(type=openapi.TYPE_STRING, description='Hadis title'), - 'text': openapi.Schema(type=openapi.TYPE_STRING, description='Arabic text of hadis'), - 'translation': openapi.Schema(type=openapi.TYPE_STRING, description='Translation in request language'), - 'explanation': openapi.Schema(type=openapi.TYPE_STRING, description='Detailed explanation'), - 'address': openapi.Schema(type=openapi.TYPE_STRING, description='Source address'), - 'hadis_status_text': openapi.Schema(type=openapi.TYPE_STRING, description='Status description'), - 'links': openapi.Schema( - type=openapi.TYPE_ARRAY, - items=openapi.Schema( - type=openapi.TYPE_OBJECT, - properties={ - 'title': openapi.Schema(type=openapi.TYPE_STRING), - 'link': openapi.Schema(type=openapi.TYPE_STRING) - } - ), - description='Related links' - ), - 'status': openapi.Schema(type=openapi.TYPE_BOOLEAN, description='Active status'), - 'category': openapi.Schema( - type=openapi.TYPE_OBJECT, - properties={ - 'id': openapi.Schema(type=openapi.TYPE_INTEGER), - 'title': openapi.Schema(type=openapi.TYPE_STRING), - 'category_type': openapi.Schema(type=openapi.TYPE_STRING) + status.HTTP_200_OK: openapi.Response( + description="Hadis metadata details", + examples={ + "application/json": { + "id": 1800, + "number": 1, + "hadis_status_text": "Agreed upon by all scholars", + "hadis_status": { + "id": 130, + "title": "Weak / Discontinued", + "color": "orange" + }, + "links": [ + { + "link": "https://example.com/source1", + "title": "Online Reference 1" + }, + { + "link": "https://example.com/source2", + "title": "PDF Source" } - ), - 'hadis_status': openapi.Schema( - type=openapi.TYPE_OBJECT, - properties={ - 'id': openapi.Schema(type=openapi.TYPE_INTEGER), - 'title': openapi.Schema(type=openapi.TYPE_STRING), - 'color': openapi.Schema(type=openapi.TYPE_STRING) + ], + "share_link": "https://imamjavad.nwhco.ir/hadis/1800", + "tags": [ + { + "id": 510, + "title": "Hajj (Pilgrimage)" + }, + { + "id": 520, + "title": "Legal Rulings" } - ), - 'tags': openapi.Schema( - type=openapi.TYPE_ARRAY, - items=openapi.Schema( - type=openapi.TYPE_OBJECT, - properties={ - 'id': openapi.Schema(type=openapi.TYPE_INTEGER), - 'title': openapi.Schema(type=openapi.TYPE_STRING) - } - ) - ), - 'transmitters': openapi.Schema( - type=openapi.TYPE_ARRAY, - items=openapi.Schema( - type=openapi.TYPE_OBJECT, - properties={ - 'id': openapi.Schema(type=openapi.TYPE_INTEGER), - 'order': openapi.Schema(type=openapi.TYPE_INTEGER), - 'is_gap': openapi.Schema(type=openapi.TYPE_BOOLEAN), - 'transmitter': openapi.Schema( - type=openapi.TYPE_OBJECT, - properties={ - 'id': openapi.Schema(type=openapi.TYPE_INTEGER), - 'full_name': openapi.Schema(type=openapi.TYPE_STRING), - 'birth_year_hijri': openapi.Schema(type=openapi.TYPE_INTEGER), - 'death_year_hijri': openapi.Schema(type=openapi.TYPE_INTEGER), - 'description': openapi.Schema(type=openapi.TYPE_STRING) - } - ) - } - ) - ), - 'references': openapi.Schema( - type=openapi.TYPE_ARRAY, - items=openapi.Schema( - type=openapi.TYPE_OBJECT, - properties={ - 'id': openapi.Schema(type=openapi.TYPE_INTEGER), - 'page_number': openapi.Schema(type=openapi.TYPE_STRING), - 'hadis_number_in_book': openapi.Schema(type=openapi.TYPE_STRING), - 'description': openapi.Schema(type=openapi.TYPE_STRING), - 'book': openapi.Schema( - type=openapi.TYPE_OBJECT, - properties={ - 'id': openapi.Schema(type=openapi.TYPE_INTEGER), - 'title': openapi.Schema(type=openapi.TYPE_STRING), - 'summary_title': openapi.Schema(type=openapi.TYPE_STRING), - 'publisher': openapi.Schema(type=openapi.TYPE_STRING), - 'year_of_publication': openapi.Schema(type=openapi.TYPE_STRING) - } - ), - 'images': openapi.Schema( - type=openapi.TYPE_ARRAY, - items=openapi.Schema( - type=openapi.TYPE_OBJECT, - properties={ - 'id': openapi.Schema(type=openapi.TYPE_INTEGER), - 'thumbnail': openapi.Schema(type=openapi.TYPE_STRING), - 'priority': openapi.Schema(type=openapi.TYPE_INTEGER) - } - ) - ) - } - ) - ) + ], + "references": [ + { + "id": 2193, + "book_title": "Sahih al-Bukhari", + "book_authors": "Imam Bukhari", + "book_description": "Chapter on Patience, Vol 2" + } + ], + "reference_images": [ + { + "id": 1768, + "thumbnail": "http://api.site.com/media/hadis/reference_images/scan_01.png", + "priority": 0 + } + ], + "address": "Page 54, Volume 2, Line 10" } - ) + } ), - 404: openapi.Response(description="Hadis not found") + status.HTTP_404_NOT_FOUND: openapi.Response( + description="Hadis not found" + ) } ) + hadis_transmitters_swagger = swagger_auto_schema( - operation_description="Get all transmitters for a specific hadis including their biographical information and transmission details", - operation_summary="Get Hadis Transmitters", - operation_id="getHadisTransmitters", + operation_description="Get the chain of transmitters for a specific Hadis. Returns the Hadis ID, the count of unique narrator generations (layers), and the ordered list of transmitters.", + operation_summary="Get Hadis Chain (Isnad)", + operation_id="getHadisChain", tags=['Hadis'], manual_parameters=[ + # Path Parameter: ID openapi.Parameter( 'hadis_id', openapi.IN_PATH, - description="ID of the hadis", + description="The ID of the Hadis to fetch", type=openapi.TYPE_INTEGER, required=True + ), + # Query Parameter: Layer Filter (Optional) + openapi.Parameter( + 'layer', + openapi.IN_QUERY, + description="Filter transmitters by narrator layer slug (e.g., ?layer=sahaba)", + type=openapi.TYPE_STRING, + required=False ) ], responses={ status.HTTP_200_OK: openapi.Response( - description="Hadis transmitters information", + description="Successful retrieval of the transmission chain.", examples={ "application/json": { - "hadis_id": 1, - "hadis_description": "This hadith emphasizes the importance of intention in all actions...", - "transmitters_count": 2, - "transmitters": [ + "id": 1800, + "layer_count": 3, + "results": [ { - "id": 1, + "id": 5992, "order": 1, "is_gap": False, - "narrator_layer": "sahaba", + "narrator_layer_description": "Companions of the Prophet (Sahaba)", + "layer": "sahaba", "transmitter": { - "id": 1, + "id": 53, "full_name": "Abu Hurairah", "birth_year_hijri": 18, "death_year_hijri": 59, - "madhhab": "sunni", - "description": "One of the most prolific narrators of hadith", - "reliability": "very_reliable" + "known_as": "Father of the Kitten", + "nickname": None, + "reliability": "very_reliable", } }, { - "id": 2, + "id": 5993, "order": 2, "is_gap": False, - "narrator_layer": "tabiin", + "narrator_layer_description": "Successors (Tabi'un)", + "layer": "tabiun", "transmitter": { - "id": 2, + "id": 60, "full_name": "Sa'id ibn al-Musayyib", "birth_year_hijri": 15, "death_year_hijri": 94, + "known_as": "Sheikh of the Tabi'un", + "nickname": None, + "reliability": "reliable", "madhhab": "sunni", - "description": "Great scholar of Medina", - "reliability": "reliable" + "generation": 2 + } + }, + { + "id": 5995, + "order": 3, + "is_gap": True, + "narrator_layer_description": None, + "layer": None, + "transmitter": { + "id": 59, + "full_name": "Unknown Person", + "birth_year_hijri": None, + "death_year_hijri": None, + "known_as": None, + "nickname": None, + "reliability": "unknown", + "madhhab": "unknown", + "generation": None } } ] @@ -602,9 +595,6 @@ hadis_transmitters_swagger = swagger_auto_schema( ), status.HTTP_404_NOT_FOUND: openapi.Response( description="Hadis not found" - ), - status.HTTP_500_INTERNAL_SERVER_ERROR: openapi.Response( - description="Internal server error" ) } ) @@ -653,7 +643,7 @@ hadis_corrections_swagger = swagger_auto_schema( description="Successfully retrieved corrections for the specified hadis", examples={ "application/json": { - "hadis_id": 1, + "hadis_id": 1800, "corrections_count": 2, "corrections": [ { @@ -751,52 +741,66 @@ hadis_info_swagger = swagger_auto_schema( # Swagger documentation for TransmitterView transmitter_list_swagger = swagger_auto_schema( - operation_description="Get list of transmitters (narrators) with optional filtering by status, madhhab, and generation", + operation_description="Get a paginated list of transmitters (narrators) with optional filtering by reliability, madhhab, and generation.", operation_summary="List Transmitters", + operation_id="listTransmitters", tags=['Hadis'], manual_parameters=[ openapi.Parameter( 'status', openapi.IN_QUERY, - description='Filter by reliability status (very_reliable, reliable, acceptable, weak, very_weak, unknown)', + description='Filter by reliability status (e.g. very_reliable, weak)', type=openapi.TYPE_STRING, required=False ), openapi.Parameter( 'madhhab', openapi.IN_QUERY, - description='Filter by madhhab/school (shia, sunni, hanafi, maliki, shafii, hanbali, other, unknown)', + description='Filter by madhhab (e.g. shia, sunni)', type=openapi.TYPE_STRING, required=False ), openapi.Parameter( 'generation', openapi.IN_QUERY, - description='Filter by generation (1-10 representing narrator layers/classes)', + description='Filter by generation integer', type=openapi.TYPE_INTEGER, required=False ), ], responses={ status.HTTP_200_OK: openapi.Response( - description="List of transmitters with optional filtering applied", + description="Paginated list of transmitters", examples={ - "application/json": [ - { - "id": 1, - "full_name": "Abu Daud Sulaiman ibn al-Ash'ath al-Azdi al-Sijistani", - "birth_year_hijri": 202, - "death_year_hijri": 275, - "description": "Imam Abu Daud, compiler of Sunan Abu Daud, one of the six major hadith collections" - }, - { - "id": 2, - "full_name": "Muhammad ibn Isma'il al-Bukhari", - "birth_year_hijri": 194, - "death_year_hijri": 256, - "description": "Imam Bukhari, compiler of Sahih al-Bukhari, the most authentic hadith collection" - } - ] + "application/json": { + "count": 10, + "next": "http://api.example.com/transmitters/?page=2", + "previous": None, + "results": [ + { + "id": 56, + "full_name": "Абу Дауд ас-Сиджистани", + "birth_year_hijri": 202, + "death_year_hijri": 275, + "known_as": "Imam Abu Daud", + "nickname": "Al-Sijistani", + "reliability": "very_reliable", + "madhhab": "sunni", + "generation": 3 + }, + { + "id": 51, + "full_name": "Мухаммад ибн Якуб Кулейни", + "birth_year_hijri": 250, + "death_year_hijri": 329, + "known_as": "Thiqat al-Islam", + "nickname": None, + "reliability": "very_reliable", + "madhhab": "shia", + "generation": 4 + } + ] + } } ), status.HTTP_500_INTERNAL_SERVER_ERROR: openapi.Response( @@ -841,7 +845,7 @@ transmitter_detail_swagger = swagger_auto_schema( "in_sahih_bukhari": False, "in_sahih_muslim": True, "description": "Imam Abu Daud, compiler of Sunan Abu Daud", - "thumbnail": "http://example.com/media/transmitters/abu_daud.jpg", + "generation": 3 } } ), @@ -854,59 +858,109 @@ transmitter_detail_swagger = swagger_auto_schema( } ) +from drf_yasg.utils import swagger_auto_schema +from drf_yasg import openapi +from rest_framework import status + transmitter_sync_swagger = swagger_auto_schema( - operation_description="Get complete transmitter (narrator) data for offline synchronization including biographical information and scholarly opinions", + operation_description="Get complete transmitter (narrator) data for offline synchronization. Returns a flat list of narrators with biographical info.", operation_summary="Sync Transmitter Data", operation_id="syncTransmitterData", tags=['Hadis'], responses={ status.HTTP_200_OK: openapi.Response( - description="Complete transmitter data for synchronization", + description="Complete transmitter data list", examples={ "application/json": { - "count": 150, - "results": { - "1": { - "id": 1, - "full_name": "Abu Daud Sulaiman ibn al-Ash'ath al-Azdi al-Sijistani", + "count": 10, + "results": [ + { + "id": 51, + "full_name": "Мухаммад ибн Якуб Кулейни", "biographical": { - "personal_info": { - "full_name": "Abu Daud Sulaiman ibn al-Ash'ath al-Azdi al-Sijistani", - "kunya": "Abu Daud", - "known_as": "Imam Abu Daud", - "nickname": "Al-Sijistani" - }, - "dates": { - "birth_year_hijri": 202, - "death_year_hijri": 275, - "age_at_death": 73 - }, - "locations": { - "origin": "Sijistan (modern Sistan)", - "lived_in": "Basra, Baghdad", - "died_in": "Basra" - }, - "religious_profile": { - "reliability": "very_reliable", - "madhhab": "shafii", - "in_sahih_muslim": True, - "in_sahih_bukhari": False - }, - "description": "One of the six canonical hadith collectors...", - "thumbnail": "http://example.com/media/transmitters/abu_daud.jpg" + "full_name": "Мухаммад ибн Якуб Кулейни", + "kunya": None, + "known_as": None, + "nickname": None, + "origin": None, + "lived_in": None, + "died_in": None, + "birth_year_hijri": 250, + "death_year_hijri": 329, + "age_at_death": None, + "generation": None, + "reliability": "unknown", + "madhhab": "unknown", + "in_sahih_muslim": False, + "in_sahih_bukhari": False, + "description": "Шейх Кулейни, автор книги Аль-Кафи и один из великих мухаддисов шиитов", + "thumbnail": None }, "scholars_opinions": [ - { + { + "id": 2, + "scholar_name": "test sc2h", + "opinion_text": "some2 opinions", + "status": "confirmed", + "created_at": "2025-12-16T11:38:19.646613", + "updated_at": "2025-12-16T11:38:19.646613" + }, + { "id": 1, - "scholar_name": "Imam al-Nawawi", - "opinion_text": "Abu Daud is reliable and trustworthy...", + "scholar_name": "test sch", + "opinion_text": "some opinions", "status": "confirmed", - "created_at": "2025-12-13T10:00:00Z", - "updated_at": "2025-12-13T10:00:00Z" - } + "created_at": "2025-12-16T11:38:02.679871", + "updated_at": "2025-12-16T11:38:02.679871" + } + ], + "original_texts": [ + { + "id": 1, + "title": "text title", + "text": "", + "translation": { + "en": "translate" + }, + "share_link": "http/exmaokns.com" + }, + { + "id": 2, + "title": "text2 title", + "text": "", + "translation": { + "en": "translate" + }, + "share_link": "http/exmaokns.com" + } ] + }, + { + "id": 56, + "full_name": "Абу Дауд ас-Сиджистани", + "biographical": { + "full_name": "Абу Дауд ас-Сиджистани", + "kunya": "Abu Daud", + "known_as": "Imam Abu Daud", + "nickname": "Al-Sijistani", + "origin": "Sijistan", + "lived_in": "Basra", + "died_in": "Basra", + "birth_year_hijri": 202, + "death_year_hijri": 275, + "age_at_death": 73, + "generation": 3, + "reliability": "very_reliable", + "madhhab": "shafii", + "in_sahih_muslim": False, + "in_sahih_bukhari": False, + "description": "Имам Абу Дауд, автор Сунан Абу Дауд", + "thumbnail": "http://example.com/media/abu_daud.jpg" + }, + "scholars_opinions": [], + "original_texts": [] } - } + ] } } ), @@ -967,8 +1021,6 @@ transmitter_opinion_swagger = swagger_auto_schema( "scholar_name": "Ibn Hajar al-Asqalani", "opinion_text": "He was a reliable and trustworthy narrator. His narrations are accepted and he is considered among the reliable transmitters of hadith. He had good memory and was known for his accuracy in transmission.", "status": "confirmed", - "created_at": "2024-01-15T10:30:00Z", - "updated_at": "2024-01-15T10:30:00Z" }, { "id": 2, @@ -976,8 +1028,6 @@ transmitter_opinion_swagger = swagger_auto_schema( "scholar_name": "Imam al-Dhahabi", "opinion_text": "A reliable narrator with good character. His narrations are generally accepted, though some scholars have noted minor issues in certain chains of transmission.", "status": "mixed", - "created_at": "2024-01-14T14:20:00Z", - "updated_at": "2024-01-14T14:20:00Z" }, { "id": 3, @@ -985,8 +1035,6 @@ transmitter_opinion_swagger = swagger_auto_schema( "scholar_name": "Ibn Ma'in", "opinion_text": "Trustworthy and reliable. His narrations are sound and he is considered among the reliable transmitters.", "status": "confirmed", - "created_at": "2024-01-13T09:15:00Z", - "updated_at": "2024-01-13T09:15:00Z" } ] } @@ -1103,144 +1151,177 @@ transmitter_original_text_swagger = swagger_auto_schema( # Swagger documentation for BookReferencesView book_references_list_swagger = swagger_auto_schema( operation_description=""" - Retrieve a comprehensive list of all book references used in hadith studies. + Retrieve a paginated list of all book references used in hadith studies. **Key Features:** - - Returns all book references in the system - - Includes metadata such as title, description, rating, and author information - - Provides book cover images and volume counts - - Useful for browsing available reference books - - **Usage:** - - Use this endpoint to get an overview of all available reference books - - The `rate` field indicates the book's rating (typically 0-5) - - The `volume_count` shows how many volumes the book has - - The `image` array contains book cover images and related images - - The `author` field contains the primary author's name + - Returns a paginated list of book references + - Includes metadata such as title, description, rating, and volume info **Response Structure:** - - Returns an array of book reference objects - - Each book includes basic information, images, and volume count - - Books are typically ordered by ID or title - - **Note:** - - Some books may have multiple volumes (indicated by `volume_count`) - - Images array may be empty if no images are available - - The author field contains the name of the primary author + - `count`: Total number of books + - `results`: Array of book objects + - `rate`: The book's rating (returned as a string, e.g., "1.20") + - `author`: List of authors associated with the book + - `volume`: Volume information (string) """, operation_summary="List Book References", + operation_id="listBookReferences", tags=['Hadis'], responses={ status.HTTP_200_OK: openapi.Response( - description="Successfully retrieved list of all book references", + description="Paginated list of book references", examples={ - "application/json": [ - { - "id": 1, - "title": "Sahih al-Bukhari", - "description": "The most authentic collection of hadith compiled by Imam Bukhari. This collection is considered the most authentic book after the Quran and contains over 7,000 hadiths.", - "rate": 5.0, - "author": "Muhammad ibn Isma'il al-Bukhari", - "image": [ - { - "id": 1, - "image": "http://example.com/media/books/bukhari_cover.jpg", - "order": 1, - "description": "Front cover of Sahih al-Bukhari", - "created_at": "2024-01-01T00:00:00Z" - }, - { - "id": 2, - "image": "http://example.com/media/books/bukhari_back.jpg", - "order": 2, - "description": "Back cover of Sahih al-Bukhari", - "created_at": "2024-01-01T00:00:00Z" - } - ], - "volume_count": 9 - }, - { - "id": 2, - "title": "Sahih Muslim", - "description": "The second most authentic collection of hadith compiled by Imam Muslim ibn al-Hajjaj. This collection is highly regarded and contains over 7,000 hadiths.", - "rate": 4.9, - "author": "Muslim ibn al-Hajjaj al-Qushayri", - "image": [ - { - "id": 3, - "image": "http://example.com/media/books/muslim_cover.jpg", - "order": 1, - "description": "Front cover of Sahih Muslim", - "created_at": "2024-01-01T00:00:00Z" - } - ], - "volume_count": 7 - }, - { - "id": 3, - "title": "Sunan Abu Daud", - "description": "A collection of hadith compiled by Imam Abu Daud. Contains approximately 4,800 hadiths focusing on legal rulings.", - "rate": 4.8, - "author": "Abu Daud Sulaiman ibn al-Ash'ath", - "image": [], - "volume_count": 4 - } - ] + "application/json": { + "count": 2, + "next": None, + "previous": None, + "results": [ + { + "id": 4, + "title": "book2", + "rate": "1.20", + "author": [ + { + 'id': 5, + 'name': 'author.name' + }, + { + 'id': 6, + 'name': 'author.name' + } + ], + "description": "book desc", + "volume": "9" + }, + { + "id": 2, + "title": "book", + "rate": "1.20", + "author": [ + { + "id": 1, + "name": "Author Name" + } + ], + "description": "book desc", + "volume": "9" + } + ] + } } ), status.HTTP_500_INTERNAL_SERVER_ERROR: openapi.Response( - description="Internal server error occurred while processing the request" + description="Internal server error" ) } ) reference_sync_swagger = swagger_auto_schema( - operation_description="Get complete book reference data for offline synchronization including basic information, detailed publication info, and related hadises", + operation_description="Get complete book reference data for offline synchronization. Returns a paginated list of books with detailed metadata.", operation_summary="Sync Book References Data", operation_id="syncReferenceData", tags=['Hadis'], responses={ status.HTTP_200_OK: openapi.Response( - description="Complete book references data for synchronization", + description="Complete book references data list", examples={ "application/json": { - "count": 50, - "results": { - "1": { - "id": 1, - "title": "Sahih al-Bukhari", - "basic_info": { - "title": "Sahih al-Bukhari", - "authors": [ - { - "id": 1, - "name": "Muhammad ibn Isma'il al-Bukhari" - } - ], - "rating": 5.0, - "description": "The most authentic collection of hadith compiled by Imam Bukhari", - "volume": "9 volumes" - }, - "information": { - "language": "Arabic", + "count": 2, + "next": None, + "previous": None, + "results": [ + { + "id": 2, + "title": "Sahih Muslim", + "rate": "4.80", + "author": [ + { + "id": 55, + "name": "Muslim ibn al-Hajjaj" + } + ], + "detail": { + "description": "One of the six major collections of Sunni hadith", + "volume": "7", + "language": "arabic", "isbn": "978-1234567890", - "year_of_publication": "846", - "number_of_pages": 4200, - "volume_info": "9 volumes", - "rating": 5.0 + "year_of_publication": "261 AH", + "number_of_pages": 1200, + "volume_info": "7 Volumes", + "rating": 4.8 }, - "hadis": [ + "image": [ { - "id": 1, - "title": "The Opening", - "title_narrator": "From Abu Hurairah", - "text": "Actions are but by intention...", - "translation": "Actions are but by intention...", - "share_link": "http://example.com/hadis/1" + "id": 101, + "image": "http://api.site.com/media/books/muslim_cover.jpg", + "description": "Front Cover", + "order": 1 + } + ], + "attribute": [ + { + "id": 10, + "title": "Publisher", + "value": "Dar-us-Salam", + "book_reference": 2 + }, + { + "id": 11, + "title": "Edition", + "value": "2nd Edition", + "book_reference": 2 + } + ], + "hadises": [ + { + "id": 1001, + "title": "The Book of Faith", + "title_narrator": "Abu Huraira", + "text": "Faith has seventy-odd branches...", + "translation": "Faith has seventy-odd branches...", + "share_link": "http://site.com/hadis/1001", } ] + }, + { + "id": 4, + "title": "Al-Kafi", + "rate": "4.90", + "author": [ + { + "id": 51, + "name": "Sheikh Kulayni" + } + ], + "detail": { + "description": "The most important Shia hadith collection", + "volume": "8", + "language": "arabic", + "isbn": "978-9876543210", + "year_of_publication": "329 AH", + "number_of_pages": 3500, + "volume_info": "8 Volumes", + "rating": 4.9 + }, + "image": [ + { + "id": 102, + "image": "http://api.site.com/media/books/kafi_cover.jpg", + "description": "Volume 1 Cover", + "order": 1 + } + ], + "attribute": [ + { + "id": 12, + "title": "Publisher", + "value": "Dar al-Kutub al-Islamiyya", + "book_reference": 4 + } + ], + "hadises": [] } - } + ] } } ), @@ -1250,7 +1331,6 @@ reference_sync_swagger = swagger_auto_schema( } ) - # Swagger documentation for BookAuthorView book_authors_list_swagger = swagger_auto_schema( operation_description="Get list of all book authors who have contributed to hadith literature", @@ -1289,131 +1369,94 @@ book_detail_swagger = swagger_auto_schema( Retrieve detailed information about a specific book reference. **Key Features:** - - Returns comprehensive details about a book reference - - Includes publication information (ISBN, year, pages, language) - - Provides author information and book images - - Lists all hadis referenced from this book - - Includes volume information and rating - - **Usage:** - - Use this endpoint to get complete details about a specific book - - The `hadis` array contains all hadis entries that reference this book - - The `author` array may contain multiple authors if applicable - - The `image` array includes all book-related images (covers, pages, etc.) - - Publication details help identify specific editions + - Returns comprehensive details including attributes, authors, images, and referenced hadis. + - Matches the `BookDetailSerializer` structure. **Response Structure:** - - Complete book metadata including all fields from the book reference model - - Related hadis entries with basic information - - Author and image information - - Volume count for multi-volume books - - **Note:** - - The `hadis` field contains hadis entries that are referenced in this book - - Each hadis includes its category information and translation - - Images are ordered by the `order` field - - The `volume_count` field shows how many volumes this book has + - `attribute`: List of extra book details (e.g., ISBN, Year, Pages). + - `author`: List of authors. + - `image`: List of book covers or reference images. + - `hadis`: List of Hadis that reference this book. """, operation_summary="Get Book Reference Details", + operation_id="getBookReferenceDetails", tags=['Hadis'], manual_parameters=[ openapi.Parameter( 'bookreference_id', openapi.IN_PATH, - description="Unique identifier of the book reference. Must be a valid book reference ID that exists in the system.", + description="Unique identifier of the book reference", type=openapi.TYPE_INTEGER, required=True, - example=1 + example=2 ) ], responses={ status.HTTP_200_OK: openapi.Response( - description="Successfully retrieved detailed information about the book reference", + description="Detailed book information", examples={ "application/json": { - "id": 1, - "title": "Sahih al-Bukhari", - "description": "The most authentic collection of hadith compiled by Imam Bukhari. This collection is considered the most authentic book after the Quran and contains over 7,000 hadiths carefully selected from hundreds of thousands of narrations.", - "language": "Arabic", - "isbn": "978-1234567890", - "volume": "9 volumes", - "year_of_publication": "846", - "number_page": 4200, - "rate": 5.0, - "created_at": "2024-01-01T00:00:00Z", - "updated_at": "2024-01-01T00:00:00Z", + "id": 2, + "title": "Sahih Muslim", + "rate": "4.80", + 'isbn':"13135487d", + 'language':'arabic', + 'number_page':1000, + "publisher": "Dar-us-Salam Publications", + "description": "One of the six major collections of Sunni hadith, recognized as authentic.", + "volume": "7 Volumes", + "slug": "sahih-muslim", + "attribute": [ + { + "id": 10, + "title": "ISBN", + "value": "978-1234567890" + }, + { + "id": 11, + "title": "Pages", + "value": "3000" + }, + { + "id": 12, + "title": "Language", + "value": "Arabic/English" + } + ], "author": [ { - "id": 1, - "name": "Muhammad ibn Isma'il al-Bukhari", - "created_at": "2024-01-01T00:00:00Z", - "updated_at": "2024-01-01T00:00:00Z" + "id": 55, + "name": "Muslim ibn al-Hajjaj" } ], "image": [ { - "id": 1, - "image": "http://example.com/media/books/bukhari_cover.jpg", - "order": 1, - "description": "Front cover of Sahih al-Bukhari", - "created_at": "2024-01-01T00:00:00Z" - }, - { - "id": 2, - "image": "http://example.com/media/books/bukhari_title_page.jpg", - "order": 2, - "description": "Title page of Sahih al-Bukhari", - "created_at": "2024-01-01T00:00:00Z" + "id": 5, + "image": "http://api.site.com/media/books/muslim_cover.jpg", + "description": "Front Cover", + "order": 1 } ], "hadis": [ { - "id": 1, - "number": 1, - "title": "The Opening", - "title_narrator": "From Abu Hurairah", - "text": "إنما الأعمال بالنيات وإنما لكل امرئ ما نوى", - "translation": "Actions are but by intention, and every person will have what they intended", - "category": { - "id": 1, - "title": "Book of Faith", - "slug": "book-of-faith", - "source_type": "hadith", - "sect_type": "sunni" - }, - "share_link": "http://example.com/hadis/1" - }, - { - "id": 2, - "number": 2, - "title": "The Second Hadith", - "title_narrator": "From Umar ibn al-Khattab", - "text": "بينما نحن عند رسول الله صلى الله عليه وسلم ذات يوم", - "translation": "While we were sitting with the Messenger of Allah (peace be upon him) one day", + "id": 105, + "title": "Hadith on Intention", + "title_narrator": "Umar ibn Khattab", + "text": "Actions are by intentions...", + "translation": "Actions are but by intentions...", + "share_link": "http://site.com/hadis/105", + "explanation": "This hadith signifies the importance of niyyah.", "category": { - "id": 1, - "title": "Book of Faith", - "slug": "book-of-faith", - "source_type": "hadith", - "sect_type": "sunni" - }, - "share_link": "http://example.com/hadis/2" + "id": 50, + "title": "Book of Faith" + } } - ], - "volume_count": 9 + ] } } ), status.HTTP_404_NOT_FOUND: openapi.Response( - description="The specified book reference ID does not exist", - examples={ - "application/json": { - "detail": "Not found." - } - } - ), - status.HTTP_500_INTERNAL_SERVER_ERROR: openapi.Response( - description="Internal server error occurred while processing the request" + description="Book reference not found" ) } ) @@ -1504,32 +1547,31 @@ categories_list_swagger = swagger_auto_schema( # Swagger documentation for CategoriesBySectView categories_by_sect_swagger = swagger_auto_schema( operation_description=""" - Retrieve a list of Hadis categories filtered by Islamic sect type. + Retrieve a paginated list of Hadis categories filtered by Islamic sect type. **Key Features:** - Filters categories by sect type (shia or sunni) - Returns all categories belonging to the specified sect - - Includes hierarchical information and hadis counts + - Includes hierarchical information (`children_count`) and hadis counts - Useful for building sect-specific category navigation **Usage:** - - Use this endpoint to get categories for a specific Islamic sect + - Use this endpoint to get root categories for a specific Islamic sect - The `sect_type` parameter must be either 'shia' or 'sunni' - - Results are ordered by the category's order field - - Use the `children_count` to build hierarchical navigation + - Use `next` and `previous` fields for traversing pages **Response Structure:** - - Returns an array of category objects - - Each category includes sect information, source type, and hierarchical data + - Returns a paginated object containing `count` and a `results` array - The `has_hadis` field indicates if the category can be used to fetch hadis directly """, operation_summary="List Categories by Sect", + operation_id="listCategoriesBySect", tags=['Hadis'], manual_parameters=[ openapi.Parameter( 'sect_type', openapi.IN_PATH, - description="Type of Islamic sect. Must be either 'shia' (Shi'a Islam) or 'sunni' (Sunni Islam). This filters categories to only those belonging to the specified sect.", + description="Type of Islamic sect. Must be either 'shia' or 'sunni'.", type=openapi.TYPE_STRING, enum=['shia', 'sunni'], required=True, @@ -1538,63 +1580,64 @@ categories_by_sect_swagger = swagger_auto_schema( ], responses={ status.HTTP_200_OK: openapi.Response( - description="Successfully retrieved list of categories for the specified sect type", + description="Paginated list of categories for the specified sect", examples={ - "application/json": [ - { - "id": 1, - "title": "Book of Faith", - "sect_id": 1, - "sect_type": "sunni", - "source_type": "hadith", - "description": "Hadiths related to Islamic faith and beliefs, including articles of faith, belief in Allah, angels, books, messengers, and the Last Day", - "slug": "book-of-faith", - "children_count": 3, - "has_hadis": False, - "hadis_count": 0 - }, - { - "id": 2, - "title": "Book of Prayer", - "sect_id": 1, - "sect_type": "sunni", - "source_type": "hadith", - "description": "Hadiths about salah (prayer) and related rulings, including prayer times, conditions, and etiquettes", - "slug": "book-of-prayer", - "children_count": 0, - "has_hadis": True, - "hadis_count": 45 - }, - { - "id": 3, - "title": "Book of Zakat", - "sect_id": 1, - "sect_type": "sunni", - "source_type": "hadith", - "description": "Hadiths about zakat (obligatory charity) and its rulings", - "slug": "book-of-zakat", - "children_count": 2, - "has_hadis": False, - "hadis_count": 0 - } - ] + "application/json": { + "count": 28, + "next": "http://api.site.com/api/hadis/categories/sunni/?limit=16&offset=16", + "previous": None, + "results": [ + { + "id": 352, + "title": "Толкование Корана", + "sect_id": 21, + "sect_type": "sunni", + "source_type": "quran", + "description": None, + "slug": "-13", + "children_count": 3, + "has_hadis": False, + "hadis_count": 0 + }, + { + "id": 358, + "title": "Толкование суры Аль-Фатиха", + "sect_id": 21, + "sect_type": "sunni", + "source_type": "quran", + "description": None, + "slug": "-12", + "children_count": 0, + "has_hadis": False, + "hadis_count": 0 + }, + { + "id": 366, + "title": "Книга очищения", + "sect_id": 21, + "sect_type": "sunni", + "source_type": "hadith", + "description": None, + "slug": "-10", + "children_count": 3, + "has_hadis": False, + "hadis_count": 0 + } + ] + } } ), status.HTTP_400_BAD_REQUEST: openapi.Response( - description="Invalid sect_type parameter. Must be 'shia' or 'sunni'", + description="Invalid parameters", examples={ "application/json": { "detail": "Invalid sect type. Must be 'shia' or 'sunni'." } } - ), - status.HTTP_500_INTERNAL_SERVER_ERROR: openapi.Response( - description="Internal server error occurred while processing the request" ) } ) - # Swagger documentation for HadisCategoryTreeBySectView categories_tree_by_sect_swagger = swagger_auto_schema( operation_description=""" @@ -1645,7 +1688,34 @@ categories_tree_by_sect_swagger = swagger_auto_schema( status.HTTP_200_OK: openapi.Response( description="Successfully retrieved list of child categories for the specified parent category", examples={ - "application/json": [ + "application/json": { + "count": 3, + "next": None, + "previous": None, + "results": [ + { + "id": 330, + "title": "Tafsir of Surah Al-Fatiha", + "source_type": "quran", + "slug": "tafsir-al-fatiha", + "sect_id": 20, + "sect_type": "shia", + "children_count": 0, + "has_hadis": True, + "hadis_count": 15 + }, + { + "id": 330, + "title": "Tafsir of Surah Al-Fatiha", + "source_type": "quran", + "slug": "tafsir-al-fatiha", + "sect_id": 20, + "sect_type": "shia", + "children_count": 0, + "has_hadis": True, + "hadis_count": 15 + }, + { "id": 330, "title": "Tafsir of Surah Al-Fatiha", @@ -1679,8 +1749,11 @@ categories_tree_by_sect_swagger = swagger_auto_schema( "has_hadis": True, "hadis_count": 22 } - ] + + ] + } } + ), status.HTTP_404_NOT_FOUND: openapi.Response( description="Parent category with the specified slug not found in the given sect type", @@ -1743,7 +1816,7 @@ categories_tree_by_sect_source_swagger = swagger_auto_schema( openapi.Parameter( 'slug', openapi.IN_PATH, - description="URL-friendly slug identifier of the parent category. Used to locate the parent category whose children you want to retrieve.", + description="URL-friendly slug identifier of the parent category.", type=openapi.TYPE_STRING, required=True, example='quran-commentary' @@ -1751,7 +1824,7 @@ categories_tree_by_sect_source_swagger = swagger_auto_schema( openapi.Parameter( 'source_type', openapi.IN_PATH, - description="Type of source material. Filters children to only those matching this source type. Options: 'quran' (Quranic commentary), 'hadith' (Prophetic traditions), 'history' (Historical accounts), 'fatwa' (Legal opinions), 'quote' (Quotations).", + description="Type of source material. Filters children to only those matching this source type.", type=openapi.TYPE_STRING, enum=['quran', 'hadith', 'history', 'fatwa', 'quote'], required=True, @@ -1760,47 +1833,52 @@ categories_tree_by_sect_source_swagger = swagger_auto_schema( ], responses={ status.HTTP_200_OK: openapi.Response( - description="Successfully retrieved list of child categories filtered by sect type and source type", + description="Paginated list of child categories filtered by sect type and source type", examples={ - "application/json": [ - { - "id": 330, - "title": "Tafsir of Surah Al-Fatiha", - "source_type": "quran", - "slug": "tafsir-al-fatiha", - "sect_id": 20, - "sect_type": "shia", - "children_count": 0, - "has_hadis": True, - "hadis_count": 15 - }, - { - "id": 331, - "title": "Tafsir of Surah Al-Baqarah", - "source_type": "quran", - "slug": "tafsir-al-baqarah", - "sect_id": 20, - "sect_type": "shia", - "children_count": 0, - "has_hadis": True, - "hadis_count": 28 - }, - { - "id": 332, - "title": "Tafsir of Surah Al-Imran", - "source_type": "quran", - "slug": "tafsir-al-imran", - "sect_id": 20, - "sect_type": "shia", - "children_count": 0, - "has_hadis": True, - "hadis_count": 22 - } - ] + "application/json": { + "count": 3, + "next": None, + "previous": None, + "results": [ + { + "id": 330, + "title": "Толкование суры Аль-Фатиха", + "source_type": "quran", + "slug": "", + "sect_id": 20, + "sect_type": "shia", + "children_count": 0, + "has_hadis": True, + "hadis_count": 10 + }, + { + "id": 331, + "title": "Толкование суры Аль-Бакара", + "source_type": "quran", + "slug": "-16", + "sect_id": 20, + "sect_type": "shia", + "children_count": 0, + "has_hadis": True, + "hadis_count": 10 + }, + { + "id": 332, + "title": "Толкование суры Аль Имран", + "source_type": "quran", + "slug": "-32", + "sect_id": 20, + "sect_type": "shia", + "children_count": 0, + "has_hadis": True, + "hadis_count": 10 + } + ] + } } ), status.HTTP_404_NOT_FOUND: openapi.Response( - description="Parent category with the specified slug not found, or no children exist with the specified source type", + description="Parent category not found or no children exist", examples={ "application/json": { "detail": "No categories found matching the specified criteria." diff --git a/apps/hadis/migrations/0038_narratorlayer_slug_alter_referenceimage_reference.py b/apps/hadis/migrations/0038_narratorlayer_slug_alter_referenceimage_reference.py new file mode 100644 index 0000000..3ac0326 --- /dev/null +++ b/apps/hadis/migrations/0038_narratorlayer_slug_alter_referenceimage_reference.py @@ -0,0 +1,30 @@ +# Generated by Django 5.2.9 on 2025-12-16 10:14 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0037_remove_bookattribute_book_references_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="narratorlayer", + name="slug", + field=models.SlugField( + blank=True, max_length=255, null=True, verbose_name="slug" + ), + ), + migrations.AlterField( + model_name="referenceimage", + name="reference", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="images", + to="hadis.hadisreference", + verbose_name="Hadis Reference", + ), + ), + ] diff --git a/apps/hadis/migrations/0039_alter_narratorlayer_slug.py b/apps/hadis/migrations/0039_alter_narratorlayer_slug.py new file mode 100644 index 0000000..e88c8a8 --- /dev/null +++ b/apps/hadis/migrations/0039_alter_narratorlayer_slug.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.9 on 2025-12-16 10:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0038_narratorlayer_slug_alter_referenceimage_reference"), + ] + + operations = [ + migrations.AlterField( + model_name="narratorlayer", + name="slug", + field=models.SlugField( + blank=True, max_length=255, unique=True, verbose_name="slug" + ), + ), + ] diff --git a/apps/hadis/migrations/0040_bookreference_slug.py b/apps/hadis/migrations/0040_bookreference_slug.py new file mode 100644 index 0000000..628da2c --- /dev/null +++ b/apps/hadis/migrations/0040_bookreference_slug.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.9 on 2025-12-16 12:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0039_alter_narratorlayer_slug"), + ] + + operations = [ + migrations.AddField( + model_name="bookreference", + name="slug", + field=models.SlugField( + blank=True, max_length=255, null=True, verbose_name="slug" + ), + ), + ] diff --git a/apps/hadis/migrations/0041_remove_bookreference_slug.py b/apps/hadis/migrations/0041_remove_bookreference_slug.py new file mode 100644 index 0000000..b6ef66a --- /dev/null +++ b/apps/hadis/migrations/0041_remove_bookreference_slug.py @@ -0,0 +1,16 @@ +# Generated by Django 5.2.9 on 2025-12-16 12:25 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0040_bookreference_slug"), + ] + + operations = [ + migrations.RemoveField( + model_name="bookreference", + name="slug", + ), + ] diff --git a/apps/hadis/migrations/0042_bookreference_slug.py b/apps/hadis/migrations/0042_bookreference_slug.py new file mode 100644 index 0000000..93806e8 --- /dev/null +++ b/apps/hadis/migrations/0042_bookreference_slug.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.9 on 2025-12-16 12:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0041_remove_bookreference_slug"), + ] + + operations = [ + migrations.AddField( + model_name="bookreference", + name="slug", + field=models.SlugField( + blank=True, max_length=255, unique=True, verbose_name="slug" + ), + ), + ] diff --git a/apps/hadis/migrations/0043_bookreference_publisher.py b/apps/hadis/migrations/0043_bookreference_publisher.py new file mode 100644 index 0000000..074088a --- /dev/null +++ b/apps/hadis/migrations/0043_bookreference_publisher.py @@ -0,0 +1,17 @@ +# Generated by Django 5.2.9 on 2025-12-16 12:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0042_bookreference_slug"), + ] + + operations = [ + migrations.AddField( + model_name="bookreference", + name="publisher", + field=models.TextField(blank=True, null=True, verbose_name="publisher"), + ), + ] diff --git a/apps/hadis/migrations/0044_remove_bookreference_publisher.py b/apps/hadis/migrations/0044_remove_bookreference_publisher.py new file mode 100644 index 0000000..57bc5bc --- /dev/null +++ b/apps/hadis/migrations/0044_remove_bookreference_publisher.py @@ -0,0 +1,16 @@ +# Generated by Django 5.2.9 on 2025-12-16 12:54 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0043_bookreference_publisher"), + ] + + operations = [ + migrations.RemoveField( + model_name="bookreference", + name="publisher", + ), + ] diff --git a/apps/hadis/migrations/0045_bookreference_publisher.py b/apps/hadis/migrations/0045_bookreference_publisher.py new file mode 100644 index 0000000..245210c --- /dev/null +++ b/apps/hadis/migrations/0045_bookreference_publisher.py @@ -0,0 +1,17 @@ +# Generated by Django 5.2.9 on 2025-12-16 12:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0044_remove_bookreference_publisher"), + ] + + operations = [ + migrations.AddField( + model_name="bookreference", + name="publisher", + field=models.TextField(blank=True, null=True, verbose_name="publisher"), + ), + ] diff --git a/apps/hadis/models/reference.py b/apps/hadis/models/reference.py index 893c46b..0fab02e 100644 --- a/apps/hadis/models/reference.py +++ b/apps/hadis/models/reference.py @@ -15,7 +15,8 @@ class BookReference(models.Model): volume = models.CharField(max_length=100, verbose_name=_('volume'), blank=True, null=True) year_of_publication = models.CharField(max_length=50, verbose_name=_('year of publication'), blank=True, null=True) number_page = models.PositiveIntegerField(verbose_name=_('number of pages'), blank=True, null=True) - # publisher = models.TextField(verbose_name=_('publisher'),blank=True,null=True) + slug = models.SlugField(max_length=255, verbose_name=_('slug'), blank=True,unique=True) + publisher = models.TextField(verbose_name=_('publisher'),blank=True,null=True) rate = models.DecimalField( max_digits=3, decimal_places=2, @@ -36,6 +37,16 @@ class BookReference(models.Model): def __str__(self): return self.title + def save(self, *args, **kwargs): + if not self.slug: + base_slug = slugify(self.title, allow_unicode=True) + slug = base_slug + counter = 1 + while BookReference.objects.filter(slug=slug).exclude(pk=self.pk).exists(): + slug = f"{base_slug}-{counter}" + counter += 1 + self.slug = slug + super().save(*args, **kwargs) class BookReferenceImage(models.Model): """ diff --git a/apps/hadis/models/transmitter.py b/apps/hadis/models/transmitter.py index 25e0b21..0226e7e 100644 --- a/apps/hadis/models/transmitter.py +++ b/apps/hadis/models/transmitter.py @@ -3,6 +3,7 @@ from django.db import models from django.utils.translation import gettext_lazy as _ from filer.fields.image import FilerImageField +from django.utils.text import slugify @@ -14,6 +15,7 @@ class NarratorLayer(models.Model): name = models.CharField(max_length=255, verbose_name=_('name')) number = models.PositiveIntegerField(verbose_name=_('layer number'), unique=True) description = models.TextField(verbose_name=_('description'), blank=True, null=True) + slug = models.SlugField(max_length=255,unique=True, verbose_name=_('slug'), blank=True) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at')) @@ -25,6 +27,12 @@ class NarratorLayer(models.Model): def __str__(self): return f"{_('Layer')} {self.number} - {self.name}" + + def save(self, *args, **kwargs): + if not self.slug: + slug = slugify(self.name) + self.slug = slug + super().save(*args, **kwargs) class Transmitters(models.Model): diff --git a/apps/hadis/serializers/hadis.py b/apps/hadis/serializers/hadis.py index bbf1da8..c1d7ee6 100644 --- a/apps/hadis/serializers/hadis.py +++ b/apps/hadis/serializers/hadis.py @@ -144,7 +144,6 @@ class HadisSyncSerializer(serializers.ModelSerializer): }) return corrections_data - class HadisListSerializer(serializers.ModelSerializer): """Serializer for Hadis list""" category = serializers.SerializerMethodField() @@ -199,6 +198,16 @@ class TransmitterSerializer(serializers.ModelSerializer): 'id', 'full_name', 'birth_year_hijri', 'death_year_hijri', "known_as",'nickname','reliability','madhhab','generation' ] + +class TransmitterShortSerializer(serializers.ModelSerializer): + """Serializer for Transmitters""" + + class Meta: + model = Transmitters + fields = [ + 'id', 'full_name', 'birth_year_hijri', 'death_year_hijri', + "known_as",'nickname','reliability' + ] class TransmitterOpinionSerializer(serializers.ModelSerializer): """ Serializer for TransmitterOpinions """ @@ -213,12 +222,6 @@ class TransmitterOriginalTextSerializer(serializers.ModelSerializer): model = TransmitterOriginalText fields = ['id', 'title', 'text', 'translation', 'share_link'] -class HadisTransmitterSerializer(serializers.ModelSerializer): - """ Serializer for HadisTransmitters """ - - class Meta: - model = HadisTransmitter - fields = '__all__' class TransmitterDetailSerializer(serializers.ModelSerializer): """ Serializer for Details of Transmitters """ @@ -304,14 +307,44 @@ class TransmitterSyncSerializer(serializers.ModelSerializer): class HadisTransmitterSerializer(serializers.ModelSerializer): """Serializer for HadisTransmitter with transmitter details""" - transmitter = TransmitterSerializer(read_only=True) - + transmitter = TransmitterShortSerializer(read_only=True) + narrator_layer_description = serializers.SerializerMethodField() + layer = serializers.SerializerMethodField() class Meta: model = HadisTransmitter fields = [ - 'id', 'transmitter', 'order', 'is_gap','narrator_layer' + 'id', 'order', 'is_gap','narrator_layer_description','layer', 'transmitter' ] + def get_narrator_layer_description(self, obj): + """Get narrator layer description""" + return obj.narrator_layer.description + + def get_layer(self, obj): + """Get narrator layer slug""" + return obj.narrator_layer.slug + +# serializers.py + +class HadisTransmitterListSerializer(serializers.ModelSerializer): + """ + The 'Parent' Serializer. + It takes a HADIS object and returns the count + the list of transmitters. + """ + layer_count = serializers.SerializerMethodField() + results = HadisTransmitterSerializer( + source='transmitters', # Access the 'transmitters' reverse relation + many=True, + read_only=True + ) + + class Meta: + model = Hadis + fields = ['id', 'layer_count', 'results'] + + def get_layer_count(self, obj): + # Calculate distinct layers efficiently + return obj.transmitters.values('narrator_layer').distinct().count() class ReferenceImageSerializer(serializers.ModelSerializer): """Serializer for ReferenceImage""" diff --git a/apps/hadis/serializers/reference.py b/apps/hadis/serializers/reference.py index c8033b6..b96e76a 100644 --- a/apps/hadis/serializers/reference.py +++ b/apps/hadis/serializers/reference.py @@ -6,12 +6,12 @@ from ..models import BookReference , BookAuthor , BookReferenceImage, HadisRefer class BookAuthorSerializer(serializers.ModelSerializer): class Meta: model = BookAuthor - fields = '__all__' + fields = ['id','name'] class BookReferenceImageSerializer(serializers.ModelSerializer): class Meta: model = BookReferenceImage - fields = '__all__' + fields = ['id','image','description','order'] class BookReferenceSerializer(serializers.ModelSerializer): image = BookReferenceImageSerializer( @@ -24,63 +24,80 @@ class BookReferenceSerializer(serializers.ModelSerializer): model = BookReference fields = ['id','title','rate','author','description','image','volume'] def get_author (self,obj): - author = obj.bookauthor_set - return author.name + author = obj.authors.all() + return [ + { + 'id': author.id, + 'name': author.name + } + for author in author + ] class BookAttributeSerializer(serializers.ModelSerializer): class Meta: model = BookAttribute - fields = ['id', 'title', 'value', 'book_reference', 'created_at', 'updated_at'] - read_only_fields = ['id', 'created_at', 'updated_at'] + fields = ['id', 'title', 'value','book_reference'] class BookDetailSerializer(serializers.ModelSerializer): attribute = BookAttributeSerializer( + many=True, read_only = True, - source = 'bookattribute_set' + source = 'attributes' + ) + author= BookAuthorSerializer( + many=True, + read_only=True, + source='authors' ) - - author = BookAuthorSerializer( - read_only = True , - source = 'bookauthor_set' - ) image = BookReferenceImageSerializer( - many= True , - read_only = True , - source = 'bookreference_set' - ) + many=True, + read_only=True, + source='images' + ) hadis = HadisListSerializer( many=True, read_only=True, - source='hadisreference_set' + source='hadis_references' ) class Meta: model = BookReference - fields = '__all__' - - # def create(self , validated_data): - # author = validated_data.pop('author') - # book = BookReference.objects.create(**validated_data) - # for author in author + fields = ['id','title','rate','isbn','language','number_page','publisher','description','volume','slug','attribute','author','image','hadis'] class BookReferenceSyncSerializer(serializers.ModelSerializer): """Serializer for syncing all book reference data for offline mode""" + attribute = BookAttributeSerializer( + many=True, + read_only = True, + source = 'attributes' + ) + author= BookAuthorSerializer( + many=True, + read_only=True, + source='authors' + ) + image = BookReferenceImageSerializer( + many=True, + read_only=True, + source='images' + ) + # Basic information detail = serializers.SerializerMethodField() # Hadis group (related hadises) hadises = serializers.SerializerMethodField() - authors = serializers.SerializerMethodField() + # authors = serializers.SerializerMethodField() class Meta: model = BookReference fields = [ - 'id', 'title','rate' , 'authors' ,'detail', 'hadises' + 'id', 'title','rate' , 'author' ,'detail','image','attribute', 'hadises' ] def get_authors(self,obj): authors = [] @@ -102,6 +119,7 @@ class BookReferenceSyncSerializer(serializers.ModelSerializer): 'volume': obj.volume, 'language': obj.language, 'isbn': obj.isbn, + 'number_page':obj.number_page, 'year_of_publication': obj.year_of_publication, 'number_of_pages': obj.number_page, 'volume_info': obj.volume, diff --git a/apps/hadis/views/hadis.py b/apps/hadis/views/hadis.py index 3535ee6..a123c9a 100644 --- a/apps/hadis/views/hadis.py +++ b/apps/hadis/views/hadis.py @@ -2,9 +2,11 @@ from rest_framework.generics import ListAPIView, RetrieveAPIView from django.shortcuts import get_object_or_404 from utils.pagination import NoPagination from rest_framework.response import Response +from django.db.models import Count +from django.db.models import Prefetch -from ..models import HadisCategory, Hadis, HadisCollection -from ..serializers import HadisListSerializer, HadisBasicSerializer, HadisDetailSerializer, HadisCollectionListSerializer, HadisSyncSerializer +from ..models import HadisCategory, Hadis, HadisCollection,HadisTransmitter +from ..serializers import HadisListSerializer, HadisBasicSerializer, HadisDetailSerializer, HadisCollectionListSerializer, HadisSyncSerializer,HadisTransmitterSerializer,HadisTransmitterListSerializer from ..docs import hadis_list_swagger, hadis_detail_swagger, hadis_collections_swagger, hadis_sync_swagger, hadis_transmitters_swagger, hadis_corrections_swagger, hadis_basic_swagger @@ -73,7 +75,12 @@ class HadisListView(ListAPIView): return Hadis.objects.filter( category_id=category_id, status=True - ).order_by('number') + ).order_by('number').annotate( + # distinct=True is CRITICAL here. + # Without it, if 3 narrators are from "Layer 1", it counts as 3. + # With it, it counts as 1 (unique layer). + layer_count=Count('transmitters__narrator_layer', distinct=True) + ) class HadisBasicView(RetrieveAPIView): @@ -116,48 +123,39 @@ class HadisDetailView(RetrieveAPIView): 'references__book_reference__description', ) - class HadisTransmittersView(RetrieveAPIView): """ - API view to retrieve transmitters for a specific hadis + Fetches a single Hadis but filters the nested Transmitters list + if a ?layer=slug param is provided. """ - serializer_class = HadisDetailSerializer - lookup_field = 'id' + serializer_class = HadisTransmitterListSerializer lookup_url_kwarg = 'hadis_id' @hadis_transmitters_swagger def get(self, request, *args, **kwargs): - hadis = self.get_object() - transmitters_data = [] - - for transmitter_rel in hadis.transmitters.all().order_by('order'): - transmitter_info = { - 'id': transmitter_rel.id, - 'order': transmitter_rel.order, - 'is_gap': transmitter_rel.is_gap, - 'narrator_layer': transmitter_rel.narrator_layer, - 'transmitter': { - 'id': transmitter_rel.transmitter.id, - 'full_name': transmitter_rel.transmitter.full_name, - 'birth_year_hijri': transmitter_rel.transmitter.birth_year_hijri, - 'death_year_hijri': transmitter_rel.transmitter.death_year_hijri, - 'madhhab': transmitter_rel.transmitter.madhhab, - 'description': transmitter_rel.transmitter.description, - 'reliability': transmitter_rel.transmitter.reliability - } - } - transmitters_data.append(transmitter_info) - - return Response({ - 'hadis_id': hadis.id, - 'hadis_description': getattr(hadis, 'description', None), - 'transmitters_count': len(transmitters_data), - 'transmitters': transmitters_data - }) + return self.retrieve(request, *args, **kwargs) def get_queryset(self): - return Hadis.objects.filter(status=True).prefetch_related('transmitters__transmitter') - + # 1. Get the filter param + layer_slug = self.request.query_params.get('layer') + + # 2. Build the query for the "Child" (Transmitters) + # We start with the base optimization (select_related) + transmitter_qs = HadisTransmitter.objects.select_related( + 'transmitter', + 'narrator_layer' + ).order_by('order') + + # 3. Apply the filter to the Child Query (if param exists) + if layer_slug: + # Assumes 'NarratorLayer' has a 'slug' field. + # If not, use 'narrator_layer__name' or 'narrator_layer__id'. + transmitter_qs = transmitter_qs.filter(narrator_layer__slug=layer_slug) + + # 4. Use the Prefetch object to inject this filtered list into the Parent + return Hadis.objects.filter(status=True).prefetch_related( + Prefetch('transmitters', queryset=transmitter_qs) + ) class HadisCorrectionsView(RetrieveAPIView): """ diff --git a/apps/hadis/views/reference.py b/apps/hadis/views/reference.py index 107e918..d545232 100644 --- a/apps/hadis/views/reference.py +++ b/apps/hadis/views/reference.py @@ -35,8 +35,8 @@ class BookDetailView(RetrieveAPIView): def get_queryset(self): return BookReference.objects.filter(id = self.kwargs['bookreference_id']).prefetch_related( - 'bookauthor_set__name', - 'bookreferenceimage_set__image', + 'authors__name', + 'images__image', ) @@ -58,22 +58,22 @@ class BookReferenceSyncView(ListAPIView): 'hadis_references__hadis' ).order_by('id') - def list(self, request, *args, **kwargs): - queryset = self.get_queryset() - serializer = self.get_serializer(queryset, many=True, context={'request': request}) + # def list(self, request, *args, **kwargs): + # queryset = self.get_queryset() + # serializer = self.get_serializer(queryset, many=True, context={'request': request}) - # Group book references by ID for easy lookup - grouped_data = {} - for book_data in serializer.data: - book_id = str(book_data['id']) - grouped_data[book_id] = book_data + # # Group book references by ID for easy lookup + # grouped_data = {} + # for book_data in serializer.data: + # book_id = str(book_data['id']) + # grouped_data[book_id] = book_data - response_data = { - 'count': queryset.count(), - 'results': grouped_data - } + # response_data = { + # 'count': queryset.count(), + # 'results': grouped_data + # } - return Response(response_data) + # return Response(response_data) @@ -84,16 +84,3 @@ class BookAttributeView(ListCreateAPIView): queryset = BookAttribute.objects.all() serializer_class = BookAttributeSerializer - # def get_queryset(self): - # """ - # Optionally filter by book_reference_id if provided as query parameter - # """ - # queryset = BookAttribute.objects.all() - # book_reference_id = self.request.query_params.get('book_reference_id', None) - # if book_reference_id: - # queryset = queryset.filter(book_references__id=book_reference_id).distinct() - # return queryset.order_by('title') - - -# class BookReferencesView(ListAPIView): -# pass \ No newline at end of file diff --git a/seeds/images/book1.png b/seeds/images/book1.png new file mode 100644 index 0000000..e1d8a8b Binary files /dev/null and b/seeds/images/book1.png differ diff --git a/seeds/images/book2.png b/seeds/images/book2.png new file mode 100644 index 0000000..508c035 Binary files /dev/null and b/seeds/images/book2.png differ diff --git a/seeds/images/book3.png b/seeds/images/book3.png new file mode 100644 index 0000000..1817624 Binary files /dev/null and b/seeds/images/book3.png differ diff --git a/seeds/images/book4.png b/seeds/images/book4.png new file mode 100644 index 0000000..f015699 Binary files /dev/null and b/seeds/images/book4.png differ diff --git a/seeds/images/book5.png b/seeds/images/book5.png new file mode 100644 index 0000000..e1d8a8b Binary files /dev/null and b/seeds/images/book5.png differ