# Planner Module (Backend) — Code-Sourced Overview (Dec 2025) Scope: `backend/igny8_core/modules/planner` (ViewSets/serializers/urls) and backing models in `backend/igny8_core/business/planning`. ## Endpoints (routed from `modules/planner/urls.py`) - `/api/v1/planner/keywords/` — CRUD + bulk actions, CSV import/export, clustering trigger. - `/api/v1/planner/clusters/` — CRUD with aggregated stats (keywords/volume/difficulty/ideas/content). - `/api/v1/planner/ideas/` — CRUD for content ideas tied to clusters/keywords. ## Models (business/planning/models.py) - **Clusters** (`SiteSectorBaseModel`, soft-delete): `name`, `description`, `keywords_count`, `volume`, `mapped_pages`, `status(new|mapped)`, `disabled`; unique per `site, sector`. Indexes on `name`, `status`, `site, sector`. - **Keywords** (`SiteSectorBaseModel`, soft-delete): FK `seed_keyword` (global), overrides `volume_override`, `difficulty_override`, `attribute_values`; FK `cluster` (same sector); `status(new|mapped)`, `disabled`. Unique per `seed_keyword, site, sector`. Properties expose `keyword`, `volume`, `difficulty`, `intent` from seed keyword. Save enforces industry/sector alignment between site/sector and seed keyword. - **ContentIdeas** (`SiteSectorBaseModel`, soft-delete): `idea_title`, `description`, `target_keywords` (legacy), M2M `keyword_objects`, FK `keyword_cluster` (same sector), `status(new|queued|completed)`, `disabled`, `estimated_word_count`, `content_type` (post/page/product/taxonomy), `content_structure` (article/guide/etc.). Tracks metadata (audience, tone, outlines, brief), CTA fields, review flags (`is_priority`, `is_assigned`, `is_review_required`), language, published URL, audit fields. Indexes on `site, sector, keyword_cluster, status, idea_title`. ## Serializers - **KeywordSerializer**: read-only keyword metrics from seed keyword; requires `seed_keyword_id` on create; optional on update; validates site/sector via the ViewSet; surfaces `cluster_name`, `sector_name`, `volume/difficulty/intent`. - **ClusterSerializer** (`cluster_serializers.py`): computes `keywords_count`, `volume`, `difficulty`, `ideas_count`, `content_count`; bulk prefetch helper to avoid N+1. Annotated volume/difficulty via overrides or seed keyword values. - **ContentIdeasSerializer**: links to clusters and keywords; exposes cluster/sector names; supports site/sector write-only ids; legacy taxonomy getter retained for backward compatibility. ## ViewSets (modules/planner/views.py) - **KeywordViewSet** (`SiteSectorModelViewSet`): - Filtering/search/order: search by seed keyword text; filter by status, cluster, intent, seed_keyword_id; ordering by created_at, volume, difficulty. - Custom filters: difficulty_min/max, volume_min/max using override-first logic. - Bulk actions: `bulk_delete`, `bulk_update` (status), `bulk_add_from_seed` (validates industry/sector alignment, requires site/sector), `import_keywords` CSV (site/sector required, creates Keywords), `export` CSV (ids filter supported). - Clustering: `auto_cluster` calls `ClusteringService.cluster_keywords`; enforces min keyword count (5) and credits; returns async task_id or sync result. - Create requires site_id and sector_id (validated against site/sector and account). - **ClusterViewSet**: - Filters/search/order: search by name; filter by status; ordering on name/created_at/keywords_count/volume/difficulty. - Annotates volume/difficulty using overrides; uses serializer prefetch to reduce N+1. - Export endpoints not present; CRUD via standard actions. - **ContentIdeasViewSet**: - CRUD for ideas; filters on status/cluster; search by title/description/keywords; ordering by created_at/status. - Generates title/brief via `IdeasService.generate_content_idea`; enforces credits (catches `InsufficientCreditsError`). - Bulk assign status not present; focused on single-item generation and listing. ## Services (business/planning/services) - **ClusteringService**: clusters keywords; invoked by `auto_cluster`; credit-aware. - **IdeasService**: generates content ideas (title/brief) from keywords; used in `ContentIdeasViewSet`. ## Permissions, tenancy, and throttling - Permissions: `IsAuthenticatedAndActive` + `IsViewerOrAbove` for all planner endpoints. - Tenancy: `SiteSectorModelViewSet` base ensures account/site/sector scoping; create/import/add-from-seed require explicit site_id + sector_id with consistency checks. - Throttling: `DebugScopedRateThrottle` with scope `planner`. ## CSV Import/Export - Export: `/planner/keywords/export` supports ids filter; outputs CSV with keyword, volume, difficulty, intent, status, cluster. - Import: `/planner/keywords/import_keywords` expects CSV with keyword, volume, difficulty, intent, status; site_id and sector_id required in query params; skips duplicates per site/sector/account. ## Notes / gaps - Two `bulk_update` methods exist in `KeywordViewSet` (duplicate definitions); consolidate to one. - No dedicated bulk idea status update; consider parity with keywords. - Legacy taxonomy references removed; ContentIdeas retains a legacy getter for taxonomy name but model FK is removed.