mirror of
https://github.com/kevinveenbirkenbach/homepage.veen.world.git
synced 2025-06-29 08:12:01 +02:00
Compare commits
13 Commits
1629d9728d
...
ae6eb6d802
Author | SHA1 | Date | |
---|---|---|---|
ae6eb6d802 | |||
c9952038d4 | |||
e08c835598 | |||
ced25bdf3b | |||
a60b3893aa | |||
71209df82e | |||
9b8e9a0f1c | |||
28cd3e1f2f | |||
dc058d16df | |||
bbc9abc7b9 | |||
9aaf86a33c | |||
9d510ec8fb | |||
8b958c8947 |
10
README.md
10
README.md
@ -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
|
||||||
```
|
```
|
||||||
@ -33,3 +33,5 @@ docker run -d -p 5000:5000 --name landingpage application-landingpage
|
|||||||
```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]()
|
||||||
|
@ -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"]:
|
||||||
|
302
app/config.yaml
302
app/config.yaml
@ -1,4 +1,88 @@
|
|||||||
---
|
---
|
||||||
|
accounts:
|
||||||
|
name: Accounts
|
||||||
|
description: My Online Accounts
|
||||||
|
icon:
|
||||||
|
class: fa-solid fa-users
|
||||||
|
subitems:
|
||||||
|
- name: Meta
|
||||||
|
description: Social and developer networks
|
||||||
|
icon:
|
||||||
|
class: fa-brands fa-meta
|
||||||
|
url:
|
||||||
|
subitems:
|
||||||
|
- name: Instagram
|
||||||
|
description: Follow me on Instagram
|
||||||
|
icon:
|
||||||
|
class: fa-brands fa-instagram
|
||||||
|
url: https://www.instagram.com/kevinveenbirkenbach/
|
||||||
|
identifier: kevinveenbirkenbach
|
||||||
|
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.
|
||||||
|
- name: Facebook
|
||||||
|
description: Like my Facebook page
|
||||||
|
icon:
|
||||||
|
class: fa-brands fa-facebook
|
||||||
|
url: https://www.facebook.com/kevinveenbirkenbach
|
||||||
|
- link: navigation.header.contact.messenger
|
||||||
|
- 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
|
||||||
|
url:
|
||||||
|
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
|
||||||
|
|
||||||
|
- name: Discourse
|
||||||
|
description: Follow me on Discourse
|
||||||
|
icon:
|
||||||
|
class: fa-brands fa-discourse
|
||||||
|
url: https://forum.veen.world/u/kevinveenbirkenbach
|
||||||
|
|
||||||
|
|
||||||
cards:
|
cards:
|
||||||
- icon:
|
- icon:
|
||||||
source: https://cloud.veen.world/s/logo_agile_coach_512x512/download
|
source: https://cloud.veen.world/s/logo_agile_coach_512x512/download
|
||||||
@ -123,25 +207,25 @@ 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 +236,13 @@ 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,25 +255,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.messenger.matrix
|
||||||
- name: Matrix
|
|
||||||
description: Chat with me on Matrix
|
|
||||||
icon:
|
|
||||||
class: fa-solid fa-cubes
|
|
||||||
identifier: "@kevinveenbirkenbach:veen.world"
|
|
||||||
info: |
|
|
||||||
#### Why Use Matrix?
|
|
||||||
Matrix is a secure, decentralized communication platform that ensures privacy and control over your data. Learn more about [Matrix](https://matrix.org/).
|
|
||||||
|
|
||||||
#### Privacy and Security
|
|
||||||
End-to-end encryption keeps your conversations private and secure.
|
|
||||||
|
|
||||||
#### Decentralized and Open
|
|
||||||
Matrix's federated network means you can host your own server or use any provider while staying connected.
|
|
||||||
|
|
||||||
#### A Movement for Digital Freedom
|
|
||||||
By using Matrix, you support open, transparent, and secure communication.
|
|
||||||
|
|
||||||
- name: Mobile
|
- name: Mobile
|
||||||
description: Call me
|
description: Call me
|
||||||
icon:
|
icon:
|
||||||
@ -215,7 +281,28 @@ navigation:
|
|||||||
|
|
||||||
#### Stand for Security
|
#### Stand for Security
|
||||||
Using PGP is more than a tool—it's a statement about valuing freedom, privacy, and the security of digital communication. Explore the principles of secure communication with [privacy guides](https://privacyguides.org/).
|
Using PGP is more than a tool—it's a statement about valuing freedom, privacy, and the security of digital communication. Explore the principles of secure communication with [privacy guides](https://privacyguides.org/).
|
||||||
|
- name: Messenger
|
||||||
|
description: Social and developer networks
|
||||||
|
icon:
|
||||||
|
class: fa-solid fa-comments
|
||||||
|
subitems:
|
||||||
|
- name: Matrix
|
||||||
|
description: Chat with me on Matrix
|
||||||
|
icon:
|
||||||
|
class: fa-solid fa-cubes
|
||||||
|
identifier: "@kevinveenbirkenbach:veen.world"
|
||||||
|
info: |
|
||||||
|
#### Why Use Matrix?
|
||||||
|
Matrix is a secure, decentralized communication platform that ensures privacy and control over your data. Learn more about [Matrix](https://matrix.org/).
|
||||||
|
|
||||||
|
#### Privacy and Security
|
||||||
|
End-to-end encryption keeps your conversations private and secure.
|
||||||
|
|
||||||
|
#### Decentralized and Open
|
||||||
|
Matrix's federated network means you can host your own server or use any provider while staying connected.
|
||||||
|
|
||||||
|
#### A Movement for Digital Freedom
|
||||||
|
By using Matrix, you support open, transparent, and secure communication.
|
||||||
- name: Signal
|
- name: Signal
|
||||||
description: Message me on Signal
|
description: Message me on Signal
|
||||||
icon:
|
icon:
|
||||||
@ -223,7 +310,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.messenger.matrix
|
||||||
- name: Telegram
|
- name: Telegram
|
||||||
description: Message me on Telegram
|
description: Message me on Telegram
|
||||||
icon:
|
icon:
|
||||||
@ -233,104 +320,29 @@ 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.messenger.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: |
|
info: Consider using decentralized and privacy-respecting alternatives to maintain control over your data, improve security, and foster healthier online interactions.
|
||||||
⚠️ **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.messenger.matrix
|
||||||
|
- link: navigation.header.contact.messenger.signal
|
||||||
|
- link: navigation.header.contact.messenger.telegram
|
||||||
|
|
||||||
footer:
|
footer:
|
||||||
- name: External Accounts
|
- link: accounts
|
||||||
description: Me on other plattforms
|
- name: Solution Hub
|
||||||
|
description: Curated collection of self hosted tools
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-external-link-alt
|
class: fa-solid fa-network-wired
|
||||||
subitems:
|
|
||||||
- name: Meta
|
|
||||||
description: Social and developer networks
|
|
||||||
icon:
|
|
||||||
class: fa-brands fa-meta
|
|
||||||
url:
|
url:
|
||||||
subitems:
|
subitems:
|
||||||
- name: Instagram
|
|
||||||
description: Follow me on Instagram
|
|
||||||
icon:
|
|
||||||
class: fa-brands fa-instagram
|
|
||||||
url: https://www.instagram.com/kevinveenbirkenbach/
|
|
||||||
- name: Facebook
|
|
||||||
description: Like my Facebook page
|
|
||||||
icon:
|
|
||||||
class: fa-brands fa-facebook
|
|
||||||
url: https://www.facebook.com/kevinveenbirkenbach
|
|
||||||
- name: Communication
|
|
||||||
description: Social and developer networks
|
|
||||||
icon:
|
|
||||||
class: fa-brands fa-meta
|
|
||||||
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
|
|
||||||
subitems: []
|
|
||||||
- name: LinkedIn
|
|
||||||
description: Connect on LinkedIn
|
|
||||||
icon:
|
|
||||||
class: bi bi-linkedin
|
|
||||||
url: https://www.linkedin.com/in/kevinveenbirkenbach
|
|
||||||
subitems: []
|
|
||||||
- name: Sports
|
|
||||||
description: My sport activities
|
|
||||||
icon:
|
|
||||||
class: fa-solid fa-running
|
|
||||||
url:
|
|
||||||
subitems:
|
|
||||||
- name: Garmin
|
|
||||||
description: My Garmin activities
|
|
||||||
icon:
|
|
||||||
class: fa-solid fa-person-running
|
|
||||||
url: https://s.veen.world/garmin
|
|
||||||
subitems: []
|
|
||||||
- name: Eversports
|
|
||||||
description: My Eversports sessions
|
|
||||||
icon:
|
|
||||||
class: fa-solid fa-dumbbell
|
|
||||||
url: https://s.veen.world/eversports
|
|
||||||
subitems: []
|
|
||||||
- name: Duolingo
|
|
||||||
description: Learn with me on Duolingo
|
|
||||||
icon:
|
|
||||||
class: fa-solid fa-language
|
|
||||||
url: https://www.duolingo.com/profile/kevinbirkenbach
|
|
||||||
subitems: []
|
|
||||||
- name: Spotify
|
|
||||||
description: Listen to my playlists
|
|
||||||
icon:
|
|
||||||
class: fa-brands fa-spotify
|
|
||||||
url: https://open.spotify.com/user/31vebfzbjf3p7oualis76qfpr5ty
|
|
||||||
subitems: []
|
|
||||||
- name: Patreon
|
|
||||||
description: Support me on Patreon
|
|
||||||
icon:
|
|
||||||
class: fa-brands fa-patreon
|
|
||||||
url: https://patreon.com/kevinveenbirkenbach
|
|
||||||
subitems: []
|
|
||||||
- name: Community
|
- name: Community
|
||||||
description: My presence in the Fediverse
|
description: Tools to manage the community
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-users
|
class: fa-solid fa-users
|
||||||
subitems:
|
subitems:
|
||||||
@ -338,122 +350,128 @@ navigation:
|
|||||||
description: Join the discussion
|
description: Join the discussion
|
||||||
icon:
|
icon:
|
||||||
class: fa-brands fa-discourse
|
class: fa-brands fa-discourse
|
||||||
url: https://forum.veen.world/u/kevinveenbirkenbach
|
url: https://forum.veen.world/
|
||||||
subitems: []
|
- name: Learning Platform
|
||||||
|
description: Learn with my academy
|
||||||
|
icon:
|
||||||
|
class: fa-solid fa-graduation-cap
|
||||||
|
url: https://academy.veen.world/
|
||||||
- name: Newsletter
|
- name: Newsletter
|
||||||
description: Subscribe to my newsletter
|
description: Subscribe to my newsletter
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-envelope-open-text
|
class: fa-solid fa-envelope-open-text
|
||||||
url: https://newsletter.veen.world/subscription/form
|
url: https://newsletter.veen.world/subscription/form
|
||||||
subitems: []
|
- name: Project Management
|
||||||
- name: Work Hub
|
description: Project Management Tools
|
||||||
description: Curated collection of self hosted tools for work, organization, and
|
|
||||||
learning.
|
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-toolbox
|
class: fa-solid fa-chart-line
|
||||||
url:
|
|
||||||
subitems:
|
subitems:
|
||||||
- name: Open Project
|
- name: Open Project
|
||||||
description: Explore my projects
|
description: Explore my projects
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-chart-line
|
class: fa-solid fa-tasks
|
||||||
url: https://project.veen.world/
|
url: https://project.veen.world/
|
||||||
subitems: []
|
|
||||||
- name: Taiga
|
- name: Taiga
|
||||||
description: View my Kanban board
|
description: View my Kanban board
|
||||||
icon:
|
icon:
|
||||||
class: bi bi-clipboard2-check-fill
|
class: bi bi-clipboard2-check-fill
|
||||||
url: https://kanban.veen.world/
|
url: https://kanban.veen.world/
|
||||||
subitems: []
|
|
||||||
- name: Matomo
|
- name: Matomo
|
||||||
description: Analyze with Matomo
|
description: Analyze with Matomo
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-chart-simple
|
class: fa-solid fa-chart-simple
|
||||||
url: https://matomo.veen.world/
|
url: https://matomo.veen.world/
|
||||||
subitems: []
|
|
||||||
- name: Baserow
|
- name: Baserow
|
||||||
description: Organize with Baserow
|
description: Organize with Baserow
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-table
|
class: fa-solid fa-table
|
||||||
url: https://baserow.veen.world/
|
url: https://baserow.veen.world/
|
||||||
subitems: []
|
- name: Communication
|
||||||
|
icon:
|
||||||
|
class: fa-solid fa-comments
|
||||||
|
subitems:
|
||||||
- name: Elements
|
- name: Elements
|
||||||
description: Chat with me
|
description: Chat with me
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-comment
|
class: fa-solid fa-comment
|
||||||
url: https://element.veen.world/
|
url: https://element.veen.world/
|
||||||
subitems: []
|
|
||||||
- name: Big Blue Button
|
- name: Big Blue Button
|
||||||
description: Join live events
|
description: Join live events
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-video
|
class: fa-solid fa-video
|
||||||
url: https://meet.veen.world/
|
url: https://meet.veen.world/
|
||||||
subitems: []
|
|
||||||
- name: Mailu
|
- name: Mailu
|
||||||
description: Send me a mail
|
description: Send me a mail
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-envelope
|
class: fa-solid fa-envelope
|
||||||
url: https://mail.veen.world/
|
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
|
- name: Yourls
|
||||||
description: Find my curated links
|
description: Find my curated links
|
||||||
icon:
|
icon:
|
||||||
class: bi bi-link
|
class: bi bi-link
|
||||||
url: https://s.veen.world/admin/
|
url: https://s.veen.world/admin/
|
||||||
subitems: []
|
|
||||||
- name: Nextcloud
|
- name: Nextcloud
|
||||||
description: Access my cloud storage
|
description: Access my cloud storage
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-cloud
|
class: fa-solid fa-cloud
|
||||||
url: https://cloud.veen.world/
|
url: https://cloud.veen.world/
|
||||||
subitems: []
|
|
||||||
|
- name: About
|
||||||
|
description: All information about me
|
||||||
|
icon:
|
||||||
|
class: fa-solid fa-user
|
||||||
|
subitems:
|
||||||
- name: Logbooks
|
- name: Logbooks
|
||||||
description: My activity logs
|
description: My activity logs
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-book
|
class: fa-solid fa-book
|
||||||
url:
|
|
||||||
subitems:
|
subitems:
|
||||||
- name: Skydiver
|
- name: Skydiver
|
||||||
description: View my skydiving logs
|
description: View my skydiving logs
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-parachute-box
|
class: fa-solid fa-parachute-box
|
||||||
url: https://s.veen.world/skydiverlog
|
url: https://s.veen.world/skydiverlog
|
||||||
subitems: []
|
|
||||||
- name: Skipper
|
- name: Skipper
|
||||||
description: See my sailing records
|
description: See my sailing records
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-sailboat
|
class: fa-solid fa-sailboat
|
||||||
url: https://s.veen.world/meilenbuch
|
url: https://s.veen.world/meilenbuch
|
||||||
subitems: []
|
|
||||||
- name: Diver
|
- name: Diver
|
||||||
description: Check my diving logs
|
description: Check my diving logs
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-fish
|
class: fa-solid fa-fish
|
||||||
url: https://s.veen.world/diverlog
|
url: https://s.veen.world/diverlog
|
||||||
subitems: []
|
|
||||||
- name: Pilot
|
- name: Pilot
|
||||||
description: Review my flight logs
|
description: Review my flight logs
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-plane
|
class: fa-solid fa-plane
|
||||||
url: https://s.veen.world/pilotlog
|
url: https://s.veen.world/pilotlog
|
||||||
subitems: []
|
|
||||||
- name: Nature
|
- name: Nature
|
||||||
description: Explore my nature logs
|
description: Explore my nature logs
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-tree
|
class: fa-solid fa-tree
|
||||||
url: https://s.veen.world/naturejournal
|
url: https://s.veen.world/naturejournal
|
||||||
- name: Vita
|
- name: Vita
|
||||||
description: View my CV and professional background
|
description: View my CV
|
||||||
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
|
||||||
subitems: []
|
- name: Certificates
|
||||||
|
description: View my certifications and degrees
|
||||||
|
icon:
|
||||||
|
class: fa-solid fa-graduation-cap
|
||||||
|
url: https://s.veen.world/lebenslauf
|
||||||
|
|
||||||
- name: Imprint
|
- name: Imprint
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-scale-balanced
|
class: fa-solid fa-scale-balanced
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
/* General link styles */
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: #000000;
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Header styles */
|
||||||
.header img {
|
.header img {
|
||||||
float: right;
|
float: right;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
@ -11,39 +14,39 @@ a {
|
|||||||
.header h1 {
|
.header h1 {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Equal-height container using flexbox */
|
||||||
.equal-height {
|
.equal-height {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Card styles */
|
||||||
|
.navbar, .card {
|
||||||
|
flex: 1; /* Ensures cards fill the height of their container */
|
||||||
|
border-radius: 5px; /* Rounded corners */
|
||||||
|
border: 1px solid #ccc; /* Optional border color */
|
||||||
|
padding: 10px; /* Inner spacing */
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); /* Subtle shadow effect */
|
||||||
|
color: #000000 !important;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
.card-body {
|
.card-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center; /* Zentriert die Inhalte horizontal */
|
align-items: center; /* Center content horizontally */
|
||||||
text-align: center; /* Zentriert den Text */
|
text-align: center; /* Center text alignment */
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-icon {
|
.card-icon {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center; /* Zentriert das Icon horizontal */
|
justify-content: center; /* Center the icon horizontally */
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-text,
|
.card-text,
|
||||||
.card ul {
|
.card ul {
|
||||||
text-align: left; /* Stellt sicher, dass der Text linksbündig ist */
|
text-align: left; /* Align text to the left */
|
||||||
}
|
|
||||||
|
|
||||||
.card{
|
|
||||||
flex: 1; /* Stellt sicher, dass die Karten die ganze Höhe ihrer Container ausfüllen */
|
|
||||||
border-width: 3px;
|
|
||||||
/*border-color: #000000;*/
|
|
||||||
}
|
|
||||||
|
|
||||||
h3.card-title{
|
|
||||||
font-size: 1.3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card .stretched-link{
|
|
||||||
font-size: 0.7em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-column {
|
.card-column {
|
||||||
@ -51,21 +54,32 @@ h3.card-title{
|
|||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3.footer-title{
|
.card .stretched-link {
|
||||||
|
font-size: 0.7em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3.card-title {
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Footer styles */
|
||||||
.footer {
|
.footer {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 0.7em;
|
font-size: 0.7em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer p, h3{
|
.footer p,
|
||||||
margin: 0px;
|
.footer h3 {
|
||||||
padding: 0px;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h3.footer-title {
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dropdown menu styles */
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
position: absolute !important;
|
position: absolute !important;
|
||||||
}
|
}
|
||||||
@ -73,40 +87,26 @@ h3.footer-title{
|
|||||||
.dropdown-menu-footer {
|
.dropdown-menu-footer {
|
||||||
position: absolute !important;
|
position: absolute !important;
|
||||||
top: auto !important;
|
top: auto !important;
|
||||||
bottom: 100%; /* Positioniert das Menü über dem Auslöser */
|
bottom: 100%; /* Positions the menu above the trigger */
|
||||||
transform: translateY(-10px); /* Optional: Sanfter Abstand */
|
transform: translateY(-10px); /* Optional spacing for smoother appearance */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Dropdown submenu styles */
|
||||||
.dropdown-submenu {
|
.dropdown-submenu {
|
||||||
position: relative;
|
position: relative;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar, .card {
|
|
||||||
border-radius: 5px; /* Runde Ecken */
|
|
||||||
border: 1px solid #ccc; /* Optionale Rahmenfarbe */
|
|
||||||
padding: 10px; /* Optionaler Abstand innen */
|
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); /* Optionale Schatteneffekte */
|
|
||||||
color: #000000 !important;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-nav {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
/* Stellt sicher, dass Submenüs korrekt positioniert sind */
|
|
||||||
.dropdown-submenu {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-submenu > .dropdown-menu {
|
.dropdown-submenu > .dropdown-menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 100%; /* Positioniert das Submenü rechts vom Hauptmenü */
|
left: 100%; /* Default position: open to the right */
|
||||||
margin-top: -1px;
|
margin-top: -1px;
|
||||||
|
z-index: 1050;
|
||||||
|
transition: opacity 0.3s ease-in-out; /* Smooth opacity transition */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handle collapse behavior for dropdowns */
|
||||||
.dropdown-menu.collapse {
|
.dropdown-menu.collapse {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -115,7 +115,7 @@ h3.footer-title{
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Standardmäßig sind die Submenüs ausgeblendet */
|
/* Ensure submenus are hidden by default */
|
||||||
.dropdown-submenu .dropdown-menu {
|
.dropdown-submenu .dropdown-menu {
|
||||||
display: none;
|
display: none;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@ -125,15 +125,19 @@ h3.footer-title{
|
|||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Beim Hover auf das Submenü-Element wird das Menü angezeigt */
|
/* Show submenu on hover */
|
||||||
.dropdown-submenu:hover > .dropdown-menu {
|
.dropdown-submenu:hover > .dropdown-menu {
|
||||||
display: block;
|
display: block;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
z-index: 1050;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Um sicherzustellen, dass es nicht sofort verschwindet */
|
/* Ensure submenu remains visible when hovered over */
|
||||||
.dropdown-submenu:hover > .dropdown-menu:hover {
|
.dropdown-submenu:hover > .dropdown-menu:hover {
|
||||||
display: block;
|
display: block;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handle dynamic submenu positioning */
|
||||||
|
.dropdown-submenu > .dropdown-menu[style*="right: 100%"] {
|
||||||
|
left: auto; /* Override left position for leftward opening */
|
||||||
|
}
|
||||||
|
@ -9,6 +9,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
const menu = submenu.querySelector('.dropdown-menu');
|
const menu = submenu.querySelector('.dropdown-menu');
|
||||||
if (menu) {
|
if (menu) {
|
||||||
|
// Dynamische Positionierung
|
||||||
|
const rect = menu.getBoundingClientRect();
|
||||||
|
const viewportWidth = window.innerWidth;
|
||||||
|
|
||||||
|
// Überprüfen, ob Platz nach rechts ist, sonst nach links öffnen
|
||||||
|
if (rect.right > viewportWidth) {
|
||||||
|
menu.style.left = 'auto';
|
||||||
|
menu.style.right = '100%';
|
||||||
|
} else {
|
||||||
|
menu.style.left = '100%';
|
||||||
|
menu.style.right = 'auto';
|
||||||
|
}
|
||||||
|
|
||||||
menu.style.display = 'block';
|
menu.style.display = 'block';
|
||||||
menu.style.opacity = '1';
|
menu.style.opacity = '1';
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
<hr />
|
<hr />
|
||||||
<h3 class="card-title">{{ card.title }}</h3>
|
<h3 class="card-title">{{ card.title }}</h3>
|
||||||
<p class="card-text">{{ card.text }}</p>
|
<p class="card-text">{{ card.text }}</p>
|
||||||
{% if card.link %}
|
{% if card.url %}
|
||||||
<a href="{{ card.link }}" class="mt-auto btn btn-light stretched-link" ><i class="fa-solid fa-globe"></i> {{ card.link_text }}</a>
|
<a href="{{ card.url }}" class="mt-auto btn btn-light stretched-link" ><i class="fa-solid fa-globe"></i> {{ card.link_text }}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<i class="fa-solid fa-hourglass"></i> {{ card.link_text }}
|
<i class="fa-solid fa-hourglass"></i> {{ card.link_text }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -4,13 +4,17 @@
|
|||||||
{% if subitem.subitems %}
|
{% if subitem.subitems %}
|
||||||
<li class="dropdown-submenu position-relative">
|
<li class="dropdown-submenu position-relative">
|
||||||
<a class="dropdown-item dropdown-toggle" href="#" title="{{ subitem.description }}">
|
<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 }}
|
<i class="{{ subitem.icon.class }}"></i> {{ subitem.name }}
|
||||||
|
{% else %}
|
||||||
|
<p>Missing icon in subitem: {{ subitem }}</p>
|
||||||
|
{% endif %}
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
{{ 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 +26,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 +37,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 +54,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">
|
||||||
|
{% if item.icon is defined and item.icon.class is defined %}
|
||||||
<i class="{{ item.icon.class }}" data-bs-toggle="tooltip"></i> {{ item.name }}
|
<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) }}
|
||||||
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
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.
|
||||||
@ -21,7 +22,9 @@ class ConfigurationResolver:
|
|||||||
for key, value in list(current_config.items()):
|
for key, value in list(current_config.items()):
|
||||||
if key == "link":
|
if key == "link":
|
||||||
try:
|
try:
|
||||||
target = self._find_entry(root_config, value.lower())
|
target = self._find_entry(root_config, value.lower(), True)
|
||||||
|
if isinstance(target, list) and len(target) > 2:
|
||||||
|
target = self._find_entry(root_config, value.lower(), False)
|
||||||
current_config.clear()
|
current_config.clear()
|
||||||
current_config.update(target)
|
current_config.update(target)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -35,7 +38,18 @@ 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 _find_entry(self, config, path):
|
def _get_subitems(self,current):
|
||||||
|
if isinstance(current, dict) and ("subitems" in current and current["subitems"]):
|
||||||
|
current = current["subitems"]
|
||||||
|
return current
|
||||||
|
|
||||||
|
def _find_by_name(self,current, part):
|
||||||
|
return next(
|
||||||
|
(item for item in current if isinstance(item, dict) and item.get("name", "").lower() == part),
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|
||||||
|
def _find_entry(self, config, path, subitems):
|
||||||
"""
|
"""
|
||||||
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 `subitems` navigation.
|
||||||
@ -45,10 +59,7 @@ class ConfigurationResolver:
|
|||||||
for part in parts:
|
for part in parts:
|
||||||
if isinstance(current, list):
|
if isinstance(current, list):
|
||||||
# Look for a matching name in the list
|
# Look for a matching name in the list
|
||||||
found = next(
|
found = self._find_by_name(current,part)
|
||||||
(item for item in current if isinstance(item, dict) and item.get("name", "").lower() == part),
|
|
||||||
None
|
|
||||||
)
|
|
||||||
if found:
|
if 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])}. "
|
||||||
@ -64,20 +75,22 @@ class ConfigurationResolver:
|
|||||||
# 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)
|
||||||
|
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])}. "
|
||||||
f"Current dictionary: {current}"
|
f"Current dictionary: {current}"
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
current = current[key]
|
current = current[key]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
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])}"
|
||||||
)
|
)
|
||||||
|
if subitems:
|
||||||
# Navigate into `subitems` if present
|
current = self._get_subitems(current)
|
||||||
if isinstance(current, dict) and ("subitems" in current and current["subitems"]):
|
|
||||||
current = current["subitems"]
|
|
||||||
|
|
||||||
return current
|
return current
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user