""" Database Models - SQLAlchemy ORM """ from sqlalchemy import Column, Integer, String, Text, Boolean, DateTime, ForeignKey, Enum, Table from sqlalchemy.orm import relationship, declarative_base from sqlalchemy.sql import func from datetime import datetime import enum Base = declarative_base() # Association tables for many-to-many relationships channel_members = Table( 'channel_members', Base.metadata, Column('channel_id', Integer, ForeignKey('channels.id', ondelete='CASCADE'), primary_key=True), Column('user_id', Integer, ForeignKey('users.id', ondelete='CASCADE'), primary_key=True), Column('joined_at', DateTime, default=func.now()) ) class UserRole(str, enum.Enum): ENGINEER = "engineer" BD = "bd" ADMIN = "admin" EXEC = "exec" class ChannelType(str, enum.Enum): PUBLIC = "public" PRIVATE = "private" class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, index=True) email = Column(String(255), unique=True, index=True, nullable=False) name = Column(String(255), nullable=False) password_hash = Column(String(255), nullable=False) role = Column(Enum(UserRole), default=UserRole.ENGINEER, nullable=False) is_active = Column(Boolean, default=True, nullable=False) is_online = Column(Boolean, default=False, nullable=False) last_seen = Column(DateTime, default=func.now()) created_at = Column(DateTime, default=func.now()) updated_at = Column(DateTime, default=func.now(), onupdate=func.now()) # Relationships messages = relationship("Message", back_populates="user", cascade="all, delete-orphan") sent_dms = relationship("DirectMessage", foreign_keys="DirectMessage.sender_id", back_populates="sender") received_dms = relationship("DirectMessage", foreign_keys="DirectMessage.recipient_id", back_populates="recipient") channels = relationship("Channel", secondary=channel_members, back_populates="members") files = relationship("File", back_populates="uploaded_by_user") def __repr__(self): return f"" class Channel(Base): __tablename__ = "channels" id = Column(Integer, primary_key=True, index=True) name = Column(String(100), unique=True, index=True, nullable=False) description = Column(Text) type = Column(Enum(ChannelType), default=ChannelType.PUBLIC, nullable=False) created_by = Column(Integer, ForeignKey('users.id', ondelete='SET NULL')) created_at = Column(DateTime, default=func.now()) # Relationships messages = relationship("Message", back_populates="channel", cascade="all, delete-orphan") members = relationship("User", secondary=channel_members, back_populates="channels") def __repr__(self): return f"" class Message(Base): __tablename__ = "messages" id = Column(Integer, primary_key=True, index=True) channel_id = Column(Integer, ForeignKey('channels.id', ondelete='CASCADE'), nullable=False) user_id = Column(Integer, ForeignKey('users.id', ondelete='SET NULL'), nullable=True) # NULL if AI content = Column(Text, nullable=False) is_ai_message = Column(Boolean, default=False, nullable=False) reply_to_message_id = Column(Integer, ForeignKey('messages.id', ondelete='SET NULL'), nullable=True) created_at = Column(DateTime, default=func.now()) edited_at = Column(DateTime, nullable=True) # Relationships channel = relationship("Channel", back_populates="messages") user = relationship("User", back_populates="messages") replies = relationship("Message", remote_side=[id], backref="parent_message") artifacts = relationship("Artifact", back_populates="message", cascade="all, delete-orphan") def __repr__(self): return f"" class DirectMessage(Base): __tablename__ = "direct_messages" id = Column(Integer, primary_key=True, index=True) sender_id = Column(Integer, ForeignKey('users.id', ondelete='CASCADE'), nullable=False) recipient_id = Column(Integer, ForeignKey('users.id', ondelete='CASCADE'), nullable=False) content = Column(Text, nullable=False) is_ai_message = Column(Boolean, default=False, nullable=False) read_at = Column(DateTime, nullable=True) created_at = Column(DateTime, default=func.now()) edited_at = Column(DateTime, nullable=True) # Relationships sender = relationship("User", foreign_keys=[sender_id], back_populates="sent_dms") recipient = relationship("User", foreign_keys=[recipient_id], back_populates="received_dms") def __repr__(self): return f"" class File(Base): __tablename__ = "files" id = Column(Integer, primary_key=True, index=True) filename = Column(String(255), nullable=False) original_filename = Column(String(255), nullable=False) file_path = Column(String(500), nullable=False) file_size = Column(Integer, nullable=False) # bytes mime_type = Column(String(100), nullable=False) uploaded_by = Column(Integer, ForeignKey('users.id', ondelete='SET NULL')) channel_id = Column(Integer, ForeignKey('channels.id', ondelete='CASCADE'), nullable=True) created_at = Column(DateTime, default=func.now()) # Relationships uploaded_by_user = relationship("User", back_populates="files") def __repr__(self): return f"" class Artifact(Base): __tablename__ = "artifacts" id = Column(Integer, primary_key=True, index=True) message_id = Column(Integer, ForeignKey('messages.id', ondelete='CASCADE'), nullable=False) filename = Column(String(255), nullable=False) file_path = Column(String(500), nullable=False) file_type = Column(String(50), nullable=False) # pdf, csv, docx, etc. created_at = Column(DateTime, default=func.now()) # Relationships message = relationship("Message", back_populates="artifacts") def __repr__(self): return f""