204 lines
7.6 KiB
Python

import os
import sys
import time
import sqlite3
from pathlib import Path
from datetime import datetime, timedelta
from sqlalchemy import inspect, text
from sqlalchemy.exc import OperationalError
from app.db.base import Base # Import all models
from app.db.session import engine, db_file
from app.core.config import settings
def init_db() -> None:
"""
Initialize database with required tables directly using both SQLAlchemy and
SQLite native commands for maximum reliability.
"""
print(f"Initializing database at {db_file}")
db_dir = db_file.parent
# Ensure database directory exists
try:
db_dir.mkdir(parents=True, exist_ok=True)
print(f"Database directory created or already exists: {db_dir}")
except Exception as e:
print(f"Error creating database directory: {e}")
# First, try to create an empty database file if it doesn't exist
if not db_file.exists():
try:
# Create an empty file
db_file.touch()
print(f"Created empty database file: {db_file}")
except Exception as e:
print(f"Failed to create database file: {e}")
# First, try direct SQLite connection to create basic structure
# This bypasses SQLAlchemy entirely for the initial database creation
try:
print(f"Attempting direct SQLite connection to {db_file}")
sqlite_conn = sqlite3.connect(str(db_file))
sqlite_conn.execute("PRAGMA journal_mode=WAL")
sqlite_conn.execute("CREATE TABLE IF NOT EXISTS _db_init_check (id INTEGER PRIMARY KEY)")
sqlite_conn.execute("INSERT OR IGNORE INTO _db_init_check VALUES (1)")
sqlite_conn.commit()
sqlite_conn.close()
print("Direct SQLite connection and initialization successful")
except Exception as e:
print(f"Direct SQLite initialization error: {e}")
# Now try with SQLAlchemy
try:
# Try to connect to check if the database is accessible
max_retries = 5
retry_count = 0
connected = False
while retry_count < max_retries and not connected:
try:
with engine.connect() as conn:
result = conn.execute(text("SELECT 1")).scalar()
print(f"Database connection successful. Test query result: {result}")
connected = True
except Exception as e:
retry_count += 1
print(f"Database connection error (attempt {retry_count}/{max_retries}): {e}")
time.sleep(1) # Wait a second before retrying
if not connected:
print(f"Failed to connect to database after {max_retries} attempts")
# Continue anyway to see if we can make progress
# Try to create tables
try:
print("Creating database tables with SQLAlchemy...")
Base.metadata.create_all(bind=engine)
# Verify tables
inspector = inspect(engine)
tables = inspector.get_table_names()
print(f"Tables in database: {', '.join(tables)}")
if 'task' not in tables:
print("WARNING: 'task' table not created!")
else:
print("'task' table successfully created.")
except Exception as e:
print(f"Error creating tables: {e}")
# Print database info for debugging
try:
print(f"Database file size: {os.path.getsize(db_file)} bytes")
print(f"Database file permissions: {oct(os.stat(db_file).st_mode)[-3:]}")
print(f"Database dir permissions: {oct(os.stat(db_dir).st_mode)[-3:]}")
except Exception as e:
print(f"Error getting file info: {e}")
print("Database initialization completed")
except Exception as e:
print(f"Database initialization error: {str(e)}")
print("Continuing anyway...")
def create_test_task():
"""Create a test task in the database to verify everything is working."""
print("Attempting to create a test task...")
try:
# Try direct SQLite approach first
try:
conn = sqlite3.connect(str(db_file))
cursor = conn.cursor()
# Check if task table exists
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='task'")
if not cursor.fetchone():
print("Task table doesn't exist - cannot create test task")
return
# Check if any tasks exist
cursor.execute("SELECT COUNT(*) FROM task")
count = cursor.fetchone()[0]
if count == 0:
# Create a task directly with SQLite
now = datetime.utcnow().isoformat()
cursor.execute(
"""
INSERT INTO task (
title, description, priority, status,
completed, created_at, updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?)
""",
(
"Test Task (Direct SQL)",
"This is a test task created directly with SQLite",
"medium",
"todo",
0, # not completed
now,
now
)
)
conn.commit()
task_id = cursor.lastrowid
print(f"Created test task with direct SQLite, ID: {task_id}")
else:
print(f"Found {count} existing tasks, no need to create test task")
conn.close()
except Exception as e:
print(f"Error with direct SQLite test task creation: {e}")
# Continue with SQLAlchemy approach
# Now try with SQLAlchemy
try:
from app.crud.task import task as task_crud
from app.schemas.task import TaskCreate
from app.db.session import SessionLocal
db = SessionLocal()
try:
# Check if there are any tasks
try:
existing_tasks = db.execute(text("SELECT COUNT(*) FROM task")).scalar()
if existing_tasks > 0:
print(f"Test task not needed, found {existing_tasks} existing tasks")
return
except Exception as e:
print(f"Error checking for existing tasks: {e}")
# Continue anyway to try creating a task
# Create a test task
test_task = TaskCreate(
title="Test Task (SQLAlchemy)",
description="This is a test task created with SQLAlchemy",
priority="medium",
status="todo",
due_date=datetime.utcnow() + timedelta(days=7),
completed=False
)
created_task = task_crud.create(db, obj_in=test_task)
print(f"Created test task with SQLAlchemy, ID: {created_task.id}")
finally:
db.close()
except Exception as e:
print(f"Error with SQLAlchemy test task creation: {e}")
except Exception as e:
print(f"Global error creating test task: {e}")
import traceback
print(traceback.format_exc())
if __name__ == "__main__":
init_db()
create_test_task()