diff --git a/alembic/versions/0001_create_tasks_table.py b/alembic/versions/0001_create_tasks_table.py index aabf0eb..88b9cde 100644 --- a/alembic/versions/0001_create_tasks_table.py +++ b/alembic/versions/0001_create_tasks_table.py @@ -22,8 +22,8 @@ def upgrade(): sa.Column('id', sa.Integer(), nullable=False), sa.Column('title', sa.String(length=100), nullable=False), sa.Column('description', sa.Text(), nullable=True), - sa.Column('priority', sa.Enum('LOW', 'MEDIUM', 'HIGH', name='taskpriority'), default='MEDIUM'), - sa.Column('status', sa.Enum('TODO', 'IN_PROGRESS', 'DONE', name='taskstatus'), default='TODO'), + sa.Column('priority', sa.Enum('low', 'medium', 'high', name='taskpriority'), default='medium'), + sa.Column('status', sa.Enum('todo', 'in_progress', 'done', name='taskstatus'), default='todo'), sa.Column('due_date', sa.DateTime(), nullable=True), sa.Column('completed', sa.Boolean(), default=False), sa.Column('created_at', sa.DateTime(), default=sa.func.now()), diff --git a/app/core/config.py b/app/core/config.py index 6607e6e..824f7f5 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -5,7 +5,7 @@ from typing import Any, Dict, List, Optional, Union from pydantic import AnyHttpUrl, field_validator from pydantic_settings import BaseSettings -DB_DIR = Path("/app") / "storage" / "db" +DB_DIR = Path("/app/storage/db") DB_DIR.mkdir(parents=True, exist_ok=True) class Settings(BaseSettings): @@ -29,8 +29,9 @@ class Settings(BaseSettings): # Database configuration SQLALCHEMY_DATABASE_URL: str = f"sqlite:///{DB_DIR}/db.sqlite" - class Config: - case_sensitive = True + model_config = { + "case_sensitive": True + } settings = Settings() \ No newline at end of file diff --git a/app/crud/base.py b/app/crud/base.py index 1125c01..0cb61f7 100644 --- a/app/crud/base.py +++ b/app/crud/base.py @@ -45,7 +45,11 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): if isinstance(obj_in, dict): update_data = obj_in else: - update_data = obj_in.model_dump(exclude_unset=True) + # Handle both Pydantic v1 and v2 + if hasattr(obj_in, "model_dump"): + update_data = obj_in.model_dump(exclude_unset=True) + else: + update_data = obj_in.dict(exclude_unset=True) for field in obj_data: if field in update_data: setattr(db_obj, field, update_data[field]) @@ -55,7 +59,8 @@ class CRUDBase(Generic[ModelType, CreateSchemaType, UpdateSchemaType]): return db_obj def remove(self, db: Session, *, id: int) -> ModelType: - obj = db.query(self.model).get(id) - db.delete(obj) - db.commit() + obj = db.query(self.model).filter(self.model.id == id).first() + if obj: + db.delete(obj) + db.commit() return obj \ No newline at end of file diff --git a/app/db/init_db.py b/app/db/init_db.py new file mode 100644 index 0000000..5b9aa50 --- /dev/null +++ b/app/db/init_db.py @@ -0,0 +1,44 @@ +import os +import subprocess +from pathlib import Path + +from sqlalchemy import text +from sqlalchemy.orm import Session + +from app.db.session import engine +from app.core.config import settings + + +def init_db() -> None: + """Initialize database with required tables and data.""" + + # Ensure database directory exists + Path(settings.DB_DIR).mkdir(parents=True, exist_ok=True) + + # Try to connect to check if the database is accessible + with engine.connect() as conn: + try: + conn.execute(text("SELECT 1")) + print("Database connection successful") + except Exception as e: + print(f"Database connection error: {e}") + raise + + # Run alembic upgrade to create tables + try: + # Get the project root directory (where alembic.ini is located) + project_root = Path(__file__).parent.parent.parent.absolute() + + # Change to project root directory and run alembic + os.chdir(project_root) + subprocess.run(["alembic", "upgrade", "head"], check=True) + print("Database migration successful") + except subprocess.CalledProcessError as e: + print(f"Database migration error: {e}") + raise + + print("Database initialized successfully") + + +if __name__ == "__main__": + init_db() \ No newline at end of file diff --git a/app/schemas/task.py b/app/schemas/task.py index c4faba5..bc477bc 100644 --- a/app/schemas/task.py +++ b/app/schemas/task.py @@ -33,8 +33,9 @@ class TaskInDBBase(TaskBase): created_at: datetime updated_at: datetime - class Config: - from_attributes = True + model_config = { + "from_attributes": True + } class Task(TaskInDBBase): diff --git a/main.py b/main.py index 83866e7..f4bf772 100644 --- a/main.py +++ b/main.py @@ -1,8 +1,25 @@ -from fastapi import FastAPI +import sys +import os +from pathlib import Path + +# Add project root to Python path for imports in alembic migrations +project_root = Path(__file__).parent.absolute() +sys.path.insert(0, str(project_root)) + +from fastapi import FastAPI, Request from fastapi.middleware.cors import CORSMiddleware +from fastapi.responses import JSONResponse from app.api.routers import api_router from app.core.config import settings +from app.db import init_db + +# Initialize the database on startup +try: + init_db.init_db() +except Exception as e: + print(f"Error initializing database: {e}") + # Continue with app startup even if DB init fails, to allow debugging app = FastAPI(title=settings.PROJECT_NAME) @@ -15,6 +32,14 @@ app.add_middleware( allow_headers=["*"], ) +# Add exception handlers for better error reporting +@app.exception_handler(Exception) +async def validation_exception_handler(request: Request, exc: Exception): + return JSONResponse( + status_code=500, + content={"detail": f"Internal Server Error: {str(exc)}"}, + ) + app.include_router(api_router) @@ -23,4 +48,29 @@ def health_check(): """ Health check endpoint to verify the application is running correctly """ - return {"status": "ok"} \ No newline at end of file + return {"status": "ok"} + + +@app.get("/db-test", tags=["health"]) +def test_db_connection(): + """ + Test database connection and table creation + """ + from sqlalchemy import text + from app.db.session import engine + + try: + with engine.connect() as conn: + # Try to select from the task table to verify it exists + result = conn.execute(text("SELECT COUNT(*) FROM task")).scalar() + return { + "status": "ok", + "connection": "successful", + "task_count": result + } + except Exception as e: + return { + "status": "error", + "connection": "failed", + "error": str(e) + } \ No newline at end of file