This commit is contained in:
IGNY8 VPS (Salman)
2025-11-29 08:59:31 +00:00
parent 4bea79a76d
commit 4237c203b4
18 changed files with 507 additions and 52 deletions

View File

@@ -0,0 +1,111 @@
#!/usr/bin/env python
"""
Fix missing taxonomy relationships for existing content
This script will:
1. Find content that should have tags/categories based on their keywords
2. Create appropriate taxonomy terms
3. Link them to the content
"""
import os
import sys
import django
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'igny8_core.settings')
django.setup()
from django.db import transaction
from django.utils.text import slugify
from igny8_core.business.content.models import Content, ContentTaxonomy
print("=" * 80)
print("FIXING MISSING TAXONOMY RELATIONSHIPS")
print("=" * 80)
# Get all content without taxonomy terms
content_without_tags = Content.objects.filter(taxonomy_terms__isnull=True).distinct()
print(f"\nFound {content_without_tags.count()} content items without tags/categories")
fixed_count = 0
for content in content_without_tags:
print(f"\nProcessing Content #{content.id}: {content.title[:50]}...")
# Generate tags from keywords
tags_to_add = []
categories_to_add = []
# Use primary keyword as a tag
if content.primary_keyword:
tags_to_add.append(content.primary_keyword)
# Use secondary keywords as tags
if content.secondary_keywords and isinstance(content.secondary_keywords, list):
tags_to_add.extend(content.secondary_keywords[:3]) # Limit to 3
# Create category based on content_type and cluster
if content.cluster:
categories_to_add.append(content.cluster.name)
# Add content_structure as category
if content.content_structure:
structure_name = content.content_structure.replace('_', ' ').title()
categories_to_add.append(structure_name)
with transaction.atomic():
# Process tags
for tag_name in tags_to_add:
if tag_name and isinstance(tag_name, str):
tag_name = tag_name.strip()
if tag_name:
try:
tag_obj, created = ContentTaxonomy.objects.get_or_create(
site=content.site,
name=tag_name,
taxonomy_type='tag',
defaults={
'slug': slugify(tag_name),
'sector': content.sector,
'account': content.account,
'description': '',
'external_taxonomy': '',
'sync_status': '',
'count': 0,
'metadata': {},
}
)
content.taxonomy_terms.add(tag_obj)
print(f" + Tag: {tag_name} ({'created' if created else 'existing'})")
except Exception as e:
print(f" ✗ Failed to add tag '{tag_name}': {e}")
# Process categories
for category_name in categories_to_add:
if category_name and isinstance(category_name, str):
category_name = category_name.strip()
if category_name:
try:
category_obj, created = ContentTaxonomy.objects.get_or_create(
site=content.site,
name=category_name,
taxonomy_type='category',
defaults={
'slug': slugify(category_name),
'sector': content.sector,
'account': content.account,
'description': '',
'external_taxonomy': '',
'sync_status': '',
'count': 0,
'metadata': {},
}
)
content.taxonomy_terms.add(category_obj)
print(f" + Category: {category_name} ({'created' if created else 'existing'})")
except Exception as e:
print(f" ✗ Failed to add category '{category_name}': {e}")
fixed_count += 1
print("\n" + "=" * 80)
print(f"FIXED {fixed_count} CONTENT ITEMS")
print("=" * 80)

View File

