from typing import Optional from django.db import models from django.db.models import F, ForeignKey from django.utils.translation import gettext_lazy as _ from django.conf import settings from django.utils.text import slugify from filer.fields.image import FilerImageField from .reference import BookReference class HadisCollection(models.Model): title = models.JSONField(default = list , verbose_name=_('Title')) slug = models.SlugField(max_length=255, unique=True, verbose_name=_('slug'), blank=True) summary = models.JSONField(default = list , verbose_name=_('Summary')) status = models.BooleanField(default=True, verbose_name=_('status')) order = models.IntegerField(default=0, verbose_name=_('order')) thumbnail = FilerImageField( related_name="+", on_delete=models.CASCADE, help_text=_('thumbnail image'), null=True, blank=True, verbose_name=_('thumbnail') ) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at')) def save(self, *args, **kwargs): if not self.slug: base_slug = slugify(self.title[0]['text'], allow_unicode=True) slug = base_slug counter = 1 while HadisCollection.objects.filter(slug=slug).exclude(pk=self.pk).exists(): slug = f"{base_slug}-{counter}" counter += 1 self.slug = slug super().save(*args, **kwargs) def __str__(self): return 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 def get_summary(self,lang): """ Get translation for a specific language """ if not self.summary or not isinstance(self.summary, list): return None for tr in self.summary: if isinstance(tr, dict) and tr.get('language_code') == lang: return tr.get('text', '') for tr in self.summary: if isinstance(tr, dict) and tr.get('language_code') == 'en': return tr.get('text', '') return None class Meta: verbose_name = _('hadis collection') verbose_name_plural = _('hadis collections') ordering = ('order',) class HadisInCollection(models.Model): hadis = models.ForeignKey('Hadis', on_delete=models.CASCADE, verbose_name=_('hadis'), related_name='collection_items') collection = models.ForeignKey(HadisCollection, on_delete=models.CASCADE, verbose_name=_('collection'), related_name='hadis_items') order = models.IntegerField(default=0, verbose_name=_('order')) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) class Meta: verbose_name = _('hadis in collection') verbose_name_plural = _('hadis in collections') ordering = ('order',) unique_together = ('hadis', 'collection') def __str__(self): return f"{self.collection.title[0]['text']} - {self.hadis.number}" class HadisTag(models.Model): title = models.JSONField(default = list , verbose_name=_('Title')) status = models.BooleanField(default=True, verbose_name=_('status')) 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 HadisStatus(models.Model): class ColorChoices(models.TextChoices): RED = 'red', _('Red') GREEN = 'green', _('Green') BLUE = 'blue', _('Blue') YELLOW = 'yellow', _('Yellow') ORANGE = 'orange', _('Orange') PURPLE = 'purple', _('Purple') GRAY = 'gray', _('Gray') title = models.JSONField(default = list , verbose_name=_('Title')) color = models.CharField(max_length=20, choices=ColorChoices.choices, verbose_name=_('color')) order = models.IntegerField(default=0, verbose_name=_('order')) def __str__(self): return 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 Meta: verbose_name = _('hadis status') verbose_name_plural = _('hadis statuses') ordering = ('order',) class Hadis(models.Model): category = models.ForeignKey("hadis.HadisCategory", on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_('category')) number = models.PositiveIntegerField(verbose_name=_('number'), default=1) title_narrator = models.JSONField(default = list , verbose_name=_('Title Narrator')) title = models.JSONField(default = list , verbose_name=_('Title')) description = models.JSONField(default = list , verbose_name=_('Description')) text = models.TextField(verbose_name=_('text')) translation = models.JSONField(verbose_name=_('translation'), default=list) status = models.BooleanField(default=True, verbose_name=_('visibility')) hadis_status = models.ForeignKey(HadisStatus, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_('hadis status')) hadis_status_text = models.JSONField(default = list , verbose_name=_('Status text')) address = models.JSONField(default = list , verbose_name=_('Address')) links = models.JSONField(verbose_name=_('links'), null=True, blank=True, default=dict) tags = models.ManyToManyField("HadisTag", related_name="hadis_overview", verbose_name=_('tags'), blank=True) share_link = models.CharField(max_length=255, verbose_name=_('share link'), null=True, blank=True) explanation = models.JSONField(default = list , verbose_name=_('Explanation')) 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.number} - {self.title[0]['text']}" if self.title else f"Hadis {self.number}" def _get_json_field(self, field_name: str, lang: Optional[str]=None , fallback: str = "en"): """ Generic getter for JSONField in our [{text, language_code}] format. Usage: self._get_json_field('title', 'fa') """ if lang is None: lang = fallback value = getattr(self, field_name, None) if not value or not isinstance(value, list): return None # 1) exact language for item in value: if isinstance(item, dict) and item.get("language_code") == lang: return item.get("text", "") # 2) fallback language if fallback and fallback != lang: for item in value: if isinstance(item, dict) and item.get("language_code") == fallback: return item.get("text", "") # 3) first available item = value[0] print(item) return item.get("text", "") if isinstance(item, dict) else None def get_translation(self, lang): return self._get_json_field("translation" , lang) def get_title(self,lang): return self._get_json_field("title" , lang) def get_description(self, lang): return self._get_json_field("description" , lang) def get_hadis_status_text(self, lang): return self._get_json_field("hadis_status_text" , lang) def get_address(self, lang): return self._get_json_field("address" , lang) def get_explanation(self, lang): return self._get_json_field("explanation" , lang) # """ # Get translation for a specific language # """ # if not self.translation or not isinstance(self.translation, list): # return None # for tr in self.translation: # if isinstance(tr, dict) and tr.get('language_code') == lang: # return tr.get('title', '') # for tr in self.translation: # if isinstance(tr, dict) and tr.get('language_code') == 'en': # return tr.get('title', '') # return None def save(self, *args, **kwargs): # ساخت share_link قبل از ذخیره if not self.share_link: self.share_link = f"{settings.SITE_DOMAIN}/hadis/{self.id}" super().save(*args, **kwargs) class Meta: verbose_name = _('hadis') verbose_name_plural = _('hadises') ordering = ('category', 'number') class HadisReference(models.Model): hadis = models.ForeignKey( Hadis, on_delete=models.CASCADE, verbose_name=_('hadis'), related_name='references' ) book_reference = models.ForeignKey( BookReference, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_('book reference'), related_name='hadis_references' ) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at')) description = models.JSONField(default = list , verbose_name=_('Description')) class Meta: verbose_name = _('Hadis Reference') verbose_name_plural = _('Hadis References') # unique_together = ('hadis', 'book_reference') def get_description(self,lang): """ Get title for a specific language """ if not self.description or not isinstance(self.description, list): return None for tr in self.description: if isinstance(tr, dict) and tr.get('language_code') == lang: return tr.get('text', '') for tr in self.description: if isinstance(tr, dict) and tr.get('language_code') == 'en': return tr.get('text', '') return None def __str__(self): return f'{self.hadis.number}-{self.book_reference.title[0]["text"] if self.book_reference else "No Book Reference"}' class ReferenceImage(models.Model): reference = models.ForeignKey(HadisReference,related_name = 'images', verbose_name="Hadis Reference", on_delete=models.CASCADE) thumbnail = models.ImageField(upload_to='hadis/reference_images/', null=True, blank=True, verbose_name=_('thumbnail')) priority = models.IntegerField( default=0, verbose_name=_("Priority"), help_text=_("Priority of the image, lower values mean higher priority.") ) class Meta: verbose_name = _('Reference Image') verbose_name_plural = _('Reference Images') def __str__(self): return f'{self.reference.title[0]["text"]}-{self.id}' def save(self, *args, **kwargs): if ReferenceImage.objects.filter(reference=self.reference, priority=self.priority).exists(): ReferenceImage.objects.filter( reference=self.reference, priority__gte=self.priority ).update(priority=F('priority') + 1) super().save(*args, **kwargs) class HadisCorrection(models.Model): hadis = models.ForeignKey(Hadis, verbose_name=_("hadis correction"), on_delete=models.CASCADE) title = models.JSONField(default = list , verbose_name=_('Title')) description =models.JSONField(default = list , verbose_name=_('Description')) translation = models.JSONField(verbose_name=_("translation"), default=list) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("created at")) updated_at = models.DateTimeField(auto_now=True, verbose_name=_("updated at")) share_link = models.CharField(max_length=255, verbose_name=_('share link'), null=True, blank=True) class Meta: verbose_name = _("Hadis Correction") verbose_name_plural = _("Hadis Corrections") ordering = ("-created_at",) def __str__(self): return f"{self.hadis.number} - {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 def get_description(self,lang): """ Get title for a specific language """ if not self.description or not isinstance(self.description, list): return None for tr in self.description: if isinstance(tr, dict) and tr.get('language_code') == lang: return tr.get('text', '') for tr in self.description: if isinstance(tr, dict) and tr.get('language_code') == 'en': return tr.get('text', '') return None def get_translation(self,lang): """ Get title for a specific language """ if not self.translation or not isinstance(self.translation, list): return None for tr in self.translation: if isinstance(tr, dict) and tr.get('language_code') == lang: return tr.get('text', '') for tr in self.translation: if isinstance(tr, dict) and tr.get('language_code') == 'en': return tr.get('text', '') return None