docs & ux improvmeents
This commit is contained in:
517
docs/90-REFERENCE/MODELS.md
Normal file
517
docs/90-REFERENCE/MODELS.md
Normal file
@@ -0,0 +1,517 @@
|
||||
# Database Models Reference
|
||||
|
||||
**Last Verified:** December 25, 2025
|
||||
|
||||
---
|
||||
|
||||
## 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)
|
||||
|
||||
status = CharField(choices=CONTENT_STATUS) # draft, review, approved, published
|
||||
|
||||
word_count = IntegerField(default=0)
|
||||
|
||||
created_by = ForeignKey(User)
|
||||
created_at = DateTimeField(auto_now_add=True)
|
||||
updated_at = DateTimeField(auto_now=True)
|
||||
```
|
||||
|
||||
**Status Values:**
|
||||
- `draft` - Initial state after generation
|
||||
- `review` - Pending human review
|
||||
- `approved` - Ready for publishing
|
||||
- `published` - Published to WordPress
|
||||
|
||||
---
|
||||
|
||||
### 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/modules/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')
|
||||
integration_type = 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)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Billing Models (`igny8_core/modules/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