split cards
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import base64
|
||||
import html
|
||||
import os
|
||||
import re
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
from urllib.parse import quote
|
||||
@@ -72,6 +74,61 @@ def build_asset_url(relative_path: str) -> str:
|
||||
return f"/api/program/assets/{quote(encode_asset_path(relative_path))}"
|
||||
|
||||
|
||||
def build_section_url(relative_path: str, section_index: int) -> str:
|
||||
token = quote(encode_asset_path(relative_path))
|
||||
return f"/api/program/assets/{token}/sections/{section_index}"
|
||||
|
||||
|
||||
def extract_svg_sections(svg_path: Path) -> list[dict]:
|
||||
text = svg_path.read_text(encoding="utf-8", errors="ignore")
|
||||
sections: list[dict] = []
|
||||
pattern = re.compile(
|
||||
r'<g\s+transform="translate\((?P<x>[-\d.]+)\s+(?P<y>[-\d.]+)\)">\s*'
|
||||
r'<rect\s+x="0"\s+y="0"\s+width="(?P<w>[-\d.]+)"\s+height="(?P<h>[-\d.]+)"[^>]*>'
|
||||
r'(?P<body>.*?)</g>',
|
||||
re.DOTALL,
|
||||
)
|
||||
for match in pattern.finditer(text):
|
||||
width = float(match.group("w"))
|
||||
height = float(match.group("h"))
|
||||
if width < 250 or height < 120:
|
||||
continue
|
||||
title_match = re.search(r'<text[^>]*>(?P<title>.*?)</text>', match.group("body"), re.DOTALL)
|
||||
raw_title = re.sub(r"<[^>]+>", "", title_match.group("title") if title_match else "")
|
||||
title = html.unescape(raw_title).strip() or f"Étape {len(sections) + 1}"
|
||||
margin = 28
|
||||
sections.append(
|
||||
{
|
||||
"index": len(sections),
|
||||
"title": title,
|
||||
"view_box": [
|
||||
max(float(match.group("x")) - margin, 0),
|
||||
max(float(match.group("y")) - margin, 0),
|
||||
width + margin * 2,
|
||||
height + margin * 2,
|
||||
],
|
||||
}
|
||||
)
|
||||
return sections
|
||||
|
||||
|
||||
def render_svg_section(path: Path, section_index: int) -> str:
|
||||
sections = extract_svg_sections(path)
|
||||
if section_index < 0 or section_index >= len(sections):
|
||||
raise IndexError("Section introuvable")
|
||||
x, y, width, height = sections[section_index]["view_box"]
|
||||
text = path.read_text(encoding="utf-8", errors="ignore")
|
||||
text = re.sub(r'\swidth="[^"]+"', f' width="{int(width)}"', text, count=1)
|
||||
text = re.sub(r'\sheight="[^"]+"', f' height="{int(height)}"', text, count=1)
|
||||
text = re.sub(
|
||||
r'\sviewBox="[^"]+"',
|
||||
f' viewBox="{x:g} {y:g} {width:g} {height:g}"',
|
||||
text,
|
||||
count=1,
|
||||
)
|
||||
return text
|
||||
|
||||
|
||||
@lru_cache(maxsize=1)
|
||||
def list_lessons() -> list[dict]:
|
||||
lessons: list[dict] = []
|
||||
@@ -106,15 +163,25 @@ def list_lessons() -> list[dict]:
|
||||
)
|
||||
title = first_heading or title
|
||||
|
||||
assets = [
|
||||
{
|
||||
"name": svg_path.stem,
|
||||
"title": title_from_slug(svg_path.stem),
|
||||
"path": svg_path.relative_to(CONTENT_ROOT).as_posix(),
|
||||
"url": build_asset_url(svg_path.relative_to(CONTENT_ROOT).as_posix()),
|
||||
}
|
||||
for svg_path in svg_files
|
||||
]
|
||||
assets = []
|
||||
for svg_path in svg_files:
|
||||
relative_path = svg_path.relative_to(CONTENT_ROOT).as_posix()
|
||||
sections = extract_svg_sections(svg_path)
|
||||
assets.append(
|
||||
{
|
||||
"name": svg_path.stem,
|
||||
"title": title_from_slug(svg_path.stem),
|
||||
"path": relative_path,
|
||||
"url": build_asset_url(relative_path),
|
||||
"sections": [
|
||||
{
|
||||
**section,
|
||||
"url": build_section_url(relative_path, section["index"]),
|
||||
}
|
||||
for section in sections
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
lessons.append(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user