diff --git a/apps/hadis/management/commands/assign_volume_images.py b/apps/hadis/management/commands/assign_volume_images.py new file mode 100644 index 0000000..0846914 --- /dev/null +++ b/apps/hadis/management/commands/assign_volume_images.py @@ -0,0 +1,63 @@ +import os +from django.core.management.base import BaseCommand +from django.conf import settings +from django.core.files import File +from apps.hadis.models import BookVolume, BookReferenceImage + +class Command(BaseCommand): + help = 'Assigns static/images/image.png to all BookVolumes in the database' + + def add_arguments(self, parser): + parser.add_argument( + '--force', + action='store_true', + help='Force re-assign even if the volume already has an image', + ) + + def handle(self, *args, **options): + force = options.get('force', False) + + # 1. Locate the image file + image_path = os.path.join(settings.BASE_DIR, 'static', 'images', 'image.png') + if not os.path.exists(image_path): + self.stdout.write(self.style.ERROR(f"Source image not found at: {image_path}")) + return + + self.stdout.write(f"Source image found at: {image_path}") + + volumes = BookVolume.objects.all() + total_volumes = volumes.count() + self.stdout.write(f"Found {total_volumes} volumes in the database.") + + created_count = 0 + updated_count = 0 + + for vol in volumes: + # Check if volume already has an image + existing_images = BookReferenceImage.objects.filter(book_volume=vol) + + if existing_images.exists() and not force: + continue + + with open(image_path, 'rb') as f: + if existing_images.exists() and force: + # Overwrite the first existing image + img_instance = existing_images.first() + img_instance.image.save('image.png', File(f), save=True) + updated_count += 1 + else: + # Create a new BookReferenceImage entry + img_instance = BookReferenceImage( + book_reference=vol.book_reference, + book_volume=vol, + order=0, + description=[{"language_code": "fa", "text": f"تصویر پیش‌فرض {vol.title or ''}"}] + ) + img_instance.image.save('image.png', File(f), save=True) + created_count += 1 + + self.stdout.write( + self.style.SUCCESS( + f"Successfully processed volumes: created {created_count}, updated {updated_count} (force={force})." + ) + ) diff --git a/apps/hadis/serializers/reference_v2.py b/apps/hadis/serializers/reference_v2.py index 1e0d990..2f060b9 100644 --- a/apps/hadis/serializers/reference_v2.py +++ b/apps/hadis/serializers/reference_v2.py @@ -23,14 +23,26 @@ class BookReferenceV2ListSerializer(serializers.ModelSerializer): authors = BookAuthorV2Serializer(many=True, read_only=True) tags = BookTagV2Serializer(many=True, read_only=True) number_of_volumes = serializers.SerializerMethodField() + image = serializers.SerializerMethodField() class Meta: model = BookReference - fields = ['id', 'slug', 'title', 'authors', 'number_of_volumes', 'tags'] + fields = ['id', 'slug', 'title', 'authors', 'number_of_volumes', 'tags', 'image'] def get_number_of_volumes(self, obj): return obj.volumes.count() + def get_image(self, obj): + first_volume = obj.volumes.first() + if first_volume: + first_image = first_volume.images.order_by('order').first() + if first_image and first_image.image: + request = self.context.get('request') + if request: + return request.build_absolute_uri(first_image.image.url) + return first_image.image.url + return None + class BookEditionV2Serializer(serializers.ModelSerializer): publisher = LocalizedField() notes = LocalizedField() @@ -54,13 +66,25 @@ class BookEditionV2Serializer(serializers.ModelSerializer): } class BookVolumeV2Serializer(serializers.ModelSerializer): + image = serializers.SerializerMethodField() + class Meta: model = BookVolume - fields = ['id', 'title'] + fields = ['id', 'title', 'image'] + + def get_image(self, obj): + first_image = obj.images.order_by('order').first() + if first_image and first_image.image: + request = self.context.get('request') + if request: + return request.build_absolute_uri(first_image.image.url) + return first_image.image.url + return None class BookReferenceV2DetailSerializer(serializers.ModelSerializer): title = LocalizedField() authors = BookAuthorV2Serializer(many=True, read_only=True) + image = serializers.SerializerMethodField() information = serializers.SerializerMethodField() excerpts = serializers.SerializerMethodField() @@ -68,7 +92,18 @@ class BookReferenceV2DetailSerializer(serializers.ModelSerializer): class Meta: model = BookReference - fields = ['id', 'title', 'authors', 'information', 'excerpts', 'volumes'] + fields = ['id', 'title', 'authors', 'image', 'information', 'excerpts', 'volumes'] + + def get_image(self, obj): + first_image = obj.images.filter(book_volume__isnull=True).order_by('order').first() + if not first_image: + first_image = obj.images.order_by('order').first() + if first_image and first_image.image: + request = self.context.get('request') + if request: + return request.build_absolute_uri(first_image.image.url) + return first_image.image.url + return None def get_information(self, obj): request = self.context.get("request") @@ -115,7 +150,7 @@ class BookReferenceV2DetailSerializer(serializers.ModelSerializer): editions_data.append({ "edition_id": ed.id, "edition_statement": ed.edition_number or f"Edition {ed.id}", - "volumes": BookVolumeV2Serializer(ed.volumes.all(), many=True).data + "volumes": BookVolumeV2Serializer(ed.volumes.all(), many=True, context=self.context).data }) return { "has_editions": True, @@ -126,7 +161,7 @@ class BookReferenceV2DetailSerializer(serializers.ModelSerializer): return { "has_editions": False, "editions_volumes": [], - "all_volumes": BookVolumeV2Serializer(obj.volumes.all(), many=True).data + "all_volumes": BookVolumeV2Serializer(obj.volumes.all(), many=True, context=self.context).data } class BookReferenceV2SyncSerializer(serializers.ModelSerializer): @@ -170,7 +205,7 @@ class BookReferenceV2SyncSerializer(serializers.ModelSerializer): editions_data.append({ "edition_id": ed.id, "edition_statement": ed.edition_number or f"Edition {ed.id}", - "volumes": BookVolumeV2Serializer(ed.volumes.all(), many=True).data + "volumes": BookVolumeV2Serializer(ed.volumes.all(), many=True, context=self.context).data }) return { "has_editions": True, @@ -181,7 +216,7 @@ class BookReferenceV2SyncSerializer(serializers.ModelSerializer): return { "has_editions": False, "editions_volumes": [], - "all_volumes": BookVolumeV2Serializer(obj.volumes.all(), many=True).data + "all_volumes": BookVolumeV2Serializer(obj.volumes.all(), many=True, context=self.context).data } from apps.hadis.models.reference import BookAuthor, BookReference diff --git a/apps/hadis/views/reference_v2.py b/apps/hadis/views/reference_v2.py index c5a2421..2727962 100644 --- a/apps/hadis/views/reference_v2.py +++ b/apps/hadis/views/reference_v2.py @@ -18,8 +18,9 @@ class BookReferenceV2DetailView(generics.RetrieveAPIView): .filter(slug=self.kwargs.get('slug')) .prefetch_related( 'authors', - 'editions__volumes', - 'volumes', + 'editions__volumes__images', + 'volumes__images', + 'images', 'hadis_references__hadis' ) ) @@ -141,7 +142,7 @@ class BookReferenceV2ListView(generics.ListAPIView): return super().get(request, *args, **kwargs) def get_queryset(self): - qs = BookReference.objects.prefetch_related('authors', 'tags', 'volumes').distinct().order_by('-created_at') + qs = BookReference.objects.prefetch_related('authors', 'tags', 'volumes__images').distinct().order_by('-created_at') search = self.request.query_params.get('search') if search: diff --git a/entrypoint.sh b/entrypoint.sh index 99e2a8f..63d677e 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -5,6 +5,7 @@ python manage.py migrate # python manage.py seed_images # python manage.py compilemessages python manage.py collectstatic --noinput +python manage.py assign_volume_images --force # python manage.py set_default_category_icons # python manage.py set_hadiscorrection_fixed_text # Seed Russian data (only runs once, skips if data exists) diff --git a/static/images/image.png b/static/images/image.png new file mode 100644 index 0000000..a94462c Binary files /dev/null and b/static/images/image.png differ