diff --git a/website/models.py b/website/models.py new file mode 100644 index 0000000..2b2e926 --- /dev/null +++ b/website/models.py @@ -0,0 +1,230 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from flask_sqlalchemy import SQLAlchemy +from flask_login import UserMixin +from datetime import datetime +from werkzeug.security import generate_password_hash, check_password_hash +from enum import Enum + +db = SQLAlchemy() + +# Beziehungstypen für Gedankenverknüpfungen +class RelationType(Enum): + SUPPORTS = "stützt" + CONTRADICTS = "widerspricht" + BUILDS_UPON = "baut auf auf" + GENERALIZES = "verallgemeinert" + SPECIFIES = "spezifiziert" + INSPIRES = "inspiriert" + +# Beziehungstabelle für viele-zu-viele Beziehung zwischen MindMapNodes +node_relationship = db.Table('node_relationship', + db.Column('parent_id', db.Integer, db.ForeignKey('mind_map_node.id'), primary_key=True), + db.Column('child_id', db.Integer, db.ForeignKey('mind_map_node.id'), primary_key=True) +) + +# Beziehungstabelle für öffentliche Knoten und Gedanken +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) +) + +# Beziehungstabelle für Benutzer-spezifische Mindmap-Knoten und Gedanken +user_mindmap_thought_association = db.Table('user_mindmap_thought_association', + db.Column('user_mindmap_id', db.Integer, db.ForeignKey('user_mindmap.id'), primary_key=True), + db.Column('thought_id', db.Integer, db.ForeignKey('thought.id'), primary_key=True) +) + +# Beziehungstabelle für Benutzer-Bookmarks von Gedanken +user_thought_bookmark = db.Table('user_thought_bookmark', + db.Column('user_id', db.Integer, db.ForeignKey('user.id'), primary_key=True), + db.Column('thought_id', db.Integer, db.ForeignKey('thought.id'), primary_key=True), + db.Column('created_at', db.DateTime, default=datetime.utcnow) +) + +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) + created_at = db.Column(db.DateTime, default=datetime.utcnow) + last_login = db.Column(db.DateTime) + avatar = db.Column(db.String(200)) + bio = db.Column(db.Text) + + # Beziehungen + thoughts = db.relationship('Thought', backref='author', lazy=True) + comments = db.relationship('Comment', backref='author', lazy=True) + user_mindmaps = db.relationship('UserMindmap', backref='user', lazy=True) + mindmap_notes = db.relationship('MindmapNote', backref='user', lazy=True) + bookmarked_thoughts = db.relationship('Thought', secondary=user_thought_bookmark, + backref=db.backref('bookmarked_by', lazy='dynamic')) + + 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 Category(db.Model): + """Wissenschaftliche Kategorien für die Gliederung der öffentlichen Mindmap""" + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(100), unique=True, nullable=False) + description = db.Column(db.Text) + color_code = db.Column(db.String(7)) # Hex color + icon = db.Column(db.String(50)) + parent_id = db.Column(db.Integer, db.ForeignKey('category.id'), nullable=True) + + # Beziehungen + children = db.relationship('Category', backref=db.backref('parent', remote_side=[id])) + nodes = db.relationship('MindMapNode', backref='category', lazy=True) + +class MindMapNode(db.Model): + """Öffentliche Mindmap-Knoten, die für alle Benutzer sichtbar sind""" + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(100), nullable=False) + description = db.Column(db.Text) + color_code = db.Column(db.String(7)) + icon = db.Column(db.String(50)) + is_public = db.Column(db.Boolean, default=True) + created_at = db.Column(db.DateTime, default=datetime.utcnow) + created_by_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True) + category_id = db.Column(db.Integer, db.ForeignKey('category.id'), nullable=True) + + # Beziehungen für Baumstruktur (mehrere Eltern möglich) + parents = db.relationship( + 'MindMapNode', + secondary=node_relationship, + primaryjoin=(node_relationship.c.child_id == id), + secondaryjoin=(node_relationship.c.parent_id == id), + backref=db.backref('children', lazy='dynamic'), + lazy='dynamic' + ) + + # Beziehungen zu Gedanken + thoughts = db.relationship('Thought', + secondary=node_thought_association, + backref=db.backref('nodes', lazy='dynamic')) + + # Beziehung zum Ersteller + created_by = db.relationship('User', backref='created_nodes') + +class UserMindmap(db.Model): + """Benutzerspezifische Mindmap, die vom Benutzer personalisierbar ist""" + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(100), nullable=False) + description = db.Column(db.Text) + user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) + created_at = db.Column(db.DateTime, default=datetime.utcnow) + last_modified = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) + is_private = db.Column(db.Boolean, default=True) + + # Beziehungen zu öffentlichen Knoten + public_nodes = db.relationship('MindMapNode', + secondary='user_mindmap_node', + backref=db.backref('in_user_mindmaps', lazy='dynamic')) + + # Beziehungen zu Gedanken + thoughts = db.relationship('Thought', + secondary=user_mindmap_thought_association, + backref=db.backref('in_user_mindmaps', lazy='dynamic')) + + # Notizen zu dieser Mindmap + notes = db.relationship('MindmapNote', backref='mindmap', lazy=True) + +# Beziehungstabelle für benutzerorientierte Mindmaps und öffentliche Knoten +class UserMindmapNode(db.Model): + """Speichert die Beziehung zwischen Benutzer-Mindmaps und öffentlichen Knoten inkl. Position""" + user_mindmap_id = db.Column(db.Integer, db.ForeignKey('user_mindmap.id'), primary_key=True) + node_id = db.Column(db.Integer, db.ForeignKey('mind_map_node.id'), primary_key=True) + x_position = db.Column(db.Float, default=0) # Position X auf der Mindmap + y_position = db.Column(db.Float, default=0) # Position Y auf der Mindmap + scale = db.Column(db.Float, default=1.0) # Größe des Knotens + added_at = db.Column(db.DateTime, default=datetime.utcnow) + +class MindmapNote(db.Model): + """Private Notizen der Benutzer zu ihrer Mindmap""" + id = db.Column(db.Integer, primary_key=True) + user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) + mindmap_id = db.Column(db.Integer, db.ForeignKey('user_mindmap.id'), nullable=False) + node_id = db.Column(db.Integer, db.ForeignKey('mind_map_node.id'), nullable=True) + thought_id = db.Column(db.Integer, db.ForeignKey('thought.id'), nullable=True) + content = db.Column(db.Text, nullable=False) + created_at = db.Column(db.DateTime, default=datetime.utcnow) + last_modified = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) + color_code = db.Column(db.String(7), default="#FFF59D") # Farbe der Notiz + +class Thought(db.Model): + """Gedanken und Inhalte, die in der Mindmap verknüpft werden können""" + id = db.Column(db.Integer, primary_key=True) + title = db.Column(db.String(200), nullable=False) + content = db.Column(db.Text, 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. + branch = db.Column(db.String(100), nullable=False) + user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) + created_at = db.Column(db.DateTime, default=datetime.utcnow) + last_modified = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) + + # Beziehungen + 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 ThoughtRelation(db.Model): + """Beziehungen zwischen Gedanken""" + 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) + + # Beziehung zum Ersteller + created_by = db.relationship('User', backref='created_relations') + +class ThoughtRating(db.Model): + """Bewertungen von Gedanken durch Benutzer""" + 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'), + ) + + # Beziehung zum Benutzer + user = db.relationship('User', backref='ratings') + +class Comment(db.Model): + """Kommentare zu Gedanken""" + id = db.Column(db.Integer, primary_key=True) + content = db.Column(db.Text, nullable=False) + thought_id = db.Column(db.Integer, db.ForeignKey('thought.id'), nullable=False) + user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False) + created_at = db.Column(db.DateTime, default=datetime.utcnow) + last_modified = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) \ No newline at end of file diff --git a/website/templates/base.html b/website/templates/base.html index 4ca2b23..688d036 100644 --- a/website/templates/base.html +++ b/website/templates/base.html @@ -734,55 +734,118 @@ diff --git a/website/templates/mindmap.html b/website/templates/mindmap.html index 7c7aa84..e449133 100644 --- a/website/templates/mindmap.html +++ b/website/templates/mindmap.html @@ -516,7 +516,7 @@ left: 0; width: 100%; height: 100%; - background-image: url('/static/network-bg.jpg'); + background-image: url('/static/network-bg.svg'); background-size: cover; background-position: center; opacity: 0.2; @@ -651,237 +651,101 @@ {% endblock %} -{% block extra_js %} - +{% block scripts %} + - - - - + + - - + {% endblock %} \ No newline at end of file