diff --git a/__pycache__/app.cpython-313.pyc b/__pycache__/app.cpython-313.pyc index d9892fb..c80ce46 100644 Binary files a/__pycache__/app.cpython-313.pyc and b/__pycache__/app.cpython-313.pyc differ diff --git a/app.py b/app.py index 9878a95..1fccf4a 100644 --- a/app.py +++ b/app.py @@ -477,487 +477,30 @@ def profile(): 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 + flash('Beim Laden Ihres Benutzerprofils ist ein Fehler aufgetreten. Wir zeigen Ihnen die Standard-Ansicht.', 'error') - # 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 -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 + # Erstelle grundlegende stats 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) + 'thought_count': 0, + 'bookmark_count': 0, + 'connections_count': 0, + 'contributions_count': 0, + 'followers_count': 0, + 'rating': 0.0 } - # 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 + # Leere Listen für Mindmaps und Gedanken + user_mindmaps = [] + thoughts = [] + location = "Deutschland" + # Zeige das normale Profil mit minimalen Daten an 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}") - flash('Dein Benutzerprofil konnte nicht geladen werden. Bitte kontaktiere den Support.', 'error') - return redirect(url_for('index')) + user=current_user, + user_mindmaps=user_mindmaps, + stats=stats, + thoughts=thoughts, + location=location) # Route für Benutzereinstellungen @app.route('/settings', methods=['GET', 'POST'])