Update OpenAI API key and enhance app functionality: Replace the OpenAI API key in the .env file for improved access. Refactor app.py to include error handling for missing API keys and implement dark mode functionality with session management. Update README.md to reflect the use of Tailwind CSS via CDN and document the Content Security Policy (CSP) adjustments. Enhance mindmap data loading with a new API endpoint for refreshing data, ensuring better user experience during database connection issues. Update styles and templates for improved UI consistency and responsiveness.

This commit is contained in:
2025-04-27 16:56:16 +02:00
parent 2d8cdc052f
commit 4a3092a4d2
42 changed files with 2458 additions and 878 deletions

649
app.py
View File

@@ -3,7 +3,7 @@
import os
from datetime import datetime, timedelta
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, session
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, session, g
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
@@ -41,7 +41,29 @@ app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=365) # Langlebige Session für Dark Mode-Einstellung
# OpenAI API-Konfiguration
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
api_key = os.environ.get("OPENAI_API_KEY")
if not api_key:
print("WARNUNG: Kein OPENAI_API_KEY in Umgebungsvariablen gefunden. KI-Funktionalität wird nicht verfügbar sein.")
api_key = "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"
client = OpenAI(api_key=api_key)
# Dark Mode Einstellung in Session speichern
@app.before_request
def handle_dark_mode():
if 'dark_mode' not in session:
session['dark_mode'] = False # Standardmäßig Light Mode
# Context processor für Dark Mode
@app.context_processor
def inject_dark_mode():
return {'dark_mode': session.get('dark_mode', False)}
# Route zum Umschalten des Dark Mode
@app.route('/toggle-dark-mode', methods=['POST'])
def toggle_dark_mode():
session['dark_mode'] = not session.get('dark_mode', False)
return jsonify({'success': True, 'dark_mode': session['dark_mode']})
# Context processor für globale Template-Variablen
@app.context_processor
@@ -63,6 +85,214 @@ db.init_app(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'
# Erst nach der App-Initialisierung die DB-Check-Funktionen importieren
from utils.db_check import check_db_connection, initialize_db_if_needed
def create_default_categories():
"""Erstellt die Standardkategorien für die Mindmap"""
# Hauptkategorien
main_categories = [
{
"name": "Philosophie",
"description": "Philosophisches Denken und Konzepte",
"color_code": "#9F7AEA",
"icon": "fa-brain",
"subcategories": [
{"name": "Ethik", "description": "Moralische Grundsätze", "icon": "fa-balance-scale"},
{"name": "Logik", "description": "Gesetze des Denkens", "icon": "fa-project-diagram"},
{"name": "Erkenntnistheorie", "description": "Natur des Wissens", "icon": "fa-lightbulb"}
]
},
{
"name": "Wissenschaft",
"description": "Wissenschaftliche Disziplinen und Forschung",
"color_code": "#48BB78",
"icon": "fa-flask",
"subcategories": [
{"name": "Physik", "description": "Gesetze der Materie und Energie", "icon": "fa-atom"},
{"name": "Biologie", "description": "Wissenschaft des Lebens", "icon": "fa-dna"},
{"name": "Mathematik", "description": "Abstrakte Strukturen", "icon": "fa-calculator"},
{"name": "Informatik", "description": "Wissenschaft der Datenverarbeitung", "icon": "fa-laptop-code"}
]
},
{
"name": "Technologie",
"description": "Technologische Entwicklungen und Anwendungen",
"color_code": "#ED8936",
"icon": "fa-microchip",
"subcategories": [
{"name": "Künstliche Intelligenz", "description": "Intelligente Maschinen", "icon": "fa-robot"},
{"name": "Programmierung", "description": "Softwareentwicklung", "icon": "fa-code"},
{"name": "Elektronik", "description": "Elektronische Systeme", "icon": "fa-memory"}
]
},
{
"name": "Künste",
"description": "Kunstformen und kulturelle Ausdrucksweisen",
"color_code": "#ED64A6",
"icon": "fa-palette",
"subcategories": [
{"name": "Literatur", "description": "Schriftliche Werke", "icon": "fa-book"},
{"name": "Musik", "description": "Klangkunst", "icon": "fa-music"},
{"name": "Bildende Kunst", "description": "Visuelle Kunstformen", "icon": "fa-paint-brush"}
]
},
{
"name": "Psychologie",
"description": "Menschliches Verhalten und Geist",
"color_code": "#4299E1",
"icon": "fa-comments",
"subcategories": [
{"name": "Kognition", "description": "Denken und Wahrnehmen", "icon": "fa-brain"},
{"name": "Emotionen", "description": "Gefühlswelt", "icon": "fa-heart"},
{"name": "Persönlichkeit", "description": "Charaktereigenschaften", "icon": "fa-user"}
]
}
]
# Kategorien erstellen
for main_cat_data in main_categories:
# Prüfen, ob die Kategorie bereits existiert
existing_cat = Category.query.filter_by(name=main_cat_data["name"]).first()
if existing_cat:
continue
# Hauptkategorie erstellen
main_category = Category(
name=main_cat_data["name"],
description=main_cat_data["description"],
color_code=main_cat_data["color_code"],
icon=main_cat_data["icon"]
)
db.session.add(main_category)
db.session.flush() # Um die ID zu generieren
# Unterkategorien erstellen
for sub_cat_data in main_cat_data.get("subcategories", []):
sub_category = Category(
name=sub_cat_data["name"],
description=sub_cat_data["description"],
color_code=main_cat_data["color_code"],
icon=sub_cat_data.get("icon", main_cat_data["icon"]),
parent_id=main_category.id
)
db.session.add(sub_category)
db.session.commit()
print("Standard-Kategorien wurden erstellt!")
def initialize_database():
"""Initialisiert die Datenbank mit Grunddaten, falls diese leer ist"""
try:
print("Initialisiere die Datenbank...")
# Erstelle alle Tabellen
db.create_all()
# Prüfe, ob bereits Benutzer existieren
if User.query.count() == 0:
print("Erstelle Admin-Benutzer...")
admin = User(
username="admin",
email="admin@example.com",
is_admin=True
)
admin.set_password("admin123") # In echter Umgebung ein sicheres Passwort verwenden!
db.session.add(admin)
# Prüfe, ob bereits Kategorien existieren
if Category.query.count() == 0:
print("Erstelle Standard-Kategorien...")
create_default_categories()
# Stelle sicher, dass die Standard-Knoten für die öffentliche Mindmap existieren
if MindMapNode.query.count() == 0:
print("Erstelle Standard-Knoten für die Mindmap...")
# Hauptknoten: Wissen
root_node = MindMapNode(
name="Wissen",
description="Zentrale Wissensbasis",
color_code="#4299E1",
is_public=True
)
db.session.add(root_node)
db.session.flush() # Um die ID zu generieren
# Verwandte Kategorien finden
philosophy = Category.query.filter_by(name="Philosophie").first()
science = Category.query.filter_by(name="Wissenschaft").first()
technology = Category.query.filter_by(name="Technologie").first()
arts = Category.query.filter_by(name="Künste").first()
# Erstelle Hauptthemenknoten
nodes = [
MindMapNode(
name="Philosophie",
description="Philosophisches Denken",
color_code="#9F7AEA",
category=philosophy,
is_public=True
),
MindMapNode(
name="Wissenschaft",
description="Wissenschaftliche Erkenntnisse",
color_code="#48BB78",
category=science,
is_public=True
),
MindMapNode(
name="Technologie",
description="Technologische Entwicklungen",
color_code="#ED8936",
category=technology,
is_public=True
),
MindMapNode(
name="Künste",
description="Künstlerische Ausdrucksformen",
color_code="#ED64A6",
category=arts,
is_public=True
)
]
# Füge Knoten zur Datenbank hinzu
for node in nodes:
db.session.add(node)
db.session.commit()
# Nachdem wir die IDs haben, füge die Verbindungen hinzu
all_nodes = MindMapNode.query.all()
root = MindMapNode.query.filter_by(name="Wissen").first()
if root:
for node in all_nodes:
if node.id != root.id:
root.children.append(node)
# Speichere die Änderungen
db.session.commit()
print("Datenbankinitialisierung abgeschlossen.")
except Exception as e:
print(f"Fehler bei der Datenbankinitialisierung: {str(e)}")
db.session.rollback()
raise
# Instead of before_first_request, which is deprecated in newer Flask versions
# Use a function to initialize the database that will be called during app creation
def init_app_database(app_instance):
"""Initialisiert die Datenbank für die Flask-App"""
with app_instance.app_context():
# Überprüfe und initialisiere die Datenbank bei Bedarf
if not initialize_db_if_needed(db, initialize_database):
print("WARNUNG: Datenbankinitialisierung fehlgeschlagen. Einige Funktionen könnten eingeschränkt sein.")
# Call the function to initialize the database
init_app_database(app)
# Benutzerdefinierter Decorator für Admin-Zugriff
def admin_required(f):
@wraps(f)
@@ -145,14 +375,22 @@ def index():
@app.route('/mindmap')
def mindmap():
"""Zeigt die öffentliche Mindmap an."""
# Sicherstellen, dass wir Kategorien haben
with app.app_context():
try:
# Sicherstellen, dass wir Kategorien haben
if Category.query.count() == 0:
create_default_categories()
# Hole alle Kategorien der obersten Ebene
categories = Category.query.filter_by(parent_id=None).all()
return render_template('mindmap.html', categories=categories)
# Hole alle Kategorien der obersten Ebene
categories = Category.query.filter_by(parent_id=None).all()
# Transformiere Kategorien in ein anzeigbares Format für die Vorlage
category_tree = [build_category_tree(cat) for cat in categories]
return render_template('mindmap.html', categories=category_tree)
except Exception as e:
# Bei Fehler leere Kategorienliste übergeben und Fehler protokollieren
print(f"Fehler beim Laden der Mindmap-Kategorien: {str(e)}")
return render_template('mindmap.html', categories=[], error=str(e))
# Route for user profile
@app.route('/profile')
@@ -160,16 +398,47 @@ def mindmap():
def profile():
# Lade Benutzer-Mindmaps
user_mindmaps = UserMindmap.query.filter_by(user_id=current_user.id).all()
# Lade Statistiken
thought_count = Thought.query.filter_by(user_id=current_user.id).count()
bookmark_count = db.session.query(func.count()).select_from(user_thought_bookmark).filter(
user_thought_bookmark.c.user_id == current_user.id
).scalar()
bookmark_count = db.session.query(user_thought_bookmark).filter(
user_thought_bookmark.c.user_id == current_user.id).count()
# Berechne tatsächliche Werte für Benutzerstatistiken
contributions_count = Comment.query.filter_by(user_id=current_user.id).count()
# Berechne Verbindungen (Anzahl der Gedankenverknüpfungen)
connections_count = ThoughtRelation.query.filter(
(ThoughtRelation.source_id.in_(
db.session.query(Thought.id).filter_by(user_id=current_user.id)
)) |
(ThoughtRelation.target_id.in_(
db.session.query(Thought.id).filter_by(user_id=current_user.id)
))
).count()
# Berechne durchschnittliche Bewertung der Gedanken des Benutzers
avg_rating = db.session.query(func.avg(ThoughtRating.relevance_score)).join(
Thought, Thought.id == ThoughtRating.thought_id
).filter(Thought.user_id == current_user.id).scalar() or 0
# Hole die Anzahl der Follower (falls implementiert)
# In diesem Beispiel nehmen wir an, dass es keine Follower-Funktionalität gibt
followers_count = 0
# Hole den Standort des Benutzers aus der Datenbank, falls vorhanden
location = "Deutschland" # Standardwert
return render_template('profile.html',
user=current_user,
user_mindmaps=user_mindmaps,
thought_count=thought_count,
bookmark_count=bookmark_count)
bookmark_count=bookmark_count,
connections_count=connections_count,
contributions_count=contributions_count,
followers_count=followers_count,
rating=round(avg_rating, 1),
location=location)
# Route für Benutzereinstellungen
@app.route('/settings', methods=['GET', 'POST'])
@@ -328,33 +597,44 @@ def get_public_mindmap():
return jsonify(result)
def build_category_tree(category):
"""Rekursive Funktion zum Aufbau der Kategoriestruktur."""
nodes = []
# Hole alle Knoten in dieser Kategorie
for node in category.nodes:
if node.is_public:
nodes.append({
'id': node.id,
'name': node.name,
'description': node.description,
'color_code': node.color_code,
'thought_count': len(node.thoughts)
})
"""
Erstellt eine Baumstruktur für eine Kategorie mit all ihren Unterkategorien
und dazugehörigen Knoten
# Rekursiv durch Unterkaterorien
children = []
for child in category.children:
children.append(build_category_tree(child))
return {
Args:
category: Ein Category-Objekt
Returns:
dict: Eine JSON-serialisierbare Darstellung der Kategoriestruktur
"""
# Kategorie-Basisinformationen
category_dict = {
'id': category.id,
'name': category.name,
'description': category.description,
'color_code': category.color_code,
'icon': category.icon,
'nodes': nodes,
'children': children
'nodes': [],
'children': []
}
# Knoten zur Kategorie hinzufügen
if category.nodes:
for node in category.nodes:
category_dict['nodes'].append({
'id': node.id,
'name': node.name,
'description': node.description or '',
'color_code': node.color_code or '#9F7AEA',
'thought_count': len(node.thoughts) if hasattr(node, 'thoughts') else 0
})
# Rekursiv Unterkategorien hinzufügen
if category.children:
for child in category.children:
category_dict['children'].append(build_category_tree(child))
return category_dict
@app.route('/api/mindmap/user/<int:mindmap_id>')
@login_required
@@ -874,17 +1154,21 @@ def bookmark_thought(thought_id):
@app.route('/api/categories')
def get_categories():
"""Liefert alle verfügbaren Kategorien."""
categories = Category.query.all()
return jsonify([{
'id': category.id,
'name': category.name,
'description': category.description,
'color_code': category.color_code,
'icon': category.icon,
'parent_id': category.parent_id
} for category in categories])
"""API-Endpunkt, der alle Kategorien als hierarchische Struktur zurückgibt"""
try:
# Hole alle Kategorien der obersten Ebene
categories = Category.query.filter_by(parent_id=None).all()
# Transformiere zu einer Baumstruktur
category_tree = [build_category_tree(cat) for cat in categories]
return jsonify(category_tree)
except Exception as e:
print(f"Fehler beim Abrufen der Kategorien: {str(e)}")
return jsonify({
'success': False,
'error': 'Kategorien konnten nicht geladen werden'
}), 500
@app.route('/api/set_dark_mode', methods=['POST'])
def set_dark_mode():
@@ -930,7 +1214,7 @@ def too_many_requests(e):
# OpenAI-Integration für KI-Assistenz
@app.route('/api/assistant', methods=['POST'])
def chat_with_assistant():
"""Chatbot-API mit OpenAI Integration."""
"""Chatbot-API mit OpenAI Integration und Datenbankzugriff."""
data = request.json
# Prüfen, ob wir ein einzelnes Prompt oder ein messages-Array haben
@@ -943,9 +1227,9 @@ def chat_with_assistant():
# Extrahiere Systemnachricht falls vorhanden, sonst Standard-Systemnachricht
system_message = next((msg['content'] for msg in messages if msg['role'] == 'system'),
"Du bist ein hilfreicher Assistent, der Menschen dabei hilft, "
"Wissen zu organisieren und zu verknüpfen. Liefere informative, "
"sachliche und gut strukturierte Antworten.")
"Du bist ein hilfreicher Assistent, der Zugriff auf die Wissensdatenbank hat. "
"Du kannst Informationen zu Gedanken, Kategorien und Mindmaps liefern. "
"Antworte informativ, sachlich und gut strukturiert auf Deutsch.")
# Formatiere Nachrichten für OpenAI API
api_messages = [{"role": "system", "content": system_message}]
@@ -966,9 +1250,9 @@ def chat_with_assistant():
# Zusammenfassen mehrerer Gedanken oder Analyse anfordern
system_message = (
"Du bist ein hilfreicher Assistent, der Menschen dabei hilft, "
"Wissen zu organisieren und zu verknüpfen. Liefere informative, "
"sachliche und gut strukturierte Antworten."
"Du bist ein hilfreicher Assistent, der Zugriff auf die Wissensdatenbank hat. "
"Du kannst Informationen zu Gedanken, Kategorien und Mindmaps liefern. "
"Antworte informativ, sachlich und gut strukturiert auf Deutsch."
)
if context:
@@ -979,14 +1263,41 @@ def chat_with_assistant():
{"role": "user", "content": prompt}
]
# Extrahiere die letzte Benutzernachricht für Datenbankabfragen
user_message = next((msg['content'] for msg in reversed(api_messages) if msg['role'] == 'user'), '')
# Prüfen, ob die Anfrage nach Datenbankinformationen sucht
db_context = check_database_query(user_message)
if db_context:
# Erweitere den Kontext mit Datenbankinformationen
api_messages.append({
"role": "system",
"content": f"Hier sind relevante Informationen aus der Datenbank:\n\n{db_context}"
})
try:
# Überprüfen ob OpenAI API-Key konfiguriert ist
if not client.api_key or client.api_key.startswith("sk-dummy"):
print("Warnung: OpenAI API-Key ist nicht oder nur als Dummy konfiguriert!")
return jsonify({
'error': 'Der OpenAI API-Key ist nicht korrekt konfiguriert. Bitte konfigurieren Sie die Umgebungsvariable OPENAI_API_KEY.'
}), 500
# API-Aufruf mit Timeout
import time
start_time = time.time()
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=api_messages,
max_tokens=300,
temperature=0.7
max_tokens=600, # Erhöht für längere, detailliertere Antworten
temperature=0.7,
timeout=20 # 20 Sekunden Timeout
)
print(f"OpenAI API-Antwortzeit: {time.time() - start_time:.2f} Sekunden")
answer = response.choices[0].message.content
# Für das neue Format erwarten wir response statt answer
@@ -995,134 +1306,77 @@ def chat_with_assistant():
})
except Exception as e:
import traceback
print(f"Fehler bei der OpenAI-Anfrage: {str(e)}")
print(traceback.format_exc())
return jsonify({
'error': f'Fehler bei der OpenAI-Anfrage: {str(e)}'
}), 500
# App-Kontext-Funktion für Initialisierung der Datenbank
def create_default_categories():
"""Erstellt die Standard-Kategorien und wissenschaftlichen Bereiche."""
categories = [
{
'name': 'Naturwissenschaften',
'description': 'Empirische Untersuchung und Erklärung natürlicher Phänomene',
'color_code': '#4CAF50',
'icon': 'flask',
'children': [
{
'name': 'Physik',
'description': 'Studium der Materie, Energie und deren Wechselwirkungen',
'color_code': '#81C784',
'icon': 'atom'
},
{
'name': 'Biologie',
'description': 'Wissenschaft des Lebens und lebender Organismen',
'color_code': '#66BB6A',
'icon': 'leaf'
},
{
'name': 'Chemie',
'description': 'Wissenschaft der Materie, ihrer Eigenschaften und Reaktionen',
'color_code': '#A5D6A7',
'icon': 'vial'
}
]
},
{
'name': 'Sozialwissenschaften',
'description': 'Untersuchung von Gesellschaft und menschlichem Verhalten',
'color_code': '#2196F3',
'icon': 'users',
'children': [
{
'name': 'Psychologie',
'description': 'Wissenschaftliches Studium des Geistes und Verhaltens',
'color_code': '#64B5F6',
'icon': 'brain'
},
{
'name': 'Soziologie',
'description': 'Studium sozialer Beziehungen und Institutionen',
'color_code': '#42A5F5',
'icon': 'network-wired'
}
]
},
{
'name': 'Geisteswissenschaften',
'description': 'Studium menschlicher Kultur und Kreativität',
'color_code': '#9C27B0',
'icon': 'book',
'children': [
{
'name': 'Philosophie',
'description': 'Untersuchung grundlegender Fragen über Existenz, Wissen und Ethik',
'color_code': '#BA68C8',
'icon': 'lightbulb'
},
{
'name': 'Geschichte',
'description': 'Studium der Vergangenheit und ihres Einflusses auf die Gegenwart',
'color_code': '#AB47BC',
'icon': 'landmark'
},
{
'name': 'Literatur',
'description': 'Studium literarischer Werke und ihrer Bedeutung',
'color_code': '#CE93D8',
'icon': 'feather'
}
]
},
{
'name': 'Technologie',
'description': 'Anwendung wissenschaftlicher Erkenntnisse für praktische Zwecke',
'color_code': '#FF9800',
'icon': 'microchip',
'children': [
{
'name': 'Informatik',
'description': 'Studium von Computern und Berechnungssystemen',
'color_code': '#FFB74D',
'icon': 'laptop-code'
},
{
'name': 'Künstliche Intelligenz',
'description': 'Entwicklung intelligenter Maschinen und Software',
'color_code': '#FFA726',
'icon': 'robot'
}
]
}
]
def check_database_query(user_message):
"""
Überprüft, ob die Benutzeranfrage nach Datenbankinformationen sucht und extrahiert
relevante Daten aus der Datenbank.
"""
context = []
# Kategorien in die Datenbank einfügen
for category_data in categories:
children_data = category_data.pop('children', [])
category = Category(**category_data)
db.session.add(category)
db.session.flush() # Um die ID zu generieren
# Prüfen auf verschiedene Datenbankabfragemuster
if any(keyword in user_message.lower() for keyword in ['gedanken', 'thought', 'beitrag', 'inhalt']):
# Suche nach relevanten Gedanken
thoughts = Thought.query.filter(
db.or_(
Thought.title.ilike(f'%{word}%') for word in user_message.split()
if len(word) > 3 # Nur längere Wörter zur Suche verwenden
)
).limit(5).all()
# Unterkategorien hinzufügen
for child_data in children_data:
child = Category(**child_data, parent_id=category.id)
db.session.add(child)
db.session.commit()
print("Standard-Kategorien wurden erstellt!")
def initialize_database():
"""Initialisiert die Datenbank, falls sie noch nicht existiert."""
db.create_all()
if thoughts:
context.append("Relevante Gedanken:")
for thought in thoughts:
context.append(f"- Titel: {thought.title}")
context.append(f" Zusammenfassung: {thought.abstract if thought.abstract else 'Keine Zusammenfassung verfügbar'}")
context.append(f" Keywords: {thought.keywords if thought.keywords else 'Keine Keywords verfügbar'}")
context.append("")
# Überprüfe, ob bereits Kategorien existieren
if Category.query.count() == 0:
create_default_categories()
# Führe die Datenbankinitialisierung beim Starten der App aus
with app.app_context():
initialize_database()
if any(keyword in user_message.lower() for keyword in ['kategorie', 'category', 'themengebiet', 'bereich']):
# Suche nach Kategorien
categories = Category.query.filter(
db.or_(
Category.name.ilike(f'%{word}%') for word in user_message.split()
if len(word) > 3
)
).limit(5).all()
if categories:
context.append("Relevante Kategorien:")
for category in categories:
context.append(f"- Name: {category.name}")
context.append(f" Beschreibung: {category.description}")
context.append("")
if any(keyword in user_message.lower() for keyword in ['mindmap', 'karte', 'visualisierung']):
# Suche nach öffentlichen Mindmaps
mindmap_nodes = MindMapNode.query.filter(
db.and_(
MindMapNode.is_public == True,
db.or_(
MindMapNode.name.ilike(f'%{word}%') for word in user_message.split()
if len(word) > 3
)
)
).limit(5).all()
if mindmap_nodes:
context.append("Relevante Mindmap-Knoten:")
for node in mindmap_nodes:
context.append(f"- Name: {node.name}")
context.append(f" Beschreibung: {node.description if node.description else 'Keine Beschreibung verfügbar'}")
if node.category:
context.append(f" Kategorie: {node.category.name}")
context.append("")
return "\n".join(context) if context else ""
@app.route('/search')
def search_thoughts_page():
@@ -1178,4 +1432,51 @@ if __name__ == '__main__':
with app.app_context():
# Make sure tables exist
db.create_all()
app.run(host="0.0.0.0", debug=True)
app.run(host="0.0.0.0", debug=True)
@app.route('/api/refresh-mindmap')
def refresh_mindmap():
"""
API-Endpunkt zum Neuladen der Mindmap-Daten,
wenn die Datenbank-Verbindung vorübergehend fehlgeschlagen ist
"""
try:
# Stelle sicher, dass wir Kategorien haben
if Category.query.count() == 0:
create_default_categories()
# Hole alle Kategorien und Knoten
categories = Category.query.filter_by(parent_id=None).all()
category_tree = [build_category_tree(cat) for cat in categories]
# Hole alle Mindmap-Knoten
nodes = MindMapNode.query.all()
node_data = []
for node in nodes:
node_obj = {
'id': node.id,
'name': node.name,
'description': node.description or '',
'color_code': node.color_code or '#9F7AEA',
'thought_count': len(node.thoughts),
'category_id': node.category_id
}
# Verbindungen hinzufügen
node_obj['connections'] = [{'target': child.id} for child in node.children]
node_data.append(node_obj)
return jsonify({
'success': True,
'categories': category_tree,
'nodes': node_data
})
except Exception as e:
print(f"Fehler beim Neuladen der Mindmap: {str(e)}")
return jsonify({
'success': False,
'error': 'Datenbankverbindung konnte nicht hergestellt werden'
}), 500