#!/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)