docs updates

This commit is contained in:
IGNY8 VPS (Salman)
2025-12-03 13:03:14 +00:00
parent 316cafab1b
commit de425e0e93
13 changed files with 2203 additions and 682 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,263 @@
# Keywords, Clusters & Ideas - Complete Field Mapping
**Date:** December 3, 2025
---
## 📊 KEYWORDS - Complete Field Reference
### Backend Model Fields
**File:** `backend/igny8_core/business/planning/models.py`
| Field Name | Type | Default | Required | Choices | Notes |
|------------|------|---------|----------|---------|-------|
| `id` | Integer PK | Auto | ✅ | - | Primary key |
| `seed_keyword` | ForeignKey | - | ✅ | - | Links to SeedKeyword (PROTECT on delete) |
| `volume_override` | Integer | NULL | ❌ | - | Site-specific override (uses seed_keyword.volume if NULL) |
| `difficulty_override` | Integer | NULL | ❌ | - | Site-specific override (uses seed_keyword.difficulty if NULL) |
| `attribute_values` | JSONField | [] | ❌ | - | Optional metadata (product specs, modifiers) |
| `cluster` | ForeignKey | NULL | ❌ | - | Parent cluster (SET_NULL on delete) |
| `status` | CharField(50) | `pending` | ✅ | `pending`, `active`, `archived` | Keyword status |
| `site` | ForeignKey | - | ✅ | - | Owner site (inherited) |
| `sector` | ForeignKey | - | ✅ | - | Owner sector (inherited) |
| `account` | ForeignKey | - | ✅ | - | Owner account (inherited) |
| `created_at` | DateTime | Auto | ✅ | - | Auto-generated |
| `updated_at` | DateTime | Auto | ✅ | - | Auto-updated |
### Frontend Table Columns
**File:** `frontend/src/config/pages/keywords.config.tsx`
| Column Key | Label | Visible Default | Sortable | Render Notes |
|----------|--------|-----------------|----------|--------------|
| `keyword` | Keyword | ✅ Yes | ✅ Yes | From seed_keyword.keyword (links to Keywords page) |
| `sector_name` | Sector | Conditional* | ✅ Yes | Badge (blue) - shown when viewing all sectors |
| `volume` | Volume | ✅ Yes | ✅ Yes | Formatted as number (e.g., 1,250) |
| `cluster_name` | Cluster | ✅ Yes | ✅ Yes | Parent cluster name or "-" |
| `difficulty` | Difficulty | ✅ Yes | ✅ Yes | Badge (1-5): 1-2=green, 3=amber, 4-5=red |
| `intent` | Intent | ✅ Yes | ✅ Yes | Badge colors: Transactional/Commercial=green, Navigational=amber |
| `status` | Status | ✅ Yes | ✅ Yes | Badge: pending=amber, active=green, archived=red |
| `created_at` | Created | ✅ Yes | ✅ Yes | Relative date (e.g., "2 hours ago") |
| (Hidden by default) | | | | |
| `updated_at` | Updated | ❌ No | ✅ Yes | Relative date |
### Frontend Filter Dropdown
**File:** `frontend/src/config/pages/keywords.config.tsx` (Lines 310-360)
| Filter Key | Label | Type | Options | Dynamic |
|-----------|-------|------|---------|---------|
| `search` | Search | Text | N/A | - |
| `status` | Status | Select | `pending`, `active`, `archived` | ❌ No |
| `intent` | Intent | Select | `informational`, `navigational`, `transactional`, `commercial` | ❌ No |
| `difficulty` | Difficulty | Select | `1-5` with labels | ❌ No |
| `cluster` | Cluster | Select | Dynamic from database | ✅ Yes (loads clusters) |
| `volume` | Volume Range | Custom | Min/Max number inputs | ❌ No (range picker) |
### Frontend Create/Edit Form
**File:** `frontend/src/config/pages/keywords.config.tsx` (Lines 560-586)
| Field Key | Label | Type | Required | Default | Options |
|-----------|-------|------|----------|---------|---------|
| `seed_keyword_id` | Seed Keyword | Select | ✅ Yes | - | Dynamic from availableSeedKeywords |
| `volume_override` | Volume Override | Number | ❌ No | NULL | Numeric input (optional override) |
| `difficulty_override` | Difficulty Override | Number | ❌ No | NULL | Select 1-5 |
| `cluster_id` | Cluster | Select | ❌ No | NULL | Dynamic from clusters array |
| `status` | Status | Select | ✅ Yes | `pending` | `pending`, `active`, `archived` |
---
## 📊 CLUSTERS - Complete Field Reference
### Backend Model Fields
**File:** `backend/igny8_core/business/planning/models.py`
| Field Name | Type | Default | Required | Choices | Notes |
|------------|------|---------|----------|---------|-------|
| `id` | Integer PK | Auto | ✅ | - | Primary key |
| `name` | CharField(255) | - | ✅ | - | Unique cluster name (unique=True) |
| `description` | TextField | NULL | ❌ | - | Optional cluster description |
| `keywords_count` | Integer | 0 | ✅ | - | Cached count of linked keywords |
| `ideas_count` | Integer | 0 | ✅ | - | Cached count of linked ideas |
| `volume` | Integer | 0 | ✅ | - | Cached total volume from keywords |
| `mapped_pages` | Integer | 0 | ✅ | - | Number of mapped pages |
| `status` | CharField(50) | `new` | ✅ | `new`, `idea`, `mapped` | Cluster status |
| `site` | ForeignKey | - | ✅ | - | Owner site (inherited) |
| `sector` | ForeignKey | - | ✅ | - | Owner sector (inherited) |
| `account` | ForeignKey | - | ✅ | - | Owner account (inherited) |
| `created_at` | DateTime | Auto | ✅ | - | Auto-generated |
| `updated_at` | DateTime | Auto | ✅ | - | Auto-updated |
### Frontend Table Columns
**File:** `frontend/src/config/pages/clusters.config.tsx`
| Column Key | Label | Visible Default | Sortable | Render Notes |
|----------|--------|-----------------|----------|--------------|
| `name` | Cluster Name | ✅ Yes | ✅ Yes | Link to cluster detail page |
| `sector_name` | Sector | Conditional* | ✅ Yes | Badge (blue) - shown when viewing all sectors |
| `keywords_count` | Keywords | ✅ Yes | ✅ Yes | Formatted as number (e.g., 45) |
| `ideas_count` | Ideas | ✅ Yes | ✅ Yes | Formatted as number (e.g., 12) |
| `volume` | Volume | ✅ Yes | ✅ Yes | Formatted as number (e.g., 5,280) |
| `difficulty` | Difficulty | ✅ Yes | ✅ Yes | Badge (1-5): 1-2=green, 3=amber, 4-5=red |
| `content_count` | Content | ✅ Yes | ✅ Yes | Formatted as number |
| `status` | Status | ✅ Yes | ✅ Yes | Badge: new=amber, idea=blue, mapped=green |
| `created_at` | Created | ✅ Yes | ✅ Yes | Relative date |
| (Hidden by default) | | | | |
| `description` | Description | ❌ No | ❌ No | Text truncated to 250px |
| `mapped_pages` | Mapped Pages | ❌ No | ✅ Yes | Formatted number |
| `updated_at` | Updated | ❌ No | ✅ Yes | Relative date |
### Frontend Filter Dropdown
**File:** `frontend/src/config/pages/clusters.config.tsx` (Lines 240-290)
| Filter Key | Label | Type | Options | Dynamic |
|-----------|-------|------|---------|---------|
| `search` | Search | Text | N/A | - |
| `status` | Status | Select | `new`, `idea`, `mapped` | ❌ No |
| `difficulty` | Difficulty | Select | `1-5` with labels | ❌ No |
| `volume` | Volume Range | Custom | Min/Max number inputs | ❌ No (range picker) |
### Frontend Create/Edit Form
**File:** `frontend/src/config/pages/clusters.config.tsx` (Lines 405-418)
| Field Key | Label | Type | Required | Default | Options |
|-----------|-------|------|----------|---------|---------|
| `name` | Cluster Name | Text | ✅ Yes | - | Text input (placeholder: "Enter cluster name") |
| `description` | Description | Textarea | ❌ No | NULL | Textarea (placeholder: "Enter cluster description") |
| `status` | Status | Select | ✅ Yes | `new` | `new`, `idea`, `mapped` |
---
## 📊 CONTENT IDEAS - Complete Field Reference
### Backend Model Fields
**File:** `backend/igny8_core/business/planning/models.py`
| Field Name | Type | Default | Required | Choices | Notes |
|------------|------|---------|----------|---------|-------|
| `id` | Integer PK | Auto | ✅ | - | Primary key |
| `idea_title` | CharField(255) | - | ✅ | - | Content idea title |
| `description` | TextField | NULL | ❌ | - | Content outline/description |
| `target_keywords` | CharField(500) | '' | ❌ | - | Comma-separated keywords (legacy) |
| `keyword_objects` | M2M(Keywords) | - | ❌ | - | Individual keywords linked to idea |
| `keyword_cluster` | ForeignKey(Clusters) | NULL | ❌ | - | Parent cluster (SET_NULL on delete) |
| `status` | CharField(50) | `new` | ✅ | `new`, `scheduled`, `completed`, `published` | Idea workflow status |
| `estimated_word_count` | Integer | 1000 | ✅ | - | Target article length |
| `content_type` | CharField(50) | `post` | ✅ | `post`, `page`, `product`, `taxonomy` | Content type |
| `content_structure` | CharField(50) | `article` | ✅ | See structures below | Content format/structure |
| `site` | ForeignKey | - | ✅ | - | Owner site (inherited) |
| `sector` | ForeignKey | - | ✅ | - | Owner sector (inherited) |
| `account` | ForeignKey | - | ✅ | - | Owner account (inherited) |
| `created_at` | DateTime | Auto | ✅ | - | Auto-generated |
| `updated_at` | DateTime | Auto | ✅ | - | Auto-updated |
**Content Structure Choices (based on content_type):**
- **Post:** `article`, `guide`, `comparison`, `review`, `listicle`
- **Page:** `landing_page`, `business_page`, `service_page`, `general`, `cluster_hub`
- **Product:** `product_page`
- **Taxonomy:** `category_archive`, `tag_archive`, `attribute_archive`
### Frontend Table Columns
**File:** `frontend/src/config/pages/ideas.config.tsx`
| Column Key | Label | Visible Default | Sortable | Render Notes |
|----------|--------|-----------------|----------|--------------|
| `idea_title` | Title | ✅ Yes | ✅ Yes | Expandable (shows description) |
| `sector_name` | Sector | Conditional* | ✅ Yes | Badge (blue) - shown when viewing all sectors |
| `content_structure` | Structure | ✅ Yes | ✅ Yes | Badge (purple): article, guide, guide, etc. |
| `content_type` | Type | ✅ Yes | ✅ Yes | Badge (blue): post, page, product, taxonomy |
| `target_keywords` | Target Keywords | ✅ Yes | ❌ No | Text truncated to 250px |
| `keyword_cluster_name` | Cluster | ✅ Yes | ✅ Yes | Parent cluster name or "-" |
| `status` | Status | ✅ Yes | ✅ Yes | Badge: new=amber, scheduled=blue, completed=blue, published=green |
| `estimated_word_count` | Words | ✅ Yes | ✅ Yes | Formatted as number (e.g., 1,500) |
| `created_at` | Created | ✅ Yes | ✅ Yes | Relative date |
| (Hidden by default) | | | | |
| `updated_at` | Updated | ❌ No | ✅ Yes | Relative date |
### Frontend Filter Dropdown
**File:** `frontend/src/config/pages/ideas.config.tsx` (Lines 218-270)
| Filter Key | Label | Type | Options | Dynamic |
|-----------|-------|------|---------|---------|
| `search` | Search | Text | N/A | - |
| `status` | Status | Select | `new`, `scheduled`, `completed`, `published` | ❌ No |
| `content_structure` | Structure | Select | article, guide, comparison, etc. (all 13 options) | ❌ No |
| `content_type` | Type | Select | `post`, `page`, `product`, `taxonomy` | ❌ No |
| `cluster` | Cluster | Select | Dynamic from database | ✅ Yes (loads clusters) |
### Frontend Create/Edit Form
**File:** `frontend/src/config/pages/ideas.config.tsx` (Lines 372-417)
| Field Key | Label | Type | Required | Default | Options |
|-----------|-------|------|----------|---------|---------|
| `idea_title` | Title | Text | ✅ Yes | - | Text input (placeholder: "Enter idea title") |
| `description` | Description | Textarea | ❌ No | NULL | Textarea (placeholder: "Enter content outline") |
| `content_type` | Content Type | Select | ✅ Yes | `post` | `post`, `page`, `product`, `taxonomy` |
| `content_structure` | Content Structure | Select | ✅ Yes | `article` | 13 structure options (depends on content_type) |
| `target_keywords` | Target Keywords | Text | ❌ No | NULL | Text input (comma-separated) |
| `keyword_cluster_id` | Cluster | Select | ❌ No | NULL | Dynamic from clusters array |
| `status` | Status | Select | ✅ Yes | `new` | `new`, `scheduled`, `completed`, `published` |
| `estimated_word_count` | Word Count | Number | ❌ No | 1000 | Numeric input |
---
## 📝 SUMMARY COMPARISON
### Status Fields
| Module | Backend Default | Backend Choices | Frontend Form Default | Frontend Form Choices |
|--------|-----------------|-----------------|----------------------|----------------------|
| **Keywords** | `pending` | pending, active, archived | `pending` | pending, active, archived |
| **Clusters** | `new` | new, idea, mapped | `new` | new, idea, mapped |
| **Ideas** | `new` | new, scheduled, completed, published | `new` | new, scheduled, completed, published |
### Required Fields (Must be filled)
| Module | Required Fields |
|--------|-----------------|
| **Keywords** | seed_keyword_id, status, site, sector, account |
| **Clusters** | name, status, site, sector, account |
| **Ideas** | idea_title, status, content_type, content_structure, estimated_word_count, site, sector, account |
### Optional Fields
| Module | Optional Fields |
|--------|-----------------|
| **Keywords** | volume_override, difficulty_override, attribute_values, cluster_id |
| **Clusters** | description |
| **Ideas** | description, target_keywords, keyword_objects, keyword_cluster_id |
### Dynamic Dropdowns (Loaded from DB)
| Module | Filter | Form |
|--------|--------|------|
| **Keywords** | cluster (dropdown) | seed_keyword_id (all available), cluster_id (all clusters for sector) |
| **Clusters** | - | - |
| **Ideas** | cluster (dropdown) | keyword_cluster_id (all clusters for sector) |
### Visible-by-Default Table Columns
| Module | Count | Primary Columns |
|--------|-------|-----------------|
| **Keywords** | 9 | keyword, volume, cluster, difficulty, intent, status, created_at |
| **Clusters** | 11 | name, keywords_count, ideas_count, volume, difficulty, content_count, status, created_at |
| **Ideas** | 10 | idea_title, content_structure, content_type, target_keywords, cluster, status, word_count, created_at |
---
## 🔑 Key Differences
### Keywords
- **Uses:** SeedKeyword (global pool) - one keyword per site/sector
- **Overrideable:** volume, difficulty (site-specific)
- **Links to:** Clusters (via cluster FK)
- **Status:** pending (awaiting cluster), active (clustered), archived
### Clusters
- **Type:** Pure topic clusters (semantic groupings)
- **Auto-updated:** keywords_count, ideas_count, volume (cached from keywords)
- **Status:** new (no ideas), idea (has ideas), mapped (has content)
- **No overrides:** All values are cached/calculated
### Ideas
- **Type:** Content concepts ready for production
- **Links:** Cluster (required for workflow), Keywords (optional M2M)
- **Customizable:** content_type, content_structure, word_count
- **Status:** new → scheduled (queued to writer) → completed (content generated) → published
---
**END OF COMPLETE FIELD MAPPING**

