Skip to content

Commit 105ddd9

Browse files
Creacion de otro mdx (entrada al blog) y ajuste del h2 header
1 parent 7c0961c commit 105ddd9

4 files changed

Lines changed: 198 additions & 10 deletions

File tree

app/_posts/instalacion_de_latex.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ isPublic: true
1717

1818
Si ya tienes instalado una version de TexLive, entonces quizás debas (no necesariamente) actualizar.
1919
Esto no es muy necesario si solo utilizas latex para generar documentos con símbolos matemáticos. Actualizar realmente puede ser un dolor de cabeza si ya estas
20-
acostubmbrado a un sistema. En (este enlace)[https://tug.org/texlive/upgrade.html]....
20+
acostubmbrado a un sistema. En [este enlace](https://tug.org/texlive/upgrade.html)....
2121

2222
Para verificar la version que tienes puedes usar
2323

app/_posts/papers_organizacion.mdx

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
---
2+
slug: "papers_organizacion"
3+
id: "python-01"
4+
date: "2025-10-31"
5+
order: 1
6+
title: "Estandarización de Nombres de Papers con Python"
7+
shortTitle: "Estandarización de Nombres de Papers con Python"
8+
author: "Eric Lucero"
9+
coverImage: https://res.cloudinary.com/dcvnw6hvt/image/upload/v1741389125/elCronopio/cover_test-2_dnehhh.jpg
10+
excerpt: "Script para organizar papers académicos automáticamente con Python."
11+
doctype: ["blog","python"]
12+
isPublic: true
13+
---
14+
15+
# De Caos a Orden: Cómo Automaticé la Organización de mis Papers con Python
16+
17+
Como todo estudiante de doctorado, mi carpeta de "Papers por leer" era un caos. Nombres como `final_v2.pdf`, `1234.5678.pdf` o `articulo_importante.pdf` hacían imposible encontrar nada. Cansado de perder tiempo, decidí aplicar lo que mejor sé hacer: usar Python para construir una herramienta que pusiera orden de una vez por todas.
18+
19+
En este post, te guiaré a través del proceso de creación de un script que:
20+
1. Vigila una carpeta de entrada (`_Inbox`).
21+
2. Extrae metadatos (Título, Autor, Año) de los archivos PDF.
22+
3. Renombra los archivos a un formato estándar y consistente.
23+
4. Lo hace de forma robusta y multiplataforma.
24+
25+
¡Vamos a transformar ese desorden en una biblioteca organizada!
26+
27+
28+
29+
## El Flujo de Trabajo: De `_Inbox` a la Gloria
30+
31+
La idea es simple. Todos los papers nuevos que descargo van a una única carpeta llamada `_Inbox`. Luego, ejecuto mi script de Python, que los procesa y los mueve a mi biblioteca principal, ya renombrados con el formato: **`Año-Titulo-resumido-(Autor).pdf`**.
32+
33+
## Paso 1: Extrayendo la Magia de los PDFs con `PyPDF2`
34+
35+
El primer desafío es conseguir la información que está dentro del PDF. Para esto, usaremos la librería `PyPDF2`. Nuestro objetivo es crear una función que, dado un archivo, nos devuelva el título, el autor y el año.
36+
37+
La clave es acceder a los **metadatos** del archivo, que es información guardada junto con el contenido principal.
38+
39+
```python
40+
# En metadata_utils.py
41+
import PyPDF2
42+
import logging
43+
44+
def extract_pdf_metadata(pdf_file):
45+
"""Devuelve (año, título, autor) a partir de los metadatos de un PDF."""
46+
try:
47+
with open(pdf_file, "rb") as f:
48+
reader = PyPDF2.PdfReader(f)
49+
meta = reader.metadata
50+
if meta:
51+
# Lógica para extraer y limpiar el año del campo 'CreationDate'
52+
year = meta.get("/CreationDate", "")[2:6]
53+
54+
# Lógica para obtener y limpiar el título
55+
title = meta.get("/Title", "NoTitle").strip()
56+
57+
# Lógica para obtener el apellido del primer autor
58+
author_full = meta.get("/Author", "Unknown").strip()
59+
author = author_full.split(",")[0].split()[-1]
60+
61+
return year, title, author
62+
except Exception as e:
63+
logging.warning(f"No se pudieron leer los metadatos de {pdf_file.name}: {e}")
64+
65+
return None, None, None
66+
```
67+
**Fundamento:** Usar un bloque `try-except` es crucial. Muchos PDFs de internet están corruptos o no tienen metadatos estándar. Nuestro script debe ser lo suficientemente robusto como para no fallar si se encuentra con uno de estos archivos.
68+
69+
## Paso 2: El Corazón del Sistema con `pathlib`
70+
71+
Para manejar las rutas de archivos, **olvídate de `os.path`**. La forma moderna, limpia y multiplataforma de hacerlo es con `pathlib`. Esta librería convierte las rutas de texto en objetos inteligentes.
72+
73+
La ventaja más visible es la unión de rutas. Es tan intuitivo como esto:
74+
```python
75+
from pathlib import Path
76+
77+
# La forma antigua y engorrosa
78+
# ruta = os.path.join(base, carpeta, archivo)
79+
80+
# La forma moderna con pathlib
81+
ruta = Path(base) / carpeta / archivo
82+
```
83+
Esta sintaxis no solo es más legible, sino que `pathlib` se encarga automáticamente de usar `/` o `\` según estemos en macOS/Linux o Windows.
84+
85+
Nuestra clase principal, `PaperOrganizer`, usará `pathlib` para todas las operaciones de archivos, como buscar PDFs en el `_Inbox`, renombrarlos y moverlos.
86+
87+
```python
88+
# En manage_names.py
89+
from pathlib import Path
90+
import sys
91+
92+
class PaperOrganizer:
93+
def __init__(self, base_path: str):
94+
self.base_path = Path(base_path)
95+
self.inbox_path = self.base_path / "_Inbox"
96+
97+
def rename_to_standard_format(self, filepath, year, title, author):
98+
path = Path(filepath)
99+
# Normalizamos el título para que sea un nombre de archivo válido
100+
title_normalized = title.replace(" ", "-")[:80] # Truncamos a 80 caracteres
101+
102+
new_name = f"{year}-{title_normalized}-({author}).pdf"
103+
new_path = path.parent / new_name
104+
105+
# El método .rename() también sirve para mover archivos
106+
path.rename(new_path)
107+
print(f"✅ Renombrado: {path.name} -> {new_name}")
108+
109+
def add_macos_tag(self, pdf_file):
110+
"""Añade una etiqueta 'Pending' solo si estamos en macOS."""
111+
if sys.platform == "darwin": # 'darwin' es macOS
112+
try:
113+
subprocess.run(["tag", "-a", "Pending", str(pdf_file)], check=True)
114+
except FileNotFoundError:
115+
print("⚠️ Comando 'tag' no encontrado. Instálalo con 'brew install tag'")
116+
117+
```
118+
**Beneficios:** Al centralizar la lógica en una clase y usar `pathlib`, creamos un sistema cohesivo donde cada parte tiene una responsabilidad clara. El método `.rename()` de `pathlib` es tan versátil que sirve tanto para renombrar en el sitio como para mover el archivo a otra carpeta, simplificando nuestro código.
119+
120+
## Paso 3: Creando una Herramienta de Terminal con `argparse`
121+
122+
Para que nuestro script sea una verdadera herramienta, le añadimos una interfaz de línea de comandos (CLI) con `argparse`. Esto nos permite ejecutar diferentes acciones (`normalize`, `hyphenize`, etc.) de forma sencilla.
123+
124+
Además, usamos `python-dotenv` para gestionar la configuración (como la ruta principal a nuestros papers) de forma segura en un archivo `.env`, en lugar de escribirla directamente en el código.
125+
126+
```python
127+
# En main.py
128+
import os
129+
from pathlib import Path
130+
import argparse
131+
from dotenv import load_dotenv
132+
133+
def main():
134+
load_dotenv() # Carga las variables de .env
135+
parser = argparse.ArgumentParser(description="Utilidad para organizar papers.")
136+
parser.add_argument("command", choices=["normalize", "hyphenize"], help="Función a ejecutar")
137+
args = parser.parse_args()
138+
139+
# Leemos la ruta desde el entorno y la convertimos a un objeto Path
140+
PHD_PATH = os.getenv("my_path")
141+
if not PHD_PATH:
142+
raise ValueError("La variable 'my_path' no está definida en .env")
143+
144+
base_dir = Path(PHD_PATH)
145+
organizer = PaperOrganizer(str(base_dir))
146+
147+
if args.command == "normalize":
148+
organizer.batch_normalize_filenames(str(base_dir / "_Inbox"))
149+
# ... otras condiciones ...
150+
151+
if __name__ == "__main__":
152+
main()
153+
```
154+
**Fundamento:** Separar la configuración (`.env`) del código (`main.py`) es una práctica profesional estándar. Permite que otras personas (o tú mismo en otro ordenador) usen tu script simplemente creando su propio archivo `.env` sin tener que modificar la lógica del programa.
155+
156+
## Conclusion: Más allá de un simple script
157+
158+
Lo que empezó como una solución a una molestia personal se convirtió en una herramienta robusta y un excelente ejercicio de "Pythonic code". Este proyecto no solo me ahorra horas de trabajo manual, sino que también es una pieza de portafolio que demuestra mi dominio de:
159+
160+
- **Programación Orientada a Objetos** en Python.
161+
- **Manipulación de archivos moderna y multiplataforma** con `pathlib`.
162+
- **Procesamiento de datos** con `PyPDF2` para extraer información de PDFs.
163+
- **Creación de herramientas de terminal** robustas con `argparse`.
164+
- **Buenas prácticas de desarrollo** como el manejo de errores, logging y gestión de la configuración.
165+
166+
Espero que este desglose te sea útil y te inspire a automatizar tus propios flujos de trabajo. ¡El orden nunca había sido tan satisfactorio!
167+
168+
Puedes encontrar el código completo de este proyecto en mi [GitHub](https://github.com/EricLuceroGonzalez/Reading_planner).

app/_posts/perceptron.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
slug: "test2"
3-
id: "mdx-02"
3+
id: "ai-001"
44
date: "2024-07-01"
55
order: 7
66
author: "Eric Lucero"

app/components/MdCompos/H2Header.js

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,36 @@
11
import { MdSubHeadA } from "@/app/ui/MarkDownComponents";
22
import { FaLink } from "react-icons/fa";
3-
4-
const getAnchor = (anchorText) => {
5-
return anchorText
6-
.normalize("NFD") // separa los acentos de las letras
7-
.replace(/[\u0300-\u036f]/g, "") // elimina los acentos
3+
const extractText = (node) => {
4+
if (typeof node === "string") return node;
5+
if (typeof node === "number") return String(node);
6+
if (Array.isArray(node)) return node.map(extractText).join("");
7+
if (typeof node === "object" && node?.props?.children) {
8+
return extractText(node.props.children);
9+
}
10+
return "";
11+
};
12+
// const getAnchor = (anchorText) => {
13+
// return (
14+
// anchorText
15+
// // .normalize("NFD") // separa los acentos de las letras
16+
// .replace(/[\u0300-\u036f]/g, "") // elimina los acentos
17+
// .toLowerCase()
18+
// .replace(/[^a-z0-9]/g, "")
19+
// .replace(/[]/g, "-")
20+
// );
21+
// };
22+
const getAnchor = (text) => {
23+
return text
24+
.normalize("NFD")
25+
.replace(/[\u0300-\u036f]/g, "")
826
.toLowerCase()
9-
.replace(/[^a-z0-9]/g, "")
10-
.replace(/[]/g, "-");
27+
.replace(/[^a-z0-9]/g, "-") // Recomiendo usar guiones en lugar de nada para separar palabras
28+
.replace(/^-+|-+$/g, ""); // Elimina guiones al inicio o final
1129
};
1230
const H2Header = ({ children }) => {
13-
const anchor = getAnchor(children[1]);
31+
const anchorText = extractText(children);
32+
const anchor = getAnchor(anchorText);
33+
// const anchor = getAnchor(children[1]);
1434
const link = `#${anchor}`;
1535
console.log(children[1]);
1636
console.log(link);

0 commit comments

Comments
 (0)