smart-chatbot-api / app /models /database.py
GitHub Actions
Deploy from GitHub Actions (2026-03-15 12:10 UTC)
e5b256c
# app/models/database.py
from datetime import datetime, timezone
import uuid
from sqlalchemy import (
create_engine,
Column,
Integer,
String,
Text,
DateTime,
Float,
Boolean,
ForeignKey,
MetaData,
)
# from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship, declarative_base
from sqlalchemy.dialects.postgresql import UUID
# Absolute import from project root
from app.utils.config import get_logger, config_manager
config = config_manager.get_config()
logger = get_logger(__name__)
convention = {
"ix": "ix_%(column_0_label)s",
"uq": "uq_%(table_name)s_%(column_0_name)s",
"ck": "ck_%(table_name)s_%(constraint_name)s",
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
"pk": "pk_%(table_name)s",
}
Base = declarative_base(metadata=MetaData(naming_convention=convention))
engine = create_engine(
config.database_url,
echo=False, # Set to True for SQL debugging
pool_pre_ping=True, # Verify connections before use
pool_recycle=300, # Recycle connections every 5 minutes
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
class User(Base):
__tablename__ = "users"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
session_id = Column(String(255), unique=True, index=True)
preferred_language = Column(String(10), default="en")
first_seen = Column(DateTime, default=lambda: datetime.now(timezone.utc))
last_seen = Column(DateTime, default=lambda: datetime.now(timezone.utc))
total_messages = Column(Integer, default=0)
tenant_id = Column(UUID(as_uuid=True), ForeignKey("tenants.id"), nullable=True)
# Relationships
conversations = relationship(
"Conversation", back_populates="user", cascade="all, delete-orphan"
)
preferences = relationship(
"UserPreference", back_populates="user", cascade="all, delete-orphan"
)
insights = relationship(
"UserInsight", back_populates="user", cascade="all, delete-orphan"
)
tenant = relationship("Tenant", back_populates="users")
def __repr__(self):
return (
f"<User(session_id={self.session_id}, language={self.preferred_language})>"
)
class Conversation(Base):
__tablename__ = "conversations"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False)
started_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
last_message_at = Column(DateTime, default=lambda: datetime.now(timezone.utc))
message_count = Column(Integer, default=0)
is_active = Column(Boolean, default=True)
tenant_id = Column(UUID(as_uuid=True), ForeignKey("tenants.id"), nullable=True)
# Relationship
user = relationship("User", back_populates="conversations")
messages = relationship(
"Message", back_populates="conversation", cascade="all, delete-orphan"
)
topics = relationship(
"ConversationTopic", back_populates="conversation", cascade="all, delete-orphan"
)
tenant = relationship("Tenant", back_populates="conversations")
def __repr__(self):
return f"<Conversation(id={self.id}, user_id={self.user_id}, messages={self.message_count})>"
class Message(Base):
__tablename__ = "messages"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
conversation_id = Column(
UUID(as_uuid=True), ForeignKey("conversations.id"), nullable=False
)
# Message content
user_input = Column(Text, nullable=False)
bot_response = Column(Text, nullable=False)
# NLP analysis
detected_language = Column(String(10))
intent = Column(String(100))
confidence = Column(Float)
entities = Column(Text) # JSON string
# Metadata
timestamp = Column(DateTime, default=lambda: datetime.now(timezone.utc))
response_time_ms = Column(Integer) # How long the bot took to respond
# Relationships
conversation = relationship("Conversation", back_populates="messages")
def __repr__(self):
return f"<Message(id={self.id}, intent={self.intent}, confidence={self.confidence})>"
def init_database():
# create all tables in the database
Base.metadata.create_all(bind=engine)
logger.info("Database tables created successfully")
def health_check():
"""Check if database connection is working"""
try:
with SessionLocal() as session:
from sqlalchemy import text # pylint: disable=import-outside-toplevel
session.execute(text("SELECT 1"))
logger.info("Database health check passed")
return True
except Exception as e: # pylint: disable=broad-exception-caught
logger.error("Database health check failed: %s: %s", type(e).__name__, str(e))
return False
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()