@@ -8,6 +8,7 @@ from typing import Dict, List, Any
from django.db import transaction
from igny8_core.ai.base import BaseAIFunction
from igny8_core.modules.writer.models import Tasks, Content
from igny8_core.business.content.models import ContentTaxonomy
from igny8_core.ai.ai_core import AICore
from igny8_core.ai.validators import validate_tasks_exist
from igny8_core.ai.prompts import PromptRegistry
@@ -183,6 +184,7 @@ class GenerateContentFunction(BaseAIFunction):
# Extract tags and categories from AI response
tags_from_response = parsed.get('tags', [])
categories_from_response = parsed.get('categories', [])
logger.info(f"Extracted from AI response - Tags: {tags_from_response}, Categories: {categories_from_response}")
else:
# Plain text response
content_html = str(parsed)
@@ -224,9 +226,13 @@ class GenerateContentFunction(BaseAIFunction):
sector=task.sector,
)
logger.info(f"Created content record ID: {content_record.id}")
logger.info(f"Processing {len(tags_from_response) if tags_from_response else 0} tags and {len(categories_from_response) if categories_from_response else 0} categories")
# Link taxonomy terms from task if available
if task.taxonomy_term:
content_record.taxonomy_terms.add(task.taxonomy_term)
logger.info(f"Added task taxonomy term: {task.taxonomy_term.name}")
# Process tags from AI response
if tags_from_response and isinstance(tags_from_response, list):
@@ -237,7 +243,7 @@ class GenerateContentFunction(BaseAIFunction):
if tag_name:
try:
# Get or create tag taxonomy term
tag_obj, _ = ContentTaxonomy.objects.get_or_create(
tag_obj, created = ContentTaxonomy.objects.get_or_create(
site=task.site,
name=tag_name,
taxonomy_type='tag',
@@ -245,11 +251,17 @@ class GenerateContentFunction(BaseAIFunction):
'slug': slugify(tag_name),
'sector': task.sector,
'account': task.account,
'description': '', # Required by database
'external_taxonomy': '', # Required by database
'sync_status': '', # Required by database
'count': 0, # Required by database
'metadata': {}, # Required by database
}
)
content_record.taxonomy_terms.add(tag_obj)
logger.info(f"{'Created' if created else 'Found'} and linked tag: {tag_name} (ID: {tag_obj.id})")
except Exception as e:
logger.warning(f"Failed to add tag '{tag_name}': {e}")
logger.error(f"Failed to add tag '{tag_name}': {e}", exc_info=True)
# Process categories from AI response
if categories_from_response and isinstance(categories_from_response, list):
@@ -260,7 +272,7 @@ class GenerateContentFunction(BaseAIFunction):
if category_name:
try:
# Get or create category taxonomy term
category_obj, _ = ContentTaxonomy.objects.get_or_create(
category_obj, created = ContentTaxonomy.objects.get_or_create(
site=task.site,
name=category_name,
taxonomy_type='category',
@@ -268,11 +280,17 @@ class GenerateContentFunction(BaseAIFunction):
'slug': slugify(category_name),
'sector': task.sector,
'account': task.account,
'description': '', # Required by database
'external_taxonomy': '', # Required by database
'sync_status': '', # Required by database
'count': 0, # Required by database
'metadata': {}, # Required by database
}
)
content_record.taxonomy_terms.add(category_obj)
logger.info(f"{'Created' if created else 'Found'} and linked category: {category_name} (ID: {category_obj.id})")
except Exception as e:
logger.warning(f"Failed to add category '{category_name}': {e}")
logger.error(f"Failed to add category '{category_name}': {e}", exc_info=True)
# STAGE 3: Update task status to completed
task.status = 'completed'

View File

