temproary docs uplaoded
This commit is contained in:
851
v2/Live Docs on Server/igny8-app-docs/90-REFERENCE/MODELS.md
Normal file
851
v2/Live Docs on Server/igny8-app-docs/90-REFERENCE/MODELS.md
Normal file
@@ -0,0 +1,851 @@
|
||||
# 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)
|
||||
```
|
||||
Reference in New Issue
Block a user