Browse Source

Refactor Hadis and Transmitter models to utilize ColorPaletteMixin for color management

- Introduced `ColorPaletteMixin` to centralize color choice fields and hex code properties across `HadisStatus`, `TransmitterReliability`, and `OpinionStatus` models.
- Updated the `color` field in these models to use the mixin, enhancing code reusability and maintainability.
- Modified serializers to include `main_color_code` and `light_color_code` properties for better color representation in API responses.
- Added a new migration to adjust the database schema for the updated models.
master
Mohsen Taba 3 months ago
parent
commit
984254b5b6
  1. 28
      apps/hadis/migrations/0007_alter_hadisstatus_color_alter_opinionstatus_color_and_more.py
  2. 37
      apps/hadis/models/hadis.py
  3. 24
      apps/hadis/models/transmitter.py
  4. 32
      apps/hadis/serializers/hadis.py
  5. 4
      apps/hadis/urls.py
  6. 50
      utils/mixins.py

28
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'),
),
]

37
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,46 +163,14 @@ 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() == ''):
# Try to get text from title field with robust error handling

24
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,21 +463,11 @@ 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() == ''):
# Try to get text from title field with robust error handling

32
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

4
apps/hadis/urls.py

@ -38,9 +38,9 @@ urlpatterns = [
path('arguments/filters/', cached_view(HadisFiltersView.as_view()), name='hadis-filters'),
# Narrator paths
path('narrators/<str:narrator_slug>/opinions', cached_view(TransmitterOpinionView.as_view()), name='narrator-opinions'),
path('narrators/<str:narrator_slug>/opinions', TransmitterOpinionView.as_view(), name='narrator-opinions'),
path('narrators/<str:narrator_slug>/original_texts', cached_view(TransmitterOriginalTextView.as_view()), name='narrator-original-texts'),
path('narrators/<str:narrator_slug>', cached_view(TransmitterDetailView.as_view()), name='narrator-detail'),
path('narrators/<str:narrator_slug>', 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'),

50
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']
Loading…
Cancel
Save