Files
fn_registry/python/functions/obsidian/slugify_obsidian_name.py
T
egutierrez eb8dbf66a1 feat(infra): auto-commit con 88 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-11 00:16:46 +02:00

71 lines
2.4 KiB
Python

"""Convierte un nombre/titulo de Obsidian a un slug kebab-case estable.
Funcion pura: transliteracion Unicode + normalizacion a [a-z0-9-]. No hace I/O.
"""
import re
import unicodedata
# Cualquier secuencia de caracteres que NO sea [a-z0-9] colapsa a un solo '-'.
_NON_SLUG_RE = re.compile(r"[^a-z0-9]+")
# Mapeo explicito de caracteres que NFKD no descompone como queremos.
_TRANSLIT_MAP = {
"ñ": "n",
"Ñ": "n",
}
def slugify_obsidian_name(name: str) -> str:
"""Convierte un nombre o titulo a un slug kebab-case estable.
Pasos, en orden:
1. Mapea caracteres especiales que NFKD no descompone (ñ/Ñ -> n).
2. Normaliza con ``unicodedata.normalize('NFKD', ...)`` y descarta los
combining marks (acentos, diacriticos), transliterando a ASCII.
3. Pasa todo a minusculas.
4. Reemplaza cualquier secuencia de caracteres no ``[a-z0-9]`` por un
unico ``-``.
5. Hace strip de ``-`` al inicio y al final. Nunca quedan guiones dobles.
Es deterministica y pura: misma entrada -> mismo slug, sin efectos
secundarios ni I/O.
Args:
name: Nombre o titulo arbitrario (puede llevar acentos, mayusculas,
espacios, simbolos).
Returns:
El slug kebab-case correspondiente. Cadena vacia si ``name`` no
contiene ningun caracter slugificable.
"""
if not name:
return ""
# 1. Mapeo explicito antes de NFKD (la cedilla de la enye se trataria mal).
for src, dst in _TRANSLIT_MAP.items():
name = name.replace(src, dst)
# 2. Transliteracion Unicode: descompone y descarta combining marks.
decomposed = unicodedata.normalize("NFKD", name)
ascii_only = "".join(ch for ch in decomposed if not unicodedata.combining(ch))
# 3. Minusculas.
lowered = ascii_only.lower()
# 4. Colapsar todo lo no [a-z0-9] a un unico '-'.
slug = _NON_SLUG_RE.sub("-", lowered)
# 5. Strip de guiones en los bordes.
return slug.strip("-")
if __name__ == "__main__":
assert slugify_obsidian_name("Enmanuel Gutiérrez Pérez") == "enmanuel-gutierrez-perez"
assert slugify_obsidian_name("Jose manuel camaño castro") == "jose-manuel-camano-castro"
assert slugify_obsidian_name("DNI de María del Mar") == "dni-de-maria-del-mar"
assert slugify_obsidian_name(" raro__nombre!! ") == "raro-nombre"
assert slugify_obsidian_name("") == ""
assert slugify_obsidian_name("!!!") == ""
print("slugify_obsidian_name smoke OK")