Refactored code and implemented new childrens loading

This commit is contained in:
Kevin Veen-Birkenbach 2025-01-15 01:35:50 +01:00
parent c01e9125aa
commit 9ff356ba70
3 changed files with 344 additions and 304 deletions

View File

@ -4,12 +4,12 @@ accounts:
description: My Online Accounts description: My Online Accounts
icon: icon:
class: fa-solid fa-users class: fa-solid fa-users
subitems: children:
- name: Publications - name: Publications
description: My Publications description: My Publications
icon: icon:
class: fas fa-newspaper class: fas fa-newspaper
subitems: children:
- name: Microblog - name: Microblog
description: Read my microblogs description: Read my microblogs
icon: icon:
@ -19,7 +19,7 @@ accounts:
- name: Pictures - name: Pictures
icon: icon:
class: fa-solid fa-images class: fa-solid fa-images
subitems: children:
- name: Pixelfed - name: Pixelfed
description: View my photo gallery description: View my photo gallery
icon: icon:
@ -49,7 +49,7 @@ accounts:
icon: icon:
class: fa-solid fa-laptop-code class: fa-solid fa-laptop-code
description: Check out my Code description: Check out my Code
subitems: children:
- name: Github - name: Github
description: View my GitHub profile description: View my GitHub profile
icon: icon:
@ -66,7 +66,7 @@ accounts:
icon: icon:
class: fa-brands fa-meta class: fa-brands fa-meta
url: url:
subitems: children:
- name: Facebook - name: Facebook
description: Like my Facebook page description: Like my Facebook page
icon: icon:
@ -76,7 +76,7 @@ accounts:
- name: Carreer Profiles - name: Carreer Profiles
icon: icon:
class: fa-solid fa-user-tie class: fa-solid fa-user-tie
subitems: children:
- name: XING - name: XING
description: Visit my XING profile description: Visit my XING profile
icon: icon:
@ -94,7 +94,7 @@ accounts:
icon: icon:
class: fa-solid fa-running class: fa-solid fa-running
url: url:
subitems: children:
- name: Garmin - name: Garmin
description: My Garmin activities description: My Garmin activities
icon: icon:
@ -251,16 +251,18 @@ company:
imprint_url: https://s.veen.world/imprint imprint_url: https://s.veen.world/imprint
navigation: navigation:
header: header:
children:
- link: accounts.publications.children
- name: Contact - name: Contact
description: Get in touch description: Get in touch
icon: icon:
class: fa-solid fa-envelope class: fa-solid fa-envelope
subitems: children:
- name: Email - name: Email
description: Send me an email description: Send me an email
icon: icon:
class: fa-solid fa-envelope class: fa-solid fa-envelope
subitems: children:
- name: Email - name: Email
description: Send me an email description: Send me an email
icon: icon:
@ -298,7 +300,7 @@ navigation:
description: Social and developer networks description: Social and developer networks
icon: icon:
class: fa-solid fa-comments class: fa-solid fa-comments
subitems: children:
- name: Matrix - name: Matrix
description: Chat with me on Matrix description: Chat with me on Matrix
icon: icon:
@ -345,20 +347,20 @@ navigation:
- link: navigation.header.contact.messenger.matrix - link: navigation.header.contact.messenger.matrix
- link: navigation.header.contact.messenger.signal - link: navigation.header.contact.messenger.signal
- link: navigation.header.contact.messenger.telegram - link: navigation.header.contact.messenger.telegram
footer: footer:
children:
- link: accounts - link: accounts
- name: Solution Hub - name: Solution Hub
description: Curated collection of self hosted tools description: Curated collection of self hosted tools
icon: icon:
class: fa-solid fa-network-wired class: fa-solid fa-network-wired
url: url:
subitems: children:
- name: Community - name: Community
description: Tools to manage the community description: Tools to manage the community
icon: icon:
class: fa-solid fa-users class: fa-solid fa-users
subitems: children:
- name: Forum - name: Forum
description: Join the discussion description: Join the discussion
icon: icon:
@ -378,7 +380,7 @@ navigation:
description: Project Management Tools description: Project Management Tools
icon: icon:
class: fa-solid fa-chart-line class: fa-solid fa-chart-line
subitems: children:
- name: Open Project - name: Open Project
description: Explore my projects description: Explore my projects
icon: icon:
@ -394,7 +396,7 @@ navigation:
- name: Communication - name: Communication
icon: icon:
class: fa-solid fa-comments class: fa-solid fa-comments
subitems: children:
- name: Elements - name: Elements
description: Chat with me description: Chat with me
icon: icon:
@ -415,7 +417,7 @@ navigation:
- name: Tools - name: Tools
icon: icon:
class: fas fa-tools class: fas fa-tools
subitems: children:
- name: Matomo - name: Matomo
description: Analyze with Matomo description: Analyze with Matomo
icon: icon:
@ -443,12 +445,12 @@ navigation:
description: All information about me description: All information about me
icon: icon:
class: fa-solid fa-user class: fa-solid fa-user
subitems: children:
- name: Logbooks - name: Logbooks
description: Access my personal logbooks (diving, flying, sailing) description: Access my personal logbooks (diving, flying, sailing)
icon: icon:
class: fa-solid fa-book class: fa-solid fa-book
subitems: children:
- name: Skydiver - name: Skydiver
description: View my skydiving logs description: View my skydiving logs
icon: icon:
@ -483,11 +485,12 @@ navigation:
icon: icon:
class: fa-solid fa-file-lines class: fa-solid fa-file-lines
url: https://s.veen.world/lebenslauf url: https://s.veen.world/lebenslauf
- link: accounts
- name: Credentials - name: Credentials
description: Access my certifications, degrees, and professional records description: Access my certifications, degrees, and professional records
icon: icon:
class: fa-solid fa-id-card class: fa-solid fa-id-card
subitems: children:
- name: Degrees - name: Degrees
description: View my academic degrees description: View my academic degrees
icon: icon:
@ -503,10 +506,10 @@ navigation:
icon: icon:
class: fa-solid fa-scroll class: fa-solid fa-scroll
url: https://s.veen.world/certifications url: https://s.veen.world/certifications
- link: accounts
- name: Imprint - name: Imprint
description: Check out the imprint information description: Check out the imprint information
icon: icon:
class: fa-solid fa-scale-balanced class: fa-solid fa-scale-balanced
url: https://s.veen.world/imprint url: https://s.veen.world/imprint

