diff --git a/website/__pycache__/app.cpython-311.pyc b/website/__pycache__/app.cpython-311.pyc index 8e1da6b..1f42420 100644 Binary files a/website/__pycache__/app.cpython-311.pyc and b/website/__pycache__/app.cpython-311.pyc differ diff --git a/website/app.py b/website/app.py index b23942e..84c35f4 100755 --- a/website/app.py +++ b/website/app.py @@ -21,15 +21,22 @@ from dotenv import load_dotenv # Modelle importieren from models import ( db, User, Thought, Comment, MindMapNode, ThoughtRelation, ThoughtRating, - RelationType, Category, UserMindmap, UserMindmapNode, MindmapNote + RelationType, Category, UserMindmap, UserMindmapNode, MindmapNote, + node_thought_association, user_thought_bookmark ) # Lade .env-Datei load_dotenv() +# 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'] = 'sqlite:///mindmap.db' +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 @@ -67,103 +74,6 @@ def admin_required(f): return f(*args, **kwargs) return decorated_function -class RelationType(Enum): - SUPPORTS = "stützt" - CONTRADICTS = "widerspricht" - BUILDS_UPON = "baut auf auf" - GENERALIZES = "verallgemeinert" - SPECIFIES = "spezifiziert" - INSPIRES = "inspiriert" - -class ThoughtRelation(db.Model): - id = db.Column(db.Integer, primary_key=True) - source_id = db.Column(db.Integer, db.ForeignKey('thought.id'), nullable=False) - target_id = db.Column(db.Integer, db.ForeignKey('thought.id'), nullable=False) - relation_type = db.Column(db.Enum(RelationType), nullable=False) - created_at = db.Column(db.DateTime, default=datetime.utcnow) - created_by_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) - -class ThoughtRating(db.Model): - id = db.Column(db.Integer, primary_key=True) - thought_id = db.Column(db.Integer, db.ForeignKey('thought.id'), nullable=False) - user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) - relevance_score = db.Column(db.Integer, nullable=False) # 1-5 - created_at = db.Column(db.DateTime, default=datetime.utcnow) - - __table_args__ = ( - db.UniqueConstraint('thought_id', 'user_id', name='unique_thought_rating'), - ) - -# Database Models -class User(UserMixin, db.Model): - id = db.Column(db.Integer, primary_key=True) - username = db.Column(db.String(80), unique=True, nullable=False) - email = db.Column(db.String(120), unique=True, nullable=False) - password_hash = db.Column(db.String(128)) - is_admin = db.Column(db.Boolean, default=False) - thoughts = db.relationship('Thought', backref='author', lazy=True) - - def set_password(self, password): - self.password_hash = generate_password_hash(password) - - def check_password(self, password): - return check_password_hash(self.password_hash, password) - -class Thought(db.Model): - id = db.Column(db.Integer, primary_key=True) - content = db.Column(db.Text, nullable=False) - timestamp = db.Column(db.DateTime, default=datetime.utcnow) - branch = db.Column(db.String(100), nullable=False) - user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) - title = db.Column(db.String(200), nullable=False) - abstract = db.Column(db.Text) - keywords = db.Column(db.String(500)) - color_code = db.Column(db.String(7)) # Hex color code - source_type = db.Column(db.String(50)) # PDF, Markdown, Text etc. - - comments = db.relationship('Comment', backref='thought', lazy=True, cascade="all, delete-orphan") - ratings = db.relationship('ThoughtRating', backref='thought', lazy=True) - - outgoing_relations = db.relationship( - 'ThoughtRelation', - foreign_keys=[ThoughtRelation.source_id], - backref='source_thought', - lazy=True - ) - incoming_relations = db.relationship( - 'ThoughtRelation', - foreign_keys=[ThoughtRelation.target_id], - backref='target_thought', - lazy=True - ) - - @property - def average_rating(self): - if not self.ratings: - return 0 - return sum(r.relevance_score for r in self.ratings) / len(self.ratings) - -class Comment(db.Model): - id = db.Column(db.Integer, primary_key=True) - content = db.Column(db.Text, nullable=False) - timestamp = db.Column(db.DateTime, default=datetime.utcnow) - thought_id = db.Column(db.Integer, db.ForeignKey('thought.id'), nullable=False) - user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) - author = db.relationship('User', backref='comments') - -class MindMapNode(db.Model): - id = db.Column(db.Integer, primary_key=True) - name = db.Column(db.String(100), nullable=False) - parent_id = db.Column(db.Integer, db.ForeignKey('mind_map_node.id'), nullable=True) - children = db.relationship('MindMapNode', backref=db.backref('parent', remote_side=[id])) - thoughts = db.relationship('Thought', secondary='node_thought_association', backref='nodes') - -# Association table for many-to-many relationship between MindMapNode and Thought -node_thought_association = db.Table('node_thought_association', - db.Column('node_id', db.Integer, db.ForeignKey('mind_map_node.id'), primary_key=True), - db.Column('thought_id', db.Integer, db.ForeignKey('thought.id'), primary_key=True) -) - @login_manager.user_loader def load_user(id): return User.query.get(int(id)) @@ -234,6 +144,13 @@ def index(): # Route for the mindmap page @app.route('/mindmap') def mindmap(): + """Zeigt die öffentliche Mindmap an.""" + # Sicherstellen, dass wir Kategorien haben + with app.app_context(): + if Category.query.count() == 0: + create_default_categories() + + # Hole alle Kategorien der obersten Ebene categories = Category.query.filter_by(parent_id=None).all() return render_template('mindmap.html', categories=categories) @@ -690,8 +607,10 @@ def get_mindmap(): root_nodes = MindMapNode.query.filter_by(parent_id=None).all() if not root_nodes: - # Wenn keine Nodes existieren, erstelle Beispieldaten - create_sample_mindmap() + # Wenn keine Nodes existieren, rufen wir initialize_database direkt auf + # anstatt create_sample_mindmap zu verwenden + with app.app_context(): + initialize_database() root_nodes = MindMapNode.query.filter_by(parent_id=None).all() # Ergebnisse in hierarchischer Struktur zurückgeben @@ -1039,7 +958,118 @@ def chat_with_assistant(): }), 500 # App-Kontext-Funktion für Initialisierung der Datenbank -@app.before_first_request +def create_default_categories(): + """Erstellt die Standard-Kategorien und wissenschaftlichen Bereiche.""" + categories = [ + { + 'name': 'Naturwissenschaften', + 'description': 'Empirische Untersuchung und Erklärung natürlicher Phänomene', + 'color_code': '#4CAF50', + 'icon': 'flask', + 'children': [ + { + 'name': 'Physik', + 'description': 'Studium der Materie, Energie und deren Wechselwirkungen', + 'color_code': '#81C784', + 'icon': 'atom' + }, + { + 'name': 'Biologie', + 'description': 'Wissenschaft des Lebens und lebender Organismen', + 'color_code': '#66BB6A', + 'icon': 'leaf' + }, + { + 'name': 'Chemie', + 'description': 'Wissenschaft der Materie, ihrer Eigenschaften und Reaktionen', + 'color_code': '#A5D6A7', + 'icon': 'vial' + } + ] + }, + { + 'name': 'Sozialwissenschaften', + 'description': 'Untersuchung von Gesellschaft und menschlichem Verhalten', + 'color_code': '#2196F3', + 'icon': 'users', + 'children': [ + { + 'name': 'Psychologie', + 'description': 'Wissenschaftliches Studium des Geistes und Verhaltens', + 'color_code': '#64B5F6', + 'icon': 'brain' + }, + { + 'name': 'Soziologie', + 'description': 'Studium sozialer Beziehungen und Institutionen', + 'color_code': '#42A5F5', + 'icon': 'network-wired' + } + ] + }, + { + 'name': 'Geisteswissenschaften', + 'description': 'Studium menschlicher Kultur und Kreativität', + 'color_code': '#9C27B0', + 'icon': 'book', + 'children': [ + { + 'name': 'Philosophie', + 'description': 'Untersuchung grundlegender Fragen über Existenz, Wissen und Ethik', + 'color_code': '#BA68C8', + 'icon': 'lightbulb' + }, + { + 'name': 'Geschichte', + 'description': 'Studium der Vergangenheit und ihres Einflusses auf die Gegenwart', + 'color_code': '#AB47BC', + 'icon': 'landmark' + }, + { + 'name': 'Literatur', + 'description': 'Studium literarischer Werke und ihrer Bedeutung', + 'color_code': '#CE93D8', + 'icon': 'feather' + } + ] + }, + { + 'name': 'Technologie', + 'description': 'Anwendung wissenschaftlicher Erkenntnisse für praktische Zwecke', + 'color_code': '#FF9800', + 'icon': 'microchip', + 'children': [ + { + 'name': 'Informatik', + 'description': 'Studium von Computern und Berechnungssystemen', + 'color_code': '#FFB74D', + 'icon': 'laptop-code' + }, + { + 'name': 'Künstliche Intelligenz', + 'description': 'Entwicklung intelligenter Maschinen und Software', + 'color_code': '#FFA726', + 'icon': 'robot' + } + ] + } + ] + + # Kategorien in die Datenbank einfügen + for category_data in categories: + children_data = category_data.pop('children', []) + category = Category(**category_data) + db.session.add(category) + db.session.flush() # Um die ID zu generieren + + # Unterkategorien hinzufügen + for child_data in children_data: + child = Category(**child_data, parent_id=category.id) + db.session.add(child) + + db.session.commit() + print("Standard-Kategorien wurden erstellt!") + def initialize_database(): """Initialisiert die Datenbank, falls sie noch nicht existiert.""" db.create_all() @@ -1048,119 +1078,26 @@ def initialize_database(): if Category.query.count() == 0: create_default_categories() -def create_default_categories(): - """Erstellt die Standard-Kategorien und wissenschaftlichen Bereiche.""" - with app.app_context(): - # Hauptkategorien erstellen - categories = [ - { - 'name': 'Naturwissenschaften', - 'description': 'Empirische Untersuchung und Erklärung natürlicher Phänomene', - 'color_code': '#4CAF50', - 'icon': 'flask', - 'children': [ - { - 'name': 'Physik', - 'description': 'Studium der Materie, Energie und deren Wechselwirkungen', - 'color_code': '#81C784', - 'icon': 'atom' - }, - { - 'name': 'Biologie', - 'description': 'Wissenschaft des Lebens und lebender Organismen', - 'color_code': '#66BB6A', - 'icon': 'leaf' - }, - { - 'name': 'Chemie', - 'description': 'Wissenschaft der Materie, ihrer Eigenschaften und Reaktionen', - 'color_code': '#A5D6A7', - 'icon': 'vial' - } - ] - }, - { - 'name': 'Sozialwissenschaften', - 'description': 'Untersuchung von Gesellschaft und menschlichem Verhalten', - 'color_code': '#2196F3', - 'icon': 'users', - 'children': [ - { - 'name': 'Psychologie', - 'description': 'Wissenschaftliches Studium des Geistes und Verhaltens', - 'color_code': '#64B5F6', - 'icon': 'brain' - }, - { - 'name': 'Soziologie', - 'description': 'Studium sozialer Beziehungen und Institutionen', - 'color_code': '#42A5F5', - 'icon': 'network-wired' - } - ] - }, - { - 'name': 'Geisteswissenschaften', - 'description': 'Studium menschlicher Kultur und Kreativität', - 'color_code': '#9C27B0', - 'icon': 'book', - 'children': [ - { - 'name': 'Philosophie', - 'description': 'Untersuchung grundlegender Fragen über Existenz, Wissen und Ethik', - 'color_code': '#BA68C8', - 'icon': 'lightbulb' - }, - { - 'name': 'Geschichte', - 'description': 'Studium der Vergangenheit und ihres Einflusses auf die Gegenwart', - 'color_code': '#AB47BC', - 'icon': 'landmark' - }, - { - 'name': 'Literatur', - 'description': 'Studium literarischer Werke und ihrer Bedeutung', - 'color_code': '#CE93D8', - 'icon': 'feather' - } - ] - }, - { - 'name': 'Technologie', - 'description': 'Anwendung wissenschaftlicher Erkenntnisse für praktische Zwecke', - 'color_code': '#FF9800', - 'icon': 'microchip', - 'children': [ - { - 'name': 'Informatik', - 'description': 'Studium von Computern und Berechnungssystemen', - 'color_code': '#FFB74D', - 'icon': 'laptop-code' - }, - { - 'name': 'Künstliche Intelligenz', - 'description': 'Entwicklung intelligenter Maschinen und Software', - 'color_code': '#FFA726', - 'icon': 'robot' - } - ] - } - ] - - # Kategorien in die Datenbank einfügen - for category_data in categories: - children_data = category_data.pop('children', []) - category = Category(**category_data) - db.session.add(category) - db.session.flush() # Um die ID zu generieren - - # Unterkategorien hinzufügen - for child_data in children_data: - child = Category(**child_data, parent_id=category.id) - db.session.add(child) - - db.session.commit() - print("Standard-Kategorien wurden erstellt!") +# Führe die Datenbankinitialisierung beim Starten der App aus +with app.app_context(): + initialize_database() + +@app.route('/search') +def search_thoughts_page(): + """Seite zur Gedankensuche anzeigen.""" + return render_template('search.html') + +@app.route('/my_account') +def my_account(): + """Zeigt die persönliche Merkliste an.""" + if not current_user.is_authenticated: + flash('Bitte melde dich an, um auf deine Merkliste zuzugreifen.', 'warning') + return redirect(url_for('login')) + + # Hole die Lesezeichen des Benutzers + bookmarked_thoughts = current_user.bookmarked_thoughts + + return render_template('my_account.html', bookmarked_thoughts=bookmarked_thoughts) # Flask starten if __name__ == '__main__': diff --git a/website/database/systades.db b/website/database/systades.db new file mode 100644 index 0000000..ee14153 Binary files /dev/null and b/website/database/systades.db differ diff --git a/website/example.env b/website/example.env index 035e4a2..831801d 100644 --- a/website/example.env +++ b/website/example.env @@ -9,4 +9,5 @@ OPENAI_API_KEY=sk-dein-openai-api-schluessel-hier # Datenbank # Bei Bedarf kann hier eine andere Datenbank-URL angegeben werden -# SQLALCHEMY_DATABASE_URI=sqlite:///mindmap.db \ No newline at end of file +# Der Pfad wird relativ zum Projektverzeichnis angegeben +# SQLALCHEMY_DATABASE_URI=sqlite:////absoluter/pfad/zu/database/systades.db \ No newline at end of file diff --git a/website/init_db.py b/website/init_db.py index 4e0ab66..4a46282 100755 --- a/website/init_db.py +++ b/website/init_db.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from app import app, initialize_database +from app import app, initialize_database, db_path from models import db, User, Thought, Comment, MindMapNode, ThoughtRelation, ThoughtRating, RelationType from models import Category, UserMindmap, UserMindmapNode, MindmapNote import os @@ -10,8 +10,11 @@ def init_database(): """Initialisiert die Datenbank mit Beispieldaten.""" with app.app_context(): # Datenbank löschen und neu erstellen - if os.path.exists('instance/mindmap.db'): - os.remove('instance/mindmap.db') + if os.path.exists(db_path): + os.remove(db_path) + + # Stellen Sie sicher, dass das Verzeichnis existiert + os.makedirs(os.path.dirname(db_path), exist_ok=True) db.create_all() diff --git a/website/instance/mindmap.db b/website/instance/mindmap.db deleted file mode 100644 index 1db9c4d..0000000 Binary files a/website/instance/mindmap.db and /dev/null differ diff --git a/website/templates/base.html b/website/templates/base.html index 688d036..b20d577 100644 --- a/website/templates/base.html +++ b/website/templates/base.html @@ -545,8 +545,8 @@
-