266 lines
12 KiB
Python
266 lines
12 KiB
Python
import os
|
|
import requests
|
|
import json
|
|
from dotenv import load_dotenv
|
|
|
|
load_dotenv(dotenv_path="/home/node/clawd/.env")
|
|
|
|
NOTION_TOKEN = os.getenv("NOTION_API_KEY")
|
|
HEADERS = {
|
|
"Authorization": f"Bearer {NOTION_TOKEN}",
|
|
"Content-Type": "application/json",
|
|
"Notion-Version": "2022-06-28"
|
|
}
|
|
|
|
# --- Load Product Mapping ---
|
|
try:
|
|
with open("data/product_mapping.json", "r") as f:
|
|
PRODUCT_MAP = json.load(f)
|
|
except FileNotFoundError:
|
|
print("❌ Product mapping not found. Run fetch_product_mapping.py first.")
|
|
exit(1)
|
|
|
|
# Helper to find DB ID
|
|
def find_db_id(query_name):
|
|
url = "https://api.notion.com/v1/search"
|
|
payload = {"query": query_name, "filter": {"value": "database", "property": "object"}}
|
|
resp = requests.post(url, headers=HEADERS, json=payload)
|
|
if resp.status_code == 200:
|
|
results = resp.json().get("results", [])
|
|
if results:
|
|
return results[0]['id']
|
|
return None
|
|
|
|
def get_page_by_vertical(db_id, vertical_name):
|
|
url = f"https://api.notion.com/v1/databases/{db_id}/query"
|
|
# Using 'Vertical' as the title property name based on previous audit
|
|
payload = {
|
|
"filter": {
|
|
"property": "Vertical",
|
|
"title": {"equals": vertical_name}
|
|
}
|
|
}
|
|
resp = requests.post(url, headers=HEADERS, json=payload)
|
|
if resp.status_code == 200:
|
|
results = resp.json().get("results", [])
|
|
if results:
|
|
return results[0]
|
|
return None
|
|
|
|
def update_page(page_id, properties):
|
|
url = f"https://api.notion.com/v1/pages/{page_id}"
|
|
payload = {"properties": properties}
|
|
resp = requests.patch(url, headers=HEADERS, json=payload)
|
|
if resp.status_code == 200:
|
|
print(f"✅ Updated '{page_id}'")
|
|
else:
|
|
print(f"❌ Error updating '{page_id}': {resp.text}")
|
|
|
|
def create_page(db_id, properties):
|
|
url = "https://api.notion.com/v1/pages"
|
|
payload = {"parent": {"database_id": db_id}, "properties": properties}
|
|
resp = requests.post(url, headers=HEADERS, json=payload)
|
|
if resp.status_code == 200:
|
|
print(f"✅ Created new page")
|
|
else:
|
|
print(f"❌ Error creating page: {resp.text}")
|
|
|
|
# --- CONTENT DEFINITION ---
|
|
|
|
# Format: Vertical -> { props... }
|
|
UPDATES = {
|
|
"Healthcare - Care Home": {
|
|
"product": "Cleaning Indoor Roboter (Wet Surface)",
|
|
"pains": """[Primary Product: Cleaning]
|
|
- Infektionsrisiko: Mangelnde Bodenhygiene und Keimverschleppung in sensiblen Bereichen gefährden Bewohner.
|
|
- Dokumentationspflicht: Lückenlose Nachweise für Hygiene-Audits binden wertvolle Pflegezeit.
|
|
- Personalmangel: Reinigungskräfte fehlen, Standards können manuell kaum gehalten werden.
|
|
|
|
[Secondary Product: Service]
|
|
- Pflegeressourcen: Fachkräfte binden bis zu 30% ihrer Zeit mit nicht-pflegerischen Transportwegen (Essen/Wäsche).
|
|
- Körperliche Belastung: Schweres Heben und weite Wege führen zu krankheitsbedingten Ausfällen im Pflegeteam.""",
|
|
"gains": """[Primary Product: Cleaning]
|
|
- Audit-Sicherheit: Automatisierte, protokollierte Reinigung sichert Compliance ohne Mehraufwand.
|
|
- Entlastung Housekeeping: Personal konzentriert sich auf Sichtreinigung und Desinfektion statt Bodenfläche.
|
|
|
|
[Secondary Product: Service]
|
|
- Mehr Zeit am Patienten: Reduktion der Laufwege gibt Pflegekräften 2-3 Std./Schicht zurück.
|
|
- Mitarbeiterzufriedenheit: Reduktion körperlicher Belastung senkt Krankenstand.""",
|
|
"ops_focus": True,
|
|
"status": "Freigegeben",
|
|
"notes": "Prio 1: Reinigung. Prio 2: Service (Essen). Fokus auf Fachkräftemangel & Hygiene."
|
|
},
|
|
"Healthcare - Hospital": {
|
|
"product": "Cleaning Indoor Roboter (Wet Surface)",
|
|
"pains": """[Primary Product: Cleaning]
|
|
- Infektionsschutz: Hohe Frequenz an Patientenbewegungen erfordert permanente Desinfektion der Böden.
|
|
- Audit-Druck: Behördliche Auflagen verlangen lückenlose Dokumentation, die manuell kaum leistbar ist.
|
|
- Kostendruck: Steigende Personalkosten bei fixen Fallpauschalen zwingen zur Effizienzsteigerung.
|
|
|
|
[Secondary Product: Service]
|
|
- Logistik-Aufwand: Transport von Proben, Wäsche und Essen bindet Pflegepersonal in unproduktiven Wegezeiten.""",
|
|
"gains": """[Primary Product: Cleaning]
|
|
- Hygiene-Standard: 24/7 gleichbleibende Reinigungsqualität reduziert Keimbelastung messbar.
|
|
- Compliance: Automatische Protokollierung aller Reinigungsfahrten für Audits.
|
|
|
|
[Secondary Product: Service]
|
|
- Prozess-Effizienz: Automatisierter Warentransport entlastet Fachpersonal für medizinische Aufgaben.""",
|
|
"ops_focus": True,
|
|
"status": "Freigegeben",
|
|
"notes": "Prio 1: Reinigung (Alex Veto). Service ist 'nice to have'. KPI: Hygiene-Sicherheit."
|
|
},
|
|
"Leisure - Entertainment": {
|
|
"product": "Service Roboter", # FIX: Changed from Cleaning to Service
|
|
"pains": """[Primary Product: Service]
|
|
- Service-Engpass: Umsatzverlust zu Stoßzeiten, da Personal nicht schnell genug Getränke/Snacks nachliefert.
|
|
- Personalmangel: Schwierige Besetzung von Spätschichten führt zu geschlossenen Stationen/Bahnen.
|
|
- Wartezeiten: Gäste sind unzufrieden, wenn Bestellungen zu lange dauern.
|
|
|
|
[Secondary Product: Cleaning]
|
|
- Bodenverschmutzung: Klebrige Böden (Getränke/Popcorn) im Foyer stören das Gästeerlebnis.""",
|
|
"gains": """[Primary Product: Service]
|
|
- Umsatzsteigerung: Permanente Verfügbarkeit von Snacks/Getränken direkt am Platz (Cross-Selling).
|
|
- Erlebnis-Faktor: Innovative Roboter begeistern Gäste und fördern Social-Media-Sichtbarkeit.
|
|
- Entlastung: Servicepersonal hat mehr Zeit für Gästebetreuung statt Laufwege.""",
|
|
"ops_focus": True, # Keep secondary focus plausible
|
|
"status": "Freigegeben",
|
|
"notes": "Prio 1: Service Robotik (BellaBot). Cleaning nur Prio 2 (Foyer/Gänge)."
|
|
},
|
|
"Logistics - Warehouse": {
|
|
"product": "Cleaning Outdoor Roboter (Sweeper)",
|
|
"pains": """[Primary Product: Sweeper]
|
|
- Prozesssicherheit: Staub auf Sensoren und Lichtschranken führt zu Anlagenstörungen und Produktionsstopps.
|
|
- Arbeitssicherheit: Verschmutzte Fahrwege durch Palettenreste/Staub erhöhen das Unfallrisiko.
|
|
- Manuelle Bindung: Fachkräfte müssen kehren statt kommissionieren.
|
|
|
|
[Secondary Product: Cleaning Wet]
|
|
- Hartnäckige Verschmutzungen: Öl/Reifenabrieb erfordern Nassreinigung, die manuell zeitintensiv ist.""",
|
|
"gains": """[Primary Product: Sweeper]
|
|
- Staubfreie Umgebung: Werterhalt des Hallenbodens und Schutz empfindlicher Ware/Anlagen.
|
|
- Produktivität: Reinigung erfolgt parallel zum Betrieb oder nachts, ohne Störung.
|
|
- Sicherheit: Saubere Fahrwege reduzieren Unfallrisiko für Flurförderzeuge.""",
|
|
"ops_focus": True,
|
|
"status": "Freigegeben",
|
|
"notes": "Prio 1: Sweeper (Staub). Prio 2: Wet. Transport schwierig wegen Paletten."
|
|
},
|
|
"Tech - Data Center": {
|
|
"product": "Security Roboter",
|
|
"pains": """[Primary Product: Security]
|
|
- Sicherheitsrisiko: Unbefugter Zutritt in Sicherheitsbereiche muss lückenlos detektiert werden (24/7).
|
|
- Personalbindung: Wachpersonal ist teuer und kann nicht überall gleichzeitig sein.
|
|
|
|
[Secondary Product: Cleaning]
|
|
- Feinstaub: Staubpartikel in Serverräumen gefährden Hardware und Kühlung.""",
|
|
"gains": """[Primary Product: Security]
|
|
- Lückenlose Überwachung: Permanente Patrouille und sofortige Alarmierung ohne Personalbindung.
|
|
- Dokumentation: Video- und Sensorprotokolle aller Ereignisse.
|
|
|
|
[Secondary Product: Cleaning]
|
|
- Ausfallsicherheit: Staubfreie Umgebung verlängert Hardware-Lebensdauer.""",
|
|
"ops_focus": True,
|
|
"status": "Klärrungsbedarf", # New, needs review
|
|
"notes": "Neu angelegt. Prio 1 Security (lt. Transkript). Prio 2 Cleaning (Staub)."
|
|
},
|
|
"Reinigungsdienstleister": {
|
|
"product": "Cleaning Indoor Roboter (Wet Surface)",
|
|
"pains": """[Primary Product: Cleaning]
|
|
- Personalmangel: Schwierigkeit, zuverlässiges Personal für alle Objekte zu finden.
|
|
- Kostendruck: Geringe Margen bei Ausschreibungen erfordern hohe Effizienz.
|
|
- Qualitätsschwankungen: Manuelle Reinigung variiert stark, Kunden beschweren sich.
|
|
- Fluktuation: Hoher Aufwand für ständige Neueinarbeitung.""",
|
|
"gains": """[Primary Product: Cleaning]
|
|
- Skalierbarkeit: Roboter übernehmen Flächenleistung, Personal macht Detailreinigung.
|
|
- Innovation: Wettbewerbsvorteil bei Ausschreibungen durch Technologie-Einsatz.
|
|
- Kalkulationssicherheit: Fixe Kosten statt variabler Personalkosten/Krankheitstage.""",
|
|
"ops_focus": False,
|
|
"status": "Klärrungsbedarf",
|
|
"notes": "Neu angelegt. Zielgruppe: Wisag, Dussmann etc. (Alex: Größter Markt)."
|
|
},
|
|
"Infrastructure - Communities": {
|
|
"product": "Cleaning Indoor Roboter (Wet Surface)",
|
|
"pains": """[Primary Product: Cleaning]
|
|
- Großflächen-Reinigung: Sporthallen, Aulen und Flure binden enorm viele Personalstunden.
|
|
- Budget-Druck: Kommunen müssen sparen, Reinigungskosten sind großer Posten.
|
|
- Nutzungs-Konflikte: Reinigung muss in engen Zeitfenstern zwischen Schul/Vereinsnutzung erfolgen.""",
|
|
"gains": """[Primary Product: Cleaning]
|
|
- Kosteneffizienz: Reduktion der Reinigungskosten pro Quadratmeter.
|
|
- Flexibilität: Reinigung kann nachts oder in Randzeiten erfolgen.
|
|
- Werterhalt: Schonende, regelmäßige Reinigung verlängert Lebensdauer von Sportböden.""",
|
|
"ops_focus": False,
|
|
"status": "Klärrungsbedarf",
|
|
"notes": "Neu angelegt (Schulen, Gemeinden)."
|
|
},
|
|
"Infrastructure Parking": {
|
|
"product": "Cleaning Outdoor Roboter (Sweeper)",
|
|
"pains": """[Primary Product: Sweeper]
|
|
- Außenwirkung: Verschmutzte Parkflächen/Müll schaden dem Image (erster Eindruck).
|
|
- Manuelle Arbeit: Fegen von großen Außenflächen ist personalintensiv und unbeliebt.
|
|
- Umwelt: Müll gelangt in die Umgebung/Kanalisation.""",
|
|
"gains": """[Primary Product: Sweeper]
|
|
- Gepflegtes Erscheinungsbild: Täglich saubere Außenanlagen.
|
|
- Autonomie: Roboter reinigt selbstständig, auch bei schlechtem Wetter.
|
|
- Entlastung: Hausmeister kann sich um Wartung kümmern statt Fegen.""",
|
|
"ops_focus": False,
|
|
"status": "Klärrungsbedarf",
|
|
"notes": "Neu angelegt (Parkplätze, Außenanlagen)."
|
|
}
|
|
}
|
|
|
|
def run_enrichment():
|
|
db_id = find_db_id("Industries")
|
|
if not db_id:
|
|
print("❌ Industries DB not found.")
|
|
return
|
|
|
|
print(f"--- Enriching Verticals in DB {db_id} ---")
|
|
|
|
for vertical, data in UPDATES.items():
|
|
# Resolve Product ID
|
|
prod_id = PRODUCT_MAP.get(data["product"])
|
|
if not prod_id:
|
|
print(f"❌ Product '{data['product']}' not found in map. Skipping {vertical}.")
|
|
continue
|
|
|
|
# Prepare Properties
|
|
props = {
|
|
"Pains": {"rich_text": [{"text": {"content": data["pains"]}}]},
|
|
"Gains": {"rich_text": [{"text": {"content": data["gains"]}}]},
|
|
"Primary Product Category": {"relation": [{"id": prod_id}]},
|
|
"Notes": {"rich_text": [{"text": {"content": data["notes"]}}]},
|
|
# Handle Status (Try Select first, then Status)
|
|
# We assume "Freigabe" exists
|
|
}
|
|
|
|
# Add checkbox if present in logic
|
|
if "ops_focus" in data:
|
|
props["Ops Focus: Secondary"] = {"checkbox": data["ops_focus"]}
|
|
|
|
# Check if page exists
|
|
page = get_page_by_vertical(db_id, vertical)
|
|
|
|
if page:
|
|
# Update existing
|
|
# Add Status Update
|
|
# (Note: Logic to detect Select vs Status type is needed, but we assume Select/Status name is consistent)
|
|
# For robustness, we check the property type in the page object
|
|
status_type = page['properties'].get("Freigabe", {}).get("type")
|
|
if status_type == "status":
|
|
props["Freigabe"] = {"status": {"name": data["status"]}}
|
|
elif status_type == "select":
|
|
props["Freigabe"] = {"select": {"name": data["status"]}}
|
|
|
|
print(f"Updating '{vertical}'...")
|
|
update_page(page['id'], props)
|
|
else:
|
|
# Create new
|
|
print(f"Creating new vertical '{vertical}'...")
|
|
props["Vertical"] = {"title": [{"text": {"content": vertical}}]}
|
|
# Guess status type (usually Select or Status) - Try Status first as default in new Notion DBs
|
|
# Or omit status if unsure, but we want to set it.
|
|
# We'll try Status format.
|
|
props["Freigabe"] = {"status": {"name": data["status"]}}
|
|
create_page(db_id, props)
|
|
|
|
if __name__ == "__main__":
|
|
run_enrichment()
|