Compare commits
7 Commits
256d38e140
...
fab8d10f03
| Author | SHA1 | Date | |
|---|---|---|---|
| fab8d10f03 | |||
| dec30e4681 | |||
| a1bd999c6a | |||
| b1d33ce643 | |||
| 293f877017 | |||
| e86d0b0f90 | |||
| 059fd167d6 |
Binary file not shown.
Binary file not shown.
861
app.py
861
app.py
@@ -26,6 +26,7 @@ import sqlalchemy
|
|||||||
import ssl
|
import ssl
|
||||||
import certifi
|
import certifi
|
||||||
import os
|
import os
|
||||||
|
from sqlalchemy.orm import joinedload
|
||||||
|
|
||||||
# Modelle importieren
|
# Modelle importieren
|
||||||
from models import (
|
from models import (
|
||||||
@@ -277,7 +278,7 @@ def create_default_categories():
|
|||||||
"icon": "fa-palette",
|
"icon": "fa-palette",
|
||||||
"subcategories": [
|
"subcategories": [
|
||||||
{"name": "Literatur", "description": "Schriftliche Werke", "icon": "fa-book"},
|
{"name": "Literatur", "description": "Schriftliche Werke", "icon": "fa-book"},
|
||||||
{"name": "Musik", "description": "Klangkunst", "icon": "fa-music"},
|
{"name": "Musik", "description": "Klangkunst", "icon": "fa-musik"},
|
||||||
{"name": "Bildende Kunst", "description": "Visuelle Kunstformen", "icon": "fa-paint-brush"}
|
{"name": "Bildende Kunst", "description": "Visuelle Kunstformen", "icon": "fa-paint-brush"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -2342,750 +2343,184 @@ if __name__ == '__main__':
|
|||||||
db.create_all()
|
db.create_all()
|
||||||
socketio.run(app, debug=True, host='0.0.0.0')
|
socketio.run(app, debug=True, host='0.0.0.0')
|
||||||
|
|
||||||
@app.route('/api/refresh-mindmap')
|
def get_category_mindmap_data(category_name):
|
||||||
def refresh_mindmap():
|
"""Generische Funktion zum Abrufen der Mindmap-Daten für eine Kategorie."""
|
||||||
"""
|
|
||||||
API-Endpunkt zum Neuladen der Mindmap-Daten,
|
|
||||||
wenn die Datenbank-Verbindung vorübergehend fehlgeschlagen ist
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
# Stelle sicher, dass wir Kategorien haben
|
# Kategorie mit allen Unterkategorien in einer Abfrage laden
|
||||||
if Category.query.count() == 0:
|
category = Category.query.filter_by(name=category_name).options(
|
||||||
create_default_categories()
|
joinedload(Category.children)
|
||||||
|
).first_or_404()
|
||||||
|
|
||||||
# Überprüfe, ob wir bereits einen "Wissen"-Knoten haben
|
# Basis-Knoten erstellen
|
||||||
wissen_node = MindMapNode.query.filter_by(name="Wissen").first()
|
nodes = [{
|
||||||
|
'id': f'cat_{category.id}',
|
||||||
|
'name': category.name,
|
||||||
|
'description': category.description or '',
|
||||||
|
'color_code': category.color_code or '#9F7AEA',
|
||||||
|
'is_center': True,
|
||||||
|
'has_children': bool(category.children),
|
||||||
|
'icon': category.icon or 'fa-solid fa-circle'
|
||||||
|
}]
|
||||||
|
|
||||||
# Wenn kein "Wissen"-Knoten existiert, erstelle ihn
|
# Unterkategorien hinzufügen
|
||||||
if not wissen_node:
|
for subcat in category.children:
|
||||||
wissen_node = MindMapNode(
|
nodes.append({
|
||||||
name="Wissen",
|
'id': f'cat_{subcat.id}',
|
||||||
description="Zentrale Wissensbasis",
|
'name': subcat.name,
|
||||||
color_code="#4299E1",
|
'description': subcat.description or '',
|
||||||
is_public=True
|
'color_code': subcat.color_code or '#9F7AEA',
|
||||||
)
|
'category': category_name,
|
||||||
db.session.add(wissen_node)
|
'has_children': bool(subcat.children),
|
||||||
db.session.commit()
|
'icon': subcat.icon or 'fa-solid fa-circle'
|
||||||
|
})
|
||||||
|
|
||||||
# Hole alle Kategorien und Knoten
|
# Kanten erstellen (vereinheitlichte Schlüssel)
|
||||||
categories = Category.query.filter_by(parent_id=None).all()
|
edges = [{
|
||||||
category_tree = [build_category_tree(cat) for cat in categories]
|
'source': f'cat_{category.id}',
|
||||||
|
'target': f'cat_{subcat.id}',
|
||||||
# Hole alle Mindmap-Knoten außer dem "Wissen"-Knoten
|
'strength': 0.8
|
||||||
nodes = MindMapNode.query.filter(MindMapNode.id != wissen_node.id).all()
|
} for subcat in category.children]
|
||||||
|
|
||||||
# Vorbereiten der Node- und Edge-Arrays für die Antwort
|
|
||||||
node_data = []
|
|
||||||
edge_data = []
|
|
||||||
|
|
||||||
# Zuerst den "Wissen"-Knoten hinzufügen
|
|
||||||
node_data.append({
|
|
||||||
'id': wissen_node.id,
|
|
||||||
'name': wissen_node.name,
|
|
||||||
'description': wissen_node.description or '',
|
|
||||||
'color_code': wissen_node.color_code or '#4299E1',
|
|
||||||
'thought_count': len(wissen_node.thoughts),
|
|
||||||
'category_id': wissen_node.category_id
|
|
||||||
})
|
|
||||||
|
|
||||||
# Dann die anderen Knoten
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
# Verbinde alle Top-Level-Knoten mit dem Wissen-Knoten
|
|
||||||
if not node.parents.all():
|
|
||||||
edge_data.append({
|
|
||||||
'source': wissen_node.id,
|
|
||||||
'target': node.id
|
|
||||||
})
|
|
||||||
|
|
||||||
# Verbindungen zwischen vorhandenen Knoten hinzufügen
|
|
||||||
node_children = node.children.all()
|
|
||||||
for child in node_children:
|
|
||||||
edge_data.append({
|
|
||||||
'source': node.id,
|
|
||||||
'target': child.id
|
|
||||||
})
|
|
||||||
|
|
||||||
node_data.append(node_obj)
|
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': True,
|
'success': True,
|
||||||
'categories': category_tree,
|
'nodes': nodes,
|
||||||
'nodes': node_data,
|
'edges': edges
|
||||||
'edges': edge_data
|
|
||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Fehler beim Neuladen der Mindmap: {str(e)}")
|
print(f"Fehler beim Abrufen der {category_name}-Mindmap: {str(e)}")
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'success': False,
|
'success': False,
|
||||||
'error': 'Datenbankverbindung konnte nicht hergestellt werden'
|
'error': f'{category_name}-Mindmap konnte nicht geladen werden',
|
||||||
|
'details': str(e)
|
||||||
}), 500
|
}), 500
|
||||||
|
|
||||||
# Die Route '/mindmap' wird bereits in Zeile 474 definiert - doppelte Definition entfernt
|
@app.route('/api/mindmap/root')
|
||||||
|
def get_root_mindmap_data():
|
||||||
# Weiterleitung für Community/Forum-Routen
|
"""Liefert die Daten für die Root-Mindmap."""
|
||||||
@app.route('/community')
|
|
||||||
@app.route('/Community')
|
|
||||||
@app.route('/forum')
|
|
||||||
@app.route('/Forum')
|
|
||||||
@app.route('/community_forum')
|
|
||||||
def redirect_community():
|
|
||||||
"""Leitet alle Community/Forum-URLs zur Startseite um"""
|
|
||||||
return redirect(url_for('index'))
|
|
||||||
|
|
||||||
@app.route('/static/js/mindmap-init.js')
|
|
||||||
def serve_mindmap_init_js():
|
|
||||||
"""Bedient die Mindmap-Initialisierungsdatei."""
|
|
||||||
return app.send_static_file('js/mindmap-init.js'), 200, {'Content-Type': 'application/javascript'}
|
|
||||||
|
|
||||||
# Datenbank-Update-Route (admin-geschützt)
|
|
||||||
@app.route('/admin/update-database', methods=['GET', 'POST'])
|
|
||||||
@admin_required
|
|
||||||
def admin_update_database():
|
|
||||||
"""Admin-Route zum Aktualisieren der Datenbank"""
|
|
||||||
message = None
|
|
||||||
success = None
|
|
||||||
|
|
||||||
if request.method == 'POST':
|
|
||||||
try:
|
|
||||||
import utils.update_db as update_db
|
|
||||||
update_success = update_db.update_user_table()
|
|
||||||
if update_success:
|
|
||||||
message = "Die Datenbank wurde erfolgreich aktualisiert."
|
|
||||||
success = True
|
|
||||||
else:
|
|
||||||
message = "Es gab ein Problem bei der Aktualisierung der Datenbank."
|
|
||||||
success = False
|
|
||||||
except Exception as e:
|
|
||||||
message = f"Fehler: {str(e)}"
|
|
||||||
success = False
|
|
||||||
|
|
||||||
return render_template('admin/update_database.html', message=message, success=success)
|
|
||||||
|
|
||||||
@app.route('/api/mindmap/<node_id>')
|
|
||||||
def get_mindmap_data(node_id):
|
|
||||||
"""
|
|
||||||
Stellt Mindmap-Daten für das Frontend bereit.
|
|
||||||
Liefert für 'root' die Hauptebene und für andere Node-IDs die entsprechenden Unterknoten.
|
|
||||||
Daten werden aus der Datenbank abgerufen.
|
|
||||||
"""
|
|
||||||
app.logger.info(f"Mindmap-Daten werden für Node '{node_id}' angefordert.")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Fallback-Daten falls Datenbankzugriff fehlschlägt
|
# Hauptkategorien mit Unterkategorien in einer Abfrage laden
|
||||||
fallback_data = get_fallback_mindmap_data(node_id)
|
categories = Category.query.filter_by(parent_id=None).options(
|
||||||
|
joinedload(Category.children)
|
||||||
|
).all()
|
||||||
|
|
||||||
if node_id == 'root':
|
# Basis-Knoten erstellen
|
||||||
# Hauptebene der Mindmap - finde den "Wissen"-Knoten und seine Verbindungen
|
nodes = [{
|
||||||
wissen_node = MindMapNode.query.filter_by(name="Wissen").first()
|
'id': 'root',
|
||||||
|
'name': 'Wissen',
|
||||||
|
'description': 'Zentrale Wissensbasis',
|
||||||
|
'color_code': '#4299E1',
|
||||||
|
'is_center': True,
|
||||||
|
'has_children': bool(categories),
|
||||||
|
'icon': 'fa-solid fa-circle'
|
||||||
|
}]
|
||||||
|
|
||||||
if not wissen_node:
|
# Kategorien als Knoten hinzufügen
|
||||||
app.logger.warning("'Wissen'-Knoten nicht in der Datenbank gefunden, Fallback zu Hardcoded-Daten.")
|
for category in categories:
|
||||||
return jsonify(fallback_data)
|
nodes.append({
|
||||||
|
'id': f'cat_{category.id}',
|
||||||
|
'name': category.name,
|
||||||
|
'description': category.description or '',
|
||||||
|
'color_code': category.color_code or '#9F7AEA',
|
||||||
|
'category': category.name,
|
||||||
|
'has_children': bool(category.children),
|
||||||
|
'icon': category.icon or 'fa-solid fa-circle'
|
||||||
|
})
|
||||||
|
|
||||||
# Zentrum der Mindmap ist der "Wissen"-Knoten
|
# Kanten erstellen (vereinheitlichte Schlüssel)
|
||||||
nodes = [{
|
edges = [{
|
||||||
"id": str(wissen_node.id),
|
'source': 'root',
|
||||||
"name": "Wissenskarte", # Frontend-Name für Root-Knoten
|
'target': f'cat_{category.id}',
|
||||||
"description": wissen_node.description or "Zentrale Wissenskarte mit allen Hauptthemen",
|
'strength': 0.8
|
||||||
"is_center": True,
|
} for category in categories]
|
||||||
"color_code": "#f5f5f5",
|
|
||||||
"has_children": True
|
|
||||||
}]
|
|
||||||
|
|
||||||
# Hauptkategorien als Knoten
|
|
||||||
main_categories = Category.query.filter_by(parent_id=None).all()
|
|
||||||
category_nodes = []
|
|
||||||
edges = []
|
|
||||||
|
|
||||||
for category in main_categories:
|
|
||||||
category_node = {
|
|
||||||
"id": f"cat_{category.id}",
|
|
||||||
"name": category.name,
|
|
||||||
"description": category.description or f"Kategorie: {category.name}",
|
|
||||||
"category": category.name,
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": category.color_code or "#9F7AEA"
|
|
||||||
}
|
|
||||||
category_nodes.append(category_node)
|
|
||||||
|
|
||||||
# Verbindung vom Wissen-Knoten zur Kategorie
|
|
||||||
edges.append({
|
|
||||||
"source_id": str(wissen_node.id),
|
|
||||||
"target_id": f"cat_{category.id}",
|
|
||||||
"strength": 0.9
|
|
||||||
})
|
|
||||||
|
|
||||||
nodes.extend(category_nodes)
|
|
||||||
|
|
||||||
response = {
|
|
||||||
"nodes": nodes,
|
|
||||||
"edges": edges
|
|
||||||
}
|
|
||||||
|
|
||||||
app.logger.info(f"Mindmap-Daten für 'root' erfolgreich aus Datenbank geladen. {len(nodes)} Knoten gefunden.")
|
|
||||||
return jsonify(response)
|
|
||||||
|
|
||||||
elif node_id.startswith('cat_'):
|
|
||||||
# Unterkategorien einer Hauptkategorie anzeigen
|
|
||||||
category_id = node_id.replace('cat_', '')
|
|
||||||
try:
|
|
||||||
category_id = int(category_id)
|
|
||||||
category = Category.query.get(category_id)
|
|
||||||
|
|
||||||
if not category:
|
|
||||||
app.logger.warning(f"Kategorie mit ID {category_id} nicht gefunden.")
|
|
||||||
return jsonify(fallback_data)
|
|
||||||
|
|
||||||
nodes = []
|
|
||||||
edges = []
|
|
||||||
|
|
||||||
# Unterkategorien dieser Kategorie
|
|
||||||
subcategories = Category.query.filter_by(parent_id=category_id).all()
|
|
||||||
|
|
||||||
for subcat in subcategories:
|
|
||||||
subcat_node = {
|
|
||||||
"id": f"subcat_{subcat.id}",
|
|
||||||
"name": subcat.name,
|
|
||||||
"description": subcat.description or f"Unterkategorie: {subcat.name}",
|
|
||||||
"category": category.name,
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": subcat.color_code or category.color_code
|
|
||||||
}
|
|
||||||
nodes.append(subcat_node)
|
|
||||||
|
|
||||||
# Verbindung von der Hauptkategorie zur Unterkategorie
|
|
||||||
edges.append({
|
|
||||||
"source_id": node_id,
|
|
||||||
"target_id": f"subcat_{subcat.id}",
|
|
||||||
"strength": 0.8
|
|
||||||
})
|
|
||||||
|
|
||||||
# Wenn es keine Unterkategorien gibt, zeige verwandte Knoten
|
|
||||||
if not subcategories:
|
|
||||||
# Im Fallback-Modus zurückfallen
|
|
||||||
app.logger.info(f"Keine Unterkategorien für Kategorie {category.name} gefunden, verwende Fallback-Daten.")
|
|
||||||
return jsonify(fallback_data)
|
|
||||||
|
|
||||||
response = {
|
|
||||||
"nodes": nodes,
|
|
||||||
"edges": edges
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonify(response)
|
|
||||||
|
|
||||||
except (ValueError, TypeError):
|
|
||||||
app.logger.error(f"Ungültige Kategorie-ID: {category_id}")
|
|
||||||
return jsonify(fallback_data)
|
|
||||||
|
|
||||||
else:
|
|
||||||
# Versuche, einen MindMapNode mit der gegebenen ID zu finden
|
|
||||||
try:
|
|
||||||
node_id_int = int(node_id)
|
|
||||||
node = MindMapNode.query.get(node_id_int)
|
|
||||||
|
|
||||||
if node:
|
|
||||||
# Zeige Gedanken oder verwandte Knoten
|
|
||||||
# Hier würden wir verwandte Knoten aus der Datenbank laden
|
|
||||||
# Für den Moment verwenden wir Fallback-Daten
|
|
||||||
app.logger.info(f"Knoten {node.name} gefunden, aber keine spezifische Verarbeitung implementiert.")
|
|
||||||
return jsonify(fallback_data)
|
|
||||||
|
|
||||||
except (ValueError, TypeError):
|
|
||||||
# Wenn die ID kein Integer ist, verwende das bisherige Verhalten
|
|
||||||
app.logger.info(f"Falle auf bisheriges Verhalten für Node ID '{node_id}' zurück.")
|
|
||||||
return jsonify(fallback_data)
|
|
||||||
|
|
||||||
# Wenn wir hier ankommen, gibt es keine spezifische Verarbeitung für diese node_id
|
|
||||||
return jsonify(fallback_data)
|
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'success': True,
|
||||||
|
'nodes': nodes,
|
||||||
|
'edges': edges
|
||||||
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
app.logger.error(f"Fehler beim Abrufen der Mindmap-Daten: {str(e)}")
|
print(f"Fehler beim Abrufen der Root-Mindmap: {str(e)}")
|
||||||
app.logger.error(f"Stack Trace: {traceback.format_exc()}")
|
return jsonify({
|
||||||
return jsonify({"error": "Fehler beim Abrufen der Mindmap-Daten"}), 500
|
'success': False,
|
||||||
|
'error': 'Root-Mindmap konnte nicht geladen werden',
|
||||||
|
'details': str(e)
|
||||||
|
}), 500
|
||||||
|
|
||||||
|
# Spezifische Routen für Kategorien
|
||||||
def get_fallback_mindmap_data(node_id):
|
@app.route('/api/mindmap/philosophy')
|
||||||
"""
|
def get_philosophy_mindmap_data():
|
||||||
Liefert Fallback-Daten für die Mindmap, wenn die Datenbank nicht verfügbar ist.
|
return get_category_mindmap_data('Philosophie')
|
||||||
"""
|
|
||||||
if node_id == 'root':
|
|
||||||
# Hauptebene der Mindmap
|
|
||||||
nodes = [
|
|
||||||
{
|
|
||||||
"id": "center",
|
|
||||||
"name": "Wissenskarte",
|
|
||||||
"description": "Zentrale Wissenskarte mit allen Hauptthemen",
|
|
||||||
"is_center": True,
|
|
||||||
"color_code": "#f5f5f5",
|
|
||||||
"has_children": True
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "philosophy",
|
|
||||||
"name": "Philosophie",
|
|
||||||
"description": "Die Lehre vom Denken und der Erkenntnis",
|
|
||||||
"category": "Philosophie",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#9F7AEA"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "science",
|
|
||||||
"name": "Wissenschaft",
|
|
||||||
"description": "Systematische Erforschung der Natur und Gesellschaft",
|
|
||||||
"category": "Wissenschaft",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#f4b400"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "technology",
|
|
||||||
"name": "Technologie",
|
|
||||||
"description": "Anwendung wissenschaftlicher Erkenntnisse",
|
|
||||||
"category": "Technologie",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#0d47a1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "arts",
|
|
||||||
"name": "Künste",
|
|
||||||
"description": "Kreativer Ausdruck und künstlerische Gestaltung",
|
|
||||||
"category": "Künste",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#c2185b"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
edges = [
|
|
||||||
{"source_id": "center", "target_id": "philosophy", "strength": 0.9},
|
|
||||||
{"source_id": "center", "target_id": "science", "strength": 0.9},
|
|
||||||
{"source_id": "center", "target_id": "technology", "strength": 0.9},
|
|
||||||
{"source_id": "center", "target_id": "arts", "strength": 0.9}
|
|
||||||
]
|
|
||||||
|
|
||||||
elif node_id == 'philosophy':
|
|
||||||
nodes = [
|
|
||||||
{
|
|
||||||
"id": "epistemology",
|
|
||||||
"name": "Erkenntnistheorie",
|
|
||||||
"description": "Untersuchung der Natur und Grenzen menschlicher Erkenntnis",
|
|
||||||
"category": "Philosophie",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#9F7AEA"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "ethics",
|
|
||||||
"name": "Ethik",
|
|
||||||
"description": "Lehre vom moralisch richtigen Handeln",
|
|
||||||
"category": "Philosophie",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#9F7AEA"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "metaphysics",
|
|
||||||
"name": "Metaphysik",
|
|
||||||
"description": "Grundfragen des Seins und der Wirklichkeit",
|
|
||||||
"category": "Philosophie",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#9F7AEA"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
edges = [
|
|
||||||
{"source_id": "philosophy", "target_id": "epistemology", "strength": 0.8},
|
|
||||||
{"source_id": "philosophy", "target_id": "ethics", "strength": 0.8},
|
|
||||||
{"source_id": "philosophy", "target_id": "metaphysics", "strength": 0.8}
|
|
||||||
]
|
|
||||||
|
|
||||||
elif node_id == 'science':
|
|
||||||
nodes = [
|
|
||||||
{
|
|
||||||
"id": "physics",
|
|
||||||
"name": "Physik",
|
|
||||||
"description": "Lehre von der Materie und ihren Wechselwirkungen",
|
|
||||||
"category": "Wissenschaft",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#f4b400"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "biology",
|
|
||||||
"name": "Biologie",
|
|
||||||
"description": "Lehre von den Lebewesen und ihren Lebensprozessen",
|
|
||||||
"category": "Wissenschaft",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#f4b400"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "chemistry",
|
|
||||||
"name": "Chemie",
|
|
||||||
"description": "Wissenschaft von den Stoffen und ihren Reaktionen",
|
|
||||||
"category": "Wissenschaft",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#f4b400"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
edges = [
|
|
||||||
{"source_id": "science", "target_id": "physics", "strength": 0.8},
|
|
||||||
{"source_id": "science", "target_id": "biology", "strength": 0.8},
|
|
||||||
{"source_id": "science", "target_id": "chemistry", "strength": 0.8}
|
|
||||||
]
|
|
||||||
|
|
||||||
elif node_id == 'technology':
|
|
||||||
nodes = [
|
|
||||||
{
|
|
||||||
"id": "ai",
|
|
||||||
"name": "Künstliche Intelligenz",
|
|
||||||
"description": "Maschinelles Lernen und intelligente Systeme",
|
|
||||||
"category": "Technologie",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#0d47a1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "robotics",
|
|
||||||
"name": "Robotik",
|
|
||||||
"description": "Entwicklung und Steuerung von Robotern",
|
|
||||||
"category": "Technologie",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#0d47a1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "quantum_computing",
|
|
||||||
"name": "Quantencomputing",
|
|
||||||
"description": "Computer basierend auf Quantenmechanik",
|
|
||||||
"category": "Technologie",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#0d47a1"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
edges = [
|
|
||||||
{"source_id": "technology", "target_id": "ai", "strength": 0.8},
|
|
||||||
{"source_id": "technology", "target_id": "robotics", "strength": 0.8},
|
|
||||||
{"source_id": "technology", "target_id": "quantum_computing", "strength": 0.8}
|
|
||||||
]
|
|
||||||
|
|
||||||
elif node_id == 'arts':
|
|
||||||
nodes = [
|
|
||||||
{
|
|
||||||
"id": "visual_arts",
|
|
||||||
"name": "Bildende Kunst",
|
|
||||||
"description": "Malerei, Bildhauerei und andere visuelle Kunstformen",
|
|
||||||
"category": "Künste",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#c2185b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "music",
|
|
||||||
"name": "Musik",
|
|
||||||
"description": "Tonkunst und musikalische Komposition",
|
|
||||||
"category": "Künste",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#c2185b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "literature",
|
|
||||||
"name": "Literatur",
|
|
||||||
"description": "Schriftliche Kunstwerke und Poesie",
|
|
||||||
"category": "Künste",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#c2185b"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
edges = [
|
|
||||||
{"source_id": "arts", "target_id": "visual_arts", "strength": 0.8},
|
|
||||||
{"source_id": "arts", "target_id": "music", "strength": 0.8},
|
|
||||||
{"source_id": "arts", "target_id": "literature", "strength": 0.8}
|
|
||||||
]
|
|
||||||
|
|
||||||
else:
|
|
||||||
# Für jede andere Node-ID geben wir eine leere Struktur zurück
|
|
||||||
nodes = []
|
|
||||||
edges = []
|
|
||||||
|
|
||||||
# Antwort zusammenstellen
|
|
||||||
return {
|
|
||||||
"nodes": nodes,
|
|
||||||
"edges": edges
|
|
||||||
}
|
|
||||||
|
|
||||||
# Diese Routen wurden an den Anfang der Datei verschoben und werden nicht mehr benötigt
|
|
||||||
|
|
||||||
@app.route('/api/mindmap/science')
|
@app.route('/api/mindmap/science')
|
||||||
def get_science_mindmap():
|
def get_science_mindmap_data():
|
||||||
"""
|
return get_category_mindmap_data('Wissenschaft')
|
||||||
Gibt die Wissenschafts-Mindmap zurück für Frontend-Kompatibilität.
|
|
||||||
"""
|
|
||||||
app.logger.info("Wissenschafts-Mindmap-Daten werden angefordert.")
|
|
||||||
|
|
||||||
try:
|
|
||||||
nodes = [
|
|
||||||
{
|
|
||||||
"id": "physics",
|
|
||||||
"name": "Physik",
|
|
||||||
"description": "Lehre von der Materie und ihren Wechselwirkungen",
|
|
||||||
"category": "Wissenschaft",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#f4b400"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "biology",
|
|
||||||
"name": "Biologie",
|
|
||||||
"description": "Lehre von den Lebewesen und ihren Lebensprozessen",
|
|
||||||
"category": "Wissenschaft",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#f4b400"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "chemistry",
|
|
||||||
"name": "Chemie",
|
|
||||||
"description": "Wissenschaft von den Stoffen und ihren Reaktionen",
|
|
||||||
"category": "Wissenschaft",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#f4b400"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
edges = [
|
|
||||||
{"source_id": "science", "target_id": "physics", "strength": 0.8},
|
|
||||||
{"source_id": "science", "target_id": "biology", "strength": 0.8},
|
|
||||||
{"source_id": "science", "target_id": "chemistry", "strength": 0.8}
|
|
||||||
]
|
|
||||||
|
|
||||||
return jsonify({
|
|
||||||
"nodes": nodes,
|
|
||||||
"edges": edges
|
|
||||||
})
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
app.logger.error(f"Fehler beim Abrufen der Wissenschafts-Mindmap-Daten: {str(e)}")
|
|
||||||
return jsonify({"error": "Fehler beim Abrufen der Mindmap-Daten"}), 500
|
|
||||||
|
|
||||||
@app.route('/api/mindmap/root')
|
|
||||||
def get_root_mindmap():
|
|
||||||
"""
|
|
||||||
Gibt die Root-Mindmap zurück, um Kompatibilität mit dem Frontend sicherzustellen.
|
|
||||||
"""
|
|
||||||
app.logger.info("Root-Mindmap-Daten werden angefordert.")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Fallback-Daten
|
|
||||||
nodes = [
|
|
||||||
{
|
|
||||||
"id": "center",
|
|
||||||
"name": "Wissenskarte",
|
|
||||||
"description": "Zentrale Wissenskarte mit allen Hauptthemen",
|
|
||||||
"is_center": True,
|
|
||||||
"color_code": "#f5f5f5",
|
|
||||||
"has_children": True
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "philosophy",
|
|
||||||
"name": "Philosophie",
|
|
||||||
"description": "Die Lehre vom Denken und der Erkenntnis",
|
|
||||||
"category": "Philosophie",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#9F7AEA"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "science",
|
|
||||||
"name": "Wissenschaft",
|
|
||||||
"description": "Systematische Erforschung der Natur und Gesellschaft",
|
|
||||||
"category": "Wissenschaft",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#f4b400"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "technology",
|
|
||||||
"name": "Technologie",
|
|
||||||
"description": "Anwendung wissenschaftlicher Erkenntnisse",
|
|
||||||
"category": "Technologie",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#0d47a1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "arts",
|
|
||||||
"name": "Künste",
|
|
||||||
"description": "Kreativer Ausdruck und künstlerische Gestaltung",
|
|
||||||
"category": "Künste",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#c2185b"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
edges = [
|
|
||||||
{"source_id": "center", "target_id": "philosophy", "strength": 0.9},
|
|
||||||
{"source_id": "center", "target_id": "science", "strength": 0.9},
|
|
||||||
{"source_id": "center", "target_id": "technology", "strength": 0.9},
|
|
||||||
{"source_id": "center", "target_id": "arts", "strength": 0.9}
|
|
||||||
]
|
|
||||||
|
|
||||||
response = {
|
|
||||||
"nodes": nodes,
|
|
||||||
"edges": edges
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonify(response)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
app.logger.error(f"Fehler beim Abrufen der Root-Mindmap-Daten: {str(e)}")
|
|
||||||
app.logger.error(f"Stack Trace: {traceback.format_exc()}")
|
|
||||||
return jsonify({"error": "Fehler beim Abrufen der Mindmap-Daten"}), 500
|
|
||||||
|
|
||||||
@app.route('/api/mindmap/technology')
|
@app.route('/api/mindmap/technology')
|
||||||
def get_technology_mindmap():
|
def get_technology_mindmap_data():
|
||||||
"""
|
return get_category_mindmap_data('Technologie')
|
||||||
Gibt die Technologie-Mindmap zurück für Frontend-Kompatibilität.
|
|
||||||
"""
|
|
||||||
app.logger.info("Technologie-Mindmap-Daten werden angefordert.")
|
|
||||||
|
|
||||||
try:
|
|
||||||
nodes = [
|
|
||||||
{
|
|
||||||
"id": "ai",
|
|
||||||
"name": "Künstliche Intelligenz",
|
|
||||||
"description": "Maschinelles Lernen und intelligente Systeme",
|
|
||||||
"category": "Technologie",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#0d47a1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "robotics",
|
|
||||||
"name": "Robotik",
|
|
||||||
"description": "Entwicklung und Steuerung von Robotern",
|
|
||||||
"category": "Technologie",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#0d47a1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "quantum_computing",
|
|
||||||
"name": "Quantencomputing",
|
|
||||||
"description": "Computer basierend auf Quantenmechanik",
|
|
||||||
"category": "Technologie",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#0d47a1"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
edges = [
|
|
||||||
{"source_id": "technology", "target_id": "ai", "strength": 0.8},
|
|
||||||
{"source_id": "technology", "target_id": "robotics", "strength": 0.8},
|
|
||||||
{"source_id": "technology", "target_id": "quantum_computing", "strength": 0.8}
|
|
||||||
]
|
|
||||||
|
|
||||||
return jsonify({
|
|
||||||
"nodes": nodes,
|
|
||||||
"edges": edges
|
|
||||||
})
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
app.logger.error(f"Fehler beim Abrufen der Technologie-Mindmap-Daten: {str(e)}")
|
|
||||||
return jsonify({"error": "Fehler beim Abrufen der Mindmap-Daten"}), 500
|
|
||||||
|
|
||||||
@app.route('/api/mindmap/arts')
|
@app.route('/api/mindmap/arts')
|
||||||
def get_arts_mindmap():
|
def get_arts_mindmap_data():
|
||||||
"""
|
return get_category_mindmap_data('Künste')
|
||||||
Gibt die Künste-Mindmap zurück für Frontend-Kompatibilität.
|
|
||||||
"""
|
|
||||||
app.logger.info("Künste-Mindmap-Daten werden angefordert.")
|
|
||||||
|
|
||||||
|
# Generische Route für spezifische Knoten
|
||||||
|
@app.route('/api/mindmap/<node_id>')
|
||||||
|
def get_mindmap_data(node_id):
|
||||||
|
"""Liefert die Daten für einen spezifischen Mindmap-Knoten."""
|
||||||
try:
|
try:
|
||||||
nodes = [
|
# Prüfen, ob es sich um eine spezielle Route handelt
|
||||||
{
|
if node_id in ['root', 'philosophy', 'science', 'technology', 'arts']:
|
||||||
"id": "visual_arts",
|
return jsonify({
|
||||||
"name": "Bildende Kunst",
|
'success': False,
|
||||||
"description": "Malerei, Bildhauerei und andere visuelle Kunstformen",
|
'error': 'Ungültige Knoten-ID',
|
||||||
"category": "Künste",
|
'details': 'Diese ID ist für spezielle Routen reserviert'
|
||||||
"has_children": True,
|
}), 400
|
||||||
"color_code": "#c2185b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "music",
|
|
||||||
"name": "Musik",
|
|
||||||
"description": "Tonkunst und musikalische Komposition",
|
|
||||||
"category": "Künste",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#c2185b"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "literature",
|
|
||||||
"name": "Literatur",
|
|
||||||
"description": "Schriftliche Kunstwerke und Poesie",
|
|
||||||
"category": "Künste",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#c2185b"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
edges = [
|
# Knoten mit Unterknoten in einer Abfrage laden
|
||||||
{"source_id": "arts", "target_id": "visual_arts", "strength": 0.8},
|
node = MindMapNode.query.options(
|
||||||
{"source_id": "arts", "target_id": "music", "strength": 0.8},
|
joinedload(MindMapNode.children)
|
||||||
{"source_id": "arts", "target_id": "literature", "strength": 0.8}
|
).get_or_404(node_id)
|
||||||
]
|
|
||||||
|
# Basis-Knoten erstellen
|
||||||
|
nodes = [{
|
||||||
|
'id': str(node.id),
|
||||||
|
'name': node.name,
|
||||||
|
'description': node.description or '',
|
||||||
|
'color_code': node.color_code or '#9F7AEA',
|
||||||
|
'is_center': True,
|
||||||
|
'has_children': bool(node.children),
|
||||||
|
'icon': node.icon or 'fa-solid fa-circle'
|
||||||
|
}]
|
||||||
|
|
||||||
|
# Unterknoten hinzufügen
|
||||||
|
for child in node.children:
|
||||||
|
nodes.append({
|
||||||
|
'id': str(child.id),
|
||||||
|
'name': child.name,
|
||||||
|
'description': child.description or '',
|
||||||
|
'color_code': child.color_code or '#9F7AEA',
|
||||||
|
'category': node.name,
|
||||||
|
'has_children': bool(child.children),
|
||||||
|
'icon': child.icon or 'fa-solid fa-circle'
|
||||||
|
})
|
||||||
|
|
||||||
|
# Kanten erstellen (vereinheitlichte Schlüssel)
|
||||||
|
edges = [{
|
||||||
|
'source': str(node.id),
|
||||||
|
'target': str(child.id),
|
||||||
|
'strength': 0.8
|
||||||
|
} for child in node.children]
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"nodes": nodes,
|
'success': True,
|
||||||
"edges": edges
|
'nodes': nodes,
|
||||||
|
'edges': edges
|
||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
app.logger.error(f"Fehler beim Abrufen der Künste-Mindmap-Daten: {str(e)}")
|
print(f"Fehler beim Abrufen der Mindmap-Daten für Knoten {node_id}: {str(e)}")
|
||||||
return jsonify({"error": "Fehler beim Abrufen der Mindmap-Daten"}), 500
|
|
||||||
|
|
||||||
@app.route('/api/mindmap/philosophy')
|
|
||||||
def get_philosophy_mindmap():
|
|
||||||
"""
|
|
||||||
Gibt die Philosophie-Mindmap zurück für Frontend-Kompatibilität.
|
|
||||||
"""
|
|
||||||
app.logger.info("Philosophie-Mindmap-Daten werden angefordert.")
|
|
||||||
|
|
||||||
try:
|
|
||||||
nodes = [
|
|
||||||
{
|
|
||||||
"id": "epistemology",
|
|
||||||
"name": "Erkenntnistheorie",
|
|
||||||
"description": "Untersuchung der Natur und Grenzen menschlicher Erkenntnis",
|
|
||||||
"category": "Philosophie",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#9F7AEA"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "ethics",
|
|
||||||
"name": "Ethik",
|
|
||||||
"description": "Lehre vom moralisch richtigen Handeln",
|
|
||||||
"category": "Philosophie",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#9F7AEA"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "metaphysics",
|
|
||||||
"name": "Metaphysik",
|
|
||||||
"description": "Grundfragen des Seins und der Wirklichkeit",
|
|
||||||
"category": "Philosophie",
|
|
||||||
"has_children": True,
|
|
||||||
"color_code": "#9F7AEA"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
edges = [
|
|
||||||
{"source_id": "philosophy", "target_id": "epistemology", "strength": 0.8},
|
|
||||||
{"source_id": "philosophy", "target_id": "ethics", "strength": 0.8},
|
|
||||||
{"source_id": "philosophy", "target_id": "metaphysics", "strength": 0.8}
|
|
||||||
]
|
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"nodes": nodes,
|
'success': False,
|
||||||
"edges": edges
|
'error': 'Mindmap-Daten konnten nicht geladen werden',
|
||||||
})
|
'details': str(e)
|
||||||
|
}), 500
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
app.logger.error(f"Fehler beim Abrufen der Philosophie-Mindmap-Daten: {str(e)}")
|
|
||||||
return jsonify({"error": "Fehler beim Abrufen der Mindmap-Daten"}), 500
|
|
||||||
2526
app.py.bak
Normal file
2526
app.py.bak
Normal file
File diff suppressed because it is too large
Load Diff
BIN
fix_routes.py
Normal file
BIN
fix_routes.py
Normal file
Binary file not shown.
4030
logs/app.log
4030
logs/app.log
File diff suppressed because it is too large
Load Diff
@@ -5,199 +5,260 @@
|
|||||||
* Implementiert Lazy Loading & Progressive Disclosure
|
* Implementiert Lazy Loading & Progressive Disclosure
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Mock-Datenbank für Subthemen (später durch echte DB-Abfragen ersetzen)
|
// Neue zentrale Konfiguration
|
||||||
const subthemesDatabase = {
|
const mindmapConfig = {
|
||||||
'philosophy': [
|
categories: {
|
||||||
{
|
'Philosophie': {
|
||||||
id: 'epistemology',
|
icon: 'fa-solid fa-lightbulb',
|
||||||
label: 'Erkenntnistheorie',
|
color: '#b71c1c',
|
||||||
category: 'Philosophie',
|
description: 'Die Lehre vom Denken und der Erkenntnis'
|
||||||
description: 'Untersuchung der Natur und Grenzen menschlicher Erkenntnis',
|
|
||||||
hasChildren: true
|
|
||||||
},
|
},
|
||||||
{
|
'Wissenschaft': {
|
||||||
id: 'ethics',
|
icon: 'fa-solid fa-atom',
|
||||||
label: 'Ethik',
|
color: '#f4b400',
|
||||||
category: 'Philosophie',
|
description: 'Systematische Erforschung der Natur und Gesellschaft'
|
||||||
description: 'Lehre vom moralisch richtigen Handeln',
|
|
||||||
hasChildren: true
|
|
||||||
},
|
},
|
||||||
{
|
'Technologie': {
|
||||||
id: 'metaphysics',
|
icon: 'fa-solid fa-microchip',
|
||||||
label: 'Metaphysik',
|
color: '#0d47a1',
|
||||||
category: 'Philosophie',
|
description: 'Anwendung wissenschaftlicher Erkenntnisse'
|
||||||
description: 'Grundfragen des Seins und der Wirklichkeit',
|
},
|
||||||
hasChildren: true
|
'Künste': {
|
||||||
|
icon: 'fa-solid fa-palette',
|
||||||
|
color: '#c2185b',
|
||||||
|
description: 'Kreativer Ausdruck und künstlerische Gestaltung'
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
'science': [
|
defaultNodeStyle: {
|
||||||
{
|
fontSize: 18,
|
||||||
id: 'physics',
|
fontColor: '#fff',
|
||||||
label: 'Physik',
|
neuronSize: 8,
|
||||||
category: 'Wissenschaft',
|
neuronActivity: 0.8
|
||||||
description: 'Lehre von der Materie und ihren Wechselwirkungen',
|
},
|
||||||
hasChildren: true
|
centerNodeStyle: {
|
||||||
},
|
fontSize: 22,
|
||||||
{
|
fontColor: '#222',
|
||||||
id: 'biology',
|
neuronSize: 12,
|
||||||
label: 'Biologie',
|
neuronActivity: 1.0,
|
||||||
category: 'Wissenschaft',
|
color: '#f5f5f5',
|
||||||
description: 'Lehre von den Lebewesen und ihren Lebensprozessen',
|
icon: 'fa-solid fa-circle'
|
||||||
hasChildren: true
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'chemistry',
|
|
||||||
label: 'Chemie',
|
|
||||||
category: 'Wissenschaft',
|
|
||||||
description: 'Wissenschaft von den Stoffen und ihren Reaktionen',
|
|
||||||
hasChildren: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'technology': [
|
|
||||||
{
|
|
||||||
id: 'ai',
|
|
||||||
label: 'Künstliche Intelligenz',
|
|
||||||
category: 'Technologie',
|
|
||||||
description: 'Maschinelles Lernen und intelligente Systeme',
|
|
||||||
hasChildren: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'robotics',
|
|
||||||
label: 'Robotik',
|
|
||||||
category: 'Technologie',
|
|
||||||
description: 'Entwicklung und Steuerung von Robotern',
|
|
||||||
hasChildren: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'quantum_computing',
|
|
||||||
label: 'Quantencomputing',
|
|
||||||
category: 'Technologie',
|
|
||||||
description: 'Computer basierend auf Quantenmechanik',
|
|
||||||
hasChildren: true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'arts': [
|
|
||||||
{
|
|
||||||
id: 'visual_arts',
|
|
||||||
label: 'Bildende Kunst',
|
|
||||||
category: 'Künste',
|
|
||||||
description: 'Malerei, Bildhauerei und andere visuelle Kunstformen',
|
|
||||||
hasChildren: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'music',
|
|
||||||
label: 'Musik',
|
|
||||||
category: 'Künste',
|
|
||||||
description: 'Tonkunst und musikalische Komposition',
|
|
||||||
hasChildren: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'literature',
|
|
||||||
label: 'Literatur',
|
|
||||||
category: 'Künste',
|
|
||||||
description: 'Schriftliche Kunstwerke und Poesie',
|
|
||||||
hasChildren: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Icon-Definitionen für Kategorien (FontAwesome oder SVG-URL)
|
// Zentrale Styling-Konfiguration
|
||||||
const categoryIcons = {
|
const mindmapStyles = {
|
||||||
'Philosophie': 'fa-solid fa-lightbulb',
|
node: {
|
||||||
'Wissenschaft': 'fa-solid fa-atom',
|
base: {
|
||||||
'Technologie': 'fa-solid fa-microchip',
|
'background-color': 'data(color)',
|
||||||
'Künste': 'fa-solid fa-palette',
|
'label': 'data(label)',
|
||||||
'Psychologie': 'fa-solid fa-brain'
|
'color': '#ffffff',
|
||||||
};
|
'text-background-color': 'rgba(0, 0, 0, 0.7)',
|
||||||
|
'text-background-opacity': 0.8,
|
||||||
// Farben für Kategorien (wie im Bild)
|
'text-background-padding': '4px',
|
||||||
const categoryColors = {
|
'text-valign': 'center',
|
||||||
'Philosophie': '#b71c1c', // Rot
|
'text-halign': 'center',
|
||||||
'Wissenschaft': '#f4b400', // Gelb
|
'font-size': 16,
|
||||||
'Technologie': '#0d47a1', // Blau
|
'width': 'mapData(neuronSize, 3, 10, 30, 60)',
|
||||||
'Künste': '#c2185b', // Pink
|
'height': 'mapData(neuronSize, 3, 10, 30, 60)',
|
||||||
'Psychologie': '#009688' // Türkis
|
'border-width': 2,
|
||||||
};
|
'border-color': '#ffffff',
|
||||||
|
'border-opacity': 0.8,
|
||||||
// Initiale Mindmap-Daten (nur oberste Ebene, mit Icon und Farbe)
|
'shape': 'ellipse',
|
||||||
const mindmapData = {
|
'background-opacity': 0.85
|
||||||
nodes: [
|
|
||||||
{
|
|
||||||
id: 'center',
|
|
||||||
label: 'Wissenskarte',
|
|
||||||
isCenter: true,
|
|
||||||
color: '#f5f5f5',
|
|
||||||
icon: 'fa-solid fa-circle',
|
|
||||||
fontColor: '#222',
|
|
||||||
fontSize: 22,
|
|
||||||
neuronSize: 12,
|
|
||||||
neuronActivity: 1.0
|
|
||||||
},
|
},
|
||||||
{
|
center: {
|
||||||
id: 'philosophy',
|
'background-color': '#f5f5f5',
|
||||||
label: 'Philosophie',
|
'color': '#222',
|
||||||
category: 'Philosophie',
|
'font-size': 20,
|
||||||
description: 'Die Lehre vom Denken und der Erkenntnis',
|
'border-width': 3,
|
||||||
hasChildren: true,
|
'width': 100,
|
||||||
expanded: false,
|
'height': 100
|
||||||
neuronSize: 8,
|
|
||||||
neuronActivity: 0.8,
|
|
||||||
color: categoryColors['Philosophie'],
|
|
||||||
icon: categoryIcons['Philosophie'],
|
|
||||||
fontColor: '#fff',
|
|
||||||
fontSize: 18
|
|
||||||
},
|
},
|
||||||
{
|
selected: {
|
||||||
id: 'science',
|
'border-color': '#f59e42',
|
||||||
label: 'Wissenschaft',
|
'border-width': 3,
|
||||||
category: 'Wissenschaft',
|
'background-opacity': 1
|
||||||
description: 'Systematische Erforschung der Natur und Gesellschaft',
|
|
||||||
hasChildren: true,
|
|
||||||
expanded: false,
|
|
||||||
neuronSize: 8,
|
|
||||||
neuronActivity: 0.8,
|
|
||||||
color: categoryColors['Wissenschaft'],
|
|
||||||
icon: categoryIcons['Wissenschaft'],
|
|
||||||
fontColor: '#fff',
|
|
||||||
fontSize: 18
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'technology',
|
|
||||||
label: 'Technologie',
|
|
||||||
category: 'Technologie',
|
|
||||||
description: 'Anwendung wissenschaftlicher Erkenntnisse',
|
|
||||||
hasChildren: true,
|
|
||||||
expanded: false,
|
|
||||||
neuronSize: 8,
|
|
||||||
neuronActivity: 0.8,
|
|
||||||
color: categoryColors['Technologie'],
|
|
||||||
icon: categoryIcons['Technologie'],
|
|
||||||
fontColor: '#fff',
|
|
||||||
fontSize: 18
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'arts',
|
|
||||||
label: 'Künste',
|
|
||||||
category: 'Künste',
|
|
||||||
description: 'Kreativer Ausdruck und künstlerische Gestaltung',
|
|
||||||
hasChildren: true,
|
|
||||||
expanded: false,
|
|
||||||
neuronSize: 8,
|
|
||||||
neuronActivity: 0.8,
|
|
||||||
color: categoryColors['Künste'],
|
|
||||||
icon: categoryIcons['Künste'],
|
|
||||||
fontColor: '#fff',
|
|
||||||
fontSize: 18
|
|
||||||
}
|
}
|
||||||
],
|
},
|
||||||
edges: [
|
edge: {
|
||||||
{ source: 'center', target: 'philosophy', strength: 0.9 },
|
base: {
|
||||||
{ source: 'center', target: 'science', strength: 0.9 },
|
'width': function(ele) {
|
||||||
{ source: 'center', target: 'technology', strength: 0.9 },
|
return ele.data('strength') ? ele.data('strength') * 2 : 1;
|
||||||
{ source: 'center', target: 'arts', strength: 0.9 }
|
},
|
||||||
]
|
'line-color': function(ele) {
|
||||||
|
const sourceColor = ele.source().data('color');
|
||||||
|
return sourceColor || '#8a8aaa';
|
||||||
|
},
|
||||||
|
'line-opacity': function(ele) {
|
||||||
|
return ele.data('strength') ? ele.data('strength') * 0.6 : 0.4;
|
||||||
|
},
|
||||||
|
'curve-style': 'bezier',
|
||||||
|
'target-arrow-shape': 'none',
|
||||||
|
'control-point-distances': [30, -30],
|
||||||
|
'control-point-weights': [0.5, 0.5]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
layout: {
|
||||||
|
base: {
|
||||||
|
name: 'cose',
|
||||||
|
animate: true,
|
||||||
|
animationDuration: 500,
|
||||||
|
refresh: 20,
|
||||||
|
fit: true,
|
||||||
|
padding: 30,
|
||||||
|
nodeRepulsion: 4500,
|
||||||
|
idealEdgeLength: 50,
|
||||||
|
edgeElasticity: 0.45,
|
||||||
|
randomize: true,
|
||||||
|
componentSpacing: 100,
|
||||||
|
nodeOverlap: 20,
|
||||||
|
gravity: 0.25,
|
||||||
|
initialTemp: 1000,
|
||||||
|
coolingFactor: 0.95,
|
||||||
|
minTemp: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Globale Variable für die Mindmap-Daten
|
||||||
|
let mindmapData = null;
|
||||||
|
|
||||||
|
// Funktion zum Laden der Mindmap-Daten aus der Datenbank
|
||||||
|
async function loadMindmapData(nodeId = null) {
|
||||||
|
try {
|
||||||
|
const apiUrl = nodeId ? `/api/mindmap/${nodeId}` : '/api/mindmap/root';
|
||||||
|
console.log('Lade Mindmap-Daten von:', apiUrl);
|
||||||
|
|
||||||
|
const response = await fetch(apiUrl);
|
||||||
|
console.log('API-Antwort Status:', response.status);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
let errorData;
|
||||||
|
try {
|
||||||
|
errorData = await response.json();
|
||||||
|
console.log('API-Fehler Details:', {
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
errorData
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Fehler beim Parsen der Fehlerantwort:', e);
|
||||||
|
errorData = {
|
||||||
|
error: `HTTP-Fehler ${response.status}: ${response.statusText}`
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fehlerobjekt für die Benachrichtigung erstellen
|
||||||
|
const errorMessage = {
|
||||||
|
error: errorData.error || errorData.message || 'Unbekannter Fehler',
|
||||||
|
details: errorData.details || null
|
||||||
|
};
|
||||||
|
|
||||||
|
showUINotification(errorMessage, 'error');
|
||||||
|
throw new Error(errorMessage.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
console.log('Geladene Mindmap-Daten:', data);
|
||||||
|
|
||||||
|
if (!data.success) {
|
||||||
|
const errorMessage = {
|
||||||
|
error: data.error || 'Mindmap-Daten konnten nicht geladen werden',
|
||||||
|
details: data.details || null
|
||||||
|
};
|
||||||
|
showUINotification(errorMessage, 'error');
|
||||||
|
throw new Error(errorMessage.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erfolgreiche Antwort
|
||||||
|
mindmapData = data; // Speichere die Daten in der globalen Variable
|
||||||
|
showUINotification('Mindmap-Daten erfolgreich geladen', 'success');
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Laden der Mindmap-Daten:', {
|
||||||
|
message: error.message,
|
||||||
|
stack: error.stack,
|
||||||
|
nodeId
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stelle sicher, dass wir eine aussagekräftige Fehlermeldung haben
|
||||||
|
const errorMessage = {
|
||||||
|
error: error.message || 'Unbekannter Fehler beim Laden der Mindmap-Daten',
|
||||||
|
details: error.stack
|
||||||
|
};
|
||||||
|
|
||||||
|
showUINotification(errorMessage, 'error');
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funktion zum Initialisieren der Mindmap
|
||||||
|
async function initializeMindmap() {
|
||||||
|
try {
|
||||||
|
const data = await loadMindmapData();
|
||||||
|
if (!data || !data.nodes || !data.edges) {
|
||||||
|
throw new Error('Ungültiges Datenformat: Mindmap-Daten fehlen oder sind unvollständig');
|
||||||
|
}
|
||||||
|
|
||||||
|
const elements = [
|
||||||
|
// Knoten
|
||||||
|
...data.nodes.map(node => ({
|
||||||
|
data: {
|
||||||
|
id: node.id,
|
||||||
|
label: node.name,
|
||||||
|
category: node.category,
|
||||||
|
description: node.description,
|
||||||
|
hasChildren: node.has_children,
|
||||||
|
expanded: false,
|
||||||
|
color: node.color_code,
|
||||||
|
fontColor: '#ffffff',
|
||||||
|
fontSize: node.is_center ? 20 : 16
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
// Kanten
|
||||||
|
...data.edges.map(edge => ({
|
||||||
|
data: {
|
||||||
|
source: edge.source,
|
||||||
|
target: edge.target,
|
||||||
|
strength: edge.strength || 0.5
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
];
|
||||||
|
|
||||||
|
// Bestehende Cytoscape-Instanz entfernen, falls vorhanden
|
||||||
|
if (window.cy) {
|
||||||
|
window.cy.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.cy = cytoscape({
|
||||||
|
container: document.getElementById('cy'),
|
||||||
|
elements: elements,
|
||||||
|
style: mindmapStyles,
|
||||||
|
layout: mindmapStyles.layout.base
|
||||||
|
});
|
||||||
|
|
||||||
|
// Event-Listener für Knoten-Klicks
|
||||||
|
cy.on('tap', 'node', async function(evt) {
|
||||||
|
const node = evt.target;
|
||||||
|
if (node.data('hasChildren') && !node.data('expanded')) {
|
||||||
|
await loadSubthemes(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Layout ausführen
|
||||||
|
cy.layout(mindmapStyles.layout.base).run();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler bei der Mindmap-Initialisierung:', error);
|
||||||
|
showUINotification({
|
||||||
|
error: 'Mindmap konnte nicht initialisiert werden',
|
||||||
|
details: error.message
|
||||||
|
}, 'error');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Warte bis DOM geladen ist
|
// Warte bis DOM geladen ist
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
console.log('DOMContentLoaded Event ausgelöst');
|
console.log('DOMContentLoaded Event ausgelöst');
|
||||||
@@ -257,79 +318,22 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
style: [
|
style: [
|
||||||
{
|
{
|
||||||
selector: 'node',
|
selector: 'node',
|
||||||
style: {
|
style: mindmapStyles.node.base
|
||||||
'background-color': 'data(color)',
|
|
||||||
'label': 'data(label)',
|
|
||||||
'color': 'data(fontColor)',
|
|
||||||
'text-valign': 'center',
|
|
||||||
'text-halign': 'center',
|
|
||||||
'font-size': 'data(fontSize)',
|
|
||||||
'width': function(ele) {
|
|
||||||
return ele.data('isCenter') ? 100 : 80;
|
|
||||||
},
|
|
||||||
'height': function(ele) {
|
|
||||||
return ele.data('isCenter') ? 100 : 80;
|
|
||||||
},
|
|
||||||
'border-width': 2,
|
|
||||||
'border-color': '#ffffff',
|
|
||||||
'border-opacity': 0.8,
|
|
||||||
'shape': 'ellipse',
|
|
||||||
'background-opacity': 0.9,
|
|
||||||
'text-wrap': 'wrap',
|
|
||||||
'text-max-width': 80,
|
|
||||||
'transition-property': 'background-color, border-width',
|
|
||||||
'transition-duration': '0.2s'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: 'node[isCenter]',
|
selector: 'node[isCenter]',
|
||||||
style: {
|
style: mindmapStyles.node.center
|
||||||
'background-color': '#f5f5f5',
|
|
||||||
'color': '#222',
|
|
||||||
'font-size': 20,
|
|
||||||
'border-width': 3,
|
|
||||||
'width': 100,
|
|
||||||
'height': 100
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: 'node:selected',
|
selector: 'node:selected',
|
||||||
style: {
|
style: mindmapStyles.node.selected
|
||||||
'border-color': '#f59e42',
|
|
||||||
'border-width': 3,
|
|
||||||
'background-opacity': 1
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: 'edge',
|
selector: 'edge',
|
||||||
style: {
|
style: mindmapStyles.edge.base
|
||||||
'width': function(ele) {
|
|
||||||
return ele.data('strength') ? ele.data('strength') * 2 : 1;
|
|
||||||
},
|
|
||||||
'line-color': function(ele) {
|
|
||||||
const sourceColor = ele.source().data('color');
|
|
||||||
return sourceColor || '#8a8aaa';
|
|
||||||
},
|
|
||||||
'line-opacity': function(ele) {
|
|
||||||
return ele.data('strength') ? ele.data('strength') * 0.6 : 0.4;
|
|
||||||
},
|
|
||||||
'curve-style': 'bezier',
|
|
||||||
'target-arrow-shape': 'none',
|
|
||||||
'control-point-distances': [30, -30],
|
|
||||||
'control-point-weights': [0.5, 0.5]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
layout: {
|
layout: mindmapStyles.layout.base
|
||||||
name: 'concentric',
|
|
||||||
fit: true,
|
|
||||||
padding: 50,
|
|
||||||
animate: true,
|
|
||||||
concentric: function(node) {
|
|
||||||
return node.data('isCenter') ? 2 : 1;
|
|
||||||
},
|
|
||||||
levelWidth: function() { return 1; }
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Cytoscape initialisiert');
|
console.log('Cytoscape initialisiert');
|
||||||
@@ -469,63 +473,42 @@ function initializeNeuralDesign(cy) {
|
|||||||
|
|
||||||
// Modifiziere die updateMindmap Funktion
|
// Modifiziere die updateMindmap Funktion
|
||||||
function updateMindmap() {
|
function updateMindmap() {
|
||||||
if (!cy) return;
|
if (!cy) return;
|
||||||
|
|
||||||
// Bestehende Elemente entfernen
|
// Bestehende Elemente entfernen
|
||||||
cy.elements().remove();
|
cy.elements().remove();
|
||||||
|
|
||||||
// Neue Knoten hinzufügen
|
// Neue Knoten hinzufügen
|
||||||
mindmapData.nodes.forEach(node => {
|
mindmapData.nodes.forEach(node => {
|
||||||
cy.add({
|
cy.add({
|
||||||
group: 'nodes',
|
group: 'nodes',
|
||||||
data: {
|
data: {
|
||||||
id: node.id,
|
id: node.id,
|
||||||
label: node.label,
|
label: node.name,
|
||||||
category: node.category,
|
category: node.category,
|
||||||
description: node.description,
|
description: node.description,
|
||||||
hasChildren: node.hasChildren,
|
hasChildren: node.has_children,
|
||||||
expanded: node.expanded,
|
expanded: false,
|
||||||
neuronSize: node.neuronSize,
|
color: node.color_code || mindmapConfig.categories[node.category]?.color || '#60a5fa',
|
||||||
neuronActivity: node.neuronActivity,
|
icon: node.icon || mindmapConfig.categories[node.category]?.icon || 'fa-solid fa-circle'
|
||||||
color: node.color,
|
}
|
||||||
icon: node.icon,
|
});
|
||||||
fontColor: node.fontColor,
|
|
||||||
fontSize: node.fontSize
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// Neue Kanten hinzufügen
|
// Neue Kanten hinzufügen
|
||||||
mindmapData.edges.forEach(edge => {
|
mindmapData.edges.forEach(edge => {
|
||||||
cy.add({
|
cy.add({
|
||||||
group: 'edges',
|
group: 'edges',
|
||||||
data: {
|
data: {
|
||||||
source: edge.source,
|
source: edge.source,
|
||||||
target: edge.target,
|
target: edge.target,
|
||||||
strength: edge.strength
|
strength: edge.strength || 0.5
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// Neuronales Design initialisieren
|
// Layout aktualisieren
|
||||||
initializeNeuralDesign(cy);
|
cy.layout(mindmapStyles.layout.base).run();
|
||||||
|
|
||||||
// Layout anwenden
|
|
||||||
cy.layout({
|
|
||||||
name: 'cose',
|
|
||||||
animate: true,
|
|
||||||
animationDuration: 1000,
|
|
||||||
nodeDimensionsIncludeLabels: true,
|
|
||||||
padding: 100,
|
|
||||||
spacingFactor: 1.8,
|
|
||||||
randomize: false,
|
|
||||||
fit: true,
|
|
||||||
componentSpacing: 100,
|
|
||||||
nodeRepulsion: 8000,
|
|
||||||
edgeElasticity: 100,
|
|
||||||
nestingFactor: 1.2,
|
|
||||||
gravity: 80
|
|
||||||
}).run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -764,31 +747,69 @@ function startNeuralActivitySimulation(cy) {
|
|||||||
|
|
||||||
// Hilfe-Funktion zum Hinzufügen eines Flash-Hinweises
|
// Hilfe-Funktion zum Hinzufügen eines Flash-Hinweises
|
||||||
function showFlash(message, type = 'info') {
|
function showFlash(message, type = 'info') {
|
||||||
const flashContainer = document.getElementById('flash-messages') || createFlashContainer();
|
const flashContainer = createFlashContainer();
|
||||||
|
const flash = document.createElement('div');
|
||||||
|
flash.className = `flash-message ${type}`;
|
||||||
|
flash.textContent = message;
|
||||||
|
flashContainer.appendChild(flash);
|
||||||
|
document.body.appendChild(flashContainer);
|
||||||
|
|
||||||
const flashMsg = document.createElement('div');
|
|
||||||
flashMsg.className = `flash-message flash-${type} mb-2 p-3 rounded`;
|
|
||||||
flashMsg.innerHTML = `
|
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<div>${message}</div>
|
|
||||||
<button class="close-flash ml-2">×</button>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
flashContainer.appendChild(flashMsg);
|
|
||||||
|
|
||||||
// Nach 5 Sekunden automatisch ausblenden
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
flashMsg.style.opacity = '0';
|
flash.classList.add('show');
|
||||||
setTimeout(() => flashMsg.remove(), 300);
|
setTimeout(() => {
|
||||||
}, 5000);
|
flash.classList.remove('show');
|
||||||
|
setTimeout(() => {
|
||||||
|
flashContainer.remove();
|
||||||
|
}, 300);
|
||||||
|
}, 3000);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
// Close-Button
|
/**
|
||||||
const closeBtn = flashMsg.querySelector('.close-flash');
|
* Zeigt eine Benachrichtigung in der UI an
|
||||||
closeBtn.addEventListener('click', () => {
|
* @param {string|object} message - Die anzuzeigende Nachricht oder ein Fehlerobjekt
|
||||||
flashMsg.style.opacity = '0';
|
* @param {string} type - Der Typ der Benachrichtigung ('info', 'success', 'warning', 'error')
|
||||||
setTimeout(() => flashMsg.remove(), 300);
|
* @param {number} duration - Die Anzeigedauer in Millisekunden (Standard: 3000)
|
||||||
});
|
*/
|
||||||
|
function showUINotification(message, type = 'info', duration = 3000) {
|
||||||
|
// Überprüfe und formatiere die Nachricht
|
||||||
|
let displayMessage;
|
||||||
|
if (typeof message === 'object') {
|
||||||
|
if (message.message) {
|
||||||
|
displayMessage = message.message;
|
||||||
|
} else if (message.error) {
|
||||||
|
displayMessage = message.error;
|
||||||
|
} else if (message.details) {
|
||||||
|
displayMessage = message.details;
|
||||||
|
} else {
|
||||||
|
console.error('Ungültiges Nachrichtenobjekt:', message);
|
||||||
|
displayMessage = 'Ein unbekannter Fehler ist aufgetreten';
|
||||||
|
}
|
||||||
|
} else if (typeof message === 'string') {
|
||||||
|
displayMessage = message;
|
||||||
|
} else {
|
||||||
|
console.error('Ungültige Nachricht für UI-Benachrichtigung:', message);
|
||||||
|
displayMessage = 'Ein unbekannter Fehler ist aufgetreten';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validiere den Typ
|
||||||
|
const validTypes = ['info', 'success', 'warning', 'error'];
|
||||||
|
if (!validTypes.includes(type)) {
|
||||||
|
console.warn(`Ungültiger Benachrichtigungstyp: ${type}. Verwende 'info' als Fallback.`);
|
||||||
|
type = 'info';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validiere die Dauer
|
||||||
|
if (typeof duration !== 'number' || duration < 1000 || duration > 10000) {
|
||||||
|
console.warn(`Ungültige Dauer: ${duration}ms. Verwende 3000ms als Fallback.`);
|
||||||
|
duration = 3000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zeige die Benachrichtigung an
|
||||||
|
showFlash(displayMessage, type);
|
||||||
|
|
||||||
|
// Logging für Debugging-Zwecke
|
||||||
|
console.log(`UI-Benachrichtigung [${type}]:`, displayMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hilfsfunktion zum Erstellen eines Flash-Containers, falls keiner existiert
|
// Hilfsfunktion zum Erstellen eines Flash-Containers, falls keiner existiert
|
||||||
@@ -800,152 +821,6 @@ function createFlashContainer() {
|
|||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Funktion zum Laden der Mindmap-Daten aus der Datenbank
|
|
||||||
async function loadMindmapData(nodeId = null) {
|
|
||||||
try {
|
|
||||||
let url;
|
|
||||||
// Wir müssen zwischen numerischen IDs und String-IDs unterscheiden
|
|
||||||
if (nodeId === null || nodeId === undefined) {
|
|
||||||
// Wenn keine ID angegeben ist, verwende 'root'
|
|
||||||
url = '/api/mindmap/root';
|
|
||||||
} else if (isNaN(parseInt(nodeId))) {
|
|
||||||
// Für String-IDs wie 'root', 'technology', 'arts' - direkte Route
|
|
||||||
url = `/api/mindmap/${nodeId}`;
|
|
||||||
} else {
|
|
||||||
// Für numerische IDs - neue Route mit /id/ Präfix
|
|
||||||
url = `/api/mindmap/id/${nodeId}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await fetch(url);
|
|
||||||
if (!response.ok) throw new Error('Fehler beim Laden der Mindmap-Daten');
|
|
||||||
return await response.json();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Fehler beim Laden der Mindmap-Daten:', error);
|
|
||||||
showFlash('Fehler beim Laden der Mindmap-Daten', 'error');
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Funktion zum Initialisieren der Mindmap
|
|
||||||
async function initializeMindmap() {
|
|
||||||
const mindmapData = await loadMindmapData();
|
|
||||||
if (!mindmapData) return;
|
|
||||||
|
|
||||||
const elements = [
|
|
||||||
// Knoten
|
|
||||||
...mindmapData.nodes.map(node => ({
|
|
||||||
data: {
|
|
||||||
id: node.id,
|
|
||||||
label: node.name,
|
|
||||||
category: node.category,
|
|
||||||
description: node.description,
|
|
||||||
hasChildren: node.has_children,
|
|
||||||
expanded: false,
|
|
||||||
color: node.color_code,
|
|
||||||
fontColor: '#ffffff',
|
|
||||||
fontSize: node.is_center ? 20 : 16
|
|
||||||
}
|
|
||||||
})),
|
|
||||||
// Kanten
|
|
||||||
...mindmapData.edges.map(edge => ({
|
|
||||||
data: {
|
|
||||||
source: edge.source_id,
|
|
||||||
target: edge.target_id,
|
|
||||||
strength: edge.strength || 0.5
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
];
|
|
||||||
|
|
||||||
window.cy = cytoscape({
|
|
||||||
container: document.getElementById('cy'),
|
|
||||||
elements: elements,
|
|
||||||
style: [
|
|
||||||
{
|
|
||||||
selector: 'node',
|
|
||||||
style: {
|
|
||||||
'background-color': 'data(color)',
|
|
||||||
'label': 'data(label)',
|
|
||||||
'color': 'data(fontColor)',
|
|
||||||
'text-valign': 'center',
|
|
||||||
'text-halign': 'center',
|
|
||||||
'font-size': 'data(fontSize)',
|
|
||||||
'width': function(ele) {
|
|
||||||
return ele.data('isCenter') ? 100 : 80;
|
|
||||||
},
|
|
||||||
'height': function(ele) {
|
|
||||||
return ele.data('isCenter') ? 100 : 80;
|
|
||||||
},
|
|
||||||
'border-width': 2,
|
|
||||||
'border-color': '#ffffff',
|
|
||||||
'border-opacity': 0.8,
|
|
||||||
'shape': 'ellipse',
|
|
||||||
'background-opacity': 0.9,
|
|
||||||
'text-wrap': 'wrap',
|
|
||||||
'text-max-width': 80,
|
|
||||||
'transition-property': 'background-color, border-width',
|
|
||||||
'transition-duration': '0.2s'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: 'node[isCenter]',
|
|
||||||
style: {
|
|
||||||
'background-color': '#f5f5f5',
|
|
||||||
'color': '#222',
|
|
||||||
'font-size': 20,
|
|
||||||
'border-width': 3,
|
|
||||||
'width': 100,
|
|
||||||
'height': 100
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: 'node:selected',
|
|
||||||
style: {
|
|
||||||
'border-color': '#f59e42',
|
|
||||||
'border-width': 3,
|
|
||||||
'background-opacity': 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: 'edge',
|
|
||||||
style: {
|
|
||||||
'width': function(ele) {
|
|
||||||
return ele.data('strength') ? ele.data('strength') * 2 : 1;
|
|
||||||
},
|
|
||||||
'line-color': function(ele) {
|
|
||||||
const sourceColor = ele.source().data('color');
|
|
||||||
return sourceColor || '#8a8aaa';
|
|
||||||
},
|
|
||||||
'line-opacity': function(ele) {
|
|
||||||
return ele.data('strength') ? ele.data('strength') * 0.6 : 0.4;
|
|
||||||
},
|
|
||||||
'curve-style': 'bezier',
|
|
||||||
'target-arrow-shape': 'none',
|
|
||||||
'control-point-distances': [30, -30],
|
|
||||||
'control-point-weights': [0.5, 0.5]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
layout: {
|
|
||||||
name: 'concentric',
|
|
||||||
fit: true,
|
|
||||||
padding: 50,
|
|
||||||
animate: true,
|
|
||||||
concentric: function(node) {
|
|
||||||
return node.data('isCenter') ? 2 : 1;
|
|
||||||
},
|
|
||||||
levelWidth: function() { return 1; }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Event-Listener für Knoten-Klicks
|
|
||||||
cy.on('tap', 'node', async function(evt) {
|
|
||||||
const node = evt.target;
|
|
||||||
if (node.data('hasChildren') && !node.data('expanded')) {
|
|
||||||
await loadSubthemes(node);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Funktion zum Laden der Subthemen
|
// Funktion zum Laden der Subthemen
|
||||||
async function loadSubthemes(node) {
|
async function loadSubthemes(node) {
|
||||||
try {
|
try {
|
||||||
@@ -1003,24 +878,7 @@ async function loadSubthemes(node) {
|
|||||||
}))
|
}))
|
||||||
],
|
],
|
||||||
style: cy.style(),
|
style: cy.style(),
|
||||||
layout: {
|
layout: mindmapStyles.layout.base
|
||||||
name: 'cose',
|
|
||||||
animate: true,
|
|
||||||
animationDuration: 500,
|
|
||||||
refresh: 20,
|
|
||||||
fit: true,
|
|
||||||
padding: 30,
|
|
||||||
nodeRepulsion: 4500,
|
|
||||||
idealEdgeLength: 50,
|
|
||||||
edgeElasticity: 0.45,
|
|
||||||
randomize: true,
|
|
||||||
componentSpacing: 100,
|
|
||||||
nodeOverlap: 20,
|
|
||||||
gravity: 0.25,
|
|
||||||
initialTemp: 1000,
|
|
||||||
coolingFactor: 0.95,
|
|
||||||
minTemp: 1
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Event-Listener für die neue Mindmap
|
// Event-Listener für die neue Mindmap
|
||||||
@@ -1120,6 +978,99 @@ style.textContent = `
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
text-shadow: 0 2px 8px rgba(0,0,0,0.25);
|
text-shadow: 0 2px 8px rgba(0,0,0,0.25);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Verbesserte Flash-Benachrichtigungen */
|
||||||
|
#flash-messages {
|
||||||
|
position: fixed;
|
||||||
|
top: 1rem;
|
||||||
|
right: 1rem;
|
||||||
|
z-index: 9999;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
max-width: 24rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flash-message {
|
||||||
|
padding: 1rem 1.25rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
background: rgba(17, 24, 39, 0.95);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.25rem;
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
|
||||||
|
0 2px 4px -1px rgba(0, 0, 0, 0.06);
|
||||||
|
transform: translateX(120%);
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flash-message.show {
|
||||||
|
transform: translateX(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flash-message.info {
|
||||||
|
border-left: 4px solid #3b82f6;
|
||||||
|
background: linear-gradient(to right, rgba(59, 130, 246, 0.1), rgba(17, 24, 39, 0.95));
|
||||||
|
}
|
||||||
|
|
||||||
|
.flash-message.success {
|
||||||
|
border-left: 4px solid #10b981;
|
||||||
|
background: linear-gradient(to right, rgba(16, 185, 129, 0.1), rgba(17, 24, 39, 0.95));
|
||||||
|
}
|
||||||
|
|
||||||
|
.flash-message.warning {
|
||||||
|
border-left: 4px solid #f59e0b;
|
||||||
|
background: linear-gradient(to right, rgba(245, 158, 11, 0.1), rgba(17, 24, 39, 0.95));
|
||||||
|
}
|
||||||
|
|
||||||
|
.flash-message.error {
|
||||||
|
border-left: 4px solid #ef4444;
|
||||||
|
background: linear-gradient(to right, rgba(239, 68, 68, 0.1), rgba(17, 24, 39, 0.95));
|
||||||
|
}
|
||||||
|
|
||||||
|
.flash-message::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(45deg,
|
||||||
|
rgba(255, 255, 255, 0.1) 0%,
|
||||||
|
rgba(255, 255, 255, 0) 100%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0% { transform: scale(1); }
|
||||||
|
50% { transform: scale(1.02); }
|
||||||
|
100% { transform: scale(1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.flash-message.show {
|
||||||
|
animation: pulse 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Neuronale Effekte für Benachrichtigungen */
|
||||||
|
.flash-message.info:hover {
|
||||||
|
box-shadow: 0 0 15px rgba(59, 130, 246, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flash-message.success:hover {
|
||||||
|
box-shadow: 0 0 15px rgba(16, 185, 129, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flash-message.warning:hover {
|
||||||
|
box-shadow: 0 0 15px rgba(245, 158, 11, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.flash-message.error:hover {
|
||||||
|
box-shadow: 0 0 15px rgba(239, 68, 68, 0.3);
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
|
|
||||||
|
|||||||
1078
static/js/update_mindmap.js.bak
Normal file
1078
static/js/update_mindmap.js.bak
Normal file
File diff suppressed because it is too large
Load Diff
BIN
static/js/update_mindmap.js.new
Normal file
BIN
static/js/update_mindmap.js.new
Normal file
Binary file not shown.
1078
static/js/update_mindmap.js.original
Normal file
1078
static/js/update_mindmap.js.original
Normal file
File diff suppressed because it is too large
Load Diff
BIN
update_routes.py
Normal file
BIN
update_routes.py
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user