pubslihign and scheduling plan updated

This commit is contained in:
IGNY8 VPS (Salman)
2026-01-16 13:28:24 +00:00
parent 22f7bdaa23
commit 79bdf52856
4 changed files with 1902 additions and 85 deletions

View File

@@ -1,6 +1,6 @@
# Scheduled Content Publishing Workflow
**Last Updated:** January 12, 2026
**Last Updated:** January 16, 2026
**Module:** Publishing / Automation
---
@@ -9,6 +9,12 @@
IGNY8 provides automated content publishing to WordPress sites. Content goes through a scheduling process before being published at the designated time.
**Current System (v2.0):**
- WordPress credentials stored directly on `Site` model (`wp_api_key`, `domain`)
- No `SiteIntegration` model required
- Publishing via `PublisherService` (not legacy Celery tasks)
- API endpoint: `POST /api/v1/publisher/publish`
---
## Content Lifecycle for Publishing
@@ -115,34 +121,38 @@ Content has **TWO separate status fields**:
- `site_status='scheduled'`
- `scheduled_publish_at <= now`
2. For each content item:
- Validates WordPress configuration exists on Site (`wp_api_key`, `domain`)
- Updates `site_status='publishing'`
- Gets the site's WordPress integration
- Queues `publish_content_to_wordpress` Celery task
- Calls `PublisherService.publish_content()` directly (synchronous)
- Updates `site_status='published'` on success or `'failed'` on error
3. Logs results and any errors
**Current Implementation (v2.0):**
- Uses `PublisherService` (current system)
- Gets config from `Site.wp_api_key` and `Site.domain`
- No `SiteIntegration` required
- Synchronous publishing (not queued to Celery)
---
### 3. `publish_content_to_wordpress`
### 3. `PublisherService.publish_content`
**Type:** On-demand Celery task (queued by `process_scheduled_publications`)
**Task Name:** `publishing.publish_content_to_wordpress`
**File:** `backend/igny8_core/tasks/wordpress_publishing.py`
**Type:** Service method (called by scheduler)
**File:** `backend/igny8_core/business/publishing/services/publisher_service.py`
#### What It Does:
1. **Load Content & Integration** - Gets content and WordPress credentials
2. **Check Already Published** - Skips if `external_id` exists
3. **Generate Excerpt** - Creates excerpt from HTML content
4. **Get Taxonomy Terms** - Loads categories and tags from `ContentTaxonomy`
5. **Get Images** - Loads featured image and gallery images
6. **Build API Payload** - Constructs WordPress REST API payload
7. **Call WordPress API** - POSTs to WordPress via IGNY8 Bridge plugin
8. **Update Content** - Sets `external_id`, `external_url`, `site_status='published'`
9. **Log Sync Event** - Records in `SyncEvent` model
1. **Load Content** - Gets content by ID
2. **Get WordPress Config** - Reads from `Site.wp_api_key` and `Site.domain`
3. **Call Adapter** - Uses `WordPressAdapter` to handle API communication
4. **WordPress API Call** - POSTs to `{domain}/wp-json/igny8/v1/publish`
5. **Update Content** - Sets `external_id`, `external_url`, `site_status='published'`
6. **Create Record** - Logs in `PublishingRecord` model
#### WordPress Connection:
- Uses the IGNY8 WordPress Bridge plugin installed on the site
- API endpoint: `{site_url}/wp-json/igny8-bridge/v1/publish`
- Authentication: API key stored in `Site.wp_api_key`
- API endpoint: `{site.domain}/wp-json/igny8/v1/publish`
- Authentication: API key from `Site.wp_api_key`
- **No SiteIntegration model needed**
---
@@ -201,15 +211,39 @@ app.conf.beat_schedule = {
## Manual Publishing
Content can also be published immediately via:
Content can be published immediately or rescheduled via API:
### API Endpoint
### Publish Now
```
POST /api/v1/content/{content_id}/publish/
POST /api/v1/publisher/publish
{
"content_id": 123,
"destinations": ["wordpress"]
}
```
### Admin Action
In Django Admin, select content and use "Publish to WordPress" action.
### Schedule for Later
```
POST /api/v1/writer/content/{id}/schedule/
{
"scheduled_at": "2026-01-20T14:00:00Z"
}
```
### Reschedule Failed Content
```
POST /api/v1/writer/content/{id}/reschedule/
{
"scheduled_at": "2026-01-20T15:00:00Z"
}
```
**Note:** Reschedule works from any `site_status` (failed, published, scheduled, etc.)
### Unschedule
```
POST /api/v1/writer/content/{id}/unschedule/
```
---
@@ -267,16 +301,40 @@ process_scheduled_publications()
| Error | Cause | Solution |
|-------|-------|----------|
| No active WordPress integration | Site doesn't have WordPress connected | Configure integration in Site settings |
| No WordPress API key | Site.wp_api_key is empty | Configure API key in Site settings |
| No domain configured | Site.domain is empty | Set domain in Site settings |
| API key invalid/expired | WordPress API key issue | Regenerate API key in WordPress plugin |
| Connection timeout | WordPress site unreachable | Check site availability |
| Plugin not active | IGNY8 Bridge plugin disabled | Enable plugin in WordPress |
| Content already published | Duplicate publish attempt | Check `external_id` field |
| Content already published | Duplicate publish attempt | Use reschedule to republish |
### Retry Policy
- `publish_content_to_wordpress` has `max_retries=3`
- Automatic retry on transient failures
- Failed content marked with `site_status='failed'`
- Use the `reschedule` action to retry publishing
- Can reschedule from any status (failed, published, etc.)
### Rescheduling Failed Content
```python
# Via API
POST /api/v1/writer/content/{id}/reschedule/
{
"scheduled_at": "2026-01-20T15:00:00Z"
}
# Via Django shell
from igny8_core.business.content.models import Content
from django.utils import timezone
from datetime import timedelta
failed_content = Content.objects.filter(site_status='failed')
for content in failed_content:
# Reschedule for 1 hour from now
content.site_status = 'scheduled'
content.scheduled_publish_at = timezone.now() + timedelta(hours=1)
content.site_status_updated_at = timezone.now()
content.save(update_fields=['site_status', 'scheduled_publish_at', 'site_status_updated_at'])
```
---
@@ -294,19 +352,37 @@ process_scheduled_publications()
1. Check Celery Beat is running: `docker compose logs igny8_celery_beat`
2. Check Celery Worker is running: `docker compose logs igny8_celery_worker`
3. Look for errors in worker logs
4. Verify WordPress integration is active
5. Test WordPress API connectivity
4. Verify Site has `wp_api_key` configured
5. Verify Site has `domain` configured
6. Test WordPress API connectivity
### Resetting Failed Content
```python
# Reset failed content to try again
from igny8_core.business.content.models import Content
Use the reschedule API endpoint:
Content.objects.filter(site_status='failed').update(
site_status='not_published',
scheduled_publish_at=None
)
```bash
# Reschedule single content
curl -X POST https://api.igny8.com/api/v1/writer/content/344/reschedule/ \
-H "Authorization: Api-Key YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"scheduled_at": "2026-01-20T15:00:00Z"}'
```
Or via Django shell:
```python
# Reset all failed content to reschedule in 1 hour
from igny8_core.business.content.models import Content
from django.utils import timezone
from datetime import timedelta
failed_content = Content.objects.filter(site_status='failed')
for content in failed_content:
content.site_status = 'scheduled'
content.scheduled_publish_at = timezone.now() + timedelta(hours=1)
content.site_status_updated_at = timezone.now()
content.save(update_fields=['site_status', 'scheduled_publish_at', 'site_status_updated_at'])
print(f"Rescheduled content {content.id}")
```
---
@@ -318,29 +394,12 @@ Content.objects.filter(site_status='failed').update(
│ IGNY8 Backend │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌──────────────────┐
│ │ Celery Beat │ │ Celery Worker
│ │ │ │ │ │
│ │ Sends tasks at │───▶│ Executes tasks │ │
│ │ scheduled times │ │ │ │
│ └──────────────────┘ └────────┬─────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Publishing Tasks │ │
│ ┌───────Validate Site.wp_api_key & domain
│ │ - Call PublisherService.publish_content()
│ │ │ │
│ │ 1. schedule_approved_content (hourly) │ │
│ │ - Find approved content │ │
│ │ - Calculate publish slots │ │
│ │ - Set scheduled_publish_at │ │
│ │ │ │
│ │ 2. process_scheduled_publications (every 5 min) │ │
│ │ - Find due content │ │
│ │ - Queue publish_content_to_wordpress │ │
│ │ │ │
│ │ 3. publish_content_to_wordpress │ │
│ │ - Build API payload │ │
│ │ - Call WordPress REST API │ │
│ │ 3. PublisherService │ │
│ │ - Get config from Site model │ │
│ │ - Call WordPressAdapter │ │
│ │ - Update content status │ │
│ └────────────────────────────────────────────────────────┘ │
│ │ │
@@ -353,6 +412,68 @@ Content.objects.filter(site_status='failed').update(
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ IGNY8 Bridge Plugin │ │
│ │ │ │
│ │ /wp-json/igny8/v1/publish │ │
│ │ - Receives content payload │ │
│ │ - Creates/updates WordPress post │ │
│ │ - Handles images, categories, tags │ │
│ │ - Returns post ID and URL │ │
│ └─────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────┘
```
---
## API Reference
### Schedule Content
```http
POST /api/v1/writer/content/{id}/schedule/
Authorization: Api-Key YOUR_KEY
Content-Type: application/json
{
"scheduled_at": "2026-01-20T14:00:00Z"
}
```
### Reschedule Content (Failed or Any Status)
```http
POST /api/v1/writer/content/{id}/reschedule/
Authorization: Api-Key YOUR_KEY
Content-Type: application/json
{
"scheduled_at": "2026-01-20T15:00:00Z"
}
```
### Unschedule Content
```http
POST /api/v1/writer/content/{id}/unschedule/
Authorization: Api-Key YOUR_KEY
```
### Publish Immediately
```http
POST /api/v1/publisher/publish
Authorization: Api-Key YOUR_KEY
Content-Type: application/json
{
"content_id": 123,
"destinations": ["wordpress"]
}
HTTPS
WordPress Site
IGNY8 Bridge Plugin
/wp-json/igny8-bridge/v1/publish
- Receives content payload
- Creates/updates WordPress post

File diff suppressed because it is too large Load Diff