From 27c72087d39e77b5cf793d8385a59cb3aa721e64 Mon Sep 17 00:00:00 2001 From: mohsentaba Date: Tue, 23 Dec 2025 09:04:22 +0330 Subject: [PATCH] Transmitters Reliability changed. --- apps/hadis/docs.py | 30 +++- .../migrate_transmitter_reliability.py | 91 ++++++++++++ .../migrations/0068_transmitterreliability.py | 47 ++++++ .../0069_alter_transmitters_reliability.py | 134 ++++++++++++++++++ .../0070_alter_transmitters_reliability.py | 24 ++++ ...70_migrate_transmitter_reliability_data.py | 109 ++++++++++++++ apps/hadis/models/transmitter.py | 65 +++++++-- apps/hadis/serializers/hadis.py | 32 ++++- 8 files changed, 514 insertions(+), 18 deletions(-) create mode 100644 apps/hadis/management/commands/migrate_transmitter_reliability.py create mode 100644 apps/hadis/migrations/0068_transmitterreliability.py create mode 100644 apps/hadis/migrations/0069_alter_transmitters_reliability.py create mode 100644 apps/hadis/migrations/0070_alter_transmitters_reliability.py create mode 100644 apps/hadis/migrations/0070_migrate_transmitter_reliability_data.py diff --git a/apps/hadis/docs.py b/apps/hadis/docs.py index 629b822..8a5f1ed 100644 --- a/apps/hadis/docs.py +++ b/apps/hadis/docs.py @@ -772,7 +772,11 @@ transmitter_list_swagger = swagger_auto_schema( "death_year_hijri": 275, "known_as": "Imam Abu Daud", "nickname": "Al-Sijistani", - "reliability": "very_reliable", + "reliability": { + "id": 7, + "title": "Very Reliable", + "color": "green" + }, "madhhab": "sunni", "generation": 3 }, @@ -784,7 +788,11 @@ transmitter_list_swagger = swagger_auto_schema( "death_year_hijri": 329, "known_as": "Thiqat al-Islam", "nickname": None, - "reliability": "very_reliable", + "reliability": { + "id": 7, + "title": "Very Reliable", + "color": "green" + }, "madhhab": "shia", "generation": 4 } @@ -829,7 +837,11 @@ transmitter_detail_swagger = swagger_auto_schema( "birth_year_hijri": 202, "death_year_hijri": 275, "age_at_death": 73, - "reliability": "very_reliable", + "reliability": { + "id": 7, + "title": "Very Reliable", + "color": "green" + }, "madhhab": "sunni", "in_sahih_bukhari": False, "in_sahih_muslim": True, @@ -878,7 +890,11 @@ transmitter_sync_swagger = swagger_auto_schema( "death_year_hijri": 329, "age_at_death": None, "generation": None, - "reliability": "unknown", + "reliability": { + "id": 7, + "title": "Very Reliable", + "color": "green" + }, "madhhab": "unknown", "in_sahih_muslim": False, "in_sahih_bukhari": False, @@ -935,7 +951,11 @@ transmitter_sync_swagger = swagger_auto_schema( "death_year_hijri": 275, "age_at_death": 73, "generation": 3, - "reliability": "very_reliable", + "reliability": { + "id": 7, + "title": "Very Reliable", + "color": "green" + }, "madhhab": "shafii", "in_sahih_muslim": False, "in_sahih_bukhari": False, diff --git a/apps/hadis/management/commands/migrate_transmitter_reliability.py b/apps/hadis/management/commands/migrate_transmitter_reliability.py new file mode 100644 index 0000000..a54d2e5 --- /dev/null +++ b/apps/hadis/management/commands/migrate_transmitter_reliability.py @@ -0,0 +1,91 @@ +""" +Management command to migrate transmitter reliability data from CharField to ForeignKey. +Run this after the migration 0069_alter_transmitters_reliability has been applied. +""" + +from django.core.management.base import BaseCommand +from apps.hadis.models import Transmitters, TransmitterReliability + + +class Command(BaseCommand): + help = 'Migrate transmitter reliability data from CharField to ForeignKey' + + def handle(self, *args, **options): + self.stdout.write('Starting transmitter reliability data migration...') + + # Get or create reliability objects + reliability_mapping = self.get_reliability_mapping() + + # Update transmitters + updated_count = 0 + for transmitter in Transmitters.objects.all(): + old_value = getattr(transmitter, 'reliability_old', None) + if old_value and old_value in reliability_mapping: + transmitter.reliability = reliability_mapping[old_value] + transmitter.save() + updated_count += 1 + elif not transmitter.reliability: + # Set default for transmitters without reliability + transmitter.reliability = reliability_mapping['unknown'] + transmitter.save() + updated_count += 1 + + self.stdout.write( + self.style.SUCCESS( + f'Successfully migrated {updated_count} transmitters' + ) + ) + + def get_reliability_mapping(self): + """Get mapping of old values to new TransmitterReliability objects""" + mapping = {} + + # Define the reliability levels + reliability_data = [ + ('very_reliable', 'Very Reliable', 'green'), + ('reliable', 'Reliable', 'blue'), + ('acceptable', 'Acceptable', 'yellow'), + ('weak', 'Weak', 'orange'), + ('very_weak', 'Very Weak', 'red'), + ('unknown', 'Unknown', 'gray'), + ] + + for value, title, color in reliability_data: + obj, created = TransmitterReliability.objects.get_or_create( + title__0__text=title, # Check if object exists by title + defaults={ + 'title': [ + {'text': title, 'language_code': 'en'}, + {'text': self.get_persian_title(title), 'language_code': 'fa'}, + {'text': self.get_russian_title(title), 'language_code': 'ru'} + ], + 'color': color + } + ) + mapping[value] = obj + + return mapping + + def get_persian_title(self, english_title): + """Get Persian translation""" + translations = { + 'Very Reliable': 'بسیار قابل اعتماد', + 'Reliable': 'قابل اعتماد', + 'Acceptable': 'قابل قبول', + 'Weak': 'ضعیف', + 'Very Weak': 'بسیار ضعیف', + 'Unknown': 'نامشخص' + } + return translations.get(english_title, english_title) + + def get_russian_title(self, english_title): + """Get Russian translation""" + translations = { + 'Very Reliable': 'Очень надежный', + 'Reliable': 'Надежный', + 'Acceptable': 'Приемлемый', + 'Weak': 'Слабый', + 'Very Weak': 'Очень слабый', + 'Unknown': 'Неизвестный' + } + return translations.get(english_title, english_title) diff --git a/apps/hadis/migrations/0068_transmitterreliability.py b/apps/hadis/migrations/0068_transmitterreliability.py new file mode 100644 index 0000000..c9c8583 --- /dev/null +++ b/apps/hadis/migrations/0068_transmitterreliability.py @@ -0,0 +1,47 @@ +# Generated by Django 4.2.27 on 2025-12-23 08:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0067_bookreference_hadis_bookr_id_1b53f6_idx_and_more"), + ] + + operations = [ + migrations.CreateModel( + name="TransmitterReliability", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("title", models.JSONField(default=list, verbose_name="Title")), + ( + "color", + models.CharField( + choices=[ + ("red", "Red"), + ("green", "Green"), + ("blue", "Blue"), + ("yellow", "Yellow"), + ("orange", "Orange"), + ("purple", "Purple"), + ("gray", "Gray"), + ], + max_length=20, + verbose_name="color", + ), + ), + ], + options={ + "verbose_name": "transmitter reliability", + "verbose_name_plural": "transmitter reliability", + }, + ), + ] diff --git a/apps/hadis/migrations/0069_alter_transmitters_reliability.py b/apps/hadis/migrations/0069_alter_transmitters_reliability.py new file mode 100644 index 0000000..12168d7 --- /dev/null +++ b/apps/hadis/migrations/0069_alter_transmitters_reliability.py @@ -0,0 +1,134 @@ +# Generated by Django 4.2.27 on 2025-12-23 08:32 + +from django.db import migrations, models +import django.db.models.deletion + + +def create_reliability_objects(apps, schema_editor): + """Create TransmitterReliability objects for each reliability level""" + TransmitterReliability = apps.get_model('hadis', 'TransmitterReliability') + + # Define the reliability levels with their data + reliability_data = [ + { + 'title': [ + {'text': 'Very Reliable', 'language_code': 'en'}, + {'text': 'بسیار قابل اعتماد', 'language_code': 'fa'}, + {'text': 'Очень надежный', 'language_code': 'ru'} + ], + 'color': 'green', + 'value': 'very_reliable' + }, + { + 'title': [ + {'text': 'Reliable', 'language_code': 'en'}, + {'text': 'قابل اعتماد', 'language_code': 'fa'}, + {'text': 'Надежный', 'language_code': 'ru'} + ], + 'color': 'blue', + 'value': 'reliable' + }, + { + 'title': [ + {'text': 'Acceptable', 'language_code': 'en'}, + {'text': 'قابل قبول', 'language_code': 'fa'}, + {'text': 'Приемлемый', 'language_code': 'ru'} + ], + 'color': 'yellow', + 'value': 'acceptable' + }, + { + 'title': [ + {'text': 'Weak', 'language_code': 'en'}, + {'text': 'ضعیف', 'language_code': 'fa'}, + {'text': 'Слабый', 'language_code': 'ru'} + ], + 'color': 'orange', + 'value': 'weak' + }, + { + 'title': [ + {'text': 'Very Weak', 'language_code': 'en'}, + {'text': 'بسیار ضعیف', 'language_code': 'fa'}, + {'text': 'Очень слабый', 'language_code': 'ru'} + ], + 'color': 'red', + 'value': 'very_weak' + }, + { + 'title': [ + {'text': 'Unknown', 'language_code': 'en'}, + {'text': 'نامشخص', 'language_code': 'fa'}, + {'text': 'Неизвестный', 'language_code': 'ru'} + ], + 'color': 'gray', + 'value': 'unknown' + } + ] + + reliability_objects = {} + for data in reliability_data: + obj = TransmitterReliability.objects.create( + title=data['title'], + color=data['color'] + ) + reliability_objects[data['value']] = obj + + return reliability_objects + + +def migrate_transmitter_data(apps, schema_editor): + """Migrate existing transmitter reliability data""" + Transmitters = apps.get_model('hadis', 'Transmitters') + + # Create reliability objects + reliability_objects = create_reliability_objects(apps, schema_editor) + + # Update all transmitters to use the new temporary field + for transmitter in Transmitters.objects.all(): + old_value = getattr(transmitter, 'reliability', None) + if old_value and old_value in reliability_objects: + transmitter.reliability_new = reliability_objects[old_value] + else: + # Default to unknown if no value or invalid value + transmitter.reliability_new = reliability_objects['unknown'] + transmitter.save() + + +def reverse_migrate(apps, schema_editor): + """Reverse migration - not needed since we're changing field types""" + pass + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0068_transmitterreliability"), + ] + + operations = [ + # Step 1: Add a temporary ForeignKey field + migrations.AddField( + model_name='transmitters', + name='reliability_new', + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="transmitters_temp", + to="hadis.transmitterreliability", + verbose_name="reliability", + null=True, + ), + ), + # Step 2: Run data migration to populate the new field + migrations.RunPython(migrate_transmitter_data, reverse_migrate), + # Step 3: Remove the old field + migrations.RemoveField( + model_name='transmitters', + name='reliability', + ), + # Step 4: Rename the new field to the final name + migrations.RenameField( + model_name='transmitters', + old_name='reliability_new', + new_name='reliability', + ), + ] diff --git a/apps/hadis/migrations/0070_alter_transmitters_reliability.py b/apps/hadis/migrations/0070_alter_transmitters_reliability.py new file mode 100644 index 0000000..da1e95d --- /dev/null +++ b/apps/hadis/migrations/0070_alter_transmitters_reliability.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.27 on 2025-12-23 08:46 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("hadis", "0069_alter_transmitters_reliability"), + ] + + operations = [ + migrations.AlterField( + model_name="transmitters", + name="reliability", + field=models.ForeignKey( + default=12, + on_delete=django.db.models.deletion.CASCADE, + related_name="transmitters", + to="hadis.transmitterreliability", + verbose_name="reliability", + ), + ), + ] diff --git a/apps/hadis/migrations/0070_migrate_transmitter_reliability_data.py b/apps/hadis/migrations/0070_migrate_transmitter_reliability_data.py new file mode 100644 index 0000000..0f990d6 --- /dev/null +++ b/apps/hadis/migrations/0070_migrate_transmitter_reliability_data.py @@ -0,0 +1,109 @@ +# Migration to handle data conversion for transmitter reliability field +from django.db import migrations + + +def create_reliability_objects(apps, schema_editor): + """Create TransmitterReliability objects for each reliability level""" + TransmitterReliability = apps.get_model('hadis', 'TransmitterReliability') + + # Define the reliability levels with their data + reliability_data = [ + { + 'title': [ + {'text': 'Very Reliable', 'language_code': 'en'}, + {'text': 'بسیار قابل اعتماد', 'language_code': 'fa'}, + {'text': 'Очень надежный', 'language_code': 'ru'} + ], + 'color': 'green', + 'value': 'very_reliable' + }, + { + 'title': [ + {'text': 'Reliable', 'language_code': 'en'}, + {'text': 'قابل اعتماد', 'language_code': 'fa'}, + {'text': 'Надежный', 'language_code': 'ru'} + ], + 'color': 'blue', + 'value': 'reliable' + }, + { + 'title': [ + {'text': 'Acceptable', 'language_code': 'en'}, + {'text': 'قابل قبول', 'language_code': 'fa'}, + {'text': 'Приемлемый', 'language_code': 'ru'} + ], + 'color': 'yellow', + 'value': 'acceptable' + }, + { + 'title': [ + {'text': 'Weak', 'language_code': 'en'}, + {'text': 'ضعیف', 'language_code': 'fa'}, + {'text': 'Слабый', 'language_code': 'ru'} + ], + 'color': 'orange', + 'value': 'weak' + }, + { + 'title': [ + {'text': 'Very Weak', 'language_code': 'en'}, + {'text': 'بسیار ضعیف', 'language_code': 'fa'}, + {'text': 'Очень слабый', 'language_code': 'ru'} + ], + 'color': 'red', + 'value': 'very_weak' + }, + { + 'title': [ + {'text': 'Unknown', 'language_code': 'en'}, + {'text': 'نامشخص', 'language_code': 'fa'}, + {'text': 'Неизвестный', 'language_code': 'ru'} + ], + 'color': 'gray', + 'value': 'unknown' + } + ] + + reliability_objects = {} + for data in reliability_data: + obj = TransmitterReliability.objects.create( + title=data['title'], + color=data['color'] + ) + reliability_objects[data['value']] = obj + + return reliability_objects + + +def migrate_transmitter_data(apps, schema_editor): + """Migrate existing transmitter reliability data""" + Transmitters = apps.get_model('hadis', 'Transmitters') + + # Create reliability objects + reliability_objects = create_reliability_objects(apps, schema_editor) + + # Update all transmitters to use the new foreign key references + for transmitter in Transmitters.objects.all(): + old_value = getattr(transmitter, 'reliability', None) + if old_value and old_value in reliability_objects: + transmitter.reliability = reliability_objects[old_value] + transmitter.save(update_fields=['reliability']) + elif old_value == 'unknown' or not old_value: + # Default to unknown if no value or unknown + transmitter.reliability = reliability_objects.get('unknown') + transmitter.save(update_fields=['reliability']) + + +def reverse_migrate(apps, schema_editor): + """Reverse migration - not needed since we're changing field types""" + pass + + +class Migration(migrations.Migration): + dependencies = [ + ('hadis', '0069_alter_transmitters_reliability'), + ] + + operations = [ + migrations.RunPython(migrate_transmitter_data, reverse_migrate), + ] diff --git a/apps/hadis/models/transmitter.py b/apps/hadis/models/transmitter.py index ff66636..3e7371a 100644 --- a/apps/hadis/models/transmitter.py +++ b/apps/hadis/models/transmitter.py @@ -1,5 +1,6 @@ +from tabnanny import verbose from django.db import models from django.utils.translation import gettext_lazy as _ from filer.fields.image import FilerImageField @@ -71,15 +72,54 @@ 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') + + title = models.JSONField(default = list , verbose_name=_('Title')) + + color = models.CharField(max_length=20, choices=ColorChoices.choices, verbose_name=_('color')) + + 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 = _('transmitter reliability') + verbose_name_plural = _('transmitter reliability') + + class Transmitters(models.Model): - class ReliabilityLevel(models.TextChoices): - VERY_RELIABLE = 'very_reliable', _('Very Reliable') - RELIABLE = 'reliable', _('Reliable') - ACCEPTABLE = 'acceptable', _('Acceptable') - WEAK = 'weak', _('Weak') - VERY_WEAK = 'very_weak', _('Very Weak') - UNKNOWN = 'unknown', _('Unknown') + # class ReliabilityLevel(models.TextChoices): + # VERY_RELIABLE = 'very_reliable', _('Very Reliable') + # RELIABLE = 'reliable', _('Reliable') + # ACCEPTABLE = 'acceptable', _('Acceptable') + # WEAK = 'weak', _('Weak') + # VERY_WEAK = 'very_weak', _('Very Weak') + # UNKNOWN = 'unknown', _('Unknown') class MadhhabChoices(models.TextChoices): SHIA = 'shia', _('Shia') @@ -109,11 +149,12 @@ class Transmitters(models.Model): age_at_death = models.PositiveIntegerField(verbose_name=_('Age at Death'), blank=True, null=True) generation = models.PositiveIntegerField(verbose_name=_('Generation'), blank=True, null=True) # Religious & Academic Information - reliability = models.CharField( - max_length=20, - choices=ReliabilityLevel.choices, - default=ReliabilityLevel.UNKNOWN, - verbose_name=_('Reliability Level') + reliability = models.ForeignKey( + TransmitterReliability, + on_delete=models.CASCADE, + verbose_name=_('reliability'), + related_name='transmitters', + default=12 # ID of 'Unknown' reliability ) madhhab = models.CharField( max_length=20, diff --git a/apps/hadis/serializers/hadis.py b/apps/hadis/serializers/hadis.py index 22b8755..66b6724 100644 --- a/apps/hadis/serializers/hadis.py +++ b/apps/hadis/serializers/hadis.py @@ -258,6 +258,7 @@ class TransmitterSerializer(serializers.ModelSerializer): full_name = LocalizedField() known_as = LocalizedField() nickname = LocalizedField() + reliability = serializers.SerializerMethodField() class Meta: model = Transmitters @@ -266,6 +267,16 @@ class TransmitterSerializer(serializers.ModelSerializer): "known_as",'nickname','reliability','madhhab','generation' ] + def get_reliability(self, obj): + """Serialize the reliability foreign key""" + if obj.reliability: + return { + 'id': obj.reliability.id, + 'title': get_localized_text(obj.reliability.title, self.context.get('request')), + 'color': obj.reliability.color + } + return None + class TransmitterShortSerializer(serializers.ModelSerializer): """Serializer for Transmitters""" full_name = LocalizedField() @@ -308,6 +319,7 @@ class TransmitterDetailSerializer(serializers.ModelSerializer): lived_in = LocalizedField() died_in = LocalizedField() description = LocalizedField() + reliability = serializers.SerializerMethodField() class Meta: model = Transmitters @@ -319,6 +331,16 @@ class TransmitterDetailSerializer(serializers.ModelSerializer): "description",'generation' ] + def get_reliability(self, obj): + """Serialize the reliability foreign key""" + if obj.reliability: + return { + 'id': obj.reliability.id, + 'title': get_localized_text(obj.reliability.title, self.context.get('request')), + 'color': obj.reliability.color + } + return None + class TransmitterSyncSerializer(serializers.ModelSerializer): """Serializer for syncing all transmitter data for offline mode""" @@ -337,6 +359,14 @@ class TransmitterSyncSerializer(serializers.ModelSerializer): def get_biographical(self, obj): """Get biographical information (flattened)""" request = self.context.get('request') # ← FIX: Define request + if obj.reliability: + r= { + 'id': obj.reliability.id, + 'title': get_localized_text(obj.reliability.title, self.context.get('request')), + 'color': obj.reliability.color + } + else : + r= None return { 'full_name': get_localized_text(obj.full_name, request), @@ -350,7 +380,7 @@ class TransmitterSyncSerializer(serializers.ModelSerializer): 'death_year_hijri': obj.death_year_hijri, 'age_at_death': obj.age_at_death, 'generation': obj.generation, - 'reliability': obj.reliability, + 'reliability': r, 'madhhab': obj.madhhab, 'in_sahih_muslim': obj.in_sahih_muslim, 'in_sahih_bukhari': obj.in_sahih_bukhari,