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


