feat: Implement initial Homelab Automation API v2 with new models, routes, and core architecture, including a SQLAlchemy model refactoring script.
Tests / Backend Tests (Python) (3.10) (push) Has been cancelled
Tests / Backend Tests (Python) (3.11) (push) Has been cancelled
Tests / Backend Tests (Python) (3.12) (push) Has been cancelled
Tests / Frontend Tests (JS) (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / All Tests Passed (push) Has been cancelled
Tests / Backend Tests (Python) (3.10) (push) Has been cancelled
Tests / Backend Tests (Python) (3.11) (push) Has been cancelled
Tests / Backend Tests (Python) (3.12) (push) Has been cancelled
Tests / Frontend Tests (JS) (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / All Tests Passed (push) Has been cancelled
This commit is contained in:
@@ -191,7 +191,7 @@ class HelpMarkdownRenderer:
|
||||
|
||||
def _process_accordions(self, html: str) -> str:
|
||||
"""Traite les accordéons."""
|
||||
pattern = r':::accordion\s+(fa-[\w-]+)\s+(\w+)\s+([^\n]+)\n(.*?):::'
|
||||
pattern = r':::accordion\s+((?:fa[bs]?\s+)?fa-[\w-]+)\s+(\w+)\s+([^\n]+)\n(.*?):::'
|
||||
|
||||
def replace_accordion(match):
|
||||
icon = match.group(1)
|
||||
@@ -385,18 +385,25 @@ class HelpMarkdownRenderer:
|
||||
return '\n'.join(result)
|
||||
|
||||
def _process_headings(self, html: str) -> str:
|
||||
"""Traite les titres H3 et H4."""
|
||||
"""Traite les titres H1, H3 et H4."""
|
||||
# H1
|
||||
html = re.sub(
|
||||
r'^#\s+([^\n{]+)(?:\s*\{#[^}]+\})?\s*$',
|
||||
r'<h1 class="text-3xl font-bold mb-6 text-white">\1</h1>',
|
||||
html,
|
||||
flags=re.MULTILINE
|
||||
)
|
||||
# H3
|
||||
html = re.sub(
|
||||
r'^### ([^\n{]+)$',
|
||||
r'^###\s+([^\n{]+)(?:\s*\{#[^}]+\})?\s*$',
|
||||
r'<h3 class="font-semibold mb-4 text-purple-400">\1</h3>',
|
||||
html,
|
||||
flags=re.MULTILINE
|
||||
)
|
||||
# H4
|
||||
html = re.sub(
|
||||
r'^#### ([^\n]+)$',
|
||||
r'<h4 class="font-semibold mb-2 flex items-center gap-2"><i class="fas fa-question-circle text-gray-400"></i>\1</h4>',
|
||||
r'^####\s+([^\n{]+)(?:\s*\{#[^}]+\})?\s*$',
|
||||
r'<h4 class="font-semibold mb-2 flex items-center gap-2">\1</h4>',
|
||||
html,
|
||||
flags=re.MULTILINE
|
||||
)
|
||||
|
||||
+45
-19
@@ -110,16 +110,41 @@ def emoji_to_png_bytes(emoji: str, size: int = 32) -> Optional[bytes]:
|
||||
return None
|
||||
|
||||
|
||||
import base64
|
||||
|
||||
def replace_emojis_with_images(text: str) -> str:
|
||||
"""Remplace les emojis par des balises <img/> pour ReportLab."""
|
||||
emoji_pattern = re.compile(
|
||||
"["
|
||||
"\U0001F600-\U0001F64F" # emoticons
|
||||
"\U0001F300-\U0001F5FF" # symbols & pictographs
|
||||
"\U0001F680-\U0001F6FF" # transport & map symbols
|
||||
"\U0001F1E0-\U0001F1FF" # flags
|
||||
"\U00002702-\U000027B0" # dingbats
|
||||
"\U000024C2-\U0001F251" # enclosed characters
|
||||
"\U0001F900-\U0001F9FF" # supplemental symbols
|
||||
"\U0001FA00-\U0001FA6F" # chess symbols
|
||||
"\U0001FA70-\U0001FAFF" # symbols extended
|
||||
"\U00002600-\U000026FF" # misc symbols
|
||||
"]"
|
||||
)
|
||||
|
||||
def repl(match):
|
||||
emoji = match.group()
|
||||
png_bytes = emoji_to_png_bytes(emoji, size=24)
|
||||
if png_bytes:
|
||||
b64_img = base64.b64encode(png_bytes).decode('ascii')
|
||||
# ReportLab image tag must be valid and size specified
|
||||
return f'<img src="data:image/png;base64,{b64_img}" width="12" height="12"/>'
|
||||
return emoji
|
||||
|
||||
return emoji_pattern.sub(repl, text)
|
||||
|
||||
def markdown_to_pdf_bytes(markdown_content: str, title: str = "Document") -> bytes:
|
||||
"""Convertit du contenu Markdown en PDF.
|
||||
"""Convertit du contenu Markdown en PDF."""
|
||||
# Nettoyer les caractères de dessin de boîte pour Courier
|
||||
markdown_content = markdown_content.replace('├──', '|--').replace('└──', '`--').replace('│', '|')
|
||||
|
||||
Args:
|
||||
markdown_content: Contenu au format Markdown
|
||||
title: Titre du document
|
||||
|
||||
Returns:
|
||||
Bytes du PDF généré
|
||||
"""
|
||||
buffer = io.BytesIO()
|
||||
doc = SimpleDocTemplate(
|
||||
buffer,
|
||||
@@ -130,7 +155,6 @@ def markdown_to_pdf_bytes(markdown_content: str, title: str = "Document") -> byt
|
||||
bottomMargin=2*cm
|
||||
)
|
||||
|
||||
# Styles
|
||||
styles = getSampleStyleSheet()
|
||||
|
||||
title_style = ParagraphStyle(
|
||||
@@ -282,19 +306,19 @@ def markdown_to_pdf_bytes(markdown_content: str, title: str = "Document") -> byt
|
||||
|
||||
# Titres
|
||||
if line.startswith('# '):
|
||||
emojis, text = extract_leading_emojis(line[2:].strip())
|
||||
display_text = f"{emojis} {text}".strip() if emojis else text
|
||||
elements.append(Paragraph(display_text, h1_style))
|
||||
text = line[2:].strip()
|
||||
text = replace_emojis_with_images(text)
|
||||
elements.append(Paragraph(text, h1_style))
|
||||
elif line.startswith('## '):
|
||||
emojis, text = extract_leading_emojis(line[3:].strip())
|
||||
display_text = f"{emojis} {text}".strip() if emojis else text
|
||||
elements.append(Paragraph(display_text, h2_style))
|
||||
text = line[3:].strip()
|
||||
text = replace_emojis_with_images(text)
|
||||
elements.append(Paragraph(text, h2_style))
|
||||
elif line.startswith('### '):
|
||||
emojis, text = extract_leading_emojis(line[4:].strip())
|
||||
display_text = f"{emojis} {text}".strip() if emojis else text
|
||||
elements.append(Paragraph(display_text, h3_style))
|
||||
text = line[4:].strip()
|
||||
text = replace_emojis_with_images(text)
|
||||
elements.append(Paragraph(text, h3_style))
|
||||
elif line.startswith('#### '):
|
||||
text = line[5:].strip()
|
||||
text = replace_emojis_with_images(line[5:].strip())
|
||||
elements.append(Paragraph(text, h4_style))
|
||||
|
||||
# Listes à puces
|
||||
@@ -303,6 +327,7 @@ def markdown_to_pdf_bytes(markdown_content: str, title: str = "Document") -> byt
|
||||
# Convertir le markdown basique
|
||||
text = re.sub(r'\*\*(.+?)\*\*', r'<b>\1</b>', text)
|
||||
text = re.sub(r'`(.+?)`', r'<font face="Courier">\1</font>', text)
|
||||
text = replace_emojis_with_images(text)
|
||||
elements.append(Paragraph(f"• {text}", bullet_style))
|
||||
|
||||
# Listes numérotées
|
||||
@@ -333,6 +358,7 @@ def markdown_to_pdf_bytes(markdown_content: str, title: str = "Document") -> byt
|
||||
text = re.sub(r'\*\*(.+?)\*\*', r'<b>\1</b>', text)
|
||||
text = re.sub(r'\*(.+?)\*', r'<i>\1</i>', text)
|
||||
text = re.sub(r'`(.+?)`', r'<font face="Courier" color="#c7254e">\1</font>', text)
|
||||
text = replace_emojis_with_images(text)
|
||||
elements.append(Paragraph(text, body_style))
|
||||
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user