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:
100
README.md
Normal file
100
README.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Delphi Database
|
||||
|
||||
A legal case management database application built with FastAPI, SQLAlchemy, and modern Python practices.
|
||||
|
||||
## Database Configuration
|
||||
|
||||
The application uses SQLAlchemy ORM with support for multiple database backends.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Configure your database connection using environment variables:
|
||||
|
||||
```bash
|
||||
# SQLite (default for development)
|
||||
DATABASE_URL=sqlite:///./delphi.db
|
||||
|
||||
# PostgreSQL (production example)
|
||||
DATABASE_URL=postgresql://username:password@localhost:5432/delphi_db
|
||||
|
||||
# MySQL (alternative)
|
||||
DATABASE_URL=mysql://username:password@localhost:3306/delphi_db
|
||||
```
|
||||
|
||||
### Database Models
|
||||
|
||||
The application includes the following models:
|
||||
|
||||
- **User**: Authentication and user management
|
||||
- **Client**: Client/contact information
|
||||
- **Phone**: Phone numbers linked to clients
|
||||
- **Case**: Legal cases with unique file numbers
|
||||
- **Transaction**: Financial transactions for cases
|
||||
- **Document**: Case-related documents
|
||||
- **Payment**: Payment records for cases
|
||||
|
||||
### Database Connection Management
|
||||
|
||||
The `app/database.py` module provides:
|
||||
|
||||
- **Database engine configuration** with connection pooling and error handling
|
||||
- **Session management** with automatic cleanup via FastAPI dependency injection
|
||||
- **Table creation utilities** for application startup
|
||||
- **Connection health monitoring** via the `/health` endpoint
|
||||
|
||||
### Usage Examples
|
||||
|
||||
#### Basic Application Startup
|
||||
|
||||
```python
|
||||
from fastapi import FastAPI, Depends
|
||||
from sqlalchemy.orm import Session
|
||||
from app.database import get_db
|
||||
|
||||
@app.get("/clients/")
|
||||
async def get_clients(db: Session = Depends(get_db)):
|
||||
# Use the database session
|
||||
return db.query(Client).all()
|
||||
```
|
||||
|
||||
#### Health Check
|
||||
|
||||
The `/health` endpoint verifies database connectivity:
|
||||
|
||||
```bash
|
||||
curl http://localhost:8000/health
|
||||
# {"status": "healthy", "database": "connected", "users": 0}
|
||||
```
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Install dependencies:
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
2. Configure your database in environment variables (see `.env` file)
|
||||
|
||||
3. Run the application:
|
||||
```bash
|
||||
uvicorn app.main:app --reload
|
||||
```
|
||||
|
||||
4. Access the API at `http://localhost:8000`
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
app/
|
||||
├── main.py # FastAPI application entry point
|
||||
├── database.py # Database configuration and session management
|
||||
├── models.py # SQLAlchemy models
|
||||
└── templates/ # HTML templates
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
- Database tables are automatically created on application startup
|
||||
- Use the health check endpoint to verify database connectivity
|
||||
- Environment variables control database configuration (never hardcode credentials)
|
||||
- SQLAlchemy provides type-safe database operations with relationship management
|
||||
BIN
app/__pycache__/database.cpython-313.pyc
Normal file
BIN
app/__pycache__/database.cpython-313.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/main.cpython-313.pyc
Normal file
BIN
app/__pycache__/main.cpython-313.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/models.cpython-313.pyc
Normal file
BIN
app/__pycache__/models.cpython-313.pyc
Normal file
Binary file not shown.
91
app/database.py
Normal file
91
app/database.py
Normal 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 "****://****:****@****/****"
|
||||
86
app/main.py
86
app/main.py
@@ -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)
|
||||
}
|
||||
|
||||
182
app/models.py
182
app/models.py
@@ -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})>"
|
||||
|
||||
Reference in New Issue
Block a user