refactor-migration again
This commit is contained in:
@@ -59,8 +59,8 @@ class KeywordsAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
|
||||
|
||||
@admin.register(ContentIdeas)
|
||||
class ContentIdeasAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
|
||||
list_display = ['idea_title', 'site', 'sector', 'description_preview', 'site_entity_type', 'cluster_role', 'status', 'keyword_cluster', 'estimated_word_count', 'created_at']
|
||||
list_filter = ['status', 'site_entity_type', 'cluster_role', 'site', 'sector']
|
||||
list_display = ['idea_title', 'site', 'sector', 'description_preview', 'content_type', 'content_structure', 'status', 'keyword_cluster', 'estimated_word_count', 'created_at']
|
||||
list_filter = ['status', 'content_type', 'content_structure', 'site', 'sector']
|
||||
search_fields = ['idea_title', 'target_keywords', 'description']
|
||||
ordering = ['-created_at']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
@@ -70,7 +70,7 @@ class ContentIdeasAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
|
||||
'fields': ('idea_title', 'description', 'status', 'site', 'sector')
|
||||
}),
|
||||
('Content Planning', {
|
||||
'fields': ('site_entity_type', 'cluster_role', 'estimated_word_count')
|
||||
'fields': ('content_type', 'content_structure', 'estimated_word_count')
|
||||
}),
|
||||
('Keywords & Clustering', {
|
||||
'fields': ('keyword_cluster', 'target_keywords', 'taxonomy')
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# Generated by Django 5.2.8 on 2025-11-26 14:31
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('igny8_core_auth', '0002_add_wp_api_key_to_site'),
|
||||
('planner', '0004_remove_clusters_igny8_clust_context_0d6bd7_idx_and_more'),
|
||||
('site_building', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveIndex(
|
||||
model_name='contentideas',
|
||||
name='igny8_conte_site_en_511349_idx',
|
||||
),
|
||||
migrations.RemoveIndex(
|
||||
model_name='contentideas',
|
||||
name='igny8_conte_cluster_234240_idx',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='contentideas',
|
||||
name='cluster_role',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='contentideas',
|
||||
name='site_entity_type',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='contentideas',
|
||||
name='content_structure',
|
||||
field=models.CharField(choices=[('article', 'Article'), ('guide', 'Guide'), ('comparison', 'Comparison'), ('review', 'Review'), ('listicle', 'Listicle'), ('landing_page', 'Landing Page'), ('business_page', 'Business Page'), ('service_page', 'Service Page'), ('general', 'General'), ('cluster_hub', 'Cluster Hub'), ('product_page', 'Product Page'), ('category_archive', 'Category Archive'), ('tag_archive', 'Tag Archive'), ('attribute_archive', 'Attribute Archive')], default='article', help_text='Content structure/format based on content type', max_length=50),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='contentideas',
|
||||
name='content_type',
|
||||
field=models.CharField(choices=[('post', 'Post'), ('page', 'Page'), ('product', 'Product'), ('taxonomy', 'Taxonomy')], default='post', help_text='Content type: post, page, product, taxonomy', max_length=50),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='contentideas',
|
||||
index=models.Index(fields=['content_type'], name='igny8_conte_content_e74415_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='contentideas',
|
||||
index=models.Index(fields=['content_structure'], name='igny8_conte_content_3eede7_idx'),
|
||||
),
|
||||
]
|
||||
@@ -171,8 +171,8 @@ class ContentIdeasSerializer(serializers.ModelSerializer):
|
||||
'id',
|
||||
'idea_title',
|
||||
'description',
|
||||
'site_entity_type',
|
||||
'cluster_role',
|
||||
'content_type',
|
||||
'content_structure',
|
||||
'target_keywords',
|
||||
'keyword_cluster_id',
|
||||
'keyword_cluster_name',
|
||||
|
||||
@@ -927,7 +927,7 @@ class ContentIdeasViewSet(SiteSectorModelViewSet):
|
||||
ordering = ['-created_at'] # Default ordering (newest first)
|
||||
|
||||
# Filter configuration (updated for new structure)
|
||||
filterset_fields = ['status', 'keyword_cluster_id', 'site_entity_type', 'cluster_role']
|
||||
filterset_fields = ['status', 'keyword_cluster_id', 'content_type', 'content_structure']
|
||||
|
||||
def perform_create(self, serializer):
|
||||
"""Require explicit site_id and sector_id - no defaults."""
|
||||
@@ -1013,27 +1013,13 @@ class ContentIdeasViewSet(SiteSectorModelViewSet):
|
||||
errors = []
|
||||
for idea in ideas:
|
||||
try:
|
||||
# STAGE 3: Map idea fields to final Task schema
|
||||
# Map site_entity_type → content_type (with fallback)
|
||||
content_type = idea.site_entity_type if idea.site_entity_type else 'post'
|
||||
|
||||
# Map cluster_role → content_structure (with fallback)
|
||||
# hub → article, supporting → guide, attribute → comparison
|
||||
role_to_structure = {
|
||||
'hub': 'article',
|
||||
'supporting': 'guide',
|
||||
'attribute': 'comparison',
|
||||
}
|
||||
cluster_role = idea.cluster_role if idea.cluster_role else 'hub'
|
||||
content_structure = role_to_structure.get(cluster_role, 'article')
|
||||
|
||||
# Create task with Stage 1 final fields
|
||||
# Direct copy - no mapping needed
|
||||
task = Tasks.objects.create(
|
||||
title=idea.idea_title,
|
||||
description=idea.description or '',
|
||||
cluster=idea.keyword_cluster,
|
||||
content_type=content_type,
|
||||
content_structure=content_structure,
|
||||
content_type=idea.content_type or 'post',
|
||||
content_structure=idea.content_structure or 'article',
|
||||
taxonomy_term=None, # Can be set later if taxonomy is available
|
||||
status='queued',
|
||||
account=idea.account,
|
||||
@@ -1056,7 +1042,7 @@ class ContentIdeasViewSet(SiteSectorModelViewSet):
|
||||
if errors:
|
||||
return error_response(
|
||||
error=f'Failed to create {len(errors)} tasks',
|
||||
details=errors,
|
||||
errors=errors,
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
request=request
|
||||
)
|
||||
|
||||
@@ -228,10 +228,10 @@ class SiteBlueprintViewSet(SiteSectorModelViewSet):
|
||||
).values_list('content_id', flat=True).distinct()
|
||||
cluster_content = content.filter(id__in=cluster_content_ids)
|
||||
|
||||
# Count by role
|
||||
hub_count = cluster_tasks.filter(cluster_role='hub').count()
|
||||
supporting_count = cluster_tasks.filter(cluster_role='supporting').count()
|
||||
attribute_count = cluster_tasks.filter(cluster_role='attribute').count()
|
||||
# Count by structure
|
||||
hub_count = cluster_tasks.filter(content_structure='cluster_hub').count()
|
||||
supporting_count = cluster_tasks.filter(content_structure__in=['article', 'guide', 'comparison']).count()
|
||||
attribute_count = cluster_tasks.filter(content_structure='attribute_archive').count()
|
||||
|
||||
cluster_progress.append({
|
||||
'cluster_id': cluster.id,
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
# Generated manually for field rename implementation
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('planner', '0005_field_rename_implementation'),
|
||||
('writer', '0007_alter_contenttaxonomyrelation_unique_together_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
# Rename database columns for Tasks
|
||||
migrations.RunSQL(
|
||||
sql="""
|
||||
-- Rename Tasks columns (only if they exist)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'igny8_tasks' AND column_name = 'entity_type') THEN
|
||||
ALTER TABLE igny8_tasks RENAME COLUMN entity_type TO content_type;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'igny8_tasks' AND column_name = 'cluster_role') THEN
|
||||
ALTER TABLE igny8_tasks RENAME COLUMN cluster_role TO content_structure;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Drop old indexes if they exist
|
||||
DROP INDEX IF EXISTS igny8_tasks_entity__1dc185_idx;
|
||||
DROP INDEX IF EXISTS igny8_tasks_cluster_c87903_idx;
|
||||
|
||||
-- Create new indexes
|
||||
CREATE INDEX IF NOT EXISTS igny8_tasks_content_type_idx ON igny8_tasks(content_type);
|
||||
CREATE INDEX IF NOT EXISTS igny8_tasks_content_structure_idx ON igny8_tasks(content_structure);
|
||||
""",
|
||||
reverse_sql="""
|
||||
ALTER TABLE igny8_tasks RENAME COLUMN content_type TO entity_type;
|
||||
ALTER TABLE igny8_tasks RENAME COLUMN content_structure TO cluster_role;
|
||||
DROP INDEX IF EXISTS igny8_tasks_content_type_idx;
|
||||
DROP INDEX IF EXISTS igny8_tasks_content_structure_idx;
|
||||
CREATE INDEX igny8_tasks_entity__1dc185_idx ON igny8_tasks(entity_type);
|
||||
CREATE INDEX igny8_tasks_cluster_c87903_idx ON igny8_tasks(cluster_role);
|
||||
"""
|
||||
),
|
||||
|
||||
# Rename database columns for Content
|
||||
migrations.RunSQL(
|
||||
sql="""
|
||||
-- Rename Content columns (only if they exist)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'igny8_content' AND column_name = 'entity_type') THEN
|
||||
ALTER TABLE igny8_content RENAME COLUMN entity_type TO content_type;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'igny8_content' AND column_name = 'cluster_role') THEN
|
||||
ALTER TABLE igny8_content RENAME COLUMN cluster_role TO content_structure;
|
||||
END IF;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'igny8_content' AND column_name = 'html_content') THEN
|
||||
ALTER TABLE igny8_content RENAME COLUMN html_content TO content_html;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Drop old indexes if they exist
|
||||
DROP INDEX IF EXISTS igny8_conte_entity__f559b3_idx;
|
||||
DROP INDEX IF EXISTS igny8_conte_cluster_32e22a_idx;
|
||||
|
||||
-- Create new indexes
|
||||
CREATE INDEX IF NOT EXISTS igny8_content_content_type_idx ON igny8_content(content_type);
|
||||
CREATE INDEX IF NOT EXISTS igny8_content_content_structure_idx ON igny8_content(content_structure);
|
||||
""",
|
||||
reverse_sql="""
|
||||
ALTER TABLE igny8_content RENAME COLUMN content_type TO entity_type;
|
||||
ALTER TABLE igny8_content RENAME COLUMN content_structure TO cluster_role;
|
||||
ALTER TABLE igny8_content RENAME COLUMN content_html TO html_content;
|
||||
DROP INDEX IF EXISTS igny8_content_content_type_idx;
|
||||
DROP INDEX IF EXISTS igny8_content_content_structure_idx;
|
||||
CREATE INDEX igny8_conte_entity__f559b3_idx ON igny8_content(entity_type);
|
||||
CREATE INDEX igny8_conte_cluster_32e22a_idx ON igny8_content(cluster_role);
|
||||
"""
|
||||
),
|
||||
|
||||
# Rename database columns for ContentTaxonomyMap
|
||||
migrations.RunSQL(
|
||||
sql="""
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'igny8_content_taxonomy_map' AND column_name = 'entity_type') THEN
|
||||
ALTER TABLE igny8_content_taxonomy_map RENAME COLUMN entity_type TO content_type;
|
||||
END IF;
|
||||
END $$;
|
||||
""",
|
||||
reverse_sql="""
|
||||
ALTER TABLE igny8_content_taxonomy_map RENAME COLUMN content_type TO entity_type;
|
||||
"""
|
||||
),
|
||||
|
||||
# Rename database columns for TaxonomyTerms
|
||||
migrations.RunSQL(
|
||||
sql="""
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM information_schema.columns
|
||||
WHERE table_name = 'igny8_taxonomy_terms' AND column_name = 'entity_type') THEN
|
||||
ALTER TABLE igny8_taxonomy_terms RENAME COLUMN entity_type TO content_type;
|
||||
END IF;
|
||||
END $$;
|
||||
""",
|
||||
reverse_sql="""
|
||||
ALTER TABLE igny8_taxonomy_terms RENAME COLUMN content_type TO entity_type;
|
||||
"""
|
||||
),
|
||||
]
|
||||
Reference in New Issue
Block a user