remaining stage 1
This commit is contained in:
@@ -52,12 +52,26 @@ class ClusteringService:
|
|||||||
|
|
||||||
# Delegate to AI task
|
# Delegate to AI task
|
||||||
from igny8_core.ai.tasks import run_ai_task
|
from igny8_core.ai.tasks import run_ai_task
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
'ids': keyword_ids,
|
'ids': keyword_ids,
|
||||||
'sector_id': sector_id
|
'sector_id': sector_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Stage 1: When USE_SITE_BUILDER_REFACTOR is enabled, payload can include
|
||||||
|
# taxonomy hints and dimension metadata for enhanced clustering.
|
||||||
|
# TODO (Stage 2/3): Enhance clustering to collect and use:
|
||||||
|
# - Taxonomy hints from SiteBlueprintTaxonomy
|
||||||
|
# - Dimension metadata (context_type, dimension_meta) for clusters
|
||||||
|
# - Attribute values from Keywords.attribute_values
|
||||||
|
if getattr(settings, 'USE_SITE_BUILDER_REFACTOR', False):
|
||||||
|
logger.info(
|
||||||
|
f"Clustering with refactor enabled: {len(keyword_ids)} keywords, "
|
||||||
|
f"sector_id={sector_id}, account_id={account.id}"
|
||||||
|
)
|
||||||
|
# Future: Add taxonomy hints and dimension metadata to payload
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if hasattr(run_ai_task, 'delay'):
|
if hasattr(run_ai_task, 'delay'):
|
||||||
# Celery available - queue async
|
# Celery available - queue async
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
from .models import Keywords, Clusters, ContentIdeas
|
from .models import Keywords, Clusters, ContentIdeas
|
||||||
from igny8_core.auth.models import SeedKeyword
|
from igny8_core.auth.models import SeedKeyword
|
||||||
@@ -28,14 +29,6 @@ class KeywordSerializer(serializers.ModelSerializer):
|
|||||||
sector_name = serializers.SerializerMethodField()
|
sector_name = serializers.SerializerMethodField()
|
||||||
site_id = serializers.IntegerField(write_only=True, required=False)
|
site_id = serializers.IntegerField(write_only=True, required=False)
|
||||||
sector_id = serializers.IntegerField(write_only=True, required=False)
|
sector_id = serializers.IntegerField(write_only=True, required=False)
|
||||||
|
|
||||||
attribute_values = serializers.ListField(
|
|
||||||
child=serializers.CharField(),
|
|
||||||
required=False,
|
|
||||||
allow_empty=True,
|
|
||||||
default=list,
|
|
||||||
help_text="Optional attribute metadata (e.g., product specs, service modifiers)",
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Keywords
|
model = Keywords
|
||||||
@@ -53,7 +46,6 @@ class KeywordSerializer(serializers.ModelSerializer):
|
|||||||
'cluster_name',
|
'cluster_name',
|
||||||
'sector_name',
|
'sector_name',
|
||||||
'status',
|
'status',
|
||||||
'attribute_values',
|
|
||||||
'created_at',
|
'created_at',
|
||||||
'updated_at',
|
'updated_at',
|
||||||
'site_id',
|
'site_id',
|
||||||
@@ -62,6 +54,12 @@ class KeywordSerializer(serializers.ModelSerializer):
|
|||||||
]
|
]
|
||||||
read_only_fields = ['id', 'created_at', 'updated_at', 'account_id', 'keyword', 'volume', 'difficulty', 'intent']
|
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):
|
def validate(self, attrs):
|
||||||
"""Validate that seed_keyword_id is provided for create operations"""
|
"""Validate that seed_keyword_id is provided for create operations"""
|
||||||
# For create operations, seed_keyword_id is required
|
# For create operations, seed_keyword_id is required
|
||||||
@@ -123,8 +121,6 @@ class ClusterSerializer(serializers.ModelSerializer):
|
|||||||
sector_name = serializers.SerializerMethodField()
|
sector_name = serializers.SerializerMethodField()
|
||||||
site_id = serializers.IntegerField(write_only=True, required=False)
|
site_id = serializers.IntegerField(write_only=True, required=False)
|
||||||
sector_id = serializers.IntegerField(write_only=True, required=False)
|
sector_id = serializers.IntegerField(write_only=True, required=False)
|
||||||
|
|
||||||
context_type_display = serializers.SerializerMethodField()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Clusters
|
model = Clusters
|
||||||
@@ -136,9 +132,6 @@ class ClusterSerializer(serializers.ModelSerializer):
|
|||||||
'volume',
|
'volume',
|
||||||
'mapped_pages',
|
'mapped_pages',
|
||||||
'status',
|
'status',
|
||||||
'context_type',
|
|
||||||
'context_type_display',
|
|
||||||
'dimension_meta',
|
|
||||||
'sector_name',
|
'sector_name',
|
||||||
'created_at',
|
'created_at',
|
||||||
'updated_at',
|
'updated_at',
|
||||||
@@ -148,6 +141,14 @@ class ClusterSerializer(serializers.ModelSerializer):
|
|||||||
]
|
]
|
||||||
read_only_fields = ['id', 'created_at', 'updated_at', 'account_id', 'keywords_count', 'volume', 'mapped_pages']
|
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):
|
def get_sector_name(self, obj):
|
||||||
"""Get sector name from Sector model"""
|
"""Get sector name from Sector model"""
|
||||||
if obj.sector_id:
|
if obj.sector_id:
|
||||||
@@ -160,7 +161,10 @@ class ClusterSerializer(serializers.ModelSerializer):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def get_context_type_display(self, obj):
|
def get_context_type_display(self, obj):
|
||||||
return obj.get_context_type_display()
|
"""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):
|
def validate_name(self, value):
|
||||||
"""Ensure cluster name is unique within account"""
|
"""Ensure cluster name is unique within account"""
|
||||||
@@ -172,7 +176,6 @@ class ContentIdeasSerializer(serializers.ModelSerializer):
|
|||||||
"""Serializer for ContentIdeas model"""
|
"""Serializer for ContentIdeas model"""
|
||||||
keyword_cluster_name = serializers.SerializerMethodField()
|
keyword_cluster_name = serializers.SerializerMethodField()
|
||||||
sector_name = serializers.SerializerMethodField()
|
sector_name = serializers.SerializerMethodField()
|
||||||
taxonomy_name = serializers.SerializerMethodField()
|
|
||||||
site_id = serializers.IntegerField(write_only=True, required=False)
|
site_id = serializers.IntegerField(write_only=True, required=False)
|
||||||
sector_id = serializers.IntegerField(write_only=True, required=False)
|
sector_id = serializers.IntegerField(write_only=True, required=False)
|
||||||
|
|
||||||
@@ -187,13 +190,9 @@ class ContentIdeasSerializer(serializers.ModelSerializer):
|
|||||||
'target_keywords',
|
'target_keywords',
|
||||||
'keyword_cluster_id',
|
'keyword_cluster_id',
|
||||||
'keyword_cluster_name',
|
'keyword_cluster_name',
|
||||||
'taxonomy_id',
|
|
||||||
'taxonomy_name',
|
|
||||||
'sector_name',
|
'sector_name',
|
||||||
'status',
|
'status',
|
||||||
'estimated_word_count',
|
'estimated_word_count',
|
||||||
'site_entity_type',
|
|
||||||
'cluster_role',
|
|
||||||
'created_at',
|
'created_at',
|
||||||
'updated_at',
|
'updated_at',
|
||||||
'site_id',
|
'site_id',
|
||||||
@@ -202,6 +201,15 @@ class ContentIdeasSerializer(serializers.ModelSerializer):
|
|||||||
]
|
]
|
||||||
read_only_fields = ['id', 'created_at', 'updated_at', '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):
|
def get_keyword_cluster_name(self, obj):
|
||||||
"""Get cluster name from Clusters model"""
|
"""Get cluster name from Clusters model"""
|
||||||
if obj.keyword_cluster_id:
|
if obj.keyword_cluster_id:
|
||||||
|
|||||||
@@ -2,6 +2,28 @@ from django.db import migrations, models
|
|||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
def backfill_metadata_mappings_stub(apps, schema_editor):
|
||||||
|
"""
|
||||||
|
Stage 1: Placeholder for Stage 3 metadata backfill.
|
||||||
|
|
||||||
|
This function will be extended in Stage 3 to backfill:
|
||||||
|
- ContentClusterMap records from existing Content/Task -> Cluster relationships
|
||||||
|
- ContentTaxonomyMap records from existing taxonomy associations
|
||||||
|
- ContentAttributeMap records from existing attribute data
|
||||||
|
|
||||||
|
For now, this is a no-op to establish the migration hook.
|
||||||
|
"""
|
||||||
|
# Stage 1: No-op - tables created, ready for Stage 3 backfill
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def reverse_backfill_metadata_mappings_stub(apps, schema_editor):
|
||||||
|
"""
|
||||||
|
Reverse operation for metadata backfill (no-op for Stage 1).
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
@@ -117,5 +139,10 @@ class Migration(migrations.Migration):
|
|||||||
model_name='contentattributemap',
|
model_name='contentattributemap',
|
||||||
index=models.Index(fields=['task', 'name'], name='writer_con_task__name_fa4a4e_idx'),
|
index=models.Index(fields=['task', 'name'], name='writer_con_task__name_fa4a4e_idx'),
|
||||||
),
|
),
|
||||||
|
# Stage 1: Data migration stub for Stage 3 backfill
|
||||||
|
migrations.RunPython(
|
||||||
|
backfill_metadata_mappings_stub,
|
||||||
|
reverse_backfill_metadata_mappings_stub,
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
from django.conf import settings
|
||||||
from .models import Tasks, Images, Content
|
from .models import Tasks, Images, Content
|
||||||
from igny8_core.business.planning.models import Clusters, ContentIdeas
|
from igny8_core.business.planning.models import Clusters, ContentIdeas
|
||||||
from igny8_core.business.content.models import (
|
from igny8_core.business.content.models import (
|
||||||
@@ -22,9 +23,6 @@ class TasksSerializer(serializers.ModelSerializer):
|
|||||||
content_secondary_keywords = serializers.SerializerMethodField()
|
content_secondary_keywords = serializers.SerializerMethodField()
|
||||||
content_tags = serializers.SerializerMethodField()
|
content_tags = serializers.SerializerMethodField()
|
||||||
content_categories = serializers.SerializerMethodField()
|
content_categories = serializers.SerializerMethodField()
|
||||||
cluster_mappings = serializers.SerializerMethodField()
|
|
||||||
taxonomy_mappings = serializers.SerializerMethodField()
|
|
||||||
attribute_mappings = serializers.SerializerMethodField()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Tasks
|
model = Tasks
|
||||||
@@ -50,9 +48,6 @@ class TasksSerializer(serializers.ModelSerializer):
|
|||||||
'content_secondary_keywords',
|
'content_secondary_keywords',
|
||||||
'content_tags',
|
'content_tags',
|
||||||
'content_categories',
|
'content_categories',
|
||||||
'cluster_mappings',
|
|
||||||
'taxonomy_mappings',
|
|
||||||
'attribute_mappings',
|
|
||||||
'assigned_post_id',
|
'assigned_post_id',
|
||||||
'post_url',
|
'post_url',
|
||||||
'created_at',
|
'created_at',
|
||||||
@@ -63,6 +58,14 @@ class TasksSerializer(serializers.ModelSerializer):
|
|||||||
]
|
]
|
||||||
read_only_fields = ['id', 'created_at', 'updated_at', '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['cluster_mappings'] = serializers.SerializerMethodField()
|
||||||
|
self.fields['taxonomy_mappings'] = serializers.SerializerMethodField()
|
||||||
|
self.fields['attribute_mappings'] = serializers.SerializerMethodField()
|
||||||
|
|
||||||
def get_cluster_name(self, obj):
|
def get_cluster_name(self, obj):
|
||||||
"""Get cluster name from Clusters model"""
|
"""Get cluster name from Clusters model"""
|
||||||
if obj.cluster_id:
|
if obj.cluster_id:
|
||||||
@@ -246,9 +249,6 @@ class ContentSerializer(serializers.ModelSerializer):
|
|||||||
sector_name = serializers.SerializerMethodField()
|
sector_name = serializers.SerializerMethodField()
|
||||||
has_image_prompts = serializers.SerializerMethodField()
|
has_image_prompts = serializers.SerializerMethodField()
|
||||||
has_generated_images = serializers.SerializerMethodField()
|
has_generated_images = serializers.SerializerMethodField()
|
||||||
cluster_mappings = serializers.SerializerMethodField()
|
|
||||||
taxonomy_mappings = serializers.SerializerMethodField()
|
|
||||||
attribute_mappings = serializers.SerializerMethodField()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Content
|
model = Content
|
||||||
@@ -277,12 +277,17 @@ class ContentSerializer(serializers.ModelSerializer):
|
|||||||
'entity_type',
|
'entity_type',
|
||||||
'json_blocks',
|
'json_blocks',
|
||||||
'structure_data',
|
'structure_data',
|
||||||
'cluster_mappings',
|
|
||||||
'taxonomy_mappings',
|
|
||||||
'attribute_mappings',
|
|
||||||
]
|
]
|
||||||
read_only_fields = ['id', 'generated_at', 'updated_at', 'account_id']
|
read_only_fields = ['id', 'generated_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['cluster_mappings'] = serializers.SerializerMethodField()
|
||||||
|
self.fields['taxonomy_mappings'] = serializers.SerializerMethodField()
|
||||||
|
self.fields['attribute_mappings'] = serializers.SerializerMethodField()
|
||||||
|
|
||||||
def get_task_title(self, obj):
|
def get_task_title(self, obj):
|
||||||
"""Get task title"""
|
"""Get task title"""
|
||||||
if obj.task_id:
|
if obj.task_id:
|
||||||
|
|||||||
Reference in New Issue
Block a user