homepage.veen.world/app/utils/configuration_resolver.py

102 lines
4.3 KiB
Python

class ConfigurationResolver:
def __init__(self, config):
self.config = config
def resolve_links(self):
"""
Resolves all `link` entries in the configuration.
"""
self._recursive_resolve(self.config, self.config)
def _recursive_resolve(self, current_config, root_config, path=""):
"""
Recursively resolves `link` entries in the configuration.
"""
if isinstance(current_config, dict):
for key, value in list(current_config.items()):
if key == "link":
try:
print(f"Resolving link '{value}' at path '{path}'") # Debugging
target = self._find_entry(root_config, value.lower().replace(" ", "_"))
if isinstance(target, dict):
current_config.clear()
current_config.update(target)
elif isinstance(target, str):
current_config[key] = target
else:
raise ValueError(f"Expected a dictionary or string for link '{value}', got {type(target)}")
except KeyError as e:
raise ValueError(
f"Key error while resolving link '{value}': {str(e)}. Current path: {key}, Current config: {current_config}"
)
else:
self._recursive_resolve(value, root_config, path=f"{path}.{key}")
elif isinstance(current_config, list):
for index, item in enumerate(current_config):
self._recursive_resolve(item, root_config, path=f"{path}[{index}]")
def _find_entry(self, config, path):
"""
Finds an entry in the configuration by a dot-separated path.
Supports both dictionaries and lists, but does not navigate into `subitems`
unless explicitly required by the path.
"""
parts = path.split('.')
current = config
for part in parts:
part = part.replace(" ", "_") # Normalize the part name
if isinstance(current, list):
# Look for a matching entry in the list
found = next(
(
item
for item in current
if isinstance(item, dict) and item.get("name", "").lower().replace(" ", "_") == part
),
None
)
if not found:
raise KeyError(
f"No matching entry for '{part}' in list. Path so far: {' > '.join(parts[:parts.index(part)+1])}. "
f"Current list: {current}"
)
current = found
elif isinstance(current, dict):
# Look for a key match in the dictionary
key = next((k for k in current if k.lower().replace(" ", "_") == part), None)
if key is None:
raise KeyError(
f"Key '{part}' not found in dictionary. Path so far: {' > '.join(parts[:parts.index(part)+1])}. "
f"Current dictionary: {current}"
)
current = current[key]
else:
raise ValueError(
f"Invalid path segment '{part}'. Current type: {type(current)}. "
f"Path so far: {' > '.join(parts[:parts.index(part)+1])}"
)
# Stop navigating into `subitems` if the path doesn't explicitly require it
if isinstance(current, dict) and "subitems" in current and isinstance(current["subitems"], list):
if part == "subitems":
current = current["subitems"]
else:
break # Do not navigate further unless explicitly in the path
# Ensure the resolved target is a dictionary or string
if not isinstance(current, (dict, str)):
raise ValueError(f"Expected a dictionary or string for path '{path}', got {type(current)}. Current value: {current}")
return current
def get_config(self):
"""
Returns the resolved configuration.
"""
return self.config