markdown template
Some checks failed
Build, Push, and Deploy to Nomad / docker-nomad (push) Has been cancelled

This commit is contained in:
2024-12-11 20:34:20 +01:00
parent f3ae944edf
commit e728d5c14e
6 changed files with 187 additions and 122 deletions

98
app.py
View File

@@ -1,20 +1,31 @@
from fastapi import FastAPI, Request from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles from fastapi.staticfiles import StaticFiles
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates from fastapi.templating import Jinja2Templates
import json import json
import os import os
from markdown_render import MarkdownRenderer from fastapi import FastAPI
from fastapi.responses import HTMLResponse
from contextlib import asynccontextmanager
from markdown_render import render_markdown_with_jinja
from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware
app = FastAPI() # Context manager for app lifespan
md_renderer = MarkdownRenderer() @asynccontextmanager
async def lifespan(app: FastAPI):
print("App startup: Processing Markdown files...")
process_markdown_files("./data", "./data") # Process all Markdown files
print("Markdown processing complete!")
yield # Allow the app to start
print("App shutdown: Cleanup complete.")
app = FastAPI(lifespan=lifespan)
app.mount("/data", StaticFiles(directory="data"), name="data")
# Mount static files # Mount static files
app.mount("/static", StaticFiles(directory="static"), name="static") app.mount("/static", StaticFiles(directory="static"), name="static")
#app.add_middleware( HTTPSRedirectMiddleware ) app.add_middleware( HTTPSRedirectMiddleware )
# Templates directory # Templates directory
templates = Jinja2Templates(directory="templates") templates = Jinja2Templates(directory="templates")
@@ -24,34 +35,22 @@ with open("mock_data.json") as file:
data = json.load(file) data = json.load(file)
@app.get("/test", response_class=HTMLResponse) @app.get("/test", response_class=HTMLResponse)
async def mark_test(): async def home_test():
markdown_content = """ # Load the Markdown content from a file
# Custom Tags Test with open("templates/example.md", "r") as f:
markdown_content = f.read()
Here is an image overlay: # Render Markdown first, then inject Jinja2 components
{img-left-overlay: src=my-cat.png} rendered_html = render_markdown_with_jinja(markdown_content)
Here is a box: # Wrap in a base HTML layout
{box: title=Important, content=This is a reusable box.} html_template = f"""
And a note:
{note: content=This is a note for the user.}
Warning section:
{warning: content=Pay attention to this warning!}
"""
# Render Markdown content
rendered_html = md_renderer.render( markdown_content )
# Wrap in a basic template
template = f"""
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Markdown Tags</title> <title>Markdown + Jinja2</title>
<style> <style>
.img-left-overlay img {{ width: 300px; }} .img-left-overlay img {{ width: 300px; }}
.box {{ border: 1px solid #ccc; padding: 10px; background-color: #f9f9f9; }} .box {{ border: 1px solid #ccc; padding: 10px; background-color: #f9f9f9; }}
@@ -66,7 +65,37 @@ async def mark_test():
</body> </body>
</html> </html>
""" """
return HTMLResponse( content = template ) return HTMLResponse(content=html_template)
def process_markdown_files(input_dir: str, output_dir: str):
"""
Recursively process all Markdown files in the input directory,
render them to HTML, and save them in the output directory.
"""
for root, _, files in os.walk(input_dir):
for file in files:
if file.endswith(".md"):
input_file_path = os.path.join(root, file)
# Determine output file path (convert .md to .html)
relative_path = os.path.relpath(input_file_path, input_dir)
output_file_path = os.path.join(output_dir, os.path.splitext(relative_path)[0] + ".html")
# Ensure the output directory exists
os.makedirs(os.path.dirname(output_file_path), exist_ok=True)
# Read Markdown content
with open(input_file_path, "r", encoding="utf-8") as md_file:
markdown_content = md_file.read()
# Render Markdown with Jinja2
print(f"Processing: {input_file_path} -> {output_file_path}")
rendered_html = render_markdown_with_jinja(markdown_content)
# Write the rendered HTML to the output file
with open(output_file_path, "w", encoding="utf-8") as html_file:
html_file.write(rendered_html)
# Index route # Index route
@app.get("/", response_class=HTMLResponse) @app.get("/", response_class=HTMLResponse)
@@ -76,7 +105,18 @@ async def get_index(request: Request):
{"request": request, "data": data, "page_title": "Forside", "author": "Henrik"} {"request": request, "data": data, "page_title": "Forside", "author": "Henrik"}
) )
@app.get("/sitemap", response_class=HTMLResponse)
async def sitemap():
"""Simple home page listing available HTML files."""
links = []
for root, _, files in os.walk("./data"):
for file in files:
if file.endswith(".html"):
relative_path = os.path.relpath(os.path.join(root, file), "./data")
link = f"<a href='/data/{relative_path}'>{relative_path}</a>"
links.append(link)
links_html = "<br>".join(links)
return HTMLResponse(content=f"<h1>Available Pages</h1>{links_html}")
# Category route # Category route
@app.get("/category/{category_name}", response_class=HTMLResponse) @app.get("/category/{category_name}", response_class=HTMLResponse)

View File

