Compare commits

...

9 Commits

6 changed files with 299 additions and 267 deletions

View File

@ -1,7 +1,7 @@
# Landingpage # Landingpage
## Access ## Access
### Locale
http://127.0.0.1:5000 http://127.0.0.1:5000
@ -19,12 +19,12 @@ docker build -t application-landingpage .
### Run ### Run
#### Development #### Run Development Environment
```bash ```bash
sudo docker run -d -p 5000:5000 --name landingpage -v $(pwd)/app/:/app -e FLASK_APP=app.py -e FLASK_ENV=development application-landingpage docker run -d -p 5000:5000 --name landingpage -v $(pwd)/app/:/app -e FLASK_APP=app.py -e FLASK_ENV=development application-landingpage
``` ```
#### Production #### Run Production Environment
```bash ```bash
docker run -d -p 5000:5000 --name landingpage application-landingpage docker run -d -p 5000:5000 --name landingpage application-landingpage
``` ```
@ -32,4 +32,6 @@ docker run -d -p 5000:5000 --name landingpage application-landingpage
### Debug ### Debug
```bash ```bash
docker logs -f landingpage docker logs -f landingpage
``` ```
## Author
This software was created from [Kevin Veen-Birkenbach](https://www.veen.world/) with the help of [ChatGPT]()

View File

@ -4,7 +4,6 @@ import requests
import hashlib import hashlib
import yaml import yaml
from utils.configuration_resolver import ConfigurationResolver from utils.configuration_resolver import ConfigurationResolver
from pprint import pprint
from utils.cache_manager import CacheManager from utils.cache_manager import CacheManager
# Initialize the CacheManager # Initialize the CacheManager
@ -35,9 +34,6 @@ FLASK_ENV = os.getenv("FLASK_ENV", "production")
def reload_config_in_dev(): def reload_config_in_dev():
if FLASK_ENV == "development": if FLASK_ENV == "development":
load_config(app) load_config(app)
print("DEVELOPMENT ENVIRONMENT")
else:
print("PRODUCTIVE ENVIRONMENT")
# Cache the icons # Cache the icons
for card in app.config["cards"]: for card in app.config["cards"]:

View File

@ -75,7 +75,6 @@ cards:
I deliver expert consulting services. Currently training for my Private Pilot I deliver expert consulting services. Currently training for my Private Pilot
License, I specialize in guiding clients through aviation regulations, safety License, I specialize in guiding clients through aviation regulations, safety
standards, and operational efficiency. standards, and operational efficiency.
url:
link_text: Website under construction link_text: Website under construction
- icon: - icon:
source: https://cloud.veen.world/s/logo_hunter_512x512/download source: https://cloud.veen.world/s/logo_hunter_512x512/download
@ -84,7 +83,6 @@ cards:
walks, survival trainings, and photo expeditions, merging ecological knowledge walks, survival trainings, and photo expeditions, merging ecological knowledge
with nature respect. My goal is to foster sustainable conservation and enhance with nature respect. My goal is to foster sustainable conservation and enhance
appreciation for the natural world through responsible practices. appreciation for the natural world through responsible practices.
url:
link_text: Website under construction link_text: Website under construction
- icon: - icon:
source: https://cloud.veen.world/s/logo_diver_512x512/download source: https://cloud.veen.world/s/logo_diver_512x512/download
@ -93,7 +91,6 @@ cards:
diving instruction, underwater photography, and guided dive tours. My experience diving instruction, underwater photography, and guided dive tours. My experience
ensures safe and enriching underwater adventures, highlighting marine conservation ensures safe and enriching underwater adventures, highlighting marine conservation
and the wonders of aquatic ecosystems. and the wonders of aquatic ecosystems.
url:
link_text: Website under construction link_text: Website under construction
- icon: - icon:
source: https://cloud.veen.world/s/logo_massage_therapist_512x512/download source: https://cloud.veen.world/s/logo_massage_therapist_512x512/download
@ -101,8 +98,84 @@ cards:
text: Certified in Tantra Massage, I offer unique full-body rituals to awaken senses text: Certified in Tantra Massage, I offer unique full-body rituals to awaken senses
and harmonize body and mind. My sessions, a blend of ancient Tantra and modern and harmonize body and mind. My sessions, a blend of ancient Tantra and modern
relaxation, focus on energy flow, personal growth, and spiritual awakening. relaxation, focus on energy flow, personal growth, and spiritual awakening.
url:
link_text: Website under construction link_text: Website under construction
accounts:
name: Accounts
description: My Accounts
icon:
class: fa-solid fa-user-group
subitems:
- name: Meta
description: Social and developer networks
icon:
class: fa-brands fa-meta
subitems:
- name: Instagram
description: Follow me on Instagram
icon:
class: fa-brands fa-instagram
url: https://www.instagram.com/kevinveenbirkenbach/
identifier: kevinveenbirkenbach
link: navigation.header.contact.whatsapp.warning
- name: Facebook
description: Like my Facebook page
icon:
class: fa-brands fa-facebook
url: https://www.facebook.com/kevinveenbirkenbach
- name: Messengers
description: Messenger Applications
icon:
class: fas fa-comments
subitems:
- link: navigation.header.contact.whatsapp
- link: navigation.header.contact.signal
- link: navigation.header.contact.telegram
- name: Carreer Profiles
icon:
class: fa-solid fa-user-tie
subitems:
- name: XING
description: Visit my XING profile
icon:
class: bi bi-building
url: https://www.xing.com/profile/Kevin_VeenBirkenbach
- name: LinkedIn
description: Connect on LinkedIn
icon:
class: bi bi-linkedin
url: https://www.linkedin.com/in/kevinveenbirkenbach
- name: Sports
description: My sport activities
icon:
class: fa-solid fa-running
subitems:
- name: Garmin
description: My Garmin activities
icon:
class: fa-solid fa-person-running
url: https://s.veen.world/garmin
- name: Eversports
description: My Eversports sessions
icon:
class: fa-solid fa-dumbbell
url: https://s.veen.world/eversports
- name: Duolingo
description: Learn with me on Duolingo
icon:
class: fa-solid fa-language
url: https://www.duolingo.com/profile/kevinbirkenbach
- name: Spotify
description: Listen to my playlists
icon:
class: fa-brands fa-spotify
url: https://open.spotify.com/user/31vebfzbjf3p7oualis76qfpr5ty
- name: Patreon
description: Support me on Patreon
icon:
class: fa-brands fa-patreon
url: https://patreon.com/kevinveenbirkenbach
company: company:
titel: Kevin Veen-Birkenbach titel: Kevin Veen-Birkenbach
subtitel: Consulting and Coaching Solutions subtitel: Consulting and Coaching Solutions
@ -116,6 +189,7 @@ company:
city: Berlin city: Berlin
country: Germany country: Germany
imprint_url: https://s.veen.world/imprint imprint_url: https://s.veen.world/imprint
navigation: navigation:
header: header:
- name: Microblog - name: Microblog
@ -123,25 +197,21 @@ navigation:
icon: icon:
class: fa-brands fa-mastodon class: fa-brands fa-mastodon
url: https://microblog.veen.world/@kevinveenbirkenbach url: https://microblog.veen.world/@kevinveenbirkenbach
subitems: []
- name: Pictures - name: Pictures
description: View my photo gallery description: View my photo gallery
icon: icon:
class: fa-solid fa-camera class: fa-solid fa-camera
url: https://picture.veen.world/kevinveenbirkenbach url: https://picture.veen.world/kevinveenbirkenbach
subitems: []
- name: Videos - name: Videos
description: Watch my videos description: Watch my videos
icon: icon:
class: fa-solid fa-video class: fa-solid fa-video
url: https://video.veen.world/a/kevinveenbirkenbach url: https://video.veen.world/a/kevinveenbirkenbach
subitems: []
- name: Blog - name: Blog
description: Read my blog description: Read my blog
icon: icon:
class: fa-solid fa-blog class: fa-solid fa-blog
url: https://blog.veen.world url: https://blog.veen.world
subitems: []
- name: Code - name: Code
icon: icon:
class: fa-solid fa-laptop-code class: fa-solid fa-laptop-code
@ -152,13 +222,11 @@ navigation:
icon: icon:
class: bi bi-github class: bi bi-github
url: https://github.com/kevinveenbirkenbach url: https://github.com/kevinveenbirkenbach
subitems: []
- name: Gitea - name: Gitea
description: Explore my code repositories description: Explore my code repositories
icon: icon:
class: fa-solid fa-code class: fa-solid fa-code
url: https://git.veen.world/kevinveenbirkenbach url: https://git.veen.world/kevinveenbirkenbach
subitems: []
- name: Contact - name: Contact
description: Get in touch description: Get in touch
icon: icon:
@ -171,7 +239,7 @@ navigation:
url: mailto:kevin@veen.world url: mailto:kevin@veen.world
identifier: kevin@veen.world identifier: kevin@veen.world
alternatives: alternatives:
- link: navigation.header.contact.matrix #- link: navigation.header.contact.matrix
- name: Matrix - name: Matrix
description: Chat with me on Matrix description: Chat with me on Matrix
icon: icon:
@ -223,7 +291,7 @@ navigation:
identifier: "+491781798023" identifier: "+491781798023"
warning: Signal is not hosted by me! warning: Signal is not hosted by me!
alternatives: alternatives:
- link: navigation.header.contact.matrix #- link: navigation.header.contact.matrix
- name: Telegram - name: Telegram
description: Message me on Telegram description: Message me on Telegram
icon: icon:
@ -233,228 +301,126 @@ navigation:
identifier: kevinveenbirkenbach identifier: kevinveenbirkenbach
warning: Telegram is not hosted by me! warning: Telegram is not hosted by me!
alternatives: alternatives:
- link: navigation.header.contact.matrix #- link: navigation.header.contact.matrix
- name: WhatsApp - name: WhatsApp
description: Chat with me on WhatsApp description: Chat with me on WhatsApp
icon: icon:
class: fa-brands fa-whatsapp class: fa-brands fa-whatsapp
url: https://wa.me/491781798023 url: https://wa.me/491781798023
identifier: "+491781798023" identifier: "+491781798023"
warning: |
⚠️ **Caution with Meta Services**
Using software and platforms from the Meta corporation (e.g., Facebook, Instagram, WhatsApp) may compromise your data privacy and digital freedom due to centralized control, extensive data collection practices, and inconsistent moderation policies. These platforms often fail to adequately address harmful content, misinformation, and abuse.
📌 **Recommendation:** Consider using decentralized and privacy-respecting alternatives to maintain control over your data, improve security, and foster healthier online interactions.
alternatives: alternatives:
- link: navigation.header.contact.matrix #- link: navigation.header.contact.matrix
warning: |
Using software and platforms from the Meta corporation (e.g., Facebook, Instagram, WhatsApp) may compromise your data privacy and digital freedom due to centralized control, extensive data collection practices, and inconsistent moderation policies. These platforms often fail to adequately address harmful content, misinformation, and abuse.
footer: footer:
- name: External Accounts - link: accounts
description: Me on other plattforms - name: Community
icon: description: My presence in the Fediverse
class: fa-solid fa-external-link-alt
subitems:
- name: Meta
description: Social and developer networks
icon: icon:
class: fa-brands fa-meta class: fa-solid fa-users
url:
subitems: subitems:
- name: Instagram - name: Forum
description: Follow me on Instagram description: Join the discussion
icon: icon:
class: fa-brands fa-instagram class: fa-brands fa-discourse
url: https://www.instagram.com/kevinveenbirkenbach/ url: https://forum.veen.world/u/kevinveenbirkenbach
- name: Facebook - name: Newsletter
description: Like my Facebook page description: Subscribe to my newsletter
icon: icon:
class: fa-brands fa-facebook class: fa-solid fa-envelope-open-text
url: https://www.facebook.com/kevinveenbirkenbach url: https://newsletter.veen.world/subscription/form
- name: Communication - name: Work Hub
description: Social and developer networks description: Curated collection of self hosted tools for work, organization, and learning.
icon: icon:
class: fa-brands fa-meta class: fa-solid fa-toolbox
subitems:
- link: navigation.header.contact.whatsapp
- link: navigation.header.contact.signal
- link: navigation.header.contact.telegram
- name: Carreer Profiles
icon:
class: fa-solid fa-user-tie
subitems: subitems:
- name: XING - name: Open Project
description: Visit my XING profile description: Explore my projects
icon: icon:
class: bi bi-building class: fa-solid fa-chart-line
url: https://www.xing.com/profile/Kevin_VeenBirkenbach url: https://project.veen.world/
subitems: [] - name: Taiga
- name: LinkedIn description: View my Kanban board
description: Connect on LinkedIn
icon: icon:
class: bi bi-linkedin class: bi bi-clipboard2-check-fill
url: https://www.linkedin.com/in/kevinveenbirkenbach url: https://kanban.veen.world/
subitems: [] - name: Matomo
- name: Sports description: Analyze with Matomo
description: My sport activities icon:
class: fa-solid fa-chart-simple
url: https://matomo.veen.world/
- name: Baserow
description: Organize with Baserow
icon:
class: fa-solid fa-table
url: https://baserow.veen.world/
- name: Elements
description: Chat with me
icon:
class: fa-solid fa-comment
url: https://element.veen.world/
- name: Big Blue Button
description: Join live events
icon:
class: fa-solid fa-video
url: https://meet.veen.world/
- name: Mailu
description: Send me a mail
icon:
class: fa-solid fa-envelope
url: https://mail.veen.world/
- name: Moodel
description: Learn with my academy
icon:
class: fa-solid fa-graduation-cap
url: https://academy.veen.world/
- name: Yourls
description: Find my curated links
icon:
class: bi bi-link
url: https://s.veen.world/admin/
- name: Nextcloud
description: Access my cloud storage
icon:
class: fa-solid fa-cloud
url: https://cloud.veen.world/
- name: Logbooks
description: My activity logs
icon: icon:
class: fa-solid fa-running class: fa-solid fa-book
url:
subitems: subitems:
- name: Garmin - name: Skydiver
description: My Garmin activities description: View my skydiving logs
icon: icon:
class: fa-solid fa-person-running class: fa-solid fa-parachute-box
url: https://s.veen.world/garmin url: https://s.veen.world/skydiverlog
subitems: [] - name: Skipper
- name: Eversports description: See my sailing records
description: My Eversports sessions
icon: icon:
class: fa-solid fa-dumbbell class: fa-solid fa-sailboat
url: https://s.veen.world/eversports url: https://s.veen.world/meilenbuch
subitems: [] - name: Diver
- name: Duolingo description: Check my diving logs
description: Learn with me on Duolingo icon:
class: fa-solid fa-fish
url: https://s.veen.world/diverlog
- name: Pilot
description: Review my flight logs
icon:
class: fa-solid fa-plane
url: https://s.veen.world/pilotlog
- name: Nature
description: Explore my nature logs
icon:
class: fa-solid fa-tree
url: https://s.veen.world/naturejournal
- name: Vita
description: View my CV and professional background
icon: icon:
class: fa-solid fa-language class: fa-solid fa-file-lines
url: https://www.duolingo.com/profile/kevinbirkenbach url: https://s.veen.world/lebenslauf
subitems: [] - name: Imprint
- name: Spotify
description: Listen to my playlists
icon: icon:
class: fa-brands fa-spotify class: fa-solid fa-scale-balanced
url: https://open.spotify.com/user/31vebfzbjf3p7oualis76qfpr5ty url: https://s.veen.world/imprint
subitems: []
- name: Patreon
description: Support me on Patreon
icon:
class: fa-brands fa-patreon
url: https://patreon.com/kevinveenbirkenbach
subitems: []
- name: Community
description: My presence in the Fediverse
icon:
class: fa-solid fa-users
subitems:
- name: Forum
description: Join the discussion
icon:
class: fa-brands fa-discourse
url: https://forum.veen.world/u/kevinveenbirkenbach
subitems: []
- name: Newsletter
description: Subscribe to my newsletter
icon:
class: fa-solid fa-envelope-open-text
url: https://newsletter.veen.world/subscription/form
subitems: []
- name: Work Hub
description: Curated collection of self hosted tools for work, organization, and
learning.
icon:
class: fa-solid fa-toolbox
url:
subitems:
- name: Open Project
description: Explore my projects
icon:
class: fa-solid fa-chart-line
url: https://project.veen.world/
subitems: []
- name: Taiga
description: View my Kanban board
icon:
class: bi bi-clipboard2-check-fill
url: https://kanban.veen.world/
subitems: []
- name: Matomo
description: Analyze with Matomo
icon:
class: fa-solid fa-chart-simple
url: https://matomo.veen.world/
subitems: []
- name: Baserow
description: Organize with Baserow
icon:
class: fa-solid fa-table
url: https://baserow.veen.world/
subitems: []
- name: Elements
description: Chat with me
icon:
class: fa-solid fa-comment
url: https://element.veen.world/
subitems: []
- name: Big Blue Button
description: Join live events
icon:
class: fa-solid fa-video
url: https://meet.veen.world/
subitems: []
- name: Mailu
description: Send me a mail
icon:
class: fa-solid fa-envelope
url: https://mail.veen.world/
subitems: []
- name: Moodel
description: Learn with my academy
icon:
class: fa-solid fa-graduation-cap
url: https://academy.veen.world/
subitems: []
- name: Yourls
description: Find my curated links
icon:
class: bi bi-link
url: https://s.veen.world/admin/
subitems: []
- name: Nextcloud
description: Access my cloud storage
icon:
class: fa-solid fa-cloud
url: https://cloud.veen.world/
subitems: []
- name: Logbooks
description: My activity logs
icon:
class: fa-solid fa-book
url:
subitems:
- name: Skydiver
description: View my skydiving logs
icon:
class: fa-solid fa-parachute-box
url: https://s.veen.world/skydiverlog
subitems: []
- name: Skipper
description: See my sailing records
icon:
class: fa-solid fa-sailboat
url: https://s.veen.world/meilenbuch
subitems: []
- name: Diver
description: Check my diving logs
icon:
class: fa-solid fa-fish
url: https://s.veen.world/diverlog
subitems: []
- name: Pilot
description: Review my flight logs
icon:
class: fa-solid fa-plane
url: https://s.veen.world/pilotlog
subitems: []
- name: Nature
description: Explore my nature logs
icon:
class: fa-solid fa-tree
url: https://s.veen.world/naturejournal
- name: Vita
description: View my CV and professional background
icon:
class: fa-solid fa-file-lines
url: https://s.veen.world/lebenslauf
subitems: []
- name: Imprint
icon:
class: fa-solid fa-scale-balanced
url: https://s.veen.world/imprint

View File

@ -10,7 +10,7 @@
{{ render_subitems(subitem.subitems) }} {{ render_subitems(subitem.subitems) }}
</ul> </ul>
</li> </li>
{% elif subitem.identifier %} {% elif subitem.identifier or subitem.warning or subitem.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({{ subitem|tojson|safe }})' data-bs-toggle="tooltip" title="{{ subitem.description }}">
<i class="{{ subitem.icon.class }}"></i> {{ subitem.name }} <i class="{{ subitem.icon.class }}"></i> {{ subitem.name }}
@ -22,7 +22,7 @@
{% if subitem.icon is defined and subitem.icon.class is defined %} {% if subitem.icon is defined and subitem.icon.class is defined %}
<i class="{{ subitem.icon.class }}"></i> {{ subitem.name }} <i class="{{ subitem.icon.class }}"></i> {{ subitem.name }}
{% else %} {% else %}
<p>Fehlendes Icon im Subitem: {{ subitem }}</p> <p>Missing icon in subitem: {{ subitem }}</p>
{% endif %} {% endif %}
</a> </a>
</li> </li>
@ -33,9 +33,6 @@
<!-- Navigation Bar --> <!-- Navigation Bar -->
<nav class="navbar navbar-expand-lg navbar-light bg-light"> <nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid"> <div class="container-fluid">
<!--
<a class="navbar-brand" href="#">Navbar</a>
-->
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav{{menu_type}}" aria-controls="navbarNav{{menu_type}}" aria-expanded="false" aria-label="Toggle navigation"> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav{{menu_type}}" aria-controls="navbarNav{{menu_type}}" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
@ -53,7 +50,11 @@
<!-- Dropdown Menu --> <!-- Dropdown Menu -->
<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" data-popper-placement="top" title="{{ item.description }}" aria-expanded="false"> <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown{{ loop.index }}" role="button" data-bs-toggle="dropdown" data-bs-display="dynamic" data-popper-placement="top" title="{{ item.description }}" aria-expanded="false">
<i class="{{ item.icon.class }}" data-bs-toggle="tooltip"></i> {{ item.name }} {% if item.icon is defined and item.icon.class is defined %}
<i class="{{ item.icon.class }}" data-bs-toggle="tooltip"></i> {{ item.name }}
{% else %}
<p>Missing icon in item: {{ item }}</p>
{% endif %}
</a> </a>
<ul class="dropdown-menu dropdown-menu-{{menu_type}}" aria-labelledby="navbarDropdown{{ loop.index }}"> <ul class="dropdown-menu dropdown-menu-{{menu_type}}" aria-labelledby="navbarDropdown{{ loop.index }}">
{{ render_subitems(item.subitems) }} {{ render_subitems(item.subitems) }}

View File

@ -23,7 +23,6 @@ class CacheManager:
""" """
if not os.path.exists(self.cache_dir): if not os.path.exists(self.cache_dir):
os.makedirs(self.cache_dir) os.makedirs(self.cache_dir)
print(f"Created cache directory: {self.cache_dir}")
def clear_cache(self): def clear_cache(self):
""" """
@ -34,7 +33,6 @@ class CacheManager:
file_path = os.path.join(self.cache_dir, filename) file_path = os.path.join(self.cache_dir, filename)
if os.path.isfile(file_path): if os.path.isfile(file_path):
os.remove(file_path) os.remove(file_path)
print(f"Deleted: {file_path}")
def cache_file(self, file_url): def cache_file(self, file_url):
""" """

View File

@ -1,88 +1,157 @@
from pprint import pprint
import inspect
class ConfigurationResolver: class ConfigurationResolver:
"""
A class to resolve `link` entries in a nested configuration structure.
Supports navigation through dictionaries, lists, and `subitems`.
"""
def __init__(self, config): def __init__(self, config):
"""
Initializes the ConfigurationResolver with a configuration dictionary.
Args:
config (dict): The configuration to resolve links in.
"""
self.config = config self.config = config
def resolve_links(self): def resolve_links(self):
""" """
Resolves all `link` entries in the configuration. Resolves all `link` entries in the configuration by replacing them with
the referenced configuration entry.
""" """
self._recursive_resolve(self.config, self.config) self._recursive_resolve(self.config, self.config)
def _recursive_resolve(self, current_config, root_config): def _recursive_resolve(self, current_config, root_config, path=""):
""" """
Recursively resolves `link` entries in the configuration. Recursively traverses the configuration to resolve all `link` entries.
Args:
current_config (dict or list): The current section of the configuration being processed.
root_config (dict): The root of the configuration to resolve links against.
path (str): The current path in the configuration for debugging purposes.
""" """
if isinstance(current_config, dict): if isinstance(current_config, dict):
self._debug(current_config,path,inspect.currentframe().f_lineno)
# Traverse all key-value pairs in the dictionary
for key, value in list(current_config.items()): for key, value in list(current_config.items()):
if key == "link": if key == "subitems" and isinstance(value, list):
pass
#self._debug(value,path,inspect.currentframe().f_lineno)
#for index, item in enumerate(current_config[key]):
# #self._debug(value,path,inspect.currentframe().f_lineno)
# if "link" in item:
# self._debug(value,path,inspect.currentframe().f_lineno)
# self._recursive_resolve(item, root_config, path=f"{path}[{index}]")
elif key == "link":
# Found a `link` entry, attempt to resolve it
try: try:
target = self._find_entry(root_config, value.lower()) target = self._find_entry(root_config, value.lower().replace(" ", "_"))
current_config.clear()
current_config.update(target) if isinstance(target, dict):
except Exception as e: self._debug(value,path,inspect.currentframe().f_lineno)
# Replace the current dictionary with the resolved dictionary
current_config.clear()
current_config.update(target)
elif isinstance(target, str):
self._debug(value,path,inspect.currentframe().f_lineno)
# Replace the `link` entry with the resolved string
current_config[key] = target
else:
raise ValueError(
f"Expected a dictionary or string for link '{value}', got {type(target)}"
)
except KeyError as e:
# Handle unresolved links
raise ValueError( raise ValueError(
f"Error resolving link '{value}': {str(e)}. " f"Key error while resolving link '{value}': {str(e)}. "
f"Current path: {key}, Current config: {current_config}" f"Current path: {path}, Current config: {current_config}"
) )
else: else:
self._recursive_resolve(value, root_config) self._debug(value,path,inspect.currentframe().f_lineno)
# Recursively resolve non-`link` entries
self._recursive_resolve(value, root_config, path=f"{path}.{key}")
elif isinstance(current_config, list): elif isinstance(current_config, list):
for item in current_config: # Traverse all items in the list
self._recursive_resolve(item, root_config) for index, item in enumerate(current_config):
self._recursive_resolve(item, root_config, path=f"{path}[{index}]")
def _debug(self, value, path, line, condition="accounts"):
if condition in path:
print("LINE:" + str(line))
print("PATH:" + path)
print("VALUE:")
pprint(value)
def _find_entry(self, config, path): def _find_entry(self, config, path):
""" """
Finds an entry in the configuration by a dot-separated path. Finds an entry in the configuration by navigating a dot-separated path.
Supports both dictionaries and lists with `subitems` navigation.
Args:
config (dict or list): The configuration to search within.
path (str): The dot-separated path to the desired entry.
Returns:
dict or str: The resolved entry.
Raises:
KeyError: If the path cannot be resolved.
ValueError: If the resolved entry is not of the expected type.
""" """
parts = path.split('.') parts = path.split('.') # Split the path into segments
current = config current = config
for part in parts: for part in parts:
part = part.replace(" ", "_") # Normalize part name
if isinstance(current, list): if isinstance(current, list):
# Look for a matching name in the list # Search for a matching entry in a list
found = next( found = next(
(item for item in current if isinstance(item, dict) and item.get("name", "").lower() == part), (
item
for item in current
if isinstance(item, dict) and item.get("name", "").lower().replace(" ", "_") == part
),
None None
) )
if found: if not found:
print( raise KeyError(
f"Matching entry for '{part}' in list. Path so far: {' > '.join(parts[:parts.index(part)+1])}. "
f"Current list: {current}"
)
else:
raise ValueError(
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 current = found
elif isinstance(current, dict): elif isinstance(current, dict):
# Case-insensitive dictionary lookup # Search for a key match in a dictionary
key = next((k for k in current if k.lower() == part), None) key = next((k for k in current if k.lower().replace(" ", "_") == part), None)
if key is None: if key is None:
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])}. "
f"Current dictionary: {current}" f"Current dictionary: {current}"
) )
current = current[key] current = current[key]
else: else:
# Invalid path segment
raise ValueError( raise ValueError(
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])}"
) )
# Navigate into `subitems` if present # Stop navigating into `subitems` unless explicitly required by the path
if isinstance(current, dict) and ("subitems" in current and current["subitems"]): if isinstance(current, dict) and "subitems" in current and isinstance(current["subitems"], list) and part != "subitems":
current = current["subitems"] break # Stop navigation if `subitems` is not 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 return current
def get_config(self): def get_config(self):
""" """
Returns the resolved configuration. Returns the fully resolved configuration.
Returns:
dict: The resolved configuration.
""" """
return self.config return self.config