diff --git a/apps/hadis/docs.py b/apps/hadis/docs.py index e27d753..f617f1b 100644 --- a/apps/hadis/docs.py +++ b/apps/hadis/docs.py @@ -80,6 +80,7 @@ hadis_category_tree_swagger = swagger_auto_schema( "order": 1, "xmind_file": "http://example.com/media/xmind/tafsir.xmind", "has_xmind_file": True, + "children_count": 2, "children": [ { "id": 2, @@ -89,6 +90,7 @@ hadis_category_tree_swagger = swagger_auto_schema( "order": 1, "xmind_file": None, "has_xmind_file": False, + "children_count": 0, "children": [] }, { @@ -99,6 +101,7 @@ hadis_category_tree_swagger = swagger_auto_schema( "order": 2, "xmind_file": "http://example.com/media/xmind/baqarah.xmind", "has_xmind_file": True, + "children_count": 0, "children": [] } ] @@ -113,6 +116,7 @@ hadis_category_tree_swagger = swagger_auto_schema( "order": 1, "xmind_file": "http://example.com/media/xmind/bukhari.xmind", "has_xmind_file": True, + "children_count": 2, "children": [ { "id": 5, @@ -122,6 +126,7 @@ hadis_category_tree_swagger = swagger_auto_schema( "order": 1, "xmind_file": None, "has_xmind_file": False, + "children_count": 0, "children": [] }, { @@ -132,6 +137,7 @@ hadis_category_tree_swagger = swagger_auto_schema( "order": 2, "xmind_file": "http://example.com/media/xmind/prayer.xmind", "has_xmind_file": True, + "children_count": 0, "children": [] } ] @@ -339,3 +345,525 @@ hadis_detail_swagger = swagger_auto_schema( 404: openapi.Response(description="Hadis not found") } ) + + +# 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_summary="List Transmitters", + tags=['Hadis'], + manual_parameters=[ + openapi.Parameter( + 'status', + openapi.IN_QUERY, + description='Filter by reliability status (very_reliable, reliable, acceptable, weak, very_weak, unknown)', + 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)', + type=openapi.TYPE_STRING, + required=False + ), + openapi.Parameter( + 'generation', + openapi.IN_QUERY, + description='Filter by generation (1-10 representing narrator layers/classes)', + type=openapi.TYPE_INTEGER, + required=False + ), + ], + responses={ + status.HTTP_200_OK: openapi.Response( + description="List of transmitters with optional filtering applied", + 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" + } + ] + } + ), + status.HTTP_500_INTERNAL_SERVER_ERROR: openapi.Response( + description="Internal server error" + ) + } +) + + +# Swagger documentation for TransmitterDetailView +transmitter_detail_swagger = swagger_auto_schema( + operation_description="Get detailed information about a specific transmitter including their scholarly opinions and hadith transmissions", + operation_summary="Get Transmitter Details", + tags=['Hadis'], + manual_parameters=[ + openapi.Parameter( + 'transmitters_id', + openapi.IN_PATH, + description="ID of the transmitter", + type=openapi.TYPE_INTEGER, + required=True + ) + ], + responses={ + status.HTTP_200_OK: openapi.Response( + description="Detailed transmitter information with nested opinions and transmissions", + examples={ + "application/json": { + "id": 1, + "full_name": "Abu Daud Sulaiman ibn al-Ash'ath al-Azdi al-Sijistani", + "kunya": "Abu Daud", + "known_as": "Imam Abu Daud", + "nickname": "Al-Sijistani", + "origin": "Sijistan (modern Sistan)", + "lived_in": "Basra, Baghdad", + "died_in": "Basra", + "birth_year_hijri": 202, + "death_year_hijri": 275, + "age_at_death": 73, + "reliability": "very_reliable", + "madhhab": "sunni", + "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", + "opinions": [ + { + "id": 1, + "scholar_name": "Ibn Hajar al-Asqalani", + "opinion_text": "Abu Daud was a reliable and trustworthy narrator", + "status": "confirmed", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" + } + ], + "hadis_transmissions": [ + { + "id": 1, + "hadis": { + "id": 1001, + "number": 1, + "title": "The first hadith", + "category": { + "id": 1, + "title": "Book of Faith" + }, + "translation": "Actions are by intentions" + }, + "order": 1, + "status": "reliable", + "narrator_layer": 1, + "created_at": "2024-01-01T00:00:00Z" + } + ] + } + } + ), + status.HTTP_404_NOT_FOUND: openapi.Response( + description="Transmitter not found" + ), + status.HTTP_500_INTERNAL_SERVER_ERROR: openapi.Response( + description="Internal server error" + ) + } +) + + +# Swagger documentation for BookReferencesView +book_references_list_swagger = swagger_auto_schema( + operation_description="Get list of all book references used in hadith studies including their metadata, images, and volume counts", + operation_summary="List Book References", + tags=['Hadis'], + responses={ + status.HTTP_200_OK: openapi.Response( + description="List of all book references", + examples={ + "application/json": [ + { + "id": 1, + "title": "Sahih al-Bukhari", + "description": "The most authentic collection of hadith compiled by Imam Bukhari", + "rate": 5.0, + "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" + } + ], + "volume_count": 9 + }, + { + "id": 2, + "title": "Sahih Muslim", + "description": "The second most authentic collection of hadith compiled by Imam Muslim", + "rate": 4.9, + "image": [], + "volume_count": 7 + } + ] + } + ), + status.HTTP_500_INTERNAL_SERVER_ERROR: openapi.Response( + description="Internal server error" + ) + } +) + + +# 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", + operation_summary="List Book Authors", + tags=['Hadis'], + responses={ + status.HTTP_200_OK: openapi.Response( + description="List of all book authors", + examples={ + "application/json": [ + { + "id": 1, + "name": "Muhammad ibn Isma'il al-Bukhari", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" + }, + { + "id": 2, + "name": "Muslim ibn al-Hajjaj al-Qushayri", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" + } + ] + } + ), + status.HTTP_500_INTERNAL_SERVER_ERROR: openapi.Response( + description="Internal server error" + ) + } +) + + +# Swagger documentation for BookDetailView +book_detail_swagger = swagger_auto_schema( + operation_description="Get detailed information about a specific book reference including authors, images, and related hadiths", + operation_summary="Get Book Reference Details", + tags=['Hadis'], + manual_parameters=[ + openapi.Parameter( + 'bookreference_id', + openapi.IN_PATH, + description="ID of the book reference", + type=openapi.TYPE_INTEGER, + required=True + ) + ], + responses={ + status.HTTP_200_OK: openapi.Response( + description="Detailed book reference information", + examples={ + "application/json": { + "id": 1, + "title": "Sahih al-Bukhari", + "description": "The most authentic collection of hadith compiled by Imam Bukhari", + "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", + "author": [ + { + "id": 1, + "name": "Muhammad ibn Isma'il al-Bukhari", + "created_at": "2024-01-01T00:00:00Z", + "updated_at": "2024-01-01T00:00:00Z" + } + ], + "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" + } + ], + "hadis": [ + { + "id": 1, + "number": 1, + "title": "The first hadith about faith", + "category": { + "id": 1, + "title": "Book of Faith" + }, + "translation": "Actions are by intentions" + } + ] + } + } + ), + status.HTTP_404_NOT_FOUND: openapi.Response( + description="Book reference not found" + ), + status.HTTP_500_INTERNAL_SERVER_ERROR: openapi.Response( + description="Internal server error" + ) + } +) + + +# Swagger documentation for CategoriesView +categories_list_swagger = swagger_auto_schema( + operation_description="Get list of all hadith categories with their associated sect information", + operation_summary="List All Categories", + tags=['Hadis'], + responses={ + status.HTTP_200_OK: openapi.Response( + description="List of all hadith categories", + 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", + "order": 1, + "slug": "book-of-faith", + "xmind_file": "http://example.com/media/xmind/faith.xmind", + "children_count": 3 + }, + { + "id": 2, + "title": "Book of Prayer", + "sect_id": 1, + "sect_type": "sunni", + "source_type": "hadith", + "description": "Hadiths about salah (prayer) and related rulings", + "order": 2, + "slug": "book-of-prayer", + "xmind_file": None, + "children_count": 0 + } + ] + } + ), + status.HTTP_500_INTERNAL_SERVER_ERROR: openapi.Response( + description="Internal server error" + ) + } +) + + +# Swagger documentation for CategoriesBySectView +categories_by_sect_swagger = swagger_auto_schema( + operation_description="Get list of hadith categories filtered by sect type (shia or sunni)", + operation_summary="List Categories by Sect", + tags=['Hadis'], + manual_parameters=[ + openapi.Parameter( + 'sect_type', + openapi.IN_PATH, + description="Type of Islamic sect (shia or sunni)", + type=openapi.TYPE_STRING, + enum=['shia', 'sunni'], + required=True + ) + ], + responses={ + status.HTTP_200_OK: openapi.Response( + description="List of hadith categories filtered by sect type", + 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", + "order": 1, + "slug": "book-of-faith", + "xmind_file": "http://example.com/media/xmind/faith.xmind", + "children_count": 3 + }, + { + "id": 2, + "title": "Book of Prayer", + "sect_id": 1, + "sect_type": "sunni", + "source_type": "hadith", + "description": "Hadiths about salah (prayer) and related rulings", + "order": 2, + "slug": "book-of-prayer", + "xmind_file": None, + "children_count": 0 + } + ] + } + ), + status.HTTP_500_INTERNAL_SERVER_ERROR: openapi.Response( + description="Internal server error" + ) + } +) + + +# Swagger documentation for HadisCategoryTreeBySectView +categories_tree_by_sect_swagger = swagger_auto_schema( + operation_description="Retrieve child categories of a specific parent category (identified by slug) within a given Islamic sect type", + operation_summary="Get Category Children by Sect and Slug", + operation_id="getCategoryChildrenBySectAndSlug", + tags=['Hadis'], + manual_parameters=[ + openapi.Parameter( + 'sect_type', + openapi.IN_PATH, + description="Type of Islamic sect (shia or sunni)", + type=openapi.TYPE_STRING, + enum=['shia', 'sunni'], + required=True + ), + openapi.Parameter( + 'slug', + openapi.IN_PATH, + description="URL slug of the parent category", + type=openapi.TYPE_STRING, + required=True + ) + ], + responses={ + status.HTTP_200_OK: openapi.Response( + description="List of child categories belonging to the specified parent category (identified by slug) within the given sect type", + examples={ + "application/json": [ + { + "id": 330, + "title": "Толкование суры Аль-Фатиха", + "source_type": "quran", + "slug": "-1", + "sect_id": 20, + "sect_type": "shia", + "children_count": 0 + }, + { + "id": 331, + "title": "Толкование суры Аль-Бакара", + "source_type": "quran", + "slug": "-19", + "sect_id": 20, + "sect_type": "shia", + "children_count": 0 + }, + { + "id": 332, + "title": "Толкование суры Аль Имран", + "source_type": "quran", + "slug": "-33", + "sect_id": 20, + "sect_type": "shia", + "children_count": 0 + } + ] + } + ), + status.HTTP_500_INTERNAL_SERVER_ERROR: openapi.Response( + description="Internal server error" + ) + } +) + + +# Swagger documentation for HadisCategoryTreeBySectSourceView +categories_tree_by_sect_source_swagger = swagger_auto_schema( + operation_description="Retrieve child categories of a specific parent category (identified by slug) within a given Islamic sect type, additionally filtered by source material type", + operation_summary="Get Category Children by Sect, Slug and Source", + operation_id="getCategoryChildrenBySectSlugAndSource", + tags=['Hadis'], + manual_parameters=[ + openapi.Parameter( + 'sect_type', + openapi.IN_PATH, + description="Type of Islamic sect (shia or sunni)", + type=openapi.TYPE_STRING, + enum=['shia', 'sunni'], + required=True + ), + openapi.Parameter( + 'slug', + openapi.IN_PATH, + description="URL slug of the parent category", + type=openapi.TYPE_STRING, + required=True + ), + openapi.Parameter( + 'source_type', + openapi.IN_PATH, + description="Type of source material (quran, hadith, history, fatwa, quote)", + type=openapi.TYPE_STRING, + enum=['quran', 'hadith', 'history', 'fatwa', 'quote'], + required=True + ) + ], + responses={ + status.HTTP_200_OK: openapi.Response( + description="List of child categories belonging to the specified parent category (identified by slug) within the given sect type, additionally filtered by source type", + examples={ + "application/json": [ + { + "id": 330, + "title": "Толкование суры Аль-Фатиха", + "source_type": "quran", + "slug": "-1", + "sect_id": 20, + "sect_type": "shia", + "children_count": 0, + "has_hadis": False + }, + { + "id": 331, + "title": "Толкование суры Аль-Бакара", + "source_type": "quran", + "slug": "-19", + "sect_id": 20, + "sect_type": "shia", + "children_count": 0, + "has_hadis": False + }, + { + "id": 332, + "title": "Толкование суры Аль Имран", + "source_type": "quran", + "slug": "-33", + "sect_id": 20, + "sect_type": "shia", + "children_count": 0, + "has_hadis": False + } + ] + } + ), + status.HTTP_500_INTERNAL_SERVER_ERROR: openapi.Response( + description="Internal server error" + ) + } +) \ No newline at end of file diff --git a/apps/hadis/migrations/0006_hadiscategory_slug.py b/apps/hadis/migrations/0006_hadiscategory_slug.py new file mode 100644 index 0000000..9520717 --- /dev/null +++ b/apps/hadis/migrations/0006_hadiscategory_slug.py @@ -0,0 +1,17 @@ +# Generated by Django 5.2.9 on 2025-12-11 13:21 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0005_auto_20251209_1620"), + ] + + operations = [ + migrations.AddField( + model_name="hadiscategory", + name="slug", + field=models.SlugField(blank=True, max_length=255, null=True), + ), + ] diff --git a/apps/hadis/migrations/0007_auto_20251211_1313.py b/apps/hadis/migrations/0007_auto_20251211_1313.py new file mode 100644 index 0000000..2d5574c --- /dev/null +++ b/apps/hadis/migrations/0007_auto_20251211_1313.py @@ -0,0 +1,29 @@ +# Generated by Django 5.2.9 on 2025-12-11 13:13 + +from django.db import migrations +from django.utils.text import slugify + +def gen_slugs(apps, schema_editor): + MyModel = apps.get_model('hadis', 'HadisCategory') # Replace with your app/model name + for row in MyModel.objects.all(): + # 1. Basic slugify + base_slug = slugify(row.title) # Assuming you slugify the 'name' field + slug = base_slug + n = 1 + + # 2. Ensure uniqueness (if two rows have the same name) + while MyModel.objects.filter(slug=slug).exists(): + slug = f"{base_slug}-{n}" + n += 1 + + row.slug = slug + row.save() + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0005_auto_20251209_1620"), + ] + + operations = [ + migrations.RunPython(gen_slugs, reverse_code=migrations.RunPython.noop), + ] diff --git a/apps/hadis/migrations/0007_auto_20251211_1324.py b/apps/hadis/migrations/0007_auto_20251211_1324.py new file mode 100644 index 0000000..6171ad5 --- /dev/null +++ b/apps/hadis/migrations/0007_auto_20251211_1324.py @@ -0,0 +1,29 @@ +from django.db import migrations +from django.utils.text import slugify + +def gen_slugs(apps, schema_editor): + MyModel = apps.get_model('hadis', 'HadisCategory') # Replace with your app/model name + for row in MyModel.objects.all(): + # 1. Basic slugify + base_slug = slugify(row.title) # Assuming you slugify the 'name' field + slug = base_slug + n = 1 + + # 2. Ensure uniqueness (if two rows have the same name) + while MyModel.objects.filter(slug=slug).exists(): + slug = f"{base_slug}-{n}" + n += 1 + + row.slug = slug + row.save() + +class Migration(migrations.Migration): + + dependencies = [ + ('hadis', '0006_hadiscategory_slug'), + ] + + operations = [ + # Call the function + migrations.RunPython(gen_slugs, reverse_code=migrations.RunPython.noop), + ] \ No newline at end of file diff --git a/apps/hadis/migrations/0008_alter_hadiscategory_slug.py b/apps/hadis/migrations/0008_alter_hadiscategory_slug.py new file mode 100644 index 0000000..6ec70ff --- /dev/null +++ b/apps/hadis/migrations/0008_alter_hadiscategory_slug.py @@ -0,0 +1,17 @@ +# Generated by Django 5.2.9 on 2025-12-11 13:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0007_auto_20251211_1324"), + ] + + operations = [ + migrations.AlterField( + model_name="hadiscategory", + name="slug", + field=models.SlugField(blank=True, max_length=255, null=True, unique=True), + ), + ] diff --git a/apps/hadis/migrations/0009_alter_hadiscategory_slug.py b/apps/hadis/migrations/0009_alter_hadiscategory_slug.py new file mode 100644 index 0000000..4d2d53e --- /dev/null +++ b/apps/hadis/migrations/0009_alter_hadiscategory_slug.py @@ -0,0 +1,17 @@ +# Generated by Django 5.2.9 on 2025-12-11 13:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0008_alter_hadiscategory_slug"), + ] + + operations = [ + migrations.AlterField( + model_name="hadiscategory", + name="slug", + field=models.SlugField(blank=True, max_length=255, null=True), + ), + ] diff --git a/apps/hadis/migrations/0010_merge_20251211_1555.py b/apps/hadis/migrations/0010_merge_20251211_1555.py new file mode 100644 index 0000000..82d8247 --- /dev/null +++ b/apps/hadis/migrations/0010_merge_20251211_1555.py @@ -0,0 +1,12 @@ +# Generated by Django 5.2.9 on 2025-12-11 15:55 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0007_auto_20251211_1313"), + ("hadis", "0009_alter_hadiscategory_slug"), + ] + + operations = [] diff --git a/apps/hadis/migrations/0011_hadis_a.py b/apps/hadis/migrations/0011_hadis_a.py new file mode 100644 index 0000000..7da9edb --- /dev/null +++ b/apps/hadis/migrations/0011_hadis_a.py @@ -0,0 +1,17 @@ +# Generated by Django 5.2.9 on 2025-12-11 16:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0010_merge_20251211_1555"), + ] + + operations = [ + migrations.AddField( + model_name="hadis", + name="a", + field=models.IntegerField(blank=True, null=True), + ), + ] diff --git a/apps/hadis/migrations/0012_remove_hadis_title_narrator.py b/apps/hadis/migrations/0012_remove_hadis_title_narrator.py new file mode 100644 index 0000000..74dc3e7 --- /dev/null +++ b/apps/hadis/migrations/0012_remove_hadis_title_narrator.py @@ -0,0 +1,16 @@ +# Generated by Django 5.2.9 on 2025-12-11 16:13 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0011_hadis_a"), + ] + + operations = [ + migrations.RemoveField( + model_name="hadis", + name="title_narrator", + ), + ] diff --git a/apps/hadis/migrations/0013_hadis_title_narrator.py b/apps/hadis/migrations/0013_hadis_title_narrator.py new file mode 100644 index 0000000..719b544 --- /dev/null +++ b/apps/hadis/migrations/0013_hadis_title_narrator.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.9 on 2025-12-11 16:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0012_remove_hadis_title_narrator"), + ] + + operations = [ + migrations.AddField( + model_name="hadis", + name="title_narrator", + field=models.CharField( + blank=True, max_length=255, null=True, verbose_name="title narrator" + ), + ), + ] diff --git a/apps/hadis/models/category.py b/apps/hadis/models/category.py index a74f23e..714b436 100644 --- a/apps/hadis/models/category.py +++ b/apps/hadis/models/category.py @@ -2,6 +2,7 @@ from django.db import models from django.utils.translation import gettext_lazy as _ from django.core.exceptions import ValidationError from mptt.models import MPTTModel, TreeForeignKey +from django.utils.text import slugify class HadisSect(models.Model): @@ -40,11 +41,21 @@ class HadisCategory(MPTTModel): description = models.TextField(verbose_name=_('Description'), null=True, blank=True) order = models.IntegerField(default=0, verbose_name=_('order')) xmind_file = models.FileField(upload_to='hadis/xmind_files/', verbose_name=_('xmind file'), null=True, blank=True) - slug = None + slug = models.SlugField(max_length=255, null=True, blank=True) content_type = None language = None language_id = None + def save(self, *args, **kwargs): + if not self.slug: + base_slug = slugify(self.title, allow_unicode=True) + slug = base_slug + while HadisCategory.objects.filter(slug=slug).exclude(pk=self.pk).exists(): + slug = f"{base_slug}" + self.slug = slug + super().save(*args, **kwargs) + + class Meta: verbose_name = _('Hadis Category') diff --git a/apps/hadis/models/hadis.py b/apps/hadis/models/hadis.py index 83babff..cf2d27c 100644 --- a/apps/hadis/models/hadis.py +++ b/apps/hadis/models/hadis.py @@ -100,6 +100,7 @@ class Hadis(models.Model): number = models.PositiveIntegerField(verbose_name=_('number'), default=1) title_narrator = models.CharField(max_length=255, verbose_name=_('title narrator'), null=True, blank=True) title = models.CharField(max_length=255, verbose_name=_('title'), null=True, blank=True) + a =models.IntegerField(blank=True ,null=True) text = models.TextField(verbose_name=_('text')) translation = models.JSONField(verbose_name=_('translation'), default=list) diff --git a/apps/hadis/models/reference.py b/apps/hadis/models/reference.py index 22077d0..f6243dc 100644 --- a/apps/hadis/models/reference.py +++ b/apps/hadis/models/reference.py @@ -1,5 +1,6 @@ from django.db import models from django.utils.translation import gettext_lazy as _ +from django.utils.text import slugify class BookReference(models.Model): @@ -14,6 +15,7 @@ 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) rate = models.DecimalField( max_digits=3, decimal_places=2, diff --git a/apps/hadis/serializers/category.py b/apps/hadis/serializers/category.py index 36f602d..2d3943e 100644 --- a/apps/hadis/serializers/category.py +++ b/apps/hadis/serializers/category.py @@ -130,11 +130,70 @@ class HadisCategoryTreeSerializer(serializers.ModelSerializer): 'children': [] if not filtered_children else [self.to_dict(i) for i in filtered_children], } +class HadisCategorySelectSerializer(serializers.ModelSerializer): + """Serializer for HadisCategory Selection Flow""" + sect_id = serializers.IntegerField(source='sect.id', read_only=True) + sect_type = serializers.CharField(source='sect.sect_type', read_only=True) + # children = serializers.SerializerMethodField() + children_count = serializers.SerializerMethodField() + has_hadis = serializers.SerializerMethodField() + + class Meta: + model = HadisCategory + fields = ['id', 'title', 'source_type','slug', 'sect_id', 'sect_type','children_count','has_hadis'] + + # def get_name(self, obj): + # """Get category name based on request language""" + # request = self.context.get('request') + # language_code = getattr(request, 'LANGUAGE_CODE', 'en') + # return obj.get_translation(language_code) if hasattr(obj, 'get_translation') else obj.title + + def get_has_hadis(self, obj): + """Check if category can have hadis (no active children) and has hadis""" + children = obj.get_children().filter(sect=obj.sect).order_by('order') + for child in children: + if Hadis.objects.filter(category=child, status=True).exists() : + return True + return False + + # def get_thumbnail(self, obj): + # """Get absolute URL for thumbnail""" + # if hasattr(obj, 'thumbnail') and obj.thumbnail: + # request = self.context.get('request') + # if request: + # return request.build_absolute_uri(obj.thumbnail.url) + # return obj.thumbnail.url + # return None + + def get_children_count(self, obj): + """Get count of active children categories that have children or hadis""" + children = obj.get_children().filter(sect=obj.sect) + # Filter children that have either children or hadis + filtered_children = [] + for child in children: + has_children = child.get_children().filter(sect=obj.sect).exists() + has_hadis = Hadis.objects.filter(category=child, status=True).exists() + if has_children or has_hadis: + filtered_children.append(child) + return len(filtered_children) class CategorySerializer(serializers.ModelSerializer): sect_id = serializers.IntegerField(source='sect.id', read_only=True) sect_type = serializers.CharField(source='sect.sect_type', read_only=True) + children_count = serializers.SerializerMethodField() class Meta: model = HadisCategory - fields = ['id', 'title', 'sect_id', 'sect_type','source_type','description','order','slug','xmind_file'] + fields = ['id', 'title', 'sect_id', 'sect_type','source_type','description','order','slug','xmind_file', 'children_count'] + + def get_children_count(self, obj): + """Get count of active children categories that have children or hadis""" + children = obj.get_children().filter(sect=obj.sect) + # Filter children that have either children or hadis + filtered_children = [] + for child in children: + has_children = child.get_children().filter(sect=obj.sect).exists() + has_hadis = Hadis.objects.filter(category=child, status=True).exists() + if has_children or has_hadis: + filtered_children.append(child) + return len(filtered_children) diff --git a/apps/hadis/serializers/hadis.py b/apps/hadis/serializers/hadis.py index 88c5547..3fe5813 100644 --- a/apps/hadis/serializers/hadis.py +++ b/apps/hadis/serializers/hadis.py @@ -115,7 +115,7 @@ class TransmitterSerializer(serializers.ModelSerializer): model = Transmitters fields = [ 'id', 'full_name', 'birth_year_hijri', 'death_year_hijri', - 'description' + 'description','reliability','madhhab', ] class TransmitterOpinionSerializer(serializers.ModelSerializer): """ Serializer for TransmitterOpinions """ diff --git a/apps/hadis/urls.py b/apps/hadis/urls.py index 1369624..fac4221 100644 --- a/apps/hadis/urls.py +++ b/apps/hadis/urls.py @@ -1,5 +1,5 @@ from django.urls import path -from .views.category import HadisCategorySectListView, HadisCategoryTreeView,CategoriesView +from .views.category import HadisCategorySectListView, HadisCategoryTreeView, CategoriesView, CategoriesBySectView, HadisCategorySelectBySectView, HadisCategorySelectBySectSourceView , HadisCategoryTreeNormalView from .views.hadis import HadisCollectionListView, HadisListView, HadisDetailView, HadisSyncView from .views.transmitter import TransmitterView ,TransmitterDetailView from .views.reference import BookDetailView, BookReferencesView @@ -10,10 +10,14 @@ urlpatterns = [ path('collections/', HadisCollectionListView.as_view(), name='hadis-collection-list'), path('sync/sects/', HadisCategorySectListView.as_view(), name='hadis-sect-list'), path('sync/categories/tree/', HadisCategoryTreeView.as_view(), name='hadis-category-tree'), + path('categories/tree/', HadisCategoryTreeNormalView.as_view(), name='hadis-category-tree-normal'), path('sync/hadis/', HadisSyncView.as_view(), name='hadis-sync'), path('info/', HadisInfoView.as_view(), name='hadis-info'), path('category//', HadisListView.as_view(), name='hadis-list'), path('/', HadisDetailView.as_view(), name='hadis-detail'), + path('categories////', HadisCategorySelectBySectSourceView.as_view(), name='categories-tree-by-sect-source'), + path('categories///', HadisCategorySelectBySectView.as_view(), name='categories-tree-by-sect'), + path('categories//', CategoriesBySectView.as_view(), name='categories-by-sect'), path('categories/', CategoriesView.as_view(), name='categories'), path('narrators/',TransmitterView.as_view(), name='narrators'), path('narrators/',TransmitterDetailView.as_view(), name='narrator-detail'), diff --git a/apps/hadis/views/category.py b/apps/hadis/views/category.py index 39e6601..d65eb2b 100644 --- a/apps/hadis/views/category.py +++ b/apps/hadis/views/category.py @@ -4,8 +4,15 @@ from django.shortcuts import get_object_or_404 from utils.pagination import NoPagination from ..models import HadisSect, HadisCategory -from ..serializers import HadisCategorySectListSerializer, HadisCategoryTreeSerializer, CategorySerializer -from ..docs import hadis_sect_list_swagger, hadis_category_tree_swagger +from ..serializers import HadisCategorySectListSerializer, HadisCategoryTreeSerializer, CategorySerializer , HadisCategorySelectSerializer +from ..docs import ( + hadis_sect_list_swagger, + hadis_category_tree_swagger, + categories_list_swagger, + categories_by_sect_swagger, + categories_tree_by_sect_swagger, + categories_tree_by_sect_source_swagger +) class HadisCategorySectListView(ListAPIView): @@ -127,16 +134,118 @@ class HadisCategoryTreeView(ListAPIView): return Response(response_data) + +class HadisCategoryTreeNormalView(ListAPIView): + """ + Normal (paginated) tree view for HadisCategory. + Unlike the sync view, this simply returns the root categories (filtered to active sects) + with their nested children, and uses the project's default pagination. + """ + serializer_class = HadisCategoryTreeSerializer + + @hadis_category_tree_swagger + def get(self, request, *args, **kwargs): + return self.list(request, *args, **kwargs) + + def get_queryset(self): + return HadisCategory.objects.filter( + parent__isnull=True, + sect__is_active=True + ).order_by('sect__order', 'order') + + +class HadisCategorySelectBySectView(ListAPIView): + """ + Tree view for HadisCategory filtered by sect_type and category slug. + Returns the children categories of the specified category (by slug) within the sect_type. + """ + serializer_class = HadisCategorySelectSerializer + + @categories_tree_by_sect_swagger + def get(self, request, *args, **kwargs): + return self.list(request, *args, **kwargs) + + def get_queryset(self): + sect_type = self.kwargs.get('sect_type') + slug = self.kwargs.get('slug') + + # Find the parent category by slug and sect_type + try: + parent_category = HadisCategory.objects.get( + slug=slug, + sect__sect_type=sect_type, + sect__is_active=True + ) + except HadisCategory.DoesNotExist: + return HadisCategory.objects.none() + + # Return children of this category, filtered as before + return HadisCategory.objects.filter( + parent=parent_category, + sect__sect_type=sect_type, + sect__is_active=True + ).order_by('order') + + +class HadisCategorySelectBySectSourceView(ListAPIView): + """ + Tree view for HadisCategory filtered by sect_type, category slug and source_type. + Returns the children categories of the specified category (by slug) within the sect_type, filtered by source_type. + """ + serializer_class = HadisCategorySelectSerializer + + @categories_tree_by_sect_source_swagger + def get(self, request, *args, **kwargs): + return self.list(request, *args, **kwargs) + + def get_queryset(self): + sect_type = self.kwargs.get('sect_type') + slug = self.kwargs.get('slug') + source_type = self.kwargs.get('source_type') + + # Find the parent category by slug and sect_type + try: + parent_category = HadisCategory.objects.get( + slug=slug, + sect__sect_type=sect_type, + sect__is_active=True + ) + except HadisCategory.DoesNotExist: + return HadisCategory.objects.none() + + # Return children of this category, filtered by source_type + return HadisCategory.objects.filter( + parent=parent_category, + sect__sect_type=sect_type, + sect__is_active=True, + source_type=source_type + ).order_by('order') + class CategoriesView(ListAPIView): """ API view to list all HadisCategories """ queryset = HadisCategory.objects.all() serializer_class = CategorySerializer - pagination_class = NoPagination + @categories_list_swagger def get(self, request, *args, **kwargs): return self.list(request, *args, **kwargs) def list(self, request, *args, **kwargs): - return super().list(request, *args, **kwargs) \ No newline at end of file + return super().list(request, *args, **kwargs) + + +class CategoriesBySectView(ListAPIView): + """ + API view to list HadisCategories filtered by sect_type + """ + serializer_class = CategorySerializer + + def get_queryset(self): + sect_type = self.kwargs.get('sect_type') + return HadisCategory.objects.filter(sect__sect_type=sect_type) + + @categories_by_sect_swagger + def get(self, request, *args, **kwargs): + return self.list(request, *args, **kwargs) \ No newline at end of file diff --git a/apps/hadis/views/reference.py b/apps/hadis/views/reference.py index 3cfbf64..70d5c43 100644 --- a/apps/hadis/views/reference.py +++ b/apps/hadis/views/reference.py @@ -1,6 +1,7 @@ from rest_framework.generics import ListAPIView, RetrieveAPIView from ..models import BookReference , BookAuthor , BookReferenceImage from ..serializers.reference import BookAuthorSerializer, BookDetailSerializer , BookReferenceSerializer +from ..docs import book_references_list_swagger, book_authors_list_swagger, book_detail_swagger from utils.pagination import NoPagination @@ -8,21 +9,31 @@ from utils.pagination import NoPagination class BookReferencesView(ListAPIView): queryset = BookReference.objects.all() serializer_class = BookReferenceSerializer - pagination_class = NoPagination + + @book_references_list_swagger + def get(self, request, *args, **kwargs): + return super().get(request, *args, **kwargs) class BookAuthorView(ListAPIView): queryset = BookAuthor.objects.all() serializer_class = BookAuthorSerializer - pagination_class = NoPagination + + @book_authors_list_swagger + def get(self, request, *args, **kwargs): + return super().get(request, *args, **kwargs) class BookDetailView(RetrieveAPIView): serializer_class = BookDetailSerializer lookup_field = 'id' lookup_url_kwarg = 'bookreference_id' + @book_detail_swagger + def get(self, request, *args, **kwargs): + return super().get(request, *args, **kwargs) + def get_queryset(self): - return BookReference.objects.all().prefetch_related( + return BookReference.objects.filter(id = self.kwargs['bookreference_id']).prefetch_related( 'bookauthor_set__name', 'bookreferenceimage_set__image', ) diff --git a/apps/hadis/views/transmitter.py b/apps/hadis/views/transmitter.py index af47c22..365be46 100644 --- a/apps/hadis/views/transmitter.py +++ b/apps/hadis/views/transmitter.py @@ -2,16 +2,53 @@ from django.contrib.admin.utils import lookup_field from rest_framework.generics import ListAPIView , RetrieveAPIView from ..models import Transmitters , TransmitterOpinion from ..serializers import TransmitterSerializer , TransmitterDetailSerializer +from ..docs import transmitter_list_swagger, transmitter_detail_swagger from utils.pagination import NoPagination + class TransmitterView(ListAPIView): queryset = Transmitters.objects.all() serializer_class = TransmitterSerializer - pagination_class = NoPagination + + @transmitter_list_swagger + def get(self, request, *args, **kwargs): + return super().get(request, *args, **kwargs) + + def get_queryset(self): + queryset = Transmitters.objects.all() + + # Filter by status (reliability) + status_filter = self.request.query_params.get('status', None) + if status_filter: + queryset = queryset.filter(reliability=status_filter) + + # Filter by madhhab + madhhab_filter = self.request.query_params.get('madhhab', None) + if madhhab_filter: + queryset = queryset.filter(madhhab=madhhab_filter) + + # Filter by generation (narrator layer) + # generation_filter = self.request.query_params.get('generation', None) + # if generation_filter: + # try: + # generation_num = int(generation_filter) + # # Filter transmitters who have hadis transmissions in the specified generation/layer + # queryset = queryset.filter( + # hadi=generation_num + # ).distinct() + # except (ValueError, TypeError): + # pass # Invalid generation number, ignore filter + + return queryset class TransmitterDetailView(RetrieveAPIView): serializer_class = TransmitterDetailSerializer lookup_field = 'id' lookup_url_kwarg = 'transmitters_id' + + @transmitter_detail_swagger + def get(self, request, *args, **kwargs): + return super().get(request, *args, **kwargs) + def get_queryset(self): input = self.kwargs['transmitters_id'] return Transmitters.objects.filter(id=input) \ No newline at end of file diff --git a/docs/categories_api_check.MD b/docs/categories_api_check.MD new file mode 100644 index 0000000..a531460 --- /dev/null +++ b/docs/categories_api_check.MD @@ -0,0 +1,535 @@ +# 📋 Categories API Response Examples + +## 1. GET `/api/hadis/categories/` +**Description**: Returns all categories with pagination (limited to 16 items per page). + +**Response Structure:** +```json +{ + "count": 56, + "next": "http://127.0.0.1:8000/api/hadis/categories/?limit=16&offset=16", + "previous": null, + "results": [ + { + "id": 324, + "title": "Толкование Корана", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 1, + "slug": null, + "xmind_file": null, + "children_count": 3 + }, + { + "id": 330, + "title": "Толкование суры Аль-Фатиха", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 1, + "slug": null, + "xmind_file": null, + "children_count": 0 + }, + { + "id": 331, + "title": "Толкование суры Аль-Бакара", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 2, + "slug": null, + "xmind_file": null, + "children_count": 0 + }, + { + "id": 332, + "title": "Толкование суры Аль Имран", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 3, + "slug": null, + "xmind_file": null, + "children_count": 0 + }, + { + "id": 325, + "title": "Аяты постановлений", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 2, + "slug": null, + "xmind_file": "http://127.0.0.1:8000/media/hadis/xmind_files/category_325_%D0%90%D1%8F%D1%82%D1%8B_%D0%BF%D0%BE%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B9.xmind", + "children_count": 3 + }, + { + "id": 333, + "title": "Аяты о молитве", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 1, + "slug": null, + "xmind_file": null, + "children_count": 0 + }, + { + "id": 334, + "title": "Аяты о посте", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 2, + "slug": null, + "xmind_file": null, + "children_count": 0 + }, + { + "id": 335, + "title": "Аяты о закяте", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 3, + "slug": null, + "xmind_file": null, + "children_count": 0 + }, + { + "id": 326, + "title": "Коранические истории", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 3, + "slug": null, + "xmind_file": "http://127.0.0.1:8000/media/hadis/xmind_files/category_326_%D0%9A%D0%BE%D1%80%D0%B0%D0%BD%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5_%D0%B8%D1%81%D1%82%D0%BE%D1%80%D0%B8%D0%B8.xmind", + "children_count": 0 + }, + { + "id": 336, + "title": "Истории пророков", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 1, + "slug": null, + "xmind_file": null, + "children_count": 0 + }, + { + "id": 337, + "title": "Истории праведников", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 2, + "slug": null, + "xmind_file": null, + "children_count": 0 + }, + { + "id": 327, + "title": "Достоинства сур", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 4, + "slug": null, + "xmind_file": "http://127.0.0.1:8000/media/hadis/xmind_files/category_327_%D0%94%D0%BE%D1%81%D1%82%D0%BE%D0%B8%D0%BD%D1%81%D1%82%D0%B2%D0%B0_%D1%81%D1%83%D1%80.xmind", + "children_count": 0 + }, + { + "id": 328, + "title": "Чудеса Корана", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 5, + "slug": null, + "xmind_file": "http://127.0.0.1:8000/media/hadis/xmind_files/category_328_%D0%A7%D1%83%D0%B4%D0%B5%D1%81%D0%B0_%D0%9A%D0%BE%D1%80%D0%B0%D0%BD%D0%B0.xmind", + "children_count": 0 + }, + { + "id": 329, + "title": "Коранические науки", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 6, + "slug": null, + "xmind_file": null, + "children_count": 0 + }, + { + "id": 338, + "title": "Книга очищения", + "sect_id": 20, + "sect_type": "shia", + "source_type": "hadith", + "description": null, + "order": 1, + "slug": null, + "xmind_file": "http://127.0.0.1:8000/media/hadis/xmind_files/category_338_%D0%9A%D0%BD%D0%B8%D0%B3%D0%B0_%D0%BE%D1%87%D0%B8%D1%89%D0%B5%D0%BD%D0%B8%D1%8F.xmind", + "children_count": 0 + }, + { + "id": 344, + "title": "Омовение", + "sect_id": 20, + "sect_type": "shia", + "source_type": "hadith", + "description": null, + "order": 1, + "slug": null, + "xmind_file": null, + "children_count": 0 + } + ] +} +``` + + +## 2. GET `/api/hadis/categories/{sect_type}/` +**Description**: Returns categories filtered by sect type (shia or sunni). + +**Parameters:** +- `sect_type`: Islamic sect type (`shia` or `sunni`) + +**Example URL:** `GET /api/hadis/categories/shia/` + +**Response Structure:** +```json +{ + "count": 28, + "next": "http://127.0.0.1:8000/api/hadis/categories/shia/?limit=16&offset=16", + "previous": null, + "results": [ + { + "id": 324, + "title": "Толкование Корана", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 1, + "slug": null, + "xmind_file": null, + "children_count": 3 + }, + { + "id": 330, + "title": "Толкование суры Аль-Фатиха", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 1, + "slug": null, + "xmind_file": null, + "children_count": 0 + }, + { + "id": 331, + "title": "Толкование суры Аль-Бакара", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 2, + "slug": null, + "xmind_file": null, + "children_count": 0 + }, + { + "id": 332, + "title": "Толкование суры Аль Имран", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 3, + "slug": null, + "xmind_file": null, + "children_count": 0 + }, + { + "id": 325, + "title": "Аяты постановлений", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 2, + "slug": null, + "xmind_file": "http://127.0.0.1:8000/media/hadis/xmind_files/category_325_%D0%90%D1%8F%D1%82%D1%8B_%D0%BF%D0%BE%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B9.xmind", + "children_count": 3 + }, + { + "id": 333, + "title": "Аяты о молитве", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 1, + "slug": null, + "xmind_file": null, + "children_count": 0 + }, + { + "id": 334, + "title": "Аяты о посте", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 2, + "slug": null, + "xmind_file": null, + "children_count": 0 + }, + { + "id": 335, + "title": "Аяты о закяте", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 3, + "slug": null, + "xmind_file": null, + "children_count": 0 + }, + { + "id": 326, + "title": "Коранические истории", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 3, + "slug": null, + "xmind_file": "http://127.0.0.1:8000/media/hadis/xmind_files/category_326_%D0%9A%D0%BE%D1%80%D0%B0%D0%BD%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5_%D0%B8%D1%81%D1%82%D0%BE%D1%80%D0%B8%D0%B8.xmind", + "children_count": 0 + }, + { + "id": 336, + "title": "Истории пророков", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 1, + "slug": null, + "xmind_file": null, + "children_count": 0 + }, + { + "id": 337, + "title": "Истории праведников", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 2, + "slug": null, + "xmind_file": null, + "children_count": 0 + }, + { + "id": 327, + "title": "Достоинства сур", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 4, + "slug": null, + "xmind_file": "http://127.0.0.1:8000/media/hadis/xmind_files/category_327_%D0%94%D0%BE%D1%81%D1%82%D0%BE%D0%B8%D0%BD%D1%81%D1%82%D0%B2%D0%B0_%D1%81%D1%83%D1%80.xmind", + "children_count": 0 + }, + { + "id": 328, + "title": "Чудеса Корана", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 5, + "slug": null, + "xmind_file": "http://127.0.0.1:8000/media/hadis/xmind_files/category_328_%D0%A7%D1%83%D0%B4%D0%B5%D1%81%D0%B0_%D0%9A%D0%BE%D1%80%D0%B0%D0%BD%D0%B0.xmind", + "children_count": 0 + }, + { + "id": 329, + "title": "Коранические науки", + "sect_id": 20, + "sect_type": "shia", + "source_type": "quran", + "description": null, + "order": 6, + "slug": null, + "xmind_file": null, + "children_count": 0 + }, + { + "id": 338, + "title": "Книга очищения", + "sect_id": 20, + "sect_type": "shia", + "source_type": "hadith", + "description": null, + "order": 1, + "slug": null, + "xmind_file": "http://127.0.0.1:8000/media/hadis/xmind_files/category_338_%D0%9A%D0%BD%D0%B8%D0%B3%D0%B0_%D0%BE%D1%87%D0%B8%D1%89%D0%B5%D0%BD%D0%B8%D1%8F.xmind", + "children_count": 0 + }, + { + "id": 344, + "title": "Омовение", + "sect_id": 20, + "sect_type": "shia", + "source_type": "hadith", + "description": null, + "order": 1, + "slug": null, + "xmind_file": null, + "children_count": 0 + } + ] +} +``` + + +## 3. GET `/api/hadis/categories/{sect_type}/{slug}/` +**Description**: Returns child categories of a specific category (by slug) within the sect type. + +**Parameters:** +- `sect_type`: Islamic sect type (`shia` or `sunni`) +- `slug`: URL slug of the parent category + +**Example URL:** `GET /api/hadis/categories/shia/some-category-slug/` + +**Response Structure:** +```json +{ + "count": 3, + "next": null, + "previous": null, + "results": [ + { + "id": 330, + "title": "Толкование суры Аль-Фатиха", + "source_type": "quran", + "sect_id": 20, + "sect_type": "shia", + "children_count": 0 + }, + { + "id": 331, + "title": "Толкование суры Аль-Бакара", + "source_type": "quran", + "sect_id": 20, + "sect_type": "shia", + "children_count": 0 + }, + { + "id": 332, + "title": "Толкование суры Аль Имран", + "source_type": "quran", + "sect_id": 20, + "sect_type": "shia", + "children_count": 0 + } + ] +} +``` + +## 4. GET `/api/hadis/categories/{sect_type}/{slug}/{source_type}/` +**Description**: Returns child categories of a specific category (by slug) within the sect type, filtered by source type. + +**Parameters:** +- `sect_type`: Islamic sect type (`shia` or `sunni`) +- `slug`: URL slug of the parent category +- `source_type`: Source material type (`quran`, `hadith`, `history`, `fatwa`, `quote`) + +**Example URL:** `GET /api/hadis/categories/shia/some-category-slug/quran/` + +**Response Structure:** +```json +{ + "count": 3, + "next": null, + "previous": null, + "results": [ + { + "id": 330, + "title": "Толкование суры Аль-Фатиха", + "source_type": "quran", + "slug": "-1", + "sect_id": 20, + "sect_type": "shia", + "children_count": 0, + "has_hadis": false + }, + { + "id": 331, + "title": "Толкование суры Аль-Бакара", + "source_type": "quran", + "slug": "-19", + "sect_id": 20, + "sect_type": "shia", + "children_count": 0, + "has_hadis": false + }, + { + "id": 332, + "title": "Толкование суры Аль Имран", + "source_type": "quran", + "slug": "-33", + "sect_id": 20, + "sect_type": "shia", + "children_count": 0, + "has_hadis": false + } + ] +} + + +``` + +--- + + + +## 🔍 Field Descriptions + +- **`id`**: Unique identifier for the category +- **`title`**: Category name in Russian +- **`sect_id`**: ID of the Islamic sect +- **`sect_type`**: Type of Islamic sect (`shia` or `sunni`) +- **`source_type`**: Type of source material (`quran`, `hadith`, `history`, `fatwa`, `quote`) +- **`description`**: Optional description of the category +- **`order`**: Display order within the hierarchy +- **`slug`**: URL-friendly identifier (auto-generated from title, unique) +- **`xmind_file`**: URL to associated XMind mind map file (optional) +- **`children_count`**: Number of active child categories +- **`has_hadis`**: Boolean indicating if category contains hadith content (tree endpoints only) \ No newline at end of file