144 lines
7.4 KiB
Python
144 lines
7.4 KiB
Python
"""
|
|
Automation Models
|
|
Tracks automation runs and configuration
|
|
"""
|
|
from django.db import models
|
|
from django.utils import timezone
|
|
from igny8_core.auth.models import Account, Site
|
|
|
|
|
|
class AutomationConfig(models.Model):
|
|
"""Per-site automation configuration"""
|
|
|
|
FREQUENCY_CHOICES = [
|
|
('daily', 'Daily'),
|
|
('weekly', 'Weekly'),
|
|
('monthly', 'Monthly'),
|
|
]
|
|
|
|
account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name='automation_configs')
|
|
site = models.OneToOneField(Site, on_delete=models.CASCADE, related_name='automation_config')
|
|
|
|
is_enabled = models.BooleanField(default=False, help_text="Whether scheduled automation is active")
|
|
frequency = models.CharField(max_length=20, choices=FREQUENCY_CHOICES, default='daily')
|
|
scheduled_time = models.TimeField(default='02:00', help_text="Time to run (e.g., 02:00)")
|
|
|
|
# Stage processing toggles
|
|
stage_1_enabled = models.BooleanField(default=True, help_text="Process Stage 1: Keywords → Clusters")
|
|
stage_2_enabled = models.BooleanField(default=True, help_text="Process Stage 2: Clusters → Ideas")
|
|
stage_3_enabled = models.BooleanField(default=True, help_text="Process Stage 3: Ideas → Tasks")
|
|
stage_4_enabled = models.BooleanField(default=True, help_text="Process Stage 4: Tasks → Content")
|
|
stage_5_enabled = models.BooleanField(default=True, help_text="Process Stage 5: Content → Image Prompts")
|
|
stage_6_enabled = models.BooleanField(default=True, help_text="Process Stage 6: Image Prompts → Images")
|
|
stage_7_enabled = models.BooleanField(default=True, help_text="Process Stage 7: Review → Published")
|
|
|
|
# Batch sizes per stage
|
|
stage_1_batch_size = models.IntegerField(default=50, help_text="Keywords per batch")
|
|
stage_2_batch_size = models.IntegerField(default=1, help_text="Clusters at a time")
|
|
stage_3_batch_size = models.IntegerField(default=20, help_text="Ideas per batch")
|
|
stage_4_batch_size = models.IntegerField(default=1, help_text="Tasks - sequential")
|
|
stage_5_batch_size = models.IntegerField(default=1, help_text="Content at a time")
|
|
stage_6_batch_size = models.IntegerField(default=1, help_text="Images - sequential")
|
|
|
|
# Delay configuration (in seconds)
|
|
within_stage_delay = models.IntegerField(default=3, help_text="Delay between batches within a stage (seconds)")
|
|
between_stage_delay = models.IntegerField(default=5, help_text="Delay between stage transitions (seconds)")
|
|
|
|
# Per-run item limits (0 = unlimited, processes all available)
|
|
# These prevent runaway automation and control resource usage
|
|
max_keywords_per_run = models.IntegerField(default=0, help_text="Max keywords to process in stage 1 (0=unlimited)")
|
|
max_clusters_per_run = models.IntegerField(default=0, help_text="Max clusters to process in stage 2 (0=unlimited)")
|
|
max_ideas_per_run = models.IntegerField(default=0, help_text="Max ideas to process in stage 3 (0=unlimited)")
|
|
max_tasks_per_run = models.IntegerField(default=0, help_text="Max tasks to process in stage 4 (0=unlimited)")
|
|
max_content_per_run = models.IntegerField(default=0, help_text="Max content pieces for image prompts in stage 5 (0=unlimited)")
|
|
max_images_per_run = models.IntegerField(default=0, help_text="Max images to generate in stage 6 (0=unlimited)")
|
|
max_approvals_per_run = models.IntegerField(default=0, help_text="Max content pieces to auto-approve in stage 7 (0=unlimited)")
|
|
|
|
# Credit budget limit per run (0 = use site's full credit balance)
|
|
max_credits_per_run = models.IntegerField(default=0, help_text="Max credits to use per run (0=unlimited)")
|
|
|
|
last_run_at = models.DateTimeField(null=True, blank=True)
|
|
next_run_at = models.DateTimeField(null=True, blank=True, help_text="Calculated based on frequency")
|
|
|
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
updated_at = models.DateTimeField(auto_now=True)
|
|
|
|
class Meta:
|
|
db_table = 'igny8_automation_configs'
|
|
verbose_name = 'Automation Config'
|
|
verbose_name_plural = 'Automation Configs'
|
|
indexes = [
|
|
models.Index(fields=['is_enabled', 'next_run_at']),
|
|
models.Index(fields=['account', 'site']),
|
|
]
|
|
|
|
def __str__(self):
|
|
return f"Automation Config: {self.site.domain} ({self.frequency})"
|
|
|
|
|
|
class AutomationRun(models.Model):
|
|
"""Tracks each automation execution"""
|
|
|
|
TRIGGER_TYPE_CHOICES = [
|
|
('manual', 'Manual'),
|
|
('scheduled', 'Scheduled'),
|
|
]
|
|
|
|
STATUS_CHOICES = [
|
|
('running', 'Running'),
|
|
('paused', 'Paused'),
|
|
('cancelled', 'Cancelled'),
|
|
('completed', 'Completed'),
|
|
('failed', 'Failed'),
|
|
]
|
|
|
|
run_id = models.CharField(max_length=100, unique=True, db_index=True, help_text="Format: run_20251203_140523_manual")
|
|
account = models.ForeignKey(Account, on_delete=models.CASCADE, related_name='automation_runs')
|
|
site = models.ForeignKey(Site, on_delete=models.CASCADE, related_name='automation_runs')
|
|
|
|
trigger_type = models.CharField(max_length=20, choices=TRIGGER_TYPE_CHOICES)
|
|
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='running', db_index=True)
|
|
current_stage = models.IntegerField(default=1, help_text="Current stage number (1-7)")
|
|
|
|
# Pause/Resume tracking
|
|
paused_at = models.DateTimeField(null=True, blank=True, help_text="When automation was paused")
|
|
resumed_at = models.DateTimeField(null=True, blank=True, help_text="When automation was last resumed")
|
|
cancelled_at = models.DateTimeField(null=True, blank=True, help_text="When automation was cancelled")
|
|
|
|
started_at = models.DateTimeField(auto_now_add=True, db_index=True)
|
|
completed_at = models.DateTimeField(null=True, blank=True)
|
|
|
|
total_credits_used = models.IntegerField(default=0)
|
|
|
|
# Initial queue snapshot - captured at run start for accurate progress tracking
|
|
initial_snapshot = models.JSONField(
|
|
default=dict,
|
|
blank=True,
|
|
help_text="Snapshot of initial queue sizes: {stage_1_initial, stage_2_initial, ..., total_initial_items}"
|
|
)
|
|
|
|
# JSON results per stage
|
|
stage_1_result = models.JSONField(null=True, blank=True, help_text="{keywords_processed, clusters_created, batches}")
|
|
stage_2_result = models.JSONField(null=True, blank=True, help_text="{clusters_processed, ideas_created}")
|
|
stage_3_result = models.JSONField(null=True, blank=True, help_text="{ideas_processed, tasks_created}")
|
|
stage_4_result = models.JSONField(null=True, blank=True, help_text="{tasks_processed, content_created, total_words}")
|
|
stage_5_result = models.JSONField(null=True, blank=True, help_text="{content_processed, prompts_created}")
|
|
stage_6_result = models.JSONField(null=True, blank=True, help_text="{images_processed, images_generated}")
|
|
stage_7_result = models.JSONField(null=True, blank=True, help_text="{ready_for_review}")
|
|
|
|
error_message = models.TextField(null=True, blank=True)
|
|
|
|
class Meta:
|
|
db_table = 'igny8_automation_runs'
|
|
verbose_name = 'Automation Run'
|
|
verbose_name_plural = 'Automation Runs'
|
|
ordering = ['-started_at']
|
|
indexes = [
|
|
models.Index(fields=['site', '-started_at']),
|
|
models.Index(fields=['status', '-started_at']),
|
|
models.Index(fields=['account', '-started_at']),
|
|
]
|
|
|
|
def __str__(self):
|
|
return f"{self.run_id} - {self.site.domain} ({self.status})"
|