- Added import_relational_radar.py for bidirectional database structure in Notion. - Added refresh_references.py to populate analysis data with grounded facts via scraping. - Updated documentation for Competitive Radar v2.0.
201 lines
6.8 KiB
Python
201 lines
6.8 KiB
Python
import json
|
|
import os
|
|
import requests
|
|
import sys
|
|
|
|
# Configuration
|
|
JSON_FILE = 'analysis_robo-planet.de.json'
|
|
TOKEN_FILE = 'notion_token.txt'
|
|
# Root Page ID from notion_integration.md
|
|
PARENT_PAGE_ID = "2e088f42-8544-8024-8289-deb383da3818"
|
|
DB_TITLE = "Competitive Radar 🎯"
|
|
|
|
def load_json_data(filepath):
|
|
try:
|
|
with open(filepath, 'r') as f:
|
|
return json.load(f)
|
|
except Exception as e:
|
|
print(f"Error loading JSON: {e}")
|
|
sys.exit(1)
|
|
|
|
def load_notion_token(filepath):
|
|
try:
|
|
with open(filepath, 'r') as f:
|
|
return f.read().strip()
|
|
except Exception as e:
|
|
print(f"Error loading token: {e}")
|
|
sys.exit(1)
|
|
|
|
def create_competitor_database(token, parent_page_id):
|
|
url = "https://api.notion.com/v1/databases"
|
|
headers = {
|
|
"Authorization": f"Bearer {token}",
|
|
"Notion-Version": "2022-06-28",
|
|
"Content-Type": "application/json"
|
|
}
|
|
|
|
payload = {
|
|
"parent": {"type": "page_id", "page_id": parent_page_id},
|
|
"title": [{"type": "text", "text": {"content": DB_TITLE}}],
|
|
"properties": {
|
|
"Competitor Name": {"title": {}},
|
|
"Website": {"url": {}},
|
|
"Target Industries": {"multi_select": {}},
|
|
"USPs / Differentiators": {"rich_text": {}},
|
|
"Silver Bullet": {"rich_text": {}},
|
|
"Landmines": {"rich_text": {}},
|
|
"Strengths vs Weaknesses": {"rich_text": {}},
|
|
"Portfolio": {"rich_text": {}},
|
|
"Known References": {"rich_text": {}}
|
|
}
|
|
}
|
|
|
|
print(f"Creating database '{DB_TITLE}'...")
|
|
response = requests.post(url, headers=headers, json=payload)
|
|
|
|
if response.status_code != 200:
|
|
print(f"Error creating database: {response.status_code}")
|
|
print(response.text)
|
|
sys.exit(1)
|
|
|
|
db_data = response.json()
|
|
print(f"Database created successfully! ID: {db_data['id']}")
|
|
return db_data['id']
|
|
|
|
def format_list_as_bullets(items):
|
|
"""Converts a python list of strings into a Notion rich_text text block with bullets."""
|
|
if not items:
|
|
return ""
|
|
text_content = ""
|
|
for item in items:
|
|
text_content += f"• {item}\n"
|
|
return text_content.strip()
|
|
|
|
def get_competitor_data(data, comp_name):
|
|
"""Aggregates data from different sections of the JSON for a single competitor."""
|
|
|
|
# Init structure
|
|
comp_data = {
|
|
"name": comp_name,
|
|
"url": "",
|
|
"industries": [],
|
|
"differentiators": [],
|
|
"portfolio": [],
|
|
"silver_bullet": "",
|
|
"landmines": [],
|
|
"strengths_weaknesses": [],
|
|
"references": []
|
|
}
|
|
|
|
# 1. Basic Info & Portfolio (from 'analyses')
|
|
for analysis in data.get('analyses', []):
|
|
c = analysis.get('competitor', {})
|
|
if c.get('name') == comp_name:
|
|
comp_data['url'] = c.get('url', '')
|
|
comp_data['industries'] = analysis.get('target_industries', [])
|
|
comp_data['differentiators'] = analysis.get('differentiators', [])
|
|
|
|
# Format Portfolio
|
|
for prod in analysis.get('portfolio', []):
|
|
p_name = prod.get('product', '')
|
|
p_purpose = prod.get('purpose', '')
|
|
comp_data['portfolio'].append(f"{p_name}: {p_purpose}")
|
|
break
|
|
|
|
# 2. Battlecards
|
|
for card in data.get('battlecards', []):
|
|
if card.get('competitor_name') == comp_name:
|
|
comp_data['silver_bullet'] = card.get('silver_bullet', '')
|
|
comp_data['landmines'] = card.get('landmine_questions', [])
|
|
comp_data['strengths_weaknesses'] = card.get('strengths_vs_weaknesses', [])
|
|
break
|
|
|
|
# 3. References
|
|
for ref_entry in data.get('reference_analysis', []):
|
|
if ref_entry.get('competitor_name') == comp_name:
|
|
for ref in ref_entry.get('references', []):
|
|
r_name = ref.get('name', 'Unknown')
|
|
r_ind = ref.get('industry', '')
|
|
entry = r_name
|
|
if r_ind:
|
|
entry += f" ({r_ind})"
|
|
comp_data['references'].append(entry)
|
|
break
|
|
|
|
return comp_data
|
|
|
|
def add_competitor_entry(token, db_id, c_data):
|
|
url = "https://api.notion.com/v1/pages"
|
|
headers = {
|
|
"Authorization": f"Bearer {token}",
|
|
"Notion-Version": "2022-06-28",
|
|
"Content-Type": "application/json"
|
|
}
|
|
|
|
# Prepare properties
|
|
props = {
|
|
"Competitor Name": {"title": [{"text": {"content": c_data['name']}}]},
|
|
"USPs / Differentiators": {"rich_text": [{"text": {"content": format_list_as_bullets(c_data['differentiators'])}}]},
|
|
"Silver Bullet": {"rich_text": [{"text": {"content": c_data['silver_bullet']}}]},
|
|
"Landmines": {"rich_text": [{"text": {"content": format_list_as_bullets(c_data['landmines'])}}]},
|
|
"Strengths vs Weaknesses": {"rich_text": [{"text": {"content": format_list_as_bullets(c_data['strengths_weaknesses'])}}]},
|
|
"Portfolio": {"rich_text": [{"text": {"content": format_list_as_bullets(c_data['portfolio'])}}]},
|
|
"Known References": {"rich_text": [{"text": {"content": format_list_as_bullets(c_data['references'])}}]}
|
|
}
|
|
|
|
if c_data['url']:
|
|
props["Website"] = {"url": c_data['url']}
|
|
|
|
# Multi-select for industries
|
|
# Note: Notion options are auto-created, but we must ensure no commas or weird chars break it
|
|
ms_options = []
|
|
for ind in c_data['industries']:
|
|
# Simple cleanup
|
|
clean_ind = ind.replace(',', '')
|
|
ms_options.append({"name": clean_ind})
|
|
|
|
props["Target Industries"] = {"multi_select": ms_options}
|
|
|
|
payload = {
|
|
"parent": {"database_id": db_id},
|
|
"properties": props
|
|
}
|
|
|
|
try:
|
|
response = requests.post(url, headers=headers, json=payload)
|
|
response.raise_for_status()
|
|
print(f" - Added: {c_data['name']}")
|
|
except requests.exceptions.HTTPError as e:
|
|
print(f" - Failed to add {c_data['name']}: {e}")
|
|
# print(response.text)
|
|
|
|
def main():
|
|
token = load_notion_token(TOKEN_FILE)
|
|
data = load_json_data(JSON_FILE)
|
|
|
|
# 1. Create DB
|
|
db_id = create_competitor_database(token, PARENT_PAGE_ID)
|
|
|
|
# 2. Collect List of Competitors
|
|
# We use the shortlist or candidates list to drive the iteration
|
|
competitor_list = data.get('competitors_shortlist', [])
|
|
if not competitor_list:
|
|
competitor_list = data.get('competitor_candidates', [])
|
|
|
|
print(f"Importing {len(competitor_list)} competitors...")
|
|
|
|
for comp in competitor_list:
|
|
c_name = comp.get('name')
|
|
if not c_name: continue
|
|
|
|
# Aggregate Data
|
|
c_data = get_competitor_data(data, c_name)
|
|
|
|
# Push to Notion
|
|
add_competitor_entry(token, db_id, c_data)
|
|
|
|
print("Import complete.")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|