from django.contrib import admin from django import forms from django.utils.translation import gettext_lazy as _ from unfold.admin import ModelAdmin, TabularInline from unfold.decorators import display, action from unfold.contrib.forms.widgets import WysiwygWidget from utils.json_editor_field import JsonEditorWidget import json from utils.admin import dovoodi_admin_site from ..models import ( Transmitters, HadisTransmitter, NarratorLayer, TransmitterReliability, OpinionStatus, TransmitterOpinion, TransmitterOriginalText ) class HadisTransmitterInline(TabularInline): """Inline for HadisTransmitter in Transmitters admin""" model = HadisTransmitter extra = 0 fields = ('hadis', 'order') class TransmitterOpinionInline(TabularInline): """Inline for TransmitterOpinion in Transmitters admin""" model = TransmitterOpinion extra = 0 fields = ('scholar_name', 'status') class TransmitterOriginalTextInline(TabularInline): """Inline for TransmitterOriginalText in Transmitters admin""" model = TransmitterOriginalText extra = 0 fields = ('title', 'slug') # Custom Forms for JSON Fields class TransmittersAdminForm(forms.ModelForm): """Custom form for Transmitters with JSON editor widgets""" class Meta: model = Transmitters fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Helper function to create standard language schema def create_language_schema(title_name, text_title): return { "type": "array", "title": title_name, "items": { "type": "object", "title": title_name[:-1] if title_name.endswith('s') else title_name, "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": text_title } }, "required": ["language_code", "text"] } } # Create schemas for all JSON fields full_name_schema = create_language_schema("Full Names", "Full Name Text") kunya_schema = create_language_schema("Kunyas", "Kunya Text") known_as_schema = create_language_schema("Known As", "Known As Text") nickname_schema = create_language_schema("Nicknames", "Nickname Text") origin_schema = create_language_schema("Origins", "Origin Text") lived_in_schema = create_language_schema("Lived In", "Lived In Text") died_in_schema = create_language_schema("Died In", "Died In Text") description_schema = create_language_schema("Descriptions", "Description Text") # Apply JSON editor widgets self.fields['full_name'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(full_name_schema), 'title': 'Full Names' }) self.fields['kunya'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(kunya_schema), 'title': 'Kunyas' }) self.fields['known_as'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(known_as_schema), 'title': 'Known As' }) self.fields['nickname'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(nickname_schema), 'title': 'Nicknames' }) self.fields['origin'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(origin_schema), 'title': 'Origins' }) self.fields['lived_in'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(lived_in_schema), 'title': 'Lived In' }) self.fields['died_in'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(died_in_schema), 'title': 'Died In' }) self.fields['description'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(description_schema), 'title': 'Descriptions' }) class TransmittersAdmin(ModelAdmin): """Admin for Transmitters model""" form = TransmittersAdminForm list_display = ('display_header', 'birth_year_hijri', 'death_year_hijri') list_filter = ('birth_year_hijri', 'death_year_hijri') search_fields = ('full_name', 'description') readonly_fields = ('created_at', 'updated_at') inlines = [HadisTransmitterInline, TransmitterOpinionInline, TransmitterOriginalTextInline] fieldsets = ( (None, { 'fields': ('full_name', 'birth_year_hijri', 'death_year_hijri','known_as','nickname','thumbnail') }), (_('Additional Information'), { 'fields': ('description','origin','lived_in','died_in','kunya'), # 'classes': ('collapse',) }), (_('Timestamps'), { 'fields': ('created_at', 'updated_at'), # 'classes': ('collapse',) }), ) @display(description=_('Full Name'), ordering='full_name') def get_full_name_display(self, obj): """ Parses the JSON full_name and returns the first item's text. """ if obj.full_name and isinstance(obj.full_name, list) and len(obj.full_name) > 0: # Safely get the first item first_item = obj.full_name[0] if isinstance(first_item, dict): return first_item.get('text', '-') return '-' @display(description=_("Transmitter"), header=True) def display_header(self, obj): from django.templatetags.static import static # Get thumbnail image path - use article's thumbnail if available, otherwise use default thumbnail_path = obj.thumbnail.url if obj.thumbnail else None return [ self.get_full_name_display(obj), None, None, { "path": thumbnail_path, "height": 30, "width": 50, "borderless": True, # "squared": True, }, ] class HadisTransmitterAdmin(ModelAdmin): """Admin for HadisTransmitter model""" list_display = ('hadis', 'transmitter', 'order', 'created_at') list_filter = ( 'created_at',) search_fields = ('hadis__title', 'transmitter__full_name') readonly_fields = ('created_at',) ordering = ('hadis', 'order') fieldsets = ( (None, { 'fields': ('hadis', 'transmitter', 'order') }), (_('Timestamps'), { 'fields': ('created_at',), 'classes': ('collapse',) }), ) # Custom Forms for JSON Fields class NarratorLayerAdminForm(forms.ModelForm): """Custom form for NarratorLayer with JSON editor widgets""" class Meta: model = NarratorLayer fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Schema for name JSON field name_schema = { "type": "array", "title": "Names", "items": { "type": "object", "title": "Name", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Name Text" } }, "required": ["language_code", "text"] } } # Schema for description JSON field description_schema = { "type": "array", "title": "Descriptions", "items": { "type": "object", "title": "Description", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Description Text" } }, "required": ["language_code", "text"] } } # Apply JSON editor widgets self.fields['name'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(name_schema), 'title': 'Names' }) self.fields['description'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(description_schema), 'title': 'Descriptions' }) class TransmitterReliabilityAdminForm(forms.ModelForm): """Custom form for TransmitterReliability with JSON editor widgets""" class Meta: model = TransmitterReliability fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Schema for title JSON field title_schema = { "type": "array", "title": "Titles", "items": { "type": "object", "title": "Title", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Title Text" } }, "required": ["language_code", "text"] } } # Apply JSON editor widgets self.fields['title'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(title_schema), 'title': 'Titles' }) class OpinionStatusAdminForm(forms.ModelForm): """Custom form for OpinionStatus with JSON editor widgets""" class Meta: model = OpinionStatus fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Schema for title JSON field title_schema = { "type": "array", "title": "Titles", "items": { "type": "object", "title": "Title", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Title Text" } }, "required": ["language_code", "text"] } } # Apply JSON editor widgets self.fields['title'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(title_schema), 'title': 'Titles' }) class TransmitterOpinionAdminForm(forms.ModelForm): """Custom form for TransmitterOpinion with JSON editor widgets""" class Meta: model = TransmitterOpinion fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Schema for scholar_name JSON field scholar_name_schema = { "type": "array", "title": "Scholar Names", "items": { "type": "object", "title": "Scholar Name", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Scholar Name Text" } }, "required": ["language_code", "text"] } } # Schema for opinion_text JSON field opinion_text_schema = { "type": "array", "title": "Opinion Texts", "items": { "type": "object", "title": "Opinion Text", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Opinion Text" } }, "required": ["language_code", "text"] } } # Apply JSON editor widgets self.fields['scholar_name'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(scholar_name_schema), 'title': 'Scholar Names' }) self.fields['opinion_text'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(opinion_text_schema), 'title': 'Opinion Texts' }) class TransmitterOriginalTextAdminForm(forms.ModelForm): """Custom form for TransmitterOriginalText with JSON editor widgets""" class Meta: model = TransmitterOriginalText fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Schema for title JSON field title_schema = { "type": "array", "title": "Titles", "items": { "type": "object", "title": "Title", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Title Text" } }, "required": ["language_code", "text"] } } # Schema for text JSON field text_schema = { "type": "array", "title": "Texts", "items": { "type": "object", "title": "Text", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Text Content" } }, "required": ["language_code", "text"] } } # Schema for translation JSON field translation_schema = { "type": "array", "title": "Translations", "items": { "type": "object", "title": "Translation", "properties": { "language_code": { "type": "string", "title": "Language Code", "enum": ["en", "fa", "ar", "ur", "ru"], "options": { "enum_titles": ["English", "Persian", "Arabic", "Urdu", "Russian"] } }, "text": { "type": "string", "title": "Translation Text" } }, "required": ["language_code", "text"] } } # Apply JSON editor widgets self.fields['title'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(title_schema), 'title': 'Titles' }) self.fields['text'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(text_schema), 'title': 'Texts' }) self.fields['translation'].widget = JsonEditorWidget(attrs={ 'schema': json.dumps(translation_schema), 'title': 'Translations' }) # Main Admin Classes class NarratorLayerAdmin(ModelAdmin): """Admin for NarratorLayer model""" form = NarratorLayerAdminForm list_display = ('number', 'get_name_display', 'slug', 'created_at') list_filter = ('created_at', 'updated_at') search_fields = ('name', 'slug') readonly_fields = ('slug', 'created_at', 'updated_at') ordering = ('number',) fieldsets = ( (None, { 'fields': ('number', 'name', 'slug') }), (_('Content'), { 'fields': ('description',) }), (_('Timestamps'), { 'fields': ('created_at', 'updated_at'), 'classes': ('collapse',) }), ) @display(description=_('Name'), ordering='name') def get_name_display(self, obj): return self._extract_first_text(obj.name) 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 TransmitterReliabilityAdmin(ModelAdmin): """Admin for TransmitterReliability model""" form = TransmitterReliabilityAdminForm list_display = ('get_title_display', 'slug', 'color') list_filter = ('color',) search_fields = ('title', 'slug') readonly_fields = ('slug',) fieldsets = ( (None, { 'fields': ('title', 'slug', 'color') }), ) @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 OpinionStatusAdmin(ModelAdmin): """Admin for OpinionStatus model""" form = OpinionStatusAdminForm list_display = ('get_title_display', 'slug', 'color') list_filter = ('color',) search_fields = ('title', 'slug') readonly_fields = ('slug',) fieldsets = ( (None, { 'fields': ('title', 'slug', 'color') }), ) @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 TransmitterOpinionAdmin(ModelAdmin): """Admin for TransmitterOpinion model""" form = TransmitterOpinionAdminForm list_display = ('transmitter', 'get_scholar_name_display', 'status', 'created_at') list_filter = ('status', 'created_at', 'transmitter') search_fields = ('transmitter__full_name', 'scholar_name') readonly_fields = ('created_at', 'updated_at') fieldsets = ( (None, { 'fields': ('transmitter', 'scholar_name') }), (_('Content'), { 'fields': ('opinion_text', 'status') }), (_('Timestamps'), { 'fields': ('created_at', 'updated_at'), 'classes': ('collapse',) }), ) @display(description=_('Scholar Name'), ordering='scholar_name') def get_scholar_name_display(self, obj): return self._extract_first_text(obj.scholar_name) 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 TransmitterOriginalTextAdmin(ModelAdmin): """Admin for TransmitterOriginalText model""" form = TransmitterOriginalTextAdminForm list_display = ('transmitter', 'get_title_display', 'slug') list_filter = ('transmitter',) search_fields = ('transmitter__full_name', 'title', 'slug') readonly_fields = ('slug',) fieldsets = ( (None, { 'fields': ('transmitter', 'title', 'slug') }), (_('Content'), { 'fields': ('text', 'translation') }), (_('Additional Information'), { 'fields': ('share_link',), 'classes': ('collapse',) }), ) @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 '-' # Register models with the custom admin site dovoodi_admin_site.register(Transmitters, TransmittersAdmin) dovoodi_admin_site.register(HadisTransmitter, HadisTransmitterAdmin) dovoodi_admin_site.register(NarratorLayer, NarratorLayerAdmin) dovoodi_admin_site.register(TransmitterReliability, TransmitterReliabilityAdmin) dovoodi_admin_site.register(OpinionStatus, OpinionStatusAdmin) dovoodi_admin_site.register(TransmitterOpinion, TransmitterOpinionAdmin) dovoodi_admin_site.register(TransmitterOriginalText, TransmitterOriginalTextAdmin)