@@ -297,8 +297,8 @@ class ContentTaxonomy(SiteSectorBaseModel):
external_taxonomy = models.CharField(
max_length=100,
blank=True,
null=True,
help_text="WordPress taxonomy slug (category, post_tag, product_cat, pa_*) - null for cluster taxonomies"
default='',
help_text="WordPress taxonomy slug (category, post_tag, product_cat, pa_*) - empty for cluster taxonomies"
)
external_id = models.IntegerField(
null=True,
@@ -306,6 +306,26 @@ class ContentTaxonomy(SiteSectorBaseModel):
db_index=True,
help_text="WordPress term_id - null for cluster taxonomies"
)
description = models.TextField(
blank=True,
default='',
help_text="Taxonomy term description"
)
sync_status = models.CharField(
max_length=50,
blank=True,
default='',
help_text="Synchronization status with external platforms"
)
count = models.IntegerField(
default=0,
help_text="Number of times this term is used"
)
metadata = models.JSONField(
default=dict,
blank=True,
help_text="Additional metadata for the taxonomy term"
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

View File

@@ -86,11 +86,12 @@ class ImagesAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
@admin.register(Content)
class ContentAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
list_display = ['title', 'content_type', 'content_structure', 'site', 'sector', 'source', 'status', 'created_at']
list_display = ['title', 'content_type', 'content_structure', 'site', 'sector', 'source', 'status', 'get_taxonomy_count', 'created_at']
list_filter = ['content_type', 'content_structure', 'source', 'status', 'site', 'sector', 'created_at']
search_fields = ['title', 'content_html', 'external_url']
ordering = ['-created_at']
readonly_fields = ['created_at', 'updated_at']
readonly_fields = ['created_at', 'updated_at', 'word_count']
filter_horizontal = ['taxonomy_terms'] # Add many-to-many widget for taxonomy terms
fieldsets = (
('Basic Info', {
@@ -99,8 +100,16 @@ class ContentAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
('Content Classification', {
'fields': ('content_type', 'content_structure', 'source')
}),
('Taxonomy Terms (Tags & Categories)', {
'fields': ('taxonomy_terms',),
'description': 'Select tags and categories for this content'
}),
('Content', {
'fields': ('content_html',)
'fields': ('content_html', 'word_count')
}),
('SEO', {
'fields': ('meta_title', 'meta_description', 'primary_keyword', 'secondary_keywords'),
'classes': ('collapse',)
}),
('WordPress Sync', {
'fields': ('external_id', 'external_url'),
@@ -112,6 +121,11 @@ class ContentAdmin(SiteSectorAdminMixin, admin.ModelAdmin):
}),
)
def get_taxonomy_count(self, obj):
"""Display count of associated taxonomy terms"""
return obj.taxonomy_terms.count()
get_taxonomy_count.short_description = 'Taxonomy Count'
def get_site_display(self, obj):
"""Safely get site name"""
try:

View File

@@ -154,6 +154,8 @@ class ContentSerializer(serializers.ModelSerializer):
cluster_name = serializers.SerializerMethodField()
sector_name = serializers.SerializerMethodField()
taxonomy_terms_data = serializers.SerializerMethodField()
tags = serializers.SerializerMethodField()
categories = serializers.SerializerMethodField()
has_image_prompts = serializers.SerializerMethodField()
image_status = serializers.SerializerMethodField()
has_generated_images = serializers.SerializerMethodField()
@@ -175,6 +177,8 @@ class ContentSerializer(serializers.ModelSerializer):
'content_type',
'content_structure',
'taxonomy_terms_data',
'tags',
'categories',
'external_id',
'external_url',
'source',
@@ -246,6 +250,20 @@ class ContentSerializer(serializers.ModelSerializer):
for term in obj.taxonomy_terms.all()
]
def get_tags(self, obj):
"""Get only tags (taxonomy_type='tag')"""
return [
term.name
for term in obj.taxonomy_terms.filter(taxonomy_type='tag')
]
def get_categories(self, obj):
"""Get only categories (taxonomy_type='category')"""
return [
term.name
for term in obj.taxonomy_terms.filter(taxonomy_type='category')
]
def get_has_image_prompts(self, obj):
"""Check if content has any image prompts (images with prompts)"""
return obj.images.filter(prompt__isnull=False).exclude(prompt='').exists()

View File

@@ -0,0 +1,41 @@
#!/usr/bin/env python
import os
import sys
import django
# Add the backend directory to the path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
# Setup Django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'igny8_core.settings')
django.setup()
from igny8_core.modules.writer.models import Content
from igny8_core.modules.writer.serializers import ContentSerializer
print("Testing ContentSerializer tags and categories fields...")
print("=" * 60)
# Get a content record
content = Content.objects.first()
if content:
serializer = ContentSerializer(content)
data = serializer.data
print(f"Content ID: {data['id']}")
print(f"Title: {data.get('title', 'N/A')}")
print(f"Tags: {data.get('tags', [])}")
print(f"Categories: {data.get('categories', [])}")
print(f"Taxonomy Terms Data: {len(data.get('taxonomy_terms_data', []))} items")
# Show taxonomy terms breakdown
taxonomy_terms = data.get('taxonomy_terms_data', [])
if taxonomy_terms:
print("\nTaxonomy Terms Details:")
for term in taxonomy_terms:
print(f" - {term['name']} ({term['taxonomy_type']})")
print("\n✓ Serializer fields test passed!")
else:
print("No content found in database")
print("This is expected if no content has been generated yet")

129
backend/verify_taxonomy.py Normal file
View File

@@ -0,0 +1,129 @@
#!/usr/bin/env python
"""
Verify Tags and Categories Implementation
Tests that ContentTaxonomy integration is working correctly
"""
import os
import sys
import django
# Add the backend directory to the path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
# Setup Django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'igny8_core.settings')
django.setup()
from igny8_core.business.content.models import Content, ContentTaxonomy
from igny8_core.modules.writer.serializers import ContentSerializer
print("=" * 80)
print("VERIFYING TAGS AND CATEGORIES IMPLEMENTATION")
print("=" * 80)
# Check if ContentTaxonomy model is accessible
print("\n1. ContentTaxonomy Model Check:")
try:
taxonomy_count = ContentTaxonomy.objects.count()
print(f" ✓ ContentTaxonomy model accessible")
print(f" ✓ Total taxonomy terms in database: {taxonomy_count}")
# Show breakdown by type
tag_count = ContentTaxonomy.objects.filter(taxonomy_type='tag').count()
category_count = ContentTaxonomy.objects.filter(taxonomy_type='category').count()
print(f" - Tags: {tag_count}")
print(f" - Categories: {category_count}")
except Exception as e:
print(f" ✗ Error accessing ContentTaxonomy: {e}")
sys.exit(1)
# Check Content model has taxonomy_terms field
print("\n2. Content Model Taxonomy Field Check:")
try:
content = Content.objects.first()
if content:
taxonomy_terms = content.taxonomy_terms.all()
print(f" ✓ Content.taxonomy_terms field accessible")
print(f" ✓ Sample content (ID: {content.id}) has {taxonomy_terms.count()} taxonomy terms")
for term in taxonomy_terms:
print(f" - {term.name} ({term.taxonomy_type})")
else:
print(" ⚠ No content found in database")
except Exception as e:
print(f" ✗ Error accessing Content.taxonomy_terms: {e}")
sys.exit(1)
# Check serializer includes tags and categories
print("\n3. ContentSerializer Tags/Categories Check:")
try:
if content:
serializer = ContentSerializer(content)
data = serializer.data
# Check if fields exist
has_tags_field = 'tags' in data
has_categories_field = 'categories' in data
has_taxonomy_data = 'taxonomy_terms_data' in data
print(f" ✓ Serializer includes 'tags' field: {has_tags_field}")
print(f" ✓ Serializer includes 'categories' field: {has_categories_field}")
print(f" ✓ Serializer includes 'taxonomy_terms_data' field: {has_taxonomy_data}")
if has_tags_field:
print(f" - Tags: {data.get('tags', [])}")
if has_categories_field:
print(f" - Categories: {data.get('categories', [])}")
else:
print(" ⚠ No content to serialize")
except Exception as e:
print(f" ✗ Error serializing content: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
# Check if we can create taxonomy terms
print("\n4. Creating Test Taxonomy Terms:")
try:
from django.utils.text import slugify
# Try to create a test tag
test_tag, created = ContentTaxonomy.objects.get_or_create(
name="Test Tag",
taxonomy_type='tag',
defaults={
'slug': slugify("Test Tag"),
}
)
if created:
print(f" ✓ Created new test tag: {test_tag.name}")
else:
print(f" ✓ Test tag already exists: {test_tag.name}")
# Try to create a test category
test_category, created = ContentTaxonomy.objects.get_or_create(
name="Test Category",
taxonomy_type='category',
defaults={
'slug': slugify("Test Category"),
}
)
if created:
print(f" ✓ Created new test category: {test_category.name}")
else:
print(f" ✓ Test category already exists: {test_category.name}")
except Exception as e:
print(f" ✗ Error creating taxonomy terms: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
print("\n" + "=" * 80)
print("VERIFICATION COMPLETE")
print("=" * 80)
print("\nNext steps:")
print("1. Access Django admin at /admin/writer/contenttaxonomy/")
print("2. Generate content via AI and check if tags/categories are saved")
print("3. Check API response includes 'tags' and 'categories' fields")
print("=" * 80)