Files
igny8/backend/NEW_ARCHITECTURE_GUIDE.md
IGNY8 VPS (Salman) 55dfd5ad19 Enhance Content Management with New Taxonomy and Attribute Models
- Introduced `ContentTaxonomy` and `ContentAttribute` models for improved content categorization and attribute management.
- Updated `Content` model to support new fields for content format, cluster role, and external type.
- Refactored serializers and views to accommodate new models, including `ContentTaxonomySerializer` and `ContentAttributeSerializer`.
- Added new API endpoints for managing taxonomies and attributes, enhancing the content management capabilities.
- Updated admin interfaces for `Content`, `ContentTaxonomy`, and `ContentAttribute` to reflect new structures and improve usability.
- Implemented backward compatibility for existing attribute mappings.
- Enhanced filtering and search capabilities in the API for better content retrieval.
2025-11-22 00:21:00 +00:00

11 KiB

IGNY8 Unified Content Architecture - Quick Reference

What Changed

Old Way

# Scattered entity types
task.entity_type = 'blog_post'
task.content_type = 'article'
task.content_structure = 'pillar_page'

# JSON arrays for taxonomies
content.categories = ['SEO', 'WordPress']
content.tags = ['tutorial', 'guide']

# Fragmented attributes
ContentAttributeMap(name='Color', value='Blue')

New Way

# Single unified entity type
content.entity_type = 'post'  # What it is
content.content_format = 'article'  # How it's structured
content.cluster_role = 'hub'  # Semantic role

# Real M2M relationships
content.taxonomies.add(seo_category)
content.taxonomies.add(tutorial_tag)

# Enhanced attributes with WP sync
ContentAttribute(
    content=content,
    attribute_type='product_spec',
    name='Color',
    value='Blue',
    external_id=101,  # WP term ID
    external_attribute_name='pa_color'
)

📚 Core Models

1. Content (Enhanced)

from igny8_core.business.content.models import Content

# Create content
content = Content.objects.create(
    title="Best SEO Tools 2025",
    entity_type='post',  # post, page, product, service, taxonomy_term
    content_format='listicle',  # article, listicle, guide, comparison, review
    cluster_role='hub',  # hub, supporting, attribute
    html_content="<h1>Best SEO Tools...</h1>",
    
    # WordPress sync
    external_id=427,  # WP post ID
    external_url="https://site.com/seo-tools/",
    external_type='post',  # WP post_type
    source='wordpress',
    sync_status='imported',
    
    # SEO
    meta_title="15 Best SEO Tools...",
    primary_keyword="seo tools",
    
    # Relationships
    cluster=seo_cluster,
    site=site,
    sector=sector,
)

# Add taxonomies
content.taxonomies.add(seo_category, tools_tag)

2. ContentTaxonomy (New)

from igny8_core.business.content.models import ContentTaxonomy

# WordPress category
category = ContentTaxonomy.objects.create(
    name="SEO",
    slug="seo",
    taxonomy_type='category',  # category, tag, product_cat, product_tag, product_attr
    description="All about SEO",
    
    # WordPress sync
    external_id=12,  # WP term ID
    external_taxonomy='category',  # WP taxonomy name
    sync_status='imported',
    count=45,  # Post count from WP
    
    site=site,
    sector=sector,
)

# Map to semantic clusters
category.clusters.add(seo_cluster, content_marketing_cluster)

# Hierarchical taxonomy
subcategory = ContentTaxonomy.objects.create(
    name="Technical SEO",
    slug="technical-seo",
    taxonomy_type='category',
    parent=category,  # Parent category
    site=site,
    sector=sector,
)

3. ContentAttribute (Enhanced)

from igny8_core.business.content.models import ContentAttribute

# WooCommerce product attribute
attribute = ContentAttribute.objects.create(
    content=product_content,
    attribute_type='product_spec',  # product_spec, service_modifier, semantic_facet
    name='Color',
    value='Blue',
    
    # WooCommerce sync
    external_id=101,  # WP attribute term ID
    external_attribute_name='pa_color',  # WP attribute slug
    
    source='wordpress',
    site=site,
    sector=sector,
)

