# Automation & Publishing Scheduling System > **Last Updated:** January 18, 2026 > **System:** IGNY8 Celery Task Scheduling --- ## Overview IGNY8 uses **Celery Beat** to schedule two distinct but related systems: 1. **Automation Pipeline** - Processes content through 7 stages (Keywords → Published) 2. **Publishing Scheduler** - Schedules and publishes approved content to WordPress ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ CELERY BEAT SCHEDULER │ │ (Persistent Schedule Store) │ └─────────────────────────────────────────────────────────────────────────────┘ │ ┌─────────────────────────┼─────────────────────────┐ ▼ ▼ ▼ ┌───────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ Every Hour │ │ Every Hour │ │ Every 5 min │ │ at :05 │ │ at :00 │ │ │ └───────────────┘ └─────────────────┘ └─────────────────┘ │ │ │ ▼ ▼ ▼ ┌───────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ check_ │ │schedule_approved│ │process_scheduled│ │ scheduled_ │ │ _content │ │ _publications │ │ automations │ │ │ │ │ └───────────────┘ └─────────────────┘ └─────────────────┘ │ │ │ ▼ ▼ ▼ ┌───────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ Automation │ │ Content gets │ │ Publishes to │ │ Pipeline │ │ scheduled_ │ │ WordPress via │ │ (7 stages) │ │ publish_at set │ │ API │ └───────────────┘ └─────────────────┘ └─────────────────┘ ``` --- ## 1. Automation Scheduling ### Celery Task Configuration | Task Name | Schedule | Purpose | |-----------|----------|---------| | `automation.check_scheduled_automations` | Every hour at `:05` | Check if any automation configs should run | | `automation.check_test_triggers` | Every 5 minutes | Check for admin test triggers (exits early if none) | ### How It Works ``` ┌──────────────────────────────────────────────────────────────────────────┐ │ AUTOMATION SCHEDULING FLOW │ └──────────────────────────────────────────────────────────────────────────┘ Celery Beat AutomationConfig Result │ │ │ │ (Every hour at :05) │ │ ▼ │ │ ┌─────────────┐ │ │ │ 02:05 check │ ─── Hour ────► scheduled_hour == 2? │ │ 03:05 check │ ─── Hour ────► scheduled_hour == 3? │ │ 04:05 check │ ─── Hour ────► scheduled_hour == 4? │ │ ... │ │ │ └─────────────┘ │ │ ▼ │ ┌────────────────┐ │ │ Match Found? │ │ └────────────────┘ │ │ │ │ Yes No │ │ │ │ ▼ └─► Skip │ ┌────────────────┐ │ │ Check Blocks: │ │ │ • 23hr block │ │ │ • Already │ │ │ running? │ │ └────────────────┘ │ │ │ Pass │ │ │ ▼ │ ┌────────────────┐ │ │ Start Run │ ────────────────────────► Run Started │ • Set last_run │ │ • Queue task │ └────────────────┘ ``` ### Hourly Matching Logic The scheduler runs at `:05` of every hour and checks if `scheduled_hour == current_hour`: | Celery Runs At | Checks Hour | Example Config | |----------------|-------------|----------------| | `02:05` | `2` | `scheduled_time = 02:00` → Matches | | `03:05` | `3` | `scheduled_time = 03:00` → Matches | | `14:05` | `14` | `scheduled_time = 14:00` (2 PM) → Matches | **Note:** Users select hour only (1-12 with AM/PM in UI), stored as `HH:00` format. The `:05` offset ensures the check happens after the configured hour begins. ### Test Mode (Admin Feature) Admins can trigger automations without waiting for the hourly schedule: ``` ┌──────────────────────────────────────────────────────────────────────────┐ │ TEST TRIGGER FLOW │ └──────────────────────────────────────────────────────────────────────────┘ Admin Action Celery (Every Minute) Result │ │ │ ▼ │ │ ┌─────────────────┐ │ │ │ Enable test mode│ │ │ │ Set trigger time│ │ │ │ (or "now") │ │ │ └─────────────────┘ │ │ │ │ │ └───────────────────────────────────▼ │ ┌─────────────────┐ │ │check_test_ │ │ │triggers task │ │ └─────────────────┘ │ │ │ ▼ │ ┌─────────────────┐ │ │test_trigger_at │ │ │<= now? │ │ └─────────────────┘ │ │ │ │ Yes No │ │ └─► Wait │ ▼ │ ┌─────────────────┐ │ │Start test run │──────────────► Run Started │trigger_type= │ (type='test') │'test' │ │Clear trigger_at │ └─────────────────┘ ``` **Test mode fields:** - `test_mode_enabled` - Enable test functionality for this config - `test_trigger_at` - When to trigger (set to `now` for immediate) **Admin actions:** - 🧪 **Trigger test run** - Sets both fields and triggers at next minute - 🧹 **Clear test mode** - Disables test mode and clears trigger ### Frequency Rules | Frequency | When It Runs | Additional Condition | |-----------|--------------|----------------------| | `daily` | Every day | `scheduled_hour == current_hour` | | `weekly` | Mondays only | `weekday() == 0` + hour match | | `monthly` | 1st of month only | `day == 1` + hour match | ### Duplicate Prevention (23-Hour Block) After a successful run, the config won't run again for **23 hours**: ```python if config.last_run_at: time_since_last_run = now - config.last_run_at if time_since_last_run < timedelta(hours=23): # SKIP - already ran recently ``` ### Schedule Change Behavior When `scheduled_time`, `frequency`, or `is_enabled` changes: - `last_run_at` is reset to `None` - `next_run_at` is recalculated - Automation becomes eligible immediately at the next hourly check **Example:** ``` Current time: 12:30 You change scheduled_time from 02:00 → 12:00 (12 PM) Hour 12 was already checked at 12:05 → Automation will run tomorrow at 12:05 (daily) or next valid day (weekly/monthly) ``` **Pro tip:** Use test mode to trigger immediately after schedule changes. --- ## 2. Publishing Scheduling ### Celery Task Configuration | Task Name | Schedule | Purpose | |-----------|----------|---------| | `publishing.schedule_approved_content` | Every hour at `:00` | Schedule approved content for future publishing | | `publishing.process_scheduled_publications` | Every 5 min | Publish content where `scheduled_publish_at <= now` | ### Publishing Flow ``` ┌──────────────────────────────────────────────────────────────────────────┐ │ PUBLISHING SCHEDULING FLOW │ └──────────────────────────────────────────────────────────────────────────┘ EVERY HOUR (:00) EVERY 5 MINUTES │ │ ▼ ▼ ┌────────────────────────┐ ┌────────────────────────┐ │schedule_approved_content│ │process_scheduled_ │ │ │ │publications │ └────────────────────────┘ └────────────────────────┘ │ │ ▼ │ ┌────────────────────────┐ │ │ For each Site with │ │ │ auto_publish_enabled: │ │ │ │ │ │ 1. Find approved │ │ │ content │ │ │ 2. Calculate slots │ │ │ based on settings │ │ │ 3. Set scheduled_ │ │ │ publish_at │ │ │ 4. Set site_status= │ │ │ 'scheduled' │ │ └────────────────────────┘ │ │ │ ▼ ▼ ┌───────────────────────────────────────────────┐ │ DATABASE │ │ ┌─────────────────────────────────────────┐ │ │ │ Content Table │ │ │ │ • status = 'approved' │ │ │ │ • site_status = 'scheduled' │ │ │ │ • scheduled_publish_at = │ │ │ └─────────────────────────────────────────┘ │ └───────────────────────────────────────────────┘ │ ▼ ┌────────────────────────┐ │ process_scheduled_ │ │ publications │ │ │ │ WHERE: │ │ • site_status = │ │ 'scheduled' │ │ • scheduled_publish_at │ │ <= NOW │ └────────────────────────┘ │ ▼ ┌────────────────────────┐ │ Publish to WordPress │ │ via PublisherService │ │ │ │ On Success: │ │ • site_status = │ │ 'published' │ │ • Set wp_post_id │ └────────────────────────┘ ``` ### Scheduling Modes | Mode | Behavior | |------|----------| | `immediate` | Content scheduled for `now`, picked up within 5 minutes | | `time_slots` | Content scheduled at specific times (e.g., 9am, 2pm, 6pm) | | `stagger` | Content spread evenly across publish hours | ### Publishing Limits | Limit | Description | |-------|-------------| | `daily_publish_limit` | Max posts per day | | `weekly_publish_limit` | Max posts per week | | `monthly_publish_limit` | Max posts per month | | `queue_limit` | Max items to schedule at once (default: 100) | --- ## 3. System Relationship ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ COMPLETE CONTENT LIFECYCLE │ └─────────────────────────────────────────────────────────────────────────────┘ AUTOMATION PIPELINE PUBLISHING PIPELINE (Every 15 min check) (Hourly + Every 5 min) │ │ ▼ │ ┌─────────────────┐ │ │ Stage 1-6 │ │ │ Keywords → │ │ │ Content Created │ │ └─────────────────┘ │ │ │ ▼ │ ┌─────────────────┐ │ │ Stage 7 │ │ │ Auto-Approval │ status='approved' │ │ (if enabled) │ ─────────────────────────────────► │ └─────────────────┘ │ ▼ ┌─────────────────────┐ │schedule_approved_ │ │content (hourly) │ │ │ │ Sets: │ │ • scheduled_publish │ │ _at │ │ • site_status = │ │ 'scheduled' │ └─────────────────────┘ │ ▼ ┌─────────────────────┐ │process_scheduled_ │ │publications (5 min) │ │ │ │ Publishes to WP │ │ • site_status = │ │ 'published' │ └─────────────────────┘ ``` --- ## 4. Celery Beat Schedule Summary | Task | Schedule | Crontab | |------|----------|---------| | `automation.check_scheduled_automations` | Every 15 min | `minute='0,15,30,45'` | | `publishing.schedule_approved_content` | Hourly | `minute=0` | | `publishing.process_scheduled_publications` | Every 5 min | `minute='*/5'` | --- ## 5. Key Configuration Tables ### AutomationConfig | Field | Type | Purpose | |-------|------|---------| | `is_enabled` | Boolean | Enable/disable scheduling | | `frequency` | Choice | `daily`, `weekly`, `monthly` | | `scheduled_time` | Time | When to run (e.g., `02:00`) | | `last_run_at` | DateTime | Last successful run (23hr block) | | `next_run_at` | DateTime | Calculated next run time | ### PublishingSettings | Field | Type | Purpose | |-------|------|---------| | `auto_publish_enabled` | Boolean | Enable auto-scheduling | | `scheduling_mode` | Choice | `immediate`, `time_slots`, `stagger` | | `publish_days` | Array | `['mon', 'tue', ...]` | | `publish_time_slots` | Array | `['09:00', '14:00', '18:00']` | | `daily_publish_limit` | Integer | Max posts/day | --- ## 6. Troubleshooting ### Automation Didn't Run | Check | Solution | |-------|----------| | `is_enabled = False` | Enable the automation | | Time not in current window | Wait for next window or change time | | `last_run_at` within 23hrs | Wait for 23hr block to expire | | Another run in progress | Wait for current run to complete | | Config updated after window | Schedule moved to next occurrence | ### Content Not Publishing | Check | Solution | |-------|----------| | `auto_publish_enabled = False` | Enable in PublishingSettings | | Content `status != 'approved'` | Approve content first | | No `wp_api_key` on Site | Configure WordPress integration | | Daily/weekly limit reached | Wait for limit reset or increase | --- ## 7. Logs Monitor these logs for scheduling issues: ```bash # Automation scheduling docker logs igny8_celery_worker 2>&1 | grep "AutomationTask" # Publishing scheduling docker logs igny8_celery_worker 2>&1 | grep "schedule_approved_content\|process_scheduled" ```