Set up database configuration and connection management

- Created app/database.py with SQLAlchemy engine, session management, and connection utilities
- Added comprehensive database models in app/models.py for User, Client, Phone, Case, Transaction, Document, and Payment
- Implemented FastAPI application with database lifecycle management in app/main.py
- Added health check endpoint to verify database connectivity
- Created README.md with database configuration documentation
- Verified database connection works correctly with SQLite backend
This commit is contained in:
HotSwapp
2025-10-06 18:22:18 -05:00
parent 36dffd5372
commit de983a73d2
8 changed files with 457 additions and 2 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

91
app/database.py Normal file
View File

@@ -0,0 +1,91 @@
"""
Database configuration and connection management for Delphi Database application.
This module handles SQLAlchemy engine creation, session management, and provides
database connection utilities for the FastAPI application.
"""
import os
from typing import Generator
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, Session
from dotenv import load_dotenv
# Load environment variables from .env file
load_dotenv()
# Database configuration from environment variables
DATABASE_URL = os.getenv(
"DATABASE_URL",
"sqlite:///./delphi.db" # Default to SQLite for development
)
# Create SQLAlchemy engine
engine = create_engine(
DATABASE_URL,
connect_args={"check_same_thread": False} if "sqlite" in DATABASE_URL else {},
pool_pre_ping=True, # Verify connections before reuse
echo=False # Set to True for SQL query logging in development
)
# Create session factory
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# Create declarative base for models
Base = declarative_base()
def get_db() -> Generator[Session, None, None]:
"""
Dependency function that provides a database session.
Yields a database session and ensures it's properly closed after use.
Used as a FastAPI dependency in route handlers.
Yields:
Session: SQLAlchemy database session
Example:
@app.get("/items/")
async def read_items(db: Session = Depends(get_db)):
return db.query(Item).all()
"""
db = SessionLocal()
try:
yield db
finally:
db.close()
def create_tables() -> None:
"""
Create all database tables defined in SQLAlchemy models.
This function should be called during application startup to ensure
all tables exist in the database.
"""
Base.metadata.create_all(bind=engine)
def get_database_url() -> str:
"""
Get the current database URL (with sensitive info masked).
Returns:
str: Database URL with password masked for logging
"""
if "sqlite" in DATABASE_URL:
return DATABASE_URL
# For PostgreSQL/MySQL, mask the password
if "@" in DATABASE_URL:
parts = DATABASE_URL.split("@")
if "://" in parts[0]:
protocol_and_auth = parts[0].split("://")[1]
if ":" in protocol_and_auth:
user_pass, host_port = protocol_and_auth.split(":", 1)
return DATABASE_URL.replace(user_pass, "****")
return "****://****:****@****/****"

View File

@@ -1 +1,85 @@
# FastAPI application entry point
"""
FastAPI application entry point for Delphi Database.
This module initializes the FastAPI application, sets up database connections,
and provides the main application instance.
"""
import logging
from contextlib import asynccontextmanager
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
from .database import create_tables, get_db, get_database_url
from .models import User
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
"""
Lifespan context manager for FastAPI application.
Handles startup and shutdown events:
- Creates database tables on startup
- Logs database connection info
"""
# Startup
logger.info("Starting Delphi Database application...")
# Create database tables
create_tables()
logger.info("Database tables created/verified")
# Log database connection info
db_url = get_database_url()
logger.info(f"Database connected: {db_url}")
yield
# Shutdown
logger.info("Shutting down Delphi Database application...")
# Create FastAPI application with lifespan management
app = FastAPI(
title="Delphi Database",
description="Legal case management database application",
version="1.0.0",
lifespan=lifespan
)
@app.get("/")
async def root():
"""
Root endpoint - health check.
"""
return {"message": "Delphi Database API is running"}
@app.get("/health")
async def health_check(db: Session = Depends(get_db)):
"""
Health check endpoint that verifies database connectivity.
"""
try:
# Test database connection by querying user count
user_count = db.query(User).count()
return {
"status": "healthy",
"database": "connected",
"users": user_count
}
except Exception as e:
logger.error(f"Health check failed: {e}")
return {
"status": "unhealthy",
"database": "error",
"error": str(e)
}

View File