View File

@ -1,33 +1,33 @@
<!-- Template for Subitems --> {% macro render_icon_and_name(item) %}
{% macro render_subitems(subitems) %} <i class="{{ item.icon.class if item.icon is defined and item.icon.class is defined else 'fa-solid fa-link' }}"></i>
{% for subitem in subitems %} {% if item.name is defined %}
{% if subitem.subitems %} {{ item.name }}
<li class="dropdown-submenu position-relative">
<a class="dropdown-item dropdown-toggle" href="#" title="{{ subitem.description }}">
{% if subitem.icon is defined and subitem.icon.class is defined %}
<i class="{{ subitem.icon.class }}"></i> {{ subitem.name }}
{% else %} {% else %}
<p>Missing icon in subitem: {{ subitem }}</p> Unnamed Item: {{item}}
{% endif %} {% endif %}
{% endmacro %}
<!-- Template for children -->
{% macro render_children(children) %}
{% for children in children %}
{% if children.children %}
<li class="dropdown-submenu position-relative">
<a class="dropdown-item dropdown-toggle" href="#" title="{{ children.description }}">
{{ render_icon_and_name(children) }}
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
{{ render_subitems(subitem.subitems) }} {{ render_children(children.children) }}
</ul> </ul>
</li> </li>
{% elif subitem.identifier or subitem.warning or subitem.info %} {% elif children.identifier or children.warning or children.info %}
<li> <li>
<a class="dropdown-item" onclick='openDynamicPopup({{ subitem|tojson|safe }})' data-bs-toggle="tooltip" title="{{ subitem.description }}"> <a class="dropdown-item" onclick='openDynamicPopup({{ children|tojson|safe }})' data-bs-toggle="tooltip" title="{{ children.description }}">
<i class="{{ subitem.icon.class }}"></i> {{ subitem.name }} {{ render_icon_and_name(children) }}
</a> </a>
</li> </li>
{% else %} {% else %}
<li> <li>
<a class="dropdown-item" href="{{ subitem.url }}" target="{{ subitem.target|default('_blank') }}" data-bs-toggle="tooltip" title="{{ subitem.description }}"> <a class="dropdown-item" href="{{ children.url }}" target="{{ children.target|default('_blank') }}" data-bs-toggle="tooltip" title="{{ children.description }}">
{% if subitem.icon is defined and subitem.icon.class is defined %} {{ render_icon_and_name(children) }}
<i class="{{ subitem.icon.class }}"></i> {{ subitem.name }}
{% else %}
<p>Missing icon in subitem: {{ subitem }}</p>
{% endif %}
</a> </a>
</li> </li>
{% endif %} {% endif %}
@ -42,12 +42,12 @@
</button> </button>
<div class="collapse navbar-collapse" id="navbarNav{{menu_type}}"> <div class="collapse navbar-collapse" id="navbarNav{{menu_type}}">
<ul class="navbar-nav {% if menu_type == 'header' %}ms-auto{% endif %}"> <ul class="navbar-nav {% if menu_type == 'header' %}ms-auto{% endif %}">
{% for item in navigation[menu_type] %} {% for item in navigation[menu_type].children %}
{% if item.url %} {% if item.url %}
<!-- Single Item --> <!-- Single Item -->
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ item.url }}" target="{{ item.target|default('_blank') }}" data-bs-toggle="tooltip" title="{{ item.description }}"> <a class="nav-link" href="{{ item.url }}" target="{{ item.target|default('_blank') }}" data-bs-toggle="tooltip" title="{{ item.description }}">
<i class="{{ item.icon.class }}"></i> {{ item.name }} {{ render_icon_and_name(item) }}
</a> </a>
</li> </li>
{% else %} {% else %}
@ -55,13 +55,13 @@
<li class="nav-item dropdown"> <li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown{{ loop.index }}" role="button" data-bs-toggle="dropdown" data-bs-display="dynamic" aria-expanded="false"> <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown{{ loop.index }}" role="button" data-bs-toggle="dropdown" data-bs-display="dynamic" aria-expanded="false">
{% if item.icon is defined and item.icon.class is defined %} {% if item.icon is defined and item.icon.class is defined %}
<i class="{{ item.icon.class }}"></i> {{ item.name }} {{ render_icon_and_name(item) }}
{% else %} {% else %}
<p>Missing icon in item: {{ item }}</p> <p>Missing icon in item: {{ item }}</p>
{% endif %} {% endif %}
</a> </a>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
{{ render_subitems(item.subitems) }} {{ render_children(item.children) }}
</ul> </ul>
</li> </li>
{% endif %} {% endif %}

