Reworked icons generation
This commit is contained in:
150
src/myfasthtml/icons/manage_icons.py
Normal file
150
src/myfasthtml/icons/manage_icons.py
Normal file
@@ -0,0 +1,150 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import typer
|
||||
|
||||
ROOT_FOLDER = "/home/kodjo/Dev/MyDocManager/src/frontend/node_modules/@sicons"
|
||||
MAX_SIZE = 2000000
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def pascal_to_snake(name: str) -> str:
|
||||
"""Convert a PascalCase or CamelCase string to snake_case."""
|
||||
# Insert underscore before capital letters (except the first one)
|
||||
s1 = re.sub(r'(.)([A-Z][a-z]+)', r'\1_\2', name)
|
||||
# Handle consecutive capital letters (like 'HTTPServer' -> 'http_server')
|
||||
s2 = re.sub(r'([a-z0-9])([A-Z])', r'\1_\2', s1)
|
||||
return s2.lower()
|
||||
|
||||
|
||||
app = typer.Typer()
|
||||
|
||||
|
||||
def list_sources(source_folder: str):
|
||||
return os.listdir(source_folder)
|
||||
|
||||
|
||||
def list_icons_from_source(source_folder: str, source: str):
|
||||
res = []
|
||||
for f in os.listdir(f"{source_folder}/{source}"):
|
||||
if f.endswith(".svg"):
|
||||
res.append(f)
|
||||
return res
|
||||
|
||||
|
||||
def read_content(source_folder: str, source: str, file_name: str):
|
||||
with open(f"{source_folder}/{source}/{file_name}", "r") as f:
|
||||
return f.read().strip()
|
||||
|
||||
|
||||
def get_dir_size(path: str | Path) -> int:
|
||||
p = Path(path)
|
||||
if p.is_file():
|
||||
return p.stat().st_size
|
||||
elif p.is_dir():
|
||||
return sum(f.stat().st_size for f in p.rglob('*') if f.is_file())
|
||||
else:
|
||||
raise FileNotFoundError(f"Path not found: {path}")
|
||||
|
||||
|
||||
def sizeof_fmt(num, suffix="B"):
|
||||
for unit in ["", "K", "M", "G", "T"]:
|
||||
if abs(num) < 1024.0:
|
||||
return f"{num:3.1f}{unit}{suffix}"
|
||||
num /= 1024.0
|
||||
return f"{num:.1f}P{suffix}"
|
||||
|
||||
|
||||
def init_buffer(source_folder: str, source: str):
|
||||
buffer = ""
|
||||
readme_file_path = f"{source_folder}/{source}/README.md"
|
||||
if os.path.exists(readme_file_path):
|
||||
with open(readme_file_path, "r") as f_readme:
|
||||
for line in f_readme:
|
||||
if line.startswith("#"):
|
||||
buffer += line
|
||||
else:
|
||||
buffer += f"# {line}"
|
||||
buffer += "\n\n"
|
||||
|
||||
buffer += "from fastcore.basics import NotStr\n\n"
|
||||
return buffer
|
||||
|
||||
|
||||
def flush(dry_run, suppress_suffix, source_folder: str, target_folder: str, buffer: str, size: int, part: int, source: str):
|
||||
suffix = '' if suppress_suffix else f"_test"
|
||||
outfile = f"{source}{suffix}.py" if part == 0 else f"{source}_p{part}{suffix}.py"
|
||||
if not dry_run:
|
||||
output_path = f"{target_folder}/{outfile}" if part == 0 else f"{target_folder}/{outfile}"
|
||||
with open(output_path, "w") as f:
|
||||
f.write(buffer)
|
||||
|
||||
typer.echo(f" Generated {source} as {outfile} ({sizeof_fmt(size)}, max={sizeof_fmt(MAX_SIZE)})")
|
||||
return init_buffer(source_folder, source), 0, part + 1
|
||||
|
||||
|
||||
@app.command("list")
|
||||
def list_icons(
|
||||
source: str = typer.Argument(None, help="The source file to list icons from"),
|
||||
source_folder: str = typer.Option(ROOT_FOLDER, help="The source folder containing icons"),
|
||||
count: bool = typer.Option(False, help="Counts the number of items"),
|
||||
size: bool = typer.Option(False, help="Gets the size of the items"),
|
||||
):
|
||||
res = []
|
||||
if source:
|
||||
res.extend(list_icons_from_source(source_folder, source))
|
||||
else:
|
||||
res.extend(list_sources(source_folder))
|
||||
|
||||
if count:
|
||||
typer.echo(len(res))
|
||||
return
|
||||
|
||||
if size:
|
||||
path = f"{source_folder}/{source}" if source else f"{source_folder}"
|
||||
size = get_dir_size(path)
|
||||
typer.echo(sizeof_fmt(size))
|
||||
return
|
||||
|
||||
for r in res:
|
||||
typer.echo(r)
|
||||
|
||||
|
||||
@app.command("generate")
|
||||
def generate_icons(
|
||||
source: str = typer.Argument(None, help="The source file to list icons from"),
|
||||
source_folder: str = typer.Option(ROOT_FOLDER, help="The source folder containing icons"),
|
||||
target_folder: str = typer.Option(".", help="The folder where to create the python files."),
|
||||
top: int = typer.Option(0, help="The number of top items to generate"),
|
||||
dry_run: bool = typer.Option(True, help="Does not generate the icons"),
|
||||
suppress_suffix: bool = typer.Option(False, help="Does not add the suffix to the icon names"),
|
||||
):
|
||||
sources = [source] if source else list_sources(source_folder)
|
||||
for current_source in sources:
|
||||
typer.echo(f"Generating icons for {current_source}")
|
||||
buffer = init_buffer(source_folder, current_source)
|
||||
size = 0
|
||||
part = 0
|
||||
for index, svg_file in enumerate(list_icons_from_source(source_folder, current_source)):
|
||||
|
||||
if 0 < top <= index:
|
||||
break
|
||||
|
||||
icon_name = os.path.splitext(os.path.basename(svg_file))[0]
|
||||
svg_content = read_content(source_folder, current_source, svg_file)
|
||||
svg_content = svg_content.replace("<svg ", f'<svg name="{current_source}-{icon_name}" ').replace("\n", "")
|
||||
|
||||
content = f"{pascal_to_snake(icon_name)} = NotStr('''{svg_content}''')"
|
||||
buffer += f"{content}\n"
|
||||
size += len(content)
|
||||
|
||||
if size > MAX_SIZE:
|
||||
buffer, size, part = flush(dry_run, suppress_suffix, source_folder, target_folder, buffer, size, part,
|
||||
current_source)
|
||||
|
||||
flush(dry_run, suppress_suffix, source_folder, target_folder, buffer, size, part, current_source)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app()
|
||||
Reference in New Issue
Block a user