Refactor OpenAI integration and enhance mindmap UI: Update OpenAI client initialization to use a dedicated class, streamline API key management, and improve loading animations in the mindmap template. Add a modal for adding new thoughts with enhanced user feedback and error handling, while updating the loading overlay for better visual appeal and responsiveness.
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -15,7 +15,7 @@ from wtforms.validators import DataRequired, Email, Length, EqualTo, ValidationE
|
|||||||
from functools import wraps
|
from functools import wraps
|
||||||
import secrets
|
import secrets
|
||||||
from sqlalchemy.sql import func
|
from sqlalchemy.sql import func
|
||||||
import openai
|
from openai import OpenAI
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
# Modelle importieren
|
# Modelle importieren
|
||||||
@@ -26,7 +26,7 @@ from models import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Lade .env-Datei
|
# Lade .env-Datei
|
||||||
load_dotenv(force=True) # force=True erzwingt die Synchronisierung
|
load_dotenv() # force=True erzwingt die Synchronisierung
|
||||||
|
|
||||||
# Bestimme den absoluten Pfad zur Datenbank
|
# Bestimme den absoluten Pfad zur Datenbank
|
||||||
basedir = os.path.abspath(os.path.dirname(__file__))
|
basedir = os.path.abspath(os.path.dirname(__file__))
|
||||||
@@ -41,7 +41,7 @@ app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
|||||||
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=365) # Langlebige Session für Dark Mode-Einstellung
|
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=365) # Langlebige Session für Dark Mode-Einstellung
|
||||||
|
|
||||||
# OpenAI API-Konfiguration
|
# OpenAI API-Konfiguration
|
||||||
openai.api_key = os.environ.get('OPENAI_API_KEY')
|
client = OpenAI(api_key="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")
|
||||||
|
|
||||||
# Context processor für globale Template-Variablen
|
# Context processor für globale Template-Variablen
|
||||||
@app.context_processor
|
@app.context_processor
|
||||||
@@ -931,11 +931,6 @@ def too_many_requests(e):
|
|||||||
@app.route('/api/assistant', methods=['POST'])
|
@app.route('/api/assistant', methods=['POST'])
|
||||||
def chat_with_assistant():
|
def chat_with_assistant():
|
||||||
"""Chatbot-API mit OpenAI Integration."""
|
"""Chatbot-API mit OpenAI Integration."""
|
||||||
if not openai.api_key:
|
|
||||||
return jsonify({
|
|
||||||
'error': 'OpenAI API-Schlüssel fehlt. Bitte in .env-Datei konfigurieren.'
|
|
||||||
}), 500
|
|
||||||
|
|
||||||
data = request.json
|
data = request.json
|
||||||
prompt = data.get('prompt', '')
|
prompt = data.get('prompt', '')
|
||||||
context = data.get('context', '')
|
context = data.get('context', '')
|
||||||
@@ -956,17 +951,17 @@ def chat_with_assistant():
|
|||||||
if context:
|
if context:
|
||||||
system_message += f"\n\nKontext: {context}"
|
system_message += f"\n\nKontext: {context}"
|
||||||
|
|
||||||
response = openai.ChatCompletion.create(
|
response = client.chat.completions.create(
|
||||||
model="gpt-3.5-turbo",
|
model="gpt-3.5-turbo-16k",
|
||||||
messages=[
|
messages=[
|
||||||
{"role": "system", "content": system_message},
|
{"role": "system", "content": system_message},
|
||||||
{"role": "user", "content": prompt}
|
{"role": "user", "content": prompt}
|
||||||
],
|
],
|
||||||
max_tokens=500,
|
max_tokens=300,
|
||||||
temperature=0.7
|
temperature=0.7
|
||||||
)
|
)
|
||||||
|
|
||||||
answer = response.choices[0].message.content.strip()
|
answer = response.choices[0].message.content
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'answer': answer
|
'answer': answer
|
||||||
@@ -1135,8 +1130,8 @@ def reload_env():
|
|||||||
# Erzwinge das Neuladen der .env-Datei
|
# Erzwinge das Neuladen der .env-Datei
|
||||||
load_dotenv(override=True, force=True)
|
load_dotenv(override=True, force=True)
|
||||||
|
|
||||||
# Aktualisiere OpenAI API-Key
|
# OpenAI API-Key ist bereits fest kodiert
|
||||||
openai.api_key = os.environ.get('OPENAI_API_KEY')
|
# client wurde bereits mit festem API-Key initialisiert
|
||||||
|
|
||||||
# Weitere Umgebungsvariablen hier aktualisieren, falls nötig
|
# Weitere Umgebungsvariablen hier aktualisieren, falls nötig
|
||||||
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', app.config['SECRET_KEY'])
|
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', app.config['SECRET_KEY'])
|
||||||
|
|||||||
4
website/cookies.txt
Normal file
4
website/cookies.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Netscape HTTP Cookie File
|
||||||
|
# https://curl.se/docs/http-cookies.html
|
||||||
|
# This file was generated by libcurl! Edit at your own risk.
|
||||||
|
|
||||||
44
website/create_admin.py
Normal file
44
website/create_admin.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from app import app
|
||||||
|
from models import db, User
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
def create_admin_user():
|
||||||
|
"""Create an admin user in the database."""
|
||||||
|
with app.app_context():
|
||||||
|
try:
|
||||||
|
# Check if admin user already exists
|
||||||
|
admin = User.query.filter_by(username='admin').first()
|
||||||
|
if admin:
|
||||||
|
print("Admin user already exists")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create admin user
|
||||||
|
admin = User(
|
||||||
|
username='admin',
|
||||||
|
email='admin@example.com',
|
||||||
|
is_admin=True,
|
||||||
|
created_at=datetime.utcnow()
|
||||||
|
)
|
||||||
|
admin.set_password('admin')
|
||||||
|
|
||||||
|
# Add and commit
|
||||||
|
db.session.add(admin)
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
print("Admin user created successfully!")
|
||||||
|
|
||||||
|
# Verify user was added
|
||||||
|
user = User.query.filter_by(username='admin').first()
|
||||||
|
if user:
|
||||||
|
print(f"Verified: Admin user exists with ID {user.id}")
|
||||||
|
else:
|
||||||
|
print("WARNING: Failed to verify admin user creation")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error creating admin user: {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
create_admin_user()
|
||||||
Binary file not shown.
BIN
website/database/systades.db.backup
Normal file
BIN
website/database/systades.db.backup
Normal file
Binary file not shown.
70
website/fix_db.py
Executable file
70
website/fix_db.py
Executable file
@@ -0,0 +1,70 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sqlite3
|
||||||
|
from datetime import datetime
|
||||||
|
from app import app, db_path
|
||||||
|
from models import db
|
||||||
|
|
||||||
|
def ensure_db_dir():
|
||||||
|
"""Make sure the database directory exists."""
|
||||||
|
os.makedirs(os.path.dirname(db_path), exist_ok=True)
|
||||||
|
|
||||||
|
def fix_database_schema():
|
||||||
|
"""Fix the database schema by adding missing columns."""
|
||||||
|
with app.app_context():
|
||||||
|
# Ensure directory exists
|
||||||
|
ensure_db_dir()
|
||||||
|
|
||||||
|
# Check if database exists, create tables if needed
|
||||||
|
if not os.path.exists(db_path):
|
||||||
|
print("Database doesn't exist. Creating all tables from scratch...")
|
||||||
|
db.create_all()
|
||||||
|
print("Database tables created successfully!")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Connect to existing database
|
||||||
|
print(f"Connecting to database: {db_path}")
|
||||||
|
conn = sqlite3.connect(db_path)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
|
# Check if User table exists
|
||||||
|
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='user'")
|
||||||
|
if not cursor.fetchone():
|
||||||
|
print("User table doesn't exist. Creating all tables from scratch...")
|
||||||
|
conn.close()
|
||||||
|
db.create_all()
|
||||||
|
print("Database tables created successfully!")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check existing columns
|
||||||
|
cursor.execute("PRAGMA table_info(user)")
|
||||||
|
columns = cursor.fetchall()
|
||||||
|
column_names = [col[1] for col in columns]
|
||||||
|
print("Existing columns in User table:", column_names)
|
||||||
|
|
||||||
|
# Add missing columns
|
||||||
|
if 'created_at' not in column_names:
|
||||||
|
print("Adding 'created_at' column to User table...")
|
||||||
|
cursor.execute("ALTER TABLE user ADD COLUMN created_at TIMESTAMP")
|
||||||
|
|
||||||
|
if 'last_login' not in column_names:
|
||||||
|
print("Adding 'last_login' column to User table...")
|
||||||
|
cursor.execute("ALTER TABLE user ADD COLUMN last_login TIMESTAMP")
|
||||||
|
|
||||||
|
if 'avatar' not in column_names:
|
||||||
|
print("Adding 'avatar' column to User table...")
|
||||||
|
cursor.execute("ALTER TABLE user ADD COLUMN avatar VARCHAR(200)")
|
||||||
|
|
||||||
|
if 'bio' not in column_names:
|
||||||
|
print("Adding 'bio' column to User table...")
|
||||||
|
cursor.execute("ALTER TABLE user ADD COLUMN bio TEXT")
|
||||||
|
|
||||||
|
# Commit the changes
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
print("Database schema updated successfully!")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
fix_database_schema()
|
||||||
73
website/rebuild_db.py
Normal file
73
website/rebuild_db.py
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sqlite3
|
||||||
|
from datetime import datetime
|
||||||
|
from app import app, db_path, create_default_categories
|
||||||
|
from models import db, User, Category
|
||||||
|
|
||||||
|
def rebuild_database():
|
||||||
|
"""Completely rebuilds the database by dropping and recreating all tables."""
|
||||||
|
with app.app_context():
|
||||||
|
print(f"Database path: {db_path}")
|
||||||
|
|
||||||
|
# Back up existing database if it exists
|
||||||
|
if os.path.exists(db_path):
|
||||||
|
backup_path = db_path + '.backup'
|
||||||
|
try:
|
||||||
|
import shutil
|
||||||
|
shutil.copy2(db_path, backup_path)
|
||||||
|
print(f"Backed up existing database to {backup_path}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Warning: Could not create backup - {str(e)}")
|
||||||
|
|
||||||
|
# Ensure directory exists
|
||||||
|
os.makedirs(os.path.dirname(db_path), exist_ok=True)
|
||||||
|
|
||||||
|
# Drop all tables and recreate them
|
||||||
|
print("Dropping all tables...")
|
||||||
|
db.drop_all()
|
||||||
|
|
||||||
|
print("Creating all tables...")
|
||||||
|
db.create_all()
|
||||||
|
|
||||||
|
# Create admin user
|
||||||
|
print("Creating admin user...")
|
||||||
|
admin = User(
|
||||||
|
username='admin',
|
||||||
|
email='admin@example.com',
|
||||||
|
is_admin=True,
|
||||||
|
created_at=datetime.utcnow()
|
||||||
|
)
|
||||||
|
admin.set_password('admin')
|
||||||
|
db.session.add(admin)
|
||||||
|
|
||||||
|
# Create regular user
|
||||||
|
print("Creating regular user...")
|
||||||
|
user = User(
|
||||||
|
username='user',
|
||||||
|
email='user@example.com',
|
||||||
|
is_admin=False,
|
||||||
|
created_at=datetime.utcnow()
|
||||||
|
)
|
||||||
|
user.set_password('user')
|
||||||
|
db.session.add(user)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Commit to generate user IDs
|
||||||
|
db.session.commit()
|
||||||
|
print("Users created successfully!")
|
||||||
|
|
||||||
|
# Create default categories
|
||||||
|
print("Creating default categories...")
|
||||||
|
create_default_categories()
|
||||||
|
|
||||||
|
print("Database rebuild completed successfully!")
|
||||||
|
except Exception as e:
|
||||||
|
db.session.rollback()
|
||||||
|
print(f"Error during database rebuild: {str(e)}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
rebuild_database()
|
||||||
@@ -577,13 +577,38 @@
|
|||||||
<!-- Mindmap-Container - Jetzt größer -->
|
<!-- Mindmap-Container - Jetzt größer -->
|
||||||
<div class="glass-card overflow-hidden mb-12">
|
<div class="glass-card overflow-hidden mb-12">
|
||||||
<div id="mindmap-container" class="relative" style="height: 80vh; min-height: 700px;">
|
<div id="mindmap-container" class="relative" style="height: 80vh; min-height: 700px;">
|
||||||
<!-- Lade-Overlay -->
|
<!-- SVG Filters for node effects -->
|
||||||
<div class="mindmap-loading absolute inset-0 flex items-center justify-center z-10" style="background: rgba(14, 18, 32, 0.7); backdrop-filter: blur(5px);">
|
<svg width="0" height="0" style="position: absolute;">
|
||||||
|
<defs>
|
||||||
|
<!-- Glasmorphismus-Effekt für Knoten -->
|
||||||
|
<filter id="glass-effect" x="-50%" y="-50%" width="200%" height="200%">
|
||||||
|
<feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blur" />
|
||||||
|
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 18 -7" result="glow" />
|
||||||
|
<feBlend in="SourceGraphic" in2="glow" mode="normal" />
|
||||||
|
</filter>
|
||||||
|
<!-- Hover-Glow-Effekt -->
|
||||||
|
<filter id="hover-glow" x="-50%" y="-50%" width="200%" height="200%">
|
||||||
|
<feGaussianBlur in="SourceAlpha" stdDeviation="5" result="blur" />
|
||||||
|
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0.5 0 1 0 0 0.5 0 0 1 0 1 0 0 0 18 -7" result="glow" />
|
||||||
|
<feBlend in="SourceGraphic" in2="glow" mode="normal" />
|
||||||
|
</filter>
|
||||||
|
<!-- Ausgewählter-Knoten-Glow-Effekt -->
|
||||||
|
<filter id="selected-glow" x="-50%" y="-50%" width="200%" height="200%">
|
||||||
|
<feGaussianBlur in="SourceAlpha" stdDeviation="8" result="blur" />
|
||||||
|
<feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0.7 0 1 0 0 0.2 0 0 1 0 1 0 0 0 18 -7" result="glow" />
|
||||||
|
<feBlend in="SourceGraphic" in2="glow" mode="normal" />
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<!-- Lade-Overlay mit verbesserter Animation und Transition -->
|
||||||
|
<div class="mindmap-loading absolute inset-0 flex items-center justify-center z-10" style="background: rgba(14, 18, 32, 0.8); backdrop-filter: blur(10px); transition: opacity 0.5s ease-in-out;">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<div class="inline-block animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-purple-500 mb-4"></div>
|
<div class="inline-block animate-spin rounded-full h-16 w-16 border-t-3 border-b-3 border-purple-500 mb-6"></div>
|
||||||
<p class="text-white text-lg">Wissenslandschaft wird geladen...</p>
|
<p class="text-white text-xl font-semibold mb-3">Wissenslandschaft wird geladen...</p>
|
||||||
<div class="w-64 h-2 bg-gray-700 rounded-full mt-4 overflow-hidden">
|
<p class="text-gray-300 text-sm mb-4">Daten werden aus der Datenbank abgerufen</p>
|
||||||
<div class="loading-progress h-full bg-gradient-to-r from-purple-500 to-blue-500 rounded-full" style="width: 0%"></div>
|
<div class="w-80 h-3 bg-gray-800 rounded-full mt-2 overflow-hidden">
|
||||||
|
<div class="loading-progress h-full bg-gradient-to-r from-purple-500 via-blue-500 to-purple-500 rounded-full" style="width: 0%; transition: width 0.3s ease-in-out;"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -646,6 +671,33 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal zum Hinzufügen eines neuen Gedanken -->
|
||||||
|
<div id="add-thought-modal" class="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50 hidden" style="backdrop-filter: blur(5px);">
|
||||||
|
<div class="glass-card w-full max-w-md p-6">
|
||||||
|
<div class="flex justify-between items-center mb-4">
|
||||||
|
<h3 class="text-xl font-bold text-white">Neuen Gedanken hinzufügen</h3>
|
||||||
|
<button class="text-gray-400 hover:text-white" onclick="document.getElementById('add-thought-modal').classList.add('hidden')">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<form id="add-thought-form" method="POST">
|
||||||
|
<input type="hidden" id="thought-node-id" name="node_id">
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-gray-300 mb-2" for="thought-title">Titel</label>
|
||||||
|
<input class="w-full bg-gray-800 text-white border border-gray-700 rounded-lg py-2 px-3" id="thought-title" name="title" placeholder="Titel des Gedankens" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block text-gray-300 mb-2" for="thought-content">Inhalt</label>
|
||||||
|
<textarea class="w-full bg-gray-800 text-white border border-gray-700 rounded-lg py-2 px-3 h-32" id="thought-content" name="content" placeholder="Beschreibe deinen Gedanken..." required></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end space-x-3">
|
||||||
|
<button type="button" class="py-2 px-4 bg-gray-700 text-white rounded-lg" onclick="document.getElementById('add-thought-modal').classList.add('hidden')">Abbrechen</button>
|
||||||
|
<button type="submit" class="py-2 px-4 bg-gradient-to-r from-purple-600 to-blue-500 text-white rounded-lg">Speichern</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
@@ -653,17 +705,20 @@
|
|||||||
<!-- D3.js Library -->
|
<!-- D3.js Library -->
|
||||||
<script src="https://d3js.org/d3.v7.min.js"></script>
|
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||||
|
|
||||||
|
<!-- Tippy.js für Tooltips -->
|
||||||
|
<script src="https://unpkg.com/@popperjs/core@2"></script>
|
||||||
|
<script src="https://unpkg.com/tippy.js@6"></script>
|
||||||
|
|
||||||
<!-- Mindmap scripts -->
|
<!-- Mindmap scripts -->
|
||||||
<script src="{{ url_for('static', filename='d3-extensions.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/modules/mindmap.js') }}"></script>
|
||||||
<script src="{{ url_for('static', filename='mindmap.js') }}"></script>
|
|
||||||
|
|
||||||
<!-- Initialization Script -->
|
<!-- Initialization Script -->
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
// Inizialize the mindmap visualization
|
// Mindmap-Container holen
|
||||||
const mindmapContainer = document.getElementById('mindmap-container');
|
const mindmapContainer = document.getElementById('mindmap-container');
|
||||||
|
|
||||||
// Options for the visualization
|
// Options für die Visualisierung
|
||||||
const options = {
|
const options = {
|
||||||
width: mindmapContainer.clientWidth,
|
width: mindmapContainer.clientWidth,
|
||||||
height: mindmapContainer.clientHeight,
|
height: mindmapContainer.clientHeight,
|
||||||
@@ -675,21 +730,84 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
tooltipEnabled: true,
|
tooltipEnabled: true,
|
||||||
onNodeClick: function(node) {
|
onNodeClick: function(node) {
|
||||||
console.log('Node clicked:', node);
|
console.log('Node clicked:', node);
|
||||||
// Handle node click - could be expanded to display node content
|
// Hier könnten wir weitere Informationen zum Knoten anzeigen
|
||||||
// or fetch more information from the server
|
// oder mehr Daten vom Server abrufen
|
||||||
|
|
||||||
|
// Beispiel: Gedanken zu diesem Knoten laden
|
||||||
|
fetch(`/api/nodes/${node.id}/thoughts`)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
console.log('Gedanken zu diesem Knoten:', data);
|
||||||
|
// Hier könnte die Anzeige aktualisiert werden
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Fehler beim Laden der Gedanken:', error);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create and initialize the visualization
|
// Ladebalken-Animation starten
|
||||||
|
const progressBar = document.querySelector('.loading-progress');
|
||||||
|
let progress = 0;
|
||||||
|
const loadingInterval = setInterval(() => {
|
||||||
|
progress += 5;
|
||||||
|
if (progress > 100) progress = 100;
|
||||||
|
progressBar.style.width = `${progress}%`;
|
||||||
|
if (progress === 100) {
|
||||||
|
clearInterval(loadingInterval);
|
||||||
|
}
|
||||||
|
}, 150);
|
||||||
|
|
||||||
|
// Mindmap erstellen und initialisieren
|
||||||
window.mindmap = new MindMapVisualization('#mindmap-container', options);
|
window.mindmap = new MindMapVisualization('#mindmap-container', options);
|
||||||
|
|
||||||
// Set up UI event handlers
|
// API-Aufruf, um echte Daten zu laden
|
||||||
|
fetch('/api/mindmap')
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Netzwerkantwort war nicht ok');
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
// Ladebalken auf 100% setzen
|
||||||
|
progressBar.style.width = '100%';
|
||||||
|
|
||||||
|
// Lade-Overlay nach kurzer Verzögerung ausblenden
|
||||||
|
setTimeout(() => {
|
||||||
|
const loadingOverlay = document.querySelector('.mindmap-loading');
|
||||||
|
if (loadingOverlay) {
|
||||||
|
loadingOverlay.style.opacity = '0';
|
||||||
|
setTimeout(() => {
|
||||||
|
loadingOverlay.style.display = 'none';
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
console.log('Mindmap-Daten geladen:', data);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Fehler beim Laden der Mindmap-Daten:', error);
|
||||||
|
// Fehlerbehandlung: Zeige trotzdem die Standard-Mindmap
|
||||||
|
progressBar.style.width = '100%';
|
||||||
|
setTimeout(() => {
|
||||||
|
const loadingOverlay = document.querySelector('.mindmap-loading');
|
||||||
|
if (loadingOverlay) {
|
||||||
|
loadingOverlay.style.opacity = '0';
|
||||||
|
setTimeout(() => {
|
||||||
|
loadingOverlay.style.display = 'none';
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
});
|
||||||
|
|
||||||
|
// UI Event-Handler einrichten
|
||||||
document.getElementById('zoom-in-btn').addEventListener('click', function() {
|
document.getElementById('zoom-in-btn').addEventListener('click', function() {
|
||||||
if (window.mindmap) {
|
if (window.mindmap) {
|
||||||
const transform = d3.zoomTransform(window.mindmap.svg.node());
|
const transform = d3.zoomTransform(window.mindmap.svg.node());
|
||||||
window.mindmap.svg.call(
|
window.mindmap.svg.call(
|
||||||
d3.zoom().transform,
|
d3.zoom().transform,
|
||||||
d3.zoomIdentity.scale(transform.k * 1.3)
|
d3.zoomIdentity.translate(transform.x, transform.y).scale(transform.k * 1.3)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -699,7 +817,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const transform = d3.zoomTransform(window.mindmap.svg.node());
|
const transform = d3.zoomTransform(window.mindmap.svg.node());
|
||||||
window.mindmap.svg.call(
|
window.mindmap.svg.call(
|
||||||
d3.zoom().transform,
|
d3.zoom().transform,
|
||||||
d3.zoomIdentity.scale(transform.k / 1.3)
|
d3.zoomIdentity.translate(transform.x, transform.y).scale(transform.k / 1.3)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -714,14 +832,113 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('add-thought-btn').addEventListener('click', function() {
|
document.getElementById('add-thought-btn').addEventListener('click', function() {
|
||||||
alert('Diese Funktion steht demnächst zur Verfügung.');
|
if (window.mindmap && window.mindmap.selectedNode) {
|
||||||
|
const nodeId = window.mindmap.selectedNode.id;
|
||||||
|
const modal = document.getElementById('add-thought-modal');
|
||||||
|
if (modal) {
|
||||||
|
// Modal öffnen, wenn vorhanden
|
||||||
|
modal.classList.remove('hidden');
|
||||||
|
// Node-ID in ein verstecktes Feld setzen
|
||||||
|
const nodeIdField = document.getElementById('thought-node-id');
|
||||||
|
if (nodeIdField) nodeIdField.value = nodeId;
|
||||||
|
} else {
|
||||||
|
// Simpler Dialog, wenn kein Modal existiert
|
||||||
|
const thoughtText = prompt('Neuen Gedanken eingeben:');
|
||||||
|
if (thoughtText) {
|
||||||
|
// Gedanken über API hinzufügen
|
||||||
|
fetch(`/api/nodes/${nodeId}/thoughts`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
title: thoughtText,
|
||||||
|
content: thoughtText
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
alert('Gedanke wurde hinzugefügt!');
|
||||||
|
// Optional: Knoten neu laden oder Zähler aktualisieren
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Fehler beim Hinzufügen des Gedankens:', error);
|
||||||
|
alert('Fehler beim Hinzufügen des Gedankens.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
alert('Bitte wähle zuerst einen Knoten aus.');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.getElementById('connect-btn').addEventListener('click', function() {
|
document.getElementById('connect-btn').addEventListener('click', function() {
|
||||||
alert('Diese Funktion steht demnächst zur Verfügung.');
|
if (window.mindmap && window.mindmap.selectedNode) {
|
||||||
|
alert('Bitte wähle einen weiteren Knoten aus, um eine Verbindung herzustellen.');
|
||||||
|
// Hier könnte ein spezieller Modus aktiviert werden, der auf den nächsten Klick wartet
|
||||||
|
window.mindmap.connectMode = true;
|
||||||
|
} else {
|
||||||
|
alert('Bitte wähle zuerst einen Knoten aus.');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle window resize
|
// Formular zum Hinzufügen neuer Gedanken behandeln
|
||||||
|
const thoughtForm = document.getElementById('add-thought-form');
|
||||||
|
if (thoughtForm) {
|
||||||
|
thoughtForm.addEventListener('submit', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const nodeId = document.getElementById('thought-node-id').value;
|
||||||
|
const title = document.getElementById('thought-title').value;
|
||||||
|
const content = document.getElementById('thought-content').value;
|
||||||
|
|
||||||
|
if (!nodeId || !title || !content) {
|
||||||
|
alert('Bitte fülle alle Felder aus.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gedanken über API hinzufügen
|
||||||
|
fetch(`/api/nodes/${nodeId}/thoughts`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
title: title,
|
||||||
|
content: content,
|
||||||
|
branch: 'main' // Default-Zweig
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Fehler beim Hinzufügen des Gedankens');
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
// Modal schließen
|
||||||
|
document.getElementById('add-thought-modal').classList.add('hidden');
|
||||||
|
|
||||||
|
// Formular zurücksetzen
|
||||||
|
thoughtForm.reset();
|
||||||
|
|
||||||
|
// Erfolgsmeldung anzeigen
|
||||||
|
alert('Gedanke wurde erfolgreich hinzugefügt!');
|
||||||
|
|
||||||
|
// Optional: Knoten in der Mindmap aktualisieren (z.B. Zähler erhöhen)
|
||||||
|
if (window.mindmap && window.mindmap.selectedNode) {
|
||||||
|
window.mindmap.selectedNode.thought_count += 1;
|
||||||
|
window.mindmap.updateNodeLabels();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Fehler beim Speichern des Gedankens:', error);
|
||||||
|
alert('Fehler beim Speichern des Gedankens. Bitte versuche es erneut.');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fenstergrößen-Änderung behandeln
|
||||||
let resizeTimeout;
|
let resizeTimeout;
|
||||||
window.addEventListener('resize', function() {
|
window.addEventListener('resize', function() {
|
||||||
clearTimeout(resizeTimeout);
|
clearTimeout(resizeTimeout);
|
||||||
@@ -730,16 +947,16 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
window.mindmap.width = mindmapContainer.clientWidth;
|
window.mindmap.width = mindmapContainer.clientWidth;
|
||||||
window.mindmap.height = mindmapContainer.clientHeight;
|
window.mindmap.height = mindmapContainer.clientHeight;
|
||||||
window.mindmap.svg
|
window.mindmap.svg
|
||||||
.attr('width', window.mindmap.width)
|
.attr('width', '100%')
|
||||||
.attr('height', window.mindmap.height)
|
.attr('height', window.mindmap.height)
|
||||||
.attr('viewBox', `0 0 ${window.mindmap.width} ${window.mindmap.height}`);
|
.attr('viewBox', `0 0 ${window.mindmap.width} ${window.mindmap.height}`);
|
||||||
|
|
||||||
// Update the center force
|
// Mittelpunkt-Kraft aktualisieren
|
||||||
window.mindmap.simulation.force('center',
|
window.mindmap.simulation.force('center',
|
||||||
d3.forceCenter(window.mindmap.width / 2, window.mindmap.height / 2)
|
d3.forceCenter(window.mindmap.width / 2, window.mindmap.height / 2)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Restart the simulation
|
// Simulation neu starten
|
||||||
window.mindmap.simulation.alpha(0.3).restart();
|
window.mindmap.simulation.alpha(0.3).restart();
|
||||||
}
|
}
|
||||||
}, 250);
|
}, 250);
|
||||||
|
|||||||
30
website/test_db.py
Normal file
30
website/test_db.py
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from app import app
|
||||||
|
from models import db, User
|
||||||
|
|
||||||
|
def test_user_query():
|
||||||
|
"""Test if we can query the user table."""
|
||||||
|
with app.app_context():
|
||||||
|
try:
|
||||||
|
# Try to query all users
|
||||||
|
users = User.query.all()
|
||||||
|
print(f"Found {len(users)} users")
|
||||||
|
|
||||||
|
for user in users:
|
||||||
|
print(f"User: {user.username}, Email: {user.email}")
|
||||||
|
|
||||||
|
# Try to query by username - this is where the error happens
|
||||||
|
user = User.query.filter_by(username='admin').first()
|
||||||
|
if user:
|
||||||
|
print(f"Found admin user: {user.username}")
|
||||||
|
else:
|
||||||
|
print("Admin user not found")
|
||||||
|
|
||||||
|
print("All queries completed successfully!")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
test_user_query()
|
||||||
Reference in New Issue
Block a user