docs updates
This commit is contained in:
1807
docs/Igny8_APP/01-IGNY8-REST-API-COMPLETE-REFERENCE.md
Normal file
1807
docs/Igny8_APP/01-IGNY8-REST-API-COMPLETE-REFERENCE.md
Normal file
File diff suppressed because it is too large
Load Diff
1467
docs/Igny8_APP/02-PLANNER-WRITER-WORKFLOW-TECHNICAL-GUIDE.md
Normal file
1467
docs/Igny8_APP/02-PLANNER-WRITER-WORKFLOW-TECHNICAL-GUIDE.md
Normal file
File diff suppressed because it is too large
Load Diff
1228
docs/Igny8_APP/05-WRITER-IMAGES-PAGE-SYSTEM-DESIGN.md
Normal file
1228
docs/Igny8_APP/05-WRITER-IMAGES-PAGE-SYSTEM-DESIGN.md
Normal file
File diff suppressed because it is too large
Load Diff
1121
docs/Igny8_APP/06-FEATURE-MODIFICATION-DEVELOPER-GUIDE.md
Normal file
1121
docs/Igny8_APP/06-FEATURE-MODIFICATION-DEVELOPER-GUIDE.md
Normal file
File diff suppressed because it is too large
Load Diff
263
docs/Igny8_APP/KEYWORDS-CLUSTERS-IDEAS-COMPLETE-MAPPING.md
Normal file
263
docs/Igny8_APP/KEYWORDS-CLUSTERS-IDEAS-COMPLETE-MAPPING.md
Normal 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**
|
||||
362
docs/Igny8_APP/TAXONOMY/QUICK-REFERENCE-TAXONOMY.md
Normal file
362
docs/Igny8_APP/TAXONOMY/QUICK-REFERENCE-TAXONOMY.md
Normal 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`
|
||||
258
docs/Igny8_APP/TAXONOMY/TAXONOMY-RELATIONSHIP-DIAGRAM.md
Normal file
258
docs/Igny8_APP/TAXONOMY/TAXONOMY-RELATIONSHIP-DIAGRAM.md
Normal 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
|
||||
Reference in New Issue
Block a user