from rest_framework import serializers from django.conf import settings from .models import Keywords, Clusters, ContentIdeas from igny8_core.auth.models import SeedKeyword from igny8_core.auth.serializers import SeedKeywordSerializer from igny8_core.business.site_building.models import SiteBlueprintTaxonomy class KeywordSerializer(serializers.ModelSerializer): """Serializer for Keywords model with SeedKeyword relationship""" # Read-only properties from seed_keyword keyword = serializers.CharField(read_only=True) # From seed_keyword.keyword volume = serializers.IntegerField(read_only=True) # From seed_keyword.volume or volume_override difficulty = serializers.IntegerField(read_only=True) # From seed_keyword.difficulty or difficulty_override intent = serializers.CharField(read_only=True) # From seed_keyword.intent # SeedKeyword relationship # Required for create, optional for update (can change seed_keyword or just update other fields) seed_keyword_id = serializers.IntegerField(write_only=True, required=False) seed_keyword = SeedKeywordSerializer(read_only=True) # Overrides volume_override = serializers.IntegerField(required=False, allow_null=True) difficulty_override = serializers.IntegerField(required=False, allow_null=True) # Related fields cluster_name = serializers.SerializerMethodField() sector_name = serializers.SerializerMethodField() site_id = serializers.IntegerField(write_only=True, required=False) sector_id = serializers.IntegerField(write_only=True, required=False) class Meta: model = Keywords fields = [ 'id', 'seed_keyword_id', 'seed_keyword', 'keyword', 'volume', 'difficulty', 'intent', 'volume_override', 'difficulty_override', 'cluster_id', 'cluster_name', 'sector_name', 'status', 'created_at', 'updated_at', 'site_id', 'sector_id', 'account_id', ] read_only_fields = ['id', 'created_at', 'updated_at', 'account_id', 'keyword', 'volume', 'difficulty', 'intent'] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Only include Stage 1 fields when feature flag is enabled if getattr(settings, 'USE_SITE_BUILDER_REFACTOR', False): self.fields['attribute_values'] = serializers.JSONField(read_only=True) def validate(self, attrs): """Validate that seed_keyword_id is provided for create operations""" # For create operations, seed_keyword_id is required if self.instance is None and 'seed_keyword_id' not in attrs: raise serializers.ValidationError({'seed_keyword_id': 'This field is required when creating a keyword.'}) return attrs def create(self, validated_data): """Create Keywords instance with seed_keyword""" seed_keyword_id = validated_data.pop('seed_keyword_id', None) if not seed_keyword_id: raise serializers.ValidationError({'seed_keyword_id': 'This field is required when creating a keyword.'}) try: seed_keyword = SeedKeyword.objects.get(id=seed_keyword_id) except SeedKeyword.DoesNotExist: raise serializers.ValidationError({'seed_keyword_id': f'SeedKeyword with id {seed_keyword_id} does not exist'}) validated_data['seed_keyword'] = seed_keyword return super().create(validated_data) def update(self, instance, validated_data): """Update Keywords instance with seed_keyword""" # seed_keyword_id is optional for updates - only update if provided if 'seed_keyword_id' in validated_data: seed_keyword_id = validated_data.pop('seed_keyword_id') try: seed_keyword = SeedKeyword.objects.get(id=seed_keyword_id) validated_data['seed_keyword'] = seed_keyword except SeedKeyword.DoesNotExist: raise serializers.ValidationError({'seed_keyword_id': f'SeedKeyword with id {seed_keyword_id} does not exist'}) return super().update(instance, validated_data) 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 class ClusterSerializer(serializers.ModelSerializer): """Serializer for Clusters model""" sector_name = serializers.SerializerMethodField() site_id = serializers.IntegerField(write_only=True, required=False) sector_id = serializers.IntegerField(write_only=True, required=False) class Meta: model = Clusters fields = [ 'id', 'name', 'description', 'keywords_count', 'volume', 'mapped_pages', 'status', 'sector_name', 'created_at', 'updated_at', 'site_id', 'sector_id', 'account_id', ] read_only_fields = ['id', 'created_at', 'updated_at', 'account_id', 'keywords_count', 'volume', 'mapped_pages'] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Only include Stage 1 fields when feature flag is enabled if getattr(settings, 'USE_SITE_BUILDER_REFACTOR', False): self.fields['context_type'] = serializers.CharField(read_only=True) self.fields['context_type_display'] = serializers.SerializerMethodField() self.fields['dimension_meta'] = serializers.JSONField(read_only=True) 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_context_type_display(self, obj): """Get context type display name (only when feature flag enabled)""" if hasattr(obj, 'get_context_type_display'): return obj.get_context_type_display() return None def validate_name(self, value): """Ensure cluster name is unique within account""" # Uniqueness is handled at model level, but we can add additional validation here return value class ContentIdeasSerializer(serializers.ModelSerializer): """Serializer for ContentIdeas model""" keyword_cluster_name = serializers.SerializerMethodField() sector_name = serializers.SerializerMethodField() site_id = serializers.IntegerField(write_only=True, required=False) sector_id = serializers.IntegerField(write_only=True, required=False) class Meta: model = ContentIdeas fields = [ 'id', 'idea_title', 'description', 'content_structure', 'content_type', 'target_keywords', 'keyword_cluster_id', 'keyword_cluster_name', 'sector_name', 'status', 'estimated_word_count', 'created_at', 'updated_at', 'site_id', 'sector_id', 'account_id', ] read_only_fields = ['id', 'created_at', 'updated_at', 'account_id'] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Only include Stage 1 fields when feature flag is enabled if getattr(settings, 'USE_SITE_BUILDER_REFACTOR', False): self.fields['taxonomy_id'] = serializers.IntegerField(read_only=True, allow_null=True) self.fields['taxonomy_name'] = serializers.SerializerMethodField() self.fields['site_entity_type'] = serializers.CharField(read_only=True) self.fields['cluster_role'] = serializers.CharField(read_only=True) def get_keyword_cluster_name(self, obj): """Get cluster name from Clusters model""" if obj.keyword_cluster_id: try: cluster = Clusters.objects.get(id=obj.keyword_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_taxonomy_name(self, obj): if obj.taxonomy_id: try: taxonomy = SiteBlueprintTaxonomy.objects.get(id=obj.taxonomy_id) return taxonomy.name except SiteBlueprintTaxonomy.DoesNotExist: return None return None