Coverage for src / local_deep_research / database / initialize.py: 92%
51 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-14 23:55 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-14 23:55 +0000
1"""
2Centralized database initialization module.
4This module provides a single entry point for database initialization,
5using Alembic for schema migrations and version control.
6"""
8from typing import Any, Optional
9from loguru import logger
10from sqlalchemy import Engine, inspect
11from sqlalchemy.orm import Session
13from ..database.models import Base
14from .alembic_runner import run_migrations
17def initialize_database(
18 engine: Engine,
19 db_session: Optional[Session] = None,
20) -> None:
21 """
22 Initialize database tables if they don't exist.
24 Uses Alembic migrations to create and update the database schema.
26 Args:
27 engine: SQLAlchemy engine for the database
28 db_session: Optional database session for settings initialization
29 """
30 inspector = inspect(engine)
31 existing_tables = inspector.get_table_names()
33 logger.info(
34 f"Initializing database with {len(existing_tables)} existing tables"
35 )
36 logger.debug(
37 f"Base.metadata has {len(Base.metadata.tables)} tables defined"
38 )
40 # Run Alembic migrations (creates tables and applies schema changes)
41 run_migrations(engine)
43 # Check what was created (need new inspector to avoid caching)
44 new_inspector = inspect(engine)
45 new_tables = new_inspector.get_table_names()
46 logger.info(f"After initialization: {len(new_tables)} tables exist")
48 # Initialize default settings if session provided
49 if db_session:
50 try:
51 _initialize_default_settings(db_session)
52 except Exception:
53 logger.warning("Could not initialize default settings")
55 logger.info("Database initialization complete")
58def _initialize_default_settings(db_session: Session) -> None:
59 """
60 Initialize default settings from the defaults file.
62 Args:
63 db_session: Database session to use for settings initialization
64 """
65 from ..settings.manager import SettingsManager
67 try:
68 settings_mgr = SettingsManager(db_session)
70 # Check if we need to update settings
71 if settings_mgr.db_version_matches_package():
72 logger.debug("Settings version matches package, skipping update")
73 return
75 logger.info("Loading default settings into database")
77 # Load settings from defaults file
78 # This will not overwrite existing settings but will add new ones
79 settings_mgr.load_from_defaults_file(overwrite=False, delete_extra=True)
81 # Update the saved version
82 settings_mgr.update_db_version()
84 logger.info("Default settings initialized successfully")
86 except Exception:
87 logger.exception("Error initializing default settings")
90def check_database_schema(engine: Engine) -> dict:
91 """
92 Check the current database schema and return information about tables.
94 Args:
95 engine: SQLAlchemy engine for the database
97 Returns:
98 Dictionary with schema information including tables and their columns
99 """
100 inspector = inspect(engine)
101 schema_info: dict[str, Any] = {
102 "tables": {},
103 "missing_tables": [],
104 "has_news_tables": False,
105 }
107 # Tables that only exist in the auth database, not per-user databases
108 auth_only_tables = {"users"}
110 # Check core tables
111 for table_name in Base.metadata.tables.keys():
112 if table_name in auth_only_tables:
113 continue
114 if inspector.has_table(table_name):
115 columns = [col["name"] for col in inspector.get_columns(table_name)]
116 schema_info["tables"][table_name] = columns
117 else:
118 schema_info["missing_tables"].append(table_name)
120 # Check if news tables exist
121 news_tables = ["news_subscription", "news_card", "news_interest"]
122 for table_name in news_tables:
123 if table_name in schema_info["tables"]: 123 ↛ 124line 123 didn't jump to line 124 because the condition on line 123 was never true
124 schema_info["has_news_tables"] = True
125 break
127 return schema_info