diff --git a/apps/hadis/migrations/0007_alter_hadisstatus_color_alter_opinionstatus_color_and_more.py b/apps/hadis/migrations/0007_alter_hadisstatus_color_alter_opinionstatus_color_and_more.py new file mode 100644 index 0000000..8e4b9bc --- /dev/null +++ b/apps/hadis/migrations/0007_alter_hadisstatus_color_alter_opinionstatus_color_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.27 on 2026-02-18 14:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('hadis', '0006_hadis_embedded_in_hadiscorrection_embedded_in_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='hadisstatus', + name='color', + field=models.CharField(choices=[('red', 'Red'), ('green', 'Green'), ('blue', 'Blue'), ('yellow', 'Yellow'), ('orange', 'Orange'), ('purple', 'Purple'), ('gray', 'Gray')], default='gray', max_length=20, verbose_name='color'), + ), + migrations.AlterField( + model_name='opinionstatus', + name='color', + field=models.CharField(choices=[('red', 'Red'), ('green', 'Green'), ('blue', 'Blue'), ('yellow', 'Yellow'), ('orange', 'Orange'), ('purple', 'Purple'), ('gray', 'Gray')], default='gray', max_length=20, verbose_name='color'), + ), + migrations.AlterField( + model_name='transmitterreliability', + name='color', + field=models.CharField(choices=[('red', 'Red'), ('green', 'Green'), ('blue', 'Blue'), ('yellow', 'Yellow'), ('orange', 'Orange'), ('purple', 'Purple'), ('gray', 'Gray')], default='gray', max_length=20, verbose_name='color'), + ), + ] diff --git a/apps/hadis/models/hadis.py b/apps/hadis/models/hadis.py index 511f66e..11ab341 100644 --- a/apps/hadis/models/hadis.py +++ b/apps/hadis/models/hadis.py @@ -7,6 +7,7 @@ from django.conf import settings from django.utils.text import slugify from .reference import BookReference from utils.slug import generate_smart_slug +from utils.mixins import ColorPaletteMixin class HadisCollection(models.Model): title = models.JSONField(default = list , verbose_name=_('Title')) @@ -162,45 +163,13 @@ class HadisTag(models.Model): 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') - - COLOR_PALETTE = { - 'red': {'main': '#D33A3A', 'light': '#FBEBEB'}, - 'green': {'main': '#1DAC43', 'light': '#E8F7EC'}, - 'blue': {'main': '#5172E1', 'light': '#FAFBFC'}, - 'yellow': {'main': '#EDC130', 'light': '#FCF8EA'}, - 'orange': {'main': '#E67E22', 'light': '#FDF1E6'}, - 'purple': {'main': '#7C5CC4', 'light': '#F2EDFA'}, - 'gray': {'main': '#374151', 'light': '#EEEFF2'}, - } +class HadisStatus(ColorPaletteMixin,models.Model): title = models.JSONField(default = list , verbose_name=_('Title')) 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')) + # 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')) - - @property - def color_hashes(self): - """Returns the dictionary of hash codes based on the selected color""" - return self.COLOR_PALETTE.get(self.color, {'main': '#000000', 'light': '#FFFFFF'}) - - # Helper for specific fields if you need them flat - @property - def main_color_code(self): - return self.color_hashes['main'] - - @property - def light_color_code(self): - return self.color_hashes['light'] def save(self, *args, **kwargs): if not self.slug or (isinstance(self.slug, str) and self.slug.strip() == ''): diff --git a/apps/hadis/models/transmitter.py b/apps/hadis/models/transmitter.py index 2d26aa4..cf510fa 100644 --- a/apps/hadis/models/transmitter.py +++ b/apps/hadis/models/transmitter.py @@ -7,6 +7,7 @@ from django.utils.text import slugify from typing import Optional from utils.slug import generate_smart_slug from django.conf import settings +from utils.mixins import ColorPaletteMixin @@ -120,20 +121,11 @@ class NarratorLayer(models.Model): self.slug = slug super().save(*args, **kwargs) -class TransmitterReliability(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') +class TransmitterReliability(ColorPaletteMixin, models.Model): title = models.JSONField(default = list , verbose_name=_('Title')) slug = models.SlugField(max_length=255, verbose_name=_('slug'), blank=True,null=True) - color = models.CharField(max_length=20, choices=ColorChoices.choices, verbose_name=_('color')) def save(self, *args, **kwargs): if not self.slug or (isinstance(self.slug, str) and self.slug.strip() == ''): @@ -471,20 +463,10 @@ class HadisTransmitter(models.Model): return f'{self.hadis.number} - {self.transmitter.full_name} ({self.order}){layer_info}' -class OpinionStatus(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') +class OpinionStatus(ColorPaletteMixin, models.Model): title = models.JSONField(default = list , verbose_name=_('Title')) slug = models.SlugField(max_length=255, verbose_name=_('slug'), blank=True,null=True) - - color = models.CharField(max_length=20, choices=ColorChoices.choices, verbose_name=_('color')) def save(self, *args, **kwargs): if not self.slug or (isinstance(self.slug, str) and self.slug.strip() == ''): diff --git a/apps/hadis/serializers/hadis.py b/apps/hadis/serializers/hadis.py index d26bd6c..5ccd6dd 100644 --- a/apps/hadis/serializers/hadis.py +++ b/apps/hadis/serializers/hadis.py @@ -64,6 +64,8 @@ class HadisSyncSerializer(serializers.ModelSerializer): 'id': obj.hadis_status.id, 'title': get_localized_text(obj.hadis_status.title, request), 'color': obj.hadis_status.color, + 'main_color_code': obj.hadis_status.main_color_code, + 'light_color_code': obj.hadis_status.light_color_code, } # tags (already prefetched) @@ -229,7 +231,9 @@ class HadisListSerializer(serializers.ModelSerializer): return { 'id': obj.hadis_status.id, 'title': title, - 'color': obj.hadis_status.color + 'color': obj.hadis_status.color, + 'main_color_code': obj.hadis_status.main_color_code, + 'light_color_code': obj.hadis_status.light_color_code, } def get_bookmark(self, obj): @@ -284,7 +288,10 @@ class TransmitterSerializer(serializers.ModelSerializer): return { 'id': obj.reliability.id, 'title': get_localized_text(obj.reliability.title, self.context.get('request')), - 'color': obj.reliability.color + 'color': obj.reliability.color, + 'main_color_code': obj.reliability.main_color_code, + 'light_color_code': obj.reliability.light_color_code, + } return None @@ -318,7 +325,9 @@ class TransmitterOpinionSerializer(serializers.ModelSerializer): 'id': obj.status.id, 'title': get_localized_text(obj.status.title, request), 'slug': obj.status.slug, - 'color': obj.status.color + 'color': obj.status.color, + 'main_color_code': obj.status.main_color_code, + 'light_color_code': obj.status.light_color_code, } return None @@ -363,7 +372,10 @@ class TransmitterDetailSerializer(serializers.ModelSerializer): return { 'id': obj.reliability.id, 'title': get_localized_text(obj.reliability.title, self.context.get('request')), - 'color': obj.reliability.color + 'color': obj.reliability.color, + 'main_color_code': obj.reliability.main_color_code, + 'light_color_code': obj.reliability.light_color_code, + } return None @@ -391,7 +403,9 @@ class TransmitterSyncSerializer(serializers.ModelSerializer): r= { 'id': obj.reliability.id, 'title': get_localized_text(obj.reliability.title, self.context.get('request')), - 'color': obj.reliability.color + 'color': obj.reliability.color, + 'main_color_code': obj.reliability.main_color_code, + 'light_color_code': obj.reliability.light_color_code, } else : r= None @@ -429,7 +443,9 @@ class TransmitterSyncSerializer(serializers.ModelSerializer): 'id': opinion.status.id, 'title': get_localized_text(opinion.status.title, request), 'slug': opinion.status.slug, - 'color': opinion.status.color + 'color': opinion.status.color, + 'main_color_code': opinion.status.main_color_code, + 'light_color_code': opinion.status.light_color_code, } if opinion.status else None, 'created_at': opinion.created_at.isoformat() if opinion.created_at else None, 'updated_at': opinion.updated_at.isoformat() if opinion.updated_at else None, @@ -480,7 +496,9 @@ class HadisTransmitterSerializer(serializers.ModelSerializer): 'id': obj.status.id, 'title': get_localized_text(obj.status.title, request), 'slug': obj.status.slug, - 'color': obj.status.color + 'color': obj.status.color, + 'main_color_code': obj.status.main_color_code, + 'light_color_code': obj.status.light_color_code, } return None diff --git a/apps/hadis/urls.py b/apps/hadis/urls.py index ae7c6c4..ee724d5 100644 --- a/apps/hadis/urls.py +++ b/apps/hadis/urls.py @@ -38,9 +38,9 @@ urlpatterns = [ path('arguments/filters/', cached_view(HadisFiltersView.as_view()), name='hadis-filters'), # Narrator paths - path('narrators//opinions', cached_view(TransmitterOpinionView.as_view()), name='narrator-opinions'), + path('narrators//opinions', TransmitterOpinionView.as_view(), name='narrator-opinions'), path('narrators//original_texts', cached_view(TransmitterOriginalTextView.as_view()), name='narrator-original-texts'), - path('narrators/', cached_view(TransmitterDetailView.as_view()), name='narrator-detail'), + path('narrators/', TransmitterDetailView.as_view(), name='narrator-detail'), path('narrators/filters/', cached_view(TransmitterFiltersView.as_view()), name='narrator-filters'), path('narrators/', cached_view(TransmitterView.as_view()), name='narrators'), diff --git a/utils/mixins.py b/utils/mixins.py new file mode 100644 index 0000000..5824158 --- /dev/null +++ b/utils/mixins.py @@ -0,0 +1,50 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ + +# 1. Define choices globally so they can be reused +class StatusColorChoices(models.TextChoices): + RED = 'red', _('Red') + GREEN = 'green', _('Green') + BLUE = 'blue', _('Blue') + YELLOW = 'yellow', _('Yellow') + ORANGE = 'orange', _('Orange') + PURPLE = 'purple', _('Purple') + GRAY = 'gray', _('Gray') + +# 2. Create the Abstract Mixin +class ColorPaletteMixin(models.Model): + """ + An abstract base class that provides color choice fields + and hex code properties to any model that inherits it. + """ + COLOR_PALETTE = { + 'red': {'main': '#D33A3A', 'light': '#FBEBEB'}, + 'green': {'main': '#1DAC43', 'light': '#E8F7EC'}, + 'blue': {'main': '#5172E1', 'light': '#FAFBFC'}, + 'yellow': {'main': '#EDC130', 'light': '#FCF8EA'}, + 'orange': {'main': '#E67E22', 'light': '#FDF1E6'}, + 'purple': {'main': '#7C5CC4', 'light': '#F2EDFA'}, + 'gray': {'main': '#374151', 'light': '#EEEFF2'}, + } + + color = models.CharField( + max_length=20, + choices=StatusColorChoices.choices, + verbose_name=_('color'), + default=StatusColorChoices.GRAY + ) + + class Meta: + abstract = True # Tells Django NOT to create a database table for this class + + @property + def color_hashes(self): + return self.COLOR_PALETTE.get(self.color, {'main': '#000000', 'light': '#FFFFFF'}) + + @property + def main_color_code(self): + return self.color_hashes['main'] + + @property + def light_color_code(self): + return self.color_hashes['light'] \ No newline at end of file