@@ -0,0 +1,43 @@
import os
from markdown_render import render_markdown_with_jinja
def process_markdown_files(input_dir: str, output_dir: str):
"""
Recursively process all Markdown files in the input directory,
render them to HTML, and save them to the output directory.
"""
for root, _, files in os.walk(input_dir):
for file in files:
if file.endswith(".md"):
input_file_path = os.path.join(root, file)
# Determine output file path (convert .md to .html)
relative_path = os.path.relpath(input_file_path, input_dir)
output_file_path = os.path.join(output_dir, os.path.splitext(relative_path)[0] + ".html")
# Ensure the output directory exists
os.makedirs(os.path.dirname(output_file_path), exist_ok=True)
# Read Markdown content
with open(input_file_path, "r", encoding="utf-8") as md_file:
markdown_content = md_file.read()
# Render Markdown with Jinja2
print(f"Processing: {input_file_path} -> {output_file_path}")
rendered_html = render_markdown_with_jinja(markdown_content)
# Write the rendered HTML to the output file
with open(output_file_path, "w", encoding="utf-8") as html_file:
html_file.write(rendered_html)
print("Markdown processing complete!")
if __name__ == "__main__":
# Input directory containing Markdown files
input_directory = "./data"
# Output directory where HTML files will be stored
output_directory = "./data"
# Start the processing
process_markdown_files(input_directory, output_directory)

View File

@@ -0,0 +1,5 @@
# BoligBolig Bolig Bolig - Hvor skal sengen placeres
Nu bliver det spænde!
{{ note("Dette er stadig en test side") }}

View File

@@ -0,0 +1,6 @@
# Lidt mere info om job
Der skal langt mere tekst her
{{ note("Husk alpha side") }}

View File

@@ -1,101 +1,59 @@
from markdown_it import MarkdownIt from jinja2 import Environment, FileSystemLoader
import re import markdown
class MarkdownRenderer: def img_left_overlay(src):
def render_img_left_overlay(self, params): """Render an image with overlay."""
src = params.get("src", "") return f'''
return f''' <div class="img-left-overlay">
<div class="img-left-overlay"> <img src="{src}" alt="Overlay Image">
<img src="{src}" alt="Overlay Image"> <div class="overlay-text">Overlay Text</div>
<div class="overlay-text">Overlay Text</div> </div>
</div> '''
'''
def render_box(self, params): def box(title, content):
title = params.get("title", "Box") """Render a box component."""
content = params.get("content", "") return f'''
return f''' <div class="box">
<div class="box"> <strong>{title}</strong>
<strong>{title}</strong> <p>{content}</p>
<p>{content}</p> </div>
</div> '''
'''
def render_note(self, params): def note(content):
content = params.get("content", "") """Render a note component."""
return f''' return f'''
<div class="note"> <div class="note">
<p>{content}</p> <p>{content}</p>
</div> </div>
''' '''
def render_warning(self, params): def warning(content):
content = params.get("content", "") """Render a warning component."""
return f''' return f'''
<div class="warning"> <div class="warning">
⚠️ <p>{content}</p> ⚠️ <p>{content}</p>
</div> </div>
''' '''
def __init__(self): def create_jinja_environment(template_dir="templates"):
self.md = MarkdownIt() """Set up Jinja2 environment and register custom components."""
self.TAG_RENDERERS = { env = Environment(loader=FileSystemLoader(template_dir))
"img-left-overlay": self.render_img_left_overlay, env.globals.update({
"box": self.render_box, "img_left_overlay": img_left_overlay,
"note": self.render_note, "box": box,
"warning": self.render_warning, "note": note,
} "warning": warning,
self._add_custom_tags_plugin() })
print("Custom Markdown renderer initialized!") return env
def _add_custom_tags_plugin(self): def render_markdown_with_jinja(markdown_content, template_data=None):
def custom_tag_rule(state, silent): """Process Markdown first, then inject Jinja2 components."""
# Match custom tags # Step 1: Convert Markdown to HTML
pattern = r"^\{(\w+):\s*([^}]*)\}$" md_html = markdown.markdown(markdown_content, extensions=['extra', 'nl2br'])
line = state.src[state.pos:].strip()
print(f"Parsing line: '{line}'") # Debug current line
match = re.match(pattern, line) # Step 2: Use Jinja2 to inject components into the already-rendered HTML
if not match: env = create_jinja_environment()
print("No match for custom tag.") template = env.from_string(md_html)
return False rendered_html = template.render(template_data or {})
tag_name = match.group(1) return rendered_html
params_raw = match.group(2)
print(f"Matched tag: {tag_name}, Params: {params_raw}")
# Parse parameters
params = {}
for pair in params_raw.split(","):
if "=" in pair:
key, value = pair.split("=", 1)
params[key.strip()] = value.strip()
# Check if the tag_name exists in TAG_RENDERERS
if tag_name in self.TAG_RENDERERS:
print(f"Tag '{tag_name}' found. Creating token...")
token = state.push("custom_tag", "", 0)
token.meta = {"tag_name": tag_name, "params": params}
state.pos += len(line)
return True
print(f"Tag '{tag_name}' not found in TAG_RENDERERS.")
return False
def render_custom_tag(tokens, idx, options, env, *args):
token = tokens[idx]
tag_name = token.meta["tag_name"]
params = token.meta["params"]
print(f"Rendering tag '{tag_name}' with params: {params}")
if tag_name in self.TAG_RENDERERS:
return self.TAG_RENDERERS[tag_name](params)
return ""
# Register the plugin
self.md.inline.ruler.before("emphasis", "custom_tags", custom_tag_rule)
self.md.add_render_rule("custom_tag", render_custom_tag)
print("Custom tag plugin registered!")
def render(self, markdown_content):
print("Rendering Markdown content...")
return self.md.render(markdown_content)

13
templates/example.md Normal file
View File

@@ -0,0 +1,13 @@
# Welcome to My Page
Here is an image overlay:
{{ img_left_overlay("my-cat.png") }}
Here is a box:
{{ box("Important Notice", "This is a reusable box.") }}
Here is a note:
{{ note("This is a very important note for the user.") }}
Here is a warning:
{{ warning("Be cautious when proceeding!") }}