Update site builder configurations and enhance migration dependencies

- Added `OptimizationConfig` to `INSTALLED_APPS` in `settings.py`.
- Updated migration dependencies in `0001_initial.py` to include `writer` for content source fields.
- Modified the `account` ForeignKey in `PageBlueprint` and `SiteBlueprint` models to use `tenant_id` for better clarity.
- Deleted obsolete implementation plan documents for phases 0-4 to streamline project documentation.
- Improved overall project structure by removing outdated files and enhancing migration clarity.
This commit is contained in:
IGNY8 VPS (Salman)
2025-11-17 20:22:29 +00:00
parent f7115190dc
commit b6b1aecdce
13 changed files with 1246 additions and 4032 deletions

Binary file not shown.

View File

@@ -0,0 +1,8 @@
from django.apps import AppConfig
class OptimizationConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'igny8_core.business.optimization'
verbose_name = 'Optimization'

View File

@@ -0,0 +1,54 @@
# Generated manually for Phase 4: Optimization System
import django.core.validators
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('igny8_core_auth', '0013_remove_ai_cost_per_request'),
('writer', '0009_add_content_site_source_fields'),
]
operations = [
migrations.CreateModel(
name='OptimizationTask',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateTimeField(auto_now_add=True, db_index=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('scores_before', models.JSONField(default=dict, help_text='Optimization scores before')),
('scores_after', models.JSONField(default=dict, help_text='Optimization scores after')),
('html_before', models.TextField(blank=True, help_text='HTML content before optimization')),
('html_after', models.TextField(blank=True, help_text='HTML content after optimization')),
('status', models.CharField(choices=[('pending', 'Pending'), ('running', 'Running'), ('completed', 'Completed'), ('failed', 'Failed')], db_index=True, default='pending', help_text='Optimization task status', max_length=20)),
('credits_used', models.IntegerField(default=0, help_text='Credits used for optimization', validators=[django.core.validators.MinValueValidator(0)])),
('metadata', models.JSONField(blank=True, default=dict, help_text='Additional metadata')),
('account', models.ForeignKey(db_column='tenant_id', on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_set', to='igny8_core_auth.tenant')),
('content', models.ForeignKey(help_text='The content being optimized', on_delete=django.db.models.deletion.CASCADE, related_name='optimization_tasks', to='writer.content')),
],
options={
'db_table': 'igny8_optimization_tasks',
'ordering': ['-created_at'],
'verbose_name': 'Optimization Task',
'verbose_name_plural': 'Optimization Tasks',
},
),
migrations.AddIndex(
model_name='optimizationtask',
index=models.Index(fields=['content', 'status'], name='igny8_optim_content_status_idx'),
),
migrations.AddIndex(
model_name='optimizationtask',
index=models.Index(fields=['account', 'status'], name='igny8_optim_account_status_idx'),
),
migrations.AddIndex(
model_name='optimizationtask',
index=models.Index(fields=['status', 'created_at'], name='igny8_optim_status_created_idx'),
),
]

View File

@@ -9,6 +9,7 @@ class Migration(migrations.Migration):
dependencies = [
('igny8_core_auth', '0014_remove_plan_operation_limits_phase0'),
('writer', '0009_add_content_site_source_fields'),
]
operations = [
@@ -26,7 +27,7 @@ class Migration(migrations.Migration):
('hosting_type', models.CharField(choices=[('igny8_sites', 'IGNY8 Sites'), ('wordpress', 'WordPress'), ('shopify', 'Shopify'), ('multi', 'Multiple Destinations')], default='igny8_sites', help_text='Target hosting platform', max_length=50)),
('version', models.IntegerField(default=1, help_text='Blueprint version', validators=[django.core.validators.MinValueValidator(1)])),
('deployed_version', models.IntegerField(blank=True, help_text='Currently deployed version', null=True)),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='siteblueprint_set', to='igny8_core_auth.account')),
('account', models.ForeignKey(db_column='tenant_id', on_delete=django.db.models.deletion.CASCADE, related_name='siteblueprint_set', to='igny8_core_auth.tenant')),
('sector', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='siteblueprint_set', to='igny8_core_auth.sector')),
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='siteblueprint_set', to='igny8_core_auth.site')),
],
@@ -49,7 +50,7 @@ class Migration(migrations.Migration):
('blocks_json', models.JSONField(default=list, help_text="Page content blocks: [{'type': 'hero', 'data': {...}}, ...]")),
('status', models.CharField(choices=[('draft', 'Draft'), ('generating', 'Generating'), ('ready', 'Ready')], db_index=True, default='draft', help_text='Page status', max_length=20)),
('order', models.IntegerField(default=0, help_text='Page order in navigation')),
('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pageblueprint_set', to='igny8_core_auth.account')),
('account', models.ForeignKey(db_column='tenant_id', on_delete=django.db.models.deletion.CASCADE, related_name='pageblueprint_set', to='igny8_core_auth.tenant')),
('sector', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pageblueprint_set', to='igny8_core_auth.sector')),
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pageblueprint_set', to='igny8_core_auth.site')),
('site_blueprint', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pages', to='site_building.siteblueprint')),

View File

@@ -53,6 +53,7 @@ INSTALLED_APPS = [
'igny8_core.modules.billing.apps.BillingConfig',
'igny8_core.modules.automation.apps.AutomationConfig',
'igny8_core.business.site_building.apps.SiteBuildingConfig',
'igny8_core.business.optimization.apps.OptimizationConfig',
'igny8_core.modules.site_builder.apps.SiteBuilderConfig',
'igny8_core.modules.linker.apps.LinkerConfig',
'igny8_core.modules.optimizer.apps.OptimizerConfig',

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,524 +0,0 @@
# PHASE 0: FOUNDATION & CREDIT SYSTEM
**Detailed Implementation Plan**
**Goal**: Migrate to credit-only model while preserving all existing functionality.
**Timeline**: 1-2 weeks
**Priority**: HIGH
**Dependencies**: None
---
## TABLE OF CONTENTS
1. [Overview](#overview)
2. [Module Settings System](#module-settings-system)
3. [Credit System Updates](#credit-system-updates)
4. [Operational Limits](#operational-limits)
5. [Database Migrations](#database-migrations)
6. [Testing & Validation](#testing--validation)
7. [Implementation Checklist](#implementation-checklist)
---
## OVERVIEW
### Objectives
- ✅ Migrate from plan-based limits to credit-only system
- ✅ Implement module enable/disable functionality
- ✅ Add credit cost tracking for all operations
- ✅ Preserve all existing functionality
- ✅ Update frontend to show credits instead of limits
### Key Principles
- **Backward Compatibility**: All existing APIs continue working
- **No Breaking Changes**: Frontend continues working without changes
- **Gradual Migration**: Add credit checks without removing existing code initially
- **Credit-Only Model**: Remove all plan limit fields, keep only credits
---
## MODULE SETTINGS SYSTEM
### 0.0 Module Settings System (Enable/Disable Modules)
**Purpose**: Allow accounts to enable/disable modules per account.
#### Backend Implementation
| Task | File | Current State | Implementation |
|------|------|---------------|----------------|
| **Extend ModuleSettings Model** | `business/system/models.py` | EXISTING (ModuleSettings) | Add `enabled` boolean field per module |
| **Module Settings API** | `modules/system/views.py` | EXISTING | Extend ViewSet to handle enable/disable |
| **Module Settings Serializer** | `modules/system/serializers.py` | EXISTING | Add enabled field to serializer |
**ModuleSettings Model Extension**:
```python
# business/system/models.py (or core/system/models.py if exists)
class ModuleSettings(AccountBaseModel):
# Existing fields...
# NEW: Module enable/disable flags
planner_enabled = models.BooleanField(default=True)
writer_enabled = models.BooleanField(default=True)
thinker_enabled = models.BooleanField(default=True)
automation_enabled = models.BooleanField(default=True)
site_builder_enabled = models.BooleanField(default=True)
linker_enabled = models.BooleanField(default=True)
optimizer_enabled = models.BooleanField(default=True)
publisher_enabled = models.BooleanField(default=True)
```
**Modules to Control**:
- Planner
- Writer
- Thinker
- Automation
- Site Builder (NEW)
- Linker (NEW)
- Optimizer (NEW)
- Publisher (NEW)
#### Frontend Implementation
| Task | File | Current State | Implementation |
|------|------|---------------|----------------|
| **Module Settings UI** | `frontend/src/pages/Settings/Modules.tsx` | EXISTING (placeholder) | Implement toggle UI for each module |
| **Frontend Module Loader** | `frontend/src/config/modules.config.ts` | NEW | Define module config with enabled checks |
| **Route Guard** | `frontend/src/components/common/ModuleGuard.tsx` | NEW | Component to check module status before rendering |
| **Sidebar Filter** | `frontend/src/layout/AppSidebar.tsx` | EXISTING | Filter out disabled modules from sidebar |
**Module Enable/Disable Logic**:
- Each module has `enabled` flag in ModuleSettings
- Frontend checks module status before loading routes
- Disabled modules don't appear in sidebar
- Disabled modules don't load code (lazy loading check)
**Module Config Example**:
```typescript
// frontend/src/config/modules.config.ts
export const MODULES = {
planner: {
name: 'Planner',
route: '/planner',
enabled: true, // Checked from API
},
writer: {
name: 'Writer',
route: '/writer',
enabled: true,
},
// ... other modules
};
```
**Route Guard Example**:
```typescript
// frontend/src/components/common/ModuleGuard.tsx
const ModuleGuard = ({ module, children }) => {
const { moduleSettings } = useSettingsStore();
const isEnabled = moduleSettings[module]?.enabled ?? true;
if (!isEnabled) {
return <Navigate to="/settings/modules" />;
}
return children;
};
```
---
## CREDIT SYSTEM UPDATES
### 0.1 Credit System Updates
**Purpose**: Migrate from plan-based limits to credit-only system.
#### Plan Model Updates
| Task | File | Current State | Implementation |
|------|------|---------------|----------------|
| **Remove Plan Limit Fields** | `core/auth/models.py` | EXISTING | Remove all limit fields, add migration |
| **Update Plan Model** | `core/auth/models.py` | EXISTING | Keep only `monthly_credits`, `support_level`, `billing_cycle`, `price` |
**Plan Model (Simplified)**:
```python
# core/auth/models.py
class Plan(models.Model):
name = models.CharField(max_length=255)
monthly_credits = models.IntegerField(default=0) # KEEP
support_level = models.CharField(max_length=50) # KEEP
billing_cycle = models.CharField(max_length=20) # KEEP
price = models.DecimalField(max_digits=10, decimal_places=2) # KEEP
features = models.JSONField(default=dict) # KEEP (for future use)
# REMOVE: All limit fields
# - max_keywords
# - max_clusters
# - max_content_ideas
# - daily_content_tasks
# - monthly_word_count_limit
# - daily_image_generation_limit
# - monthly_image_count
# - etc.
```
**Migration Strategy**:
1. Create migration to add defaults for removed fields (if needed)
2. Create migration to remove limit fields
3. Ensure existing accounts have credit balances set
#### Credit Cost Constants
| Task | File | Current State | Implementation |
|------|------|---------------|----------------|
| **Add Credit Costs** | `business/billing/constants.py` | NEW | Define credit costs per operation |
**Credit Cost Constants**:
```python
# business/billing/constants.py
CREDIT_COSTS = {
'clustering': 10, # Per clustering request
'idea_generation': 15, # Per cluster → ideas request
'content_generation': 1, # Per 100 words
'image_prompt_extraction': 2, # Per content piece
'image_generation': 5, # Per image
'linking': 8, # Per content piece (NEW)
'optimization': 1, # Per 200 words (NEW)
'site_structure_generation': 50, # Per site blueprint (NEW)
'site_page_generation': 20, # Per page (NEW)
}
```
#### CreditService Updates
| Task | File | Current State | Implementation |
|------|------|---------------|----------------|
| **Update CreditService** | `business/billing/services/credit_service.py` | EXISTING | Add credit cost constants, update methods |
**CreditService Methods**:
```python
# business/billing/services/credit_service.py
class CreditService:
def check_credits(self, account, operation_type, amount=None):
"""Check if account has sufficient credits"""
required = self.get_credit_cost(operation_type, amount)
if account.credits < required:
raise InsufficientCreditsError(f"Need {required} credits, have {account.credits}")
return True
def deduct_credits(self, account, operation_type, amount=None):
"""Deduct credits after operation"""
cost = self.get_credit_cost(operation_type, amount)
account.credits -= cost
account.save()
# Log usage
CreditUsageLog.objects.create(...)
def get_credit_cost(self, operation_type, amount=None):
"""Get credit cost for operation"""
base_cost = CREDIT_COSTS.get(operation_type, 0)
if operation_type == 'content_generation' and amount:
return base_cost * (amount / 100) # Per 100 words
if operation_type == 'optimization' and amount:
return base_cost * (amount / 200) # Per 200 words
return base_cost
```
#### AI Engine Updates
| Task | File | Current State | Implementation |
|------|------|---------------|----------------|
| **Update AI Engine** | `infrastructure/ai/engine.py` | EXISTING | Check credits before AI calls |
**AI Engine Credit Check**:
```python
# infrastructure/ai/engine.py
class AIEngine:
def execute(self, function, payload, account):
# Check credits BEFORE AI call
operation_type = function.get_operation_type()
estimated_cost = function.get_estimated_cost(payload)
credit_service.check_credits(account, operation_type, estimated_cost)
# Execute AI function
result = function.execute(payload)
# Deduct credits AFTER successful execution
credit_service.deduct_credits(account, operation_type, actual_cost)
return result
```
#### Content Generation Updates
| Task | File | Current State | Implementation |
|------|------|---------------|----------------|
| **Update Content Generation** | `business/content/services/content_generation_service.py` | NEW (Phase 1) | Check credits before generation |
**Content Generation Credit Check**:
```python
# business/content/services/content_generation_service.py
class ContentGenerationService:
def generate_content(self, task, account):
# Check credits before generation
estimated_words = task.estimated_word_count or 1000
credit_service.check_credits(account, 'content_generation', estimated_words)
# Generate content
content = self._generate(task)
# Deduct credits after generation
actual_words = content.word_count
credit_service.deduct_credits(account, 'content_generation', actual_words)
return content
```
#### Image Generation Updates
| Task | File | Current State | Implementation |
|------|------|---------------|----------------|
| **Update Image Generation** | `infrastructure/ai/functions/generate_images.py` | EXISTING | Check credits before generation |
**Image Generation Credit Check**:
```python
# infrastructure/ai/functions/generate_images.py
class GenerateImagesFunction(BaseAIFunction):
def execute(self, payload, account):
image_ids = payload['image_ids']
# Check credits before generation
credit_service.check_credits(account, 'image_generation', len(image_ids))
# Generate images
results = self._generate_images(image_ids)
# Deduct credits after generation
credit_service.deduct_credits(account, 'image_generation', len(results))
return results
```
#### Remove Limit Checks
| Task | File | Current State | Implementation |
|------|------|---------------|----------------|
| **Remove Limit Checks** | All services | EXISTING | Remove all plan limit validations |
**Files to Update**:
- `modules/planner/views.py` - Remove keyword/cluster limit checks
- `modules/writer/views.py` - Remove task/content limit checks
- `infrastructure/ai/engine.py` - Remove plan limit checks
- All ViewSets - Remove limit validation
**Before (Remove)**:
```python
# OLD: Check plan limits
if account.plan.max_keywords and keywords_count > account.plan.max_keywords:
raise ValidationError("Exceeds plan limit")
```
**After (Credit Only)**:
```python
# NEW: Check credits only
credit_service.check_credits(account, 'clustering', keyword_count)
```
#### Usage Logging Updates
| Task | File | Current State | Implementation |
|------|------|---------------|----------------|
| **Update Usage Logging** | `business/billing/models.py` | EXISTING | Ensure all operations log credits |
**CreditUsageLog Model**:
```python
# business/billing/models.py
class CreditUsageLog(AccountBaseModel):
account = models.ForeignKey(Account, on_delete=models.CASCADE)
operation_type = models.CharField(max_length=50)
credits_used = models.IntegerField()
related_object_type = models.CharField(max_length=50, blank=True)
related_object_id = models.IntegerField(null=True, blank=True)
metadata = models.JSONField(default=dict)
created_at = models.DateTimeField(auto_now_add=True)
```
#### Frontend Updates
| Task | File | Current State | Implementation |
|------|------|---------------|----------------|
| **Update Frontend Limits UI** | `frontend/src/pages/Billing/` | EXISTING | Replace limits display with credit display |
**Frontend Changes**:
- Remove plan limit displays
- Show credit balance prominently
- Show credit costs per operation
- Show usage history by operation type
---
## OPERATIONAL LIMITS
### 0.2 Operational Limits (Keep)
**Purpose**: Technical constraints, not business limits.
| Limit | Value | Location | Implementation | Reason |
|-------|-------|----------|----------------|--------|
| **Keywords per request** | 50 | `modules/planner/views.py` | Request validation | API payload size, processing time |
| **Images per request** | 6 | `modules/writer/views.py` | Request validation | Queue management (user sees as batch) |
| **Images per AI call** | 1 | `infrastructure/ai/functions/generate_images.py` | Internal | Image API limitation |
**Note**: These are **NOT** business limits - they're technical constraints for request processing.
---
## DATABASE MIGRATIONS
### 0.3 Database Migrations
| Migration | Purpose | Risk | Implementation |
|-----------|---------|------|----------------|
| **Remove limit fields from Plan** | Clean up unused fields | LOW - Add defaults first | Create migration to remove fields |
| **Add credit cost tracking** | Enhance CreditUsageLog | LOW - Additive only | Add fields to CreditUsageLog |
| **Monthly credit replenishment** | Celery Beat task | LOW - New feature | Add scheduled task |
**Migration 1: Remove Plan Limit Fields**:
```python
# core/auth/migrations/XXXX_remove_plan_limits.py
class Migration(migrations.Migration):
operations = [
migrations.RemoveField(model_name='plan', name='max_keywords'),
migrations.RemoveField(model_name='plan', name='max_clusters'),
# ... remove all limit fields
]
```
**Migration 2: Add Credit Cost Tracking**:
```python
# business/billing/migrations/XXXX_add_credit_tracking.py
class Migration(migrations.Migration):
operations = [
migrations.AddField(
model_name='creditusagelog',
name='related_object_type',
field=models.CharField(max_length=50, blank=True),
),
migrations.AddField(
model_name='creditusagelog',
name='related_object_id',
field=models.IntegerField(null=True, blank=True),
),
migrations.AddField(
model_name='creditusagelog',
name='metadata',
field=models.JSONField(default=dict),
),
]
```
**Migration 3: Monthly Credit Replenishment**:
- Add Celery Beat task (see Automation section)
---
## TESTING & VALIDATION
### 0.4 Testing
**Test Cases**:
1. **Credit System Tests**:
- ✅ All existing features work with credit checks
- ✅ Credit deduction happens correctly
- ✅ Insufficient credits show clear error
- ✅ Usage logging tracks all operations
- ✅ Frontend shows credit balance, not limits
2. **Module Settings Tests**:
- ✅ Disabled modules don't appear in sidebar
- ✅ Disabled modules don't load routes
- ✅ Disabled modules return 403/404 appropriately
- ✅ Module settings persist correctly
3. **Backward Compatibility Tests**:
- ✅ All existing API endpoints work
- ✅ All existing workflows function
- ✅ Frontend continues working
- ✅ No data loss during migration
**Test Files to Create**:
- `backend/tests/test_credit_system.py`
- `backend/tests/test_module_settings.py`
- `frontend/src/__tests__/ModuleGuard.test.tsx`
---
## IMPLEMENTATION CHECKLIST
### Backend Tasks
- [ ] Create `business/billing/constants.py` with credit costs
- [ ] Update `CreditService` with credit cost methods
- [ ] Update `Plan` model - remove limit fields
- [ ] Create migration to remove plan limit fields
- [ ] Update `AIEngine` to check credits before AI calls
- [ ] Update content generation to check credits
- [ ] Update image generation to check credits
- [ ] Remove all plan limit checks from ViewSets
- [ ] Update `CreditUsageLog` model with tracking fields
- [ ] Create migration for credit tracking
- [ ] Extend `ModuleSettings` model with enabled flags
- [ ] Update module settings API
- [ ] Add monthly credit replenishment Celery Beat task
### Frontend Tasks
- [ ] Implement `frontend/src/pages/Settings/Modules.tsx`
- [ ] Create `frontend/src/config/modules.config.ts`
- [ ] Create `frontend/src/components/common/ModuleGuard.tsx`
- [ ] Update `frontend/src/App.tsx` with conditional route loading
- [ ] Update `frontend/src/layout/AppSidebar.tsx` to filter disabled modules
- [ ] Update `frontend/src/pages/Billing/` to show credits instead of limits
- [ ] Update billing UI to show credit costs per operation
### Testing Tasks
- [ ] Test credit deduction for all operations
- [ ] Test insufficient credits error handling
- [ ] Test module enable/disable functionality
- [ ] Test disabled modules don't load
- [ ] Test backward compatibility
- [ ] Test migration safety
---
## RISK ASSESSMENT
| Risk | Level | Mitigation |
|------|-------|------------|
| **Breaking existing functionality** | MEDIUM | Extensive testing, gradual rollout |
| **Credit calculation errors** | MEDIUM | Unit tests for credit calculations |
| **Migration data loss** | LOW | Backup before migration, test on staging |
| **Frontend breaking changes** | LOW | Backward compatible API changes |
---
## SUCCESS CRITERIA
- ✅ All existing features work with credit checks
- ✅ Credit deduction happens correctly for all operations
- ✅ Insufficient credits show clear error messages
- ✅ Usage logging tracks all operations
- ✅ Frontend shows credit balance, not limits
- ✅ Module settings enable/disable modules correctly
- ✅ Disabled modules don't appear in UI
- ✅ No breaking changes for existing users
---
**END OF PHASE 0 DOCUMENT**

View File

@@ -1,434 +0,0 @@
# PHASE 1: SERVICE LAYER REFACTORING
**Detailed Implementation Plan**
**Goal**: Extract business logic from ViewSets into services, preserving all existing functionality.
**Timeline**: 2-3 weeks
**Priority**: HIGH
**Dependencies**: Phase 0
---
## TABLE OF CONTENTS
1. [Overview](#overview)
2. [Create Business Structure](#create-business-structure)
3. [Move Models to Business](#move-models-to-business)
4. [Create Services](#create-services)
5. [Refactor ViewSets](#refactor-viewsets)
6. [Testing & Validation](#testing--validation)
7. [Implementation Checklist](#implementation-checklist)
---
## OVERVIEW
### Objectives
- ✅ Create `business/` folder structure
- ✅ Move models from `modules/` to `business/`
- ✅ Extract business logic from ViewSets to services
- ✅ Keep ViewSets as thin wrappers
- ✅ Preserve all existing API functionality
### Key Principles
- **Service Layer Pattern**: Business logic in services, not ViewSets
- **Testable Services**: Services can be tested independently
- **Clean Architecture**: Clear separation between API layer (modules/) and business logic (business/)
---
## CREATE BUSINESS STRUCTURE
### 1.1 Create Business Structure
**Purpose**: Organize code by business logic, not technical layers.
#### Folder Structure
```
backend/igny8_core/
├── business/ # NEW: Business logic layer
│ ├── content/ # Content business logic
│ │ ├── __init__.py
│ │ ├── models.py # Content, Tasks, Images
│ │ ├── services/
│ │ │ ├── __init__.py
│ │ │ ├── content_generation_service.py
│ │ │ ├── content_pipeline_service.py
│ │ │ └── content_versioning_service.py
│ │ └── migrations/
│ │
│ ├── planning/ # Planning business logic
│ │ ├── __init__.py
│ │ ├── models.py # Keywords, Clusters, Ideas
│ │ ├── services/
│ │ │ ├── __init__.py
│ │ │ ├── clustering_service.py
│ │ │ └── ideas_service.py
│ │ └── migrations/
│ │
│ ├── billing/ # Billing business logic (already exists)
│ │ ├── models.py # Credits, Transactions
│ │ └── services/
│ │ └── credit_service.py # Already exists
│ │
│ └── automation/ # Automation business logic (Phase 2)
│ ├── models.py
│ └── services/
```
#### Implementation Tasks
| Task | File | Current Location | New Location | Risk |
|------|------|------------------|--------------|------|
| **Create business/ folder** | `backend/igny8_core/business/` | N/A | NEW | LOW |
| **Create content business** | `business/content/` | N/A | NEW | LOW |
| **Create planning business** | `business/planning/` | N/A | NEW | LOW |
| **Create billing business** | `business/billing/` | `modules/billing/` | MOVE | LOW |
| **Create automation business** | `business/automation/` | N/A | NEW (Phase 2) | LOW |
---
## MOVE MODELS TO BUSINESS
### 1.2 Move Models to Business
**Purpose**: Move models from `modules/` to `business/` to separate business logic from API layer.
#### Content Models Migration
| Model | Current Location | New Location | Changes Needed |
|------|------------------|--------------|----------------|
| `Content` | `modules/writer/models.py` | `business/content/models.py` | Move, update imports |
| `Tasks` | `modules/writer/models.py` | `business/content/models.py` | Move, update imports |
| `Images` | `modules/writer/models.py` | `business/content/models.py` | Move, update imports |
**Migration Steps**:
1. Create `business/content/models.py`
2. Copy models from `modules/writer/models.py`
3. Update imports in `modules/writer/views.py`
4. Create migration to ensure no data loss
5. Update all references to models
#### Planning Models Migration
| Model | Current Location | New Location | Changes Needed |
|------|------------------|--------------|----------------|
| `Keywords` | `modules/planner/models.py` | `business/planning/models.py` | Move, update imports |
| `Clusters` | `modules/planner/models.py` | `business/planning/models.py` | Move, update imports |
| `ContentIdeas` | `modules/planner/models.py` | `business/planning/models.py` | Move, update imports |
**Migration Steps**:
1. Create `business/planning/models.py`
2. Copy models from `modules/planner/models.py`
3. Update imports in `modules/planner/views.py`
4. Create migration to ensure no data loss
5. Update all references to models
#### Billing Models Migration
| Model | Current Location | New Location | Changes Needed |
|------|------------------|--------------|----------------|
| `CreditTransaction` | `modules/billing/models.py` | `business/billing/models.py` | Move, update imports |
| `CreditUsageLog` | `modules/billing/models.py` | `business/billing/models.py` | Move, update imports |
**Migration Steps**:
1. Create `business/billing/models.py`
2. Copy models from `modules/billing/models.py`
3. Move `CreditService` to `business/billing/services/credit_service.py`
4. Update imports in `modules/billing/views.py`
5. Create migration to ensure no data loss
---
## CREATE SERVICES
### 1.3 Create Services
**Purpose**: Extract business logic from ViewSets into reusable services.
#### ContentService
| Task | File | Purpose | Dependencies |
|------|------|---------|--------------|
| **Create ContentService** | `business/content/services/content_generation_service.py` | Unified content generation | Existing Writer logic, CreditService |
**ContentService Methods**:
```python
# business/content/services/content_generation_service.py
class ContentGenerationService:
def __init__(self):
self.credit_service = CreditService()
def generate_content(self, task, account):
"""Generate content for a task"""
# Check credits
self.credit_service.check_credits(account, 'content_generation', task.estimated_word_count)
# Generate content (existing logic from Writer ViewSet)
content = self._generate(task)
# Deduct credits
self.credit_service.deduct_credits(account, 'content_generation', content.word_count)
return content
def _generate(self, task):
"""Internal content generation logic"""
# Move logic from Writer ViewSet here
pass
```
#### PlanningService
| Task | File | Purpose | Dependencies |
|------|------|---------|--------------|
| **Create PlanningService** | `business/planning/services/clustering_service.py` | Keyword clustering | Existing Planner logic, CreditService |
**PlanningService Methods**:
```python
# business/planning/services/clustering_service.py
class ClusteringService:
def __init__(self):
self.credit_service = CreditService()
def cluster_keywords(self, keyword_ids, account):
"""Cluster keywords using AI"""
# Check credits
self.credit_service.check_credits(account, 'clustering', len(keyword_ids))
# Cluster keywords (existing logic from Planner ViewSet)
clusters = self._cluster(keyword_ids)
# Deduct credits
self.credit_service.deduct_credits(account, 'clustering', len(keyword_ids))
return clusters
```
#### IdeasService
| Task | File | Purpose | Dependencies |
|------|------|---------|--------------|
| **Create IdeasService** | `business/planning/services/ideas_service.py` | Generate content ideas | Existing Planner logic, CreditService |
**IdeasService Methods**:
```python
# business/planning/services/ideas_service.py
class IdeasService:
def __init__(self):
self.credit_service = CreditService()
def generate_ideas(self, cluster_ids, account):
"""Generate content ideas from clusters"""
# Check credits
self.credit_service.check_credits(account, 'idea_generation', len(cluster_ids))
# Generate ideas (existing logic from Planner ViewSet)
ideas = self._generate_ideas(cluster_ids)
# Deduct credits
self.credit_service.deduct_credits(account, 'idea_generation', len(ideas))
return ideas
```
---
## REFACTOR VIEWSETS
### 1.4 Refactor ViewSets (Keep APIs Working)
**Purpose**: Make ViewSets thin wrappers that delegate to services.
#### Planner ViewSets Refactoring
| ViewSet | Current | New | Risk |
|---------|---------|-----|------|
| **KeywordViewSet** | Business logic in views | Delegate to services | LOW |
| **ClusterViewSet** | Business logic in views | Delegate to services | LOW |
| **ContentIdeasViewSet** | Business logic in views | Delegate to services | LOW |
**Before (Business Logic in ViewSet)**:
```python
# modules/planner/views.py
class ClusterViewSet(SiteSectorModelViewSet):
@action(detail=False, methods=['post'])
def auto_generate_ideas(self, request):
cluster_ids = request.data.get('cluster_ids')
# Business logic here (50+ lines)
clusters = Cluster.objects.filter(id__in=cluster_ids)
# AI call logic
# Idea creation logic
# etc.
return Response(...)
```
**After (Delegate to Service)**:
```python
# modules/planner/views.py
class ClusterViewSet(SiteSectorModelViewSet):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.ideas_service = IdeasService()
@action(detail=False, methods=['post'])
def auto_generate_ideas(self, request):
cluster_ids = request.data.get('cluster_ids')
account = request.account
# Delegate to service
ideas = self.ideas_service.generate_ideas(cluster_ids, account)
# Serialize and return
serializer = ContentIdeasSerializer(ideas, many=True)
return Response(serializer.data)
```
#### Writer ViewSets Refactoring
| ViewSet | Current | New | Risk |
|---------|---------|-----|------|
| **TasksViewSet** | Business logic in views | Delegate to services | LOW |
| **ImagesViewSet** | Business logic in views | Delegate to services | LOW |
**Before (Business Logic in ViewSet)**:
```python
# modules/writer/views.py
class TasksViewSet(SiteSectorModelViewSet):
@action(detail=False, methods=['post'])
def auto_generate_content(self, request):
task_ids = request.data.get('task_ids')
# Business logic here (100+ lines)
tasks = Task.objects.filter(id__in=task_ids)
# AI call logic
# Content creation logic
# etc.
return Response(...)
```
**After (Delegate to Service)**:
```python
# modules/writer/views.py
class TasksViewSet(SiteSectorModelViewSet):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.content_service = ContentGenerationService()
@action(detail=False, methods=['post'])
def auto_generate_content(self, request):
task_ids = request.data.get('task_ids')
account = request.account
# Delegate to service
contents = []
for task_id in task_ids:
task = Task.objects.get(id=task_id)
content = self.content_service.generate_content(task, account)
contents.append(content)
# Serialize and return
serializer = ContentSerializer(contents, many=True)
return Response(serializer.data)
```
#### Billing ViewSets
| ViewSet | Current | New | Risk |
|---------|---------|-----|------|
| **CreditTransactionViewSet** | Already uses CreditService | Keep as-is | NONE |
| **CreditUsageLogViewSet** | Already uses CreditService | Keep as-is | NONE |
**Note**: Billing ViewSets already use CreditService, so no changes needed.
---
## TESTING & VALIDATION
### 1.5 Testing
**Test Cases**:
1. **Service Tests**:
- ✅ Services can be tested independently
- ✅ Services handle errors correctly
- ✅ Services check credits before operations
- ✅ Services deduct credits after operations
2. **API Compatibility Tests**:
- ✅ All existing API endpoints work identically
- ✅ Response formats unchanged
- ✅ No breaking changes for frontend
- ✅ All ViewSet actions work correctly
3. **Model Migration Tests**:
- ✅ Models work after migration
- ✅ All relationships preserved
- ✅ No data loss during migration
- ✅ All queries work correctly
**Test Files to Create**:
- `backend/tests/test_content_service.py`
- `backend/tests/test_planning_service.py`
- `backend/tests/test_ideas_service.py`
- `backend/tests/test_viewset_refactoring.py`
---
## IMPLEMENTATION CHECKLIST
### Backend Tasks
- [ ] Create `business/` folder structure
- [ ] Create `business/content/` folder
- [ ] Create `business/planning/` folder
- [ ] Create `business/billing/` folder (move existing)
- [ ] Move Content models to `business/content/models.py`
- [ ] Move Planning models to `business/planning/models.py`
- [ ] Move Billing models to `business/billing/models.py`
- [ ] Create migrations for model moves
- [ ] Create `ContentGenerationService`
- [ ] Create `ClusteringService`
- [ ] Create `IdeasService`
- [ ] Refactor `KeywordViewSet` to use services
- [ ] Refactor `ClusterViewSet` to use services
- [ ] Refactor `ContentIdeasViewSet` to use services
- [ ] Refactor `TasksViewSet` to use services
- [ ] Refactor `ImagesViewSet` to use services
- [ ] Update all imports
- [ ] Test all API endpoints
### Testing Tasks
- [ ] Test all existing API endpoints work
- [ ] Test response formats unchanged
- [ ] Test services independently
- [ ] Test model migrations
- [ ] Test backward compatibility
---
## RISK ASSESSMENT
| Risk | Level | Mitigation |
|------|-------|------------|
| **Import errors** | MEDIUM | Update all imports systematically |
| **Data loss during migration** | LOW | Backup before migration, test on staging |
| **Service logic errors** | MEDIUM | Unit tests for all services |
| **Model migration complexity** | MEDIUM | Use Django migrations, test thoroughly |
---
## SUCCESS CRITERIA
- ✅ Services are testable independently
- ✅ Business logic extracted from ViewSets
- ✅ ViewSets are thin wrappers that delegate to services
- ✅ All models moved to business layer
- ✅ All imports updated correctly
- ✅ Services handle credit checks and business rules
---
**END OF PHASE 1 DOCUMENT**

View File

@@ -1,594 +0,0 @@
# PHASE 2: AUTOMATION SYSTEM
**Detailed Implementation Plan**
**Goal**: Implement automation rules and scheduled tasks.
**Timeline**: 2-3 weeks
**Priority**: HIGH
**Dependencies**: Phase 1
---
## TABLE OF CONTENTS
1. [Overview](#overview)
2. [Automation Models](#automation-models)
3. [Automation Service](#automation-service)
4. [Celery Beat Tasks](#celery-beat-tasks)
5. [Automation API](#automation-api)
6. [Automation UI](#automation-ui)
7. [Testing & Validation](#testing--validation)
8. [Implementation Checklist](#implementation-checklist)
---
## OVERVIEW
### Objectives
- ✅ Create AutomationRule and ScheduledTask models
- ✅ Build AutomationService with rule execution engine
- ✅ Implement Celery Beat scheduled tasks
- ✅ Create automation API endpoints
- ✅ Build automation UI (Dashboard, Rules, History)
### Key Principles
- **Rule-Based**: Users create rules with triggers, conditions, actions
- **Scheduled Execution**: Rules can run on schedule or event triggers
- **Credit-Aware**: Automation respects credit limits
- **Audit Trail**: All automation executions logged
---
## AUTOMATION MODELS
### 2.1 Automation Models
**Purpose**: Store automation rules and scheduled task records.
#### AutomationRule Model
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **AutomationRule Model** | `business/automation/models.py` | Phase 1 | Create model with trigger, conditions, actions, schedule |
**AutomationRule Model**:
```python
# business/automation/models.py
class AutomationRule(SiteSectorBaseModel):
name = models.CharField(max_length=255)
description = models.TextField(blank=True)
# Trigger configuration
trigger = models.CharField(
max_length=50,
choices=[
('schedule', 'Scheduled'),
('keyword_added', 'Keyword Added'),
('cluster_created', 'Cluster Created'),
('idea_created', 'Idea Created'),
('content_generated', 'Content Generated'),
('task_created', 'Task Created'),
]
)
# Condition evaluation
conditions = models.JSONField(default=dict)
# Example: {'field': 'status', 'operator': 'eq', 'value': 'draft'}
# Actions to execute
actions = models.JSONField(default=list)
# Example: [{'type': 'generate_ideas', 'params': {'cluster_ids': [1, 2]}}]
# Schedule configuration (for scheduled triggers)
schedule = models.JSONField(default=dict)
# Example: {'cron': '0 9 * * *', 'timezone': 'UTC'}
# Execution limits
is_active = models.BooleanField(default=True)
max_executions_per_day = models.IntegerField(default=10)
credit_limit_per_execution = models.IntegerField(default=100)
# Tracking
last_executed_at = models.DateTimeField(null=True, blank=True)
execution_count_today = models.IntegerField(default=0)
last_reset_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-created_at']
```
#### ScheduledTask Model
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **ScheduledTask Model** | `business/automation/models.py` | Phase 1 | Create model to track scheduled executions |
**ScheduledTask Model**:
```python
# business/automation/models.py
class ScheduledTask(SiteSectorBaseModel):
automation_rule = models.ForeignKey(AutomationRule, on_delete=models.CASCADE)
scheduled_at = models.DateTimeField()
executed_at = models.DateTimeField(null=True, blank=True)
status = models.CharField(
max_length=20,
choices=[
('pending', 'Pending'),
('running', 'Running'),
('completed', 'Completed'),
('failed', 'Failed'),
('skipped', 'Skipped'),
],
default='pending'
)
result = models.JSONField(default=dict, blank=True)
error_message = models.TextField(blank=True)
credits_used = models.IntegerField(default=0)
class Meta:
ordering = ['-scheduled_at']
```
#### Automation Migrations
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Automation Migrations** | `business/automation/migrations/` | Phase 1 | Create initial migrations |
---
## AUTOMATION SERVICE
### 2.2 Automation Service
**Purpose**: Execute automation rules with condition evaluation and action execution.
#### AutomationService
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **AutomationService** | `business/automation/services/automation_service.py` | Phase 1 services | Main service for rule execution |
**AutomationService Methods**:
```python
# business/automation/services/automation_service.py
class AutomationService:
def __init__(self):
self.rule_engine = RuleEngine()
self.condition_evaluator = ConditionEvaluator()
self.action_executor = ActionExecutor()
self.credit_service = CreditService()
def execute_rule(self, rule, context=None):
"""Execute an automation rule"""
# Check if rule is active
if not rule.is_active:
return {'status': 'skipped', 'reason': 'Rule is inactive'}
# Check execution limits
if not self._check_execution_limits(rule):
return {'status': 'skipped', 'reason': 'Execution limit reached'}
# Check credits
if not self.credit_service.check_credits(rule.account, 'automation', rule.credit_limit_per_execution):
return {'status': 'skipped', 'reason': 'Insufficient credits'}
# Evaluate conditions
if not self.condition_evaluator.evaluate(rule.conditions, context):
return {'status': 'skipped', 'reason': 'Conditions not met'}
# Execute actions
results = self.action_executor.execute(rule.actions, context)
# Update rule tracking
rule.last_executed_at = timezone.now()
rule.execution_count_today += 1
rule.save()
return {'status': 'completed', 'results': results}
def _check_execution_limits(self, rule):
"""Check if rule can execute (daily limit)"""
# Reset counter if new day
if rule.last_reset_at.date() < timezone.now().date():
rule.execution_count_today = 0
rule.last_reset_at = timezone.now()
rule.save()
return rule.execution_count_today < rule.max_executions_per_day
```
#### Rule Execution Engine
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Rule Execution Engine** | `business/automation/services/rule_engine.py` | Phase 1 services | Orchestrates rule execution |
**RuleEngine Methods**:
```python
# business/automation/services/rule_engine.py
class RuleEngine:
def execute_rule(self, rule, context):
"""Orchestrate rule execution"""
# Validate rule
# Check conditions
# Execute actions
# Handle errors
pass
```
#### Condition Evaluator
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Condition Evaluator** | `business/automation/services/condition_evaluator.py` | None | Evaluates rule conditions |
**ConditionEvaluator Methods**:
```python
# business/automation/services/condition_evaluator.py
class ConditionEvaluator:
def evaluate(self, conditions, context):
"""Evaluate rule conditions"""
# Support operators: eq, ne, gt, gte, lt, lte, in, contains
# Example: {'field': 'status', 'operator': 'eq', 'value': 'draft'}
pass
```
#### Action Executor
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Action Executor** | `business/automation/services/action_executor.py` | Phase 1 services | Executes rule actions |
**ActionExecutor Methods**:
```python
# business/automation/services/action_executor.py
class ActionExecutor:
def __init__(self):
self.clustering_service = ClusteringService()
self.ideas_service = IdeasService()
self.content_service = ContentGenerationService()
def execute(self, actions, context):
"""Execute rule actions"""
results = []
for action in actions:
action_type = action['type']
params = action.get('params', {})
if action_type == 'generate_ideas':
result = self.ideas_service.generate_ideas(params['cluster_ids'], context['account'])
elif action_type == 'generate_content':
result = self.content_service.generate_content(params['task_id'], context['account'])
# ... other action types
results.append(result)
return results
```
---
## CELERY BEAT TASKS
### 2.3 Celery Beat Tasks
**Purpose**: Schedule automation rules and monthly credit replenishment.
#### Scheduled Automation Task
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Scheduled Automation Task** | `infrastructure/messaging/automation_tasks.py` | AutomationService | Periodic task to execute scheduled rules |
**Scheduled Automation Task**:
```python
# infrastructure/messaging/automation_tasks.py
from celery import shared_task
from celery.schedules import crontab
@shared_task
def execute_scheduled_automation_rules():
"""Execute all scheduled automation rules"""
from business.automation.services.automation_service import AutomationService
service = AutomationService()
rules = AutomationRule.objects.filter(
trigger='schedule',
is_active=True
)
for rule in rules:
# Check if rule should execute based on schedule
if should_execute_now(rule.schedule):
service.execute_rule(rule)
```
#### Monthly Credit Replenishment
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Monthly Credit Replenishment** | `infrastructure/messaging/automation_tasks.py` | CreditService | Add credits monthly to accounts |
**Monthly Credit Replenishment Task**:
```python
# infrastructure/messaging/automation_tasks.py
@shared_task
def replenish_monthly_credits():
"""Replenish monthly credits for all active accounts"""
from business.billing.services.credit_service import CreditService
service = CreditService()
accounts = Account.objects.filter(status='active')
for account in accounts:
if account.plan:
monthly_credits = account.plan.monthly_credits
if monthly_credits > 0:
service.add_credits(account, monthly_credits, 'monthly_replenishment')
```
#### Celery Beat Configuration
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Celery Beat Configuration** | `backend/igny8_core/celery.py` | None | Configure periodic tasks |
**Celery Beat Configuration**:
```python
# backend/igny8_core/celery.py
from celery.schedules import crontab
app.conf.beat_schedule = {
'execute-scheduled-automation-rules': {
'task': 'infrastructure.messaging.automation_tasks.execute_scheduled_automation_rules',
'schedule': crontab(minute='*/15'), # Every 15 minutes
},
'replenish-monthly-credits': {
'task': 'infrastructure.messaging.automation_tasks.replenish_monthly_credits',
'schedule': crontab(hour=0, minute=0, day_of_month=1), # First day of month
},
}
```
---
## AUTOMATION API
### 2.4 Automation API
**Purpose**: CRUD API for automation rules and scheduled tasks.
#### AutomationRule ViewSet
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **AutomationRule ViewSet** | `modules/automation/views.py` | AutomationService | CRUD operations for rules |
**AutomationRule ViewSet**:
```python
# modules/automation/views.py
class AutomationRuleViewSet(AccountModelViewSet):
queryset = AutomationRule.objects.all()
serializer_class = AutomationRuleSerializer
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.automation_service = AutomationService()
@action(detail=True, methods=['post'])
def execute(self, request, pk=None):
"""Manually execute a rule"""
rule = self.get_object()
result = self.automation_service.execute_rule(rule, {'account': request.account})
return Response(result)
@action(detail=True, methods=['post'])
def test(self, request, pk=None):
"""Test rule conditions without executing"""
rule = self.get_object()
# Test condition evaluation
pass
```
#### ScheduledTask ViewSet
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **ScheduledTask ViewSet** | `modules/automation/views.py` | AutomationService | View scheduled task history |
**ScheduledTask ViewSet**:
```python
# modules/automation/views.py
class ScheduledTaskViewSet(AccountModelViewSet):
queryset = ScheduledTask.objects.all()
serializer_class = ScheduledTaskSerializer
filterset_fields = ['status', 'automation_rule']
ordering = ['-scheduled_at']
```
#### Automation URLs
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Automation URLs** | `modules/automation/urls.py` | None | Register automation routes |
**Automation URLs**:
```python
# modules/automation/urls.py
from rest_framework.routers import DefaultRouter
from .views import AutomationRuleViewSet, ScheduledTaskViewSet
router = DefaultRouter()
router.register(r'rules', AutomationRuleViewSet, basename='automation-rule')
router.register(r'scheduled-tasks', ScheduledTaskViewSet, basename='scheduled-task')
urlpatterns = router.urls
```
---
## AUTOMATION UI
### 2.5 Automation UI
**Purpose**: User interface for managing automation rules and viewing history.
#### Automation Dashboard
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Automation Dashboard** | `frontend/src/pages/Automation/Dashboard.tsx` | EXISTING (placeholder) | Overview of automation status |
**Dashboard Features**:
- Active rules count
- Recent executions
- Success/failure rates
- Credit usage from automation
- Quick actions (create rule, view history)
#### Rules Management
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Rules Management** | `frontend/src/pages/Automation/Rules.tsx` | NEW | CRUD interface for rules |
**Rules Management Features**:
- List all rules
- Create new rule (wizard)
- Edit existing rule
- Enable/disable rule
- Delete rule
- Test rule
- Manual execution
#### Schedules (Part of Automation Menu)
**Note**: Schedules functionality will be integrated into the Automation menu group, not as a separate page.
**Schedules Features** (within Automation Dashboard):
- List scheduled tasks
- Filter by status, rule, date
- View execution results
- View error messages
- Retry failed tasks
#### Automation API Client
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Automation API Client** | `frontend/src/services/automation.api.ts` | NEW | API client for automation endpoints |
**Automation API Client**:
```typescript
// frontend/src/services/automation.api.ts
export const automationApi = {
getRules: () => fetchAPI('/automation/rules/'),
createRule: (data) => fetchAPI('/automation/rules/', { method: 'POST', body: data }),
updateRule: (id, data) => fetchAPI(`/automation/rules/${id}/`, { method: 'PUT', body: data }),
deleteRule: (id) => fetchAPI(`/automation/rules/${id}/`, { method: 'DELETE' }),
executeRule: (id) => fetchAPI(`/automation/rules/${id}/execute/`, { method: 'POST' }),
getScheduledTasks: (filters) => fetchAPI('/automation/scheduled-tasks/', { params: filters }),
};
```
---
## TESTING & VALIDATION
### 2.6 Testing
**Test Cases**:
1. **Automation Service Tests**:
- ✅ Rules execute correctly
- ✅ Conditions evaluate correctly
- ✅ Actions execute correctly
- ✅ Execution limits enforced
- ✅ Credit checks work
2. **Scheduled Tasks Tests**:
- ✅ Scheduled tasks run on time
- ✅ Credit replenishment works monthly
- ✅ Task status tracking works
3. **API Tests**:
- ✅ CRUD operations work
- ✅ Rule execution endpoint works
- ✅ Scheduled task history works
4. **UI Tests**:
- ✅ Dashboard displays correctly
- ✅ Rules management works
- ✅ Schedule history displays correctly
---
## IMPLEMENTATION CHECKLIST
### Backend Tasks
- [ ] Create `business/automation/models.py`
- [ ] Create AutomationRule model
- [ ] Create ScheduledTask model
- [ ] Create automation migrations
- [ ] Create `business/automation/services/automation_service.py`
- [ ] Create `business/automation/services/rule_engine.py`
- [ ] Create `business/automation/services/condition_evaluator.py`
- [ ] Create `business/automation/services/action_executor.py`
- [ ] Create `infrastructure/messaging/automation_tasks.py`
- [ ] Add scheduled automation task
- [ ] Add monthly credit replenishment task
- [ ] Configure Celery Beat
- [ ] Create `modules/automation/views.py`
- [ ] Create AutomationRule ViewSet
- [ ] Create ScheduledTask ViewSet
- [ ] Create `modules/automation/serializers.py`
- [ ] Create `modules/automation/urls.py`
- [ ] Register automation URLs in main urls.py
### Frontend Tasks
- [ ] Implement `frontend/src/pages/Automation/Dashboard.tsx`
- [ ] Create `frontend/src/pages/Automation/Rules.tsx`
- [ ] Integrate schedules functionality into Automation Dashboard (not as separate page)
- [ ] Create `frontend/src/services/automation.api.ts`
- [ ] Create rule creation wizard
- [ ] Create rule editor
- [ ] Create schedule history table (within Automation Dashboard)
### Testing Tasks
- [ ] Test automation rule execution
- [ ] Test scheduled tasks
- [ ] Test credit replenishment
- [ ] Test API endpoints
- [ ] Test UI components
---
## RISK ASSESSMENT
| Risk | Level | Mitigation |
|------|-------|------------|
| **Rule execution errors** | MEDIUM | Comprehensive error handling, logging |
| **Credit limit violations** | MEDIUM | Credit checks before execution |
| **Scheduled task failures** | MEDIUM | Retry mechanism, error logging |
| **Performance issues** | LOW | Background processing, rate limiting |
---
## SUCCESS CRITERIA
- ✅ Automation rules execute correctly
- ✅ Scheduled tasks run on time
- ✅ Credit replenishment works monthly
- ✅ UI shows automation status
- ✅ Rules can be created, edited, deleted
- ✅ Execution history is tracked
- ✅ All automation respects credit limits
---
**END OF PHASE 2 DOCUMENT**

View File

@@ -1,642 +0,0 @@
# PHASE 3: SITE BUILDER
**Detailed Implementation Plan**
**Goal**: Build Site Builder for creating sites via wizard.
**Timeline**: 3-4 weeks
**Priority**: HIGH
**Dependencies**: Phase 1, Phase 2
---
## TABLE OF CONTENTS
1. [Overview](#overview)
2. [Sites Folder Access & File Management](#sites-folder-access--file-management)
3. [Site Builder Models](#site-builder-models)
4. [Site Structure Generation](#site-structure-generation)
5. [Site Builder API](#site-builder-api)
6. [Site Builder Frontend](#site-builder-frontend)
7. [Global Component Library](#global-component-library)
8. [Page Generation](#page-generation)
9. [Testing & Validation](#testing--validation)
10. [Implementation Checklist](#implementation-checklist)
---
## OVERVIEW
### Objectives
- ✅ Create Site Builder wizard for site creation
- ✅ Generate site structure using AI
- ✅ Build preview canvas for site editing
- ✅ Create shared component library
- ✅ Support multiple layouts and templates
- ✅ Enable file management for site assets
### Key Principles
- **Wizard-Based**: Step-by-step site creation process
- **AI-Powered**: AI generates site structure from business brief
- **Component Reuse**: Shared components across Site Builder, Sites Renderer, Main App
- **User-Friendly**: "Website Builder" or "Site Creator" in UI
---
## SITES FOLDER ACCESS & FILE MANAGEMENT
### 3.0 Sites Folder Access & File Management
**Purpose**: Manage site files and assets with proper access control.
#### Sites Folder Structure
```
/data/app/sites-data/
└── clients/
└── {site_id}/
└── v{version}/
├── site.json # Site definition
├── pages/ # Page definitions
│ ├── home.json
│ ├── about.json
│ └── ...
└── assets/ # User-managed files
├── images/
├── documents/
└── media/
```
#### User Access Rules
- **Owner/Admin**: Full access to all account sites
- **Editor**: Access to granted sites (via SiteUserAccess)
- **Viewer**: Read-only access to granted sites
- **File operations**: Scoped to user's accessible sites only
#### Site File Management Service
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Site File Management Service** | `business/site_building/services/file_management_service.py` | Phase 1 | File upload, delete, organize |
**FileManagementService**:
```python
# business/site_building/services/file_management_service.py
class SiteBuilderFileService:
def get_user_accessible_sites(self, user):
"""Get sites user can access for file management"""
if user.is_owner_or_admin():
return Site.objects.filter(account=user.account)
return user.get_accessible_sites()
def get_site_files_path(self, site_id, version=1):
"""Get site's files directory"""
return f"/data/app/sites-data/clients/{site_id}/v{version}/assets/"
def check_file_access(self, user, site_id):
"""Check if user can access site's files"""
accessible_sites = self.get_user_accessible_sites(user)
return any(site.id == site_id for site in accessible_sites)
def upload_file(self, user, site_id, file, folder='images'):
"""Upload file to site's assets folder"""
if not self.check_file_access(user, site_id):
raise PermissionDenied("No access to this site")
# Check storage quota
if not self.check_storage_quota(site_id, file.size):
raise ValidationError("Storage quota exceeded")
# Upload file
file_path = self._save_file(site_id, file, folder)
return file_path
```
#### File Upload API
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **File Upload API** | `modules/site_builder/views.py` | File Management Service | Handle file uploads |
#### File Browser UI
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **File Browser UI** | `site-builder/src/components/files/FileBrowser.tsx` | NEW | File browser component |
#### Storage Quota Check
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Storage Quota Check** | `infrastructure/storage/file_storage.py` | Phase 1 | Check site storage quota |
---
## SITE BUILDER MODELS
### 3.1 Site Builder Models
**Purpose**: Store site blueprints and page definitions.
#### SiteBlueprint Model
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **SiteBlueprint Model** | `business/site_building/models.py` | Phase 1 | Store site structure |
**SiteBlueprint Model**:
```python
# business/site_building/models.py
class SiteBlueprint(SiteSectorBaseModel):
name = models.CharField(max_length=255)
description = models.TextField(blank=True)
# Site configuration
config_json = models.JSONField(default=dict)
# Example: {'business_type': 'ecommerce', 'style': 'modern'}
# Generated structure
structure_json = models.JSONField(default=dict)
# Example: {'pages': [...], 'layout': 'default', 'theme': {...}}
# Status tracking
status = models.CharField(
max_length=20,
choices=[
('draft', 'Draft'),
('generating', 'Generating'),
('ready', 'Ready'),
('deployed', 'Deployed'),
],
default='draft'
)
# Hosting configuration
hosting_type = models.CharField(
max_length=50,
choices=[
('igny8_sites', 'IGNY8 Sites'),
('wordpress', 'WordPress'),
('shopify', 'Shopify'),
('multi', 'Multiple Destinations'),
],
default='igny8_sites'
)
# Version tracking
version = models.IntegerField(default=1)
deployed_version = models.IntegerField(null=True, blank=True)
class Meta:
ordering = ['-created_at']
```
#### PageBlueprint Model
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **PageBlueprint Model** | `business/site_building/models.py` | Phase 1 | Store page definitions |
**PageBlueprint Model**:
```python
# business/site_building/models.py
class PageBlueprint(SiteSectorBaseModel):
site_blueprint = models.ForeignKey(SiteBlueprint, on_delete=models.CASCADE, related_name='pages')
slug = models.SlugField(max_length=255)
title = models.CharField(max_length=255)
# Page type
type = models.CharField(
max_length=50,
choices=[
('home', 'Home'),
('about', 'About'),
('services', 'Services'),
('products', 'Products'),
('blog', 'Blog'),
('contact', 'Contact'),
('custom', 'Custom'),
]
)
# Page content (blocks)
blocks_json = models.JSONField(default=list)
# Example: [{'type': 'hero', 'data': {...}}, {'type': 'features', 'data': {...}}]
# Status
status = models.CharField(
max_length=20,
choices=[
('draft', 'Draft'),
('generating', 'Generating'),
('ready', 'Ready'),
],
default='draft'
)
# Order
order = models.IntegerField(default=0)
class Meta:
ordering = ['order', 'created_at']
unique_together = [['site_blueprint', 'slug']]
```
#### Site Builder Migrations
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Site Builder Migrations** | `business/site_building/migrations/` | Phase 1 | Create initial migrations |
---
## SITE STRUCTURE GENERATION
### 3.2 Site Structure Generation
**Purpose**: Use AI to generate site structure from business brief.
#### Structure Generation AI Function
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Structure Generation AI Function** | `infrastructure/ai/functions/generate_site_structure.py` | Existing AI framework | AI function for structure generation |
**GenerateSiteStructureFunction**:
```python
# infrastructure/ai/functions/generate_site_structure.py
class GenerateSiteStructureFunction(BaseAIFunction):
def get_operation_type(self):
return 'site_structure_generation'
def get_estimated_cost(self, payload):
return CREDIT_COSTS['site_structure_generation']
def execute(self, payload, account):
"""Generate site structure from business brief"""
business_brief = payload['business_brief']
objectives = payload.get('objectives', [])
style_preferences = payload.get('style', {})
# Build prompt
prompt = self._build_prompt(business_brief, objectives, style_preferences)
# Call AI
response = self.ai_core.generate(prompt, model='gpt-4')
# Parse response to structure JSON
structure = self._parse_structure(response)
return structure
```
#### Structure Generation Service
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Structure Generation Service** | `business/site_building/services/structure_generation_service.py` | Phase 1, AI framework | Service to generate site structure |
**StructureGenerationService**:
```python
# business/site_building/services/structure_generation_service.py
class StructureGenerationService:
def __init__(self):
self.ai_function = GenerateSiteStructureFunction()
self.credit_service = CreditService()
def generate_structure(self, site_blueprint, business_brief, objectives, style):
"""Generate site structure for blueprint"""
account = site_blueprint.account
# Check credits
self.credit_service.check_credits(account, 'site_structure_generation')
# Update status
site_blueprint.status = 'generating'
site_blueprint.save()
# Generate structure
payload = {
'business_brief': business_brief,
'objectives': objectives,
'style': style,
}
structure = self.ai_function.execute(payload, account)
# Deduct credits
self.credit_service.deduct_credits(account, 'site_structure_generation')
# Update blueprint
site_blueprint.structure_json = structure
site_blueprint.status = 'ready'
site_blueprint.save()
# Create page blueprints
self._create_page_blueprints(site_blueprint, structure)
return site_blueprint
```
#### Site Structure Prompts
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Site Structure Prompts** | `infrastructure/ai/prompts.py` | Existing prompt system | Add site structure prompts |
---
## SITE BUILDER API
### 3.3 Site Builder API
**Purpose**: API endpoints for site builder operations.
#### Site Builder ViewSet
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Site Builder ViewSet** | `modules/site_builder/views.py` | Structure Generation Service | CRUD operations for site blueprints |
**SiteBuilderViewSet**:
```python
# modules/site_builder/views.py
class SiteBuilderViewSet(AccountModelViewSet):
queryset = SiteBlueprint.objects.all()
serializer_class = SiteBlueprintSerializer
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.structure_service = StructureGenerationService()
@action(detail=True, methods=['post'])
def generate_structure(self, request, pk=None):
"""Generate site structure"""
blueprint = self.get_object()
business_brief = request.data.get('business_brief')
objectives = request.data.get('objectives', [])
style = request.data.get('style', {})
blueprint = self.structure_service.generate_structure(
blueprint, business_brief, objectives, style
)
serializer = self.get_serializer(blueprint)
return Response(serializer.data)
```
#### Site Builder URLs
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Site Builder URLs** | `modules/site_builder/urls.py` | None | Register site builder routes |
#### Site Builder Serializers
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Site Builder Serializers** | `modules/site_builder/serializers.py` | None | Serializers for SiteBlueprint and PageBlueprint |
---
## SITE BUILDER FRONTEND
### 3.4 Site Builder Frontend (New Container)
**User-Friendly Name**: "Website Builder" or "Site Creator"
#### Create Site Builder Container
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Create Site Builder Container** | `docker-compose.app.yml` | None | Add new container for site builder |
**Docker Compose Configuration**:
```yaml
# docker-compose.app.yml
igny8_site_builder:
build: ./site-builder
ports:
- "8022:5175"
volumes:
- /data/app/igny8/site-builder:/app
environment:
- VITE_API_URL=http://igny8_backend:8010
```
#### Wizard Steps
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Wizard Steps** | `site-builder/src/pages/wizard/` | NEW | Step-by-step wizard components |
**Wizard Steps**:
- Step 1: Type Selection (Business type, industry)
- Step 2: Business Brief (Description, goals)
- Step 3: Objectives (What pages needed)
- Step 4: Style Preferences (Colors, fonts, layout)
#### Preview Canvas
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Preview Canvas** | `site-builder/src/pages/preview/` | NEW | Live preview of site |
#### Site Builder State
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Site Builder State** | `site-builder/src/state/builderStore.ts` | NEW | Zustand store for builder state |
#### Site Builder API Client
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Site Builder API Client** | `site-builder/src/api/builder.api.ts` | NEW | API client for site builder |
#### Layout Selection
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Layout Selection** | `site-builder/src/components/layouts/` | NEW | Layout selector component |
#### Template Library
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Template Library** | `site-builder/src/components/templates/` | NEW | Template selector component |
#### Block Components
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Block Components** | `site-builder/src/components/blocks/` | NEW | Block components (imports from shared) |
---
## GLOBAL COMPONENT LIBRARY
### 3.7 Global Component Library
**Purpose**: Shared components across Site Builder, Sites Renderer, and Main App.
#### Create Shared Component Library
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Create Shared Component Library** | `frontend/src/components/shared/` | None | Create shared component structure |
**Component Library Structure**:
```
frontend/src/components/shared/
├── blocks/
│ ├── Hero.tsx
│ ├── Features.tsx
│ ├── Services.tsx
│ ├── Products.tsx
│ ├── Testimonials.tsx
│ ├── ContactForm.tsx
│ └── ...
├── layouts/
│ ├── DefaultLayout.tsx
│ ├── MinimalLayout.tsx
│ ├── MagazineLayout.tsx
│ ├── EcommerceLayout.tsx
│ ├── PortfolioLayout.tsx
│ ├── BlogLayout.tsx
│ └── CorporateLayout.tsx
└── templates/
├── BlogTemplate.tsx
├── BusinessTemplate.tsx
├── PortfolioTemplate.tsx
└── ...
```
**Usage**: Site Builder, Sites Renderer, and Main App all use same components (no duplicates)
#### Component Documentation
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Component Documentation** | `frontend/src/components/shared/README.md` | None | Document all shared components |
---
## PAGE GENERATION
### 3.5 Page Generation (Reuse Content Service)
**Purpose**: Generate page content using existing ContentService.
#### Extend ContentService
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Extend ContentService** | `business/content/services/content_generation_service.py` | Phase 1 | Add site page generation method |
#### Add Site Page Type
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Add Site Page Type** | `business/content/models.py` | Phase 1 | Add site page content type |
#### Page Generation Prompts
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Page Generation Prompts** | `infrastructure/ai/prompts.py` | Existing prompt system | Add page generation prompts |
---
## TESTING & VALIDATION
### 3.6 Testing
**Test Cases**:
1. **Site Builder Tests**:
- ✅ Site Builder wizard works end-to-end
- ✅ Structure generation creates valid blueprints
- ✅ Preview renders correctly
- ✅ Page generation reuses existing content service
2. **File Management Tests**:
- ✅ File upload works
- ✅ File access control works
- ✅ Storage quota enforced
3. **Component Library Tests**:
- ✅ Components render correctly
- ✅ Components work in Site Builder
- ✅ Components work in Sites Renderer
---
## IMPLEMENTATION CHECKLIST
### Backend Tasks
- [ ] Create `business/site_building/models.py`
- [ ] Create SiteBlueprint model
- [ ] Create PageBlueprint model
- [ ] Create site builder migrations
- [ ] Create `business/site_building/services/file_management_service.py`
- [ ] Create `business/site_building/services/structure_generation_service.py`
- [ ] Create `infrastructure/ai/functions/generate_site_structure.py`
- [ ] Add site structure prompts
- [ ] Create `modules/site_builder/views.py`
- [ ] Create SiteBuilder ViewSet
- [ ] Create `modules/site_builder/serializers.py`
- [ ] Create `modules/site_builder/urls.py`
- [ ] Extend ContentService for page generation
### Frontend Tasks
- [ ] Create `site-builder/` folder structure
- [ ] Create Site Builder container in docker-compose
- [ ] Create wizard steps
- [ ] Create preview canvas
- [ ] Create builder state store
- [ ] Create API client
- [ ] Create layout selector
- [ ] Create template library
- [ ] Create `frontend/src/components/shared/` structure
- [ ] Create block components
- [ ] Create layout components
- [ ] Create template components
- [ ] Create component documentation
### Testing Tasks
- [ ] Test site builder wizard
- [ ] Test structure generation
- [ ] Test file management
- [ ] Test component library
- [ ] Test page generation
---
## RISK ASSESSMENT
| Risk | Level | Mitigation |
|------|-------|------------|
| **AI structure generation quality** | MEDIUM | Prompt engineering, validation |
| **Component compatibility** | MEDIUM | Shared component library, testing |
| **File management security** | MEDIUM | Access control, validation |
| **Performance with large sites** | LOW | Optimization, caching |
---
## SUCCESS CRITERIA
- ✅ Site Builder wizard works end-to-end
- ✅ Structure generation creates valid blueprints
- ✅ Preview renders correctly
- ✅ Page generation reuses existing content service
- ✅ File management works correctly
- ✅ Shared components work across all apps
- ✅ Multiple layouts supported
---
**END OF PHASE 3 DOCUMENT**

View File

@@ -1,391 +0,0 @@
# PHASE 4: LINKER & OPTIMIZER
**Detailed Implementation Plan**
**Goal**: Add linking and optimization as post-processing stages with multiple entry points.
**Timeline**: 4-5 weeks
**Priority**: MEDIUM
**Dependencies**: Phase 1
---
## TABLE OF CONTENTS
1. [Overview](#overview)
2. [Content Workflow & Entry Points](#content-workflow--entry-points)
3. [Content Model Extensions](#content-model-extensions)
4. [Linker Implementation](#linker-implementation)
5. [Optimizer Implementation](#optimizer-implementation)
6. [Content Pipeline Service](#content-pipeline-service)
7. [Linker & Optimizer APIs](#linker--optimizer-apis)
8. [Linker & Optimizer UI](#linker--optimizer-ui)
9. [Testing & Validation](#testing--validation)
10. [Implementation Checklist](#implementation-checklist)
---
## OVERVIEW
### Objectives
- ✅ Add internal linking to content
- ✅ Add content optimization
- ✅ Support multiple entry points (Writer, WordPress Sync, 3rd Party, Manual)
- ✅ Create content pipeline service
- ✅ Build UI for linker and optimizer
### Key Principles
- **Multiple Entry Points**: Optimizer works from any content source
- **Unified Content Model**: All content stored in same model with source tracking
- **Pipeline Orchestration**: Linker → Optimizer → Publish workflow
- **Source Agnostic**: Optimizer works on any content regardless of source
---
## CONTENT WORKFLOW & ENTRY POINTS
### 4.0 Content Workflow & Entry Points
**Content Sources**:
1. **IGNY8 Generated** - Content created via Writer module
2. **WordPress Synced** - Content synced from WordPress via plugin
3. **3rd Party Synced** - Content synced from external sources (Shopify, custom APIs)
**Workflow Entry Points**:
```
Entry Point 1: Writer → Linker → Optimizer → Publish
Entry Point 2: WordPress Sync → Optimizer → Publish
Entry Point 3: 3rd Party Sync → Optimizer → Publish
Entry Point 4: Manual Selection → Linker/Optimizer
```
**Content Storage Strategy**:
- All content stored in unified `Content` model
- `source` field: `'igny8'`, `'wordpress'`, `'shopify'`, `'custom'`
- `sync_status` field: `'native'`, `'imported'`, `'synced'`
---
## CONTENT MODEL EXTENSIONS
### 4.1 Content Model Extensions
**Purpose**: Add fields to track content source and sync status.
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Add source field** | `business/content/models.py` | Phase 1 | Track content source |
| **Add sync_status field** | `business/content/models.py` | Phase 1 | Track sync status |
| **Add external_id field** | `business/content/models.py` | Phase 1 | Store external platform ID |
| **Add sync_metadata field** | `business/content/models.py` | Phase 1 | Store platform-specific metadata |
**Content Model Extensions**:
```python
# business/content/models.py
class Content(SiteSectorBaseModel):
# Existing fields...
# NEW: Source tracking
source = models.CharField(
max_length=50,
choices=[
('igny8', 'IGNY8 Generated'),
('wordpress', 'WordPress Synced'),
('shopify', 'Shopify Synced'),
('custom', 'Custom API Synced'),
],
default='igny8'
)
# NEW: Sync status
sync_status = models.CharField(
max_length=50,
choices=[
('native', 'Native IGNY8 Content'),
('imported', 'Imported from External'),
('synced', 'Synced from External'),
],
default='native'
)
# NEW: External reference
external_id = models.CharField(max_length=255, blank=True, null=True)
external_url = models.URLField(blank=True, null=True)
sync_metadata = models.JSONField(default=dict)
# NEW: Linking fields
internal_links = models.JSONField(default=list)
linker_version = models.IntegerField(default=0)
# NEW: Optimization fields
optimizer_version = models.IntegerField(default=0)
optimization_scores = models.JSONField(default=dict)
```
---
## LINKER IMPLEMENTATION
### 4.2 Linker Models
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **InternalLink Model** | `business/linking/models.py` | Phase 1 | Store link relationships |
| **LinkGraph Model** | `business/linking/models.py` | Phase 1 | Store link graph |
### 4.3 Linker Service
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **LinkerService** | `business/linking/services/linker_service.py` | Phase 1, ContentService | Main linking service |
| **Link Candidate Engine** | `business/linking/services/candidate_engine.py` | Phase 1 | Find link candidates |
| **Link Injection Engine** | `business/linking/services/injection_engine.py` | Phase 1 | Inject links into content |
**LinkerService**:
```python
# business/linking/services/linker_service.py
class LinkerService:
def process(self, content_id):
"""Process content for linking"""
content = Content.objects.get(id=content_id)
# Check credits
credit_service.check_credits(content.account, 'linking')
# Find link candidates
candidates = self.candidate_engine.find_candidates(content)
# Inject links
linked_content = self.injection_engine.inject_links(content, candidates)
# Update content
content.internal_links = linked_content['links']
content.linker_version += 1
content.save()
# Deduct credits
credit_service.deduct_credits(content.account, 'linking')
return content
```
---
## OPTIMIZER IMPLEMENTATION
### 4.5 Optimizer Models
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **OptimizationTask Model** | `business/optimization/models.py` | Phase 1 | Store optimization results |
| **OptimizationScores Model** | `business/optimization/models.py` | Phase 1 | Store optimization scores |
### 4.6 Optimizer Service (Multiple Entry Points)
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **OptimizerService** | `business/optimization/services/optimizer_service.py` | Phase 1, ContentService | Main optimization service |
| **Content Analyzer** | `business/optimization/services/analyzer.py` | Phase 1 | Analyze content quality |
| **Optimization AI Function** | `infrastructure/ai/functions/optimize_content.py` | Existing AI framework | AI optimization function |
**OptimizerService**:
```python
# business/optimization/services/optimizer_service.py
class OptimizerService:
def optimize_from_writer(self, content_id):
"""Entry Point 1: Writer → Optimizer"""
content = Content.objects.get(id=content_id, source='igny8')
return self.optimize(content)
def optimize_from_wordpress_sync(self, content_id):
"""Entry Point 2: WordPress Sync → Optimizer"""
content = Content.objects.get(id=content_id, source='wordpress')
return self.optimize(content)
def optimize_from_external_sync(self, content_id):
"""Entry Point 3: External Sync → Optimizer"""
content = Content.objects.get(id=content_id, source__in=['shopify', 'custom'])
return self.optimize(content)
def optimize_manual(self, content_id):
"""Entry Point 4: Manual Selection → Optimizer"""
content = Content.objects.get(id=content_id)
return self.optimize(content)
def optimize(self, content):
"""Unified optimization logic"""
# Check credits
credit_service.check_credits(content.account, 'optimization', content.word_count)
# Analyze content
scores_before = self.analyzer.analyze(content)
# Optimize content
optimized = self.ai_function.optimize(content)
# Analyze optimized content
scores_after = self.analyzer.analyze(optimized)
# Store optimization task
OptimizationTask.objects.create(
content=content,
scores_before=scores_before,
scores_after=scores_after,
html_before=content.html_content,
html_after=optimized['html_content'],
)
# Update content
content.optimizer_version += 1
content.optimization_scores = scores_after
content.save()
# Deduct credits
credit_service.deduct_credits(content.account, 'optimization', content.word_count)
return content
```
---
## CONTENT PIPELINE SERVICE
### 4.7 Content Pipeline Service
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **ContentPipelineService** | `business/content/services/content_pipeline_service.py` | LinkerService, OptimizerService | Orchestrate content pipeline |
**Pipeline Workflow States**:
```
Content States:
- 'draft' → Generated, not processed
- 'linked' → Links added, ready for optimization
- 'optimized' → Optimized, ready for review
- 'review' → Ready for publishing
- 'published' → Published to destination(s)
```
**ContentPipelineService**:
```python
# business/content/services/content_pipeline_service.py
class ContentPipelineService:
def process_writer_content(self, content_id, stages=['linking', 'optimization']):
"""Writer → Linker → Optimizer pipeline"""
content = Content.objects.get(id=content_id, source='igny8')
if 'linking' in stages:
content = linker_service.process(content.id)
if 'optimization' in stages:
content = optimizer_service.optimize_from_writer(content.id)
return content
def process_synced_content(self, content_id, stages=['optimization']):
"""Synced Content → Optimizer (skip linking if needed)"""
content = Content.objects.get(id=content_id)
if 'optimization' in stages:
content = optimizer_service.optimize_manual(content.id)
return content
```
---
## LINKER & OPTIMIZER APIs
### 4.8 Linker & Optimizer APIs
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Linker ViewSet** | `modules/linker/views.py` | LinkerService | API for linker operations |
| **Optimizer ViewSet** | `modules/optimizer/views.py` | OptimizerService | API for optimizer operations |
---
## LINKER & OPTIMIZER UI
### 4.9 Linker & Optimizer UI
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Linker Dashboard** | `frontend/src/pages/Linker/Dashboard.tsx` | NEW | Linker overview |
| **Optimizer Dashboard** | `frontend/src/pages/Optimizer/Dashboard.tsx` | NEW | Optimizer overview |
| **Content Selection UI** | `frontend/src/components/optimizer/ContentSelector.tsx` | NEW | Select content for optimization |
| **Source Badge Component** | `frontend/src/components/content/SourceBadge.tsx` | NEW | Show content source |
**Optimizer UI Features**:
- Show content source (IGNY8, WordPress, Shopify badge)
- Show sync status (Native, Synced, Imported badge)
- Entry point selection (from Writer, from Sync, Manual)
- Content list with source filters
- "Send to Optimizer" button (works for any source)
### 4.10 Content Filtering & Display
| Task | File | Dependencies | Implementation |
|------|------|--------------|----------------|
| **Content Filter Component** | `frontend/src/components/content/ContentFilter.tsx` | NEW | Filter content by source |
| **Source Filter** | `frontend/src/components/content/SourceFilter.tsx` | NEW | Filter by source |
| **Sync Status Filter** | `frontend/src/components/content/SyncStatusFilter.tsx` | NEW | Filter by sync status |
---
## TESTING & VALIDATION
### 4.11 Testing
**Test Cases**:
- ✅ Writer → Linker handover works
- ✅ Linker finds appropriate link candidates
- ✅ Links inject correctly into content
- ✅ Optimizer works from Writer entry point
- ✅ Optimizer works from WordPress sync entry point
- ✅ Optimizer works from 3rd party sync entry point
- ✅ Optimizer works from manual selection
- ✅ Synced content stored correctly with source flags
- ✅ Content filtering works (by source, sync_status)
- ✅ Pipeline orchestrates correctly
- ✅ All entry points use same optimization logic
---
## IMPLEMENTATION CHECKLIST
### Backend Tasks
- [ ] Extend Content model with source/sync fields
- [ ] Create `business/linking/models.py`
- [ ] Create LinkerService
- [ ] Create `business/optimization/models.py`
- [ ] Create OptimizerService
- [ ] Create optimization AI function
- [ ] Create ContentPipelineService
- [ ] Create Linker ViewSet
- [ ] Create Optimizer ViewSet
- [ ] Create content sync service (for Phase 6)
### Frontend Tasks
- [ ] Create Linker Dashboard
- [ ] Create Optimizer Dashboard
- [ ] Create content selection UI
- [ ] Create source badge component
- [ ] Create content filters
- [ ] Update content list with filters
---
## SUCCESS CRITERIA
- ✅ Writer → Linker handover works
- ✅ Optimizer works from all entry points
- ✅ Content source tracking works
- ✅ Pipeline orchestrates correctly
- ✅ UI shows content sources and filters
---
**END OF PHASE 4 DOCUMENT**