generated from hjess/PythonTemplateProject
Lets test
This commit is contained in:
0
app/services/__init__.py
Normal file
0
app/services/__init__.py
Normal file
87
app/services/markdown_processor.py
Normal file
87
app/services/markdown_processor.py
Normal file
@@ -0,0 +1,87 @@
|
||||
import os
|
||||
from app.services.markdown_render import render_markdown_with_jinja # Your custom renderer
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
|
||||
class MarkdownProcessor:
|
||||
"""
|
||||
A class to process Markdown files, extract metadata, and generate a single
|
||||
'index.html' per category directory using a custom rendering engine.
|
||||
"""
|
||||
|
||||
def __init__(self, input_dir: str, templates_dir: str):
|
||||
"""
|
||||
Initialize the MarkdownProcessor.
|
||||
|
||||
Args:
|
||||
input_dir (str): Root directory containing category subdirectories.
|
||||
templates_dir (str): Directory containing Jinja2 templates.
|
||||
"""
|
||||
self.input_dir = input_dir
|
||||
self.env = Environment(loader=FileSystemLoader(templates_dir))
|
||||
|
||||
def _process_markdown_files_in_directory(self, directory_path: str) -> list:
|
||||
"""
|
||||
Process all Markdown files in a directory using Markdown and Jinja2 custom tags.
|
||||
|
||||
Args:
|
||||
directory_path (str): Path to the category directory.
|
||||
|
||||
Returns:
|
||||
list: A list of processed sections containing metadata and rendered content.
|
||||
"""
|
||||
sections = []
|
||||
for file in sorted(os.listdir(directory_path)):
|
||||
if file.endswith(".md"):
|
||||
file_path = os.path.join(directory_path, file)
|
||||
with open(file_path, "r", encoding="utf-8") as md_file:
|
||||
markdown_content = md_file.read()
|
||||
|
||||
# Process Markdown and Jinja2
|
||||
rendered_content, metadata = render_markdown_with_jinja(markdown_content)
|
||||
|
||||
# Append the section to the list
|
||||
sections.append({
|
||||
"name": metadata.get("title", "Untitled"),
|
||||
"content": rendered_content,
|
||||
"summary": metadata.get("summary", ""),
|
||||
"author": metadata.get("author", "Unknown"),
|
||||
})
|
||||
|
||||
return sections
|
||||
|
||||
def _generate_index_html(self, directory_path: str, sections: list, output_file: str):
|
||||
"""
|
||||
Generate the index.html file for a category using the combined sections.
|
||||
|
||||
Args:
|
||||
directory_path (str): Path to the category directory.
|
||||
sections (list): List of processed Markdown content and metadata.
|
||||
output_file (str): Path to save the generated index.html.
|
||||
"""
|
||||
# Render the template with the combined sections
|
||||
template = self.env.get_template("combined_template.html")
|
||||
rendered_html = template.render(
|
||||
title=os.path.basename(directory_path).capitalize(),
|
||||
sections=sections
|
||||
)
|
||||
|
||||
# Write the rendered HTML to index.html
|
||||
os.makedirs(directory_path, exist_ok=True)
|
||||
with open(output_file, "w", encoding="utf-8") as output:
|
||||
output.write(rendered_html)
|
||||
print(f"Generated: {output_file}")
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
Run the Markdown processing workflow: one 'index.html' per category.
|
||||
"""
|
||||
for root, dirs, _ in os.walk(self.input_dir):
|
||||
for directory in dirs:
|
||||
category_path = os.path.join(root, directory)
|
||||
output_file = os.path.join(category_path, "index.html")
|
||||
|
||||
# Process all Markdown files in the current category directory
|
||||
sections = self._process_markdown_files_in_directory(category_path)
|
||||
if sections:
|
||||
self._generate_index_html(category_path, sections, output_file)
|
||||
70
app/services/markdown_render.py
Normal file
70
app/services/markdown_render.py
Normal file
@@ -0,0 +1,70 @@
|
||||
import markdown
|
||||
from jinja2 import Environment, DictLoader
|
||||
|
||||
# Define Jinja2 custom functions
|
||||
def img_left_overlay(src):
|
||||
"""Render an image with overlay."""
|
||||
return f'''
|
||||
<div class="img-left-overlay">
|
||||
<img src="{src}" alt="Overlay Image">
|
||||
<div class="overlay-text">Overlay Text</div>
|
||||
</div>
|
||||
'''
|
||||
|
||||
def box(title, content):
|
||||
"""Render a box component."""
|
||||
return f'''
|
||||
<div class="box">
|
||||
<strong>{title}</strong>
|
||||
<p>{content}</p>
|
||||
</div>
|
||||
'''
|
||||
|
||||
def note(content):
|
||||
"""Render a note component."""
|
||||
return f'''
|
||||
<div class="note">
|
||||
<p>{content}</p>
|
||||
</div>
|
||||
'''
|
||||
|
||||
def warning(content):
|
||||
"""Render a warning component."""
|
||||
return f'''
|
||||
<div class="warning">
|
||||
⚠️ <p>{content}</p>
|
||||
</div>
|
||||
'''
|
||||
|
||||
def create_jinja_environment():
|
||||
"""Set up Jinja2 environment and register custom components."""
|
||||
env = Environment(loader=DictLoader({"base_template": "{{ content | safe }}"}))
|
||||
env.globals.update({
|
||||
"img_left_overlay": img_left_overlay,
|
||||
"box": box,
|
||||
"note": note,
|
||||
"warning": warning,
|
||||
})
|
||||
return env
|
||||
|
||||
def render_markdown_with_jinja(markdown_content: str):
|
||||
"""
|
||||
Convert Markdown to HTML and apply Jinja2 rendering for custom tags.
|
||||
|
||||
Args:
|
||||
markdown_content (str): Raw Markdown content.
|
||||
|
||||
Returns:
|
||||
tuple: Rendered HTML content and metadata as a dictionary.
|
||||
"""
|
||||
# Step 1: Convert Markdown to HTML and extract metadata
|
||||
md = markdown.Markdown(extensions=["extra", "nl2br", "meta"])
|
||||
html_content = md.convert(markdown_content)
|
||||
metadata = {key: " ".join(value) for key, value in md.Meta.items()} if md.Meta else {}
|
||||
|
||||
# Step 2: Render the HTML with Jinja2 to apply custom tags
|
||||
env = create_jinja_environment()
|
||||
template = env.get_template("base_template")
|
||||
final_html = template.render(content=html_content)
|
||||
|
||||
return final_html, metadata
|
||||
83
app/services/metadata_processor.py
Normal file
83
app/services/metadata_processor.py
Normal file
@@ -0,0 +1,83 @@
|
||||
import os
|
||||
import markdown
|
||||
import json
|
||||
from typing import List, Dict
|
||||
|
||||
|
||||
class MetadataProcessor:
|
||||
"""
|
||||
A class to scan Markdown files, extract front matter metadata,
|
||||
and generate a structured JSON file.
|
||||
"""
|
||||
|
||||
def __init__(self, input_dir: str, output_file: str):
|
||||
"""
|
||||
Initialize the MetadataProcessor.
|
||||
|
||||
Args:
|
||||
input_dir (str): Directory containing Markdown files.
|
||||
output_file (str): Path to save the generated JSON file.
|
||||
"""
|
||||
self.input_dir = input_dir
|
||||
self.output_file = output_file
|
||||
self.data = {"categories": [], "favorites": []}
|
||||
|
||||
def _extract_metadata(self, file_path: str) -> Dict:
|
||||
"""
|
||||
Extract front matter metadata using the 'markdown' package.
|
||||
|
||||
Args:
|
||||
file_path (str): Path to the Markdown file.
|
||||
|
||||
Returns:
|
||||
dict: A dictionary containing the extracted metadata.
|
||||
"""
|
||||
with open(file_path, "r", encoding="utf-8") as file:
|
||||
markdown_content = file.read()
|
||||
|
||||
# Initialize Markdown with meta extension
|
||||
md = markdown.Markdown(extensions=["extra", "nl2br", "meta"])
|
||||
md.convert(markdown_content)
|
||||
|
||||
# Metadata is stored in md.Meta as a dictionary of lists
|
||||
meta = {key: " ".join(value) for key, value in md.Meta.items()} if md.Meta else {}
|
||||
return meta
|
||||
|
||||
def _process_directory(self):
|
||||
"""
|
||||
Recursively scan the input directory for Markdown files
|
||||
and extract metadata to build the JSON structure.
|
||||
"""
|
||||
for root, _, files in os.walk(self.input_dir):
|
||||
for file in files:
|
||||
if file.endswith(".md"):
|
||||
file_path = os.path.join(root, file)
|
||||
metadata = self._extract_metadata(file_path)
|
||||
|
||||
if metadata:
|
||||
# Add to 'categories'
|
||||
self.data["categories"].append({
|
||||
"name": metadata.get("name", "Unknown"),
|
||||
"path": os.path.relpath(root, self.input_dir).replace(os.sep, "/"),
|
||||
"author": metadata.get("author", "Unknown")
|
||||
})
|
||||
|
||||
# Add to 'favorites' if 'favorite' is true
|
||||
if metadata.get("favorite") and metadata["favorite"].lower() == "true":
|
||||
self.data["favorites"].append({
|
||||
"name": metadata.get("name", "Unknown"),
|
||||
"image": metadata.get("image", "images/default.jpg"),
|
||||
"description": metadata.get("summary", "No description provided")
|
||||
})
|
||||
|
||||
def generate_json(self):
|
||||
"""
|
||||
Generate the JSON structure and save it to the output file.
|
||||
"""
|
||||
self._process_directory()
|
||||
|
||||
# Save JSON to file
|
||||
with open(self.output_file, "w", encoding="utf-8") as json_file:
|
||||
json.dump(self.data, json_file, indent=4, ensure_ascii=False)
|
||||
|
||||
print(f"Generated JSON saved to {self.output_file}")
|
||||
Reference in New Issue
Block a user