Files
igny8/docs/90-REFERENCE/MODELS.md
2026-01-13 09:23:54 +00:00

25 KiB
Raw Blame History

Database Models Reference

Last Verified: January 6, 2026


Data Scoping Overview

Scope Models Base Class Filter By
Global IntegrationProvider, AIModelConfig, SystemAISettings, GlobalAIPrompt, GlobalAuthorProfile, GlobalStrategy, GlobalModuleSettings, Industry, SeedKeyword models.Model None (platform-wide)
Account Account, User, Plan, AccountSettings, 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

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.

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.

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).

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.

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.

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)

# 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

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

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

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

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

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

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

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

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

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

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

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

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

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.

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)

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:

# Get or create with defaults
settings, created = PublishingSettings.get_or_create_for_site(site)

Billing Models (igny8_core/business/billing/models.py)

Plan

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

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

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

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

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

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

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

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

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)