298 lines
12 KiB
Python
298 lines
12 KiB
Python
"""
|
|
utils.py
|
|
|
|
This module contains two classes, ConfigLoader and AppConfig, which are used to manage application-specific configuration settings.
|
|
|
|
The ConfigLoader class provides methods for loading a configuration file, applying overrides, and restoring default values for specified keys. It also includes methods for recursively updating nested keys and getting the default configuration.
|
|
|
|
The AppConfig class extends ConfigLoader and provides additional methods for setting global variables, launch options, and layout options from the configuration. It also includes methods for checking and setting file paths, and getting layout options.
|
|
|
|
Classes:
|
|
ConfigLoader: Manages application-specific configuration settings.
|
|
AppConfig: Extends ConfigLoader to provide additional methods for managing application-specific configuration settings.
|
|
"""
|
|
import os
|
|
import warnings
|
|
import yaml
|
|
from typing import Any, Dict, Optional
|
|
|
|
import scraibe.app.global_var as gv
|
|
|
|
CURRENT_PATH = os.path.dirname(os.path.realpath(__file__))
|
|
|
|
class ConfigLoader:
|
|
"""A class that extends ConfigLoader to manage application-specific configuration settings.
|
|
|
|
This class provides methods for setting global variables, launch options, and layout options from the configuration.
|
|
|
|
Attributes:
|
|
config (Dict[str, Any]): The current configuration settings.
|
|
launch (Dict[str, Any]): The launch configuration settings.
|
|
model (Dict[str, Any]): The model configuration settings.
|
|
advanced (Dict[str, Any]): The advanced configuration settings.
|
|
queue (Dict[str, Any]): The queue configuration settings.
|
|
layout (Dict[str, Any]): The layout configuration settings.
|
|
"""
|
|
def __init__(self, config: Dict[str, Any]):
|
|
"""Initializes a new instance of the ConfigLoader class.
|
|
|
|
Args:
|
|
config (dict): The configuration dictionary.
|
|
"""
|
|
self.config = config
|
|
|
|
def restore_defaults_for_keys(self, *args: str):
|
|
"""Restores specified keys to their default values, including nested keys.
|
|
|
|
Args:
|
|
*args (str): A list of keys or paths to keys (for nested dictionaries) to restore to default values.
|
|
Each key or path should be a list of keys leading to the desired key.
|
|
"""
|
|
default_config = self.get_default_config()
|
|
|
|
for key in args:
|
|
self.apply_overrides(self.config, default_config, key)
|
|
|
|
|
|
|
|
@classmethod
|
|
def load_config(cls, yaml_path: Optional[str] = None, **kwargs: Any) -> 'ConfigLoader':
|
|
"""Load the configuration file and apply overrides.
|
|
|
|
Args:
|
|
yaml_path (str, optional): Path to the YAML file containing overrides.
|
|
**kwargs: Additional overrides as keyword arguments.
|
|
|
|
Returns:
|
|
ConfigLoader: A ConfigLoader object with the loaded configuration.
|
|
"""
|
|
|
|
# Load the original configuration
|
|
config = cls.get_default_config()
|
|
|
|
# Override with another YAML file if provided
|
|
|
|
if yaml_path:
|
|
with open(yaml_path, 'r') as file:
|
|
override_config = yaml.safe_load(file)
|
|
cls.apply_overrides(config, override_config)
|
|
|
|
# Apply overrides from kwargs
|
|
cls.apply_overrides(config, kwargs)
|
|
return cls(config)
|
|
|
|
@staticmethod
|
|
def apply_overrides(orig_dict: Dict[str, Any], override_dict: Dict[str, Any], specific: Optional[str] = None):
|
|
"""Recursively apply overrides to the configuration, only for specific keys.
|
|
|
|
Args:
|
|
orig_dict (Dict[str, Any]): The original dictionary.
|
|
override_dict (Dict[str, Any]): The override dictionary.
|
|
specific (str, optional): The specific key to override.
|
|
"""
|
|
for key, value in override_dict.items():
|
|
|
|
if isinstance(value, dict):
|
|
# If the value is a dict, apply recursively
|
|
sub_dict = orig_dict.get(key, {})
|
|
ConfigLoader.apply_overrides(sub_dict, value, specific)
|
|
orig_dict[key] = sub_dict
|
|
else:
|
|
# Apply override for this key
|
|
if specific is None:
|
|
# If no specific keys are provided, update the key
|
|
# If the value is not a dict, search for the key and update
|
|
if ConfigLoader.update_nested_key(orig_dict, key, value):
|
|
continue # Key was found and updated
|
|
orig_dict[key] = value # Key not found, update at this level
|
|
|
|
elif key in specific:
|
|
# If specific keys are provided, only update if the key is in the list
|
|
if ConfigLoader.update_nested_key(orig_dict, specific, value):
|
|
continue # Key was found and updated
|
|
orig_dict[specific] = value
|
|
|
|
@staticmethod
|
|
def update_nested_key(d, key, value):
|
|
"""Recursively search and update the key in nested dictionary.
|
|
|
|
Args:
|
|
d (Dict[str, Any]): The dictionary.
|
|
key (str): The key to update.
|
|
value (Any): The new value.
|
|
|
|
Returns:
|
|
bool: True if the key was found and updated, False otherwise.
|
|
"""
|
|
|
|
if key in d:
|
|
d[key] = value
|
|
return True
|
|
for k, v in d.items():
|
|
if isinstance(v, dict) and ConfigLoader.update_nested_key(v, key, value):
|
|
return True
|
|
return False
|
|
|
|
@staticmethod
|
|
def get_default_config():
|
|
"""Return the default configuration.
|
|
|
|
Returns:
|
|
Dict[str, Any]: The default configuration.
|
|
"""
|
|
with open(gv.DEFAULT_APP_CONIFG_PATH , 'r') as file:
|
|
config = yaml.safe_load(file)
|
|
return config
|
|
|
|
|
|
class AppConfig(ConfigLoader):
|
|
"""A class that extends ConfigLoader to manage application-specific configuration settings.
|
|
|
|
This class provides methods for setting global variables, launch options, and layout options from the configuration.
|
|
|
|
Attributes:
|
|
config (dict): The current configuration settings.
|
|
launch (dict): The launch configuration settings.
|
|
model (dict): The model configuration settings.
|
|
advanced (dict): The advanced configuration settings.
|
|
queue (dict): The queue configuration settings.
|
|
layout (dict): The layout configuration settings.
|
|
"""
|
|
def __init__(self, config : Dict[str, Any]):
|
|
"""Initializes a new instance of the AppConfig class.
|
|
|
|
Args:
|
|
config (dict): The configuration dictionary.
|
|
"""
|
|
self.config = config
|
|
|
|
self.set_global_vars_from_config()
|
|
self.set_launch_options()
|
|
self.set_layout_options()
|
|
|
|
self.launch = self.config.get("launch")
|
|
self.model = self.config.get("model")
|
|
self.advanced = self.config.get("advanced")
|
|
self.queue = self.config.get("queue")
|
|
self.layout = self.config.get("layout")
|
|
|
|
def set_global_vars_from_config(self) -> None:
|
|
"""Sets the global variables from a configuration dictionary.
|
|
|
|
Args:
|
|
config (dict): A dictionary containing the parameters for the model. Modify the default parameters in the config.yml file.
|
|
|
|
Returns:
|
|
None
|
|
"""
|
|
|
|
gv.MODEL_PARAMS = self.config.get('model')
|
|
gv.TIMEOUT = self.config.get("advanced").get('timeout')
|
|
|
|
def set_launch_options(self) -> None:
|
|
"""Sets the launch options from a configuration dictionary.
|
|
|
|
Args:
|
|
None
|
|
|
|
Returns:
|
|
None
|
|
"""
|
|
launch_options = self.config.get("launch")
|
|
|
|
if launch_options.get('auth').pop('auth_enabled'):
|
|
self.config['launch']['auth'] = (launch_options.get('auth').pop('auth_username'),
|
|
launch_options.get('auth').pop('auth_password'))
|
|
else:
|
|
self.config['launch']['auth'] = None
|
|
|
|
def set_layout_options(self) -> None:
|
|
"""Sets the layout options from a configuration dictionary.
|
|
|
|
Args:
|
|
None
|
|
|
|
Returns:
|
|
None
|
|
"""
|
|
self.config['layout']['header'] = self.check_and_set_path(self.config['layout'], 'header')
|
|
self.config['layout']['footer'] = self.check_and_set_path(self.config['layout'], 'footer')
|
|
self.config['layout']['logo'] = self.check_and_set_path(self.config['layout'], 'logo')
|
|
|
|
def get_layout(self) -> Dict[str, str]:
|
|
"""Gets the layout options from a configuration dictionary.
|
|
|
|
Args:
|
|
None
|
|
|
|
Returns:
|
|
dict: A dictionary containing the header and footer layout options.
|
|
"""
|
|
if not os.path.exists(self.config['layout']['header']) and \
|
|
self.config['layout']['header'] == "scraibe/app/header.html":
|
|
|
|
hname = os.path.join(CURRENT_PATH, "header.html")
|
|
|
|
header = open(hname).read()
|
|
|
|
elif not os.path.exists(self.config['layout']['header']) and self.config['layout']['header'] != "scraibe/app/header.html":
|
|
warnings.warn(f"Header file not found: {self.config['layout']['header']} \n" \
|
|
"fall back to default.")
|
|
|
|
hname = os.path.join(CURRENT_PATH, "header.html")
|
|
|
|
header = open(hname).read()
|
|
elif os.path.exists(self.config['layout']['header']):
|
|
header = open(self.config['layout']['header']).read()
|
|
else:
|
|
warnings.warn(f"Header file not found: {self.config['layout']['header']}")
|
|
header = None
|
|
|
|
|
|
if header != None:
|
|
if self.config['layout']['logo'] == "scraibe/app/logo.svg":
|
|
header = header.replace("/file=logo.svg", f"/file={os.path.join(CURRENT_PATH, 'logo.svg')}")
|
|
elif self.config['layout']['logo'] != "scraibe/app/logo.svg":
|
|
header = header.replace("/file=logo.svg", f"/file={self.config['layout']['logo']}")
|
|
else:
|
|
warnings.warn(f"Logo file not found: {self.config['layout']['logo']}")
|
|
|
|
|
|
if self.config['layout']['footer'] != None:
|
|
if os.path.exists(self.config['layout']['footer']):
|
|
footer = open(self.config['layout']['footer']).read()
|
|
elif self.config['layout']['footer'] == None:
|
|
footer = None
|
|
else:
|
|
warnings.warn(f"Footer file not found: {self.config['layout']['footer']}")
|
|
else:
|
|
footer = None
|
|
return {'header' : header ,
|
|
'footer' : footer}
|
|
|
|
@staticmethod
|
|
def check_and_set_path(config_item: dict, key: str) -> Optional[str]:
|
|
"""Check if the file exists at the given path. If not, try with CURRENT_PATH.
|
|
Raise FileNotFoundError if the file still doesn't exist.
|
|
|
|
Args:
|
|
config_item (dict): The configuration item.
|
|
key (str): The key to check in the configuration item.
|
|
|
|
Returns:
|
|
str: The path to the file if it exists, None otherwise.
|
|
"""
|
|
_current_path = os.path.dirname(os.path.realpath(__file__)) # Define your CURRENT_PATH
|
|
|
|
file_path = config_item.get(key)
|
|
if file_path is None:
|
|
return None
|
|
if not os.path.exists(file_path):
|
|
new_path = os.path.join(_current_path, file_path)
|
|
if not os.path.exists(new_path):
|
|
warnings.warn(f"{key.capitalize()} file not found: {config_item[key]} \n" \
|
|
"fall back to default.")
|
|
else:
|
|
config_item[key] = new_path
|
|
|
|
return config_item[key] |