diff --git a/apps/hadis/admin/hadis.py b/apps/hadis/admin/hadis.py index 56b6cf2..f3dd9a1 100644 --- a/apps/hadis/admin/hadis.py +++ b/apps/hadis/admin/hadis.py @@ -186,14 +186,15 @@ class HadisTagAdmin(ModelAdmin): class HadisStatusAdmin(ModelAdmin): """Admin for HadisStatus model""" - list_display = ('title', 'color', 'order') + list_display = ('title', 'color', 'order', 'description') list_filter = ('color',) - search_fields = ('title',) + search_fields = ('title', 'description') ordering = ('order',) + readonly_fields = ('slug',) fieldsets = ( (None, { - 'fields': ('title', 'color', 'order') + 'fields': ('title', 'slug', 'color', 'order', 'description') }), ) diff --git a/apps/hadis/admin/reference.py b/apps/hadis/admin/reference.py index ef6f2db..c7c9ad9 100644 --- a/apps/hadis/admin/reference.py +++ b/apps/hadis/admin/reference.py @@ -14,7 +14,9 @@ from ..models import ( BookReference, BookReferenceImage, BookAuthor, - BookAttribute + BookAttribute, + BookSubjectArea, + BookType ) # ----------------------------------------------------------------------------- @@ -259,31 +261,34 @@ class BookReferenceAdmin(ModelAdmin): # Use custom methods for JSON fields to show readable text list_display = ( - 'get_title_display', - 'slug', - 'get_publisher_display', - 'year_of_publication', - 'rate', + 'get_title_display', + 'slug', + 'get_publisher_display', + 'year_of_publication', + 'rate', 'created_at' ) - - list_filter = ('year_of_publication', 'rate', 'created_at') - - # Searching by JSON fields via string is limited in Django, + + list_filter = ('year_of_publication', 'rate', 'created_at', 'type') + + # Searching by JSON fields via string is limited in Django, # so we focus on standard fields search_fields = ('isbn', 'slug', 'volume') - + readonly_fields = ('created_at', 'updated_at') - + # Add the inlines to manage images and attributes on the same page inlines = [BookReferenceImageInline, BookAttributeInline] + # Use filter_horizontal for ManyToMany fields to make selection easier + filter_horizontal = ('subject_area',) + fieldsets = ( (_('Basic Info'), { - 'fields': ('title', 'description', 'slug', 'language') + 'fields': ('title', 'description', 'slug', 'language', 'subject_area') }), (_('Publication Info'), { - 'fields': ('publisher', 'isbn', 'year_of_publication', 'number_page', 'volume') + 'fields': ('publisher', 'isbn', 'year_of_publication', 'number_page', 'volume', 'type') }), (_('Rating & Stats'), { 'fields': ('rate', 'created_at', 'updated_at') @@ -311,14 +316,34 @@ class BookReferenceAdmin(ModelAdmin): class BookAuthorAdmin(ModelAdmin): """Admin for BookAuthor model""" - - list_display = ('get_name_display', 'created_at', 'updated_at') + + list_display = ('get_name_display', 'birth_year_hijri', 'death_year_hijri', 'birth_year_miladi', 'death_year_miladi', 'created_at', 'updated_at') search_fields = ('name',) # Note: Search works best on exact text matches readonly_fields = ('created_at', 'updated_at') - + list_filter = ('birth_year_hijri', 'death_year_hijri', 'birth_year_miladi', 'death_year_miladi', 'created_at') + # Use filter_horizontal for ManyToMany fields to make selection easier filter_horizontal = ('book_references',) + fieldsets = ( + (_('Basic Info'), { + 'fields': ('name',) + }), + (_('Dates (Hijri)'), { + 'fields': ('birth_year_hijri', 'death_year_hijri') + }), + (_('Dates (Miladi)'), { + 'fields': ('birth_year_miladi', 'death_year_miladi') + }), + (_('References'), { + 'fields': ('book_references',) + }), + (_('Timestamps'), { + 'fields': ('created_at', 'updated_at'), + 'classes': ('collapse',) + }), + ) + @display(description=_('Name'), ordering='name') def get_name_display(self, obj): if obj.name and isinstance(obj.name, list) and len(obj.name) > 0: @@ -379,6 +404,46 @@ class BookAttributeAdmin(ModelAdmin): return '-' +class BookSubjectAreaAdmin(ModelAdmin): + """Admin for BookSubjectArea model""" + + list_display = ('get_title_display', 'created_at', 'updated_at') + search_fields = ('title',) + readonly_fields = ('created_at', 'updated_at') + + @display(description=_('Title'), ordering='title') + def get_title_display(self, obj): + return self._extract_first_text(obj.title) + + def _extract_first_text(self, json_data): + """Helper to safely extract the first 'text' from a JSON list""" + if json_data and isinstance(json_data, list) and len(json_data) > 0: + first_item = json_data[0] + if isinstance(first_item, dict): + return first_item.get('text', '-') + return '-' + + +class BookTypeAdmin(ModelAdmin): + """Admin for BookType model""" + + list_display = ('get_title_display', 'created_at', 'updated_at') + search_fields = ('title',) + readonly_fields = ('created_at', 'updated_at') + + @display(description=_('Title'), ordering='title') + def get_title_display(self, obj): + return self._extract_first_text(obj.title) + + def _extract_first_text(self, json_data): + """Helper to safely extract the first 'text' from a JSON list""" + if json_data and isinstance(json_data, list) and len(json_data) > 0: + first_item = json_data[0] + if isinstance(first_item, dict): + return first_item.get('text', '-') + return '-' + + # ----------------------------------------------------------------------------- # 3. Registration # ----------------------------------------------------------------------------- @@ -386,4 +451,6 @@ class BookAttributeAdmin(ModelAdmin): dovoodi_admin_site.register(BookReference, BookReferenceAdmin) dovoodi_admin_site.register(BookAuthor, BookAuthorAdmin) dovoodi_admin_site.register(BookAttribute, BookAttributeAdmin) -dovoodi_admin_site.register(BookReferenceImage, BookReferenceImageAdmin) \ No newline at end of file +dovoodi_admin_site.register(BookReferenceImage, BookReferenceImageAdmin) +dovoodi_admin_site.register(BookSubjectArea, BookSubjectAreaAdmin) +dovoodi_admin_site.register(BookType, BookTypeAdmin) \ No newline at end of file diff --git a/apps/hadis/migrations/0003_booksubjectarea_booktype_hadisstatus_description_and_more.py b/apps/hadis/migrations/0003_booksubjectarea_booktype_hadisstatus_description_and_more.py new file mode 100644 index 0000000..cb1b357 --- /dev/null +++ b/apps/hadis/migrations/0003_booksubjectarea_booktype_hadisstatus_description_and_more.py @@ -0,0 +1,85 @@ +# Generated by Django 4.2.27 on 2026-01-22 11:07 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0002_bookauthor_birth_year_hijri_and_more"), + ] + + operations = [ + migrations.CreateModel( + name="BookSubjectArea", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.JSONField(default=list, verbose_name="Title")), + ( + "created_at", + models.DateTimeField(auto_now_add=True, verbose_name="created at"), + ), + ( + "updated_at", + models.DateTimeField(auto_now=True, verbose_name="updated at"), + ), + ], + ), + migrations.CreateModel( + name="BookType", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.JSONField(default=list, verbose_name="Title")), + ( + "created_at", + models.DateTimeField(auto_now_add=True, verbose_name="created at"), + ), + ( + "updated_at", + models.DateTimeField(auto_now=True, verbose_name="updated at"), + ), + ], + ), + migrations.AddField( + model_name="hadisstatus", + name="description", + field=models.JSONField(default=list, verbose_name="Description"), + ), + migrations.AddField( + model_name="bookreference", + name="subject_area", + field=models.ManyToManyField( + blank=True, + related_name="book_subjects", + to="hadis.booksubjectarea", + verbose_name="subject area", + ), + ), + migrations.AddField( + model_name="bookreference", + name="type", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + to="hadis.booktype", + verbose_name="type", + ), + ), + ] diff --git a/apps/hadis/models/hadis.py b/apps/hadis/models/hadis.py index 92c9ec2..f30313e 100644 --- a/apps/hadis/models/hadis.py +++ b/apps/hadis/models/hadis.py @@ -137,6 +137,7 @@ class HadisStatus(models.Model): slug= models.SlugField(max_length=255, verbose_name=_('slug'), blank=True,unique = True) color = models.CharField(max_length=20, choices=ColorChoices.choices, verbose_name=_('color')) order = models.IntegerField(default=0, verbose_name=_('order')) + description = models.JSONField(default = list , verbose_name=_('Description')) def save(self, *args, **kwargs): if not self.slug: diff --git a/apps/hadis/models/reference.py b/apps/hadis/models/reference.py index 1e74e95..9e0cb09 100644 --- a/apps/hadis/models/reference.py +++ b/apps/hadis/models/reference.py @@ -3,6 +3,56 @@ from django.utils.translation import gettext_lazy as _ from django.utils.text import slugify from typing import Optional +class BookSubjectArea(models.Model): + title = models.JSONField(default = list , verbose_name=_('Title')) + created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) + updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at')) + + def __str__(self): + return f"{self.title[0]['text']}" + + def get_title(self,lang): + """ + Get title for a specific language + """ + + if not self.title or not isinstance(self.title, list): + return None + + for tr in self.title: + if isinstance(tr, dict) and tr.get('language_code') == lang: + return tr.get('text', '') + + for tr in self.title: + if isinstance(tr, dict) and tr.get('language_code') == 'en': + return tr.get('text', '') + return None + + +class BookType(models.Model): + title = models.JSONField(default = list , verbose_name=_('Title')) + created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) + updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at')) + + def __str__(self): + return f"{self.title[0]['text']}" + + def get_title(self,lang): + """ + Get title for a specific language + """ + + if not self.title or not isinstance(self.title, list): + return None + + for tr in self.title: + if isinstance(tr, dict) and tr.get('language_code') == lang: + return tr.get('text', '') + + for tr in self.title: + if isinstance(tr, dict) and tr.get('language_code') == 'en': + return tr.get('text', '') + return None class BookReference(models.Model): """ @@ -18,6 +68,8 @@ class BookReference(models.Model): number_page = models.PositiveIntegerField(verbose_name=_('number of pages'), blank=True, null=True) slug = models.SlugField(max_length=255, verbose_name=_('slug'), blank=True,unique=True) publisher = models.JSONField(default = list , verbose_name=_('Publisher')) + subject_area = models.ManyToManyField(BookSubjectArea, related_name="book_subjects", verbose_name=_('subject area'), blank=True) + type = models.ForeignKey(BookType, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_('type')) rate = models.DecimalField( max_digits=3, decimal_places=2,