feat: update app.py to improve performance and optimize resource usage
This commit is contained in:
479
app.py
479
app.py
@@ -385,6 +385,485 @@ def mindmap():
|
||||
|
||||
return render_template('mindmap.html', user_mindmaps=user_mindmaps, mindmap_js_path=mindmap_js_path)
|
||||
|
||||
# Route for user profile
|
||||
@app.route('/profile')
|
||||
@login_required
|
||||
def profile():
|
||||
try:
|
||||
# Versuche auf die neue Benutzermodellstruktur zuzugreifen
|
||||
_ = current_user.bio # Dies wird fehlschlagen, wenn die Spalte nicht existiert
|
||||
|
||||
# Wenn keine Ausnahme, fahre mit normalem Profil fort
|
||||
# Lade Benutzer-Mindmaps
|
||||
user_mindmaps = UserMindmap.query.filter_by(user_id=current_user.id).all()
|
||||
|
||||
# Prüfe, ob der Benutzer eine Standard-Mindmap hat, sonst erstelle eine
|
||||
if not user_mindmaps:
|
||||
try:
|
||||
default_mindmap = UserMindmap(
|
||||
name='Meine Mindmap',
|
||||
description='Meine persönliche Wissenslandschaft',
|
||||
user_id=current_user.id
|
||||
)
|
||||
db.session.add(default_mindmap)
|
||||
db.session.commit()
|
||||
|
||||
# Aktualisiere die Liste nach dem Erstellen
|
||||
user_mindmaps = [default_mindmap]
|
||||
except Exception as e:
|
||||
print(f"Fehler beim Erstellen der Standard-Mindmap in Profil: {e}")
|
||||
# Flash-Nachricht für den Benutzer
|
||||
flash('Es gab ein Problem beim Laden deiner Mindmaps. Bitte versuche es später erneut.', 'warning')
|
||||
|
||||
# Lade Statistiken
|
||||
thought_count = Thought.query.filter_by(user_id=current_user.id).count()
|
||||
bookmark_count = db.session.query(user_thought_bookmark).filter(
|
||||
user_thought_bookmark.c.user_id == current_user.id).count()
|
||||
|
||||
# Berechne tatsächliche Werte für Benutzerstatistiken
|
||||
contributions_count = Comment.query.filter_by(user_id=current_user.id).count()
|
||||
|
||||
# Berechne Verbindungen (Anzahl der Gedankenverknüpfungen)
|
||||
connections_count = ThoughtRelation.query.filter(
|
||||
(ThoughtRelation.source_id.in_(
|
||||
db.session.query(Thought.id).filter_by(user_id=current_user.id)
|
||||
)) |
|
||||
(ThoughtRelation.target_id.in_(
|
||||
db.session.query(Thought.id).filter_by(user_id=current_user.id)
|
||||
))
|
||||
).count()
|
||||
|
||||
# Berechne durchschnittliche Bewertung der Gedanken des Benutzers
|
||||
avg_rating = db.session.query(func.avg(ThoughtRating.relevance_score)).join(
|
||||
Thought, Thought.id == ThoughtRating.thought_id
|
||||
).filter(Thought.user_id == current_user.id).scalar() or 0
|
||||
|
||||
# Sammle alle Statistiken in einem Wörterbuch
|
||||
stats = {
|
||||
'thought_count': thought_count,
|
||||
'bookmark_count': bookmark_count,
|
||||
'connections_count': connections_count,
|
||||
'contributions_count': contributions_count,
|
||||
'followers_count': 0, # Platzhalter für zukünftige Funktionalität
|
||||
'rating': round(avg_rating, 1)
|
||||
}
|
||||
|
||||
# Hole die letzten Gedanken des Benutzers
|
||||
thoughts = Thought.query.filter_by(user_id=current_user.id).order_by(Thought.created_at.desc()).limit(5).all()
|
||||
|
||||
# Hole den Standort des Benutzers aus der Datenbank, falls vorhanden
|
||||
location = "Deutschland" # Standardwert
|
||||
|
||||
return render_template('profile.html',
|
||||
user=current_user,
|
||||
user_mindmaps=user_mindmaps,
|
||||
stats=stats,
|
||||
thoughts=thoughts,
|
||||
location=location)
|
||||
|
||||
except (AttributeError, sqlalchemy.exc.OperationalError) as e:
|
||||
# Die Spalte existiert nicht, verwende stattdessen das einfache Profil
|
||||
print(f"Verwende einfaches Profil wegen Datenbankfehler: {e}")
|
||||
flash('Dein Profil wird im einfachen Modus angezeigt, bis die Datenbank aktualisiert wird.', 'warning')
|
||||
|
||||
# Lade nur die grundlegenden Informationen
|
||||
user_mindmaps = UserMindmap.query.filter_by(user_id=current_user.id).all()
|
||||
thoughts = Thought.query.filter_by(user_id=current_user.id).order_by(Thought.created_at.desc()).limit(5).all()
|
||||
|
||||
return render_template('simple_profile.html',
|
||||
user=current_user,
|
||||
user_mindmaps=user_mindmaps,
|
||||
thoughts=thoughts)
|
||||
except Exception as e:
|
||||
# Eine andere Ausnahme ist aufgetreten
|
||||
print(f"Fehler beim Laden des Profils: {e}")
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, session, g
|
||||
from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
import json
|
||||
from enum import Enum
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, PasswordField, BooleanField, TextAreaField, SelectField, HiddenField
|
||||
from wtforms.validators import DataRequired, Email, Length, EqualTo, ValidationError
|
||||
from functools import wraps
|
||||
import secrets
|
||||
from sqlalchemy.sql import func
|
||||
from openai import OpenAI
|
||||
from dotenv import load_dotenv
|
||||
from flask_socketio import SocketIO, emit
|
||||
from flask_migrate import Migrate
|
||||
import sqlalchemy
|
||||
|
||||
# Modelle importieren
|
||||
from models import (
|
||||
db, User, Thought, Comment, MindMapNode, ThoughtRelation, ThoughtRating,
|
||||
RelationType, Category, UserMindmap, UserMindmapNode, MindmapNote,
|
||||
node_thought_association, user_thought_bookmark, node_relationship, ForumCategory, ForumPost
|
||||
)
|
||||
|
||||
# Lade .env-Datei
|
||||
load_dotenv() # force=True erzwingt die Synchronisierung
|
||||
|
||||
# Bestimme den absoluten Pfad zur Datenbank
|
||||
basedir = os.path.abspath(os.path.dirname(__file__))
|
||||
db_path = os.path.join(basedir, 'database', 'systades.db')
|
||||
# Stellen Sie sicher, dass das Verzeichnis existiert
|
||||
os.makedirs(os.path.dirname(db_path), exist_ok=True)
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'default-dev-key')
|
||||
app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{db_path}'
|
||||
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
|
||||
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=365) # Langlebige Session für Dark Mode-Einstellung
|
||||
app.config['UPLOAD_FOLDER'] = os.getenv('UPLOAD_FOLDER', os.path.join(os.getcwd(), 'uploads'))
|
||||
app.config['WTF_CSRF_ENABLED'] = False
|
||||
|
||||
# OpenAI API-Konfiguration
|
||||
api_key = "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"
|
||||
# api_key = os.environ.get("OPENAI_API_KEY")
|
||||
# if not api_key:
|
||||
# print("WARNUNG: Kein OPENAI_API_KEY in Umgebungsvariablen gefunden. KI-Funktionalität wird nicht verfügbar sein.")
|
||||
|
||||
client = OpenAI(api_key=api_key)
|
||||
|
||||
# Dark Mode Einstellung in Session speichern
|
||||
@app.before_request
|
||||
def handle_dark_mode():
|
||||
if 'dark_mode' not in session:
|
||||
session['dark_mode'] = False # Standardmäßig Light Mode
|
||||
|
||||
# Context processor für Dark Mode
|
||||
@app.context_processor
|
||||
def inject_dark_mode():
|
||||
return {'dark_mode': session.get('dark_mode', False)}
|
||||
|
||||
# Route zum Umschalten des Dark Mode
|
||||
@app.route('/toggle-dark-mode', methods=['POST'])
|
||||
def toggle_dark_mode():
|
||||
session['dark_mode'] = not session.get('dark_mode', False)
|
||||
return jsonify({'success': True, 'dark_mode': session['dark_mode']})
|
||||
|
||||
# Context processor für globale Template-Variablen
|
||||
@app.context_processor
|
||||
def inject_globals():
|
||||
"""Inject global variables into all templates."""
|
||||
return {
|
||||
'current_year': datetime.now().year
|
||||
}
|
||||
|
||||
# Context-Prozessor für alle Templates
|
||||
@app.context_processor
|
||||
def inject_current_year():
|
||||
return {'current_year': datetime.now().year}
|
||||
|
||||
# Initialisiere die Datenbank
|
||||
db.init_app(app)
|
||||
|
||||
# Initialisiere den Login-Manager
|
||||
login_manager = LoginManager(app)
|
||||
login_manager.login_view = 'login'
|
||||
|
||||
# Erst nach der App-Initialisierung die DB-Check-Funktionen importieren
|
||||
from utils.db_check import check_db_connection, initialize_db_if_needed
|
||||
|
||||
# SocketIO initialisieren
|
||||
socketio = SocketIO(app)
|
||||
|
||||
migrate = Migrate(app, db)
|
||||
|
||||
def create_default_categories():
|
||||
"""Erstellt die Standardkategorien für die Mindmap"""
|
||||
# Hauptkategorien
|
||||
main_categories = [
|
||||
{
|
||||
"name": "Philosophie",
|
||||
"description": "Philosophisches Denken und Konzepte",
|
||||
"color_code": "#9F7AEA",
|
||||
"icon": "fa-brain",
|
||||
"subcategories": [
|
||||
{"name": "Ethik", "description": "Moralische Grundsätze", "icon": "fa-balance-scale"},
|
||||
{"name": "Logik", "description": "Gesetze des Denkens", "icon": "fa-project-diagram"},
|
||||
{"name": "Erkenntnistheorie", "description": "Natur des Wissens", "icon": "fa-lightbulb"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Wissenschaft",
|
||||
"description": "Wissenschaftliche Disziplinen und Forschung",
|
||||
"color_code": "#48BB78",
|
||||
"icon": "fa-flask",
|
||||
"subcategories": [
|
||||
{"name": "Physik", "description": "Gesetze der Materie und Energie", "icon": "fa-atom"},
|
||||
{"name": "Biologie", "description": "Wissenschaft des Lebens", "icon": "fa-dna"},
|
||||
{"name": "Mathematik", "description": "Abstrakte Strukturen", "icon": "fa-calculator"},
|
||||
{"name": "Informatik", "description": "Wissenschaft der Datenverarbeitung", "icon": "fa-laptop-code"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Technologie",
|
||||
"description": "Technologische Entwicklungen und Anwendungen",
|
||||
"color_code": "#ED8936",
|
||||
"icon": "fa-microchip",
|
||||
"subcategories": [
|
||||
{"name": "Künstliche Intelligenz", "description": "Intelligente Maschinen", "icon": "fa-robot"},
|
||||
{"name": "Programmierung", "description": "Softwareentwicklung", "icon": "fa-code"},
|
||||
{"name": "Elektronik", "description": "Elektronische Systeme", "icon": "fa-memory"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Künste",
|
||||
"description": "Kunstformen und kulturelle Ausdrucksweisen",
|
||||
"color_code": "#ED64A6",
|
||||
"icon": "fa-palette",
|
||||
"subcategories": [
|
||||
{"name": "Literatur", "description": "Schriftliche Werke", "icon": "fa-book"},
|
||||
{"name": "Musik", "description": "Klangkunst", "icon": "fa-music"},
|
||||
{"name": "Bildende Kunst", "description": "Visuelle Kunstformen", "icon": "fa-paint-brush"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Psychologie",
|
||||
"description": "Menschliches Verhalten und Geist",
|
||||
"color_code": "#4299E1",
|
||||
"icon": "fa-comments",
|
||||
"subcategories": [
|
||||
{"name": "Kognition", "description": "Denken und Wahrnehmen", "icon": "fa-brain"},
|
||||
{"name": "Emotionen", "description": "Gefühlswelt", "icon": "fa-heart"},
|
||||
{"name": "Persönlichkeit", "description": "Charaktereigenschaften", "icon": "fa-user"}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
# Kategorien erstellen
|
||||
for main_cat_data in main_categories:
|
||||
# Prüfen, ob die Kategorie bereits existiert
|
||||
existing_cat = Category.query.filter_by(name=main_cat_data["name"]).first()
|
||||
if existing_cat:
|
||||
continue
|
||||
|
||||
# Hauptkategorie erstellen
|
||||
main_category = Category(
|
||||
name=main_cat_data["name"],
|
||||
description=main_cat_data["description"],
|
||||
color_code=main_cat_data["color_code"],
|
||||
icon=main_cat_data["icon"]
|
||||
)
|
||||
db.session.add(main_category)
|
||||
db.session.flush() # Um die ID zu generieren
|
||||
|
||||
# Unterkategorien erstellen
|
||||
for sub_cat_data in main_cat_data.get("subcategories", []):
|
||||
sub_category = Category(
|
||||
name=sub_cat_data["name"],
|
||||
description=sub_cat_data["description"],
|
||||
color_code=main_cat_data["color_code"],
|
||||
icon=sub_cat_data.get("icon", main_cat_data["icon"]),
|
||||
parent_id=main_category.id
|
||||
)
|
||||
db.session.add(sub_category)
|
||||
|
||||
db.session.commit()
|
||||
print("Standard-Kategorien wurden erstellt!")
|
||||
|
||||
def create_forum_categories():
|
||||
"""Erstellt Forum-Kategorien basierend auf Hauptknotenpunkten der Mindmap"""
|
||||
# Hauptknotenpunkte abrufen (nur die, die keine Elternknoten haben)
|
||||
main_nodes = MindMapNode.query.filter(~MindMapNode.id.in_(
|
||||
db.session.query(node_relationship.c.child_id)
|
||||
)).all()
|
||||
|
||||
for node in main_nodes:
|
||||
# Prüfen, ob eine Forum-Kategorie für diesen Knoten bereits existiert
|
||||
existing_category = ForumCategory.query.filter_by(node_id=node.id).first()
|
||||
if existing_category:
|
||||
continue
|
||||
|
||||
# Neue Kategorie erstellen
|
||||
forum_category = ForumCategory(
|
||||
node_id=node.id,
|
||||
title=node.name,
|
||||
description=node.description,
|
||||
is_active=True
|
||||
)
|
||||
db.session.add(forum_category)
|
||||
|
||||
db.session.commit()
|
||||
print("Forum-Kategorien wurden für alle Hauptknotenpunkte erstellt!")
|
||||
|
||||
def initialize_database():
|
||||
"""Initialisiert die Datenbank mit Grunddaten, falls diese leer ist"""
|
||||
try:
|
||||
print("Initialisiere die Datenbank...")
|
||||
|
||||
# Erstelle alle Tabellen
|
||||
db.create_all()
|
||||
|
||||
# Prüfen, ob bereits Kategorien existieren
|
||||
categories_count = Category.query.count()
|
||||
users_count = User.query.count()
|
||||
|
||||
# Erstelle Standarddaten, wenn es keine Kategorien gibt
|
||||
if categories_count == 0:
|
||||
create_default_categories()
|
||||
|
||||
# Admin-Benutzer erstellen, wenn keine Benutzer vorhanden sind
|
||||
if users_count == 0:
|
||||
admin_user = User(
|
||||
username="admin",
|
||||
email="admin@example.com",
|
||||
role="admin",
|
||||
is_active=True
|
||||
)
|
||||
admin_user.set_password("admin123") # Sicheres Passwort in der Produktion verwenden!
|
||||
db.session.add(admin_user)
|
||||
db.session.commit()
|
||||
print("Admin-Benutzer wurde erstellt!")
|
||||
|
||||
# Forum-Kategorien erstellen
|
||||
create_forum_categories()
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"Fehler bei Datenbank-Initialisierung: {e}")
|
||||
return False
|
||||
|
||||
# Instead of before_first_request, which is deprecated in newer Flask versions
|
||||
# Use a function to initialize the database that will be called during app creation
|
||||
def init_app_database(app_instance):
|
||||
"""Initialisiert die Datenbank für die Flask-App"""
|
||||
with app_instance.app_context():
|
||||
# Überprüfe und initialisiere die Datenbank bei Bedarf
|
||||
if not initialize_db_if_needed(db, initialize_database):
|
||||
print("WARNUNG: Datenbankinitialisierung fehlgeschlagen. Einige Funktionen könnten eingeschränkt sein.")
|
||||
|
||||
# Call the function to initialize the database
|
||||
init_app_database(app)
|
||||
|
||||
# Benutzerdefinierter Decorator für Admin-Zugriff
|
||||
def admin_required(f):
|
||||
@wraps(f)
|
||||
@login_required
|
||||
def decorated_function(*args, **kwargs):
|
||||
if not current_user.is_admin:
|
||||
flash('Zugriff verweigert. Nur Administratoren dürfen diese Seite aufrufen.', 'error')
|
||||
return redirect(url_for('index'))
|
||||
return f(*args, **kwargs)
|
||||
return decorated_function
|
||||
|
||||
@login_manager.user_loader
|
||||
def load_user(id):
|
||||
# Verwende session.get() anstelle von query.get() für SQLAlchemy 2.0 Kompatibilität
|
||||
return db.session.get(User, int(id))
|
||||
|
||||
# Routes for authentication
|
||||
@app.route('/login', methods=['GET', 'POST'])
|
||||
def login():
|
||||
if request.method == 'POST':
|
||||
username = request.form.get('username')
|
||||
password = request.form.get('password')
|
||||
|
||||
user = User.query.filter_by(username=username).first()
|
||||
|
||||
if user and user.check_password(password):
|
||||
login_user(user)
|
||||
# Aktualisiere letzten Login-Zeitpunkt
|
||||
user.last_login = datetime.now(timezone.utc)
|
||||
db.session.commit()
|
||||
|
||||
next_page = request.args.get('next')
|
||||
return redirect(next_page or url_for('index'))
|
||||
|
||||
flash('Ungültiger Benutzername oder Passwort')
|
||||
return render_template('login.html')
|
||||
|
||||
@app.route('/register', methods=['GET', 'POST'])
|
||||
def register():
|
||||
if request.method == 'POST':
|
||||
username = request.form.get('username')
|
||||
email = request.form.get('email')
|
||||
password = request.form.get('password')
|
||||
|
||||
if User.query.filter_by(username=username).first():
|
||||
flash('Benutzername existiert bereits')
|
||||
return redirect(url_for('register'))
|
||||
|
||||
if User.query.filter_by(email=email).first():
|
||||
flash('E-Mail ist bereits registriert')
|
||||
return redirect(url_for('register'))
|
||||
|
||||
user = User(username=username, email=email)
|
||||
user.set_password(password)
|
||||
db.session.add(user)
|
||||
db.session.commit() # Commit, um eine ID für den Benutzer zu erhalten
|
||||
|
||||
# Erstelle eine Standard-Mindmap für den neuen Benutzer
|
||||
try:
|
||||
default_mindmap = UserMindmap(
|
||||
name='Meine Mindmap',
|
||||
description='Meine persönliche Wissenslandschaft',
|
||||
user_id=user.id
|
||||
)
|
||||
db.session.add(default_mindmap)
|
||||
db.session.commit()
|
||||
except Exception as e:
|
||||
print(f"Fehler beim Erstellen der Standard-Mindmap: {e}")
|
||||
# Stelle sicher, dass wir trotzdem weitermachen können
|
||||
db.session.rollback()
|
||||
|
||||
login_user(user)
|
||||
flash('Dein Konto wurde erfolgreich erstellt!', 'success')
|
||||
return redirect(url_for('index'))
|
||||
return render_template('register.html')
|
||||
|
||||
@app.route('/logout')
|
||||
@login_required
|
||||
def logout():
|
||||
logout_user()
|
||||
return redirect(url_for('index'))
|
||||
|
||||
# Route for the homepage
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('index.html')
|
||||
|
||||
# Route for the mindmap page
|
||||
@app.route('/mindmap')
|
||||
def mindmap():
|
||||
"""Zeigt die Mindmap-Seite an."""
|
||||
|
||||
# Benutzer-Mindmaps, falls angemeldet
|
||||
user_mindmaps = []
|
||||
if current_user.is_authenticated:
|
||||
user_mindmaps = UserMindmap.query.filter_by(user_id=current_user.id).all()
|
||||
|
||||
# Stelle sicher, dass der "Wissen"-Knoten existiert
|
||||
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()
|
||||
print("'Wissen'-Knoten wurde erstellt")
|
||||
|
||||
# Überprüfe, ob es Kategorien gibt, sonst erstelle sie
|
||||
if Category.query.count() == 0:
|
||||
create_default_categories()
|
||||
print("Kategorien wurden erstellt")
|
||||
|
||||
# Stelle sicher, dass die Route für statische Dateien korrekt ist
|
||||
mindmap_js_path = url_for('static', filename='js/mindmap-init.js')
|
||||
|
||||
return render_template('mindmap.html', user_mindmaps=user_mindmaps, mindmap_js_path=mindmap_js_path)
|
||||
|
||||
# Route for user profile
|
||||
@app.route('/profile')
|
||||
@login_required
|
||||
|
||||
Reference in New Issue
Block a user