from rest_framework import serializers from django.db import models from .models import Tasks, Images, Content from igny8_core.modules.planner.models import Clusters, ContentIdeas class TasksSerializer(serializers.ModelSerializer): """Serializer for Tasks model""" cluster_name = serializers.SerializerMethodField() sector_name = serializers.SerializerMethodField() idea_title = serializers.SerializerMethodField() site_id = serializers.IntegerField(write_only=True, required=False) sector_id = serializers.IntegerField(write_only=True, required=False) content_html = serializers.SerializerMethodField() content_primary_keyword = serializers.SerializerMethodField() content_secondary_keywords = serializers.SerializerMethodField() content_tags = serializers.SerializerMethodField() content_categories = serializers.SerializerMethodField() class Meta: model = Tasks fields = [ 'id', 'title', 'description', 'keywords', 'cluster_id', 'cluster_name', 'sector_name', 'idea_id', 'idea_title', 'content_structure', 'content_type', 'status', 'content', 'word_count', 'meta_title', 'meta_description', 'content_html', 'content_primary_keyword', 'content_secondary_keywords', 'content_tags', 'content_categories', 'assigned_post_id', 'post_url', 'created_at', 'updated_at', 'site_id', 'sector_id', 'account_id', ] read_only_fields = ['id', 'created_at', 'updated_at', 'account_id'] def get_cluster_name(self, obj): """Get cluster name from Clusters model""" if obj.cluster_id: try: cluster = Clusters.objects.get(id=obj.cluster_id) return cluster.name except Clusters.DoesNotExist: return None return None def get_sector_name(self, obj): """Get sector name from Sector model""" if obj.sector_id: try: from igny8_core.auth.models import Sector sector = Sector.objects.get(id=obj.sector_id) return sector.name except Sector.DoesNotExist: return None return None def get_idea_title(self, obj): """Get idea title from ContentIdeas model""" if obj.idea_id: try: idea = ContentIdeas.objects.get(id=obj.idea_id) return idea.idea_title except ContentIdeas.DoesNotExist: return None return None def _get_content_record(self, obj): try: return obj.content_record except AttributeError: return None def get_content_html(self, obj): record = self._get_content_record(obj) return record.html_content if record else None def get_content_primary_keyword(self, obj): record = self._get_content_record(obj) return record.primary_keyword if record else None def get_content_secondary_keywords(self, obj): record = self._get_content_record(obj) return record.secondary_keywords if record else [] def get_content_tags(self, obj): record = self._get_content_record(obj) return record.tags if record else [] def get_content_categories(self, obj): record = self._get_content_record(obj) return record.categories if record else [] class ImagesSerializer(serializers.ModelSerializer): """Serializer for Images model""" task_title = serializers.SerializerMethodField() content_title = serializers.SerializerMethodField() class Meta: model = Images fields = [ 'id', 'task_id', 'task_title', 'content_id', 'content_title', 'image_type', 'image_url', 'image_path', 'prompt', 'status', 'position', 'created_at', 'updated_at', 'account_id', ] read_only_fields = ['id', 'created_at', 'updated_at', 'account_id'] def get_task_title(self, obj): """Get task title""" if obj.task_id: try: task = Tasks.objects.get(id=obj.task_id) return task.title except Tasks.DoesNotExist: return None return None def get_content_title(self, obj): """Get content title""" if obj.content: return obj.content.title or obj.content.meta_title return None class ContentImageSerializer(serializers.ModelSerializer): """Serializer for individual image in grouped content images""" image_url = serializers.SerializerMethodField() class Meta: model = Images fields = [ 'id', 'image_type', 'image_url', 'image_path', 'prompt', 'status', 'position', 'created_at', 'updated_at', ] def get_image_url(self, obj): """ Return proper HTTP URL for image. Priority: If image_path exists and is in ai-images folder, return web URL, otherwise return file endpoint URL or image_url (API URL). """ if obj.image_path: # Check if path is in ai-images folder (web-accessible) if 'ai-images' in obj.image_path: # Extract filename from path filename = obj.image_path.split('ai-images/')[-1] if 'ai-images/' in obj.image_path else obj.image_path.split('ai-images\\')[-1] if filename: # Return web-accessible URL (like /images/logo/logo.svg) return f'/images/ai-images/{filename}' # For other local paths, use file endpoint request = self.context.get('request') if request: # Build absolute URL for file endpoint file_url = request.build_absolute_uri(f'/api/v1/writer/images/{obj.id}/file/') return file_url else: # Fallback: return relative URL if no request context return f'/api/v1/writer/images/{obj.id}/file/' # Fallback to original image_url (API URL) if no local path return obj.image_url class ContentImagesGroupSerializer(serializers.Serializer): """Serializer for grouped content images - one row per content""" content_id = serializers.IntegerField() content_title = serializers.CharField() featured_image = ContentImageSerializer(allow_null=True) in_article_images = ContentImageSerializer(many=True) overall_status = serializers.CharField() # 'pending', 'partial', 'complete', 'failed' class ContentSerializer(serializers.ModelSerializer): """Serializer for Content model""" task_title = serializers.SerializerMethodField() sector_name = serializers.SerializerMethodField() has_image_prompts = serializers.SerializerMethodField() has_generated_images = serializers.SerializerMethodField() class Meta: model = Content fields = [ 'id', 'task_id', 'task_title', 'sector_name', 'html_content', 'word_count', 'metadata', 'title', 'meta_title', 'meta_description', 'primary_keyword', 'secondary_keywords', 'tags', 'categories', 'status', 'generated_at', 'updated_at', 'account_id', 'has_image_prompts', 'has_generated_images', ] read_only_fields = ['id', 'generated_at', 'updated_at', 'account_id'] def get_task_title(self, obj): """Get task title""" if obj.task_id: try: task = Tasks.objects.get(id=obj.task_id) return task.title except Tasks.DoesNotExist: return None return None def get_sector_name(self, obj): """Get sector name from Sector model""" if obj.sector_id: try: from igny8_core.auth.models import Sector sector = Sector.objects.get(id=obj.sector_id) return sector.name except Sector.DoesNotExist: return None return None def get_has_image_prompts(self, obj): """Check if content has any image prompts generated""" # Check if any images exist with prompts for this content return Images.objects.filter( models.Q(content=obj) | models.Q(task=obj.task) ).exclude(prompt__isnull=True).exclude(prompt='').exists() def get_has_generated_images(self, obj): """Check if content has any generated images (status='generated' and has URL)""" # Check if any images are generated (have status='generated' and image_url) return Images.objects.filter( models.Q(content=obj) | models.Q(task=obj.task), status='generated', image_url__isnull=False ).exclude(image_url='').exists()