@@ -1 +1,181 @@
# SQLAlchemy models for the Delphi database
"""
SQLAlchemy models for the Delphi database.
All models inherit from Base which is configured in the database module.
"""
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, Float, Text, Boolean
from sqlalchemy.orm import relationship
from sqlalchemy.sql import func
from .database import Base
class User(Base):
"""
User model for authentication.
Stores user credentials and basic information for login functionality.
"""
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
username = Column(String(50), unique=True, index=True, nullable=False)
password_hash = Column(String(255), nullable=False)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
def __repr__(self):
return f"<User(id={self.id}, username='{self.username}')>"
class Client(Base):
"""
Client model representing individuals or entities.
Core client information imported from ROLODEX data.
"""
__tablename__ = "clients"
id = Column(Integer, primary_key=True, index=True)
rolodex_id = Column(String(20), unique=True, index=True)
last_name = Column(String(50))
first_name = Column(String(50))
middle_initial = Column(String(10))
company = Column(String(100))
address = Column(String(255))
city = Column(String(50))
state = Column(String(2))
zip_code = Column(String(10))
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Relationships
phones = relationship("Phone", back_populates="client")
cases = relationship("Case", back_populates="client")
def __repr__(self):
return f"<Client(id={self.id}, name='{self.first_name} {self.last_name}')>"
class Phone(Base):
"""
Phone number model linked to clients.
Stores phone number information for clients.
"""
__tablename__ = "phones"
id = Column(Integer, primary_key=True, index=True)
client_id = Column(Integer, ForeignKey("clients.id"), nullable=False)
phone_type = Column(String(20)) # home, work, mobile, fax, etc.
phone_number = Column(String(20))
extension = Column(String(10))
created_at = Column(DateTime(timezone=True), server_default=func.now())
# Relationships
client = relationship("Client", back_populates="phones")
def __repr__(self):
return f"<Phone(id={self.id}, number='{self.phone_number}')>"
class Case(Base):
"""
Case model representing legal cases or files.
Main case information imported from FILES data.
"""
__tablename__ = "cases"
id = Column(Integer, primary_key=True, index=True)
file_no = Column(String(20), unique=True, index=True, nullable=False)
client_id = Column(Integer, ForeignKey("clients.id"), nullable=False)
status = Column(String(20), default="active")
case_type = Column(String(50))
description = Column(Text)
open_date = Column(DateTime(timezone=True))
close_date = Column(DateTime(timezone=True))
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
# Relationships
client = relationship("Client", back_populates="cases")
transactions = relationship("Transaction", back_populates="case")
documents = relationship("Document", back_populates="case")
payments = relationship("Payment", back_populates="case")
def __repr__(self):
return f"<Case(id={self.id}, file_no='{self.file_no}')>"
class Transaction(Base):
"""
Transaction model for financial transactions.
Records financial activities related to cases.
"""
__tablename__ = "transactions"
id = Column(Integer, primary_key=True, index=True)
case_id = Column(Integer, ForeignKey("cases.id"), nullable=False)
transaction_date = Column(DateTime(timezone=True))
transaction_type = Column(String(20))
amount = Column(Float)
description = Column(Text)
reference = Column(String(50))
created_at = Column(DateTime(timezone=True), server_default=func.now())
# Relationships
case = relationship("Case", back_populates="transactions")
def __repr__(self):
return f"<Transaction(id={self.id}, amount={self.amount})>"
class Document(Base):
"""
Document model for case-related documents.
Stores information about documents associated with cases.
"""
__tablename__ = "documents"
id = Column(Integer, primary_key=True, index=True)
case_id = Column(Integer, ForeignKey("cases.id"), nullable=False)
document_type = Column(String(50))
file_name = Column(String(255))
file_path = Column(String(500))
description = Column(Text)
uploaded_date = Column(DateTime(timezone=True))
created_at = Column(DateTime(timezone=True), server_default=func.now())
# Relationships
case = relationship("Case", back_populates="documents")
def __repr__(self):
return f"<Document(id={self.id}, file_name='{self.file_name}')>"
class Payment(Base):
"""
Payment model for payment records.
Records payments made or received for cases.
"""
__tablename__ = "payments"
id = Column(Integer, primary_key=True, index=True)
case_id = Column(Integer, ForeignKey("cases.id"), nullable=False)
payment_date = Column(DateTime(timezone=True))
payment_type = Column(String(20))
amount = Column(Float)
description = Column(Text)
check_number = Column(String(20))
created_at = Column(DateTime(timezone=True), server_default=func.now())
# Relationships
case = relationship("Case", back_populates="payments")
def __repr__(self):
return f"<Payment(id={self.id}, amount={self.amount})>"