Files
igny8/docs/90-REFERENCE/MODELS.md
IGNY8 VPS (Salman) c777e5ccb2 dos updates
2026-01-20 14:45:21 +00:00

852 lines
26 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Database Models Reference
**Last Verified:** January 20, 2026
**Version:** 1.8.4
**Total Models:** 52+
---
## Data Scoping Overview
| Scope | Models | Base Class | Filter By |
|-------|--------|------------|-----------|
| **Global** | `IntegrationProvider`, `AIModelConfig`, `SystemAISettings`, `GlobalAIPrompt`, `GlobalAuthorProfile`, `GlobalStrategy`, `GlobalModuleSettings`, `Industry`, `IndustrySector`, `SeedKeyword` | `models.Model` | None (platform-wide) |
| **Account** | `Account`, `User`, `Plan`, `Subscription`, `AccountSettings`, `ModuleEnableSettings`, `AISettings`, `AIPrompt`, `AuthorProfile`, `CreditBalance`, `PasswordResetToken` | `AccountBaseModel` | `account` |
| **Site** | `Site`, `PublishingSettings`, `AutomationConfig`, `DefaultAutomationConfig`, `AutomationRun`, `SiteIntegration`, `SiteUserAccess` | `AccountBaseModel` | `account`, `site` |
| **Site+Sector** | `Keywords`, `Clusters`, `ContentIdeas`, `Tasks`, `Content`, `Images`, `ContentTaxonomyRelation` | `SiteSectorBaseModel` | `site`, `sector` |
| **Billing** | `CreditCostConfig`, `BillingConfiguration`, `CreditPackage`, `PaymentMethodConfig`, `WebhookEvent` | `models.Model` | varies |
| **System** | `SystemSettings`, `UserSettings`, `EmailSettings`, `EmailTemplate`, `EmailLog` | `models.Model` | varies |
| **Plugins** | `Plugin`, `PluginVersion`, `PluginInstallation`, `PluginDownload` | `models.Model` | varies |
---
## Model Count by Location
| Location | Count | Models |
|----------|-------|--------|
| `auth/models.py` | 10 | Account, User, Plan, Subscription, Industry, IndustrySector, SeedKeyword, Site, SiteUserAccess, PasswordResetToken |
| `modules/system/` | 10 | IntegrationProvider, SystemAISettings, SystemSettings, UserSettings, EmailSettings, EmailTemplate, EmailLog, GlobalAIPrompt, GlobalAuthorProfile, GlobalStrategy |
| `business/automation/` | 3 | DefaultAutomationConfig, AutomationConfig, AutomationRun |
| `business/billing/` | 6 | CreditCostConfig, BillingConfiguration, CreditPackage, PaymentMethodConfig, AIModelConfig, WebhookEvent |
| `business/content/` | 1 | ContentTaxonomyRelation |
| `plugins/` | 4 | Plugin, PluginVersion, PluginInstallation, PluginDownload |
| `modules/planner/` | 3 | Keywords, Clusters, ContentIdeas |
| `modules/writer/` | 3 | Tasks, Content, Images |
---
## System Models (v1.4.0+) (`igny8_core/modules/system/`)
**Purpose:** Centralized AI configuration, provider API keys, and system-wide defaults.
### IntegrationProvider (NEW v1.4.0)
Centralized storage for ALL external service API keys. Admin-only.
```python
class IntegrationProvider(models.Model):
"""Per final-model-schemas.md - Centralized API key storage"""
provider_id = CharField(max_length=50, primary_key=True) # openai, runware, stripe, etc.
display_name = CharField(max_length=100)
provider_type = CharField(max_length=20) # ai, payment, email, storage
# Authentication
api_key = CharField(max_length=500, blank=True)
api_secret = CharField(max_length=500, blank=True)
webhook_secret = CharField(max_length=500, blank=True)
api_endpoint = URLField(blank=True)
# Configuration
config = JSONField(default=dict)
is_active = BooleanField(default=True)
is_sandbox = BooleanField(default=False)
# Audit
updated_by = ForeignKey(User, null=True)
created_at = DateTimeField(auto_now_add=True)
updated_at = DateTimeField(auto_now=True)
```
**Seeded Providers:**
- `openai` - AI (text + DALL-E)
- `runware` - AI (images)
- `anthropic` - AI (future)
- `stripe` - Payment
- `paypal` - Payment
- `resend` - Email
**Helper Methods:**
- `IntegrationProvider.get_provider(provider_id)` - Get active provider
- `IntegrationProvider.get_api_key(provider_id)` - Get API key for provider
- `IntegrationProvider.get_providers_by_type(type)` - List providers by type
---
### AIModelConfig (NEW v1.4.0)
Single Source of Truth for all AI models with pricing and credit configuration.
```python
class AIModelConfig(models.Model):
"""Per final-model-schemas.md - Model definitions + pricing"""
model_name = CharField(max_length=100, unique=True) # gpt-4o-mini, dall-e-3, runware:97@1
model_type = CharField(max_length=20) # text, image
provider = CharField(max_length=50) # Links to IntegrationProvider
display_name = CharField(max_length=200)
is_default = BooleanField(default=False) # One default per type
is_active = BooleanField(default=True)
# Text Model Pricing (per 1K tokens)
cost_per_1k_input = DecimalField(max_digits=10, decimal_places=6, null=True)
cost_per_1k_output = DecimalField(max_digits=10, decimal_places=6, null=True)
tokens_per_credit = IntegerField(null=True) # e.g., 1000, 10000
# Image Model Pricing
credits_per_image = IntegerField(null=True) # e.g., 1, 5, 15
quality_tier = CharField(max_length=20, null=True) # basic, quality, premium
# Model Limits
max_tokens = IntegerField(null=True)
context_window = IntegerField(null=True)
capabilities = JSONField(default=dict) # vision, function_calling, etc.
created_at = DateTimeField(auto_now_add=True)
updated_at = DateTimeField(auto_now=True)
```
**Credit Configuration Examples:**
| Model | Type | tokens_per_credit | credits_per_image | quality_tier |
|-------|------|-------------------|-------------------|--------------|
| gpt-4o | text | 1000 | - | - |
| gpt-4o-mini | text | 10000 | - | - |
| runware:97@1 | image | - | 1 | basic |
| dall-e-3 | image | - | 5 | quality |
| google:4@2 | image | - | 15 | premium |
**Image Model Reference (v1.5.0 Planned):**
| Model | AIR ID | Tier | Supported Dimensions |
|-------|--------|------|---------------------|
| Hi Dream Full | `runware:97@1` | Basic | 1024×1024, 1280×768 |
| Bria 3.2 | `bria:10@1` | Quality | 1024×1024, 1344×768 |
| Nano Banana | `google:4@2` | Premium | 1024×1024, 1376×768 |
**Helper Methods:**
- `AIModelConfig.get_default_text_model()` - Get default text model
- `AIModelConfig.get_default_image_model()` - Get default image model
- `AIModelConfig.get_image_models_by_tier()` - List image models by quality tier
---
### SystemAISettings (NEW v1.4.0)
System-wide AI defaults. Singleton (pk=1).
```python
class SystemAISettings(models.Model):
"""Per final-model-schemas.md - Renamed from GlobalIntegrationSettings"""
# AI Parameters
temperature = FloatField(default=0.7) # 0.0-2.0
max_tokens = IntegerField(default=8192)
# Image Generation Settings
image_style = CharField(max_length=30, default='photorealistic')
image_quality = CharField(max_length=20, default='standard') # standard, hd
max_images_per_article = IntegerField(default=4) # 1-8
image_size = CharField(max_length=20, default='1024x1024')
# Audit
updated_by = ForeignKey(User, null=True)
updated_at = DateTimeField(auto_now=True)
```
**Image Style Choices:**
- `photorealistic` - Ultra realistic photography
- `illustration` - Digital illustration
- `3d_render` - Computer generated 3D
- `minimal_flat` - Minimal / Flat Design
- `artistic` - Artistic / Painterly
- `cartoon` - Cartoon / Stylized
**Image Size Choices:**
- `1024x1024` - Square
- `1792x1024` - Landscape
- `1024x1792` - Portrait
**Helper Methods:**
- `SystemAISettings.get_instance()` - Get singleton instance
- `SystemAISettings.get_effective_temperature(account)` - Get with account override
- `SystemAISettings.get_effective_image_style(account)` - Get with account override
---
### AccountSettings (Per-Account Overrides)
Generic key-value store for account-specific settings.
```python
class AccountSettings(AccountBaseModel):
"""Per final-model-schemas.md - Account overrides"""
key = CharField(max_length=100) # Setting key
value = JSONField(default=dict) # Setting value
created_at = DateTimeField(auto_now_add=True)
updated_at = DateTimeField(auto_now=True)
```
**AI-Related Keys** (override SystemAISettings defaults):
| Key | Type | Example | Notes |
|-----|------|---------|-------|
| `ai.temperature` | float | 0.8 | Override system default |
| `ai.max_tokens` | int | 8192 | Override system default |
| `ai.image_style` | string | "illustration" | Override system default |
| `ai.image_quality` | string | "hd" | Override system default |
| `ai.max_images` | int | 6 | Override system default |
| `ai.image_quality_tier` | string | "premium" | User's preferred tier |
---
### CreditCostConfig (Operation-Level Pricing)
Fixed credit costs per operation type.
```python
class CreditCostConfig(models.Model):
"""Per final-model-schemas.md - Operation pricing"""
operation_type = CharField(max_length=50, primary_key=True) # Unique operation ID
display_name = CharField(max_length=100)
base_credits = IntegerField(default=1) # Fixed credits per operation
is_active = BooleanField(default=True)
description = TextField(blank=True)
```
**Note:** `tokens_per_credit` moved to AIModelConfig as of v1.4.0.
---
## DEPRECATED Models
### GlobalIntegrationSettings (DEPRECATED in v1.4.0)
**Replaced by:** `IntegrationProvider` (API keys) + `AIModelConfig` (model configs) + `SystemAISettings` (defaults)
```python
# DEPRECATED - Do not use
class GlobalIntegrationSettings(models.Model):
# API keys now in IntegrationProvider
openai_api_key = CharField(max_length=500) # → IntegrationProvider.get_api_key('openai')
runware_api_key = CharField(max_length=500) # → IntegrationProvider.get_api_key('runware')
# Models now in AIModelConfig
openai_model = CharField(default='gpt-4o-mini') # → AIModelConfig.is_default
# Settings now in SystemAISettings
openai_temperature = FloatField(default=0.7) # → SystemAISettings.temperature
openai_max_tokens = IntegerField(default=8192) # → SystemAISettings.max_tokens
```
---
### GlobalAIPrompt
```python
class GlobalAIPrompt(models.Model):
prompt_type = CharField(max_length=100) # clustering, ideas, content_generation
prompt_value = TextField()
variables = JSONField(default=list)
is_active = BooleanField(default=True)
```
### GlobalAuthorProfile
```python
class GlobalAuthorProfile(models.Model):
name = CharField(max_length=255)
tone = CharField(max_length=50) # professional, casual, technical
language = CharField(max_length=10, default='en')
is_active = BooleanField(default=True)
```
---
## Auth Models (`igny8_core/auth/models/`)
### User
```python
class User(AbstractBaseUser, PermissionsMixin):
id = UUIDField(primary_key=True)
email = EmailField(unique=True)
first_name = CharField(max_length=150)
last_name = CharField(max_length=150)
account = ForeignKey(Account, related_name='users')
role = ForeignKey(Group, null=True)
is_active = BooleanField(default=True)
is_staff = BooleanField(default=False)
created_at = DateTimeField(auto_now_add=True)
updated_at = DateTimeField(auto_now=True)
```
**Relations:** Account (many-to-one)
---
### Account
```python
class Account(models.Model):
id = UUIDField(primary_key=True)
name = CharField(max_length=255)
plan = ForeignKey(Plan, null=True)
owner = ForeignKey(User, related_name='owned_accounts')
is_active = BooleanField(default=True)
created_at = DateTimeField(auto_now_add=True)
```
**Relations:** Plan (many-to-one), Users (one-to-many), Sites (one-to-many)
---
### Site
```python
class Site(models.Model):
id = UUIDField(primary_key=True)
name = CharField(max_length=255)
domain = CharField(max_length=255, blank=True)
account = ForeignKey(Account, related_name='sites')
industry = ForeignKey(Industry, null=True)
is_active = BooleanField(default=True)
created_at = DateTimeField(auto_now_add=True)
```
**Relations:** Account (many-to-one), Sectors (one-to-many), Industries (many-to-one)
---
### Sector
```python
class Sector(models.Model):
id = UUIDField(primary_key=True)
name = CharField(max_length=255)
description = TextField(blank=True)
site = ForeignKey(Site, related_name='sectors')
is_active = BooleanField(default=True)
created_at = DateTimeField(auto_now_add=True)
```
**Relations:** Site (many-to-one)
---
### Industry
```python
class Industry(models.Model):
id = UUIDField(primary_key=True)
name = CharField(max_length=255)
description = TextField(blank=True)
```
**Used for:** Default seed keywords, industry-specific prompts
---
## Planner Models (`igny8_core/modules/planner/models.py`)
### Keyword
```python
class Keyword(models.Model):
id = UUIDField(primary_key=True)
keyword = CharField(max_length=255)
site = ForeignKey(Site, related_name='keywords')
sector = ForeignKey(Sector, null=True, related_name='keywords')
cluster = ForeignKey(Cluster, null=True, related_name='keywords')
search_volume = IntegerField(null=True)
difficulty = IntegerField(null=True)
cpc = DecimalField(null=True)
status = CharField(choices=KEYWORD_STATUS) # new, mapped
created_by = ForeignKey(User)
created_at = DateTimeField(auto_now_add=True)
```
**Status Values:**
- `new` - Ready for clustering
- `mapped` - Assigned to a cluster
---
### Cluster
```python
class Cluster(models.Model):
id = UUIDField(primary_key=True)
name = CharField(max_length=255)
description = TextField(blank=True)
site = ForeignKey(Site, related_name='clusters')
sector = ForeignKey(Sector, null=True, related_name='clusters')
created_by = ForeignKey(User)
created_at = DateTimeField(auto_now_add=True)
```
**Relations:** Site, Sector, Keywords (one-to-many), ContentIdeas (one-to-many)
---
### ContentIdea
```python
class ContentIdea(models.Model):
id = UUIDField(primary_key=True)
title = CharField(max_length=255)
description = TextField(blank=True)
site = ForeignKey(Site, related_name='ideas')
sector = ForeignKey(Sector, null=True)
cluster = ForeignKey(Cluster, related_name='ideas')
primary_keyword = ForeignKey(Keyword, related_name='primary_ideas')
secondary_keywords = ManyToManyField(Keyword, related_name='secondary_ideas')
status = CharField(choices=IDEA_STATUS) # pending, approved, used, archived
created_at = DateTimeField(auto_now_add=True)
```
---
## Writer Models (`igny8_core/modules/writer/models.py`)
### Task
```python
class Task(models.Model):
id = UUIDField(primary_key=True)
title = CharField(max_length=255)
brief = TextField(blank=True)
site = ForeignKey(Site, related_name='tasks')
sector = ForeignKey(Sector, null=True)
idea = ForeignKey(ContentIdea, null=True, related_name='tasks')
primary_keyword = CharField(max_length=255)
secondary_keywords = JSONField(default=list)
status = CharField(choices=TASK_STATUS) # queued, completed
assigned_to = ForeignKey(User, null=True)
due_date = DateField(null=True)
created_by = ForeignKey(User)
created_at = DateTimeField(auto_now_add=True)
```
---
### Content
```python
class Content(models.Model):
id = UUIDField(primary_key=True)
title = CharField(max_length=255)
body = TextField() # HTML content
excerpt = TextField(blank=True)
site = ForeignKey(Site, related_name='content')
sector = ForeignKey(Sector, null=True)
task = ForeignKey(Task, related_name='content')
meta_title = CharField(max_length=255, blank=True)
meta_description = TextField(blank=True)
# Workflow status
status = CharField(choices=CONTENT_STATUS) # draft, review, approved, published
# Publishing status (v1.3.2)
site_status = CharField(choices=SITE_STATUS) # not_published, scheduled, publishing, published, failed
scheduled_publish_at = DateTimeField(null=True)
site_status_updated_at = DateTimeField(null=True)
# External site reference
external_id = CharField(max_length=255, blank=True) # WordPress post ID
external_url = URLField(blank=True) # Published URL
word_count = IntegerField(default=0)
created_by = ForeignKey(User)
created_at = DateTimeField(auto_now_add=True)
updated_at = DateTimeField(auto_now=True)
```
**Workflow Status Values (status):**
- `draft` - Initial state after generation
- `review` - Pending human review
- `approved` - Ready for publishing (v1.3.2: NEW status)
- `published` - Published to WordPress
**Publishing Status Values (site_status) - v1.3.2:**
- `not_published` - Not yet scheduled for external site
- `scheduled` - Scheduled for future publishing
- `publishing` - Currently being published
- `published` - Successfully published to external site
- `failed` - Publishing failed
---
### ContentImage
```python
class ContentImage(models.Model):
id = UUIDField(primary_key=True)
content = ForeignKey(Content, related_name='images')
url = URLField()
thumbnail_url = URLField(blank=True)
alt_text = CharField(max_length=255)
caption = TextField(blank=True)
is_featured = BooleanField(default=False)
position = IntegerField(default=0)
# AI generation metadata
prompt = TextField(blank=True)
provider = CharField(max_length=50) # dalle, runware
created_at = DateTimeField(auto_now_add=True)
```
---
## Integration Models (`igny8_core/business/integration/models.py`)
### SiteIntegration
**⚠️ Note:** For WordPress, `Site.wp_api_key` is the **SINGLE source of truth** for API authentication. SiteIntegration is used for sync tracking and future multi-platform support.
```python
class SiteIntegration(models.Model):
id = UUIDField(primary_key=True)
name = CharField(max_length=255)
site = ForeignKey(Site, related_name='integrations')
platform = CharField(max_length=50) # wordpress, shopify (future)
# Configuration
external_site_url = URLField()
config_json = JSONField(default=dict) # Platform-specific settings
credentials_json = JSONField(default=dict) # Reserved for future platforms (NOT for WordPress)
# Sync Tracking
sync_enabled = BooleanField(default=True)
sync_status = CharField(max_length=50) # pending/syncing/completed/error
last_sync_at = DateTimeField(null=True)
sync_error = TextField(null=True)
# Connection
connection_status = CharField(max_length=50) # connected/error
is_active = BooleanField(default=True)
# Cached WordPress structure (from initial sync)
categories = JSONField(default=list)
tags = JSONField(default=list)
authors = JSONField(default=list)
post_types = JSONField(default=list)
structure_updated_at = DateTimeField(null=True)
created_at = DateTimeField(auto_now_add=True)
```
---
### PublishingSettings (NEW v1.3.2)
```python
class PublishingSettings(AccountBaseModel):
"""Site-level publishing configuration. Controls auto-approval, publishing limits, and scheduling."""
site = OneToOneField(Site, related_name='publishing_settings')
# Auto-approval settings
auto_approval_enabled = BooleanField(default=True)
# Auto-publish settings
auto_publish_enabled = BooleanField(default=True)
# Publishing limits
daily_publish_limit = PositiveIntegerField(default=3)
weekly_publish_limit = PositiveIntegerField(default=15)
monthly_publish_limit = PositiveIntegerField(default=50)
# Publishing schedule
publish_days = JSONField(default=['mon', 'tue', 'wed', 'thu', 'fri'])
publish_time_slots = JSONField(default=['09:00', '14:00', '18:00'])
created_at = DateTimeField(auto_now_add=True)
updated_at = DateTimeField(auto_now=True)
```
**Default Values:**
- `auto_approval_enabled`: True (auto-approve after review)
- `auto_publish_enabled`: True (auto-publish approved content)
- `daily_publish_limit`: 3 articles per day
- `weekly_publish_limit`: 15 articles per week
- `monthly_publish_limit`: 50 articles per month
- `publish_days`: Monday through Friday
- `publish_time_slots`: 9:00 AM, 2:00 PM, 6:00 PM
**Usage:**
```python
# Get or create with defaults
settings, created = PublishingSettings.get_or_create_for_site(site)
```
---
## Billing Models (`igny8_core/business/billing/models.py`)
### Plan
```python
class Plan(models.Model):
id = UUIDField(primary_key=True)
name = CharField(max_length=100)
slug = SlugField(unique=True)
idea_credits = IntegerField(default=0)
content_credits = IntegerField(default=0)
image_credits = IntegerField(default=0)
optimization_credits = IntegerField(default=0)
max_sites = IntegerField(default=1)
max_users = IntegerField(default=1)
price_monthly = DecimalField(max_digits=10, decimal_places=2)
price_yearly = DecimalField(max_digits=10, decimal_places=2)
is_active = BooleanField(default=True)
is_internal = BooleanField(default=False)
```
---
### CreditBalance
```python
class CreditBalance(models.Model):
account = ForeignKey(Account, related_name='credit_balances')
site = ForeignKey(Site, null=True, related_name='credit_balances')
idea_credits = IntegerField(default=0)
content_credits = IntegerField(default=0)
image_credits = IntegerField(default=0)
optimization_credits = IntegerField(default=0)
period_start = DateField()
period_end = DateField()
created_at = DateTimeField(auto_now_add=True)
updated_at = DateTimeField(auto_now=True)
```
---
### CreditUsage
```python
class CreditUsage(models.Model):
account = ForeignKey(Account, related_name='credit_usage')
site = ForeignKey(Site, null=True)
user = ForeignKey(User)
credit_type = CharField(max_length=50) # idea, content, image, optimization
amount = IntegerField()
operation = CharField(max_length=100) # generate_content, etc.
related_content_type = ForeignKey(ContentType, null=True)
related_object_id = UUIDField(null=True)
created_at = DateTimeField(auto_now_add=True)
```
---
## System Models (`igny8_core/modules/system/`)
### ModuleEnableSettings
```python
class ModuleEnableSettings(models.Model):
account = OneToOneField(Account, primary_key=True)
planner_enabled = BooleanField(default=True)
writer_enabled = BooleanField(default=True)
linker_enabled = BooleanField(default=False)
optimizer_enabled = BooleanField(default=False)
automation_enabled = BooleanField(default=True)
integration_enabled = BooleanField(default=True)
publisher_enabled = BooleanField(default=True)
```
---
### AIIntegrationSettings
```python
class AIIntegrationSettings(models.Model):
account = ForeignKey(Account, related_name='ai_settings')
# OpenAI
openai_api_key = CharField(max_length=255, blank=True)
openai_model = CharField(max_length=50, default='gpt-4')
# Image generation
image_provider = CharField(max_length=50, default='dalle') # dalle, runware
dalle_api_key = CharField(max_length=255, blank=True)
runware_api_key = CharField(max_length=255, blank=True)
is_validated = BooleanField(default=False)
validated_at = DateTimeField(null=True)
```
---
### PromptTemplate
```python
class PromptTemplate(models.Model):
account = ForeignKey(Account, null=True) # null = system default
prompt_type = CharField(max_length=100) # auto_cluster, generate_ideas, etc.
template = TextField()
variables = JSONField(default=list)
is_active = BooleanField(default=True)
created_at = DateTimeField(auto_now_add=True)
updated_at = DateTimeField(auto_now=True)
```
---
## Publisher Models (`igny8_core/modules/publisher/models.py`)
### PublishingRecord
```python
class PublishingRecord(models.Model):
id = UUIDField(primary_key=True)
content = ForeignKey(Content, related_name='publishing_records')
integration = ForeignKey(SiteIntegration, related_name='publishing_records')
external_id = CharField(max_length=255) # WordPress post ID
external_url = URLField(blank=True)
status = CharField(max_length=50) # pending, published, failed
published_at = DateTimeField(null=True)
created_at = DateTimeField(auto_now_add=True)
```
---
## Automation Models (`igny8_core/modules/automation/models.py`)
### AutomationConfig
```python
class AutomationConfig(models.Model):
site = ForeignKey(Site, related_name='automation_configs')
# Stage limits
clustering_limit = IntegerField(default=10)
ideas_limit = IntegerField(default=10)
content_limit = IntegerField(default=5)
image_limit = IntegerField(default=10)
publish_limit = IntegerField(default=5)
# Timing
delay_between_operations = IntegerField(default=5) # seconds
max_runtime = IntegerField(default=3600) # 1 hour
# Behavior
auto_approve = BooleanField(default=False)
auto_publish = BooleanField(default=False)
stop_on_error = BooleanField(default=True)
is_active = BooleanField(default=True)
```
---
### AutomationRun
```python
class AutomationRun(models.Model):
id = UUIDField(primary_key=True)
site = ForeignKey(Site, related_name='automation_runs')
config = ForeignKey(AutomationConfig)
status = CharField(max_length=50) # pending, running, paused, completed, failed, cancelled
# Progress tracking
current_stage = CharField(max_length=50, blank=True)
items_processed = IntegerField(default=0)
items_total = IntegerField(default=0)
# Timing
started_at = DateTimeField(null=True)
completed_at = DateTimeField(null=True)
# Results
error_message = TextField(blank=True)
started_by = ForeignKey(User)
created_at = DateTimeField(auto_now_add=True)
```
---
## Entity Relationship Overview
```
Account
├── Users (many)
├── Sites (many)
│ ├── Sectors (many)
│ ├── Keywords (many)
│ ├── Clusters (many)
│ ├── ContentIdeas (many)
│ ├── Tasks (many)
│ ├── Content (many)
│ │ └── ContentImages (many)
│ ├── SiteIntegrations (many)
│ │ └── PublishingRecords (many)
│ └── AutomationConfigs (many)
│ └── AutomationRuns (many)
├── Plan (one)
├── CreditBalances (many)
├── CreditUsage (many)
├── ModuleEnableSettings (one)
├── AIIntegrationSettings (many)
└── PromptTemplates (many)
```