Changes: - Fix datetime serialization with UTC 'Z' suffix for correct timezone display - Add PDF upload support with extension fallback for MIME detection - Fix LOT add/remove by creating new list for SQLAlchemy JSON change detection - Add file message components (FileMessage, ImageLightbox, UploadPreview) - Add multi-file upload support with progress tracking - Link uploaded files to chat messages via message_id - Include file attachments in AI report generation - Update specs for file-storage, realtime-messaging, and ai-report-generation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
115 lines
3.9 KiB
Python
115 lines
3.9 KiB
Python
"""SQLAlchemy models for chat room management
|
|
|
|
Tables:
|
|
- tr_incident_rooms: Stores room metadata and configuration
|
|
- tr_room_members: User-room associations with roles
|
|
|
|
Note: All tables use 'tr_' prefix to avoid conflicts in shared database.
|
|
"""
|
|
from sqlalchemy import Column, Integer, String, Text, DateTime, Enum, ForeignKey, UniqueConstraint, Index, JSON
|
|
from sqlalchemy.orm import relationship
|
|
from datetime import datetime
|
|
import enum
|
|
import uuid
|
|
from app.core.database import Base
|
|
|
|
|
|
class IncidentType(str, enum.Enum):
|
|
"""Types of production incidents"""
|
|
EQUIPMENT_FAILURE = "equipment_failure"
|
|
MATERIAL_SHORTAGE = "material_shortage"
|
|
QUALITY_ISSUE = "quality_issue"
|
|
OTHER = "other"
|
|
|
|
|
|
class SeverityLevel(str, enum.Enum):
|
|
"""Incident severity levels"""
|
|
LOW = "low"
|
|
MEDIUM = "medium"
|
|
HIGH = "high"
|
|
CRITICAL = "critical"
|
|
|
|
|
|
class RoomStatus(str, enum.Enum):
|
|
"""Room lifecycle status"""
|
|
ACTIVE = "active"
|
|
RESOLVED = "resolved"
|
|
ARCHIVED = "archived"
|
|
|
|
|
|
class MemberRole(str, enum.Enum):
|
|
"""Room member roles"""
|
|
OWNER = "owner"
|
|
EDITOR = "editor"
|
|
VIEWER = "viewer"
|
|
|
|
|
|
class IncidentRoom(Base):
|
|
"""Incident room model for production incidents"""
|
|
|
|
__tablename__ = "tr_incident_rooms"
|
|
|
|
room_id = Column(String(36), primary_key=True, default=lambda: str(uuid.uuid4()))
|
|
title = Column(String(255), nullable=False)
|
|
incident_type = Column(Enum(IncidentType), nullable=False)
|
|
severity = Column(Enum(SeverityLevel), nullable=False)
|
|
status = Column(Enum(RoomStatus), default=RoomStatus.ACTIVE, nullable=False)
|
|
location = Column(String(255))
|
|
description = Column(Text)
|
|
resolution_notes = Column(Text)
|
|
lots = Column(JSON, default=list, nullable=False, comment="LOT batch numbers (JSON array)")
|
|
|
|
# User tracking
|
|
created_by = Column(String(255), nullable=False) # User email/ID who created the room
|
|
|
|
# Timestamps
|
|
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
|
resolved_at = Column(DateTime)
|
|
archived_at = Column(DateTime)
|
|
last_activity_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
|
last_updated_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
|
|
|
# Ownership transfer tracking
|
|
ownership_transferred_at = Column(DateTime)
|
|
ownership_transferred_by = Column(String(255))
|
|
|
|
# Denormalized count for performance
|
|
member_count = Column(Integer, default=0, nullable=False)
|
|
|
|
# Relationships
|
|
members = relationship("RoomMember", back_populates="room", cascade="all, delete-orphan")
|
|
files = relationship("RoomFile", back_populates="room", cascade="all, delete-orphan")
|
|
reports = relationship("GeneratedReport", back_populates="room", cascade="all, delete-orphan")
|
|
|
|
# Indexes for common queries
|
|
__table_args__ = (
|
|
Index("ix_tr_incident_rooms_status_created", "status", "created_at"),
|
|
Index("ix_tr_incident_rooms_created_by", "created_by"),
|
|
)
|
|
|
|
|
|
class RoomMember(Base):
|
|
"""Room membership model"""
|
|
|
|
__tablename__ = "tr_room_members"
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
room_id = Column(String(36), ForeignKey("tr_incident_rooms.room_id", ondelete="CASCADE"), nullable=False)
|
|
user_id = Column(String(255), nullable=False) # User email/ID
|
|
role = Column(Enum(MemberRole), nullable=False)
|
|
|
|
# Tracking
|
|
added_by = Column(String(255), nullable=False) # Who added this member
|
|
added_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
|
removed_at = Column(DateTime) # Soft delete timestamp
|
|
|
|
# Relationships
|
|
room = relationship("IncidentRoom", back_populates="members")
|
|
|
|
# Constraints and indexes
|
|
__table_args__ = (
|
|
# Ensure unique active membership (where removed_at IS NULL)
|
|
UniqueConstraint("room_id", "user_id", "removed_at", name="uq_tr_room_member_active"),
|
|
Index("ix_tr_room_members_room_user", "room_id", "user_id"),
|
|
Index("ix_tr_room_members_user", "user_id"),
|
|
) |