Source code for astrodb_utils.loaders

import argparse
import logging
import os
import sys
import tomllib
from dataclasses import dataclass

from astrodbkit.astrodb import Database, create_database

from astrodb_utils.utils import AstroDBError

__all__ = [
    "build_db_from_json",
    "read_db_from_file",
    "DatabaseSettings",
]

logger = logging.getLogger(__name__)


[docs] @dataclass class DatabaseSettings: """Database settings dataclass""" settings_file: str base_path: str = "." db_name: str = None felis_path: str = "schema.yaml" data_path: str = "data" lookup_tables: list = None # default cannot be mutable list def __post_init__(self): # Use a base_path if provided, otherwise default to the current directory if self.base_path is None: self.base_path = "." self.settings_file = os.path.join(self.base_path, self.settings_file) self.settings_file = self._check_path(self.settings_file) # Read the settings file self.settings = self._read_settings() # Prepare and check the Felis path self.felis_path = os.path.join(self.base_path, self.settings.get("felis_path", self.felis_path)) self.felis_path = self._check_path(self.felis_path) # Prepare and check the data path self.data_path = os.path.join(self.base_path, self.settings.get("data_path", self.data_path)) self.data_path = self._check_path(self.data_path) # Prepare the lookup tables self.lookup_tables = [ "Publications", "Telescopes", "Instruments", "Versions", "PhotometryFilters", "Regimes", "AssociationList", "ParameterList", "CompanionList", "SourceTypeList", ] self.lookup_tables = self.settings.get("lookup_tables", self.lookup_tables) # Prepare and check the database name if self.db_name is None: self.db_name = self.settings["db_name"] def _check_path(self, path): if not os.path.exists(path): raise AstroDBError(f"{path} does not exist.") return path def _read_settings(self): with open(self.settings_file, "rb") as f: settings = tomllib.load(f) return settings
[docs] def build_db_from_json( # noqa: PLR0913 settings_file: str = "database.toml", *, base_path: str = ".", db_name: str = None, felis_path: str = None, data_path: str = None, lookup_tables: list = None, ): """Build an SQLite database from JSON files. Default is to get the database settings from a TOML settings file. Creates the database .sqlite file in the current directory. If a database file with the same name already exists, it is removed. This creates a .sqlite database file. Inputs ------ settings_file : str Name of the TOML file containing the database settings Default: database.toml base_path : str Path to the directory containing the TOML file Default: None, assumes TOML file is in current directory db_name : str Name of the database file (without .sqlite extension) Default: None, reads from TOML file felis_path : str Path to the Felis schema file Default: None, reads from TOML file data_path : str Path to the data directory containing the JSON files Default: None, reads from TOML file lookup_tables : list List of tables to consider as lookup tables. Default: None, reads from TOML file Returns ------- db : astrodbkit.astrodb.Database Astrodbkit Database object """ # Read and validate the database settings db_settings = DatabaseSettings( settings_file=settings_file, base_path=base_path, db_name=db_name, felis_path=felis_path, data_path=data_path, lookup_tables=lookup_tables, ) db_file = db_settings.db_name + ".sqlite" if os.path.exists(db_file): os.remove(db_file) msg = f"Removed old database file {db_file}." logger.info(msg) logger.info(f"Creating new database file: {db_file}") db_connection_string = "sqlite:///" + db_file # Create database create_database(db_connection_string, felis_schema=db_settings.felis_path) # Connect and load the database db = Database(db_connection_string, lookup_tables=db_settings.lookup_tables) if logger.parent.level <= 10: # noqa: PLR2004 db.load_database(db_settings.data_path, verbose=True) else: db.load_database(db_settings.data_path) return db
[docs] def read_db_from_file(db_name: str, db_path: str = None): """Read an SQLite database from a file. Parameters ---------- db_name : str Name of the database file (without .sqlite extension) db_path : str, optional Path to the directory containing the database .sqlite file Default: None, assumes database file is in current directory Returns ------- db : astrodbkit.astrodb.Database Astrodbkit Database object """ if db_path is not None: db_file = os.path.join(db_path, db_name + ".sqlite") else: db_file = db_name + ".sqlite" db_connection_string = "sqlite:///" + db_file logger.debug(f"Database connection string: {db_connection_string}") db = Database(db_connection_string) return db
def main(): """Command-line interface for building a database from JSON files.""" parser = argparse.ArgumentParser( description="Build an SQLite database from JSON files using a TOML configuration.", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" Examples: python -m astrodb_utils.loaders database.toml python -m astrodb_utils.loaders database.toml --base-path /path/to/data python -m astrodb_utils.loaders database.toml --db-name my_database --data-path data/ """, ) parser.add_argument( "settings_file", nargs="?", default="database.toml", help="Name of the TOML file containing database settings (default: database.toml)", ) parser.add_argument( "--base-path", default=".", help="Path to the directory containing the TOML file (default: current directory)", ) parser.add_argument( "--db-name", default=None, help="Name of the database file without .sqlite extension (overrides TOML setting)", ) parser.add_argument( "--felis-path", default=None, help="Path to the Felis schema file (overrides TOML setting)", ) parser.add_argument( "--data-path", default=None, help="Path to the data directory containing JSON files (overrides TOML setting)", ) parser.add_argument( "--lookup-tables", nargs="+", default=None, help="List of tables to consider as lookup tables (overrides TOML setting)", ) parser.add_argument( "-v", "--verbose", action="store_true", help="Enable verbose logging output", ) args = parser.parse_args() # Set logging level if args.verbose: logging.basicConfig(level=logging.DEBUG) else: logging.basicConfig(level=logging.INFO) try: logger.info(f"Building database from {args.settings_file}...") _ = build_db_from_json( settings_file=args.settings_file, base_path=args.base_path, db_name=args.db_name, felis_path=args.felis_path, data_path=args.data_path, lookup_tables=args.lookup_tables, ) logger.info("Database created successfully!") return 0 except Exception as e: logger.error(f"Error building database: {e}") return 1 if __name__ == "__main__": sys.exit(main())