import sys import os import csv import requests import json import logging from collections import defaultdict # Setup Logger logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s') logger = logging.getLogger("NotionPusher") # Config CSV_PATH = "./docs/miller_heiman_augmented.csv" NOTION_TOKEN_FILE = "/app/notion_token.txt" INDUSTRIES_DB_ID = "2ec88f4285448014ab38ea664b4c2b81" def load_notion_token(): try: with open(NOTION_TOKEN_FILE, "r") as f: return f.read().strip() except: logger.error("Token file not found.") sys.exit(1) def add_strategy_property(token): """Try to add the 'Strategy Briefing' property to the database schema.""" url = f"https://api.notion.com/v1/databases/{INDUSTRIES_DB_ID}" headers = { "Authorization": f"Bearer {token}", "Notion-Version": "2022-06-28", "Content-Type": "application/json" } # First, get current schema to check if it exists resp = requests.get(url, headers=headers) if resp.status_code != 200: logger.error(f"Failed to fetch DB schema: {resp.text}") return False current_properties = resp.json().get("properties", {}) if "Strategy Briefing" in current_properties: logger.info("Property 'Strategy Briefing' already exists.") return True logger.info("Property 'Strategy Briefing' missing. Attempting to create...") payload = { "properties": { "Strategy Briefing": { "rich_text": {} } } } update_resp = requests.patch(url, headers=headers, json=payload) if update_resp.status_code == 200: logger.info("Successfully added 'Strategy Briefing' property.") return True else: logger.error(f"Failed to add property: {update_resp.text}") return False def get_pages_map(token): """Returns a map of Vertical Name -> Page ID""" url = f"https://api.notion.com/v1/databases/{INDUSTRIES_DB_ID}/query" headers = { "Authorization": f"Bearer {token}", "Notion-Version": "2022-06-28", "Content-Type": "application/json" } mapping = {} has_more = True next_cursor = None while has_more: payload = {} if next_cursor: payload["start_cursor"] = next_cursor resp = requests.post(url, headers=headers, json=payload) if resp.status_code != 200: break data = resp.json() for page in data.get("results", []): props = page.get("properties", {}) name_parts = props.get("Vertical", {}).get("title", []) if name_parts: name = "".join([t.get("plain_text", "") for t in name_parts]) mapping[name] = page["id"] has_more = data.get("has_more", False) next_cursor = data.get("next_cursor") return mapping def update_page(token, page_id, pains, gains, strategy): url = f"https://api.notion.com/v1/pages/{page_id}" headers = { "Authorization": f"Bearer {token}", "Notion-Version": "2022-06-28", "Content-Type": "application/json" } # Construct Payload props = {} # Only update if content exists (safety) if pains: props["Pains"] = {"rich_text": [{"text": {"content": pains[:2000]}}]} # Limit to 2000 chars to be safe if gains: props["Gains"] = {"rich_text": [{"text": {"content": gains[:2000]}}]} if strategy: props["Strategy Briefing"] = {"rich_text": [{"text": {"content": strategy[:2000]}}]} if not props: return payload = {"properties": props} resp = requests.patch(url, headers=headers, json=payload) if resp.status_code != 200: logger.error(f"Failed to update page {page_id}: {resp.text}") else: logger.info(f"Updated page {page_id}") def main(): token = load_notion_token() # 1. Ensure Schema if not add_strategy_property(token): logger.warning("Could not add 'Strategy Briefing' column. Please add it manually in Notion as a 'Text' property.") # We continue, maybe it failed because of permissions but column exists? # Actually if it failed, the update later will fail too if key is missing. # But let's try. # 2. Map Notion Verticals logger.info("Mapping existing Notion pages...") notion_map = get_pages_map(token) logger.info(f"Found {len(notion_map)} verticals in Notion.") # 3. Read CSV logger.info(f"Reading CSV: {CSV_PATH}") csv_data = {} with open(CSV_PATH, "r", encoding="utf-8-sig") as f: reader = csv.DictReader(f, delimiter=";") for row in reader: vertical = row.get("Vertical") if not vertical: continue # Aggregate Strategy parts = [] if row.get("MH Coach Hypothesis"): parts.append(f"🧠 MH Coach: {row.get('MH Coach Hypothesis')}") if row.get("MH Early Red Flags"): parts.append(f"đŸš© Red Flags: {row.get('MH Early Red Flags')}") if row.get("MH Adjustments / ErgĂ€nzungen"): parts.append(f"🔧 Adjustments: {row.get('MH Adjustments / ErgĂ€nzungen')}") strategy = "\n\n".join(parts) # Store (last row wins if duplicates, but they should be identical for vertical data) csv_data[vertical] = { "pains": row.get("Pains (clean)", ""), "gains": row.get("Gains (clean)", ""), "strategy": strategy } # 4. Push Updates logger.info("Starting Batch Update...") count = 0 for vertical, data in csv_data.items(): if vertical in notion_map: page_id = notion_map[vertical] logger.info(f"Updating '{vertical}'...") update_page(token, page_id, data["pains"], data["gains"], data["strategy"]) count += 1 else: logger.warning(f"Skipping '{vertical}' (Not found in Notion)") logger.info(f"Finished. Updated {count} verticals.") if __name__ == "__main__": main()