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.responses import HTMLResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
|
||||
class CategoryController:
|
||||
@@ -22,6 +23,10 @@ class CategoryController:
|
||||
methods=["GET"],
|
||||
response_class=HTMLResponse,
|
||||
)
|
||||
self.router.add_api_route(
|
||||
"/categories", self.list_categories, methods = ["GET"], response_class = JSONResponse
|
||||
)
|
||||
|
||||
def _load_data(self, data_file):
|
||||
"""Load JSON data from a 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)
|
||||
|
||||
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
|
||||
return Response(f"No content found for {route_name}", status_code=404)
|
||||
|
||||
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 contextlib import asynccontextmanager
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from app.controllers.route_to_web import RouteToWeb
|
||||
from app.services.markdown_processor import MarkdownProcessor
|
||||
from app.services.metadata_processor import MetadataProcessor
|
||||
from app.controllers.dynamic_controller import DynamicController
|
||||
@@ -38,11 +40,13 @@ class Application:
|
||||
def _include_routers(self):
|
||||
"""Include all route controllers."""
|
||||
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( 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>
|
||||
</ul>
|
||||
<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>
|
||||
<div class="modal" id="modal7F1670_0"> <div class="modal-content">
|
||||
<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="modal6E3E27_0"> <div class="modal-content">
|
||||
<h2>Modal 0</h2>
|
||||
<img src="https://lifecooler.com/files/registos/imagens/404151/158177.jpg">
|
||||
<div class="modal-buttons">
|
||||
<button onclick="closeModal('modal7F1670_0')">Close</button>
|
||||
<button class="next-btn" onclick="nextModal('modal7F1670_0', 'modal7F1670_0_1')">Next</button>
|
||||
<button onclick="closeModal('modal6E3E27_0')">Close</button>
|
||||
<button class="next-btn" onclick="nextModal('modal6E3E27_0', 'modal6E3E27_0_1')">Next</button>
|
||||
</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>
|
||||
<div class="modal" id="modal7F1670_0_1"> <div class="modal-content">
|
||||
<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="modal6E3E27_0_1"> <div class="modal-content">
|
||||
<h2>Modal 1</h2>
|
||||
<img src="https://lifecooler.com/files/registos/imagens/404151/158170.jpg">
|
||||
<div class="modal-buttons">
|
||||
<button onclick="closeModal('modal7F1670_0_1')">Close</button>
|
||||
<button class="next-btn" onclick="nextModal('modal7F1670_0_1', 'modal7F1670_0_1_2')">Next</button>
|
||||
<button onclick="closeModal('modal6E3E27_0_1')">Close</button>
|
||||
<button class="next-btn" onclick="nextModal('modal6E3E27_0_1', 'modal6E3E27_0_1_2')">Next</button>
|
||||
</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>
|
||||
<div class="modal" id="modal7F1670_0_1_2"> <div class="modal-content">
|
||||
<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="modal6E3E27_0_1_2"> <div class="modal-content">
|
||||
<h2>Modal 2</h2>
|
||||
<img src="https://upload.wikimedia.org/wikipedia/commons/8/88/Fachada_S%C3%A3oJorge.jpg">
|
||||
<div class="modal-buttons">
|
||||
<button onclick="closeModal('modal7F1670_0_1_2')">Close</button>
|
||||
<button class="next-btn" onclick="nextModal('modal7F1670_0_1_2', 'modal7F1670_0_1_2_0')">Next</button>
|
||||
<button onclick="closeModal('modal6E3E27_0_1_2')">Close</button>
|
||||
<button class="next-btn" onclick="nextModal('modal6E3E27_0_1_2', 'modal6E3E27_0_1_2_0')">Next</button>
|
||||
</div> </div></div>
|
||||
</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>
|
||||
@@ -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>
|
||||
<h3>Lidt billeder</h3>
|
||||
<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>
|
||||
<div class="modal" id="modalBA6A40_0"> <div class="modal-content">
|
||||
<button onclick="openModal('modal28D1BC_0')" class="stacked-button"> <img src="https://picsum.photos/id/34/500/375" class="thumbnail"></button>
|
||||
<div class="modal" id="modal28D1BC_0"> <div class="modal-content">
|
||||
<h2>Modal 0</h2>
|
||||
<img src="https://picsum.photos/id/34/500/375">
|
||||
<div class="modal-buttons">
|
||||
<button onclick="closeModal('modalBA6A40_0')">Close</button>
|
||||
<button class="next-btn" onclick="nextModal('modalBA6A40_0', 'modalBA6A40_0_1')">Next</button>
|
||||
<button onclick="closeModal('modal28D1BC_0')">Close</button>
|
||||
<button class="next-btn" onclick="nextModal('modal28D1BC_0', 'modal28D1BC_0_1')">Next</button>
|
||||
</div> </div></div>
|
||||
<button onclick="openModal('modalBA6A40_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">
|
||||
<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="modal28D1BC_0_1"> <div class="modal-content">
|
||||
<h2>Modal 1</h2>
|
||||
<img src="https://picsum.photos/id/42/500/375">
|
||||
<div class="modal-buttons">
|
||||
<button onclick="closeModal('modalBA6A40_0_1')">Close</button>
|
||||
<button class="next-btn" onclick="nextModal('modalBA6A40_0_1', 'modalBA6A40_0_1_2')">Next</button>
|
||||
<button onclick="closeModal('modal28D1BC_0_1')">Close</button>
|
||||
<button class="next-btn" onclick="nextModal('modal28D1BC_0_1', 'modal28D1BC_0_1_2')">Next</button>
|
||||
</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>
|
||||
<div class="modal" id="modalBA6A40_0_1_2"> <div class="modal-content">
|
||||
<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="modal28D1BC_0_1_2"> <div class="modal-content">
|
||||
<h2>Modal 2</h2>
|
||||
<img src="https://picsum.photos/id/72/500/375">
|
||||
<div class="modal-buttons">
|
||||
<button onclick="closeModal('modalBA6A40_0_1_2')">Close</button>
|
||||
<button class="next-btn" onclick="nextModal('modalBA6A40_0_1_2', 'modalBA6A40_0_1_2_3')">Next</button>
|
||||
<button onclick="closeModal('modal28D1BC_0_1_2')">Close</button>
|
||||
<button class="next-btn" onclick="nextModal('modal28D1BC_0_1_2', 'modal28D1BC_0_1_2_3')">Next</button>
|
||||
</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>
|
||||
<div class="modal" id="modalBA6A40_0_1_2_3"> <div class="modal-content">
|
||||
<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="modal28D1BC_0_1_2_3"> <div class="modal-content">
|
||||
<h2>Modal 3</h2>
|
||||
<img src="https://picsum.photos/id/94/500/375">
|
||||
<div class="modal-buttons">
|
||||
<button onclick="closeModal('modalBA6A40_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 onclick="closeModal('modal28D1BC_0_1_2_3')">Close</button>
|
||||
<button class="next-btn" onclick="nextModal('modal28D1BC_0_1_2_3', 'modal28D1BC_0_1_2_3_0')">Next</button>
|
||||
</div> </div></div>
|
||||
</div></p>
|
||||
</div>
|
||||
|
||||
@@ -3063,6 +3063,21 @@ button:disabled,
|
||||
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 ul {
|
||||
-moz-user-select: none;
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -15,4 +15,21 @@
|
||||
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 */
|
||||
|
||||
&: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 -->
|
||||
{% 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>
|
||||
|
||||
|
||||
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