From 69e80ada378a7269b389db4ffefc35d03bed2a26 Mon Sep 17 00:00:00 2001 From: mohsentaba Date: Tue, 6 Jan 2026 15:32:23 +0330 Subject: [PATCH] fixed referene image script --- apps/hadis/apps.py | 6 +- .../commands/populate_refrence_images.py | 144 +++++++++++------- entrypoint.sh | 2 +- 3 files changed, 96 insertions(+), 56 deletions(-) diff --git a/apps/hadis/apps.py b/apps/hadis/apps.py index 4a21e14..cd72637 100644 --- a/apps/hadis/apps.py +++ b/apps/hadis/apps.py @@ -4,6 +4,6 @@ from django.apps import AppConfig class HadisConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'apps.hadis' - def ready(self): - # Import the signals module when the app starts - import apps.hadis.signals + # def ready(self): + # # Import the signals module when the app starts + # # import apps.hadis.signals diff --git a/apps/hadis/management/commands/populate_refrence_images.py b/apps/hadis/management/commands/populate_refrence_images.py index ec2d78e..29f7643 100644 --- a/apps/hadis/management/commands/populate_refrence_images.py +++ b/apps/hadis/management/commands/populate_refrence_images.py @@ -2,22 +2,21 @@ import os import random from django.core.management.base import BaseCommand from django.core.files import File +from django.core.files.base import ContentFile from django.conf import settings +from django.db import transaction + # REMEMBER: Change 'your_app' to your actual app name from apps.hadis.models.hadis import Hadis, HadisReference, ReferenceImage from apps.hadis.models.reference import BookReference class Command(BaseCommand): - help = 'Wipes old reference data and seeds new HadisReference and ReferenceImages' + help = 'Seeds HadisReferences ensuring every Book has 2+ hadiths AND every Hadith has 1+ reference.' def handle(self, *args, **kwargs): self.stdout.write(self.style.WARNING('--- Starting Cleanup ---')) # 1. DELETE PREVIOUS DATA - # We delete HadisReference. Because of on_delete=models.CASCADE in ReferenceImage, - # deleting the reference will automatically delete the associated images. - # However, we can delete both explicitly to be sure and show counts. - deleted_imgs_count, _ = ReferenceImage.objects.all().delete() deleted_refs_count, _ = HadisReference.objects.all().delete() @@ -25,69 +24,110 @@ class Command(BaseCommand): self.stdout.write(self.style.SUCCESS(f'Deleted {deleted_refs_count} old HadisReferences.')) self.stdout.write('------------------------') - # 2. Setup Paths for Seeding - base_image_path = os.path.join(settings.BASE_DIR, 'seeds', 'images') - # We look for ref1.png, ref2.png, ref3.png, ref4.png - image_files = [f'ref{i}.png' for i in range(1, 5)] + # 2. Setup Paths + self.base_image_path = os.path.join(settings.BASE_DIR, 'seeds', 'images') + self.image_files = [f'ref{i}.png' for i in range(1, 5)] - # Verify images exist locally before starting - for img_name in image_files: - full_path = os.path.join(base_image_path, img_name) + # Verify images + for img_name in self.image_files: + full_path = os.path.join(self.base_image_path, img_name) if not os.path.exists(full_path): self.stdout.write(self.style.ERROR(f'CRITICAL ERROR: Image not found at {full_path}')) return - # 3. Fetch Parents + # 3. Fetch Data books = list(BookReference.objects.all()) hadiths = list(Hadis.objects.all()) - if len(hadiths) < 2: - self.stdout.write(self.style.ERROR('Not enough Hadiths in DB (need at least 2).')) - return - - if not books: - self.stdout.write(self.style.ERROR('No BookReferences found in DB.')) + if not books or not hadiths: + self.stdout.write(self.style.ERROR('Missing Books or Hadiths in DB.')) return self.stdout.write('--- Starting Seeding ---') - counter_refs = 0 - counter_imgs = 0 + self.counter_refs = 0 + self.counter_imgs = 0 + + # Track which Hadith IDs have been covered + covered_hadith_ids = set() - # 4. Iterate Books (Constraint 1: Each book must relate to at least 2 hadith) + # ========================================== + # PHASE 1: Satisfy Book Constraint + # "Each book must relate to at least 2 hadith" + # ========================================== + self.stdout.write("Phase 1: Linking Books to Hadiths...") + for book in books: # Pick 2 random hadiths for this book - selected_hadiths = random.sample(hadiths, 2) + # We use distinct logic if we want to ensure we don't pick the same one twice for one book + selected_hadiths = random.sample(hadiths, k=min(len(hadiths), 2)) for hadis_obj in selected_hadiths: - # Create the Link (HadisReference) - hadis_ref = HadisReference.objects.create( - hadis=hadis_obj, - book_reference=book, - description=[ - {'language_code': 'en', 'text': f'Reference for {book.title[0].get("text", "Book")} - Hadith {hadis_obj.number}'}, - {'language_code': 'ar', 'text': 'وصف مرجعي'} - ] - ) - counter_refs += 1 - - # 5. Connect Images (Constraint 2: Each hadith ref must have 2 images) - # We pick 2 random images from our list of 4 - selected_images = random.sample(image_files, 2) - - for priority_index, img_name in enumerate(selected_images): - img_path = os.path.join(base_image_path, img_name) - - with open(img_path, 'rb') as f: - # Create ReferenceImage - ref_img = ReferenceImage( - reference=hadis_ref, - priority=priority_index # 0 and 1 - ) - # Save file to the ImageField - ref_img.thumbnail.save(img_name, File(f), save=True) - counter_imgs += 1 + self._create_reference_chain(hadis_obj, book) + covered_hadith_ids.add(hadis_obj.id) + + # ========================================== + # PHASE 2: Satisfy Hadith Constraint + # "Each hadith must have a hadith reference object" + # ========================================== + self.stdout.write("Phase 2: Checking for orphan Hadiths...") + + for hadis_obj in hadiths: + if hadis_obj.id not in covered_hadith_ids: + # This hadith was skipped in Phase 1. + # We must assign it to a Book (Reference). + # Pick a random book to associate with + random_book = random.choice(books) + + self._create_reference_chain(hadis_obj, random_book) + covered_hadith_ids.add(hadis_obj.id) self.stdout.write(self.style.SUCCESS( - f'DONE: Created {counter_refs} HadisReferences and {counter_imgs} ReferenceImages.' - )) \ No newline at end of file + f'DONE: Created {self.counter_refs} HadisReferences (covering {len(covered_hadith_ids)} Hadiths) and {self.counter_imgs} Images.' + )) + + def _create_reference_chain(self, hadis_obj, book_obj): + """ + Helper to create the Reference and its Images + """ + # Create the HadisReference + hadis_ref = HadisReference.objects.create( + hadis=hadis_obj, + book_reference=book_obj, + description=[ + {'language_code': 'en', 'text': f'Reference for {book_obj.title[0].get("text", "Book")} - Hadith {hadis_obj.number}'}, + {'language_code': 'ar', 'text': 'وصف مرجعي'} + ] + ) + self.counter_refs += 1 + + # Create Images for this Reference + self._create_images_for_ref(hadis_ref) + + def _create_images_for_ref(self, hadis_ref): + """ + Helper to create 2 images for a specific HadisReference + """ + selected_images = random.sample(self.image_files, 2) + + for priority_index, img_name in enumerate(selected_images): + img_path = os.path.join(self.base_image_path, img_name) + + # Read file safely + with open(img_path, 'rb') as f: + file_content = f.read() + + ref_img = ReferenceImage( + reference=hadis_ref, + priority=priority_index + ) + + # Assign content without auto-saving + ref_img.thumbnail.save(img_name, ContentFile(file_content), save=False) + + try: + # Explicit save to handle your custom model logic / signals + ref_img.save() + self.counter_imgs += 1 + except Exception as e: + self.stdout.write(self.style.ERROR(f"Failed to save image {img_name}: {e}")) \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh index 515ac32..7b77cb3 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -7,6 +7,6 @@ python manage.py collectstatic --noinput # python manage.py populate_books # python manage.py populate_book_reference -python manage.py populate_reference_images +python manage.py populate_refrence_images exec "$@"