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:
Binary file not shown.
8
backend/igny8_core/business/optimization/apps.py
Normal file
8
backend/igny8_core/business/optimization/apps.py
Normal 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'
|
||||||
|
|
||||||
@@ -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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
@@ -9,6 +9,7 @@ class Migration(migrations.Migration):
|
|||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('igny8_core_auth', '0014_remove_plan_operation_limits_phase0'),
|
('igny8_core_auth', '0014_remove_plan_operation_limits_phase0'),
|
||||||
|
('writer', '0009_add_content_site_source_fields'),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
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)),
|
('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)])),
|
('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)),
|
('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')),
|
('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')),
|
('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': {...}}, ...]")),
|
('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)),
|
('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')),
|
('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')),
|
('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', 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')),
|
('site_blueprint', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='pages', to='site_building.siteblueprint')),
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ INSTALLED_APPS = [
|
|||||||
'igny8_core.modules.billing.apps.BillingConfig',
|
'igny8_core.modules.billing.apps.BillingConfig',
|
||||||
'igny8_core.modules.automation.apps.AutomationConfig',
|
'igny8_core.modules.automation.apps.AutomationConfig',
|
||||||
'igny8_core.business.site_building.apps.SiteBuildingConfig',
|
'igny8_core.business.site_building.apps.SiteBuildingConfig',
|
||||||
|
'igny8_core.business.optimization.apps.OptimizationConfig',
|
||||||
'igny8_core.modules.site_builder.apps.SiteBuilderConfig',
|
'igny8_core.modules.site_builder.apps.SiteBuilderConfig',
|
||||||
'igny8_core.modules.linker.apps.LinkerConfig',
|
'igny8_core.modules.linker.apps.LinkerConfig',
|
||||||
'igny8_core.modules.optimizer.apps.OptimizerConfig',
|
'igny8_core.modules.optimizer.apps.OptimizerConfig',
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
1180
docs/planning/phases/PHASE-0-4-FOUNDATION-TO-LINKER-OPTIMIZER.md
Normal file
1180
docs/planning/phases/PHASE-0-4-FOUNDATION-TO-LINKER-OPTIMIZER.md
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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**
|
|
||||||
|
|
||||||
@@ -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**
|
|
||||||
|
|
||||||
@@ -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**
|
|
||||||
|
|
||||||
@@ -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**
|
|
||||||
|
|
||||||
@@ -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**
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user