# Semantic cluster attribute
semantic_attr = ContentAttribute.objects.create(
    cluster=enterprise_seo_cluster,
    attribute_type='semantic_facet',
    name='Target Audience',
    value='Enterprise',
    source='manual',
    site=site,
    sector=sector,
)

🔄 WordPress Sync Workflows

Scenario 1: Import WP Categories

from igny8_core.business.content.models import ContentTaxonomy

# Fetch from WP /wp-json/wp/v2/categories
wp_categories = [
    {'id': 12, 'name': 'SEO', 'slug': 'seo', 'count': 45},
    {'id': 15, 'name': 'WordPress', 'slug': 'wordpress', 'count': 32},
]

for wp_cat in wp_categories:
    taxonomy, created = ContentTaxonomy.objects.update_or_create(
        site=site,
        external_id=wp_cat['id'],
        external_taxonomy='category',
        defaults={
            'name': wp_cat['name'],
            'slug': wp_cat['slug'],
            'taxonomy_type': 'category',
            'count': wp_cat['count'],
            'sync_status': 'imported',
            'sector': site.sectors.first(),
        }
    )

Scenario 2: Import WP Posts (Titles Only)

from igny8_core.business.content.models import Content, ContentTaxonomy

# Fetch from WP /wp-json/wp/v2/posts
wp_posts = [
    {
        'id': 427,
        'title': {'rendered': 'Best SEO Tools 2025'},
        'link': 'https://site.com/seo-tools/',
        'type': 'post',
        'categories': [12, 15],
        'tags': [45, 67],
    }
]

for wp_post in wp_posts:
    # Create content (title only, no html_content yet)
    content, created = Content.objects.update_or_create(
        site=site,
        external_id=wp_post['id'],
        defaults={
            'title': wp_post['title']['rendered'],
            'entity_type': 'post',
            'external_url': wp_post['link'],
            'external_type': wp_post['type'],
            'source': 'wordpress',
            'sync_status': 'imported',
            'sector': site.sectors.first(),
        }
    )
    
    # Map categories
    for cat_id in wp_post['categories']:
        try:
            taxonomy = ContentTaxonomy.objects.get(
                site=site,
                external_id=cat_id,
                taxonomy_type='category'
            )
            content.taxonomies.add(taxonomy)
        except ContentTaxonomy.DoesNotExist:
            pass
    
    # Map tags
    for tag_id in wp_post['tags']:
        try:
            taxonomy = ContentTaxonomy.objects.get(
                site=site,
                external_id=tag_id,
                taxonomy_type='tag'
            )
            content.taxonomies.add(taxonomy)
        except ContentTaxonomy.DoesNotExist:
            pass

Scenario 3: Fetch Full Content On-Demand

def fetch_full_content(content_id):
    """Fetch full HTML content from WP when needed for AI analysis."""
    content = Content.objects.get(id=content_id)
    
    if content.source == 'wordpress' and content.external_id:
        # Fetch from WP /wp-json/wp/v2/posts/{external_id}
        wp_response = requests.get(
            f"{content.site.url}/wp-json/wp/v2/posts/{content.external_id}"
        )
        wp_data = wp_response.json()
        
        # Update content
        content.html_content = wp_data['content']['rendered']
        content.word_count = len(wp_data['content']['rendered'].split())
        content.meta_title = wp_data.get('yoast_head_json', {}).get('title', '')
        content.meta_description = wp_data.get('yoast_head_json', {}).get('description', '')
        content.save()
    
    return content

Scenario 4: Import WooCommerce Product Attributes

from igny8_core.business.content.models import Content, ContentAttribute

# Fetch from WP /wp-json/wc/v3/products/{id}
wp_product = {
    'id': 88,
    'name': 'Blue Widget',
    'type': 'simple',
    'attributes': [
        {'id': 1, 'name': 'Color', 'slug': 'pa_color', 'option': 'Blue'},
        {'id': 2, 'name': 'Size', 'slug': 'pa_size', 'option': 'Large'},
    ]
}