View File

@@ -0,0 +1,362 @@
# Quick Reference: Content & Taxonomy After SiteBuilder Removal
## Django Admin URLs
```
Content Management:
http://your-domain/admin/writer/content/
Taxonomy Management:
http://your-domain/admin/writer/contenttaxonomy/
Tasks Queue:
http://your-domain/admin/writer/tasks/
```
## Common Django ORM Queries
### Working with Content
```python
from igny8_core.business.content.models import Content, ContentTaxonomy
# Get content with its taxonomy
content = Content.objects.get(id=1)
categories = content.taxonomy_terms.filter(taxonomy_type='category')
tags = content.taxonomy_terms.filter(taxonomy_type='tag')
# Create content with taxonomy
content = Content.objects.create(
account=account,
site=site,
sector=sector,
cluster=cluster,
title="My Article",
content_html="<p>Content here</p>",
content_type='post',
content_structure='article'
)
# Add categories and tags
tech_cat = ContentTaxonomy.objects.get(name='Technology', taxonomy_type='category')
tutorial_tag = ContentTaxonomy.objects.get(name='Tutorial', taxonomy_type='tag')
content.taxonomy_terms.add(tech_cat, tutorial_tag)
# Remove taxonomy
content.taxonomy_terms.remove(tech_cat)
# Clear all taxonomy
content.taxonomy_terms.clear()
```
### Working with Taxonomy
```python
# Create category
category = ContentTaxonomy.objects.create(
account=account,
site=site,
sector=sector,
name='Technology',
slug='technology',
taxonomy_type='category',
description='Tech-related content'
)
# Create tag
tag = ContentTaxonomy.objects.create(
account=account,
site=site,
sector=sector,
name='Tutorial',
slug='tutorial',
taxonomy_type='tag'
)
# Get all content with this taxonomy
tech_content = category.contents.all()
# Get WordPress-synced taxonomy
wp_category = ContentTaxonomy.objects.get(
external_id=5,
external_taxonomy='category',
site=site
)
```
### WordPress Publishing
```python
from igny8_core.tasks.wordpress_publishing import publish_content_to_wordpress
# Publish content (categories/tags extracted automatically)
result = publish_content_to_wordpress.delay(
content_id=content.id,
site_url='https://example.com',
username='admin',
app_password='xxxx xxxx xxxx xxxx'
)
# The task automatically extracts:
categories = [
term.name
for term in content.taxonomy_terms.filter(taxonomy_type='category')
]
tags = [
term.name
for term in content.taxonomy_terms.filter(taxonomy_type='tag')
]
```
## API Endpoints (REST)
```
GET /api/v1/writer/content/ - List all content
POST /api/v1/writer/content/ - Create content
GET /api/v1/writer/content/{id}/ - Get content detail
PATCH /api/v1/writer/content/{id}/ - Update content
DELETE /api/v1/writer/content/{id}/ - Delete content
GET /api/v1/writer/taxonomy/ - List all taxonomy
POST /api/v1/writer/taxonomy/ - Create taxonomy
GET /api/v1/writer/taxonomy/{id}/ - Get taxonomy detail
PATCH /api/v1/writer/taxonomy/{id}/ - Update taxonomy
DELETE /api/v1/writer/taxonomy/{id}/ - Delete taxonomy
POST /api/v1/publisher/publish/ - Publish content
```
## Database Schema
### Content Table (igny8_content)
| Column | Type | Description |
|--------|------|-------------|
| id | PK | Primary key |
| site_id | FK | Multi-tenant site |
| sector_id | FK | Multi-tenant sector |
| cluster_id | FK | Parent cluster (required) |
| title | VARCHAR(255) | Content title |
| content_html | TEXT | Final HTML content |
| word_count | INTEGER | Calculated word count |
| meta_title | VARCHAR(255) | SEO title |
| meta_description | TEXT | SEO description |
| primary_keyword | VARCHAR(255) | Primary SEO keyword |
| secondary_keywords | JSON | Secondary keywords |
| content_type | VARCHAR(50) | post, page, product, taxonomy |
| content_structure | VARCHAR(50) | article, guide, review, etc. |
| external_id | VARCHAR(255) | WordPress post ID |
| external_url | URL | WordPress URL |
| external_type | VARCHAR(100) | WordPress post type |
| sync_status | VARCHAR(50) | Sync status |
| source | VARCHAR(50) | igny8 or wordpress |
| status | VARCHAR(50) | draft, review, published |
### Taxonomy Table (igny8_content_taxonomy_terms)
| Column | Type | Description |
|--------|------|-------------|
| id | PK | Primary key |
| site_id | FK | Multi-tenant site |
| sector_id | FK | Multi-tenant sector |
| name | VARCHAR(255) | Term name |
| slug | VARCHAR(255) | URL slug |
| taxonomy_type | VARCHAR(50) | category or tag |
| description | TEXT | Term description |
| count | INTEGER | Usage count |
| external_taxonomy | VARCHAR(100) | category, post_tag |
| external_id | INTEGER | WordPress term_id |
| metadata | JSON | Additional metadata |
### Relation Table (igny8_content_taxonomy_relations)
| Column | Type | Description |
|--------|------|-------------|
| id | PK | Primary key |
| content_id | FK | Content reference |
| taxonomy_id | FK | Taxonomy reference |
| created_at | TIMESTAMP | Creation timestamp |
| updated_at | TIMESTAMP | Update timestamp |
**Constraints:**
- UNIQUE(content_id, taxonomy_id)
## Workflow Commands
### 1. Run Migrations (When Ready)
```bash
# Apply blueprint removal migration
docker exec -it igny8_backend python manage.py migrate
# Check migration status
docker exec -it igny8_backend python manage.py showmigrations
```
### 2. Create Test Data
```bash
# Django shell
docker exec -it igny8_backend python manage.py shell
# Then in shell:
from igny8_core.auth.models import Account, Site, Sector
from igny8_core.business.planning.models import Keywords, Clusters
from igny8_core.business.content.models import Content, ContentTaxonomy
# Get your site/sector
account = Account.objects.first()
site = account.sites.first()
sector = site.sectors.first()
cluster = Clusters.objects.filter(sector=sector).first()
# Create taxonomy
cat = ContentTaxonomy.objects.create(
account=account,
site=site,
sector=sector,
name='Tech',
slug='tech',
taxonomy_type='category'
)
tag = ContentTaxonomy.objects.create(
account=account,
site=site,
sector=sector,
name='Tutorial',
slug='tutorial',
taxonomy_type='tag'
)
# Create content
content = Content.objects.create(
account=account,
site=site,
sector=sector,
cluster=cluster,
title='Test Article',
content_html='<p>Test content</p>',
content_type='post',
content_structure='article'
)
# Add taxonomy
content.taxonomy_terms.add(cat, tag)
# Verify
print(content.taxonomy_terms.all())
```
### 3. Test WordPress Publishing
```bash
# Check celery is running
docker logs igny8_celery_worker --tail 50
# Check publish logs
tail -f backend/logs/publish-sync-logs/*.log
# Manually trigger publish (Django shell)
from igny8_core.tasks.wordpress_publishing import publish_content_to_wordpress
result = publish_content_to_wordpress.delay(
content_id=1,
site_url='https://your-site.com',
username='admin',
app_password='xxxx xxxx xxxx xxxx'
)
```
## Troubleshooting
### Backend Won't Start
```bash
# Check logs
docker logs igny8_backend --tail 100
# Force recreate (clears Python bytecode cache)
docker compose -f docker-compose.app.yml up -d --force-recreate igny8_backend
# Check for import errors
docker exec -it igny8_backend python manage.py check
```
### Celery Not Processing Tasks
```bash
# Check celery logs
docker logs igny8_celery_worker --tail 100
# Restart celery
docker compose -f docker-compose.app.yml restart igny8_celery_worker
# Test celery connection
docker exec -it igny8_backend python manage.py shell
>>> from celery import current_app
>>> current_app.connection().ensure_connection(max_retries=3)
```
### Migration Issues
```bash
# Check current migrations
docker exec -it igny8_backend python manage.py showmigrations
# Create new migration (if needed)
docker exec -it igny8_backend python manage.py makemigrations
# Fake migration (if tables already dropped manually)
docker exec -it igny8_backend python manage.py migrate site_building 0002 --fake
```
### WordPress Sync Not Working
```bash
# Check publish logs
tail -f backend/logs/publish-sync-logs/*.log
# Check WordPress plugin logs (on WordPress server)
tail -f wp-content/plugins/igny8-bridge/logs/*.log
# Test WordPress REST API manually
curl -X GET https://your-site.com/wp-json/wp/v2/posts \
-u "username:app_password"
```
## File Locations Reference
```
Backend Code:
├─ backend/igny8_core/business/content/models.py # Content & Taxonomy models
├─ backend/igny8_core/business/publishing/models.py # Publishing records
├─ backend/igny8_core/modules/publisher/views.py # Publisher API
├─ backend/igny8_core/tasks/wordpress_publishing.py # WordPress publish task
└─ backend/igny8_core/settings.py # Django settings
Frontend Code:
├─ frontend/src/services/api.ts # API client
└─ frontend/src/modules/writer/ # Writer UI
Documentation:
├─ docs/SITEBUILDER-REMOVAL-SUMMARY.md # This removal summary
├─ docs/TAXONOMY-RELATIONSHIP-DIAGRAM.md # Taxonomy diagrams
├─ docs/02-PLANNER-WRITER-WORKFLOW-TECHNICAL-GUIDE.md # Workflow guide
└─ docs/04-WORDPRESS-BIDIRECTIONAL-SYNC-REFERENCE.md # WordPress sync
Migrations:
└─ backend/igny8_core/business/site_building/migrations/0002_remove_blueprint_models.py
Logs:
├─ backend/logs/publish-sync-logs/*.log # Publishing logs
└─ igny8-wp-plugin/logs/*.log # WordPress plugin logs
```
## Support Resources
1. **Backend Logs:** `docker logs igny8_backend`
2. **Celery Logs:** `docker logs igny8_celery_worker`
3. **Publishing Logs:** `backend/logs/publish-sync-logs/`
4. **Django Admin:** `http://your-domain/admin/`
5. **API Docs:** `http://your-domain/api/v1/`
6. **Workflow Guide:** `docs/02-PLANNER-WRITER-WORKFLOW-TECHNICAL-GUIDE.md`

