feat(notion): Initial PoC for Notion Integration
- Added documentation for Notion setup and resources (notion_integration.md). - Added scripts for authentication test, database creation, and product insertion. - Successfully tested connection and data mapping for 'RoboPlanet Product Master'.
This commit is contained in:
91
add_product_to_notion.py
Normal file
91
add_product_to_notion.py
Normal file
@@ -0,0 +1,91 @@
|
||||
import requests
|
||||
import json
|
||||
import os
|
||||
|
||||
TOKEN_FILE = 'notion_api_key.txt'
|
||||
DATABASE_ID = "2e088f42-8544-815e-a3f9-e226f817bded"
|
||||
|
||||
# Data from the VIGGO S100-N analysis
|
||||
PRODUCT_DATA = {
|
||||
"specs": {
|
||||
"metadata": {
|
||||
"brand": "VIGGO",
|
||||
"model_name": "S100-N",
|
||||
"category": "cleaning",
|
||||
"manufacturer_url": None
|
||||
},
|
||||
"core_specs": {
|
||||
"battery_runtime_min": 360,
|
||||
"charge_time_min": 270,
|
||||
"weight_kg": 395.0,
|
||||
"max_slope_deg": 10.0
|
||||
},
|
||||
"layers": {
|
||||
"cleaning": {
|
||||
"fresh_water_l": 60.0,
|
||||
"area_performance_sqm_h": 3000.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def add_to_notion(token):
|
||||
url = "https://api.notion.com/v1/pages"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Notion-Version": "2022-06-28",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
specs = PRODUCT_DATA["specs"]
|
||||
meta = specs["metadata"]
|
||||
core = specs["core_specs"]
|
||||
cleaning = specs["layers"].get("cleaning", {})
|
||||
|
||||
properties = {
|
||||
"Model Name": {"title": [{"text": {"content": meta["model_name"]}}]},
|
||||
"Brand": {"select": {"name": meta["brand"]}},
|
||||
"Category": {"select": {"name": meta["category"]}},
|
||||
"Battery Runtime (min)": {"number": core.get("battery_runtime_min")},
|
||||
"Charge Time (min)": {"number": core.get("charge_time_min")},
|
||||
"Weight (kg)": {"number": core.get("weight_kg")},
|
||||
"Max Slope (deg)": {"number": core.get("max_slope_deg")},
|
||||
"Fresh Water (l)": {"number": cleaning.get("fresh_water_l")},
|
||||
"Area Performance (m2/h)": {"number": cleaning.get("area_performance_sqm_h")}
|
||||
}
|
||||
|
||||
# Add URL if present
|
||||
if meta.get("manufacturer_url"):
|
||||
properties["Manufacturer URL"] = {"url": meta["manufacturer_url"]}
|
||||
|
||||
payload = {
|
||||
"parent": {"database_id": DATABASE_ID},
|
||||
"properties": properties
|
||||
}
|
||||
|
||||
print(f"Adding {meta['brand']} {meta['model_name']} to Notion database...")
|
||||
|
||||
try:
|
||||
response = requests.post(url, headers=headers, json=payload)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
print("\n=== SUCCESS ===")
|
||||
print(f"Product added to database!")
|
||||
print(f"Page URL: {data.get('url')}")
|
||||
except requests.exceptions.HTTPError as e:
|
||||
print(f"\n=== ERROR ===")
|
||||
print(f"HTTP Error: {e}")
|
||||
print(f"Response: {response.text}")
|
||||
|
||||
def main():
|
||||
try:
|
||||
with open(TOKEN_FILE, 'r') as f:
|
||||
token = f.read().strip()
|
||||
except FileNotFoundError:
|
||||
print(f"Error: Could not find '{TOKEN_FILE}'")
|
||||
return
|
||||
|
||||
add_to_notion(token)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
71
create_notion_db.py
Normal file
71
create_notion_db.py
Normal file
@@ -0,0 +1,71 @@
|
||||
import requests
|
||||
import json
|
||||
import os
|
||||
|
||||
TOKEN_FILE = 'notion_api_key.txt'
|
||||
PARENT_PAGE_ID = "2e088f42-8544-8024-8289-deb383da3818" # "Roboplanet" page
|
||||
|
||||
def create_product_database(token):
|
||||
print(f"Creating '📦 RoboPlanet Product Master' database under parent {PARENT_PAGE_ID}...")
|
||||
|
||||
url = "https://api.notion.com/v1/databases"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Notion-Version": "2022-06-28",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
database_definition = {
|
||||
"parent": {"type": "page_id", "page_id": PARENT_PAGE_ID},
|
||||
"title": [{"type": "text", "text": {"content": "📦 RoboPlanet Product Master"}}],
|
||||
"properties": {
|
||||
"Model Name": {"title": {}},
|
||||
"Brand": {"select": {"options": [
|
||||
{"name": "VIGGO", "color": "blue"},
|
||||
{"name": "PUDU", "color": "orange"}
|
||||
]}},
|
||||
"Category": {"select": {"options": [
|
||||
{"name": "cleaning", "color": "green"},
|
||||
{"name": "service", "color": "blue"},
|
||||
{"name": "security", "color": "red"}
|
||||
]}},
|
||||
# Core Specs
|
||||
"Battery Runtime (min)": {"number": {"format": "number"}},
|
||||
"Charge Time (min)": {"number": {"format": "number"}},
|
||||
"Weight (kg)": {"number": {"format": "number"}},
|
||||
"Max Slope (deg)": {"number": {"format": "number"}},
|
||||
# Cleaning Layer
|
||||
"Fresh Water (l)": {"number": {"format": "number"}},
|
||||
"Area Performance (m2/h)": {"number": {"format": "number"}},
|
||||
# Metadata
|
||||
"Manufacturer URL": {"url": {}},
|
||||
"GTM Status": {"status": {}}
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(url, headers=headers, json=database_definition)
|
||||
response.raise_for_status()
|
||||
new_db = response.json()
|
||||
print(f"\n=== SUCCESS ===")
|
||||
print(f"Database created! ID: {new_db['id']}")
|
||||
print(f"URL: {new_db.get('url')}")
|
||||
return new_db['id']
|
||||
except requests.exceptions.HTTPError as e:
|
||||
print(f"\n=== ERROR ===")
|
||||
print(f"HTTP Error: {e}")
|
||||
print(f"Response: {response.text}")
|
||||
return None
|
||||
|
||||
def main():
|
||||
try:
|
||||
with open(TOKEN_FILE, 'r') as f:
|
||||
token = f.read().strip()
|
||||
except FileNotFoundError:
|
||||
print(f"Error: Could not find '{TOKEN_FILE}'")
|
||||
return
|
||||
|
||||
db_id = create_product_database(token)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
72
hello_notion.py
Normal file
72
hello_notion.py
Normal file
@@ -0,0 +1,72 @@
|
||||
import requests
|
||||
import json
|
||||
import os
|
||||
|
||||
TOKEN_FILE = 'notion_api_key.txt'
|
||||
PARENT_PAGE_ID = "2e088f42-8544-8024-8289-deb383da3818" # "Roboplanet" page
|
||||
|
||||
def main():
|
||||
try:
|
||||
with open(TOKEN_FILE, 'r') as f:
|
||||
token = f.read().strip()
|
||||
except FileNotFoundError:
|
||||
print(f"Error: Could not find '{TOKEN_FILE}'")
|
||||
return
|
||||
|
||||
print(f"Creating 'Hello World' page under parent {PARENT_PAGE_ID}...")
|
||||
|
||||
url = "https://api.notion.com/v1/pages"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Notion-Version": "2022-06-28",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
payload = {
|
||||
"parent": { "page_id": PARENT_PAGE_ID },
|
||||
"properties": {
|
||||
"title": [
|
||||
{
|
||||
"text": {
|
||||
"content": "Hello World"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"object": "block",
|
||||
"type": "paragraph",
|
||||
"paragraph": {
|
||||
"rich_text": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": {
|
||||
"content": "This page was created automatically by the GTM Engine Bot."
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.post(url, headers=headers, json=payload)
|
||||
response.raise_for_status()
|
||||
|
||||
data = response.json()
|
||||
print("\n=== SUCCESS ===")
|
||||
print(f"New page created!")
|
||||
print(f"URL: {data.get('url')}")
|
||||
|
||||
except requests.exceptions.HTTPError as e:
|
||||
print(f"\n=== ERROR ===")
|
||||
print(f"HTTP Error: {e}")
|
||||
print(f"Response: {response.text}")
|
||||
except Exception as e:
|
||||
print(f"\n=== ERROR ===")
|
||||
print(f"An error occurred: {e}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
65
notion_integration.md
Normal file
65
notion_integration.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Notion Integration Dokumentation
|
||||
|
||||
**Stand:** 06. Januar 2026
|
||||
**Status:** Proof of Concept (PoC) erfolgreich
|
||||
|
||||
Diese Dokumentation beschreibt die erfolgreiche Anbindung der GTM Architect Engine an Notion. Es wurden Skripte erstellt, um eine Verbindung herzustellen, Seiten und Datenbanken anzulegen und strukturierte Produktdaten (Hard Facts) automatisch einzutragen.
|
||||
|
||||
## 1. Konfiguration
|
||||
|
||||
* **API Token:** Das Notion-Token wird aus der Datei `notion_api_key.txt` (im Root-Verzeichnis) gelesen. Diese Datei ist im `.gitignore` und wird nicht versioniert.
|
||||
* **Bot Identity:** "RoboPlanet GTM Engine"
|
||||
|
||||
## 2. Ressourcen IDs
|
||||
|
||||
Wichtige IDs für die weitere Entwicklung und Integration:
|
||||
|
||||
* **Root Page (Roboplanet):** `2e088f42-8544-8024-8289-deb383da3818`
|
||||
* **Product Master Database:** `2e088f42-8544-815e-a3f9-e226f817bded`
|
||||
|
||||
## 3. Skripte (PoC)
|
||||
|
||||
Folgende Python-Skripte wurden entwickelt und getestet:
|
||||
|
||||
### 3.1. `hello_notion.py`
|
||||
* **Zweck:** Testet die Authentifizierung und erstellt eine einfache "Hello World"-Seite.
|
||||
* **Funktion:**
|
||||
1. Liest Token.
|
||||
2. Authentifiziert sich als Bot.
|
||||
3. Erstellt eine Page "Hello World" unterhalb der Root Page.
|
||||
|
||||
### 3.2. `create_notion_db.py`
|
||||
* **Zweck:** Erstellt die zentrale Produktdatenbank ("📦 RoboPlanet Product Master").
|
||||
* **Struktur:** Legt Properties für alle relevanten "Hard Facts" an, die in Phase 1 des GTM Architect extrahiert werden:
|
||||
* **Core Specs:** Battery Runtime, Charge Time, Weight, Max Slope.
|
||||
* **Layers:** Fresh Water, Area Performance.
|
||||
* **Metadata:** Brand, Category, Manufacturer URL, GTM Status.
|
||||
|
||||
### 3.3. `add_product_to_notion.py`
|
||||
* **Zweck:** Schreibt ein analysiertes Produkt in die Datenbank.
|
||||
* **Input:** Erwartet ein JSON-Objekt im Format der `specs` aus Phase 1 (siehe `templates/json_struktur_roboplanet.txt`).
|
||||
* **Mapping:** Mapped die JSON-Felder (z.B. `core_specs.battery_runtime_min`) auf die entsprechenden Notion-Datenbank-Spalten.
|
||||
|
||||
## 4. Datenmodell (Product Master)
|
||||
|
||||
Die Datenbank "📦 RoboPlanet Product Master" hat folgendes Schema:
|
||||
|
||||
| Notion Property | Typ | Source JSON Field |
|
||||
| :--- | :--- | :--- |
|
||||
| **Model Name** | Title | `metadata.model_name` |
|
||||
| **Brand** | Select | `metadata.brand` |
|
||||
| **Category** | Select | `metadata.category` |
|
||||
| **Battery Runtime (min)** | Number | `core_specs.battery_runtime_min` |
|
||||
| **Charge Time (min)** | Number | `core_specs.charge_time_min` |
|
||||
| **Weight (kg)** | Number | `core_specs.weight_kg` |
|
||||
| **Max Slope (deg)** | Number | `core_specs.max_slope_deg` |
|
||||
| **Fresh Water (l)** | Number | `layers.cleaning.fresh_water_l` |
|
||||
| **Area Performance (m2/h)** | Number | `layers.cleaning.area_performance_sqm_h` |
|
||||
| **Manufacturer URL** | URL | `metadata.manufacturer_url` |
|
||||
| **GTM Status** | Status | *(Initial: Not Started)* |
|
||||
|
||||
## 5. Nächste Schritte
|
||||
|
||||
1. Integration der Logik aus `add_product_to_notion.py` direkt in `gtm_architect_orchestrator.py` (Phase 1).
|
||||
2. Erweiterung um "Content"-Blöcke (z.B. generierte Zusammenfassung als Page Content).
|
||||
3. Status-Updates (GTM Status) je nach Fortschritt der Phasen (z.B. "Phase 3 Complete").
|
||||
Reference in New Issue
Block a user