Files
AirlockTools/utils/setup.py
T
Zarithas 89654d3a8c
Build Library / Build Library (push) Failing after 5m55s
Config Refactor: unify config handling
- Consolidated all system/user config logic into configmanager.py
- Removed duplicate loaders from setup.py and TUI.py
- Eliminated .env redundancy; now only stores WORKING_DIR
- Clarified boundaries: system config immutable, user config mutable
- Updated TUI to use save_user_config()
- Removed all deprecated/legacy config functions and aliases
2025-11-17 12:00:32 -05:00

161 lines
5.8 KiB
Python

# Copyright (C) 2025 James Brotosky, Brandon Wickline
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import logging
import logging.config
import logging.handlers
import os
from pathlib import Path
import platform
from dotenv import load_dotenv, set_key
from utils.configmanager import (
get_system_value,
load_system_config,
load_user_config,
)
def get_base_directory() -> Path:
system = platform.system()
home = Path.home()
if system == "Windows":
return Path(os.getenv("APPDATA", home / "AppData" / "Roaming")) / "Loxide"
elif system == "Darwin":
return home / "Library" / "Application Support" / "Loxide"
else:
return home / ".local" / "share" / "Loxide"
def configure_logging(log_dir: Path, log_level: str = "INFO"):
log_file = log_dir / "Loxide.log"
config = {
"version": 1, # Required key for dictConfig format version
"disable_existing_loggers": False, # Keeps existing loggers active
"formatters": {
"detailed": {
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
# Includes timestamp, logger name, level, and message
},
"simple": {
"format": "%(levelname)s - %(message)s"
# Minimal format for console output
},
},
"handlers": {
"file": {
"class": "logging.handlers.TimedRotatingFileHandler",
"filename": str(log_file),
"when": "midnight", # Rotate logs at midnight
"interval": 1, # Every 1 day
"backupCount": 7, # Keep 7 days of logs
"encoding": "utf-8", # Ensure UTF-8 encoding
"level": "DEBUG", # Always log DEBUG and above to file
"formatter": "detailed", # Use detailed format
},
"console": {
"class": "logging.StreamHandler",
"level": log_level.upper(), # System-configured level for console
"formatter": "simple", # Use simple format
},
},
"root": {
"level": "DEBUG", # Root logger level
"handlers": ["file", "console"], # Attach both handlers
},
}
# Add Windows Event Log handler if on Windows
if platform.system() == "Windows":
try:
config["handlers"]["eventlog"] = {
"class": "logging.handlers.NTEventLogHandler",
"appname": "Loxide", # Event log source name
"level": "CRITICAL", # Only log critical errors
"formatter": "simple", # Use simple format
}
config["root"]["handlers"].append("eventlog")
except Exception as e:
logging.warning(f"Could not attach Windows Event Log handler: {e}")
# Apply the logging configuration
logging.config.dictConfig(config)
logging.getLogger().debug("✅ Logging configured.")
def setup():
"""
Initialize the application environment:
1. Create directory structure
2. Load system config (immutable, from system_config.json)
3. Load user config (mutable, from user_config.json)
4. Configure logging
5. Set up .env with WORKING_DIR only
"""
base_dir = get_base_directory()
dirs = {
"config": base_dir / "config",
"cache": base_dir / "cache",
"logs": base_dir / "logs",
}
for name, path in dirs.items():
path.mkdir(parents=True, exist_ok=True)
logging.debug(f"{name.capitalize()} directory ensured at: {path}")
# Load system config (immutable)
system_config = load_system_config()
# Configure logging with system-defined log level
log_level = get_system_value("LOG_LEVEL", str, "INFO")
configure_logging(dirs["logs"], log_level)
# Load user config (mutable)
user_config = load_user_config(dirs["config"])
# Set up .env file - ONLY for WORKING_DIR (runtime-configurable value)
env_path = base_dir / ".env"
if not env_path.exists():
env_path.touch()
load_dotenv(dotenv_path=env_path, override=True)
# Set up working directory (only dynamic value in .env)
working_dir = Path(os.getenv("WORKING_DIR") or (base_dir / "data"))
working_dir.mkdir(parents=True, exist_ok=True)
set_key(str(env_path), "WORKING_DIR", str(working_dir))
os.environ["WORKING_DIR"] = str(working_dir)
logging.debug(f"Working directory set to: {working_dir}")
# Create folder structure in working directory
folders_structure = {
"Approved": [],
"Needs_Review": ["Review_First", "Review_Second", "HTML"],
"Preflight": ["HTML"],
"Archived": [],
}
for folder_name, subfolders in folders_structure.items():
folder_path = working_dir / folder_name
folder_path.mkdir(parents=True, exist_ok=True)
logging.debug(f"'{folder_name}' folder ensured at: {folder_path}")
for subfolder in subfolders:
subfolder_path = folder_path / subfolder
subfolder_path.mkdir(parents=True, exist_ok=True)
logging.debug(f" └─ '{subfolder}' subfolder created at: {subfolder_path}")
logging.info("✅ Setup complete")