View File

@@ -0,0 +1,258 @@
# Content Taxonomy Relationship Diagram
## Current Architecture (Simplified)
```
┌─────────────────────────────────────────────────────────────────────┐
│ IGNY8 Content System │
└─────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────┐
│ Multi-Tenant Hierarchy │
│ │
│ Account ──┬── Site ──┬── Sector ──┬── Keywords │
│ │ │ ├── Clusters │
│ │ │ ├── Ideas │
│ │ │ ├── Tasks │
│ │ │ ├── Content │
│ │ │ └── ContentTaxonomy │
│ │ └── Sector 2 │
│ └── Site 2 │
└──────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────┐
│ Planner → Writer → Publisher Workflow │
│ │
│ Phase 1-3: PLANNER │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Keywords │ ──> │ Clusters │ ──> │ Ideas │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │ │
│ │ │ │ │
│ Phase 4: WRITER │
│ │ │ │ │
│ └─────────────────┴─────────────────┘ │
│ │ │
│ v │
│ ┌──────────┐ ┌──────────┐ │
│ │ Tasks │ ──> │ Content │ │
│ └──────────┘ └──────────┘ │
│ │ │
│ │ │
│ Phase 5: PUBLISHER │ │
│ v │
│ ┌─────────────┐ │
│ │ WordPress │ │
│ │ Shopify │ │
│ │ Sites │ │
│ └─────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────────────┐
│ Content ↔ Taxonomy Relationship (Many-to-Many) │
└──────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────┐
│ Content Model │
│─────────────────────────────────│
│ PK: id │
│ FK: site_id ──────┐ │
│ FK: sector_id │ │
│ FK: cluster_id │ │
│ │ │
│ title │ │
│ content_html │ │
│ word_count │ │
│ meta_title │ │
│ meta_description │ │
│ │ │
│ content_type │ │
│ content_structure │ │
│ │ │
│ external_id │ │
│ external_url │ │
│ sync_status │ │
│ │ │
│ source │ │
│ status │ │
└───────────┬───────┘ │
│ │
│ Many-to-Many │
│ (via ContentTaxonomyRelation)
│ │
v │
┌─────────────────────────────────┐│
│ ContentTaxonomyRelation Model ││
│─────────────────────────────────││
│ PK: id ││
│ FK: content_id ──────────────────┘
│ FK: taxonomy_id ────────────┐
│ │
│ created_at │
│ updated_at │
│ │
│ UNIQUE(content, taxonomy) │
└─────────────────────────────┘
v
┌─────────────────────────────────┐
│ ContentTaxonomy Model │
│─────────────────────────────────│
│ PK: id │
│ FK: site_id ─────┐ │
│ FK: sector_id │ │
│ │ │
│ name │ │
│ slug │ │
│ taxonomy_type │ ◄─── "category" or "tag"
│ description │ │
│ count │ │
│ │ │
│ WordPress Sync: │ │
│ external_taxonomy│ ◄─── "category", "post_tag"
│ external_id │ ◄─── WordPress term_id
│ │ │
│ metadata (JSON) │ │
└──────────────────┘ │
│ │
│ │
UNIQUE(site, slug, taxonomy_type)│
UNIQUE(site, external_id, external_taxonomy)
│ │
└──────────────┘
┌──────────────────────────────────────────────────────────────────────┐
│ Usage Example │
└──────────────────────────────────────────────────────────────────────┘
# Get all categories for a content piece
categories = content.taxonomy_terms.filter(taxonomy_type='category')
# Get all tags for a content piece
tags = content.taxonomy_terms.filter(taxonomy_type='tag')
# Add a category to content
tech_category = ContentTaxonomy.objects.get(name='Technology')
content.taxonomy_terms.add(tech_category)
# Get all content with a specific tag
tutorial_tag = ContentTaxonomy.objects.get(name='Tutorial')
contents = tutorial_tag.contents.all()
# WordPress publishing (automatic)
wp_categories = [term.name for term in content.taxonomy_terms.filter(taxonomy_type='category')]
wp_tags = [term.name for term in content.taxonomy_terms.filter(taxonomy_type='tag')]
┌──────────────────────────────────────────────────────────────────────┐
│ WordPress Integration (Bidirectional Sync) │
└──────────────────────────────────────────────────────────────────────┘
IGNY8 → WordPress:
─────────────────
1. Content created in IGNY8
2. Categories/tags assigned via taxonomy_terms M2M
3. Publishing task created
4. wordpress_publishing.py extracts:
- categories = content.taxonomy_terms.filter(taxonomy_type='category')
- tags = content.taxonomy_terms.filter(taxonomy_type='tag')
5. REST API creates WordPress post with terms
6. external_id saved back to Content model
7. Log: [5-homeg8.com] [POST] Published: "Article Title" (ID: 123)
WordPress → IGNY8:
─────────────────
1. WordPress plugin detects post update
2. REST API sends post data + terms to IGNY8
3. Content updated/created with external_id
4. ContentTaxonomy created/updated with external_id
5. ContentTaxonomyRelation created linking them
6. Log: [5-homeg8.com] [SYNC] Imported: "Article Title"
┌──────────────────────────────────────────────────────────────────────┐
│ Database Tables Summary │
└──────────────────────────────────────────────────────────────────────┘
igny8_content
├─ id (PK)
├─ site_id (FK → sites)
├─ sector_id (FK → sectors)
├─ cluster_id (FK → clusters)
├─ title, content_html, word_count
├─ meta_title, meta_description, keywords
├─ content_type, content_structure
├─ external_id, external_url, sync_status
└─ source, status, timestamps
igny8_content_taxonomy_relations (Through Table)
├─ id (PK)
├─ content_id (FK → igny8_content)
├─ taxonomy_id (FK → igny8_content_taxonomy_terms)
└─ timestamps
UNIQUE(content_id, taxonomy_id)
igny8_content_taxonomy_terms
├─ id (PK)
├─ site_id (FK → sites)
├─ sector_id (FK → sectors)
├─ name, slug
├─ taxonomy_type ('category' | 'tag')
├─ description, count
├─ external_taxonomy ('category' | 'post_tag')
├─ external_id (WordPress term_id)
├─ metadata (JSON)
└─ timestamps
UNIQUE(site_id, slug, taxonomy_type)
UNIQUE(site_id, external_id, external_taxonomy)
┌──────────────────────────────────────────────────────────────────────┐
│ Before vs After Comparison │
└──────────────────────────────────────────────────────────────────────┘
BEFORE (Complex - with SiteBlueprint):
────────────────────────────────────
SiteBlueprint ──┬── SiteBlueprintCluster ──> Clusters
├── SiteBlueprintTaxonomy ──> ContentTaxonomy
└── PageBlueprint ──> Content
Content ──> ContentTaxonomyMap ──> ContentTaxonomy
(separate table with FK)
PublishingRecord ──┬── content_id
└── site_blueprint_id
DeploymentRecord ──┬── content_id
└── site_blueprint_id
AFTER (Simple - SiteBlueprint Removed):
──────────────────────────────────────
Keywords ──> Clusters ──> Ideas ──> Tasks ──> Content
Content ↔ ContentTaxonomy
(M2M via ContentTaxonomyRelation)
PublishingRecord ──> content_id
DeploymentRecord ──> content_id
┌──────────────────────────────────────────────────────────────────────┐
│ Key Benefits │
└──────────────────────────────────────────────────────────────────────┘
✅ Simpler architecture - removed 4 models
✅ Direct M2M relationship - easier to query
✅ Less database joins - better performance
✅ Clear taxonomy model in Django admin
✅ WordPress sync unchanged - still works perfectly
✅ Planner-Writer-Publisher workflow intact
✅ Multi-tenant security maintained
✅ No circular dependencies
✅ Clean codebase - no legacy blueprint code