generated from hjess/PythonTemplateProject
Middelware update and footer content
All checks were successful
Build, Push, and Deploy to Nomad / docker-nomad (push) Successful in 39s
All checks were successful
Build, Push, and Deploy to Nomad / docker-nomad (push) Successful in 39s
This commit is contained in:
@@ -3,6 +3,7 @@ import json
|
|||||||
from fastapi import APIRouter, Request
|
from fastapi import APIRouter, Request
|
||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
|
from fastapi.responses import JSONResponse
|
||||||
|
|
||||||
|
|
||||||
class CategoryController:
|
class CategoryController:
|
||||||
@@ -22,6 +23,10 @@ class CategoryController:
|
|||||||
methods=["GET"],
|
methods=["GET"],
|
||||||
response_class=HTMLResponse,
|
response_class=HTMLResponse,
|
||||||
)
|
)
|
||||||
|
self.router.add_api_route(
|
||||||
|
"/categories", self.list_categories, methods = ["GET"], response_class = JSONResponse
|
||||||
|
)
|
||||||
|
|
||||||
def _load_data(self, data_file):
|
def _load_data(self, data_file):
|
||||||
"""Load JSON data from a file."""
|
"""Load JSON data from a file."""
|
||||||
with open(data_file, "r", encoding="utf-8") as file:
|
with open(data_file, "r", encoding="utf-8") as file:
|
||||||
@@ -53,3 +58,11 @@ class CategoryController:
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
return HTMLResponse("Kategori ikke fundet", status_code=404)
|
return HTMLResponse("Kategori ikke fundet", status_code=404)
|
||||||
|
|
||||||
|
async def list_categories(self, request: Request):
|
||||||
|
"""Return a list of all categories with their name and path."""
|
||||||
|
categories = [
|
||||||
|
{ "name": category["name"], "path": category["path"] }
|
||||||
|
for category in self.data.get( "categories", [] )
|
||||||
|
]
|
||||||
|
return JSONResponse( content = categories )
|
||||||
@@ -61,5 +61,4 @@ class DynamicController:
|
|||||||
|
|
||||||
# Fallback: Return a 404 if no content is found
|
# Fallback: Return a 404 if no content is found
|
||||||
return Response(f"No content found for {route_name}", status_code=404)
|
return Response(f"No content found for {route_name}", status_code=404)
|
||||||
|
|
||||||
return route_handler
|
return route_handler
|
||||||
|
|||||||
89
app/controllers/route_to_web.py
Normal file
89
app/controllers/route_to_web.py
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
import json
|
||||||
|
import random
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Request, FastAPI
|
||||||
|
from fastapi.templating import Jinja2Templates
|
||||||
|
from app.controllers.category_controller import CategoryController
|
||||||
|
|
||||||
|
class RouteToWeb:
|
||||||
|
def __init__(self, app: FastAPI):
|
||||||
|
"""Initialize the controller."""
|
||||||
|
self.router = APIRouter()
|
||||||
|
self.templates = Jinja2Templates(directory="templates")
|
||||||
|
self.app = app
|
||||||
|
self.category_controller = CategoryController()
|
||||||
|
self._add_routes()
|
||||||
|
self._add_global_middleware()
|
||||||
|
|
||||||
|
def _add_routes(self):
|
||||||
|
"""Add routes to the router."""
|
||||||
|
@self.router.get("/route-list", tags=["system"])
|
||||||
|
async def route_list(request: Request):
|
||||||
|
"""Render route list with categories."""
|
||||||
|
routes = [
|
||||||
|
{"path": route.path, "name": route.name or "Unnamed"}
|
||||||
|
for route in self.app.routes
|
||||||
|
]
|
||||||
|
categories = request.state.categories
|
||||||
|
return self.templates.TemplateResponse(
|
||||||
|
"route_list.html",
|
||||||
|
{"request": request, "routes": routes, "categories": categories, "page_title": "Route og Kategori Liste"},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _add_global_middleware(self):
|
||||||
|
"""Middleware to add categories and next category globally to all requests."""
|
||||||
|
|
||||||
|
@self.app.middleware( "http" )
|
||||||
|
async def add_categories_to_request(request: Request, call_next):
|
||||||
|
def generate_dynamic_description(category_name: str) -> str:
|
||||||
|
"""Generate a dynamic and engaging link text for a category."""
|
||||||
|
templates = [
|
||||||
|
"Dyk ned i kategorien {category} og bliv inspireret!",
|
||||||
|
"Opdag alt, hvad du behøver at vide i kategorien {category}.",
|
||||||
|
"Udforsk {category}-kategorien og find noget nyt og spændende.",
|
||||||
|
"Lad dig fordybe i kategorien {category} – der er meget at se!",
|
||||||
|
"Find din næste læseoplevelse i {category}-kategorien.",
|
||||||
|
"Gå på opdagelse i kategorien {category} og bliv klogere.",
|
||||||
|
"Der venter spændende indhold i {category}-kategorien – klik her!",
|
||||||
|
"Vil du vide mere? Hele kategorien {category} er kun ét klik væk.",
|
||||||
|
"Læs videre i kategorien {category} og få ny inspiration.",
|
||||||
|
"Fordyb dig i {category}-kategorien og opdag nyt indhold.",
|
||||||
|
"Spring ind i {category}-kategorien og gå på opdagelse!",
|
||||||
|
"Find masser af viden og gode læseoplevelser i {category}-kategorien.",
|
||||||
|
"Udforsk hele kategorien {category} og bliv beriget med ny viden.",
|
||||||
|
"Der er mere at læse i {category}-kategorien – gå ikke glip af det!",
|
||||||
|
"Tag et dybere kig i kategorien {category} og bliv inspireret!"
|
||||||
|
]
|
||||||
|
template = random.choice( templates )
|
||||||
|
return template.format( category = category_name.lower() )
|
||||||
|
"""Inject categories and next category into request.state globally."""
|
||||||
|
# Hent kategorier direkte fra CategoryController
|
||||||
|
categories_response = await self.category_controller.list_categories( request )
|
||||||
|
categories_data = categories_response.body.decode()
|
||||||
|
categories = json.loads( categories_data )
|
||||||
|
|
||||||
|
# Tilføj kategorier til request.state
|
||||||
|
request.state.categories = categories
|
||||||
|
|
||||||
|
# Find den aktuelle og næste kategori
|
||||||
|
current_path = request.url.path.split("/")[-1]
|
||||||
|
next_category = None
|
||||||
|
print(current_path)
|
||||||
|
|
||||||
|
for index, category in enumerate( categories ):
|
||||||
|
print(category)
|
||||||
|
if category["path"] == current_path:
|
||||||
|
# Find næste kategori (cirkulær, hvis det er den sidste)
|
||||||
|
next_index = (index + 1) % len( categories )
|
||||||
|
next_category = categories[next_index]
|
||||||
|
next_category["description"] = generate_dynamic_description( next_category["path"] )
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
|
# Tilføj næste kategori til request.state
|
||||||
|
request.state.next_category = next_category
|
||||||
|
|
||||||
|
response = await call_next( request )
|
||||||
|
return response
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
|
|
||||||
|
from app.controllers.route_to_web import RouteToWeb
|
||||||
from app.services.markdown_processor import MarkdownProcessor
|
from app.services.markdown_processor import MarkdownProcessor
|
||||||
from app.services.metadata_processor import MetadataProcessor
|
from app.services.metadata_processor import MetadataProcessor
|
||||||
from app.controllers.dynamic_controller import DynamicController
|
from app.controllers.dynamic_controller import DynamicController
|
||||||
@@ -38,11 +40,13 @@ class Application:
|
|||||||
def _include_routers(self):
|
def _include_routers(self):
|
||||||
"""Include all route controllers."""
|
"""Include all route controllers."""
|
||||||
category_controller = CategoryController()
|
category_controller = CategoryController()
|
||||||
dynamic_controller = DynamicController( "./data" )
|
#dynamic_controller = DynamicController( "./data" )
|
||||||
|
route_to_web = RouteToWeb(self.app)
|
||||||
|
|
||||||
|
|
||||||
self.app.include_router( category_controller.router )
|
self.app.include_router( category_controller.router )
|
||||||
self.app.include_router( dynamic_controller.router )
|
#self.app.include_router( dynamic_controller.router )
|
||||||
|
self.app.include_router(route_to_web.router)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -54,29 +54,29 @@
|
|||||||
<li><strong>UCI El Corte Inglés</strong>: Den moderne biografoplevelse med de nyeste blockbusters. Sammenligneligt med Palads i København, men med en lidt mere luksuriøs oplevelse i selve komplekset.</li>
|
<li><strong>UCI El Corte Inglés</strong>: Den moderne biografoplevelse med de nyeste blockbusters. Sammenligneligt med Palads i København, men med en lidt mere luksuriøs oplevelse i selve komplekset.</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p><div class="button-stack">
|
<p><div class="button-stack">
|
||||||
<button onclick="openModal('modal7F1670_0')" class="stacked-button"> <img src="https://lifecooler.com/files/registos/imagens/404151/158177.jpg" class="thumbnail"></button>
|
<button onclick="openModal('modal6E3E27_0')" class="stacked-button"> <img src="https://lifecooler.com/files/registos/imagens/404151/158177.jpg" class="thumbnail"></button>
|
||||||
<div class="modal" id="modal7F1670_0"> <div class="modal-content">
|
<div class="modal" id="modal6E3E27_0"> <div class="modal-content">
|
||||||
<h2>Modal 0</h2>
|
<h2>Modal 0</h2>
|
||||||
<img src="https://lifecooler.com/files/registos/imagens/404151/158177.jpg">
|
<img src="https://lifecooler.com/files/registos/imagens/404151/158177.jpg">
|
||||||
<div class="modal-buttons">
|
<div class="modal-buttons">
|
||||||
<button onclick="closeModal('modal7F1670_0')">Close</button>
|
<button onclick="closeModal('modal6E3E27_0')">Close</button>
|
||||||
<button class="next-btn" onclick="nextModal('modal7F1670_0', 'modal7F1670_0_1')">Next</button>
|
<button class="next-btn" onclick="nextModal('modal6E3E27_0', 'modal6E3E27_0_1')">Next</button>
|
||||||
</div> </div></div>
|
</div> </div></div>
|
||||||
<button onclick="openModal('modal7F1670_0_1')" class="stacked-button"> <img src="https://lifecooler.com/files/registos/imagens/404151/158170.jpg" class="thumbnail" ></button>
|
<button onclick="openModal('modal6E3E27_0_1')" class="stacked-button"> <img src="https://lifecooler.com/files/registos/imagens/404151/158170.jpg" class="thumbnail" ></button>
|
||||||
<div class="modal" id="modal7F1670_0_1"> <div class="modal-content">
|
<div class="modal" id="modal6E3E27_0_1"> <div class="modal-content">
|
||||||
<h2>Modal 1</h2>
|
<h2>Modal 1</h2>
|
||||||
<img src="https://lifecooler.com/files/registos/imagens/404151/158170.jpg">
|
<img src="https://lifecooler.com/files/registos/imagens/404151/158170.jpg">
|
||||||
<div class="modal-buttons">
|
<div class="modal-buttons">
|
||||||
<button onclick="closeModal('modal7F1670_0_1')">Close</button>
|
<button onclick="closeModal('modal6E3E27_0_1')">Close</button>
|
||||||
<button class="next-btn" onclick="nextModal('modal7F1670_0_1', 'modal7F1670_0_1_2')">Next</button>
|
<button class="next-btn" onclick="nextModal('modal6E3E27_0_1', 'modal6E3E27_0_1_2')">Next</button>
|
||||||
</div> </div></div>
|
</div> </div></div>
|
||||||
<button onclick="openModal('modal7F1670_0_1_2')" class="stacked-button"> <img src="https://upload.wikimedia.org/wikipedia/commons/8/88/Fachada_S%C3%A3oJorge.jpg" class="thumbnail"></button>
|
<button onclick="openModal('modal6E3E27_0_1_2')" class="stacked-button"> <img src="https://upload.wikimedia.org/wikipedia/commons/8/88/Fachada_S%C3%A3oJorge.jpg" class="thumbnail"></button>
|
||||||
<div class="modal" id="modal7F1670_0_1_2"> <div class="modal-content">
|
<div class="modal" id="modal6E3E27_0_1_2"> <div class="modal-content">
|
||||||
<h2>Modal 2</h2>
|
<h2>Modal 2</h2>
|
||||||
<img src="https://upload.wikimedia.org/wikipedia/commons/8/88/Fachada_S%C3%A3oJorge.jpg">
|
<img src="https://upload.wikimedia.org/wikipedia/commons/8/88/Fachada_S%C3%A3oJorge.jpg">
|
||||||
<div class="modal-buttons">
|
<div class="modal-buttons">
|
||||||
<button onclick="closeModal('modal7F1670_0_1_2')">Close</button>
|
<button onclick="closeModal('modal6E3E27_0_1_2')">Close</button>
|
||||||
<button class="next-btn" onclick="nextModal('modal7F1670_0_1_2', 'modal7F1670_0_1_2_0')">Next</button>
|
<button class="next-btn" onclick="nextModal('modal6E3E27_0_1_2', 'modal6E3E27_0_1_2_0')">Next</button>
|
||||||
</div> </div></div>
|
</div> </div></div>
|
||||||
</div></p>
|
</div></p>
|
||||||
<p>Biografbesøg i Lissabon er en afslappet måde at tilbringe tid med familien. Jeg nyder selv at tage afsted med Erika for at dele både film og hygge.</p>
|
<p>Biografbesøg i Lissabon er en afslappet måde at tilbringe tid med familien. Jeg nyder selv at tage afsted med Erika for at dele både film og hygge.</p>
|
||||||
@@ -104,37 +104,37 @@
|
|||||||
<p>Samtidig er mange oplevelser billigere end i København, og her er en atmosfære, der er svær at finde nordpå: Lissabon har en unik blanding af tradition og modernitet, som gør den til en by, der bliver ved med at fascinere.</p>
|
<p>Samtidig er mange oplevelser billigere end i København, og her er en atmosfære, der er svær at finde nordpå: Lissabon har en unik blanding af tradition og modernitet, som gør den til en by, der bliver ved med at fascinere.</p>
|
||||||
<h3>Lidt billeder</h3>
|
<h3>Lidt billeder</h3>
|
||||||
<p><div class="button-stack">
|
<p><div class="button-stack">
|
||||||
<button onclick="openModal('modalBA6A40_0')" class="stacked-button"> <img src="https://picsum.photos/id/34/500/375" class="thumbnail"></button>
|
<button onclick="openModal('modal28D1BC_0')" class="stacked-button"> <img src="https://picsum.photos/id/34/500/375" class="thumbnail"></button>
|
||||||
<div class="modal" id="modalBA6A40_0"> <div class="modal-content">
|
<div class="modal" id="modal28D1BC_0"> <div class="modal-content">
|
||||||
<h2>Modal 0</h2>
|
<h2>Modal 0</h2>
|
||||||
<img src="https://picsum.photos/id/34/500/375">
|
<img src="https://picsum.photos/id/34/500/375">
|
||||||
<div class="modal-buttons">
|
<div class="modal-buttons">
|
||||||
<button onclick="closeModal('modalBA6A40_0')">Close</button>
|
<button onclick="closeModal('modal28D1BC_0')">Close</button>
|
||||||
<button class="next-btn" onclick="nextModal('modalBA6A40_0', 'modalBA6A40_0_1')">Next</button>
|
<button class="next-btn" onclick="nextModal('modal28D1BC_0', 'modal28D1BC_0_1')">Next</button>
|
||||||
</div> </div></div>
|
</div> </div></div>
|
||||||
<button onclick="openModal('modalBA6A40_0_1')" class="stacked-button"> <img src="https://picsum.photos/id/42/500/375" class="thumbnail" ></button>
|
<button onclick="openModal('modal28D1BC_0_1')" class="stacked-button"> <img src="https://picsum.photos/id/42/500/375" class="thumbnail" ></button>
|
||||||
<div class="modal" id="modalBA6A40_0_1"> <div class="modal-content">
|
<div class="modal" id="modal28D1BC_0_1"> <div class="modal-content">
|
||||||
<h2>Modal 1</h2>
|
<h2>Modal 1</h2>
|
||||||
<img src="https://picsum.photos/id/42/500/375">
|
<img src="https://picsum.photos/id/42/500/375">
|
||||||
<div class="modal-buttons">
|
<div class="modal-buttons">
|
||||||
<button onclick="closeModal('modalBA6A40_0_1')">Close</button>
|
<button onclick="closeModal('modal28D1BC_0_1')">Close</button>
|
||||||
<button class="next-btn" onclick="nextModal('modalBA6A40_0_1', 'modalBA6A40_0_1_2')">Next</button>
|
<button class="next-btn" onclick="nextModal('modal28D1BC_0_1', 'modal28D1BC_0_1_2')">Next</button>
|
||||||
</div> </div></div>
|
</div> </div></div>
|
||||||
<button onclick="openModal('modalBA6A40_0_1_2')" class="stacked-button"> <img src="https://picsum.photos/id/72/500/375" class="thumbnail"></button>
|
<button onclick="openModal('modal28D1BC_0_1_2')" class="stacked-button"> <img src="https://picsum.photos/id/72/500/375" class="thumbnail"></button>
|
||||||
<div class="modal" id="modalBA6A40_0_1_2"> <div class="modal-content">
|
<div class="modal" id="modal28D1BC_0_1_2"> <div class="modal-content">
|
||||||
<h2>Modal 2</h2>
|
<h2>Modal 2</h2>
|
||||||
<img src="https://picsum.photos/id/72/500/375">
|
<img src="https://picsum.photos/id/72/500/375">
|
||||||
<div class="modal-buttons">
|
<div class="modal-buttons">
|
||||||
<button onclick="closeModal('modalBA6A40_0_1_2')">Close</button>
|
<button onclick="closeModal('modal28D1BC_0_1_2')">Close</button>
|
||||||
<button class="next-btn" onclick="nextModal('modalBA6A40_0_1_2', 'modalBA6A40_0_1_2_3')">Next</button>
|
<button class="next-btn" onclick="nextModal('modal28D1BC_0_1_2', 'modal28D1BC_0_1_2_3')">Next</button>
|
||||||
</div> </div></div>
|
</div> </div></div>
|
||||||
<button onclick="openModal('modalBA6A40_0_1_2_3')" class="stacked-button"> <img src="https://picsum.photos/id/94/500/375" class="thumbnail" ></button>
|
<button onclick="openModal('modal28D1BC_0_1_2_3')" class="stacked-button"> <img src="https://picsum.photos/id/94/500/375" class="thumbnail" ></button>
|
||||||
<div class="modal" id="modalBA6A40_0_1_2_3"> <div class="modal-content">
|
<div class="modal" id="modal28D1BC_0_1_2_3"> <div class="modal-content">
|
||||||
<h2>Modal 3</h2>
|
<h2>Modal 3</h2>
|
||||||
<img src="https://picsum.photos/id/94/500/375">
|
<img src="https://picsum.photos/id/94/500/375">
|
||||||
<div class="modal-buttons">
|
<div class="modal-buttons">
|
||||||
<button onclick="closeModal('modalBA6A40_0_1_2_3')">Close</button>
|
<button onclick="closeModal('modal28D1BC_0_1_2_3')">Close</button>
|
||||||
<button class="next-btn" onclick="nextModal('modalBA6A40_0_1_2_3', 'modalBA6A40_0_1_2_3_0')">Next</button>
|
<button class="next-btn" onclick="nextModal('modal28D1BC_0_1_2_3', 'modal28D1BC_0_1_2_3_0')">Next</button>
|
||||||
</div> </div></div>
|
</div> </div></div>
|
||||||
</div></p>
|
</div></p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3063,6 +3063,21 @@ button:disabled,
|
|||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.footer-link {
|
||||||
|
font-size: 0.9rem; /* Reduceret skriftstørrelse */
|
||||||
|
font-weight: normal; /* Normal vægt, ikke fed */
|
||||||
|
text-decoration: none; /* Fjerner understregning */
|
||||||
|
display: inline-block; /* Bedre layoutjustering */
|
||||||
|
margin: 8px 0; /* Lidt luft omkring linket */
|
||||||
|
text-align: center; /* Centreret, hvis relevant */
|
||||||
|
transition: color 0.3s ease; /* Glidende farveskift ved hover */
|
||||||
|
}
|
||||||
|
.footer-link:hover {
|
||||||
|
color: #000; /* Mørkere farve ved hover */
|
||||||
|
text-decoration: none; /* Understregning ved hover */
|
||||||
|
transition: color 0.5s ease; /* Glidende farveskift ved hover */
|
||||||
|
}
|
||||||
|
|
||||||
/* Menu */
|
/* Menu */
|
||||||
#menu ul {
|
#menu ul {
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -16,3 +16,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.footer-link {
|
||||||
|
font-size: 0.9rem; /* Reduceret skriftstørrelse */
|
||||||
|
font-weight: normal; /* Normal vægt, ikke fed */
|
||||||
|
text-decoration: none; /* Fjerner understregning */
|
||||||
|
display: inline-block; /* Bedre layoutjustering */
|
||||||
|
margin: 8px 0; /* Lidt luft omkring linket */
|
||||||
|
text-align: center; /* Centreret, hvis relevant */
|
||||||
|
transition: color 0.3s ease; /* Glidende farveskift ved hover */
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #000; /* Mørkere farve ved hover */
|
||||||
|
text-decoration: none; /* Understregning ved hover */
|
||||||
|
transition: color 0.5s ease; /* Glidende farveskift ved hover */
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,18 @@
|
|||||||
|
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
{% if request.state.next_category %}
|
||||||
|
<p>
|
||||||
|
<a href="/category/{{ request.state.next_category.path }}" class="footer-link">
|
||||||
|
{{ request.state.next_category.description }}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
{% else %}
|
||||||
|
<p>Ingen næste kategori.</p>
|
||||||
|
{% endif %}
|
||||||
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
11
templates/route_list.html
Normal file
11
templates/route_list.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
<h3>Næste Kategori</h3>
|
||||||
|
{% if request.state.next_category %}
|
||||||
|
<p>
|
||||||
|
<a href="/category/{{ request.state.next_category.path }}">
|
||||||
|
{{ request.state.next_category.name }}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
{% else %}
|
||||||
|
<p>Ingen næste kategori.</p>
|
||||||
|
{% endif %}
|
||||||
Reference in New Issue
Block a user