# IGNY8 Unified Content Architecture - Quick Reference ## ✅ What Changed ### Old Way ❌ ```python # 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 ✅ ```python # 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) ```python 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="

Best SEO Tools...

", # 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) ```python 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) ```python 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 ```python 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) ```python 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 ```python 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 ```python 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 ```python # 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 ```python # 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 ```python # 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:** ```python # ❌ 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:** ```python # ✅ 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: ```python # 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.