# Create product content
product = Content.objects.create(
    site=site,
    title=wp_product['name'],
    entity_type='product',
    external_id=wp_product['id'],
    external_type='product',
    source='wordpress',
    sync_status='imported',
    sector=site.sectors.first(),
)

# Import attributes
for attr in wp_product['attributes']:
    ContentAttribute.objects.create(
        content=product,
        attribute_type='product_spec',
        name=attr['name'],
        value=attr['option'],
        external_attribute_name=attr['slug'],
        source='wordpress',
        site=site,
        sector=site.sectors.first(),
    )

🔍 Query Examples

Find Content by Entity Type

# All blog posts
posts = Content.objects.filter(entity_type='post')

# All listicles
listicles = Content.objects.filter(entity_type='post', content_format='listicle')

# All hub pages
hubs = Content.objects.filter(cluster_role='hub')

# All WP-synced products
products = Content.objects.filter(
    entity_type='product',
    source='wordpress',
    sync_status='imported'
)

Find Taxonomies

# All categories with WP sync
categories = ContentTaxonomy.objects.filter(
    taxonomy_type='category',
    external_id__isnull=False
)

# Product attributes (color, size, etc.)
product_attrs = ContentTaxonomy.objects.filter(taxonomy_type='product_attr')

# Taxonomies mapped to a cluster
cluster_terms = ContentTaxonomy.objects.filter(clusters=seo_cluster)

# Get all content for a taxonomy
seo_content = Content.objects.filter(taxonomies=seo_category)

Find Attributes

# All product specs for a content
specs = ContentAttribute.objects.filter(
    content=product,
    attribute_type='product_spec'
)

# All attributes in a cluster
cluster_attrs = ContentAttribute.objects.filter(
    cluster=enterprise_cluster,
    attribute_type='semantic_facet'
)

# Find content by attribute value
blue_products = Content.objects.filter(
    attributes__name='Color',
    attributes__value='Blue'
)

📊 Relationships Diagram

Site
 ├─ Content (post, page, product, service, taxonomy_term)
 │   ├─ entity_type (what it is)
 │   ├─ content_format (how it's structured)
 │   ├─ cluster_role (semantic role)
 │   ├─ cluster FK → Clusters
 │   ├─ taxonomies M2M → ContentTaxonomy
 │   └─ attributes FK ← ContentAttribute
 │
 ├─ ContentTaxonomy (category, tag, product_cat, product_tag, product_attr)
 │   ├─ external_id (WP term ID)
 │   ├─ external_taxonomy (WP taxonomy name)
 │   ├─ parent FK → self (hierarchical)
 │   ├─ clusters M2M → Clusters
 │   └─ contents M2M ← Content
 │
 └─ Clusters
     ├─ contents FK ← Content
     ├─ taxonomy_terms M2M ← ContentTaxonomy
     └─ attributes FK ← ContentAttribute

⚠️ Migration Notes

Deprecated Fields (Still Available)

Don't use these anymore:

# ❌ Old way
task.content = "..."  # Use Content.html_content
task.entity_type = "..."  # Use Content.entity_type
content.categories = ["SEO"]  # Use content.taxonomies M2M
content.tags = ["tutorial"]  # Use content.taxonomies M2M

Use these instead:

# ✅ New way
content.html_content = "..."
content.entity_type = "post"
content.taxonomies.add(seo_category)
content.taxonomies.add(tutorial_tag)

Backward Compatibility

Legacy values still work:

# These still map correctly
content.entity_type = 'blog_post'  # → internally handled as 'post'
content.entity_type = 'article'  # → internally handled as 'post'

🚀 Next: Frontend Integration

Ready for Phase 4:

  1. Site Settings → "Content Types" tab
  2. Display imported taxonomies
  3. Enable/disable sync per type
  4. Set fetch limits
  5. Trigger AI semantic mapping

Questions? Check /data/app/igny8/backend/MIGRATION_SUMMARY.md for full migration details.