"""Campaign config — active posts per campaign. Local dev: reads/writes output/campaign_config.json (settings.campaign_config_file). """ from __future__ import annotations import json from pathlib import Path from typing import Any, cast from marketing_intelligence.core.config import settings from marketing_intelligence.core.models import CampaignPost def _config_file() -> Path: return Path(settings.campaign_config_file) def _load() -> dict[str, list[dict[str, Any]]]: f = _config_file() if not f.exists(): return {} return cast( dict[str, list[dict[str, Any]]], json.loads(f.read_text(encoding="utf-8")) ) def _save(data: dict[str, list[dict[str, Any]]]) -> None: f = _config_file() f.parent.mkdir(parents=True, exist_ok=True) f.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8") def list_active_campaign_ids() -> list[str]: """Return campaign IDs that have at least one active post.""" data = _load() return [ cid for cid, posts in data.items() if any(p.get("is_active", True) for p in posts) ] def list_posts(campaign_id: str) -> list[CampaignPost]: """Return all active posts for a campaign.""" return [ CampaignPost.model_validate(p) for p in _load().get(campaign_id, []) if p.get("is_active", True) ] def add_posts(campaign_id: str, posts: list[CampaignPost]) -> int: """Register discovered posts. Returns count of newly added.""" index = _load() existing = {p["post_id"]: p for p in index.get(campaign_id, [])} added = 0 for post in posts: if post.post_id in existing: existing[post.post_id]["is_active"] = True continue existing[post.post_id] = post.model_dump() added += 1 index[campaign_id] = list(existing.values()) _save(index) return added def deactivate_posts(campaign_id: str) -> int: """Deactivate all posts for a campaign (called at start of a new discovery run).""" index = _load() entries = index.get(campaign_id, []) count = 0 for entry in entries: if entry.get("is_active", True): entry["is_active"] = False count += 1 index[campaign_id] = entries _save(index) return count