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:
2025-04-27 07:43:03 +02:00
parent 8f0a6d4372
commit d58aba26c2
11 changed files with 469 additions and 36 deletions

View File

@@ -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
View 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
View 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.

Binary file not shown.

70
website/fix_db.py Executable file
View 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
View 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()

View File

@@ -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
View 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()