diff --git a/app.py b/app.py index cb16aca..6a7a92f 100644 --- a/app.py +++ b/app.py @@ -1879,4 +1879,73 @@ def admin_update_database(): message = f"Fehler: {str(e)}" success = False - return render_template('admin/update_database.html', message=message, success=success) \ No newline at end of file + return render_template('admin/update_database.html', message=message, success=success) + +@app.route('/api/mindmap/') +def get_mindmap_node(node_id): + """Liefert die Mindmap-Daten für einen bestimmten Knoten und seine Subthemen.""" + try: + if node_id == 'root': + # Hauptknoten (Wissen) abrufen + wissen_node = MindMapNode.query.filter_by(name="Wissen").first() + if not wissen_node: + wissen_node = MindMapNode( + name="Wissen", + description="Zentrale Wissensbasis", + color_code="#4299E1", + is_public=True + ) + db.session.add(wissen_node) + db.session.commit() + + # Alle direkten Kinder des Wissen-Knotens holen + nodes = wissen_node.children.all() + else: + # Bestimmten Knoten und seine Kinder abrufen + parent_node = MindMapNode.query.get_or_404(node_id) + nodes = parent_node.children.all() + wissen_node = parent_node + + # Ergebnisdaten vorbereiten + nodes_data = [] + edges_data = [] + + # Hauptknoten hinzufügen + nodes_data.append({ + 'id': wissen_node.id, + 'name': wissen_node.name, + 'description': wissen_node.description or '', + 'color_code': wissen_node.color_code or '#4299E1', + 'is_center': True, + 'has_children': len(nodes) > 0 + }) + + # Kinder hinzufügen + for node in nodes: + nodes_data.append({ + 'id': node.id, + 'name': node.name, + 'description': node.description or '', + 'color_code': node.color_code or '#9F7AEA', + 'is_center': False, + 'has_children': len(node.children.all()) > 0 + }) + + # Verbindung zum Elternknoten hinzufügen + edges_data.append({ + 'source_id': wissen_node.id, + 'target_id': node.id, + 'strength': 0.8 + }) + + return jsonify({ + 'nodes': nodes_data, + 'edges': edges_data + }) + + except Exception as e: + print(f"Fehler beim Abrufen der Mindmap-Daten: {str(e)}") + return jsonify({ + 'success': False, + 'error': 'Mindmap-Daten konnten nicht geladen werden' + }), 500 \ No newline at end of file diff --git a/static/js/update_mindmap.js b/static/js/update_mindmap.js index aabc533..4145129 100644 --- a/static/js/update_mindmap.js +++ b/static/js/update_mindmap.js @@ -800,17 +800,144 @@ function createFlashContainer() { return container; } +// Funktion zum Laden der Mindmap-Daten aus der Datenbank +async function loadMindmapData(nodeId = null) { + try { + const response = await fetch(`/api/mindmap/${nodeId || 'root'}`); + 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 async function loadSubthemes(node) { try { - console.log('Loading subthemes for node:', node.id()); - - const subthemes = subthemesDatabase[node.id()]; - - if (!subthemes) { - console.log('No subthemes found for node:', node.id()); - return; - } + const mindmapData = await loadMindmapData(node.id()); + if (!mindmapData) return; showFlash('Lade Subthemen...', 'info'); @@ -840,7 +967,28 @@ async function loadSubthemes(node) { const newCy = cytoscape({ container: newContainer, - elements: [], + elements: [ + ...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: 16 + } + })), + ...mindmapData.edges.map(edge => ({ + data: { + source: edge.source_id, + target: edge.target_id, + strength: edge.strength || 0.5 + } + })) + ], style: cy.style(), layout: { name: 'cose', @@ -862,43 +1010,6 @@ async function loadSubthemes(node) { } }); - // Neue Knoten hinzufügen - subthemes.forEach(subtheme => { - newCy.add({ - group: 'nodes', - data: { - id: subtheme.id, - label: subtheme.label, - category: subtheme.category, - description: subtheme.description, - hasChildren: subtheme.hasChildren, - expanded: false, - neuronSize: 6, - neuronActivity: 0.7, - color: categoryColors[subtheme.category] || '#60a5fa', - fontColor: '#ffffff', - fontSize: 14 - } - }); - }); - - // Kanten zwischen den Subthemen erstellen - for (let i = 0; i < subthemes.length; i++) { - for (let j = i + 1; j < subthemes.length; j++) { - newCy.add({ - group: 'edges', - data: { - source: subthemes[i].id, - target: subthemes[j].id, - strength: Math.random() * 0.5 + 0.3 - } - }); - } - } - - // Neuronales Design für die neue Mindmap initialisieren - initializeNeuralDesign(newCy); - // Event-Listener für die neue Mindmap newCy.on('tap', 'node', async function(evt) { const clickedNode = evt.target; @@ -911,26 +1022,6 @@ async function loadSubthemes(node) { cy.container().style.display = 'none'; newPage.style.display = 'block'; - // Layout ausführen - newCy.layout({ - 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 - }).run(); - showFlash('Subthemen erfolgreich geladen', 'success'); } catch (error) { @@ -1017,4 +1108,7 @@ style.textContent = ` text-shadow: 0 2px 8px rgba(0,0,0,0.25); } `; -document.head.appendChild(style); \ No newline at end of file +document.head.appendChild(style); + +// Initialisiere die Mindmap beim Laden der Seite +document.addEventListener('DOMContentLoaded', initializeMindmap); \ No newline at end of file