637 lines
18 KiB
Markdown
637 lines
18 KiB
Markdown
# Database Models Reference
|
|
|
|
**Last Verified:** January 3, 2026
|
|
|
|
---
|
|
|
|
## Data Scoping Overview
|
|
|
|
| Scope | Models | Base Class | Filter By |
|
|
|-------|--------|------------|-----------|
|
|
| **Global** | `GlobalIntegrationSettings`, `GlobalAIPrompt`, `GlobalAuthorProfile`, `GlobalStrategy`, `GlobalModuleSettings`, `Industry`, `SeedKeyword` | `models.Model` | None (platform-wide) |
|
|
| **Account** | `Account`, `User`, `Plan`, `IntegrationSettings`, `ModuleEnableSettings`, `AISettings`, `AIPrompt`, `AuthorProfile`, `CreditBalance` | `AccountBaseModel` | `account` |
|
|
| **Site** | `Site`, `PublishingSettings`, `AutomationConfig`, `SiteIntegration` | `AccountBaseModel` | `account`, `site` |
|
|
| **Site+Sector** | `Keywords`, `Clusters`, `ContentIdeas`, `Tasks`, `Content`, `Images` | `SiteSectorBaseModel` | `site`, `sector` |
|
|
|
|
---
|
|
|
|
## Global Models (`igny8_core/modules/system/global_settings_models.py`)
|
|
|
|
**Purpose:** Platform-wide defaults and API keys. Admin-only. NOT account-scoped.
|
|
|
|
### GlobalIntegrationSettings (Singleton)
|
|
|
|
```python
|
|
class GlobalIntegrationSettings(models.Model):
|
|
# OpenAI (used by ALL accounts)
|
|
openai_api_key = CharField(max_length=500)
|
|
openai_model = CharField(default='gpt-4o-mini')
|
|
openai_temperature = FloatField(default=0.7)
|
|
openai_max_tokens = IntegerField(default=8192)
|
|
|
|
# DALL-E / Image Generation
|
|
dalle_api_key = CharField(max_length=500)
|
|
dalle_model = CharField(default='dall-e-3')
|
|
dalle_size = CharField(default='1024x1024')
|
|
|
|
# Runware
|
|
runware_api_key = CharField(max_length=500)
|
|
```
|
|
|
|
**Critical:** Singleton (pk=1). API keys here are used by ALL accounts.
|
|
|
|
### 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) # pending, clustered, used, archived
|
|
|
|
created_by = ForeignKey(User)
|
|
created_at = DateTimeField(auto_now_add=True)
|
|
```
|
|
|
|
**Status Values:**
|
|
- `pending` - New, awaiting clustering
|
|
- `clustered` - Assigned to a cluster
|
|
- `used` - Used in content idea
|
|
- `archived` - No longer active
|
|
|
|
---
|
|
|
|
### 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) # pending, in_progress, completed, cancelled
|
|
priority = CharField(choices=PRIORITY) # low, medium, high
|
|
|
|
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
|
|
|
|
```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
|
|
|
|
# Credentials (encrypted)
|
|
url = URLField()
|
|
username = CharField(max_length=255)
|
|
api_key = CharField(max_length=255) # Application password
|
|
|
|
# Cached structure
|
|
categories = JSONField(default=list)
|
|
tags = JSONField(default=list)
|
|
authors = JSONField(default=list)
|
|
post_types = JSONField(default=list)
|
|
|
|
# Status
|
|
is_active = BooleanField(default=True)
|
|
last_sync_at = DateTimeField(null=True)
|
|
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)
|
|
```
|