View File

@ -2,7 +2,7 @@ from pprint import pprint
class ConfigurationResolver: class ConfigurationResolver:
""" """
A class to resolve `link` entries in a nested configuration structure. A class to resolve `link` entries in a nested configuration structure.
Supports navigation through dictionaries, lists, and `subitems`. Supports navigation through dictionaries, lists, and `children`.
""" """
def __init__(self, config): def __init__(self, config):
@ -14,23 +14,58 @@ class ConfigurationResolver:
""" """
self._recursive_resolve(self.config, self.config) self._recursive_resolve(self.config, self.config)
def __load_children(self,path):
"""
Check if explicitly children should be loaded and not parent
"""
return path.split('.').pop() == "children"
def _replace_in_dict_by_dict(self, dict_origine, old_key, new_dict):
if old_key in dict_origine:
# Entferne den alten Key
old_value = dict_origine.pop(old_key)
# Füge die neuen Key-Value-Paare hinzu
dict_origine.update(new_dict)
def _replace_in_list_by_list(self, list_origine, old_element, new_elements):
index = list_origine.index(old_element)
list_origine[index:index+1] = new_elements
def _replace_element_in_list(self, list_origine, old_element, new_element):
index = list_origine.index(old_element)
list_origine[index] = new_element
def _recursive_resolve(self, current_config, root_config): def _recursive_resolve(self, current_config, root_config):
""" """
Recursively resolves `link` entries in the configuration. Recursively resolves `link` entries in the configuration.
""" """
if isinstance(current_config, dict): if isinstance(current_config, dict):
for key, value in list(current_config.items()): for key, value in list(current_config.items()):
if key == "link": if key == "children":
for item in value:
if "link" in item:
loaded_link = self._find_entry(root_config, item['link'].lower(), False)
if isinstance(loaded_link, list):
self._replace_in_list_by_list(value,item,loaded_link)
else:
self._replace_element_in_list(value,item,loaded_link)
elif key == "link":
try: try:
target = self._find_entry(root_config, value.lower(), True) if self.__load_children(value):
if isinstance(target, list) and len(target) > 2: loaded = self._find_entry(root_config, value.lower(), False)
target = self._find_entry(root_config, value.lower(), False) self._replace_in_dict_by_dict(
current_config,key,loaded
)
else:
loaded = self._find_entry(root_config, value.lower(), True)
if isinstance(loaded, list) and len(loaded) > 2:
loaded = self._find_entry(root_config, value.lower(), False)
current_config.clear() current_config.clear()
current_config.update(target) current_config.update(loaded)
except Exception as e: except Exception as e:
raise ValueError( raise ValueError(
f"Error resolving link '{value}': {str(e)}. " f"Error resolving link '{value}': {str(e)}. "
f"Current path: {key}, Current config: {current_config}" f"Current path: {key}, Current config: {current_config}" + (f", Loaded: {loaded}" if 'loaded' in locals() or 'loaded' in globals() else "")
) )
else: else:
self._recursive_resolve(value, root_config) self._recursive_resolve(value, root_config)
@ -38,9 +73,9 @@ class ConfigurationResolver:
for item in current_config: for item in current_config:
self._recursive_resolve(item, root_config) self._recursive_resolve(item, root_config)
def _get_subitems(self,current): def _get_children(self,current):
if isinstance(current, dict) and ("subitems" in current and current["subitems"]): if isinstance(current, dict) and ("children" in current and current["children"]):
current = current["subitems"] current = current["children"]
return current return current
def _find_by_name(self,current, part): def _find_by_name(self,current, part):
@ -49,18 +84,21 @@ class ConfigurationResolver:
None None
) )
def _find_entry(self, config, path, subitems): def _find_entry(self, config, path, children):
""" """
Finds an entry in the configuration by a dot-separated path. Finds an entry in the configuration by a dot-separated path.
Supports both dictionaries and lists with `subitems` navigation. Supports both dictionaries and lists with `children` navigation.
""" """
parts = path.split('.') parts = path.split('.')
current = config current = config
for part in parts: for part in parts:
if isinstance(current, list): if isinstance(current, list):
# If children explicit declared just load children
if part != "children":
# Look for a matching name in the list # Look for a matching name in the list
found = self._find_by_name(current,part) found = self._find_by_name(current,part)
if found: if found:
current = found
print( print(
f"Matching entry for '{part}' in list. Path so far: {' > '.join(parts[:parts.index(part)+1])}. " f"Matching entry for '{part}' in list. Path so far: {' > '.join(parts[:parts.index(part)+1])}. "
f"Current list: {current}" f"Current list: {current}"
@ -70,12 +108,11 @@ class ConfigurationResolver:
f"No matching entry for '{part}' in list. Path so far: {' > '.join(parts[:parts.index(part)+1])}. " f"No matching entry for '{part}' in list. Path so far: {' > '.join(parts[:parts.index(part)+1])}. "
f"Current list: {current}" f"Current list: {current}"
) )
current = found
elif isinstance(current, dict): elif isinstance(current, dict):
# Case-insensitive dictionary lookup # Case-insensitive dictionary lookup
key = next((k for k in current if k.lower() == part), None) key = next((k for k in current if k.lower() == part), None)
if key is None: if key is None:
current = self._find_by_name(current["subitems"],part) current = self._find_by_name(current["children"],part)
if not current: if not current:
raise KeyError( raise KeyError(
f"Key '{part}' not found in dictionary. Path so far: {' > '.join(parts[:parts.index(part)+1])}. " f"Key '{part}' not found in dictionary. Path so far: {' > '.join(parts[:parts.index(part)+1])}. "
@ -89,8 +126,8 @@ class ConfigurationResolver:
f"Invalid path segment '{part}'. Current type: {type(current)}. " f"Invalid path segment '{part}'. Current type: {type(current)}. "
f"Path so far: {' > '.join(parts[:parts.index(part)+1])}" f"Path so far: {' > '.join(parts[:parts.index(part)+1])}"
) )
if subitems: if children:
current = self._get_subitems(current) current = self._get_children(current)
return current return current