import uuid from datetime import datetime from typing import Optional, List from sqlalchemy import ( create_engine, Column, String, Float, Integer, Boolean, Text, ForeignKey, DateTime, JSON ) from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmaker, relationship import os from dotenv import load_dotenv load_dotenv() DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./innervoice.db") engine = create_engine( DATABASE_URL, connect_args={"check_same_thread": False} if "sqlite" in DATABASE_URL else {} ) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) Base = declarative_base() def get_db(): db = SessionLocal() try: yield db finally: db.close() class User(Base): __tablename__ = "users" id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4())) email = Column(String, unique=True, nullable=False, index=True) name = Column(String, nullable=False) password_hash = Column(String, nullable=False) created_at = Column(DateTime, default=datetime.utcnow) baseline_pitch = Column(Float, nullable=True) baseline_energy = Column(Float, nullable=True) baseline_speech_rate = Column(Float, nullable=True) voice_entries = relationship("VoiceEntry", back_populates="user", cascade="all, delete-orphan") mood_alerts = relationship("MoodAlert", back_populates="user", cascade="all, delete-orphan") chat_messages = relationship("ChatMessage", back_populates="user", cascade="all, delete-orphan") chat_channels = relationship("ChatChannel", back_populates="user", cascade="all, delete-orphan") trusted_members = relationship("TrustedMember", back_populates="user", cascade="all, delete-orphan") class VoiceEntry(Base): __tablename__ = "voice_entries" id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4())) user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True) created_at = Column(DateTime, default=datetime.utcnow) audio_url = Column(String, nullable=True) duration_seconds = Column(Float, nullable=True) transcription = Column(Text, nullable=True) primary_emotion = Column(String, nullable=True) emotion_confidence = Column(Float, nullable=True) energy_score = Column(Integer, nullable=True) calmness_score = Column(Integer, nullable=True) mood_score = Column(Integer, nullable=True) clarity_score = Column(Integer, nullable=True) pitch_mean = Column(Float, nullable=True) pitch_std = Column(Float, nullable=True) energy_raw = Column(Float, nullable=True) speech_rate = Column(Float, nullable=True) pause_count = Column(Integer, nullable=True) avg_pause_duration = Column(Float, nullable=True) filler_rate = Column(Float, nullable=True) mfcc_features = Column(JSON, nullable=True) sleep_hours = Column(Float, nullable=True) # hours slept before this check-in user = relationship("User", back_populates="voice_entries") chat_messages = relationship("ChatMessage", back_populates="voice_entry") class MoodAlert(Base): __tablename__ = "mood_alerts" id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4())) user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True) created_at = Column(DateTime, default=datetime.utcnow) alert_type = Column(String, nullable=False) severity = Column(String, nullable=False) message = Column(Text, nullable=False) is_read = Column(Boolean, default=False) suggested_action = Column(Text, nullable=True) user = relationship("User", back_populates="mood_alerts") class ChatChannel(Base): __tablename__ = "chat_channels" id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4())) user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True) title = Column(String, nullable=False, default="New Chat") created_at = Column(DateTime, default=datetime.utcnow) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) user = relationship("User", back_populates="chat_channels") messages = relationship("ChatMessage", back_populates="channel", cascade="all, delete-orphan") class ChatMessage(Base): __tablename__ = "chat_messages" id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4())) user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True) channel_id = Column(String, ForeignKey("chat_channels.id"), nullable=True, index=True) created_at = Column(DateTime, default=datetime.utcnow) role = Column(String, nullable=False) # user / assistant content = Column(Text, nullable=False) voice_entry_id = Column(String, ForeignKey("voice_entries.id"), nullable=True) user = relationship("User", back_populates="chat_messages") channel = relationship("ChatChannel", back_populates="messages") voice_entry = relationship("VoiceEntry", back_populates="chat_messages") class TrustedMember(Base): __tablename__ = "trusted_members" id = Column(String, primary_key=True, default=lambda: str(uuid.uuid4())) user_id = Column(String, ForeignKey("users.id"), nullable=False, index=True) email = Column(String, nullable=False) created_at = Column(DateTime, default=datetime.utcnow) is_active = Column(Boolean, default=True) user = relationship("User", back_populates="trusted_members") def init_db(): Base.metadata.create_all(bind=engine)