# >_ Antonio Blanco - Full Content > Desarrollo software, construyo productos y comparto el proceso. --- ## Cómo montar tu segundo cerebro con IA 100% local URL: https://ablancodev.com/ia/como-montar-tu-segundo-cerebro-con-ia-local/ Vamos a montar un sistema donde vuelcas tus notas, PDFs y artículos, y luego le preguntas en lenguaje natural, bien desde una web, desde Claude, Codex o Cursor. Pero nosotros vamos a ir un paso más allá y vamos a usar IA local, y no sólo la IA, sino que toda la info quede en nuestro equipo, sin enviar nada a la nube: los embeddings y la base de datos viven en tu propia máquina. ## ¿Qué es un «segundo cerebro» y por qué local? Seguro que has escuchado eso del «segundo cerebro» y que la gente guarda ahí toda su vida, pero ¿qué es eso de un segundo cerebro o second brain? Un segundo cerebro (o *second brain*) es una base de conocimiento personal: en vez de buscar por nombre de fichero o por carpetas, guardas todo y luego **preguntas por significado**. «¿Qué anoté sobre la fermentación de la masa madre?» y el sistema te devuelve el fragmento exacto, aunque tú hubieras escrito «levado» en lugar de «fermentación». Eso se consigue con **búsqueda semántica**: cada texto se convierte en un vector (una lista de números que representa su significado) y se buscan los vectores más parecidos a tu pregunta. En nuestro caso, la palabra clave del montaje es **local**: los modelos de IA corren en tu ordenador (con Ollama), tus notas nunca salen de tu disco (privacidad total, sin cuotas de API, sin depender de internet) y además no sólo vamos a montar una web desde el que poder usarlo, sino que le daremos superpoderes MCP para que puedas conectarlo con tu claude, codex o moelo de IA local que permita «tools» y así poder buscar, editar o subir a nuestro cerebro. ## La arquitectura de un vistazo * - **Ollama + bge-m3** — convierte texto en vectores (embeddings). Corre nativo en tu Mac/PC (montarlo en Docker supone pérdida de rendimiento, así que los modelos de IA, siempre quedamos vamos a montarlos directamente en el equipo) - **PostgreSQL + pgvector** — nuestra base de datos vectorial. Guarda notas, trozos y vectores; busca por similitud. Corre en Docker. - **Servidor MCP (Python)** — conecta el asistente con las dos piezas anteriores. Lo lanza el cliente. - **Interfaz web** – Montada con FastAPI en Python (más que nada para que puedas ver algo chulo, porque te digo yo que el menda va a tirar de mcp en su día a día) ### El flujo, en dos direcciones **Cuando guardas algo** (una nota, un PDF, una URL): el texto se trocea en fragmentos (chunks), cada fragmento se manda a Ollama que devuelve su vector de 1024 dimensiones, y fragmento + vector se guardan en PostgreSQL/pgvector. **Cuando preguntas:** tu pregunta se convierte en un vector (mismo modelo bge-m3), pgvector busca los fragmentos más cercanos por similitud coseno (índice HNSW), y el asistente recibe esos fragmentos con su fuente original (título, ruta/URL, página) y te responde citándolos. ## Por qué cada tecnología - **Ollama** ejecuta modelos de IA en local, sin nube. Aquí lo usamos solo para embeddings, barato y rápido incluso sin GPU dedicada. - **bge-m3** es un modelo de embeddings multilingüe (va estupendo en español) que genera vectores de 1024 dimensiones y aguanta bien textos largos. - **PostgreSQL + pgvector** añade el tipo vector a Postgres y permite búsquedas por similitud coseno con índices HNSW. Bonus: es una base de datos «de verdad», con copias de seguridad y SQL de siempre. - **MCP (Model Context Protocol)** es el estándar que permite exponer herramientas a Claude, Codex o Cursor. Nuestro servidor publica tools como search_notes o add_note. - **Web**, por tener una interfaz chula con la que trabajar, montada con FastAPI en Python (cuando lo montemos, irá a http://127.0.0.1:8800) ## Manual de montaje ### Requisitos - Docker Desktop (para Postgres). - Ollama nativo + el modelo de embeddings: ollama pull bge-m3. - uv (gestor de Python moderno) para lanzar el MCP. ### 1. La base de datos con pgvector (Docker) services: db: image: pgvector/pgvector:pg16 container_name: secondbrain-db environment: POSTGRES_USER: secondbrain POSTGRES_PASSWORD: secondbrain POSTGRES_DB: secondbrain ports: - "5433:5432" volumes: - ./storage/postgres:/var/lib/postgresql/data - ./init.sql:/docker-entrypoint-initdb.d/init.sql:ro Mapeamos el puerto a **5433** para no chocar con un Postgres que ya tuvieras en el 5432. Los datos persisten en ./storage/postgres. ### 2. El esquema: cerebros, notas y chunks CREATE EXTENSION IF NOT EXISTS vector; CREATE TABLE chunks ( id BIGSERIAL PRIMARY KEY, note_id BIGINT NOT NULL REFERENCES notes(id) ON DELETE CASCADE, chunk_idx INT NOT NULL, content TEXT NOT NULL, embedding VECTOR(1024) -- bge-m3 = 1024 dimensiones ); CREATE INDEX chunks_embedding_hnsw ON chunks USING hnsw (embedding vector_cosine_ops); Dos ideas importantes: la dimensión del VECTOR(1024) debe coincidir con el modelo (bge-m3 son 1024), y guardamos también un índice full-text en español para combinar búsqueda semántica con búsqueda por palabra exacta. ### 3. El servidor MCP en Python El servidor MCP es una capa fina (FastMCP). Toda la lógica vive en un paquete core/ desacoplado, y las interfaces (MCP, API REST, web) solo la invocan. Así puedes extenderla mañana sin reescribir nada. Las tools que el asistente ve: search_notes, related_notes, add_note, add_file, add_url, get_note, list_notes, más las de gestión de cerebros (así podemos tener varios cerebros y que cada uno trabaje con el suyo, o poder tener varios, uno para el trabajo, otro para lo personal …). ### 4. Conectarlo con tu asistente El servidor MCP lo lanza el cliente por stdio, así que solo necesitas registrarlo. En Claude Code: claude mcp add secondbrain -- uv --directory "$(pwd)" run secondbrain-mcp En Claude Desktop, Codex o Cursor, el mismo bloque JSON: { "mcpServers": { "secondbrain": { "command": "uv", "args": ["--directory", "/ruta/a/secondbrain-local", "run", "secondbrain-mcp"] } } } Si un cliente muestra Failed to connect, casi siempre es que Postgres u Ollama están parados. ## Second brain vs RAG Un second brain es la idea (tu memoria externa personal). Un RAG es la maquinaria que la hace consultable por una IA. Este proyecto es un second brain implementado como RAG local: sin nube, sin coste de API y con tus datos sin salir de tu máquina. En resumen: **Second Brain –> El producto** **RAG –> La tecnología o arquitectura** ## Caso práctico: El cerebro «Antonio» Para probar que la búsqueda semántica separa bien los temas, sembré un cerebro con notas de tres dominios muy distintos: **IA/infra** (Ollama, bge-m3, pgvector, MCP…), **cocina** (paella, masa madre, pan) y **viajes** (Japón, Kioto). Además del chat del asistente, monté una interfaz web local (dark mode, como buen informático, con buscador semántico y vistas de lista y grafo (que esto de grafo vende mucho)) para ver* el cerebro por dentro: * Ahora, desde el buscador, basta con pedirlo en lenguaje natural: «¿Cómo genero embeddings en local sin usar la nube?». El sistema devuelve primero «Ollama en local» (65%), seguido del stack (49%), embeddings (46%) y pgvector (43%); las notas de cocina quedan muy por debajo. **Compara significado*, no palabras.** * Si nos vamos a la vista de **grafo de conocimiento** tipo Obsidian, pero sin enlaces manuales, los enlaces entre nodos no van metidos a mano en los propios documentos como hace Obsidian, las conexiones entre notas se calculan solas por similitud entre sus embeddings (y por etiquetas compartidas). Se ve a simple vista cómo la IA agrupa las notas por tema sin que yo pusiera ni un solo enlace a mano: el clúster morado (IA/infra), el rojo (cocina) y el verde (viajes) aparecen separados porque sus embeddings así lo dicen. ## Ventajas de hacerlo local - **Privacidad**: tus notas y tus PDFs no salen del disco. - **Coste cero de API**: los embeddings los calcula tu máquina. - **Sin internet**: funciona en un avión. - **Portable**: es Postgres estándar — copias de seguridad y consultas SQL de siempre. - **Multi-cliente**: el mismo cerebro sirve a Claude, Codex y Cursor a la vez. Montado con Ollama (bge-m3), PostgreSQL + pgvector y un servidor MCP en Python. IA 100% local: tus datos se quedan en casa*, de donde nunca deberían salir 🙃 ## El repositorio Seguro que quieres tener este sistema corriendo en tu máquina desde ya, así que te dejo el repositorio para que puedes descargarlo gratis y poder usarlo y evolucionarlo a tu antojo. Ir al repo de Github --- ## Guía para empezar con IA Local URL: https://ablancodev.com/ia/guia-para-empezar-con-ia-local/ ## Instala y usa Ollama y LM Studio en tu ordenador En este post vamos a ver cómo empezar con la IA local, desde un punto lo menos técnico posible, lo más genérico para el común de los mortales. ## ¿Qué es la IA local, la IA open source? Lo primero, aunque solemos llamar IA local e IA open source a lo mismo, no es lo mismo: - local: Está en tu propio equipo. - Open source: Es de «código abierto», es decir , puedes editar el modelo (en modelos LLM no sabemos el código como tal, lo que se sabe son los pesos de la red neuronal que lo compone, por eso esucharás «open weights) Pero bueno, todos entendemos que se trata de tener un modelo libre en nuestro equipo o incluso en nuestro servidor, y quitarnos o compaginar con los grandes como chatGPT, claude o Gemini. En este post vamos a ver los primero spasos para tener nuestro propio «chatGPT». ## Software para modelos IA locales Nos encontramos principalmente con 2 software como los más usados: Ollama y LMStudio ### LMStudio : El más sencillo de usar Empezamos por LMStudio: Es tan sencillo como descargarlo e instalarlo como cualquier aplicación de escritorio. Y como verás la aplicación de llama ahora Bionic … algo tendrán en mente esta gente, porque antes si se llamaba LMStudio Y una vez la ejecutemos veremos una aplicación muy parecida a lo que todos estamos acostumbrados con chatGPT: Verás que junto al botón de enviar te viene para elegir el modelo que quieres usar. Los que vienen por defecto son modelos bastantes potentes que puedes usar si te conectas a su nube (porque los de LMStudio y Ollama tienen su propio modelo Saas para que puedas correr los modelos en sus servidores), pero a nosotros nos interesa montarlos nosotros, así que nos iremos a: **Settings –> Explore** Ahí veremos muuuuuchos modelos, pero muuuchos. Elegiremos el que más nos convenga .. tranqui más abajo dejo cual elegiría yo, pero como aplica tanto a LMStudio como a Ollama, lo dejo tras ver Ollama para estar unificado. ### Ollama: El más usado El otro gran player del mercado es Ollama (el de la llama): Al igual que ocurría con LMStudio, Ollama es prácticamente instalar como cualquier app de escritorio y ya casi lo tenemos. Pero Antonio, ¡ si has dicho que LMStudio era el fácil ! Inicialmente Ollama no tenía ni aplicación de escritorio, era por consola o terminal, de ahí que si te da susto abrir el terminal de tu equipo, quédate con LMStudio, pero si no te asusta, porque realmente vamos a ejecutar 3 instrucciones, ollama puede ser tu sistema, y si luego vamos a dar el paso a desarrollo es el más usado. Vamos a verlo: Te suena, eh. Al final todos intentan emular a chatGPT como norma de facto. La diferencia viene en el manejo de los modelos, que en el caso de ollama tendremos que abrir un terminal, y con estas 3 instrucciones te serán suficiente: ollama list Muestra el listado de modelos cargados. ollama pull nombre_del_modelo Descarga el model «nombre_del_modelo» si no lo tenemos aún. ollama run nombre_del_modelo Carga un modelo para poder empezar a usarlo (en el caso de no tenerlo aún, también lo descarga) ollama rm nombre_del_modelo Elimina el modelo indicado. Como puedes ver son sencillas y sin mucho secreto, pero entiendo que hay mucha gente que ya abrir la terminal les supongo no estar cómodos, y para ellos, LMStudio es su solución. ## ¿Qué modelo de IA local elegir? Este respuesta seguro que evolucionará en breve, ya que no paran de salir modelos nuevos día a día. Así que la voy a reducir a 3 / 4 cosillas a tener en cuenta: - Por lo general hay familias de modelos: Gemma (de Google), Mistral (la que es Europea), Nemotron (de Nvidia), Qwen (de las más usadas), DeepSeek (muy conocidas como las primeras de origen chino que plantaron cara a las principales), Llama (del equipo de Meta (facebook)) …. - Revisad lo que ocupan, pensando que eso irá a memoria RAM o de GPU si es que podemos. Es decir que si vemos 4 gigas, serán a groso modo los que se ocupen en tu memoria, cuidado, no en tu disco duro que de ese tenemos muchos gigas, incluso Teras. - La capacidad o potencia a veces se suele medir en los millones / billones de parámetros del modelo, y eso nos lo dan los 4B, 8B, 16B … con uno de 4B nos suele valer para tareas diarias. Y por si esperas algún modelo, yo diría empezar probando con un gemma3:4B o un Qwen3.5:4B Una vez lo descargues lo tendrás disponible para usar con LMStudio, y ya tienes tu IA en local funcionando, sin depender de terceros, incluso cuando no tengas internet, y con la mayor privacidad y control al estar todo en tu equipo 🥳 🥳 🥳 Si quieres bichear cientos y cientos de modelos, hay un lugar donde viven todos estos duendecillos, y se llama Huggingface, que con su filtro lateral puedes indicar que te muestre por ejemplo para LMStudio u Ollama, el tamaño, el propósito… Y con esto queda completa la primera toma de contacto con IA local, que como has podido ver, es prácticamente instalar una app, una pequeña configuración (buscar y descargar modelo, la primera vez) y listo a chatear con nuestra propia IA. --- ## Creo mi buscador de imágenes semántico con IA local URL: https://ablancodev.com/ia/creo-mi-buscador-de-imagenes-semantico-con-ia-local/ ## … y ahora puedo buscar en mi galería tanto por imagen como por descripción. El número de imágenes crece y crece y cada vez te es más difícil encontrarlas, y más si buscas por una descripción que ni le has puesto a la imagen, ni a su galería, ni en sus etiquetas. Esto es lo que me ha llevado a pensar en crear un buscador semántico de imágenes. Y yo tengo unas cientos, pero imagina tu equipo de diseño de tu empresa, que se tiran horas buscando imágenes que ya tienen en su banco de fotos inmenso. El buscador en acción ## Vamos al lio 💪🏻 La idea principal es usar al igual que usamos en sistemas RAG de IA, en el que creamos embeddings de textos para «geolocalizarlos» dentro de una base de datos vectorial y así poder luego buscar y encontrar textos «cercanos», que en principio hablan de lo mismo o parecido. Pues sería llevarnos ese mismo proceso pero a imágenes. ## Diagrama de arquitectura Veamos las piezas: - El frontend, una web, donde poder adjuntar una imagen a buscar, o un texto, y por ahora en esta versión una opción para adjuntar imágenes a «indexar» - El backend, donde recibiremos las peticiones de búsqueda y embedding, un FastAPI en Python. - CLIP de OpenAI, en local, nos decargamos una versión de poco peso y sencilla que siempre podremos aumentar. - Una BBDD vectorial que será un PostgreSQL con el módulo de vectores. Para esta creación de embeddings usaremos un modelo especializado en imágenes, CLIP de OpenAI. Pero Antonio, si has dicho que es un modelo en local, ¿qué hacemos tirando de OpenAI? Pues porque OpenAI nos da el modelo para poder descargarlo, sin tener que tirar de su infra y consumir API, así que genialísimo, modelo para nosotros que montaremos en local. Nos ofrece distintas versiones de mayor o menor capacidad y tamaño, pero en cualquier caso es un modelo que cualquier ordenador medio normalito puede ejecutar, es decir, que no se trata de correr lo último de DeepSeek que necesita un tocho de ordenador. El repo de CLIP de OpenAI: https://github.com/openai/CLIP Vale ya tenemos todas las piezas, ahora veamos un par de flujos. ## Alimentemos a la bestia 🍔 🍕 Indexemos en base de datos nuestra galería. Para ello lo que haremos es pasarle CLIP la imagen y que nos genere un vector, que será el que insertaremos en BBDD. En esta versión inicial tienes en la web un formulario donde poder arrastrar varias imágenes y mandarlas a indexar. Vale es algo sencillo y a la vez tedioso, pero al ser un proyecto open source la ventaja que tiene es que siempre podemos iterarlo y modificarlo o ampliarlo, por ejemplo con la creación de un cron que revise nuestra galería y la indexe completa él solo, o que se conecte a nuestro servidor por ftp donde tenemos todas y que las vaya indexando. Así por ejemplo podríamos tener a todo el equipo volcando las imágenes que genere a un servidor común por FTP y podremos tener este buscador semántico para que todos puedan usarlo y buscar imágenes por contenido en el servidor de fotos. El flujo podría quedar algo tal que así: ## Usemos el buscador de imágenes En un formulario pasamos una imagen a buscar o un texto descriptivo, y la idea es que nos muestre un listado de imágenes por similitud (ordenadas por un valor de similitud, que a más alto, más iguales son) Este «prompt» (imagen o texto), lo que haremos es crear su embedding también, con el que tenemos un vector que o situa en el espacio, junto con elementos cercanos que son muy parecidos, es decir, que lo pasaremos a CLIP para generar el vector, que usaremos en un SELECT a base de datos, la que nos devolverá dicho listado de imágenes ordenadas por el índice de «cercanía». Le añadimos un simple filtro de similitud que podamos ajustar sobre los resultados y así de forma sencilla nos podemos quedar con las que sean más o menos parecidas. Suena sencillo, ¿no? prácticamente una mini web, que llama a una API que usa un modelo local CLIP para crear embeddings de imágenes y texto que guardamos en una base de datos PostgreSQL. Pues en montarlo tardas 5 minutos y te olvidas de dar vueltas buscando entre tus imágenes, el repo lo tienes aquí 👇🏻 👇🏻 👇🏻 https://github.com/ablancodev/image-search-local-AI Imagina tener esto en tu empresa, todas las imágenes centralizadas, pero con la capacidad de búsqueda semántica entre ellas, y encima con 0€ de coste en tokens ni API, pues ya sabes avísame, lo miramos y seguro que te puedo echar una mano. --- ## MCP, un protocolo más humano de lo que parece URL: https://ablancodev.com/essays/mcp-un-protocolo-mas-humano-de-lo-que-parece/ Cuando hablamos del protocolo MCP (Model Context Protocol), todos pensamos en IA, LLMs y agentes que hablan con herramientas mediante este «lenguaje». Pero y si te digo que tras analizarlo, le veo un punto más humano, más natural de lo que habías imaginado. En ingeniería del software cuando te pones a tomar requisitos, construir la arquitectura, analizar los flujos y funcionalidades que va a tener tu software, uno de los diagramas que se suele usar en UML, es el diagrama de Casos de Uso, si, ese del muñequito Que se suele usar para ir definiendo y descubriendo las funcionalidades que cada actor o role podrá hacer en el software. En un modelo clásico, esto nos valía para, sabiendo lo que el actor quiere conseguir, poder bajarlo a funcionalidades, funcionalidades que probablemente se viesen finalmente bajadas a funciones o incluso endpoints en APIs. Pasando de un : **Actor**: Administrador **Caso de uso**: Crear factura Desglosado: - El administrador selecciona un cliente. - Añade productos. - El sistema calcula impuestos. - Se genera la factura. - Se guarda. Que al final se traducirá en diseño de sistema: API / funciones / BBDD Es decir, hemos hecho el viaje: **Intención humana –> especificación –> diseño –> funciones técnicas** Un camino natural cuando bajamos a tierra lo que queremos construir, pero un camino que cada vez nos acerca más a lo técnico, lo cual nos aleja más de lo humano. Este camino nos llevaría a construir una API por ejemplo: POST /invoices GET /invoices/{id} Y hasta aquí no hay nada malo, es más, hemos construido software así durante décadas. Pero hemos pasado de un «quiero crear una factura», a métodos HTTP, endpoints, parámetros …. Si volvemos al principio del proceso, el usuario nunca pensó en endpoints, ni funciones, él pensó: ** Quiero crear una factura** Es decir, el actor piensa **qué quiere**, no **cómo conseguirlo**. Si miras el diagrama inicial de casos de uso, eso es precisamente lo que representa. En este sistema clásico de definición de funciones / API … software, la pregunta que se resuelve es: ** ¿Cómo accedo al sistema?** Pensamos en el modelo de datos, y como podemos construir puertas para conseguir lo que el usuario quiere, o como otros software obtiene dicha info. ## Ahora vayamos al modelo de MCP. En este caso, el consumidor va a ser a priori un Agente IA. Y cuando diseñamos para él, realmente los casos de uso van a ser los mismos: **Crear factura / Obtener factura / ver informe** Pero en este caso la pregunta que queremos resolver es: ** ¿qué puede hacer el actor (agente) con el sistema?** Si intentas contestarla, tu mente se queda en los distintos nodos del diagrama de casos de uso, es decir, tu mente se queda en **ese nivel de abstracción**. No le hace falta bajar al modelo de datos, para saber que una factura tiene un cliente asociado y por consiguiente tiene que haber un endpoint GET /clients … Evidentemente esto no significa que una tool de MCP tenga que corresponder exactamente con un caso de uso. Pero conceptualmente existe una similitud muy interesante: **Ambos nos permiten pensar en términos de capacidades que un actor puede realizar, en vez de pensar en detalles de implementación.** Durante años hemos ido transformando intenciones humanas en abstracciones cada vez más técnicas: **intención → caso de uso → diseño → servicio → función → endpoint** Ahora aparece un nuevo tipo de consumidor: un agente capaz de interpretar intenciones. Y resulta curioso que, para conectar estos agentes con nuestros sistemas, estemos recuperando una forma de describirlos que nos resulta muy familiar: **¿Qué puede hacer un actor con este sistema?** Y esto ya no va de «tecnología MCP», sino de modelo de abstracción MCP, más cercano al humano, más cercano al natural. Quizá por eso MCP me parece un protocolo **más humano de lo que parece**. Y si te estás preguntando cómo podría quedar este mismo sistema de facturación llevado a tierra, podríamos tener algo así: Mismo sistema. Distinta forma de exponerlo. La API nos acerca a la implementación y a los recursos del sistema. MCP nos permite exponer las capacidades que un agente puede utilizar. Y quizá ahí esté precisamente la parte «humana» de MCP. --- ## Conociendo a CURRO, nuestra IA familiar URL: https://ablancodev.com/build-in-public/conociendo-a-curro-nuestra-ia-familiar/ Hoy nace nuestro asistente de IA familiar, «CURRO». En casa cada día usamos más la IA, nada del otro mundo, pero se va integrando en nuestras vidas. Por lo general para las consultas típicas de cultura general, y yo si que lo tengo mucho más adaptado en mi vida, tanto laboral como para cualquier cosa que hago. Y llevo tiempo con ganas de montar un asistente IA que lo tengamos más a mano, que sea una especia de Siri o Google Home, pero vitaminado, al que poder añadir ya no sólo conocimiento con según el modelo de turno, sino que pueda hacer cositas, tanto digitales como gestionar el calendario, que me cree alertas, etc … como incluso algunas llevadas al mundo físico, me apetece que pueda controlar ciertas cosas de la casa, en definitiva que sea uno más de la familia. Pero como todo proyecto que se precie, vamos paso a paso, no lo matemos antes de empezar, así que vamos a por una primera iteración, que será tener el cerebro (la IA), y de momento un frontend con el que interactuar, que empezaremos por una simple web chat. Curro AI – Bienvenido Nuestro Curro irá al MacMini que tenemos medio abandonado en el salón, así cuando tenga voz y escucha lo tendremos más a mano para todos. ## Arquitectura Aunque irá creciendo para la base del proyecto, tiraremos de un frontal (donde de momento tendremos el chat), y un backend, en nuestro caso con Python para poder montar una API de forma sencilla. Y la IA que usaremos, que seguro es lo que más te interesa, tenía a priori 2 opciones: - **Una de las grandes: ChatGPT, claude o Gemini**. Todos sabemos que la potencia que ofrecen es espectacular, pero tenemos que pasar por caja por las llamadas API y consumo de tokens. - **Un modelo open source**. Estos nos dan independencia, no pasar por caja y tener nuestra info 100% bajo nuestro control. En esta primera iteración usaremos uno de los grandes, en concreto Gemini de Google (porque me he quedado sin presupuesto de los otros jejeje). Esto me permitirá de momento no tener que centrarme en montar modelo local, sobrecargar el Mac mini, buscar espacio en disco y demás, vamos a un modelo más Lean. Lo que si que vamos a plantear en ese sentido, es evitar que el frontal llame directamente a la API del LLM, ya que esto si que nos iría limitando en el futuro, de ahí que montemos nuestro backend, que hará de capa intermedia, tipo APIGateway. Esto nos permitirá que si queremos cambiar de proveedor sea transparente para el frontend, incluso si pasamos a modelo local, el frontend ni se inmute. ## Código Curro será open source, pero sin su conocimiento, que las cosas de casa se quedan en casa 🤩, pero podrás montarte tu propio Curro, esta primera versión la tienes en: https://github.com/ablancodev/curro-ai/releases/tag/v0.1.0 ## Próximos pasos Sé que pensarás que esto es un simple chat wrapper sobre IA … pues si, un pequeño paso en el desarrollo, pero un gran paso para la familia 🏡 El siguiente paso será ponerle oídos, porque queremos que Curro nos escuche (lo mismo se arrepiente) --- ## Generar manuales automáticamente con Playwright y Claude Code URL: https://ablancodev.com/build-in-public/generar-manuales-automaticamente-con-playwright-y-claude-code/ Hoy tras terminar una de las nuevas piezas del ecosistema selfhosted-saas de GrowthPlugins, GrowthBalance, tocaba lo de siempre, empezar a documentar para los usuarios, una parte de crear productos que no es precisamente la que más me guste, ni se me de bien …. me da mucha pereza ponerme a tomar capturas de pantallas, grabar video etc. ¿Y si hago la «la IA» me ayude a automatizar la creación del manual de usuario? ## Veamos las piezas que necesitamos - Que Claude (mi IA favorita ❤️), pueda **«ver» el navegador web**, bien por su propia extensión, o de otra forma que veamos. - **Capturas de pantallas**. Aquí fue directo, PLaywright, ya lo había usado en oros proyectos y sabía que tiene la capacidad de levantar un navegador y tomar capturas de pantallas completas o de un elemento en concreto. - **Video**. Aunque eso sería segunda fase, sabía que Playwright tiene un módulo de video que se le activa y es capaz de grabar la sesión, así que solucionado de momento. Puede ver la web, tomar capturas e incluso video, ya sólo falta que sea capaz de «interpretarla», es decir que si le digo que haga login, entienda lo que tiene que hacer, y este si veía que podía ser el agujero negro que todo proyecto tiene y que puede ser donde el proyecto acabe, así que es lo primero a atacar … y para mi sorpresa, fue mucho más directo de lo esperado, ya que como con Playwright obtiene el dom, es capaz de interpretar la web y saber cómo actuar en la mayoría de casos, así que agujero negro cerrado. ## ¿Qué forma le damos a la solución? La idea es poder usarlo en cualquier proyecto, incluso en algunos que no sean mios, que la gente de soporte pueda usarlo, cuando un usuario te abre un ticket y te dice que no sabe por ejemplo obtener la API KEY, le das la instrucción a este bicho, y que genere el video, se lo pasa, e incluso podemos añadirlo a nuestra base de conocimiento …. pedazo de caso de uso que me acabo de sacar de la manga !! El primer pensamiento impuro fue un docker para Playwrigth y luego un mega prompt super curado de los que te prometen los gurús de Youtube … 😖 Segundo pensamiento, el prompt a una skill … algo mejor, pero 😖 Vamos a por el tercer pensamiento: ¿me permitirá una skill montar todo el tinglado sin tener que marear al personal con dockers y demás? … pues bingo, si, puedo crear un script que me deje una skill (que pueden ser más que un simple fichero .md con instrucciones) totalmente preparada 😆 Guay, porque una skill la puedo poner a nivel de usuario y ya la tengo para cualquier proyecto o sesión con Claude. ¿Un ejemplito? que la chapa que estoy dando es monumental. ### Caso real de ejemplo El prompt: /auto-manual http://localhost/intranet.ablancodev.com/freelance-balance/ haz login con usuario blanco y password 123, muestra el listado de proyectos, luego edita uno para que se vea su ficha y como se añade tiempo y como se añade por ejemplo un gasto. Hazme el maual en inglés y en el video que se vean bien los clicks que vayas haciendo manual Manual de uso básico de GrowthBalance ## Algunas iteraciones Que esto no es llegar y besar el santo, algunas cosillas dieron guerra y son cosillas a saber y a tener en cuenta para proyectos futuros o similares. **El video es eterno** El video generado era largo y aburrido, pero aburrido, ya que Playwrigth graba la sesión completa, que incluye cuando Claude toma una captura, la procesa y piensa sobre ella y luego sigue, y esos segundos te los tira tu mirando un video que está en un frame quieto. Así que la skill incluye posprocesado usando la librería ffmepg, recortando los trozos en los que no hay interacción. **¿Y el ratón?** Playwright al levantar el navegador en modo no visual, no usa un ratón como tal, sino que simula acciones, por lo que para el video y capturas no tenemos un puntero, y sabemos que en los videos a todos se nos van los ojos al puntero del profesor, así que tuvimos que añadir un svg que simula el puntero y el propio sistema hace que se mueva con coherencia. ## El resultado, el código Si lo quieres, lo tienes directamente en github: https://github.com/ablancodev/skill-auto-manual Y el proceso es muy sencillo: - Te lo descargas de github - En tu equipo entras en la carpeta y desde terminal ejecutas install.sh Esperas a que termine, la primera vez tarda un ratillo, y ya lo tienes listo para cualquier proyecto. En cualquier sesión con Claude simplemente llamas a la skill con /auto-manual, por ejemplo: /auto-manual https://mi-app.com "cómo crear una factura" ## Mejoras futuras **Video narrado** El video mola, pero ya si estuviese narrado sería la guinda del pastel. El sistema está preparado para este sentido, ya que incluye la generación de un fichero con los textos y timing del video, que podremos usar para subtítulos o posibles narraciones automáticas por ejemplo usando la API de Elevenlabs. Esto si me gustaría evolucionarlo más adelante, bien en esta misma skill o quizás en otra aparte para no mezclarlas ya que no todo el mundo lo necesita, a ver si la vida me da un rato libre 🥵 **PDF** Ahora el manual «escrito» es un html, que es relativamente sencillo generar un pdf a partir de él (pídeselo a Claude y te lo hace en un minuto), pero quizás esté bien que lo haga directamente, ya que suele ser como se comparten estos manuales en el día a día. ## Conclusión Si te instalas esta skill, podrás generar tus manuales, tanto propios, como para compartirlos, de tus desarrollos, o incluso de portales que no son tuyos de forma automatizada ahorrándote esa parte tan tediosa que muchos detestamos. Incluso puedes usarlo como parte de la documentación de tu proyecto o para crear videos para redes. Lo importante es pensar en qué te duele en el día a día y ver si esa parte la puedes reducir al mínimo automatizando, aplicando IA u otras soluciones que seguro que podemos encontrar. Y si ya has dado con tu dolor, pero no sabes por donde meterle mano, avísame que seguro que vemos luz al final del tunel. --- ## Vigilancia casera con IA local: un móvil reciclado, un Mac y Docker URL: https://ablancodev.com/build-in-public/vigilancia-casera-con-ia-local-un-movil-reciclado-un-mac-y-docker/ Un móvil antiguo como cámara, detección de movimiento con OpenCV e**interpretación de la escena con un modelo de visión local (Ollama). Todo en tu red,sin servicios en la nube. Alertas a Telegram. Se acercan las vacaciones y dejar el piso sin ningún tipo de vigilancia me hacía poca gracia, así que tocaba darle una vuelta al tema, ya que la solución de pasar por caja y comprar cámaras de seguridad de momento no era viable. ## ¿Qué necesitamos? ### Una cámara La cámara … a ver, tengo unos móviles viejos por ahi que tienen cámara, así que ya tengo candidatos. La idea es usar la cámara del móvil, y que cada X segundos mande una foto a algún sitio que sea capaz de interpretarla, o detectar que ha habido cambios con respecto a la toma anterior. 2 Androids y 1 iPhone, asi que puedo optar por crear apps nativas y luego ver cómo meterlas en ellos, ya que no las voy a publicar en la apps store, pero en modo desarrollador podría instalarlas, pero la verdad es que para una funcionalidad tán básica me da bastante pereza, así que mejor montar una web PWA que nos permita activar la cámara, y mandar cada X segundos una captura a un endpoint. Esto nos valdrá independientemente del sistema operativo del móvil, como de la versión de él, ya que al ser móviles viejos son versiones ya obsoletas. ## El cerebro Vale, ya vemos lo que hay en el entorno, ahora toca analizarlo. Lo primero que se me ocurre es crear diferencia entre imágenes, así podré ver el porcentaje de cambio entre ambas y deducir que sobre cierto porcentaje lo vamos a considerar «intruso» Para la detección del movimiento usaremos la librería de visión por computador OpenCV, así no tenemos que irnos a bajo nivel a hacer restas de imágenes y demás. Aunque me gustaría meterle algo de IA, porque hoy en día si no le metes IA a todo no eres nadie … 😖 … y sobre todo por ver si puede detectar por ejemplo que es un perro y entonces no alertar, ya que será nuestro Lobo dando una vuelta por casa. Para la capa de IA usaremos Ollama con el modelo MoonDream diseñado para detectar elementos. Pero con la IA hemos topado y nos hemos encontrado con 2 problemas: - El primero es menor, pero es inglés a full, así que nada de pasarle el prompt en español, porque pilla poco, pero bueno, un mal menor. - El segundo ya es mayor, y es la latencia de respuesta, que debido a que mi equipo no tiene una gran GPU ni capacidades, y además se agrava un poco al querer montarlo en un docker, ya que quería hacer el repo lo más dockerizado para que cualquiera pueda montar su sistema en casa. De momento tiraremos así, y lo que vamos a hacer es poner la IA opcional y que el sistema pueda funcionar sin ella, aunque tengamos algún que otro falso positivo. Así tendremos dos sistemas de alertas, uno para cuando se detecte movimiento, y otro para cuando la IA detecte lo que hay en la imagen cuando se ha detectado movimiento. Pero para no turrarnos a alertas, le pondremos un calmdown de 60 segundos, para que no se repitan alertas en menos de 60 segundos. Todo esto montados tras una FastAPI en Python, nos permitirá tener un endpoint al que la web que se abre en los móviles puedan enviar la imagen para procesarla. Vale, ¿y dónde lo montamos? fácil, para eso está mi Mac mini del 2018 (creo) que ya está muerto de risa debajo de la TV, que poco más que de backup o navegador es lo que hace hoy en día. Un Mac Mini intel, de 10 Gigas, el cual ya no puedo actualizar por lo que tuve que buscar mejora, ya que como desarrollador de Apps hay que ir probando todo lo nuevo que nos ofrecen desde Apple. Pero Antonio, ¿y como ponemos esto visible en internet para que el navegador web de los móviles puedan llamarla?** De ninguna manera 😎 Eso es lo mejor, que como estamos en casa, los dispositivos se comunicarán entre ellos dentro de la misma red wifi, por lo que usaremos las IPs para reconocerse, asi que en nuestro navegador pondremos algo como: https://192.168.0.54:8000 (si …0.54 es la de nuestro Mac mini) ## El resultado Ya tenemos las cámaras, el cerebro, ¿y como queda esto ya montado? Desde los móviles, da igual iphone que android es tan sencillo como abrir un navegador web y abrir la url https://192.168.0.54:8000 Cuando iniciamos el proceso, se activa la cámara y podemos fijar una escena de referencia, es decir la imagen de cuando todo está en calma y sobre la que revisaremos el posible movimiento. Uno de los pequeños inconvenientes de este sistema de página web, es que para que el sistema pueda acceder a la cámara tiene que estar abierto, y claro la pantalla emitiría luz, y en ocasiones no queremos que se vea, que sea una cámara oculta, por lo que una posible mejora sería que cuando esté grabando, el navgeador conste de una simple pantalla negra, pero de momento para esta prueba lo dejamos así, así podremos ver lo que está pasando, ya que incluye incluso un sencillo log. Las capturas se quedan guardadas en el mac mini, pero si nos vamos de vacaciones, dejamos el Mac Mini en casa, los móviles-cámaras en casa y todo en local, ¿cómo nos enteramos de que alguien ha entrado? ### Notificaciones La idea inicial era mandar un email, pero un email …. no sé tu, pero yo no suelo ser tan reactivo a la llegada de emails nuevos, así que había que buscar otra alternativa. Y pensando me acordé de los chatBots de Telegram (si no has creado nunca ninguno, en el post «Creando un chatbot en Telegram …» te explico cómo crearlo), así que lo mandaremos a un Bot en Telegram. Así si se detecta movimiento por ejemplo como el depravado de la derecha 👉🏻 nos llegará a Telegram una notificación. En este caso podemos ver los 2 tipos de notificaciones implementadas, la primera que proviene de OpenCV, al que le hemos puesto un margen de cambio del 5%, y la segunda, la de la IA, que interpreta lo que hay en la imagen y nos manda la descripción, pero hemos tenidos que hacer ambas en paralelo e independientes para poder anular la IA y poder gestionar el delay del modelo local. ## ¿Mejor con IA o sin ella? Aunque desde el primer minuto mi idea era IA, IA e IA, conforme iba avanzando el proyecto me di cuenta que con la librería OpenCV es más que suficiente para detectar movimiento, y que la IA lo que aporta es una «interpretación» de la imagen, es decir es capaz de decirnos si hay un gato, o incluso podemos añadir alabras claves para evitar que nos avise, por ejemplo si indicamos «gato» o «perro» al detectarlo no nos avisaría. En contraprestación el modelo local tiene una latencia bastante alta, alrededor de unos 30 segundos, un mayor consumo de nuestro Mac Mini (que como la idea es usar un equipo viejo, lo mismo no es lo más apropiado). Así que mi veredicto es … 🥁: estas vacaciones se queda sin IA, lo dejamos con el sistema de OpenCV. ## Descarga el proyecto El proyecto lo tienes disponible en GitHub, está todo dockerizado para que puedes descargarlo y desplegarlo en tu Mac Mini o equipo que vaya a hacer de cerebro (te vale Windows, don’t worry, la magia de docker) Ir a GitHub --- ## Conectando el MCP de WordPress con claude code / desktop URL: https://ablancodev.com/build-in-public/conectando-el-mcp-de-wordpress-con-claude-code-desktop/ La idea es una vez que tenemos WordPress con las abilities, exponerlas con un mcp para que nuestro claude de consola o desktop pueda usarlas y así «hablar» con nuestra tienda / blog / saas o lo que tengamos en el wordpress. Necesitamos: - WordPress >= 6.9 - mcp-adapter - (no hace falta instalar el plugin de IA de WordPress, ya que ese es para tener la «IA integrada» y nosotros en este caso lo que queremos es exponer y que una «IA externa» nos consuma. ## **Plugin mcp-adapter** El repo oficial de WordPress es este: https://github.com/WordPress/mcp-adapter El problema que tiene es que una vez descargado y demás hay que hacer: composer install y entonces ya lo tenemos preparado. Para evitar tener que hacer composer install, he creado mcp-adapter-ready, que ya viene con el composer hecho y además en los perfiles de usuarios le he puesto una funcionalidad de crear código base 64 a partir del usuario:app_password, que lo necesitaremos más adelante y aunque se puede hacer por consola, creo que el común de los mortales, hacer composer install y el base64 se les puede escapar, así que aquí estamos, a servir: Plugin MCP Adapter Ready ## Claude terminal En claude términal es algo más «técnico», así que te aconsejo que empieces por claude desktop, que tienes más abajo ( ir a Desktop ) Instrucción para generar el base64: echo -n "blanco:uhrf MVRI GStx gZao sZWd P480" | base64 Al código de arriba que se ejecuta en consola Mac por ejemplo, me devuelve un codigo largo que es el que vamos a usar ahora en Authorization Basic. Instrucción en terminal donde añadir el mcp a claude code terminal: claude mcp add --transport http lab-ablancodev https://tuweb.com/wp-json/mcp/mcp-adapter-default-server \ --header "Authorization: Basic YmxhbmNvOnVocmYgTVZSSSBHU3R4IGdaYW8gc1pXxxxxx" Aquí ya podríamos lanzar claude code y con /mcp ver que lo tenemos por ahi. ## Claude desktop Para claude desktop es un poco distinto, pero para eso te he hecho este pedazo de manual en la primera parte del video para que sepas por donde tienes que ir trasteando: Desde la app desktop, en **Ajustes -> Desarrollo** si le damos a editar, nos abre la carpeta donde está el fichero que usa, a dicho archivo hay que añadir el json del gist como nivel 0. ** La verdad que no es muy intuitivo, porque editar ficheros json no es lo más cómodo para todo el mundo (ya buscaré alguna otra alternativa) Ejemplo de mi fichero claude_desktop_config.json { "mcpServers": { "MiWordpress": { "command": "npx", "args": [ "-y", "@automattic/mcp-wordpress-remote@latest" ], "env": { "WP_API_URL": "http://midominio.com/wp-json/mcp/mcp-adapter-default-server", "WP_API_USERNAME": "miUsuario", "WP_API_PASSWORD": "w1Lf fMvP UCzr ZRnD xxxx xxxx" } } }, "preferences": { ... }, "coworkUserFilesPath": "/Users/ablancodev/Claude" } Y con esto ya tendríamos tanto terminal como la app desktop enganchados a nuestro WordPress. Ahora es turno de sacarle provecho a esto, imagina preguntar cuales son tus posts mejorables a nivel SEO, o datos sobre tu WooCommerce, o si tienes tu propia intranet en WordPress poder «hablar» de tus tareas, o tus finanzas, o tickets de soporte … ## PD:** Este plugin tiene buena pinta para tener un conjunto de abilities disponibles core que no ofrece de momento WP: https://wordpress.org/plugins/enable-abilities-for-mcp/ --- ## IA local: el kit de supervivencia cuando caigan las grandes IA URL: https://ablancodev.com/build-in-public/ia-local-el-kit-de-supervivencia-cuando-caigan-las-grandes-ia/ #### Tu kit de supervivencia para cuando los grandes cierren el grifo Imagina que mañana te levantas y: - ChatGPT Plus cuesta 200€/mes en vez de 22. - Tu API key de OpenAI ha sido baneada porque tu caso de uso «viola las políticas» (no te explican cuál). - El modelo de Anthropic que usaba tu app en producción está discontinuado, tienes 30 días. - Tu país decide regular la IA y bloquea servicios extranjeros. - Midjourney se ha vendido y los nuevos dueños quintuplican el plan. ¿Qué haces? ¿Lloras? ¿Pagas lo que pidan? ¿Migras a mano las 47 integraciones que dependen de OpenAI? O bajas al sótano, enciendes el generador, y sigues trabajando. Este post va de ese sótano. ## La idea: IA como infraestructura crítica personal Tienes copias de seguridad de tus fotos. Tienes un NAS. Tienes un gestor de contraseñas con vault local. Tienes una linterna en el cajón por si se va la luz. Pero probablemente **toda tu vida con IA depende de tres empresas californianas**. ChatGPT para pensar. Copilot para programar. Midjourney para imágenes. Whisper o un servicio cloud para transcribir. ElevenLabs para voz. Tu SaaS, si tienes uno, le manda 200€/mes a OpenAI sin que te des cuenta. Y esos tres pueden: - Subir precios cuando quieran. - Cambiar términos cuando quieran. - Banearte cuando quieran. - Discontinuar el modelo que usas cuando quieran. - Decidir que tu industria, país o caso de uso ya no encaja. No estoy diciendo que vayan a hacerlo mañana. Estoy diciendo que **pueden**, y que estar a su merced no debería ser tu plan B. Lo mismo que tener un NAS no significa odiar a Google Photos: tener IA local no significa odiar a OpenAI. Es prudencia, no paranoia. ** Tu objetivo no es casarte con una IA. Es poder divorciarte en cualquier momento. ## El kit, perfil bajo He empaquetado todo lo que necesitas en un repo con Docker Compose. Un git clone y un docker compose up y tienes tu propio ChatGPT corriendo en tu máquina. El repo está aquí: ai-survival-kit. Filosofía: - No reemplaces a OpenAI. Haz que OpenAI sea reemplazable.** Tu código sigue llamando a gpt-4o. Solo cambias la URL base. - **Modular por perfiles.** Instalas solo lo que vas a usar. Desde «necesito chatear» hasta «todo el stack». - **Un comando para arrancar.** Cero CUDA hell. Cero «no me funciona Python». El kit tiene cuatro niveles, según cuánto búnker quieras: ### Nivel 1 — apagón básico Lo mínimo: un LLM local con interfaz tipo ChatGPT. - **Ollama** como runtime de modelos. - **Open WebUI** como interfaz (es prácticamente idéntica a ChatGPT). - **Qwen 2.5** como modelo por defecto (multilingüe, hace buen español). Con esto ya puedes: - Resumir un PDF largo arrastrándolo a la conversación. - Reescribir emails sin que tu jefe se entere de que usas IA. - Pedir ideas para un proyecto. - Traducir cosas. - Explicarle a tu LLM local cosas que prefieres no contarle a OpenAI. docker compose --profile basic up -d Y a los 5 minutos tienes ChatGPT en localhost:3000. Tuyo. Sin que nadie mire. Sin contador de mensajes. ### Nivel 2 — modo creador Le añadimos audio: escuchar y hablar. - **faster-whisper-server** para transcribir audio (mismo motor que usa OpenAI por dentro). - **Kokoro** para generar voz. Ambos exponen API **compatible con OpenAI**, lo cual es brutal: si tienes una app que ya usa whisper-1 o tts-1, cambias dos líneas y pasa a usar tu versión local. Casos de uso reales: - **Transcribir todas tus reuniones** sin pagar 30€/mes a Otter o similar, y sin enviar audio confidencial a la nube. - **Procesar notas de voz** y volcarlas a Notion / Obsidian / lo que uses. - **Generar locuciones** para vídeos, podcasts, audiolibros sin ElevenLabs. - **Asistente de voz casero** que vive solo en tu casa. ### Nivel 3 — taller de imágenes (y vídeo) Aquí entra **ComfyUI**, el «Photoshop del prompt». - Generar miniaturas para YouTube sin facturas de Midjourney. - Mockups para clientes. - Ilustraciones para posts. - Assets para juegos. - Vídeo generativo (CogVideoX, Wan, HunyuanVideo) si tienes una GPU seria. Avisos sinceros: - Para que sea usable necesitas **GPU NVIDIA** o **Mac Apple Silicon decente**. En CPU pura tarda minutos por imagen. - En Mac Docker no puede usar Metal, así que el repo incluye un script (scripts/comfyui-mac.sh) que instala ComfyUI nativo dentro del propio repo. Reversible con un rm -rf. - Hay una trampa: las versiones recientes de ComfyUI traen workflows «API» (Seedance, Kling, Veo, Flux Pro…) que **piden pagar créditos** porque llaman a servicios cloud. Eso no es local. Usa workflows con nodos Load Checkpoint / KSampler y serás libre. ### Nivel 4 — el búnker bien equipado Esta es la pieza más potente del kit, especialmente si tienes un negocio o un side-project que ya usa la API de OpenAI. **LiteLLM** es un proxy que vive en localhost:4000 y habla **exactamente la misma API que OpenAI**. Pero por debajo enruta a tus servicios locales: - Llamadas a gpt-4o → Ollama con Qwen 2.5 - Llamadas a whisper-1 → tu faster-whisper local - Llamadas a tts-1 → tu Kokoro local Es decir: tu código en Python que hoy dice esto: client = OpenAI(api_key="sk-...") client.chat.completions.create(model="gpt-4o", messages=[...]) Se convierte en esto: client = OpenAI(base_url="http://localhost:4000/v1", api_key="not-needed") client.chat.completions.create(model="gpt-4o", messages=[...]) **Dos líneas. Sin tocar la lógica de tu aplicación.** Y de repente tu SaaS funciona sin pagarle a nadie. O sigue pagándole a OpenAI pero **sabiendo que puedes desenchufarlo en 30 segundos** si sube precios. ** Todo el resto del kit te da autonomía técnica. LiteLLM te da autonomía operativa. ## El cálculo de servilleta Mira lo que probablemente estás pagando ahora mismo si trabajas con IA a diario: ServicioPrecio típicoChatGPT Plus~22€/mesGitHub Copilot~10€/mesMidjourney~30€/mesElevenLabs~22€/mesServicio de transcripción~15€/mesAPI calls de tu SaaSvariable, fácilmente 50-500€/mes Sumando lo personal: ~100€/mes**. 1200€/año. Sin contar la cuenta de la empresa. Lo que necesitas para el kit: - **Si ya tienes un Mac con M1/M2/M3 con 16+ GB de RAM:** nada nuevo, te corre todo. - **Si tienes que comprar hardware:** una mini PC con 32 GB de RAM (~500€) o un PC con RTX 4070 (~800€) corre prácticamente todo bien. - **El bunker definitivo:** Mac Studio o servidor con GPU usada, ~2000-3000€. Es decir, **lo que pagas en 1-2 años de suscripciones** se convierte en hardware que es tuyo, te dura 5+ años, y nadie puede ratelimitarte. Y la cuenta de tu empresa, si la tienes, se vuelve más interesante: bajas de pagar 500€/mes en APIs a pagar 0€/mes a cambio de un servidor que ya tenías. ## Pero, ¿es realmente equivalente? No te voy a engañar. **Hoy no.** Qwen 2.5 7B no es GPT-4o. FLUX local no es Midjourney v6 al 100%. ComfyUI en Mac no genera vídeo Sora. Pero: - **Qwen 2.5 14B y 32B se acercan muchísimo a GPT-4o** para la mayoría de tareas comunes (resumir, traducir, escribir, razonamiento básico, código). - **Llama 3.3 70B** ya es competitivo con los frontier models para muchísimos casos. - **FLUX.1-schnell** genera imágenes que hace 18 meses la gente pagaba por hacer en Midjourney. - **El gap se cierra cada mes.** En 2026 lo que hoy es «casi GPT-4» será «GPT-4 sin discusión». Y para muchísimos casos de uso **no necesitas el mejor modelo del mundo**. Necesitas uno bueno, predecible, tuyo, gratis y disponible. Eso ya lo tienes. Mi recomendación práctica: **híbrido**. Usa los modelos cloud cuando los necesites de verdad. Usa los locales para el 80% restante. Cuando los cloud te fallen (que te fallarán), tienes un plan B operativo. ## ¿Cómo se monta esto? El repo está pensado para que un git clone y un docker compose up te dejen funcionando. Las instrucciones técnicas detalladas están en el README, pero el resumen es: git clone https://github.com/ablancodev/ai-survival-kit.git cd ai-survival-kit cp .env.example .env # Elige tu nivel de paranoia: docker compose --profile basic up -d # chat local docker compose --profile creator up -d # + audio docker compose --profile bunker up -d # + proxy OpenAI para tus apps Para ComfyUI hay dos caminos (Docker NVIDIA / script nativo en Mac) explicados en el README. Disco: cuenta con 15 GB para el básico, 30-60 GB cómodo, 100+ si vas a meter modelos de vídeo. Y ojo: es el disco **virtual de Docker Desktop**, no el del sistema. Sube su límite en Settings → Resources si vas justo. ## La pregunta incómoda Después de un par de tardes montando esto, te das cuenta de algo: **Probablemente no necesitabas ChatGPT Plus.** Probablemente no necesitabas la suite completa de cinco suscripciones. Lo que necesitabas era una buena interfaz, un par de modelos decentes, y un poco de paciencia para configurarlo una vez. Lo que pagas a OpenAI, Anthropic, Midjourney y compañía es muy útil — pero también es muy reemplazable. Y el día que decidan que tu uso, tu industria, tu país o tu cara no encajan, vas a desear haber montado esto antes. En el búnker nadie te limita, el bunker es tuyo ## Enlaces Repo del kit: ai-survival-kit — código, instrucciones técnicas, troubleshooting. Si solo quieres probarlo: - clónalo - docker compose --profile basic up -d - abre localhost:3000 Si te ha resultado útil, comparte el repo. Cuanta más gente tenga su búnker montado, menos podrá ratelimitarnos cualquiera. --- ## Creo mi propio “Lovable” con Pencil CLI y Claude Code URL: https://ablancodev.com/build-in-public/creo-mi-propio-lovable-con-pencil-cli-y-claude-code/ En mi día a día ya uso Pencil y Claude. Pero en sus versiones “normales”: - Pencil como app de escritorio - Claude desde terminal Y funcionan bien. Muy bien, de hecho. Pero no puedo decir: - “genera este diseño mientras voy en el tren” - “lánzalo desde otro sistema” - “déjamelo preparado y luego lo reviso” Siempre es lo mismo:**abrir la app, escribir, esperar, exportar… Todo muy manual. ## “Esto debería ser una API” Así que me propuse algo sencillo: “Quiero generar diseños de interfaces con una API REST. Le mando texto, me devuelve un diseño editable.” Y de paso, aprender cómo encajar herramientas nuevas como Pencil CLI y Claude en algo más real. La idea era esta: curl -X POST http://localhost:8000/designs/generate \ -d '{"prompt": "Landing web para un curso de formación sobre desarrollo."}' Y recibir algo así: { "success": true, "download_url": "...design.pen", "preview_url": "...design.png" } Es decir: - Un .pen editable (formato de Pencil) - Un PNG para ver rápido el resultado ## La idea: “Pencil as a Service” El invento básicamente hace esto: Cliente → POST /designs/generate → FastAPI → Pencil CLI → Claude genera el diseño → Guardo .pen + PNG → Devuelvo URLs Y ya está, ¿fácil no? pues no jejeje ## Stack He usado: - FastAPI** → porque es rápido de montar y async - **Docker** → porque esto sin docker no hay quien lo reproduzca - **Pencil CLI** → para generar diseños desde terminal - **Claude Code** → como cerebro por debajo Y algo importante: ** No hay frontend. Y no pasa nada. De momento … que todos sabemos que sin frontend no le gusta a nadie 🫥 ## Cómo funciona realmente El core está en ejecutar Pencil CLI desde Python. Algo así: pencil --out design.pen --prompt "landing page for a coffee shop" Y luego, para la exportación a .png pencil --in design.pen --export design.png --export-type png Todo esto envuelto en un servicio que: - gestiona timeouts - guarda archivos - devuelve URLs Pero eso si, necesitamos que se use la suscripción de Claude, paso de pagar por la API de Anthropic** cuando ya les suelto todos los meses mis 100 pavazos … no tengo ninero 😖 ## Arquitectura Aquí ya empiezan las cosas interesantes. Necesitaba: - Node (por el CLI de Pencil) - Python (por FastAPI) Así que acabé con algo tipo: - node:20-slim como base - Python encima - instalar: @pencil.dev/cli - @anthropic-ai/claude-code Hasta aquí todo bien. Hasta que deja de ir. ## Problema #1: Claude no quiere ejecutarse como root Error: --dangerously-skip-permissions cannot be used with root/sudo (que dios nos pille confesados !!!) Traducción: ** “No voy a funcionar si estás usando root, campeón” Y claro… Docker usa root por defecto. ### Solución Usar el usuario node que ya viene en la imagen. Lección aprendida: No luches contra la imagen base. Ya trae cosas pensadas. ## Problema #2: Claude se inventaba los .pen Este fue el mejor, porque si tiras de vibe coding a tope, a veces no te enteras de lo que ocurre debajo del capó, y Claude decidió que la mejor opción era mandar el prompt a Claude y que este usara el MCP de pencil para generarlo, pero eso no va todo lo bien que queremos. Yo le decía: “genera un archivo .pen” Y Claude: “Claro que sí” Y me devolvía… un JSON inventado. { "version": "1.0.0" } No era un .pen válido. ### Solución Dejar de hacer inventos y usar directamente: 👉 Pencil CLI en vez de intentar orquestarlo desde Claude** Lección: Vive coding a tope, pero pide con criterio, no presupongas que lo va a hacer como tu quieres. ## Problema #3: dos CLIs con el mismo nombre (más o menos) - @open-pencil/cli → openpencil 🚫 - @pencil.dev/cli → pencil ✅ Le hablé de «pencil», y tiró por OpenPencil, en vez de Pencil.dev … de ahí también que el .pen que me generaba estuviese mal. ### Solución Comprobar siempre: npm view paquete bin Lección: npm es el salvaje oeste. ## Problema #4: No le llega la CLI KEY Yo tenía esto: PENCIL_CLI_KEY=... Python lo leía bien. Pero el proceso hijo (Pencil CLI) no. ### Por qué Porque Pydantic carga el .env…pero no lo exporta a os.environ. ### Solución from dotenv import load_dotenv load_dotenv() Y en Docker: env_file: - .env Lección: Que Python vea una variable no significa que el sistema la vea. ## Problema #5: nombres de modelos inventados Yo usando: CLAUDE_MODEL=sonnet Pencil: no sé qué es eso Modelos reales: - claude-haiku-4-5 - claude-sonnet-4-6 - claude-opus-4-6 Lección: los nombres “bonitos” no funcionan en producción. ## Problema #6: esto tarda… bastante Generar un diseño no es instantáneo. Puede tardar: 5–10 minutos Mi timeout inicial: CLAUDE_TIMEOUT=300 Resultado: petaba. ### Solución CLAUDE_TIMEOUT=600 Lección: la IA es rápida… hasta que deja de serlo. ## Lo que tengo ahora mismo Una API que: - recibe texto - genera diseños reales - exporta previews - está dockerizada - funciona sin frontend Y que puedes levantar con: docker compose up --build ## A mejorar - Es síncrono → te quedas esperando - No hay cola de trabajos - No hay autenticación - No hay UI (de momento) Pero funciona. Y eso ya es mucho. ## Ideas para evolucionarlo convertir esto en una pieza dentro de un sistema mayor Si le metemos un validador, es decir, un endpoint donde poder decir OK a un diseño, podríamos pasarlo a desarrollo, ya que el contenedor ya tiene una instancia de claude code disponible 🚀 🚀 🚀 … y ya puestos, teniendo un endpoint, tenemos la opción de hacer las llamadas desde un bot de Telegram (aquí el post de cómo cree uno hace tiempo) ## Está chulo, ¿eh? Si te interesa el proyecto, lo puedes ver y descargar desde el repo: https://github.com/ablancodev/pencil-as-a-service Y si quieres que le meta frontend o lo lleve más lejos… dímelo, que seguramente ya lo esté pensando 😄 --- ## Refactorizando flujo Telegram – WordPress URL: https://ablancodev.com/build-in-public/refactorizando-flujo-telegram-wordpress/ Este fin de semana ha tocado refactorizar el flujo que tenía entre Telegram y WordPress para secondbrainbot.com, flujo que en su día ChatGPT decidió que era el mejor (me cago en sus cables 🫥) La idea es tener vinculada la cuenta de usuario de WordPress con la de Telegram, para que cuando un usuario escriba a nuestro bot de Telegram, y mandemos el mensaje al Saas WordPress, vaya vinculado a un usuario en concreto de WP. Este vínculo lo podemos hacer con un chat_id que tiene Telegram cuando le escribimos, pero es verdad que teníamos un flujo un poco rarete, lo que teníamos era: - El usuario se crea una cuenta en WordPress. - En ese WP consulta un ID que le asignábamos (lo tenía en modo base, el id de usuario) - EL usuario se va al Bot de Telegram y escribía algo como /start MI_ID - Ese mensaje llegaba a WordPress y comprobábamos el ID de usuario: Si aún no tenía chat_id asignado, se lo asignábamos. - Si ya tenía, pues nada, ya estaban vinculados. Lo cual era poco intuitivo para el usuario, ya que para empezar tenía que estar saltando entre plataformas, siendo un proceso poco transparente para él, pero bueno, esa la la solución que en día copilot me dio y acepté, que conste. IA, sujétame el cubata … Vamos a pensar en el flujo lo más transparente posible para el usuario, que sería el flujo ideal, ya veremos luego si es implementable o no: - Como usuario, abro Telegram, y escribo una idea, una url o lo que quiera guardar en mi segundo cerebro. Si tengo cuenta de usuario en el Saas, pues que el mensaje vaya a mi cuenta. - Si no tengo, pues que me lo cree y envíe el mensaje. Operativo desde el segundo 1, y sin tener que pensar en logins, ni nada de momento, eso ya lo veré luego cuando quiera consultar mi SecondBrain. Y si nos ponemos a bajarlo e implementarlo, parece que es sencillo: - Cuando el usuario manda un mensaje, ahí va el chat_id, por lo que tan sencillo como comprobar en base de datos que haya algún usuario con dicho chat_id. Si hay un usuario ya con ese chat_id, pues simplemente procesamos el mensaje y se lo guardamos en su SecondBrain. - Si no hay usuario vinculado a dicho chat_id, lo creamos y: Procesamos el mensaje y se lo guardamos en su SecondBain - Devolvemos como mensaje a Telegram, el ok con el nombre de usuario, y el típico enlace de establecer contraseña. Mucho mejor, ¿no? Si quieres probarlo online directamente funcionando en un Saas, lo tienes en SecondBrainBot.com, o directamente escribiéndole al bot de Telegram: https://t.me/com_second_brain_bot NEWSLETTER · EL LABORATORIO ## Únete al laboratorio 🧪 Si quieres el código de la sincronización entre Telegram y WordPress para que lo puedas usar en tus proyectos y mucho más contenido exclusivo, es sencillo y gratuito, apúntate a la newsletter. Cero spam, sólo te escribiré cuando lance proyecto nuevo y para enviarte el código fuente o algún recurso exclusivo. Sin spam. Me leerás cuando tenga algo que merezca la pena. --- ## Publicando en TikTok via API URL: https://ablancodev.com/build-in-public/publicando-en-tiktok-via-api/ Ayer me tropecé con este video de tiktok de Carliyoelnervio, que no puede ser más divertido su canal, y en el que proponía tener una cuenta «abierta» a todo el mundo en la que cualquiera pueda subir videos (no me pueden gustar más estas chaladuras jeje) @carliyoelnervio Cualquier desarrollador o programador que me escriba por cualquier lado y lo hacemos para YA #somoslosdertisto #carliyoelnervio ♬ sonido original – Carlos García Él propone un bot, o algo así, pero ¿y un formulario? un sencillo formulario donde cualquiera pueda subir un video y que dicho video se publique en una cuenta de TikTok … me gusta el proyecto 🥰 Lo primero, vamos a ver si la API de TikTok nos permite algo de esto … y parece que si, que tiene la Content Posting API ( https://developers.tiktok.com/doc/content-posting-api-get-started?enter_method=left_navigation ) que nos permite tanto publicar directamente como subir videos y dejarlos pendientes de aprobar (que sería lo recomendable), por lo que empezamos bien 👏🏻 ## ¿API KEY? Empieza el mambo, porque no vamos por API KEY clásica, sino que tenemos en modo Meta, tener que registrar una APP en el portal de desarrollador de TikTok https://developers.tiktok.com/doc/getting-started-create-an-app Lo bueno es que no hay que pagar ni nada, y el registro como desarrollador es sencillo y rápido. Así que creamos nuestra App Nos pedirán info pues como en casi todas las apps de este estilo que vayamos a crear en cualquier plataforma, pero algunas distintivas han sido: - Nos piden dominios desde donde se llamará, políticas legales y redirection url, pero hay que «validar» el dominio, por lo que vamos a tener que añadir un registro TXT en las DNS de nuestro dominio para la validación. - Tras añadir toda la info vamos a necesitar que TikTok nos la valide … que perezaaaaa, pero bueno, eso si, tiene un modo sandbox 🙌🏻 🙌🏻 🙌🏻 - Otra cosilla a tener en cuenta, es añadir los scope necesarios, que en este caso son sencillos, ya que si marcamos que la app es para Content Posting API, no s pedirán tambien para Login API y todo listo, marcamos que queremos poder publicar directamente videos y todo solucionado. Pasamos el modo Sandbox, así de momento no tenemos que esperar aprobación, y en el modo SandBox nos piden casi los mismos datos (aprobación de dominio incluida), y tenemos que añadir cuenta para las pruebas, la damos de alta y listo, ya tenemos nuestro Client Key y Client Secret para poder ponernos a programar. ## Implementando la solución La idea es: - Un formulario HTML al que pueda acceder cualquier usuario, subir su video, poner un título y breve descripción. - Dicho formulario enviará la imagen a la API de TikTok, pero para ello la API necesita un token de acceso. - ¿un token Antonio? pero si eso no lo tenemos!! Con el Client Key y Client Secret que teníamos de nuestra App (que van a un fichero config.php de variables), se obtiene el token, que caduca cada x tiempo, pero que es el que usaremos para la llamada de subida de contenido de la API. Parece sencillo, así que con un /oauth.php vinculamos nuestra cuenta (en la que queremos que se publique el contenido), y eso guardará el token (esta url sólo la visitamos nosotros o Carliyoelnervio para vincular cuenta una vez o cuando el token se vaya caducando), el resto de los mortales sólo visitaría el formulario para subir el video. Y cuando un usuario rellene el formulario, para dicha cuenta que se va el contenido 🚀 Cosillas que me he encontrado en las pruebas y quiero que te evites tener que perder el tiempo, ya lo he hecho yo por ti: - Si el formato del video que le subimos no es el correcto, no lo admitirá, me refiero tanto a extensión como dimensiones (si queremos que sea abierto a todo cristo, lo mismo habría que hacer un preproceso del video … ni idea ahora de cómo, pero seguro que algo hay por ahi) - Añadí la opción de publicar directamente y la de Enviar a bandeja. La de enviar a bandeja lo que hace es subirlo, tiktok, lo preprocesa y te manda una notificación cuando está preparado, pero no lo llega a publicar (que en el caso de que la gente meta mucha mierda y se prefiera prevalidación es la mejor opción).La opción de publicar directamente en una cuenta Sandbox no funciona, aunque marques la opción de que si lo permites, por lo visto la única forma de hacerlo funcionar en sandbox sería poner tu cuenta como privada, que se suba y luego ponerla como pública, entonces el video seguiría ahí … eso cuenta la leyenda, porque paso de poner mi super cuenta de 100 seguidores en privada. ## Conclusión Poder se puede, tendríamos que montarlo en un dominio del que seamos dueños (por eso de la DNS) y esperar la aprobación de TikTok, que supongo que no darán guerra, pero eso nunca se sabe. ## El código Si alguno se viene arriba y quiere tirarle, os lo dejo en un repo en github, editad el config.php con vuestros datos de acceso de la App y la redirection url y a trastearlo. https://github.com/ablancodev/carliyo-tiktok --- ## Vibe Coding nivel PRO con los Simpsons : Ralph URL: https://ablancodev.com/build-in-public/vibe-coding-con-los-simpsons-ralph/ Si, con los Simpsons 🎉**Esta semana atrás me he tropezado con algo que se estaba viralizando a nivel de vibe coding, y es este framework o metodología llevada a plugin de Claude Code, llamada «Ralph Wiggum», que permite que el sistema sea iterativo, pasando del modelo clásico de simple iteración, a un sistema iterativo … ahora si te podrás ir a tomar un café mientras claude curra sin parar por ti. En este post, el creador de la criatura, Geoffrey Huntley explica el concepto, que a modo de resumen si lo bajamos a claude code, es interceptarlo antes del stop, y pedirle que siga iterando el número de veces que queremos. Ya Antonio, y ¿esto cómo lo conseguimos?**, pues aquí aparece el artefacto **«plugin»** para Claude, que nos permite «interactuar» con el bicharraco: https://awesomeclaude.ai/ralph-wiggum Según dicha web, la cosa será sencilla, 3 comandos y a correr …. la realidad …. como siempre, nada va del tirón, así que cuando ejecutemos el primer comando de instalar el plugin, te dirá que no lo encuentra, y es porque no tiene el «marketplace», así que lo primero que tendremos que añadir es el repo del plugin Ralph-loop (si, han quitado lo de Wiggum, por esas cosas de no terminar en la cárcel y demás): https://github.com/anthropics/claude-plugins-official/tree/main/plugins/ralph-loop Bien lo tenemos 🎉, ahora a llamar a Ralph y que curre … espera, que Ralph tiene que ejecutar .sh y otras cositas, y claro, eso así por las buenas va a ser que no tenemos permisos. He probado otras soluciones, y nada de nada, lo que me funcionó es ejecutar claude en modo sin miedo a la muerte: claude --dangerously-skip-permissions Ahora ya si que si, sin miedo a la muerte podemos ejecutar Ralph-loop pasándole un prompt, y un número máximo de iteraciones y aquello se pondrá a picar código como si no hubiese mañana. ## ¿Lo usaré? Mola, porque mola mil dejarlo ahí currando y que pueda él mismo ir iterando su curro, pero claro, si ya de por si cuando iteramos de forma manual en cuanto te descuidas pierdes el control del código y aquello se te va de las manos, imagina ahora diciéndole que para algo itere 20 veces 🙃 Así que ahora más que nunca mejor pasarle una buena especificación y condiciones que tiene que seguir, y git a tope. Para pequeños cambios e ir retomando control del código, no lo usaría, pero para la primera versión, creo que va a pasar a mi día a día 🤜🏻 🤛🏻 --- ## Cómo creé mi Bot Avatar: del concepto a la realidad URL: https://ablancodev.com/build-in-public/como-cree-mi-bot-avatar/ Así de la manga me acabo de sacar lo de «Bot Avatar», pero es que estoy de los chatbot clásicos hasta las narices. Hemos pasado de un chatbot tradicional de máquina de estados con respuestas predecibles, a un chatbot mucho más potente con IA, pero estéticamente nos hemos quedado con lo mismo (bueno, le hemos puesto la barita mágica para indicar que hay IA por detrás 🪄 ) Y en esto estaba yo pensando, cuando me he dicho que un asistente de tienda, o un profesor en un LMS, o incluso un personaje de videojuegos en una tienda de gaming molaría mucho más, ganando el usuario en engagement con la marca y una experiencia más divertida. ## Cómo monté el avatar 3D Cuando hice el master de Negocios en el Metaverso, ya tuve que montar un modelo 3D en una web, por lo que sabía más o menos por donde tirar. Usaremos algún modelo 3D por ejemplo de la web Sketchfab.com, en este caso he usado este gratuito que me gustóhttps://sketchfab.com/3d-models/bow-trigger-game-character-3df5553dfa5848fb9b1005d15b757d5d del creador zeintendy Puedes exportarlos en varias extensiones, pero la «más nativa» web es la .glb, y en eso me acordé de Ready PLayer Me, pero no recordaba contraseña y demás, así que nada, nos quedamos con el de Sketchfab. Para montarlo en la web, tenemos la librería treejs.org 👏🏻 👏🏻 👏🏻 que para 3D web es sencilla de usar y muy extendida en la comunidad. ## Cómo monté la parte conversacional En este caso, como lo interesante era darle una vuelta a la parte estética que se le presenta al usuario, el chat es simplemente llamadas simples a la API de OpenAI, ningún misterio, lo ideal es montar un RAG con la info de la empresa, la tienda, el LMS o lo que querramos, pero eso en este momento ni fu ni fa. ## Flujo completo Así que ya tenemos el flujo completo: Añadir la opción de audio-audio me pareció una buena opción para conseguir ese efecto de «hablar» con tu asistente, profesor, mascota o como se quiera vender al avatar. Pero en este caso no me compliqué con servicios TTS como Whisper de OpenAI, en este caso al ser navegador tiré de lo nativo y listo, como POC es suficiente y ahorramos en costes. ## Power Ups Algunos superpoderes a añadir a esta prueba de concepto: - ElevenLabs para la voz. - Tu propio modelo 3D, por ejemplo generado con IA con MeshyAI. Imagina que el que habla eres tu o tu equipo, nada más «personal» que el chatbot de una web sea el creador, con su tono y demás – uuuh esto está pidiendo una V2 a gritos 💡 - Añadir más interacciones con el modelo, por ejemplo cuando haya acertado, que lo celebre. ## V 0.1 Al modelo en 3D no me podía resistir a así trasteando MeshyAI, tan fácil como añadirle una foto mía, indicar donde están las articulaciones y tras varios intentos de movimientos fallidos al estilo demogordons, me he quedado con el mejor de los peores 😳 Ahora imagina que le enchufas por RAG la info de tu empresa, de tu academia, o tu CV y tienes el About Me más molón del mundo mundial !! Va video del antes y del después. ## ¿El plugin? El código del plugin WordPress para que puedas trastearlo y evolucionarlo lo tienes en la newsletter 👇🏻 👇🏻 👇🏻 --- ## TaskQuest: Gamifica tu ClickUp URL: https://ablancodev.com/build-in-public/taskquest-gamifica-tu-clickup/ ### El Problema: El «Done» no me daba dopamina Hablemos claro. El momento más esperado del día de un desarrollador es mover la tarjetita de «Doing» a «Done». O darle al tick verde en ClickUp. Pero seamos sinceros… es decepcionante. Un pequeño «pop» y ya está. ¿Dónde está la gloria? ¿Dónde está el desafío? ¿Dónde está la fanfarria? La semana pasada, harto de la burocracia de cerrar tareas, pensé: **«Si voy a trabajar, al menos quiero que sea un reto»**. Y así, en una tarde de *vibe coding* y cafeína, nació **TaskQuest**. ### ¿Qué demonios es TaskQuest? * Es la extensión de Chrome más anti-productiva (y a la vez satisfactoria) que vas a instalar. Su función es simple: **Secuestra tu ClickUp.** - Intentas darle al botón de «Completar Tarea». - **¡QUIETO AHÍ!** 🛑 Nada de cerrar tickets gratis. - Aparece un overlay* con un minijuego (tipo Flappy Bird o Marcianitos). - ❌ **Si pierdes:** La tarea se queda abierta. Tu jefe te mira mal. Tu dignidad cae. - ✅ **Si ganas:** Se dispara el evento, la tarea se cierra y sientes que eres el rey del mundo. **Mira el desastre en acción:** *(Aquí incrustas el vídeo de Screen Studio)* ### La parte técnica: Cómo hackear una SPA sin morir en el intento *Nota: Si no eres dev, puedes saltarte esto e ir a la descarga. Si eres dev, esto te interesa.* Lo gracioso de **TaskQuest** no es el juego (es un Canvas básico de JS). Lo difícil fue la «fontanería» para que funcionase en una web moderna como ClickUp. ClickUp es una SPA (*Single Page Application*) hecha en React. Esto significa que el botón de «Completar» no existe cuando cargas la página; aparece y desaparece dinámicamente según navegas. **El reto técnico:** Si usas un document.querySelector normal al cargar la página, te devolverá null y tu extensión fallará. **La solución:** Tuve que implementar MutationObservers para vigilar el DOM de ClickUp en tiempo real. La extensión «espía» la web y, en el milisegundo exacto en que React renderiza el botón de cerrar tarea, le inyecta mi *Event Listener* malicioso que bloquea la acción original. Es sucio. Es complejo. Pero funciona de maravilla. ### Descargas (Elige tu píldora) He preparado dos opciones. Una para los que quieren sufrir jugando y otra para los que quieren aprender cómo lo hice. #### 🎮 Para Usuarios (Gratis) Bájate la extensión, cárgala en tu Chrome y empieza a gamificar tu sufrimiento. *(Nota: Requiere activar «Modo Desarrollador» en Chrome).* #### 👨‍💻 Para Desarrolladores (Source Code) ¿Quieres crear tu propia extensión que modifique webs como Gmail, Twitter o LinkedIn? Te ahorrarás unas 10-15 horas de pelearte con la documentación de Chrome Manifest V3 y la inyección de scripts en SPAs. **¿Qué incluye el ZIP?** - 📂 Proyecto completo (HTML/JS/CSS). - 📜 El manifest.json V3 configurado correctamente. - 🧠 El script de inyección con MutationObserver (la joya de la corona). - 📝 README en español explicando dónde tocar para adaptarlo a otra web. ### Descarga gratis la extensión Tu Email --- ## ClickUp Wrapped, genera tu resumen anual URL: https://ablancodev.com/build-in-public/clickup-wrapped-genera-tu-resumen-anual/ *Un resumen anual de tu trabajo, al estilo Spotify Wrapped*, siéntete orgulloso de lo que has currado, y de lo que no 🙃 ### La idea Todos los años, cuando llega el **Spotify Wrapped**, acabamos compartiendo estadísticas que nunca habíamos pedido… pero que nos encanta ver. Y como casi todo lo que hago últimamente gira alrededor de **ClickUp**, me surgió la pregunta: ** ¿Y si pudiéramos tener un “Wrapped” de nuestro trabajo? De ahí nace ClickUp Wrapped**: un pequeño portal donde, conectando tu cuenta de ClickUp, puedes generar un resumen visual de tu actividad (tareas, estados, tiempos, etc.) y compartirlo fácilmente. No es un producto grande ni complejo. Es un experimento. Un proyecto de laboratorio. * ### El origen: algo solo para el equipo La primera versión no tenía ninguna ambición pública. Era un script **local**, pensado únicamente para el equipo: - Conectar a la API de ClickUp - Sacar algunas métricas interesantes - Mostrar un resumen visual sencillo Nada más. Sin login, sin base de datos, sin usuarios.**Solo código, una API Key y curiosidad. Pero cuando lo compartí internamente, pasó lo inevitable: “¿Esto lo puede usar más gente?” ### De script local a mini SaaS Ahí fue cuando decidí darle una vuelta y convertirlo en algo reutilizable. La idea era clara: - Que cualquiera** pudiera generar su ClickUp Wrapped - Sin registros - Sin almacenar datos sensibles - Manteniendo el proyecto **lo más simple posible** Así que el enfoque fue: ** Configuras → generas → compartes → listo* ### Cómo funciona (flujo general) El flujo del proyecto es bastante directo: - El usuario introduce: API_KEY de ClickUp - Algunos datos de configuración (workspace, fechas, etc.) - El sistema: Consulta la API de ClickUp** - Procesa los datos en tiempo real - Se genera el Wrapped: Métricas - Pantallas tipo “story” - Resumen visual - Opcionalmente: Se genera una **versión estática en HTML** - Lista para compartir sin volver a regenerar nada Este último punto es clave. ### Compartir sin recalcular todo Uno de los problemas que quería evitar era: - Reconsultar la API constantemente - Volver a procesar los datos cada vez que alguien comparte su Wrapped La solución fue simple y efectiva: Cuando el usuario decide **compartir su Wrapped**, el sistema: - Renderiza el resultado final - Genera un **HTML estático** - Lo guarda como una página independiente De esta forma: - El enlace es rápido - No depende de la API - No expone la API Key - No hay cálculos repetidos Es literalmente una “foto” del Wrapped en ese momento. ### Stack técnico Nada raro, nada de moda innecesaria. El stack es deliberadamente sencillo: - **PHP** Conexión con la API de ClickUp - Procesado de datos - Generación del HTML final - **JavaScript** Interacciones - Navegación tipo “slides / stories” - **CSS** Estilos - Animaciones simples - **Sin base de datos** Ni usuarios - Ni sesiones persistentes - Ni almacenamiento de datos personales Todo vive en memoria o en archivos estáticos cuando se comparte. ### ¿Por qué sin base de datos? Fue una decisión consciente. Para este proyecto: - No necesito persistencia compleja - No quiero gestionar usuarios - No quiero almacenar tokens - No quiero aumentar superficie de problemas Menos piezas = menos mantenimiento. Además, encaja perfectamente con la filosofía del proyecto: ** Genera → disfruta → comparte → desaparece ### Lo que más me gustó del proyecto Más allá del resultado, lo interesante fue el proceso: - Empezó como una herramienta interna - Evolucionó de forma natural - Se mantuvo pequeño y controlable - Me obligó a pensar en: Flujo - Privacidad - Performance - Simplicidad Es uno de esos proyectos que no intentan “escalar”, sino resolver algo concreto y divertido**. ### Y ahora te toca a ti generar tu wrapped 2025 Generar resumen anual --- ## Adios RAG, hola Gemini File Search Store URL: https://ablancodev.com/essays/adios-rag-hola-gemini-file-search-store/ ## **Integración real de Google File Search con WooCommerce** Si en el anterior artículo hablaba de cómo montar tu propio RAG para búsqueda de productos en Woocommerce, viene Google y añade una capa de abstracción para que nos olvidemos de embedings, chunks y demás, lanzando **Gemini File Search**, una nueva capacidad de Gemini que permite realizar búsquedas semánticas sobre datos propios de manera rápida y precisa. Es como tener un “mini Google” entrenado específicamente con tus documentos… pero totalmente integrado en cualquier proyecto. En este post quiero mostrar un ejemplo real:**Cómo he conectado WooCommerce con Google File Search** para permitir búsquedas inteligentes de productos de un WooCommerce ## ¿Qué es File Search? Es una herramienta que permite a los modelos Gemini buscar en documentos privados que tú subes a un **File Search Store**.**Los modelos ya no responden sólo con “conocimiento general”: ahora pueden usar tus propios datos de forma controlada y con grounding fiable. Es lo que viene siendo un RAG, para poder dar contextos muchos más grandes a los LLM, pero ahora con una capa de abstracción para que te olvides de bajar al barro. Este es el flujo con el que lo describen desde Google en su página de documentación. Esto abre muchas posibilidades: - motores de búsqueda internos - catálogo de ecommerce inteligente - soporte técnico automatizado - onboarding empresarial - chatbots conectados a tu base real de información ## ¿Qué hace la integración con WooCommerce? Pues claro, ver esto y no trastearlo hubiese sido un pecado, así que manos a la obra, y a usarlo en nuestro buscador semántico en WooCommerce (yo lo uso ahí, pero puedes usarlo como referencia para conectarlo con cualqueir otra fuente de documentación que se te ocurra). Cada producto de la tienda se convierte en un documento que Gemini puede encontrar, interpretar y recomendar. Cuando el usuario busca algo, el sistema: - Le envía la consulta al modelo gemini-2.5-flash - Usa la herramienta file_search** para buscar dentro del Store del sitio - Obtiene los groundingChunks con la info del producto - Extrae el **ID del producto.** - Recupera la información real desde WooCommerce a partir de dicho ID. - Devuelve resultados precisos, sin alucinaciones Así si buscamos algo como: «No sé donde poner la tele», donde con la búsqueda tradicional es casi seguro que obtuvieses cero resultados, ahora el sistema consigue hacer una búsqueda semántica y devolverte el mueble para televisión que tenemos. ** Ejemplo de búsqueda semántica ## 🔧 Flujo técnico explicado ### 1. Creación del File Search Store** Al activar el plugin se crea automáticamente un Store con el nombre del sitio.**Este Store será la base de datos donde se indexarán todos los productos. POST /v1beta/fileSearchStores Un Store es el contenedor donde tendremos distintos documentos con información. En nuestro caso será «la carpeta» donde estarán los documentos de nuestros productos (un doc por producto). ### 2. Sincronización de productos** #### Cuando un producto es nuevo Se crea un documento usando upload resumible: - Start upload - Subir el contenido (texto plano estructurado) - Guardar el document_id devuelto por la API El contenido contiene: - nombre - descripción - precio - SKU - categorías - URL - ID #### Cuando un producto se actualiza - Se elimina el documento antiguo (si existe) - Se crea uno nuevo con la información actualizada Si, es raro, pero no tienen forma de actualizar un documento, así que toca hacerlo en dos pasos y supongo que consumiendo más recursos, pero … es lo que hay cuando la API no nos da la mejor de las soluciones. ### **3. La búsqueda «simple»** Para consultas como “mueble”, se llama a: POST /models/gemini-2.5-flash:generateContent Con la herramienta: "file_search": { "file_search_store_names": ["{store_id}"] } Gemini devuelve grounding chunks con fragmentos reales de los productos.**El sistema procesa estos chunks, extrae IDs y devuelve los productos. Lo que viene siendo una búsqueda semántica al estilo de lo que montábamos con el RAG. Screenshot ### 4. Búsqueda con IA (AI-Powered Search)** Para consultas abiertas como: ** “donde pongo la tele” El modelo genera una respuesta natural y, además, devuelve grounding chunks.Así entregamos: - una recomendación explicada por IA - productos reales recuperados con file search - cero alucionaciones (porque todo está anclado a documentos) Vaya, que esta segunda búsqueda es dar una capa de verbose a la primera, que quizás esté más orientada a un chatbot o algo así, pero …. tenía que probarlo 🤓 ### 5. Eliminación de producto** Cuando borras un producto en WooCommerce, también se borra su documento del Store, manteniendo todo consistente. ## Resultado final Con esta integración, WooCommerce obtiene: - búsqueda semántica instantánea - mejores recomendaciones - menos frustración para el usuario - IA conectada a datos reales - contenido totalmente generado a partir de tus productos En resumen: **una experiencia de búsqueda moderna y potente sin depender de plugins de terceros o motores externos**. ## Si quieres probarlo 👇🏻 Te dejo el repo del plugin en Github: https://github.com/ablancodev/google-file-search --- ## Como creé un buscador semántico para WordPress URL: https://ablancodev.com/build-in-public/como-cree-un-buscador-semantico-para-wordpress/ ## WordPress + RAG + ChatGPT = Buscador Semántico Llevo tiempo dándole vueltas a crear un buscador semántico para la web, porque en la era de la IA, la mayoría nos estamos acostumbrando a escribir mal en los buscadores e inputs y que eso ya no sea un problema, ya que los LLMs «lo entienden». Pero claro, te vas a cualquier eCommerce o blog, y si escribes algo mal (un par de letras traspuestas) o algo no exacto, te encuentras con el típico: **«No posts were found»** Lo primero que viene a la cabeza es un wrapper de ChatGPT y todos contentos, pero … - ¿Cómo le paso la info de mis productos o posts? Tengo unos poquitos 😳 - Y si la gente se dedica a preguntarle por el tiempo 🌤️ - ¿Money? 💵 Vamos a usar un RAG (es el proceso de optimización de la salida de un modelo de lenguaje de gran tamaño, de modo que haga referencia a una base de conocimientos autorizada fuera de los orígenes de datos de entrenamiento antes de generar una respuesta. — by AWS) para darle el conocimiento que nosotros queremos, el de nuestros artículos o productos. ** El proceso será: - Troceamos nuestros posts y creamos un embedding (vector para base de datos vectoriales, que nos lo dará chatGPT) - Eso va a Pinecone (base de datos vectorial). Ya tenemos nuestra base de conocimiento. Ahora el proceso de búsqueda de un usuario: - Inserta para la Búsqueda un término o frase - Se la mandamos a chatGPT para que nos de el embedding. - Hacemos la busqueda en Pinecone. Esto nos dará los trozos de texto más «cercanos» en significado a nuestra búsqueda. - Ya podemos mostrar a los usuarios los posts resultado, hasta con un % de similitud. La idea de resultado sería algo como esto: Buscador semántico Esto no sólo nos dará un buscador semántico, sino que al tener el contenido ya preparado para darle al LLM de turno la info que es relevante, podremos hacer que el usuario pueda «hablar» con o sobre el contenido, llevando el buscador a un flujo de conversación. Y aunque la idea inicial fue un buscador semántico, para ecommerce, se puede aplicar a por ejemplo la wiki de la empresa, donde podremos preguntar por los procesos, con quien tengo que contactar para X o como tengo que hacer Y … o si tienen un membership site, puedes dar esta funcionalidad sólo a los usuarios miembros, dándoles un valor añadido, o si tienes un LMS, los alumnos tendrán otra forma de buscar y consultar el temario … sólo tienes que pensar tu caso de uso y verás las posibilidades que tiene. ## La solución Opté por crearlo a modo de plugin para WordPress**, para que cualquiera pueda instalarlo y tener un buscador que te flipas en su web/ecommerce/intranet/… El usuario necesita 3 puntos: - El plugin, que lo he puesto en mi tienda, pero si necesitas una prueba pídemela: https://growthplugins.com/plugin/rag-wordpress-ai-powered/ - Una cuenta en Pinecone, donde tendremos nuestra BBDD vectorial: https://www.pinecone.io/ - Una API KEY de chatGPT (no te preocupes, los precios de la API ya empiezan a ser ridículos): https://platform.openai.com/api-keys Settings Una vez tenganos todo enganchado (es más fácil de lo que parece), es hora de empezar a indexar contenido. Indicamos los custom post types que queremos enganchar y cuando se creen o editen, serán troceados, creados los embeddings y metidos en Pinecone. Indexed Ya estamos casi casi, ahora es usar el shortcode en una página o donde querramos y tendremos nuestro buscador: Buscador Y ya todo listo para que el usuario empiece a encontrar nuestros productos o información y adios a los No posts were founds, 🥳 🥳 🥳 🥳 🥳 Extra … y quien dice un buscador, dice un chatBot, pero recuerda, con nuestro contenido. Chat ## **Monta tu buscador semántico** El Plugin --- ## Cómo creé mi inspector de Ideas URL: https://ablancodev.com/build-in-public/como-cree-mi-inspector-de-ideas/ Siempre ando buscando nuevas ideas a implementar o plugins WordPress que crear para mi tienda GrowthPlugins, y qué mejor que escuchar a los usuarios para encontrar sus problemas, sus necesidades. El proceso era siempre el mismo, buscar por foros, buscar por reddit .. en resumen, tiempo que no tengo, así que era hora de ponerse manos a la obra. Empecemos por un MVP, usemos Reddit como fuente de conocimiento, y ya luego iremos añadiendo otras fuentes. **Herramienta interna + primera iteración = Claude Code 🦸🏻‍♂️** ## Making of ### Primera iteración Creamos la base, proyectos, que pueden tener distintas fuentes, de las cuales vamos a monitorizar una serie de tags, que se convertiran en posibles oportunidades de ideas. De momento fuente Reddit, pero que sea escalable a posibles futuras fuentes. ### Segunda iteración Arregla una pocas de cosas, porque ni se puede activar el plugin 😔 ### Tercera iteración Aunque las iteraciones tienen cierto grado de scoring por el match con las tags, es hora de que un LLM las analice, así que vamos a tirar de mi «librería» Simple LLM PHP https://github.com/ablancodev/simple-llm-php ### Cuarta iteración Arregla cosas porque la mitad te las has cargado 😔 ### Quinta iteración Es hora de que la máquina vaya sola, así que cron para todo, pero con cuidado, que no me quiero arruinar pagando API de OpenAI. ## MVP listo y corriendo 🎉 --- ## Montar un LLM sin filtros URL: https://ablancodev.com/essays/montar-un-llm-sin-filtros/ Voy a montar un LLM sin filtros, pero en Ollama. El problema es que la mayoría de HuggingFace no soportan el formato de Ollama y hay que “convertirlos” Vamos a usar: **openhermes-2.5-mistral-7b** ### 1. Descarga el modelo GGUF Ollama no permite montar directamente LLMs de HuggingFace, pero los .GGUF si, y en HaggingFace hay unos pocos. En tu servidor, ejecuta: mkdir -p ~/ollama/models/openhermes cd ~/ollama/models/openhermes wget <https://huggingface.co/TheBloke/OpenHermes-2.5-Mistral-7B-GGUF/resolve/main/openhermes-2.5-mistral-7b.Q4_K_M.gguf> -O model.gguf ### 2. Crea el Modelfile correcto En el mismo directorio, crea un archivo Modelfile con este contenido válido para Ollama: sudo nano Modelfile Y le ponemos este contenido: FROM mistral PARAMETER efficient FILE model.gguf ### 3. Contenedor Docker Ollama Si no tenemos aún el docker montado: docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama ### 4. Entra al contenedor y crea el modelo Accede al contenedor: docker exec -it ollama bash Y dentro ejecuta: cd /models/openhermes ollama create openhermes -f Modelfile ### 5. Ejecuta el modelo docker exec -it ollama ollama run openhermes ### 6. Usar vía API curl <http://localhost:11434/api/generate> -d '{ "model": "openhermes", "prompt": "Describe una historia cruda y sin censura sobre el futuro de la humanidad.", "stream": false }' Y listo ya tenemos un LLM libre de censura para usar y que Dios nos pille confesados de las barbaridades que se te ocurrirán hablar con él. --- ## Desarrollando un Asistente de Rutas con IA: De la Idea al MVP URL: https://ablancodev.com/build-in-public/desarrollando-un-asistente-de-rutas-con-ia/ Ayer tocaba entrega de paquete de Amazon en casa (para variar 🤪), pero no estábamos, y lo primero que pensé fue: «vaya putada para el repartidor». Y con esto me acordé de mi época de instalador, donde teníamos que hacer una ruta por Andalucía instalando ordenadores, tabletas … organizando la ruta el día anterior, llamando a clientes … Esto hoy en día se puede mejorar 💯, ahora es cuando alguien con dos dedos de luces haría research y se encontraría mil soluciones en el mercado, pero qué sería de la vida si no nos pusiésemos a picar código del tirón !! Vamos a atacar el problema en principio desde 2 puntos: - Mejora de la ruta física. Es decir, que si tengo que hacer 10 entregas, que se me ordenen de la forma más eficiente posible. - Minimizar las posibles entregas fallidas, preavisando a los clientes, para que nos puedan confirmar o rechazar si van a estar en casa. ## Optimizando la ruta Empezamos con el paso de optimizar ruta, y como no, vamos a usar la aplicación por excelencia de mapas, Google Maps, y su API. Las 2 APIs que vamos a tener que activar son: - Maps JavaScript API - Directions API Desde Google Cloud Console podrás crear tu proyecto, y habilitarle ambas APIs Teniendo nuestra herramienta de optimización de rutas preparadas, es hora de empezar a picar código. Vamos a empezar a simplificar, y la forma más sencilla de poder gestionar ubicaciones o paradas, es usando un custom post type en WordPress, al que llamaremos ‘delivery_address’, con atributos como el título y la dirección en formato texto en principio tenemos para esta versión. Vamos a atacar la llamada a la API https://maps.googleapis.com/maps/api/js, lo que nos permitirá obtener el mapa con las ubicaciones ya ordenadas y optimizadas (¿sencillo no?, porque recuerdo la que tuve que liar en una práctica de la facultad **a few years ago** 👴🏻 para optimizar rutas en un espacio 2D) <div id="map" style="height: 500px; width: 100%;"></div> <script> function initMap() { var map = new google.maps.Map(document.getElementById('map'), { zoom: 6, center: { lat: 40.4168, lng: -3.7038 } // Centro en Madrid por defecto }); var directionsService = new google.maps.DirectionsService(); var directionsRenderer = new google.maps.DirectionsRenderer(); directionsRenderer.setMap(map); directionsService.route({ origin: "<?= urldecode($origin) ?>", destination: "<?= urldecode($destination) ?>", waypoints: [ <?php foreach ($addresses as $address) : ?> { location: "<?= $address ?>", stopover: true }, <?php endforeach; ?> ], travelMode: google.maps.TravelMode.DRIVING, optimizeWaypoints: true }, function(response, status) { if (status === "OK") { directionsRenderer.setDirections(response); // mostramos más información var route = response.routes[0]; var summaryPanel = document.getElementById('directions-panel'); summaryPanel.innerHTML = ''; for (var i = 0; i < route.legs.length; i++) { var routeSegment = i + 1; summaryPanel.innerHTML += '<b>Ruta ' + routeSegment + '</b><br>'; summaryPanel.innerHTML += route.legs[i].start_address + ' a '; summaryPanel.innerHTML += route.legs[i].end_address + '<br>'; summaryPanel.innerHTML += route.legs[i].distance.text + '<br><br>'; // tiempos var time = route.legs[i].duration.text; summaryPanel.innerHTML += 'Tiempo estimado: ' + time + '<br><br>'; } } else { alert("Error al calcular la ruta: " + status); } }); } </script> <script async defer src="https://maps.googleapis.com/maps/api/js?key=<?= $apiKey ?>&callback=initMap"></script> <div id="directions-panel"></div> Obtendremos un mapita como este, además de más info como distancias y tiempos de recorridos. Parece que una primera versión de la parte de ruta la tenemos ya 🥳 🥳 🥳 ## Preavisando a los clientes Entre la información que nos da la API de Google al crear la ruta, están los tiempos de trayectos, por lo que si consideramos que vamos a salir ya (o quizás podamos indicar una hora y día de salida, así los clientes tendrán más tiempo para confirmar su precencia), y teniendo en cuenta que el tiempo de parada va a ser de 15min por ejemplo, tendremos las distintas horas de llegada estimadas. // ... for (var i = 0; i < route.legs.length; i++) { // tiempos var time = route.legs[i].duration.text; times.push(time); } var now = new Date(); var totalSeconds = now.getHours() * 3600 + now.getMinutes() * 60 + now.getSeconds(); var arrivalTimes = []; times.forEach((time, index) => { // Procesar el formato "Xh Y min" const timeStr = time.replace(' min', '').split('h '); const hours = parseInt(timeStr[0]) || 0; const minutes = parseInt(timeStr[1]) || 0; // Añadir el tiempo de viaje más 15 minutos de parada totalSeconds += (hours * 3600) + (minutes * 60) + (15 * 60); // Calcular la hora de llegada const arrivalHours = Math.floor(totalSeconds / 3600) % 24; // Usamos módulo 24 para formato 24h const arrivalMinutes = Math.floor((totalSeconds % 3600) / 60); // Formatear la hora de llegada arrivalTimes.push( `${arrivalHours.toString().padStart(2, '0')}:${arrivalMinutes.toString().padStart(2, '0')}` ); console.log(`Parada ${index + 1}: ${time} -> Llegada: ${arrivalTimes[arrivalTimes.length - 1]}`); }); // ... Parece que ya tenemos la data necesaria para esta segunda funcionalidad, por lo que podemos empezar a notificar a los clientes. Atacar a la API de Whatsapp directamente quedará para futuros episodios, ya que Meta se ha lucido con ella, no como mi querido Telegram que es una maravilla a nivel de potencial de desarrollo … pero no todo el mundo tiene Telegram 😓 Una alternativa que parece bastante potente es Twilio, ya que no sólo es una capa por encima de Whatsapp facilitando las integraciones, sino que tiene SMS y otros sistemas, además de tener integraciones con Zapier, lo cual siempre es un aliciente a usarlo. Y ya puestos a hacernos los modernos, podríamos tirar de los agentes de IA de ElevenLabs, que podrían llamar a los usuarios y confirmar o no su asistencia., aunque en este caso quizás sea venirnos muy arriba. Pero en esta primera fase en la que estamos en exploración de mercado y validación de la idea, y dado que la cuesta de Enero llega casi hasta Junio, es hora de reducir costes y empezar a escuchar a los usuarios lo antes posible, así que atacaremos a los clientes con el medio de comunicación más tradicional y efectivo … el email 📩 Para ellocrearemos un endpoint donde enviar la data ( /wp-json/deliveria/v1/notify ) y que el servidor pueda enviar los emails Con esa notificación podremos jugar, por ejemplo haciendo que el botón de «No voy a estar en casa» notifique en un endpoint, y así se pueda reajustar la ruta, o que sea un formulario indicando cuando voy a estar y así reajustar ruta, o un simple tel: y que nos llame directamente (ya que a veces aunque no esté alguien hay opciones de entrega a vecinos y demás). Aquí la imaginación al poder 🚀 Y con esto tendríamos una primera versión MVP base sobre la que ir iterando, validando con clientes, tanto los repartidores, como los usuarios finales. Permitiéndonos ahorrar tiempo ⏱️, dinero 💰 y frustraciones 😓 --- ## “Pinta” páginas WordPress con IA URL: https://ablancodev.com/build-in-public/pinta-paginas-wordpress-con-ia/ Esto de que la IA sea capaz de interpretar imágenes abre un abanico bastante grande de oportunidades y automatizaciones. Si nos vamos al mundo web, con mi querido WordPress y su sistemas de bloques Gutenberg, vemos que tenemos un sistema de componentes ya predefinido con el que podremos montar prácticamente cualquier web. A esto le unimos que yo soy muy de tirar a inicios de lápiz y papel, a la vieja escuela, sobre todo diagramas que es lo que queda más en mi tejado, pero veo que mis compis de UX también dejan viajar la imaginación más facilmente con lapiz y papel. Pues ya tengo los 2 ingredientes a unir con este wrapper de GPT, un sistema con el que a partir de un boceto como entrada, tengamos como salida una página WordPress 💥 El plugin tendrá una pequeña sección de admin, donde poder indicar la API KEY de OpenAI, ya que será ChatGPT nuestra “IA”, y al tratarse de una funcionalidad general de parte de editores y redactores, la haremos toda desde el admin de WordPress en su propia sección. Inicialmente empecé con la opción sólo de subir una imagen, ya que así podemos controlar exáctamente qué contenido estamos pasando, pero tras varias pruebas pude ver que era un poco rollo el tener que hacer la imagen en otro software, o en un papel, echarle una foto y subirla, por lo que añadí la opción de <canvas> HTML5, así podremos pintar directamente desde la sección ✍🏻 Lo de usar canvas mola, pero puede dar muchos quebraderos de cabeza inesperados, que fue lo que me pasó, cuando pasaba la imagen y ChatGPT no “entendía” nada 😫 … y el problema era que no le había dado un fondo al canvas !! (una hora de mi vida perdida por un fondo) Solventado el problema, y pasando la imagen a ChatGPT via API, basta con decirle que quieres que use el sistema de bloques de WordPress, que ya él conoce, y que te genere el contenido, creamos la página y listo ✅ $prompt = 'Dame el content de una página wordpress basada en los bloques por defecto de WordPress, que represnete lo que veo en la imagen. Date el contenido para copiar-pegar en el editor modo código del admin. Las imágenes que necesites, usa urls de https://placehold.co/ .Devuelveme sólo el content del editor en texto plano, no verbose.'; $api_key = 'sk-proj-q4XG1Jj7EOPzJXA7UHHfT3BlbkFJTyqQGCxxxxx'; $response = Ablancodev_Openai::openai_api_request_image($prompt, $image_url, $api_key); $response = str_replace('```html', '', $response); $response = str_replace('```', '', $response); // creamos una página con el contenido $post = array( 'post_title' => 'Página creada por UX To Code', 'post_content' => $response, 'post_status' => 'publish', 'post_author' => 1, 'post_type' => 'page' ); $post_id = wp_insert_post($post); Fijaros que ni el prompt está demasiado currado, por lo que con un poco de iteración seguro que conseguimos resultados muy muy profesionales. El repositorio del proyecto que podéis descargar completamente gratis: https://github.com/ablancodev/uxtocode … y el video demo, que sé que es lo que más mola 😎 Este MVP genera páginas con contenido dummy e imágenes de placehold.co, pero piensa en su evolución natural, creando contenido adaptado según un prompt inicial e imágenes para ello 🚀 --- ## TellMe 🎙️ ¡Convierte tus ideas en artículos! 🚀 URL: https://ablancodev.com/build-in-public/tellme-convierte-tus-ideas-en-articulos-wordpress/ 🚀 ¡Atención a todos los que aman hablar sin parar y odian sentarse a escribir! 🚀 Si eres de los míos, de los que habla por los codos, de los que te gusta mucho más contar que sentarte delante de la pantalla o con un folio en blanco a escribir, ¡tengo la solución perfecta para ti! 🎙️📝 Imagina poder convertir tus notas de voz en artículos para tu blog, redes sociales o cualquier medio, ¡sin tener que escribir una sola palabra! ¿Increíble, verdad? 😲 ## ¿Cómo lo hacemos? ### El audio 🗣️ Lo primero es grabar tu voz, pero no te preocupes, no tendrás que hacerlo delante de un ordenador. Lo primero que se me ocurrió fue cómo no, crear un shortcode o página en el admin de WordPress desde donde poder grabar el audio, pero claro, volvemos a estar delante de un ordenador, aunque podemos grabar con el micro estando dando vueltas por el cuarto, que al es algo, pero no me terminaba de convencer. ¡Imagina grabar tus ideas en plena naturaleza, paseando al perro o relajado en el sofá! 🌳🐕🌙 Pues ¡manos a la obra y crear una aplicación en Swift de iOS! 📱 Con un simple botón de grabar, parar y enviar, podrás enviar tu audio a un endpoint de tu API en WordPress. Ya tenemos nuestro audio, pasamos a crear el contenido, pasamos a WordPress ✍🏻 ### La redacción de texto ✍🏻 Lo primero de todo es poder recoger el audio que enviamos, y para ello creamos un nuevo endpoint: // creamos un endpoint donde recibiremos un fichro de audio add_action('rest_api_init', 'tellme_register_routes'); function tellme_register_routes() { register_rest_route('tellme/v1', '/audio', array( 'methods' => 'POST', 'callback' => 'tellme_audio' )); } La primera fue que tras recibir el audio, creáramos el artículo, pero tras varias pruebas y pensándolo bien, teniendo en cuenta que no siempre soltamos la chapa de golpe, porque nos interrumpen, porque nos quedamos en blanco y demás, me iba a encontrar con muuuchos posts que en realidad no iban a ser válidos, así que mejor guardarlos y desde el admin poder generar el artículo cuando quiera. Llega el momento mágico de la inteligencia artificial. Con la librería Whisper de OpenAI, convertiremos tu audio en texto. $url = 'https://api.openai.com/v1/audio/transcriptions'; // hacemos la llamada a la API $curl = curl_init(); $fields = array( 'model' => 'whisper-1', 'file' => new CURLFile($file) ); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_POST, TRUE); curl_setopt($curl, CURLOPT_POSTFIELDS, $fields); curl_setopt($curl, CURLOPT_HTTPHEADER, array('Authorization: Bearer ' . get_option('tellme_apikey'))); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true ); $data = curl_exec($curl); Y ya tenemos nuestra transcripción, nuestro audio en texto 🎉 Así que nada, creamos un nuevo post en borrador, pasándole esta transcripción como contenido y listo !!, ya tenemos cómo redactar contenido desde audio, desde el campo, desde donde seamos más productivos, desde donde nos dé la gana 😎 ### La guinda 🤖✨ Pero ¿por qué conformarnos con eso? ¡Con ChatGPT, daremos un toque adicional a nuestros artículos! Vamos a refinar (que no siempre mejorar) nuestro contenido, y podemos darle una pasada por la API de ChatGPT, para que nos lo redacte tomando como base el texto transcrito, pudiendo indicarle en el prompt, por ejemplo que le de un toque divertido, formal o que mejor se adapte a nuestro tono de voz. $url = 'https://api.openai.com/v1/chat/completions'; Y ahora vamos a pedirle a Dall-e que nos cree una imagen destacada para el artículo: $url = 'https://api.openai.com/v1/images/generations'; Y ya tenemos la versión «mejorada» de nuestra divagación, ¿la versión final? probablemente no, pero si el 60-70’% de nuestro post, a mejorar. Este post, por ejemplo, lo estoy «escribiendo» mientras paseo por el campo con mi amigo Lobo. ¡Aprovechando cada momento para plasmar mis ideas! 🐾💭 Os dejo el repositorio de GitHub tanto con el código del plugin de WordPress, como de la app IOS. Por si quieres montártelo y que dejes de perder en el olvido esas ideas que sólo surgen en los mejores momentos. **¡Espero que lo disfrutes tanto como yo! 💻🚀** --- ## Traduciendo videos de Youtube con IA URL: https://ablancodev.com/build-in-public/traduciendo-videos-de-youtube-con-ia/ No sé tu nivel de inglés, pero mi «english’s town» tiene sus limitaciones, y en ocasiones ver algunos videos en Youtube en inglés son más un suplicio que un disfrute, y hacen que parte de mi atención se pierda en la traducción. ### Así que ¿y si montamos un traductor de videos de youtube? Veamos primero el flujo antes de empezar a picar código …. 😈 dice que mejor picar código, pero 😇 dice que mejor sentarse a plantear la solución …. gane de momento 😇 - Usaremos la librería Pytube de Python para poder extraer el audio del video de Youtube 🐍 import pytube youtubeUrl = "https://www.youtube.com/watch?v=fHyh0D7IENs" youtubeVideo = pytube.YouTube(youtubeUrl) audio = youtubeVideo.streams.filter(only_audio=True).first() audio.download(filename='videoyou_aldi.mp4') - Una vez tengamos el audio en inglés, lo pasaremos a texto con Whisper de OpenAI. 🎧 client = OpenAI(api_key = 'sk-xxxxxxxxxxx') speech_file_path = "videoyou_aldi.mp4" audio_file = open(speech_file_path, "rb") response = client.audio.transcriptions.create( model="whisper-1", file=audio_file, response_format="text" ) print(response) - Ya tenemos la transcripción del video, es hora de traducirlo, y para ello usaremos al hermano mayor de OpenAI, ChatGPT. 🤖 response = client.chat.completions.create( model="gpt-4", messages=[ {"role": "system", "content": "You are a helpful spanish translator. Not verbose, only give me the tanslation.Please translate this text to spanish:"}, {"role": "user", "content": response} ] ) print(response.choices[0].message.content) - Bravo mi arma olé 💃 ya estamos en español. que alguien lo lea 🗣️!!! Para ello tiramos de Text_to_Speech de OpenAI. speech_file_path = "videoyou_aldi.mp3" response = client.audio.speech.create( model="tts-1", voice="alloy", input=response.choices[0].message.content ) response.stream_to_file(speech_file_path) ### 💥 El resultado Así podemos pasar de míticos videos como el del gran Steve Jobs A tenerlo hablando en español, así que ya sabes, o te pones el video le quitas el audio y le das al play, o te lo llevas en modo Podcast y a dejarte inspirar 💪🏻 --- ## >_ 006: Creando un chatBot en Telegram con chatGPT URL: https://ablancodev.com/build-in-public/006-creando-un-chatbot-en-telegram-con-chatgpt/ ## ¿Qué tal sería chatear con Papa Noel? Así llevo desde ayer 🥹 y pues claro … manos a la obra. La idea es usar ChatGPT para darle personalidad a nuestro Papa Noel, y luego crear un chat mediante el que intercambiar mensajes. La idea es sencilla: * #### Web Primero pensé en un formulario web, y montar ahí el chat, vale, guay, control total, pero aburrido de narices, y más que yo para el diseño soy un cero pelotero, así que había que pensar algo más divertido. #### ¿Whatsapp? Ehhh, whatsapp mola 😎 Me pongo a mirar su API, a crearme la app en Meta para poder empezar, y empiezan los problemas: … que si añadir forma de pago … que si justificar tu cuenta de empresa … que si pago por uso puff pereza máxima, y encima pasar por caja, además de la caja de OpenAI, muchas cajas son esas para un pobre como yo, «I have not money !!!» #### Telegram !! Nunca he trasteado Telegram pero colegas me han dicho que a nivel API y dev está muy bien pensado. Pues nada a telegramear se dijo. Página con doc para crear un bot: https://core.telegram.org/bots Chula, eh?, pues espera que crear un primer bot es más chulo aún. Añades a «BotFather» : https://core.telegram.org/bots Ahora si que has flipado, eh??? Mandándole un mensaje te deja hacer mil cosas, el «Hello World» más sencillo de la historia 👏🏻 👏🏻 👏🏻 👏🏻 Pues nada /newbot *y creamos a **Noel_IA_Bot** 🤖 , nuestro chatbot de Papa Noel (creado, nos da nuestro Token de acceso API y doc). Ya podemos escribirle, pero contestar va a contestar poco. Pero … donde van nuestros mensajes ? Tiramos de doc: https://core.telegram.org/bots/api#setwebhook Y bingo, tenemos que definir un webhook donde irán nuestros mensajes, pues nada creamos uno PHP que usaremos para recibir el mensaje del usuario y mandarle ese mensaje a ChatGPT-Noel y que nos conteste. **Creamos nuestro webhook en PHP:** <?php /** * Script para recibir mensajes de Telegram y responder con una respuesta de ChatGPT * Version: 0.1 * Author: ablancodev */ require_once 'chatgpt.php'; // Sustituye el valor por tu token real define('BOT_TOKEN', '5817262589:AAF0fleEXXXXXXXXXXXX'); define('API_URL', 'https://api.telegram.org/bot'.BOT_TOKEN.'/'); // Lee el cuerpo de la solicitud $content = file_get_contents("php://input"); // Decodifica el JSON $update = json_decode($content, true); // Comprueba si es un mensaje de texto if(isset($update["message"])) { $message = $update["message"]; if(isset($message["text"])) { $chatId = $message["chat"]["id"]; $text = $message["text"]; // llamamos a nuestra función call_chatgpt() para obtener la respuesta $response = call_chatgpt($text, $message["from"]["first_name"]); // Aquí procesarías el mensaje y construirías una respuesta //$response = "Eco: " . $text; // Envía la respuesta al chat file_get_contents(API_URL."sendMessage?chat_id=".$chatId."&text=".urlencode($response)); } } // guardamos en en fichero log.txt el contenido de la variable $update file_put_contents("log.txt", print_r($update, true)); // Para asegurarte de que el script se ejecutó correctamente, puedes devolver un "OK" echo "OK"; ?> Y ya con esto tenemos que cuando alguien escribe a Papa Noel ChatBot, Telegram manda un webhook a nuestro servidor, donde lo recibimos, y pasamos la info a nuestro ChatGPT-Noel, que nos contestará amablemente como suele hacerlo y dicha contestación se la pasamos a nuestro chatBot, círculo cerrado !! 🥳 Ahora a refinar el prompt, que el que he hecho es muy muy básico, ya que me he centrado en el flujo de conexiones e información. ## Repositorio Aquí dejo el repo con el código completo del webhook, sólo tienes que cambiar tu token de OpenAI y el de Telegram, montarlo en una web y decirle a tu chatBot que ese es su webhook a notificar. https://github.com/ablancodev/telegram-chatgpt --- ## Monitoreando el Page Speed Insights de nuestras webs URL: https://ablancodev.com/build-in-public/monitoreando-el-page-speed-insights-de-nuestras-webs/ El page speed, ese que nos trae de cabeza cuando desarrollamos proyectos webs, y que tanto odiamos. Ese que cuando se te ha ido al carajo te enteras un mes más tarde porque alguien te ha avisado o si tienes suerte en tu empresa han “implementado un proceso de vigilancia” que se trata de que una vez al mes el junior del equipo haga un informe. Ese es al que vamos a atacar hoy, y con el que pretendo hacer un dashboard que me permita no sólo ver su evolución en el tiempo para tener un histórico, sino montar algún sistema de alerta que me avise cuando algo no va como debería. ¿Y un histórico para qué Antonio? Pues por ejemplo, si sabes que el día 2 de Febrero las métricas se piraron al espacio exterior y que ese día hicisteis una subida a producción de la feature X, pues blanco y en botella, por ejemplo. ## Manos a la obra **Esto va a constar de:** - Un cron que se ejecute cada X días. - Ese proceso, que me consulte Google PageSpeed Insights via API para traerme el Score y las Core Vitals, que serán las métricas que vamos a monitorizar. - Las revisamos, y si algo no nos gusta, notificación que enviamos. - Las guardamos en base de datos para poder tener el histórico. - Creamos un dashboard con la info. #### Paso 1 Podríamos usar Zapier o Microsoft Power Automate, pero pagar por esto es tontería, así que un simple cron en el servidor, que nos ejecute un fichero PHP (prácticamente todos los hosting ofrecen esto), así que en este paso no me entretengo más. #### Paso 2 En la página de PageSpeed Insights tenemos cómo crear nuestra API KEY y la url de la llamada que tendremos que hacer: https://developers.google.com/speed/docs/insights/v5/get-started?hl=es Ahora nos vamos a nuestro código PHP. El código lo pongo en Gist: La parte interesante del código son los campo del json una vez convertido a array, ya que la verdad es que hay poca info en la documentación de Google. Con esto ya lo tendríamos mostrando en pantalla, algo como: #### Paso 3 Ahora miramos si los core vitals tienen valor “SLOW”, en cuyo caso notificaremos por el método que acordemos.// Notificamos? Core vitals // Notificamos? if ( $data['loadingExperience']['metrics']['CUMULATIVE_LAYOUT_SHIFT_SCORE']['category'] == 'SLOW' ) { echo "<p>Notificar a los desarrolladores</p>"; } // Notificamos si la performance es menor a 0.5 if ( $data['lighthouseResult']['categories']['performance']['score'] < 0.5 ) { echo "<p>Notificar a los desarrolladores</p>"; } Con esto ya tenemos nuestro watchdog de performance web disponible. #### Paso 4 ¿Histórico? Para esto tiramos de base de datos y guardamos las métricas. // guardamos en base de datos $servername = "localhost"; $username = "root"; $password = ""; $dbname = "pagespeed"; // Create connection $conn = new mysqli($servername, $username, $password, $dbname); // Check connection if ($conn->connect_error) { die("Fallo de conexión: " . $conn->connect_error); } // core vitals if ( $metrics ) { foreach ( $metrics as $key=>$metric ) { $sql = "INSERT INTO pagespeed (url, strategy, name, value) VALUES ('" . $url_destino . "', '" . $strategy . "', '" . $key . "', '" . $metric['category'] . "')"; if ($conn->query($sql) === TRUE) { echo "Datos guardados correctamente"; } else { echo "Error: " . $sql . "<br>" . $conn->error; } } } // performance $audits = $data['lighthouseResult']['categories']; if ( $audits ) { foreach ( $audits as $key => $value ) { $sql = "INSERT INTO pagespeed (url, strategy, name, value) VALUES ('" . $url_destino . "', '" . $strategy . "','" . $key . "', '" . $value . "')"; if ($conn->query($sql) === TRUE) { echo "Datos guardados correctamente"; } else { echo "Error: " . $sql . "<br>" . $conn->error; } } } Ya lo tenemos en nuestra base de datos 🥳 #### Paso 5 Tenemos la data, tenemos el proceso que se encarga de obtenerla, it’s time to draw !! Y si me conoces, este paso es el que me falta, primero porque no tengo data histórica aún, pero a eso le sumamos que soy más de diseñar motor que chapa y pintura. De esta forma tan sencilla y completamente gratuito tenemos no sólo un dashboard para ver la evolución de los core vitals y performance de nuestras webs, pudiendo asociarlos a releases y analizar posibles impactos. Amén de tener un chivato que nos informe cuando nuestras métricas se vayan de los parámetros “razonables” que consideremos. 🚀🚀🚀 Puedes ver el código y descargarlo desde el repositorio de Github. Ir al repositorio --- ## >_005 : Generando facturas en segundos con IA URL: https://ablancodev.com/build-in-public/005-generando-facturas-en-segundos-con-ia/ Pues si, vamos a darle una vuelta a cómo la inteligencia artificial nos puede usar como autónomos en la generación de facturas. En este caso nos vamos a chatGPT y vamos a estrujar su potencial como intérprete de lenguaje para pasándole lo que pasa en nuestra empresa, nos genere un json con información sobre una futura factura a generar, y que posteriormente usaremos en nuestro plugin de facturación. ## Prompt Vamos a por el prompt. Tras varias pruebas, nos quedamos con el siguiente: Actua como un gestor de facturas de una empresa que sólo hablas json, no devuelvas texto normal, y teniendo en cuenta que estos son los servicios que ofrecemos: [‘ . $productos . ‘]. Generame una factura para: [‘ . $tema . ‘]. Formato de salida: json con la siguiente estructura: [{«nombre» : «», «apellidos» : «», «items» : [{«nombre»:»», «cantidad»:»», «precio»:»»}]}] Donde: $productos nos permitirá tener información de nuestros productos, por ejemplo con su nombre y precio. $tema será el input que le indicaremos, por ejemplo: «Juan Antonio se lleva una barandilla de terraza de 15 metros a 120€ el metro, un par de rejas pequeñas y una puerta de garaje de 700€» ## Código fuente Teniendo ya la estructura que queremos montar y el prompt, vamos a picar código 🧑🏻‍💻 Tenemos un formulario con un input donde escribiremos la información que queremos facturar, el input, que al enviarlo (si, en esta primera versión podemos tirar de una versión no ajax) La llamada a chatGPT no tiene demasiado misterio, así que ponemos sólo la parte principal: $url = 'https://api.openai.com/v1/completions'; $curl = curl_init(); $productos = '{"reja pequeña" : "100", "reja grande" : "200", "Cerradura" : "35", }'; $prompt = 'actua como un gestor de facturas de una empresa que sólo hablas json, no devuelvas texto normal, y teniendo en cuenta que estos son los servicios que ofrecemos: [' . $productos . ']. Generame una factura para: [' . $tema . ']. Formato de salida: json con la siguiente estructura: [{"nombre" : "", "apellidos" : "", "items" : [{"nombre":"", "cantidad":"", "precio":""}]}]'; $fields = array( 'model' => 'text-davinci-003', 'prompt' => $prompt, 'max_tokens' => 999, 'temperature' => 0.7, ); $json_string = json_encode($fields); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_POST, TRUE); curl_setopt($curl, CURLOPT_POSTFIELDS, $json_string); curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type:application/json', 'Authorization: Bearer sk-etuThSZ9UEs5Y96GUZWfT3BlbkFJPdjrFGb6JLn21Kxxxxxx')); curl_setopt($curl, CURLOPT_RETURNTRANSFER, true ); $data = curl_exec($curl); curl_close($curl); $data = json_decode($data); Ya tenemos la información en formato json: { "nombre": "Juan", "apellidos": "Antonio", "items": [ { "nombre": "Barandilla de terraza", "cantidad": "15", "precio": "120" }, { "nombre": "Rejas pequeñas", "cantidad": "2", "precio": "100" }, { "nombre": "Puerta de garaje", "cantidad": "1", "precio": "700" } ] } Que usaremos para «inyectar» en nuestro software de gestión. Aquí un pequeño extracto de código PHP donde usamos este json para mostrarlo: if ( isset($data->choices) ) { $receta = $data->choices[0]->text; $page .= '&lt;h2>Factura en formato json para:&lt;/h2>'; $page .= '&lt;h3>' . $tema . '&lt;/h3>'; $page .= '&lt;pre>&lt;code>' . $receta . '&lt;/code>&lt;/pre>'; $page .= '&lt;hr>'; $datos_array = json_decode($receta, true); $items = $datos_array['items']; $client_name = $datos_array['nombre']; $client_surname = $datos_array['apellidos']; $items = $datos_array['items']; } else { $page .= "Algo no ha ido como debía y no me ha generado nada"; } } ## Prototipo @ablancodev Prompt para generar facturas usando Inteligencia Artificial. #AI #chatGPT #facturas #automatizacion ♬ The Champion – Lux-Inspira ## Imagina que Imagina que le añadimos una capa inicial de voz a texto, por ejemplo con el servicio Speech to text de Azure. Esto nos permitiría tener a nuestro «contable», al que simplemente contándole de viva voz lo que está pasando en nuestro negocio, nos vaya generando las facturas 🧞‍♂️ --- ## >_004: Montando un sistema de onboarding con IA URL: https://ablancodev.com/build-in-public/004-montando-un-sistema-de-onboarding-con-ia/ **¿Nuevo en la empresa?** Seguro que te da un poco de palo estar todo el tiempo preguntando cosillas a tus compañeros. **¿Tienes nuevos empleados?** Ya sabrás lo que puede suponer el onboarding de ellos, tanto a nivel de tiempos como de dinero. **Pues es hora de pedirle a la IA que nos eche una mano.** Vamos a montar un sistema de chat que los nuevos empleados, y no tan nuevos, tengan disponible y que les resuelva las distintas dudas que puedan tener en el día a día. ## Arquitectura La idea de arquitectura de la aplicación es: - Usar ChatGPT para comunicarnos. - Cargar una serie de ficheros (PDFs o txt) que serán nuestra base de conocimiento. - No queremos pasar toda esa info a ChatGPT, tampoco es plan de compartirlo todo. Así que sólo le pasaremos la información estrictamente necesaria. ## Manos a la obra Vamos a montar una aplicación en Python 🐍, que nos lea los ficheros PDFs y txt que haya en una carpeta, y «almacenaremos» dicho conocimiento en local, haciendo uso de la librería **llama-index **(https://pypi.org/project/llama-index/) 🦙 Llama-index nos monta un sistema de machine learning que nos permitirá almacenar en local los datos, estructurarlos vectorialmente para poder consultarlos y demás. Una vez montado el sistema y los datos cargados, lo que vamos a hacer es preguntarle a nuestro sistema ML y sólo obtener de él los fragmentos que sean relacionados con la pregunta, y serán sólo esos los fragmentos que usaremos para preguntar a ChatGPT y que nos de la respuesta que estamos esperando. Veamos el código propuesto: https://gist.github.com/ablancodev/f4a29385f052da3a301c6536d0040741 import os import openai from llama_index import GPTVectorStoreIndex, SimpleDirectoryReader, LLMPredictor, ServiceContext from langchain.chat_models import ChatOpenAI import textwrap #Cámbiala por tu API de OpenAI os.environ["OPENAI_API_KEY"] = 'sk-VzM0uF3aeOhzEUPntVtsT3BlbkFJ12S4ecEXBnDXXXXXXX' #Leer los datos pdf = SimpleDirectoryReader('datos').load_data() #Definir e instanciar el modelo modelo = LLMPredictor(llm=ChatOpenAI(temperature=0.7, model_name='gpt-3.5-turbo')) #Indexar el contenido de los PDFs service_context = ServiceContext.from_defaults(llm_predictor=modelo) index = GPTVectorStoreIndex.from_documents(pdf, service_context = service_context) # Bucle de conversación while True: pregunta = input('¿Qué necesitas saber? \n') respuesta = index.as_query_engine().query(pregunta) for frase in textwrap.wrap(respuesta.response, width=100): print(frase) ## Datos, datos, datos 💾 Vale, ya lo tenemos todo preparado, vamos a ver de qué tipo de información vamos a nutrir a nuestro sistema. Pensando en un sistema para el onboarding de empleados, vamos a añadir 3 pdfs de ejemplo: - 1 documento con un listado contactos de la empresa. - 1 documento con ciertos procedimientos, en nuestro caso va a ser del departamento de desarrollo 😎 - 1 documento de modelo de negocio (es de Telefónica, no es plan de escribir un documento entero) He aquí los documentos: contactosDescarga procesosDescarga negocioDescarga ## Play 🕹️ Montamos nuestro nuevo sistema de onboarding basado en machine learning y ChatGPT …. ENTER …. y a preguntar, que como nuevo en la empresa ando perdido 🥹 Aquí mi conversación con Ombo, el asistente de la empresa, y eso que la info que le he pasado es muy básica. A nivel empresarial no le he preguntado nada, se me ha pasado completamente, pero si quieres probarlo ya sabes, móntalo, descárgate los PDFs y a preguntar, que verás que da igual que el PDF tenga muchos estilos, es capaz de sacar información de él de forma bastante eficiente. 🚀 🚀 🚀 --- ## >_003: Intentando mejorar el SEO de Amazon con IA URL: https://ablancodev.com/build-in-public/_003-intentando-mejorar-el-seo-de-amazon-con-ia/ Ya todos sabemos lo que es Amazon, el marketplace de marketplaces, en el que tengo cuenta de afiliados por lo visto desde hace ya cerca de 5 años (sabe dios qué andaría yo trasteando por esos tiempos) Y pensando me he dicho, ¿serán mejorables las fichas de productos a nivel SEO si le damos un toque de IA? ## Prototipo y roadmap Para ir validando etapas, o al menos tenerlas para que no se me quede en proyecto a medio hacer, lo vamos a fasear en las siguientes releases: ### Prototipo 0.1: **2023 Mayo** Web con los 10 productos más buscados o vendidos en el que el contenido lo meteré manualmente pidiendo el texto por ejemplo a ChatGPT (Bard, ya te tocará, tu tranquilo) ### MVP 1.x: **2023 Junio** Tiraremos de la API de Amazon para crear una cantidad ya más decente de productos en nuestra tienda (como hicimos con Mercadona en neveravacia.es). Y por cron diario iremos pidiendo a ChatGPT contenido en tandas por ejemplo de 10 productos. Esto lo hago así para que la multa de OpenAI no me arruine, y ya de paso le vamos dando cambios frecuentes a Google en la web. ### Growth 2.x: **Finales 2023** Esta fase se trata de darle un poco de cariño al proyecto, analizar resultados, hacer mejoras, etc … ## Prototipo 0.1 Dividiendo por tareas principales, me anoto: - Crear la web básica. - Mirar estructuras recomendadas a nivel SEO para páginas de producto. - Montar un «ecommerce» de productos afiliados, y personalizar la página de producto con los campos que voy a necesitar. Toda esta parte es desarrollo. - Vamos a la parte de content, que en esta fase será buscar 10 productos más o menos top, y meterlos en la tienda, y pedirle a la IA los contenidos. ### 22 de Mayo 2023 Como era de esperar en mi, no he podido esperar !!! Así que pasamos a la siguiente versión, con sólo 2 productos creados, aunque eso así, tenemos dominio ya online para que empiece la indexación por parte de los buscadores. Home 5 🥳 🥳 🥳 🥳 🥳 🥳 🥳 🥳 🥳 🥳 🥳 🥳 🥳 🥳 ## MVP 1.0 Es hora de automatizar, que esto de copia-pega ya no se lleva, vámonos a por la API de Amazon. Aquí tenemos info sobre la API y sus endpoints: https://developer-docs.amazon.com/amazon-business/docs/ab-api-endpoints Con llamadas por ejemplo: https://eu.business-api.amazon.com/products/2020-08-26/products Pero para tener los datos de acceso para poder hacer las llamadas, tenemos que crear una App en su entorno de desarrollo, por lo que tendremos que darnos de alta como desarrolladores en https://developercentral.amazon.com/ lo cual implica una validación por parte de ellos. Ohhhh, así que nos quedamos de momento a la espera de ello. Esta vez no voy a esperar a cerrar el proyecto y entonces publicar el post, sino que va a ser un post abierto en el que ir contando un poco el día a día y cómo va avanzando el proyecto. --- ## >_002: Reduciendo la deuda diseño-desarrollo URL: https://ablancodev.com/build-in-public/_002-reduciendo-la-deuda-diseno-desarrollo/ En el día a día nos encontramos que lo que se publica en producción no es exactamente lo diseñado en figma, o lo que tenemos en preproducción. ## ¿Qué solemos hacer? Lupa, tiempo (y por consiguiente dinero) y a poner a l@s chic@s a revisar una a una las páginas o módulos para encontrar las 7 diferencias 🕵🏻 ## ¿Qué deberíamos hacer? Este proceso de búsqueda de diferencias deberíamos automatizarlo lo máximo posible, para ir a tiro hecho a por los que nos den posible error. El proceso que vamos a montar es: - Obtenemos una captura de pantalla de la sección de la web que está online. - Cogemos una imagen de referencia con la que comparar. - Hacemos la diferencia entre ambas imágenes y obtenemos una imagen resultado con las diferencias. Para ello tenemos disponible una librería que es la leche, Cypress. Entre muchas otras cosas que nos permite Cypress, es visitar una url y hacer una captura de alguna de sus partes, con simplemente indicarle un selector javascript, como puede ser un id (#el-id), o una clase (.la-clase), o incluso un atributo () cy.viewport(1280, 1162); cy.visit('https://ablancodev.com') cy.wait(5000) cy.get('#intro').screenshot('modulo_intro') Con las instrucciones de arriba le estamos definiendo un viewport, con el que indicamos las medidas (esto nos permitirá «visitar» la página por ejemplo en versión móvil o tablet) El wait de 5 segundos es para esperar que la página «se estabilice», ya que hay algunas que entre que terminan de cargarse, cargan animaciones y demás, lo necesitan (yo por defecto lo dejo). Y por último le decimos que obtenga el tag con id = intro, y que genere una captura de pantalla ‘modulo_intro.png’ Vale, ya tenemos la primera parte, ahora vamos a por la segunda. Supongamos que vamos a comparar producción con un diseño que tenemos hecho. Se trataría de tener una imagen de referencia. Esta parte la vamos a hacer manual, y colocamos la imagen de referencia del módulo con las mismas dimensiones de lo que se debería generar (el viewport nos da por ejemplo el ancho) Teniendo ya las dos imágenes a comparar, usaremos pixelmatch para hacerlo y obtener el % de diferencia. Ya con el flujo montado, ya sólo nos falta ver el caso de uso del prototipo montado: Web de referencia, la mía, la sección intro. Como la implementación es pixel perfect, no se encontrarían diferencias, por lo que voy a «manchar» la imagen de referencia con un par de puntos, y le he eliminado el menú, para que podamos ver las diferencias (de pixel perfect nada, lo que pasa es que no tengo figma de referencia, por lo que primero le he hecho una captura para usarla 😎): Imagen referencia Captura obtenida en vivo de la web Si ejecutamos nuestro script Cypress, se nos generará una imagen con las diferencias, así como un cálculo del % de píxeles diferentes. Obtenemos casi un 5% de diferencia de píxeles, y esta imagen de diferencia (lo rojo es lo distinto): Así que ya no dependemos del ojo del buen cubero para encontrar las diferencias, sino que podemos encontrar las diferencias a nivel de pixel y de forma automatizada, ahorrando esfuerzos y dinero a la empresa. ## El código Cypress.Commands.add('Checksite', (url, get, cnt) => { //cy.viewport(1024, 1162); cy.visit(url) cy.wait(10000) cy.get(get).screenshot('modulo_slide_' + cnt) // PNGJS lets me load the picture from disk const PNG = require('pngjs').PNG; // pixelmatch library will handle comparison const pixelmatch = require('pixelmatch'); cy.readFile( './cypress/screenshots/ablancodev_test1.cy.js/modulo_slide_' + cnt + '.png', 'base64' ).then(baseImage => { cy.readFile( './cypress/screenshotsreferencia.js/modulo_slide_' + cnt + '.png', 'base64' ).then(studentImage => { // load both pictures const img1 = PNG.sync.read(Buffer.from(baseImage, 'base64')); const img2 = PNG.sync.read(Buffer.from(studentImage, 'base64')); const { width, height } = img1; const { width2, height2 } = img2; const diff = new PNG({ width, height }); // calling pixelmatch return how many pixels are different const numDiffPixels = pixelmatch(img1.data, img2.data, diff.data, width, height, { threshold: 0.1 }); // calculating a percent diff const diffPercent = (numDiffPixels / (width * height) * 100); cy.task('log', `Found a ${diffPercent.toFixed(2)}% pixel difference`); cy.log(`Found a ${diffPercent.toFixed(2)}% pixel difference`); cy.writeFile('diff_' + cnt + '.png', PNG.sync.write(diff)); expect(diffPercent).to.be.below(50); }); }); }); const pages=[ {url: "https://www.marajosl11.sg-host.com", id: 1, get: '#intro'} ] it('Check landings',()=>{ pages.forEach((page)=>{ cy.Checksite(page.url, page.get, page.id); }); }) ## Otras aplicaciones ### Pre vs Pro Imagina que en vez de usar una imagen de referencia, usamos la llamada al entorno de preproducción para generar la captura de referencia. Tendríamos un comparador pre-pro 🚀 ### ¿Nos han hackeado? No es la primera vez que escucho que han hackeado una web, y se enteran a los pocos días cuando o bien alguien ha avisado, o alguien de la empresa se ha tropezado con el desaguisado. Con este sistema podríamos ejecutar el script de forma periódica y detectar cuando el nivel de diferencia sea muy alto, avisando a las autoridades competentes 🚀 ### Bulk Si al array final le sumamos una batería de webs/landings que tengamos, nos hemos montado un sistema de QA que supervisa todas las webs/landings de nuestra empresa, sean 1, 10, 100 o 1000. 🚀 --- ## Gestión de proyectos: Planning URL: https://ablancodev.com/essays/gestion-de-proyectos-planning/ Este post no será nada SEO friendly, y probablemente algunas cosas se te queden cortas, ya que es sólo un post de apuntes de la fase de **gestión de proyectos, planificación**, del curso Project Management de Google. La fase de planificación, puede ser muy distinta entre distintos proyectos o empresas, pero suelen incluir **3 puntos claves**: - **Planificación (schedule)** - **Presupuesto (budget)** - **Plan de gestión de riesgos** ## Kick-off meeting Es la primera reunión a modo de inicio de proyecto, en el que se comparte con todos la visión, objetivos y alcance del proyecto. Deben estar: - Project team - Project stakeholders - Project sponsor Aprox. 1h ## Tareas e hitos **Tarea**: Es una actividad que tiene que ser completada en un determinado periodo de tiempo. **Hito**: Es un punto importante en la planificación de un proyecto. Suele indicar un progreso o finalización de algún entregable o fase del proyecto. #### Work breakdown structure (WBS) (estructura de desglose de trabajo) Herramienta que ordena los hitos (milestones) y tareas de un proyecto jerárquicamente en el orden en el que deben ser completadas.**Artículo interesante de cómo hacerlo y sus ventajas: https://www.lucidchart.com/blog/how-to-create-a-work-breakdown-structure-and-why-you-should ## Creando un Project Plan Un project plan incluye: - Tareas - Hitos - Personas y sus roles - Documentación (por ejemplo: RACI chart, presupuesto o project charter) - Tiempos Adicionalmente podemos añadir los siguientes componentes: - Alcance y metas (scope and goals). Suelen estar en el Project charter. - WBS - Presupuesto - Plan de gestión. Como son el plan de gestión de cambios, el plan de gestión de riesgos y el plan de comunicación. ## Estimación de tiempos Es distinto el tiempo estimado, que el esfuerzo estimado**. El tiempo estimado incluye el tiempo que no se trabaja, por ejemplo: Para pintar una pared, el esfuerzo estimado es de 1h que es lo que tarda el pintor, pero el tiempo estimado es de 24h, ya que hay 23h de secado. Debido a cosas que no se conocen, o que son impredecibles, se suele usar un buffer de tiempo. Pudiendo tener buffer de tiempos a nivel de tareas o de proyecto general. #### Capacidad La capacidad es la cantidad de trabajo que la gente o recursos asignados a un proyecto pueden razonablemente completar en un periodo de tiempo. #### Camino crítico Listado de hitos que hay que cumplir en orden para llegar a la meta del proyecto en tiempos, así como las tareas obligatorias que son necesarias para completar cada hito. ## Gantt Ejemplo de Gantt hecho en excell. En esta web hay muchas plantillas, entre ellas, para Project plan. ## Presupuestos **El presupuesto de un proyecto** es la cantidad de dinero necesaria para llevar a cabo los objetivos y metas del proyecto. **Un pronóstico** es un coste estimado o predicción sobre un periodo de tiempo. Cuando se crea un presupuesto hay que tener en cuenta: - Comprender lo que los stakeholders necesitan. - Presupuestar para gastos sorpresas. - Manteniendo adaptabilidad. - Revisar y replantear a lo largo del proyecto. **Un análisis de reserva** es un método para chequear los recursos restantes del proyecto. **Un presupuesto de contingencia** es dinero que es incluido para cubrir potenciales imprevistos que no se contabilizaron en la estimación de costes (suele ser un 5%). **Coste de la calidad**, es el incurrido para prevenir errores con productos, procesos o tareas. Algunos **tips para presupuestar**: - Básate en presupuestos pasados, y aprende de los aciertos y errores que cometistes. - Utiliza a tu equipo para preguntarles y que te den feedback. - Divide tu presupuesto por fases/tiempo. Esto te ayudará a ir comparando lo gastado con lo planeado y poder corregir desviaciones. - Chequea, chequea y chequea, para que no se te cuele ningún error. #### Categoriza diferentes tipos de costes. **Costes directos** Son costes para items que son necesarios para llevar a cabo el proyecto, como: - Salario de los empleados y externos. - Costes de material. - Alquiler de equipos. - Licencias software. - Viajes relacionados con el proyecto. - Formación del personal. **Costes indirectos** Costes para items que no son directamente necesarios para llevar a cabo el proyecto, pero que son esenciales para que el equipo pueda realizar su trabajo, como pueden ser: - Costes administrativos. - Utilidades - Seguros - Equipamiento general de la oficina. - Seguridad. #### Presupuesto de referencia Se suele crear un presupuesto de referencia para la aprobación del proyecto, y así poder tener una guía sobre la que ir viendo la desviación del día a día. Aquí una plantilla de presupuesto en Google sheets. ## Obtención Obtener todos los materiales, servicios y suministros necesarios para completar el proyecto. **Vendedores**: Individuales o empresas quienes proveen bienes y servicios esenciales. La obtención de recursos para el proyecto suele llevar 5 fases: - Inicialización - Selección - Escribir contratos - Control - Completando Gestión **agile** de compras: - Colaboración con el equipo y el proveedor final. - Énfasis en la relación entre ambas partes. - El equipo del proyecto juega un papel importante en identificar lo que se necesita adquirir. - Contratos vivos. #### Documentos - NDA - RFP (request of purposal) - SOW (statement of work) : Describe los productos o servicios que un proveedor dará a la organización. Incluye tanto lo que la organización necesita, como lo que necesita y requerimientos del vendedor para llevar a cabo los servicios. ## Gestión de riesgos Un **riesgo** es que existe una probabilidad de que ocurra. Un problema (**issue**) es un problema real conocido. **La gestión de riesgos** es el proceso de identificar y evaluar potenciales riesgos y problemas que podrían impactar en el proyecto. Nos ayuda a comprender: - Qué podría ir mal - A quién tendríamos que consultar - Como el riesgo puede ser reducido De la gestión de riesgos pueden surgir oportunidades que sin ellos no hubiésemos tenidos en cuenta. (Artículo sobre ello del PMI) Un **diagrama de causa-efecto** (o diagrama de espina de pescado) muestra las posibles causas de un evento o riesgo. Este diagrama lo podemos definir en 4 fases: - Definir el problema (será como la cabeza del pescado) - Identificar las categorías (las puntas de espinas principales - Brainstorming de las causas (las ramas que salen de cada espina) - Analizar las causas (esto podará algunas ramas) La **evaluación de riesgos** es una etapa de la gestión de riesgos donde los riesgos son estimados o medidos. Matriz de probabilidad e impacto. **Principales tipos de riesgos:** - Tiempo - Presupuesto - Alcance Un **single point of failure** (punto simple de fallo) es un riesgo que puede suponer el parón general del proyecto. ## Plan de comunicación y documentación **Comunicación es un flujo de información** e incluye qué se comparte, cómo y con quién. Una comunicación efectiva es: - Clara - Honesta - Relevante - Frecuente Un **plan de comunicación** organiza y documenta el proceso, tipos y expectativas de comunicación para un proyecto. Suele responder a las preguntas: - Qué necesita ser comunicado - Quién necesita ser comunicado - Cuándo hay que comunicar - Por qué y cómo comunicarlo - Dónde la información comunicada es guardada Ejemplo de documento relleno de un plan de comunicación: Exemplar_-Communication-plan-1Descarga Documentación: Aquí un ejemplo de project plan, usado como un único excel desde donde consultar la mayoría de las cosas. Template_-Project-planDescarga Glosario sobre esta parte de la gestión de proyecto: Glossary-_-PM-Terms-and-DefinitionsDescarga ## Offtopic Plantilla para retrospective: Google-Project-Management-Certificate_-Retrospective-Template.docxDescarga --- ## Aplicando ingeniería inversa a LinkedIn URL: https://ablancodev.com/build-in-public/aplicando-ingenieria-inversa-a-linkedin/ Vamos a destripar un poco la red profesional por excelencia, LinkedIn. A ver si con ello vemos parte de sus entresijos, que seguro podremos reutilizar en algún proyecto futuro. Echando una visual general, además de quedarnos claro de que se trata de una red social orientada al mundo laboral, podemos distinguir los siguientes módulos: - **Feed**: Es el muro de noticias. - **Contacts**: La parte de contactos, donde haremos crecer nuestra red profesional. - **Jobs**: La parte referente al empleo, ofertas y postulaciones. - **Profile**: Nuestro perfil, nuestra carta de presentación al mundo. ## Casos de uso 🧍 Basándonos en una versión reducida del proyecto, podemos entender que lo que los usuarios van a querer es crear, leer, actualizar o eliminar (CRUD) los distintos elementos que tenemos en los módulos a tratar. ### Módulo contact. ### Módulo feed. ### Módulo jobs. ### Módulo profile. ## Clases y entidades 🧩 Haciendo una primera aproximación, podemos encontrar las siguientes entidades o clases. ### Módulo contact. **User**: Serán los usuarios de la aplicación. Existe una relación entre dos entidades «User», que llamaremos «Contact», pudiendo tener esta relación su propio estado (aceptado/rechazado o pendiente) ### Módulo feed. **Feed**: Representa al muro de noticias de la aplicación. Este es el tipo de entidad que en ocasiones te planteas si debe de existir como entidad o simplemente es una «pantallla» de representación de datos. En principio vamos a dejarla, estando asociada 1 a 1 con los usuarios. **Publication**: Será una publicación de usuario en el feed. Aunque un feed está claro que consta de 0 ó más publicaciones, una publicación no es única para un feed, ya que piensa que tu publicación aparecerá en feeds de distintos usuarios. Si te fijas, va tomando fuerza eliminar el elemento feed, y que las publicaciones estén directamente relacionadas con los usuarios. Convirtiendo el «feed» en una simple forma de representación de datos. **Comment**: Serán los comentarios que se pueden hacer a nivel de publicación, sea nuestra o no. Con esto pasamos de una primera aproximación: A esta segunda. ### Módulo Jobs. El módulo de Trabajo, lo vamos a resumir en que el usuario puede «aplicar» a tantos trabajos como quiera. Donde cada trabajo (job) pertenece a una empresa, pudiendo tener cada empresa tantos trabajos publicados como necesiten. ### Módulo Profile. Tal y como vimos en los casos de uso, el módulo de Profile lo vamos a dividir en la gestión de 3 entidades principales: Education (educación), Experience (experiencia profesional), y Skills (habilidades) ## Sequencia de una petición de contacto 🤜🏻 🤛🏻 Veamos uno de los procesos que suelen ser recurrentes en el uso de esta red social, y es el caso de pedir petición de contacto a otra persona. Cuando enviamos una petición de contacto a otra persona, puede o bien aceptarla, en cuyo caso ambos ya seréis «contactos», y rechazarla en cuyo caso, te quedas con las ganas 😭. **PD:** Que me perdonen los chicos de LinkedIn 🙏🏻 por este análisis tan simplista de su aplicación, pero me he centrado en la funcionalidad principal para poder analizarla y así poder aplicar ingeniería inversa al producto. --- ## Historias de usuario vs Casos de uso URL: https://ablancodev.com/essays/historias-de-usuario-vs-casos-de-uso/ **¿Pero no son lo mismo?** Cuando uno empieza en el mundo agile, y ve por primera vez las historias de usuario, lo primero que piensas es: «pero si estos son los casos de uso de toda la vida». Y la respuesta es un si pero no, ya que aunque en ciertos puntos son parecidos, existen diferencias entre ellos. **Las historias de usuario están centradas en el resultado y beneficio de lo que estás describiendo, mientras que los casos de uso están más centrados en cómo el sistema actuará.** ## Similitudes - Ambos están escritos desde la perspectiva del usuario, buscando cómo usar el producto. - Los dos contienen el objetivo y criterio de aceptación. - Tienen elementos comunes: un actor, flujo de eventos y condiciones a conseguir. ## Diferencias - Los casos de uso llegan a más detalle en la documentación. - Las historias de usuario dejan detalles sin especificar, para provocar conversaciones en las reuniones scrum. - Pequeños incrementos con feedback en lugar de algo más detallado y cerrado como serían los casos de uso. ## Qué son las historias de usuario Las historias de usuario son breves descripciones, desde el punto de vista del usuario, que recogen qué hace o necesita hacer el usuario. Se centran en lo que el usuario necesita, en lugar de lo de que sistema debe entregar. Lo cual da lugar a futuras conversaciones sobre lo que el sistema debe hacer para cubrir dicha necesidad del usuario, fomentando un entorno agile. Todos conocemos el concepto de escribir las historias de usuario como tarjetas siguiendo la estructura de:**«Yo como <role>, <quiero> <para>.»** Pero las historias de usuario son mucho más. Como comenté antes, se suelen dejar con poco nivel de detalle para fomentar discutir sobre ellas, por lo que las conversaciones entre los distintos stakeholders son parte de ellas. Así como el criterio de aceptación, que nos valdrá para darla por hecha o no, asegurando un nivel en los desarrollos, el llamado Definition of Done (DOD). ## Qué es un Caso de Uso Son una forma de describir requerimientos funcionales usando el punto de vista del actor. Captura las distintas formas en las que el usuario y el producto interactuan para conseguir un objetivo. Especifica cómo el usuario interactúa con el producto y cómo el producto responde las a las acciones del usuario.**Un caso de uso tipo suele incluir: - Descripción - Precondición - Postcondición - Flujo básico (si va todo bien) - Flujos alternativos (caminos si no todo ha ido correctamente) ## ¿ Uno u otro ? Sé que nos movemos normalmente en entornos agile, y que queda mucho más «cool» trabajar con historias de usuario que con casos de uso. Pero particularmente los casos de uso me ayudan a tener una visión más global de la funcionalidad del producto**, saber qué es lo que tiene que hacer y cubrir. Mientras que **las historias de usuarios son más útiles a la hora de pelotear **con los distintos stakeholders **y saber exáctamente qué es lo que ellos quieren**. Así que ni uno ni otro, los dos 🍻 --- ## Bajando las historias de usuario URL: https://ablancodev.com/essays/bajando-las-historias-de-usuario/ Las** historias de usuario** son descripciones cortas de una característica, contada desde la perspectiva del usuario. En ellas vemos 3 elementos destacados: - Usuario - Acción - Beneficio Por lo que se suelen formular siguiendo la estructura:***» Como <usuario>**, quiero esta **<acción>**, para conseguir este **<beneficio>**«* Como vemos, el centro de todo es el usuario, su experiencia, su feedback. Por ello es conveniente definir lo que se llaman «User persona», es decir, los distintos usuarios tipo que la usarán, personalizándonos y haciéndolos «reales». Por ejemplo: **Leo** CEO de una empresa que busca personal cualificado en nuestra plataforma. **Julia** Profesional que busca empleo en nuestra plataforma. Esto nos permitirá darle un toque personal a las historias y hacerlas más cercanas a todos los miembros del equipo, ayudando a su comprensión. Teniendo en mente el Product Backlog como hoja de ruta del desarrollo de producto, donde todo el equipo puede ver hacia donde vamos y qué cosas futuras se nos vienen, vamos a bajar un poco los distintos elementos de dicho backlog, las historias de usuario (user story). Para cada uno de los items deberíamos tener identificadas las siguientes propiedades: Num. OrdenDescripciónValorEstimación **Numero de orden**: Las distintas historias de usuarios las ordenaremos por prioridad, estando en las primeras posiciones las de mayor prioridad. **Descripción**: Se trata de una descripción de la historia, pudiendo tener historias más detalladas y otras menos. Las historias de mayor prioridad deberían estar más detalladas, ya que están más próximas a ser realizadas. **Valor**: Como valor entendemos lo que aporta a nivel de negocio, de producto este item. Por lo general encontraremos los elementos con mayor valor, con prioridad mayor, ya que nuestra prioridad suele ser aportar el mayor valor posible a nuestro producto en cada entrega. **Estimación**: Cuánto esfuerzo nos va a costar realizar dicha característica. Es el equipo de desarrollo el verdadero dueño de esta columna, y con el que debemos trabajar conjuntamente para que nos puedan dar la estimación más acertada. --- ## Aplicando ingeniería inversa a Trello URL: https://ablancodev.com/build-in-public/aplicando-ingenieria-inversa-a-trello/ ¿Quién no ha usado Trello para la gestión de tareas? Si no lo has hecho, vas tarde compañero. Vamos a ver algunos puntos básicos que usaremos a modo de toma de requisitos para nuestra versión MVP de Trello: - Se trata de un gestor de tareas. - Existen distintos estados por los que pasarán las tareas. Estos estados deberán estar representados como columnas, lo cual ayudará de forma visual a ver las transiciones. - Las tareas podrán tener etiquetas, para poder agruparlas por distintos conceptos. - Las tareas deben poder asignarse a usuarios. - Las tareas podrán tener etiquetas para poder categorizarlas de distintas maneras. - Y por último, no olvidar, que las tareas pertenecerán a un tablón, que es la organización primaria que tiene el sistema. ## Casos de uso 🧍 Basándonos en una versión reducida del proyecto, podemos entender que lo que los usuarios van a querer es crear, leer, actualizar o eliminar (CRUD) los distintos elementos que tenemos: tableros, tarjetas, etiquetas y estados. Viendo a nivel de roles, podemos distinguir 3: **Admin**: Serán los creadores de los tableros/tarjetas. Por lo que podrán crear/leer/editar/eliminar sus tableros/tarjetas. **Miembros**: Los usuarios «miembros» de un tablero, podrán crear/leer/editar/eliminar elementos de dicho tablero, aunque no editar/eliminar el tablero como tal. **Observador**: Los usuarios «observadores» de un tablero, pueden leer sus elementos, pero no modificarlos. ## Diagrama de clases 🧩 Haciendo una primera aproximación, podemos encontrar las siguientes entidades o clases, con una primera versión de atributos mínima. **Board**: Representará a los tableros donde se encuentran las tarjetas. - id : int - title : string **Card**: Serán las tarjetas, elemento principal de la app. - id . int - title : string - description : string **Status**: Son las columnas de nuestros tableros. Aunque en principio parezca más razonable nombrarlas Columnas, lo vamos a dejar como Status, ya que esto nos permitirá en un futuro poder usar como columna otros tipos de categorizaciones, como podrían ser etiquetas o usuarios. - id : int - title : string **Tag**: Son las etiquetas que podemos ponerle a las tareas, así tendremos otro punto de organización. - id : int - title : string - color : string **User**: Serán los usuarios de la aplicación. - id : int - username : string - pass : string - email : string Vamos a relacionar estas clases, analizando: - Un usuario puede tener 0 o más tableros, y un tablero puede tener 1 o más usuarios (los roles de momento lo dejamos pendiente) - Un tablero puede tener 0 o más tarjetas, y una tarjeta puede estar en un único tablero. - Una tarjeta tiene un único estado, y un estado puedo tener 0 o más tarjetas. - Una tarjeta puede tener 0 o más etiquetas, y una etiqueta puede estar en 0 o más tarjetas (vamos a permitir crear etiquetas sin estar asignadas inicialmente a alguna tarjeta) - Un tarjeta puede estar asociada a 0 o más usuarios, y un usuario puede estar asociado a 0 o más tarjetas. ## Ciclo de vida de una tarjeta 🧬 Veamos el ciclo de vida de una tarjeta, como entidad más destacada del modelo. Una vez una tarjeta es creada, y hasta que no es eliminada, pasa por distintos estados (funcionalidad que es el late motive de la app), es etiquetada, y recibe comentarios (funcionalidad que hemos sacado de esta primera versión de análisis de la aplicación). **PD:** Que me perdonen los chicos de Trello 🙏🏻 por este análisis tan simplista de su aplicación, pero me he centrado en la funcionalidad principal para poder analizarla y así poder aplicar ingeniería inversa al producto. --- ## DOKR : Dashboard Objetive Key Results URL: https://ablancodev.com/build-in-public/dokr-dashboard-objetive-key-results/ Genial, ya tenemos nuestros OKRs definidos, ahora vamos a ponerlos en práctica, y lo primero que tenemos que hacer es poner a todos los miembros alineados con ellos, que sepan hacia donde vamos, qué queremos conseguir y el por qué de las decisiones y tareas del día a día. ## Excel vs Dashboard ¿ Un Excel ? No sé, no termina de parecerme lo suficientemente cool 😎 Y si en vez de tener un excel por ahí en la nube, le damos forma a nuestros OKRs y creamos un dashboard donde desde un simple vistazo podemos saber el status general de los resultados, creemos un **DOKR** 🚀 He trabajado en varias compañías en las que aunque no aplicaban OKR como metodología, usaban una pantalla general en la oficina donde se iban viendo los progresos, por ejemplo de ventas, me gustaba !! ## Dashboard vs las personas Pero el otro día hablándolo con un compi de curro, me comentó algo como: «ya, pero lo que pone ahí como resultado no es responsabilidad directa de todo el mundo, y eso les hace sentirse culpables o que no hacen su trabajo» (algo así, no es literal), y la verdad es que me hizo pensar y llevaba cierta razón. Vale, ¿y si usamos los dashboard no sólo para mostrar el seguimiento de nuestros OKRs, sino como método para motivar a nuestros equipos?**Celebremos cuando se lleguen a ciertos hitos, un dashboard no sólo tiene que ser un conjunto de barras y gráficos circulares, podemos tener animaciones, eventos, …. Para poder tener una visión general, que la empresa esté alineada y demás, me parece genial tener un dashboard de objetivos, y si es visible mejor que mejor, esto nos servirá para «retroanimarnos» a conseguirlos. Pero a nivel personal, me gusta tener mis propios objetivos claros, metas o incluso hitos personales que yo decida ponerme a mí o a mi equipo. Esto me lleva a, ¿y si creamos dashboard personales? ¿o por equipos? Esto ya si que me va gustando más 🙌🏻 ## Qué guay, ¿no?, vamos a irlo bajando. Sin haberme sentado a implementar, me tengo que anotar Google Chart como posible librería a usar, pinta muy bien, y tiene un gráfico que Gantt que para mostrar status de proyectos, o roadmaps puede ser muy útil. Teniendo los 3 niveles que hablamos antes, crearía una vista por cada uno de ellos: General o de organización.** 👀 : Este lo usaría como pantalla general, visible en la oficina. 🧐 : Visible a todo el mundo, incluso posibles clientes que entren en las instalaciones. **Por departamento / proyecto.** 👀 : Este quizás opte por una página web restringida a los miembros. 🧐 : Sería un dashboard menos visible, restringiéndolo a los miembros de cada departamento / proyecto. **Personales.** 👀 : Este sí que sí es una página web. 🧐 : Pues aquí tengo dudas. Si son sólo objetivos puestos por un responsable, deberían poderlos ver el responsable y la persona encargada de realizarlos. Pero me gusta la opción de poder tener objetivos personales autoimpuestos, y esos creo que sólo la persona debería poder verlos. ## Dashboard dev Más o menos con estas premisas, tocaría ponerlos a implementar, que eso será en otro momento, que hoy es sábado y toca dar una vuelta por ahí o dos. 🤟🏻 --- ## # ORK: Objetives (What) + Key Results (How) URL: https://ablancodev.com/essays/apuntes-okr/ Aquí unos apuntes tomados sobre OKR, para que no se me pierdan en el olvido. Combina un objetivo y una métrica para determinar un resultado medible. ## Objetive: - Define qué necesitamos lograr. - Describe una salida deseada. **Objetivos fuertes** siguen los criterios: - Aspiracional - Alineados con las metas de la organización. - Orientados a la acción. - Concreto. - Significativo. **Por ejemplo:** - Incrementar la retención de usuarios. Preguntas que ayudan a refinarlo los objetivos: - ¿Pide un cambio incremental/salto o descubrimiento? - ¿Puede ser más específico? - ¿Puede ser más orienta a la acción? - ¿Se puede decir de una forma más concisa? - ¿Puedes vincularlo a tus valores? ## Key Results: La salida medible que define cuando el objetivo ha sido cumplido. Una forma de obtener los key results iniciales sobre los que trabajar, es contestar a la siguiente pregunta: **¿Cuales son las 3 o 5 cosas importantes que tendrían que cambiar en los siguientes 90 dias para que el objetivo se hiciese realidad? ** Key Results fuertes siguen los criterios: - Orientado a los resultados (no una tarea) - Medible y verificable. - Específico y temporal. - Agresivo, pero realista. **Por ejemplo:** - Conseguir un 90% de valoración de satisfacción de cliente al final del trimestre. ## Niveles OKR Los 3 niveles tienen que estar alineados. - Compañía / organización - Departamento / equipo - Proyecto ## Tipos de éxitos en OKR - **Comprometido**. Los que todos queremos lograr para tener éxito. - **Aspiracional**. Nos empujan a llegar más allá de lo habitual. - **Aprendizaje**. Son exploraciones o experimentos para probar una hipótesis. ## URL de interés - Google’s OKR playbook - Planning company goals - OKRs and SMART goals: What’s the difference? - OKRs and KPIs: What They Are and How They Work Together - How OKR and project management work together - OKR Examples ### Plantilla .doc para definir OKRs Template_OKR.docxDescarga ### OKR VS KPI OKRKPIObjective and Key ResultKey Performance IndicatorFocus on the FutureBusiness as UsualAmbitious GoalAttainable GoalsQualitative NatureQuantitative NatureGrowth OrientedOutput Oriented ### SETTING YOUR OKR CADENCE When you’re getting started with OKRs, I suggest the following OKR cadence: Strategic EvaluationAnnuallyCompanyTactical EvaluationQuarterlyCompanyOKR EvaluationQuarterlyCompany & TeamOKR Monthly ReviewMonthlyCompany & TeamOKR Weekly Check-inWeeklyTeam & Personal ## OKRs en WordPress He creado un plugin open source para poder definir tus propios OKRs y poder hacer un seguimiento de ellos. Instálalo y empieza a crear los Objetivos, Key Results y Ciclos que necesites.**Con el shortcode **** tendrás una visión general de ellos para que todo el equipo esté alineado. Lo tienes disponible en Github: OKR-wordpress-plugin. --- ## Gestión de proyectos: Inicialización URL: https://ablancodev.com/essays/gestion-de-proyectos-inicializacion/ Este post no será nada SEO friendly, y probablemente algunas cosas se te queden cortas, ya que es sólo un post de apuntes de la primera fase en la **gestión de proyectos, la inicialización**, del curso Project Management de Google. ## Análisis coste-beneficio. El beneficio del proyecto siempre debería ser mayor al coste de este. Algunas preguntas que nos ayudarán en el **análisis de BENEFICIOS:** - ¿Qué valor crea? - ¿Cuánto dinero ahorra a la compañía? - ¿Cuánto dinero traerá de los clientes actuales? - ¿Cuánto tiempo ahorra? - ¿Mejora la experiencia del usuario? Algunas preguntas que nos ayudarán en el **análisis de COSTES:** - ¿Cuánto tiempo le llevará al personal hacer el proyecto? - ¿Cuál será el coste único? - ¿Hay algún coste continuo? - ¿Qué pasa con los costes a largo plazo? ## Componentes claves en la inicialización de proyectos. - Metas (Goals) - Alcance (Scope) - Entregables (Deliverables) - Criterio de aceptación (success criteria) - Partes interesadas (stakeholders) - Recursos (resources) Presupuesto - Personas - Material Una vez tenemos los componentes claves, deberíamos crear una carta de proyecto (**project charter**), que contiene los detalles del proyecto y nos servirá como hoja de ruta. Escuchar, escuchar y escuchar a todas las partes es fundamental en esta primera fase del proyecto para comprenderlo lo máximo posible y así poder aprender sus metas y alcances. ## Retorno de la inversión: ROI Una forma sencilla de calcularlo sería: **( G – C ) / C = ROI** Siendo: G: Ganancias financieras C: Costes iniciales y continuos **Ejemplo**: Tenemos un coste inicial de 6000€ y 25€ al mes durante un año (300€). Y se estima que el proyecto nos traerá de ganancias unos 10.000€ al final de año. Esto nos da un ROI de 0.58 (58%) Un par de artículos para entender el análisis coste-beneficio son: https://www.projectmanager.com/blog/cost-benefit-analysis-for-projects-a-step-by-step-guide https://pmstudycircle.com/cost-benefit-or-benefit-cost-analysis/ ## Goals Es el resultado deseado por el proyecto. Estos nos darán el roadmap del proyecto. Deben estar bien definidos, y ser medibles, para poder saber si llegamos o no. Una vez definamos las metas (goals), deberemos definir los entregables (productos o servicios que creamos para el cliente). Debemos documentar las entregas, para que quede constancia y nos ayuden a cuantificar y ver el impacto del proyecto. (Mirar Matriz RASCI o RACI para la asignación de responsabilidades) Las metas deben de ser lo más **SMART** posible: ### **S**pecific (específica) : Posibles preguntas: - ¿Qué quiero lograr? - ¿Por qué es una meta? - ¿Quién está involucrado? - ¿Dónde debería cumplirse el objetivo? - ¿En qué grado? ### **M**easurable (medible) - ¿Cuanto cuesta? - ¿Cuantos? - ¿Cómo sé que está completa? ### **A**ttainable (alcanzable) : Aquí es importante balancear lo que es imposible y lo que es demasiado sencillo, es decir, que suponga un esfuerzo llegar a él, pero que sea alcanzable. ### **R**elevant (relevante) : - ¿Tiene sentido esta meta? - ¿Vale la pena? - ¿Es el momento correcto? ### **T**ime-bound (temporal) : Hay una fecha de entrega o rango de tiempo. ### URL interesante sobre SMART goals: https://www.smartsheet.com/blog/essential-guide-writing-smart-goals ## Alcance del proyecto Nos indicará qué incluye el projecto, y lo que queda fuera de él, establece límites. Incluye: - Tiempos - Presupuesto - Recursos Tipos de preguntas que ayudan a conseguir información: - Stakeholders - Goals - Deliverables - Resources - Budget - Schedule - Flexibility Mejores prácticas para evitar que en el alcance se nos cuelen cosas nuevas y nos hagan desviarnos y poner en peligro el proyecto: - Define los requisitos del proyecto. - Establece una planificación del proyecto clara. Tiempos y gestión de tareas. - Define lo que está fuera de alcance. - Da alternativas. Darles alternativas les puede ayudar a ver que nuevos cambios añaden nuevos riesgos. Se puede hacer un análisis coste-beneficio si hiciese falta. - Definir un proceso de control de cambios. - Aprende a decir no. - Apunta el coste del trabajo fuera de alcance. ### **Modelo de triple restricción.** Alcance / Tiempo / Coste. Un cambio en alguno de ellos implica que el resto de métricas cambian. Artículo interesante sobre el model de triple restricción: https://thedigitalprojectmanager.com/triple-constraint/ ### **Launching vs landing** Launching es cuando lanzamos el producto al cliente (por ejemplo, publicamos una web), y landing es cuando una vaz lanzado están todas las tareas hechas y alcanza los criterios de éxito. Esto es importante que todos los stakeholders lo sepan, principalmente porque se suele caer en el error de por ejemplo, una vez lanzada la web, piensan que ya no hay nada más que hacer, y eso no suele ser así. **Success criterials (criterios de éxito)** Nos indicaran si un proyecto está o no completado y en qué medida. Para determinar los criterios de éxito: - Identifica los aspectos medibles del proyecto. - Ontener aclaraciones de los stakeholders en los requisitos y expectaciones del poryecto. Plantilla ejemplo de success criterial para un proyecto: Success-Criteria-Template.docxDescarga ## Stakeholders Podemos organizarlos por ejemplo en una «**tabla de poder**«, según su influencia (poder) y su interés. Esta PPT está muy bien, sobre todo la primera tabla. G8UtmTcJQi6FLZk3CXIu-A_39b3edd4f4644e80a92d4bb19f3302f1_Activity-Template_-Stakeholder-analysis-and-power-gridDescarga ### Gráfica RACI Ayuda a definir los roles y responsabilidades individuales o de los equipos. Hay 4 responsabilidades: **R**esponsible : Realiza la tarea **A**ccountable : Se aseguran que el trabajo se haga **C**onsulted : Dan feedback (expertos o los que son consultados antes de tomar decisiones) **I**nformed : Sólo necesitan saber las decisiones finales o que la tarea está hecha. Quizás en esta segunda tabla lo veo más intuitivo: raciDescarga Contar por ejemplo el número de Rs que recaen sobre una persona puede ayudarnos a localizar sobrecargas de trabajo. ## Recursos Incluye: **Presupuesto, personas y material**. **Presupuesto** - Equipo: Coste de las peronas que harán el trabajo. - Servicios: servicios externos que nos ayudan en el proyecto. - Materiales: Elementos tangibles que vamos a necesitar. **Personas** El equipo que construimos para llevar a cabo el proyecto. Debemos asegurarnos de tener las personas suficientes para llevar a cabo todas las tareas que requiere el proyecto. **Materiales** Pueden ser tangibles e intangibles: Discos duros, software, o formación para los empleados. ## Documentación Propuesta de proyecto (**project proposal**): Para convencer a los stakeholder del comienzo del proyecto. Es lo primero del todo, es a modo de convencer para realizar el proyecto, tiene expresiones aspiracionales. Carta del proyecto (**project charter**): Documento que define claramente el proyecto y describe los detalles para alcanzar los objetivos. Al final de la primera fase de inciio del proyecto, ya con los objetivos definidos.Suele incluir un análisis coste-beneficio (valor de negocio creado, dinero ahorrado, tiempo invertido) --- ## Prototipando con WordPress URL: https://ablancodev.com/notes/prototipando-con-wordpress/ Sin entrar a discutir si WordPress es bueno o no, ni entrar en batallas a bajo nivel, nadie podrá negar que WordPress nos permite crear páginas webs en cuestión de minutos. ## ¿Una web en cuestión de minutos? Con ciertas limitaciones claro está, pero «tiras una instalación WordPress» (expresión que odio), instalas algún tema disponible, los plugins básicos que necesites y listo. Vale, llegado a este punto tendrás tu web versión Beta Beta, pero la tendrás como punto de partida sobre la que discutir a nivel interno sobre ella, es decir, tendrás tu primer prototipo yujuuuu !! ## ¿Y si vamos un paso más allá? Si nuestra web es algo más que un catálogo o una web empresarial, y conlleva un flujo que el usuario tiene que seguir, ofrece alguna funcionalidad avanzada para el usuario final, es un Saas o cualquier otro modelo que implique al usuario como algo más que un simple «visitador». En estos casos es cuando nos interesa tener feedback del usuario lo antes posible, y aplicar metodología Agile para reducir nuestro sesgo de incertidumbre, y adaptarnos a los cambios que el producto seguro va a requerir. Con los miles y miles de plugins que hay en el repositorio oficial de WordPress seguro que encuentras uno que te ofrece esos primeros pasos en tu funcionalidad que te permitirá arrancar tu prototipo, para poder analizarlo, o incluso hacer una primera salida de producto, convirtiendo tu prototipo en tu MVP. ## Frontend Ya sabemos que en los prototipos el front no es que sea de lo que más importe, pero si deben de seguir cierta estética, o al menos cierta usabilidad, ya que en ocasiones abstraerse de un front horrendo es muy dificil, y puede que no se valide el prototipo por las partes interesadas, simplemente porque el front ha sido un freno en seco. Los editores gráficos disponibles como pueden ser Visual Composer, Elementor o mejor aún, Gutenberg nos permitirá crear «pantallas» en cuestión de minutos. Hoy en día es muy frecuente encontrarse con prototipos digitales basados en Figma, centrados en el diseño, ya que este facilita mucho el entendimiento por parte de los distintos stakeholders. Pero con falta de definición del core del producto, de su funcionalidad, de su back. ## Backend Si nuestro prototipo o MVP no sólo se queda en cuestión de mostrar pantallas, sino que vamos a necesitar montar un core de funcionalidad, tenemos que modelar parte del mundo que nos rodea, es cuando vamos a tener que hacer uso de los plugins que nos ofrece WordPress, que seguro dentro de los miles que ofrece, hay alguno que te da ese primer empujón. Si la solución va a ser más personalizada, los custom post types y taxonomías WordPress son una forma rápida de dar vida a tus diferentes clases y objetos. He creado un generador de código para poder centrarte en tu diagrama de clases más que en cómo de programan los CPTs y Taxonomías.**Generador de custom post type — Generador de taxonomías. Pues tenemos chapa y pintura (front)**, y **motor (back)**, así que ya casi casi tenemos «algo» que nos puede ayudar a entender el coche que queremos tener. Vale que crear un prototipo es sólo el primer paso, pero cuanto antes lo demos, y más flexibles al cambio seamos, mayores serán nuestras posibilidades de conseguir el producto final que el usuario quiere. Aprovecha lo que open source te da, como es WordPress y ya luego verás si sigues con él, o no. ## Ejemplo práctico de prototipado. ### Un gestor de mantenimientos de coches. Queremos desarrollar un producto digital que permita a los usuarios llevar la gestión del mantenimiento de sus vehículos. Que les notifique cuando tienen que hacer un cambio de aceite, pasar la ITV, revisar los niveles de líquidos … ### Prototipo Los distintos departamentos deciden que aunque lo ideal sería tener apps nativas para que los usuarios tengan dicho servicio, además de una web, como MVP se va a atacar a la parte web, desarrollando PWA (aplicaciones web progresivas). ### Front Los chicos de UX nos indican los distintos elementos que vamos a necesitar, y una disposición de ellos adecuada. Buscamos una plantilla «limpia», que nos dé la sensación de bocetos, y desde nuestra nueva instalación de WordPress usamos gutenberg para disponer los distintos elementos según nos han indicado desde UX. Esta primera versión nos permitirá ir discutiendo sobre la apariencia del servicio, y quizás salgan a relucir elementos que sobren o falten. ### Back Es hora de modelar el funcionamiento del producto. Con WordPress ya tenemos solucionada la parte del loguin/registros de usuarios (incluso si el servicio fuese de pago). Analizando los elementos, vemos que necesitamos definir: - **Cars**: Representa a los coches. - **Components**: Los distintos componentes de los coches susceptibles de requerir mantenimientos. - **Notifications**: Notificaciones que enviaremos a los usuarios cuando algún componente de sus vehículos necesite una revisión. Tres elementos que con el generador de CPT se implementan en 10 minutos. Es hora de añadir los atributos a los elementos. Para ellos Advanced Custom Fields es nuestro mejor amigo. Un plugin que nos permite añadir atributos a posts/páginas y cpt. Llegado a este punto tenemos un primer prototipo sobre el que poder discutir sobre el producto, no sólo a nivel estético, sino a nivel funcional. Lo que permitirá al equipo tener una primera aproximación y descubrir elementos y funcionalidades que inicialmente no se tuvieron en cuenta. --- ## Cómo definir un MVP URL: https://ablancodev.com/essays/como-definir-un-mvp/ Habrás escuchado cientos de veces el concepto de MVP (producto mínimo viable), sobre todo desde que la metodología Lean nos ha invadido en nuestro día a día. ## ¿Para qué un MVP? Pero si podemos sacar un producto completado, ¿porqué vamos a lanzar un Producto Mínimo Viable? Pues simplemente para empezar a escuchar a nuestros clientes lo antes posible. Es nuestro proyecto, nuestro niño pequeño, pero no puedes perder el foco de por y para quién está hecho, para el cliente final, por lo que todo el foco debe estar en ellos, ellos son los verdaderos Product Owners. ## ¿Por dónde empezamos? Difícil respuesta, cada producto, cada mercado y cada tipo de cliente es un mundo, y deberemos adaptarnos a ellos, pero veamos algunos puntos que nos pueden ayudar a analizar por dónde empezamos. - **Objetivo / propósito del producto.** Tener siempre presente hacia donde queremos ir es fundamental para ver los pasos que vamos a necesitar para llegar. Quizás cada uno de esos pasos sea una iteración sobre tu MVP. - **El foco, el cliente.** Pensemos en el cliente, hablemos con ellos, empecemos a escucharles antes de empezar nada. ¿Qué es lo que más valoraría sobre el producto? ¿Qué es lo mínimo minimísimo que necesita para empezar a funcionar? Mejor que él, nadie nos puede orientar para definir las características de nuestro MVP. - **Divide y vencerás.** Un clásico en el desarrollo software. Divide el todo en tareas más pequeñas, que podamos analizar, ver costes (tanto de esfuerzo como dinero). Teniendo tareas menores, podremos ver si serán parte de nuestro producto mínimo viable o no. ### M: Mínimo ¿Pero cuánto de mínimo? Siempre he estado a favor de lo mínimo posible, siempre y cuando no dejes una mala imagen del producto. Que el usuario que vaya a usar esta primera versión sea consciente de que vienen cambios, sé sincero, indícale que es una versión Beta, sin complejos. Que sepa que esto es todo … por ahora. Esto además le motivará para darte feedback, no cabrearse porque algo en concreto no vaya 100% y se sienta parte del equipo si sus aportaciones son escuchadas. ### V: Viable Quizás el punto más complicado de definir. ¿Cuanto es necesario para considerarse viable? Dificil contestación en muchos proyectos. Para mi viable es que resuelva parte del problema del usuario final. No es necesario que resuelva todos sus problemas o dé respuestas a todas sus inquietudes. Empieza resolviéndole algo, que vea que el producto es útil, y que en futuras iteraciones le podrá aportar más valor. Veamos un ejemplo muy muy sencillo: Queremos crear una página web corporativa, para dar a conocer nuestros servicios, nuestros productos, … En vez de esperar a tener una web diseñada de la muerte, con varios efectos, decenas de secciones para SEO, etc, que nos llevará un par de meses (con suerte) desarrollarla, ¿qué tal si montamos una landing con la información básica que el usuario que nos visite pueda usar, como son los métodos de contacto, y alguna que otra descripción de servicios/productos? Esto podemos tenerlo en 1 o 2 semanas, o quizás menos, el usuario tendrá información útil, y encima el señor Google puede empezar a indexarnos. **win-win ** ### P: Producto Pues eso, estamos trabajando con un producto, así que centrémosnos en el producto. Genera un buen producto, y los clientes ya vendrán. ## Be Agile my MVP Aunque son conceptos distintos, adoptar un modelo de desarrollo de productos basados en MVP, nos permitirá adaptarnos a alguna metodología agile de forma natural. Podremos por ejemplo aportar valor al producto/usuario, con cada Sprint Scrum de 3 semanas, generando y sacando a producción versiones MVP evolucionadas de una original (que sacaríamos en la primera iteración). ## Validación del MVP He aquí el tesoro de todo esto. Un MVP se desarrolla para poder tener feedback y validación por parte del cliente final lo antes posible. El producto es para ellos, no para ti como desarrollador, por lo que serán ellos los que digan qué quieren y qué no quieren. --- ## Qué es eso del Definition of done (DOD) URL: https://ablancodev.com/essays/que-es-eso-del-definition-of-done-dod/ Cuidado con presionar para que se cumplan los plazos y las entregas, ya que eso nos puede llevar a crear software de poca calidad, lo cual incrementará nuestra deuda tecnológica, hasta el punto de que en cada nueva iteración dedicaremos más tiempo a arreglos y adaptar, que a desarrollar nuevas funcionalidades. ## Definition of done (DOD) De ahí la importancia del Definition of Done (DOD), es decir, establecer las pautas que a nivel organizacional nos indican que un incremento de software se da por válido como entregado. Este DOD nos permitirá entre otras cosas ir reduciendo nuestra deuda tecnológica cuanto más estricto sea. Algunas características de un DOD: - **Lista de verificación corta y medible**: Intentar tener cosas que se puedan medir y probar, y si es de forma automática, mejor que mejor. - **Que se pueda enviar** (poner en producción). Aunque en ocasiones una vez hecho no se puede enviar el producto, debemos pensar que el Product Owner lo va a ver y lo va a querer enviar de inmediato. - **Ningún trabajo adicional**: No debería requerir ningún trabajo por parte de los desarrolladores para poder ser enviado a producción. Si lo requiere, entonces significa que no está terminado. **EL DOD es el compromiso con la calidad para el Incremento.** Aumentar el DOD es fundamental y debería ser monitorizado constantemente para conseguir software con mayor calidad, pero es sobre todo en la Retrospectiva cuando debemos evaluar nuestro DOD, ver dónde falla, dónde podemos mejorarlo. ## ¿Es el DOD lo mismo que cumplir las especificaciones? Esta es un falsa creencia o mala praxis, es decir, considerar que con cumplir las especificaciones estamos cumpliendo con el definition o done de todas las subtareas. Desarrollar podemos desarrollar, pero a muchos precios. Podemos desarrollar para cumplir las especificaciones indicadas, pero olvidándonos de crear software de calidad, que cumpla ciertos estándares y que nos asegure una integración sin problemas. Incluso podemos cumplir las especificaciones a sabiendas de ciertos «agujeros de conocimiento» que a la larga nos harán volver a abrir la tarea. Y es para eso para lo que la DOD nos debe asegurar el tercero de los puntos, es decir, que no vamos a requerir trabajo adicional una vez cumplamos con DONE. --- ## Login desde el front, redirect y otras cosas de registro URL: https://ablancodev.com/notes/login-desde-el-front-redirect-y-otras-cosas-de-registro/ ## Login / Registro desde el frontend. Si estamos en una tienda con WooCommerce, el plugin ya dispone en el core de un shortcode para ello: ## Redirect. Para WordPress, disponemos del plugin Peter’s Login Redirect que nos permite indicar las urls destino tras el login/logout o registro. Misteriosamente Peter’s no funciona con el login/registro de WooCoommerce, por lo que he creado un sencillo plugin con los filtros necesarios: ablancodev_woocommerce_redirect, disponible en GitHub. ## Aprobar nuevos usuarios Para aprobar o rechazar nuevos usuarios, podemos usar el plugin New User Approve. Este plugin por defecto manda el email de notificación al admin del website. Si queremos cambiarlo, podemos usar su filtro ‘new_user_approve_email_admins’, por ejemplo: add_filter('new_user_approve_email_admins', 'ablancodev_new_user_approve_email_admins', 10, 1); function ablancodev_new_user_approve_email_admins( $array_emails ) { return array('email1@dominio.com', 'email2@dominio.com'); } ## Vitaminando el registro En muchas ocasiones vamos a necesitar añadir campos personalizados en el registro, y tenemos 2 opciones de hacerlo: mediante código, o mediante plugin. **Mediante código:** Un ejemplo de código puede ser: function ablancodev_extra_register_fields() { ?> <p class="form-row form-row-wide"> <label for="reg_billing_first_name">Nombre de la empresa<span class="required">*</span></label> <input type="text" class="input-text" name="billing_company" id="reg_billing_company" value="<?php if ( ! empty( $_POST['billing_company'] ) ) esc_attr_e( $_POST['billing_company'] ); ?>" /> </p> <p class="form-row form-row-first"> <label for="reg_billing_phone">CIF<span class="required">*</span></label> <input type="text" class="input-text" name="billing_vat_number" id="reg_billing_vat_number" value="<?php esc_attr_e( $_POST['billing_vat_number'] ); ?>" /> </p> <p class="form-row form-row-last"> <label for="reg_billing_phone">Teléfono de contacto<span class="required">*</span></label> <input type="text" class="input-text" name="billing_phone" id="reg_billing_phone" value="<?php esc_attr_e( $_POST['billing_phone'] ); ?>" /> </p> <p class="form-row form-row-wide"> <label for="reg_billing_last_name">Nombre de la persona de contacto</label> <input type="text" class="input-text" name="contact_first_name" id="reg_billing_first_name" value="<?php if ( ! empty( $_POST['contact_first_name'] ) ) esc_attr_e( $_POST['contact_first_name'] ); ?>" /> </p> <div class="clear"></div> <?php } add_action( 'woocommerce_register_form_start', 'ablancodev_extra_register_fields' ); function ablancodev_save_extra_register_fields( $customer_id ) { if ( isset( $_POST['billing_phone'] ) ) { update_user_meta( $customer_id, 'billing_phone', sanitize_text_field( $_POST['billing_phone'] ) ); } if ( isset( $_POST['billing_vat_number'] ) ) { update_user_meta( $customer_id, 'billing_vat_number', sanitize_text_field( $_POST['billing_vat_number'] ) ); } if ( isset( $_POST['billing_company'] ) ) { update_user_meta( $customer_id, 'billing_company', sanitize_text_field( $_POST['billing_company'] ) ); } if ( isset( $_POST['contact_first_name'] ) ) { update_user_meta( $customer_id, 'contact_first_name', sanitize_text_field( $_POST['contact_first_name'] ) ); } } add_action( 'woocommerce_created_customer', 'ablancodev_save_extra_register_fields' ); El artículo del que he sacado el código, y que está muy completito es este: https://www.cloudways.com/blog/add-woocommerce-registration-form-fields/ **Mediante plugin: ** Hay muchos plugins para ello, pero el que he trasteado y me ha sido de solución, lo podemos encontrar en la store de WooCommerce, es: Custom User Registration fields for Woocommerce. Vale, son $49, pero dependiendo del proyecto, tiene todo el sentido gastarlos. ## Menú items condicionales Para la gestión de items de menú, según estés o no logueado, incluso la posibilidad de tener textos personalizados como el nombre de usuario, lo podemos gestionar con el plugin User Menus disponible en el repositorio de WordPress. Un caso práctico de los items de menú sería: - Ver «Logout» sólo para usuarios registrados. - Ver «Login para usuarios no registrados. - Tener «Mi cuenta» sólo para usuarios registrados, o incluso en vez de llamarlo «Mi Cuenta», que se llame «Hola Antonio». --- ## Estás usando mal los cupones descuentos URL: https://ablancodev.com/notes/estas-usando-mal-los-cupones-descuentos/ Si, si, **estás usando mal los cupones descuentos**. La mejor forma de usar los cupones descuentos en ecommerce, es **desactivarlos**. Los cupones **te pueden hacer disminuir tus ventas**. ¿Qué los cupones descuentos me hacen disminuir mis ventas? ¿Pero qué me cuentas? Pues si, aunque parezca completamente lo contrario, ya que solemos crear cupones descuentos para aumentar nuestras ventas, ponte en el lugar de nuestro posible cliente. Llega a nuestra tienda, añade los productos al carrito. Pasa a la página de carrito, lo revisa, todo correcto, pasa al checkout, y …. OH sorpresa,* «puedo usar cupones, pero no tengo ninguno»*, y lo primero que piensa es, *«fijo que en internet hay algún cupón dando vueltas, que puedo usar, voy a Googlear un poco»*. ¿Qué ocurrirá después? Pues ni idea, quizás vuelva, quizás no vuelva, no lo sabemos. Lo que sí sabemos es que **estaba con la tarjeta en la mano para pagar, y ahora la tiene guardada** y está dando vueltas por internet. Espero que con este ejemplo práctico hayas podido ver a lo que me refiero cuando digo que la mejor forma de usar los cupones descuentos, es no usarlos. **¿Qué hacemos con las campañas promocionales sin cupones?** Llega el Black Friday, la campaña de navidad o cualquier otra campaña que queremos lanzar para aumentar nuestros ingresos, y hasta ahora siempre hemos creado cupones, ¿qué hacemos ahora? Normalmente los cupones que usamos son cupones para conseguir un descuento del % sobre nuestro carro de compra. Lo que podemos conseguir si aplicamos dicho descuento sobre los productos, poniéndolos directamente en oferta mientras dure la promoción. ## Veamos un ejemplo: un 30% de descuento para el Black Friday. ### **Con cupones:** Usando cupones descuentos, ¿qué hacíamos? Creábamos el cupón BLACKFRIDAY que al ser aplicado, descontaba un 30% en el carrito. Lo dábamos a conocer por todos sitios, y en la web poníamos o un banner o un modal con algo como: «Obtén un descuento del 30% por el Black Friday usando el cupón: BLACKFRIDAY» Ya no sólo tenemos el problema de «la fuga» en busca del cupón perdido, sino que obligamos al usuario a hacer más acciones antes de finalizar la compra, teniendo que escribir el cupón en el checkout. ### Sin cupones: Sin necesidad de usar cupones, evitando no sólo tener un punto de fuga, sino que facilitamos al usuario el proceso de compra y no tener que hacer nada adicional. Así que ponemos toda la tienda «en oferta» al 30%, tranquilo, no tienes que ir producto a producto, hay plugins para ello. Y en nuestro banner / modal, podemos poner algo como: «Tiene toda la tienda al 30% de descuento por el Black Friday» **Sin punto de fuga, y poniéndoselo al usuario lo más sencillo posible.** Es por todo esto por lo que siempre digo que la mejor forma de usar los cupones descuentos en nuestras tiendas online, es desactivándolos. --- ## Botón Añadir al carro con connectif URL: https://ablancodev.com/notes/boton-anadir-al-carro-con-connectif/ Marketsostenibles.es es uno de los proyectos en los que trabajo, y en los que tienen integrada una solución de marketing automation llamada Connectif. Sin entrar mucho en detalle, Connectif permite entre otras muchas funcionalidades ofrecer al usuario productos relacionados , que aplicando inteligencia artificial, en principio serán propicios o con alta probabilidad de ser clickados por el usuario. Ellos ya ofrecen una integración oficial con WordPress, pero en el diseño que ofrecen, no tienen la posibilidad de ofrecer un botón de «Añadir al carrito», así que ha tocado arremangarse y al lío. Aunque no tenemos dicha opción en Connectif, lo que sí nos permite es añadir javascript, así que llamaremos a una función anadirAlCarro( url ), que se encargará de ello. Ya tenemos el botón de añadir al carro en connectif, ahora nos toca hacer magia en WordPress. El planteamiento general será que la función anadirAlCarro() haga una llamada ajax, pasándole la url del producto (ya que desde Connectif no tenemos el ID del producto, pero sí su url). Añadimos nuestro código javascript con wp_head add_action('wp_head', 'ablancodev_script_add_cart'); function ablancodev_script_add_cart() { ?> <script> function anadirAlCarro(p_url) { var data = { action: 'woocommerce_ajax_add_to_cart', product_url: p_url, product_sku: '', quantity: 1 }; jQuery.ajax({ type: 'post', url: wc_add_to_cart_params.ajax_url, data: data, success: function (response) { if (response.error && response.product_url) { window.location = response.product_url; return; } else { jQuery(document.body).trigger('added_to_cart', [response.fragments, response.cart_hash]); } }, }); } </script> <?php } Teniendo ya la parte de front, vamos al back, y hagamos que el servidor responda a nuestra llamada **ajax**. add_action('wp_ajax_woocommerce_ajax_add_to_cart', 'woocommerce_ajax_add_to_cart'); add_action('wp_ajax_nopriv_woocommerce_ajax_add_to_cart', 'woocommerce_ajax_add_to_cart'); function woocommerce_ajax_add_to_cart() { $product_id = apply_filters('woocommerce_add_to_cart_product_id', url_to_postid(sanitize_text_field($_POST['product_url']))); $quantity = empty($_POST['quantity']) ? 1 : wc_stock_amount($_POST['quantity']); $passed_validation = apply_filters('woocommerce_add_to_cart_validation', true, $product_id, $quantity); $product_status = get_post_status($product_id); if ($passed_validation && WC()->cart->add_to_cart($product_id, $quantity) && 'publish' === $product_status) { do_action('woocommerce_ajax_added_to_cart', $product_id); if ('yes' === get_option('woocommerce_cart_redirect_after_add')) { wc_add_to_cart_message(array($product_id => $quantity), true); } WC_AJAX :: get_refreshed_fragments(); } else { $data = array( 'error' => true, 'product_url' => apply_filters('woocommerce_cart_redirect_after_error', get_permalink($product_id), $product_id)); echo wp_send_json($data); } wp_die(); } Y con eso ya tenemos nuestro botón **«Añadir al carro»** de Connectif 100% funcional en nuestro WooCommerce. 🚀 👨🏻‍💻 🤟🏻 --- ## Qué tipo de Product Owner eres URL: https://ablancodev.com/essays/que-tipo-de-product-owner-eres/ Dentro del perfil de Product Owner podemos encontrar diferentes tipos, ya que no todas las empresas y proyectos permiten ejecutar nuestra labor de Product Owner tal y como quisiéramos. Los distintos tipos de Product Owner que podemos encontrar son: ## El escriba Prácticamente tu labor es capturar requisitos de las distintas reuniones. Aunque estás en el lado tecnológico, te verás sin capacidad de decisiones, con labores de simple secretariado. ## El Proxy Aunque tienes un perfil tecnológico, sueles ser visto como miembro de negocio, por tus conocimientos de analítica. Al estar entre ambos equipos y hacer de punto de comunicación, en la mayoría de los casos incluyes retrasos, ya que tu contestación en la mayoría de las ocasiones será: «lo pregunto …» ## El representante del negocio Ya más del lado de negocio, con un mayor conocimiento del dominio y expectaciones de los stackeholders. Aunque suele ser un símbolo del compromiso de negocio con el producto, sueles estar muy limitado en la toma de decisiones propias. ## El Sponsor del negocio Es un paso más en role de Product Owner. Como parte del negocio, tienes la capacidad de tomas de decisiones financieras y de producto. El equipo de desarrollo se puede centrar más en sus tareas, y ser más eficientes. ## El empresario o emprendedor Es alguien que se está gastando su propio dinero para llevar a cabo el desarrollo tanto del negocio como del producto. Esto le da total responsabilidad sobre la toma de decisiones, tanto empresariales como de producto. Aunque en la mayoría de las empresas no es el caso, ya que tú eres el Product Owner, y no uno de los socios, tener esta mentalidad «startupera», considerar el producto como tuyo, y tener el foco en el ROI, te llevará a tener este role de Product Owner. El **escriba y proxy** son roles que se encuentran en el *receiving side*, es decir, que ellos **reciben por parte de terceros las mejoras propuestas sobre el producto**. Los roles de **representante del negocio, sponsor o empresario**, se encuentran más en el *initiating side*, es decir, que **proponen o inician mejoras sobre el producto**. ## ¿Cuál es tu role de Product Owner? Teniendo identificados los distintos tipos, puedes ubicarte dentro de ellos, pero de forma sincera, no te preocupes si no eres el tipo que te gustaría ser, no siempre depende de ti, de tus aptitudes ni capacidades. Son muchos los tipos de empresas, y cada una te permitirá llegar a un determinado escalón. --- ## Qué es el game loop URL: https://ablancodev.com/essays/game-loop/ 👋 **Un segundo antes de empezar:** si has aterrizado aquí buscando *GameLoop*, el emulador de Android para jugar a PUBG Mobile y compañía… esto no es eso 😅. Aquí no vas a descargar nada. Vamos a hablar de **ingeniería y patrones de software**: del bucle que hace latir a cualquier videojuego por dentro. Si te pica la curiosidad por entender *cómo funcionan las cosas por debajo del capó*, quédate, que te va a gustar. Si vas a aprender a desarrollar videojuegos, o ya te dedicas a ello, habrás oido hablar del **«game loop»**. En este post te comento desde el game loop clásico hasta el enfoque moderno: - Loop clásico - Loop con frame limiting - Frame callback moderno ## Loop clásico El Game Loop es el corazón básico de todo juego, el bucle principal que hace que todo funcione. Piensa en cualquier tipo de videojuego, ¿qué ocurre mientras estás jugando? - Pasan cosas en la pantalla. Se mueven los enemigos 👾 , suena la música, o incluso nuestro player hace algo. - Si interactúas, por ejemplo moviendo el joystick 🕹 , gamepad 🎮, o pulsando alguna tecla ⌨️ , el juego reacciona a dicho estímulo, y volvemos a esperar otro estímulo. ### ¿Cómo funciona un game loop? Si traducimos esto a pseudo-código, nos quedaría un bucle tal que así: while (true) { processInput(); update(); render(); } **processInput**: Procesa la entrada del usuario desde la última iteración. **update**: Actualiza el estado del juego un paso más. Ejecutando la inteligencia artificial o la física en caso de haberlas. **render**: Muestra los resultados del nuevo estado, en pantalla. Si ejecutamos este bucle infinito, las instrucciones se ejecutarán unas tras otras, tan rápidas como la máquina nos lo permita, y así de forma indefinida, por lo que nos encontramos con 2 problemas principales: - La sobrecarga, el procesador estará funcionando al 100%. - La velocidad, dependiendo de la potencia de la máquina, las iteraciones del bucle se ejecutarán en mayor o menor tiempo, dando lugar a una ejecución más o menos lenta del juego. Por lo que aunque ya tenemos nuestro game loop básico, tendremos que mejorarlo para evitar dichos problemas. ## Loop con frame limiting ¿Y si ponemos una espera tras cada ciclo? Nos quedaría algo como: while (true) { processInput(); update(); render(); sleep( time ); } Con *sleep*, «dormiríamos» el proceso un determinado tiempo, para una vez transcurrido, volver a iterar en nuestro bucle. Recuerda, que comentamos que dependiendo de la potencia de nuestra máquina, tardará más o menos en procesar las instrucciones, y esto podría hacer que nuestro juego vaya a diferente velocidad, por lo que tendremos que tener en cuenta dicha velocidad. Para ello, se suele establecer una velocidad deseada, por ejemplo 60 FPS (frames por segundo), que significa que cada frame se ejecuta cada 1/60 = 16 milisegundos. Así el tiempo que tenemos que dormir será de 16 milisegundos, menos el transcurrido en ejecutar processInput() + update() + render(). Quedando el código: while (true) { var start = getCurrentTime(); processInput(); update(); render( start + (1/FPS) - getCurrentTime() ); } Con esto tendríamos un Game Loop más «estable». ### Implementación paso a paso Veamos ahora un ejemplo muy básico desarrollado en javascript (que me perdonen los expertos en javascript, que seguro hay mejores formas de hacerlo a nivel de código). Partimos de un HTML muy básico, con un div que representará nuestra «paleta», que moveremos con las teclas k y l a izquierda y derecha respectivamente. <html> <head> <title>Primer juego</title> <script src="game.js"></script> <style> .racket { background-color: red; width: 5%; height: 15px; position: absolute; bottom: 10px; left: 100px; } </style> </head> <body style="background-color: #000;"> <div id="racket" class="racket"></div> </body> </html> Ya tenemos nuestra raqueta colocada, Play Now !! const KEY_NONE = false; const KEY_LEFT = 'a'; const KEY_RIGHT = 'd'; let keyPressed = KEY_NONE; let direction = ''; let position = 100; window.onload = function() { setTimeout(gameloop, 100); }; function gameloop() { play(); // Llamamos a gameloop() la siguiente vez. Esto emula el while(true) setTimeout(gameloop, 100); } // Loop principal del juego function play() { processInput(); update(); render(); } function processInput() { if ( keyPressed == KEY_LEFT ) { direction = '+'; } else if ( keyPressed == KEY_RIGHT ) { direction = '-'; } } function update() { if ( direction == '+' ) { position = position + 10; } else if ( direction == '-' ) { position = position - 10; } // Aquí actualizaríamos el estado del juego, y otros elementos como enemigos } function render() { let racket = document.getElementById('racket') racket.style.left = position + "px"; } // ------- document.addEventListener('keydown', function(e){ if ((e.key === KEY_LEFT) || (e.key === KEY_RIGHT)) { keyPressed = e.key; } }); document.addEventListener('keyup', function(e){ if ((e.key === KEY_LEFT) || (e.key === KEY_RIGHT)) { keyPressed = KEY_NONE; } }); function sleep(ms) { var unixtime_ms = new Date().getTime(); while(new Date().getTime() &lt; unixtime_ms + ms) {} } Aunque no es mi fuerte javascript, he querido implementarlo en este lenguaje, para que veamos una forma diferente de implementar el while(true), y el sleep(), pero que veamos en esencia de que se trata de lo mismo, es decir, un bucle «infinito», y una espera en cada iteración. Y ya tenemos nuestro cutre-juego interactuando con nosotros. *Game Beta-Beta-Beta ## Frame callback moderno Hasta ahora hemos visto el enfoque clásico donde el juego controla completamente el loop principal. Este modelo sigue siendo válido y continúa utilizándose en muchos engines y librerías actuales. Sin embargo, en plataformas modernas como navegadores o sistemas móviles existe otro enfoque muy común: el frame callback. En este modelo el sistema operativo o el navegador notifican cuándo es el momento adecuado para generar el siguiente frame. ### ¿Por qué apareció este modelo? Con el tiempo aparecieron nuevos problemas: - Consumo energético - Gestión térmica - Multiples aplicaciones compartiendo GPU - Monitores con refresco variable - Sistemas multitarea complejos - Navegadores y compositores gráficos En este contexto, dejar que cada aplicación ejecutase un loop infinito sin coordinación empezó a ser poco eficiente. Por eso los sistemas modernos comenzaron a ofrecer «APIs» sincronizadas con el refresco real de pantalla. El caso más conocido: requestAnimationFrame En JavaScript moderno el patrón habitual es: function gameLoop() { update(); render(); requestAnimationFrame(gameLoop); } requestAnimationFrame(gameLoop); Aquí ya no existe un while(running) manual. En su lugar: El navegador decide cuándo debe renderizarse el próximo frame - Invoca nuestro callback - El juego actualiza y renderiza - Se solicita el siguiente frame ### ¿Qué ventajas tiene? - Sincronización automática con el monitor - El navegador coordina el renderizado con el refresco real de pantalla. - Reduce frames innecesarios - Menos consumo de CPU y batería. Con requestAnimationFrame, el navegador normalmente limitará el ritmo a 60 FPS o 120 FPS o el refresco disponible. El navegador puede pausar pestañas ocultas, reducir FPS en segundo plano, coordinar GPU y compositor u optimizar el scheduling. Todo esto ocurre automáticamente. El game loop sigue existiendo, es importante entender que el concepto de game loop no desaparece. Seguimos teniendo: input**updaterender La diferencia es únicamente quién controla el ritmo de ejecución, antes era nuestro juego quien decidía cuando generar frames, y ahora es el navegador o sistema. Frame callback en otras plataformas: - iOS con CADisplayLink - Android con Choreographer - Motores modernos sincronizados con vsync - APIs gráficas integradas con compositores modernos Y en plataformas modernas, especialmente web y móviles, este segundo enfoque suele ser el predominante. ## Aplicando un game loop en un juego real (Godot + vibe coding) Si en esta nueva era en la que la IA ha venido a darnos super poderes, lo tuyo es el vibe coding, que sepas que puedes conectar motores como Godot con ella. En este vídeo puedes ver cómo se aplica este concepto al crear un juego real con Godot. Aunque no se explica directamente el término «game loop», verás claramente cómo se repite el ciclo de actualización y renderizado. ## Del game loop a cualquier sistema que construyas Fíjate en la idea de fondo del game loop: observar, decidir, actuar… y repetir**. Ese mismo ciclo —input, update, render— es el esqueleto de casi todo lo que construyo fuera de los videojuegos: agentes de IA que iteran sobre un problema hasta resolverlo, automatizaciones que vigilan algo y reaccionan, herramientas que se corrigen solas. Cambia el vocabulario, pero el patrón por debajo es el mismo. Si te ha enganchado esto de entender cómo se piensan y se construyen las cosas* (que es de lo que va esta web), te dejo unos cuantos ensayos y experimentos donde ese mismo criterio se ve en acción: - Vibe Coding nivel PRO con los Simpsons: Ralph — el bucle iterativo llevado al extremo: agentes de IA que trabajan solos, iteración tras iteración. - Creo mi propio «Lovable» con Pencil CLI y Claude Code — de una idea suelta a una herramienta propia que funciona. - Historias de usuario vs Casos de uso — otro patrón de fondo, esta vez para pensar bien el *qué* antes de lanzarse al *cómo*. --- ## Usando la API de WordPress URL: https://ablancodev.com/notes/usando-la-api-de-wordpress/ ¿No lo sabías?, pues sí, **WordPress viene por defecto con una API REST implementada**, que podemos usar para lo que necesitemos. Esto nos permite usar WordPress como un verdadero CMS, encargado del contenido, y aislarlo de la vista o vistas que queramos implementar. A esto se le viene llamando **WordPress Headless**. Usaremos la API para conectar nuestro WordPress con las distintas vistas, por lo que vamos a ver qué tenemos por defecto, y qué mejoras vamos a necesitar. Empecemos con un par de endpoints básicos: **/wp-json/wp/v2/posts** : Consulta el listado de posts **/wp-json/wp/v2/posts/123** : Consulta la información de un post en concreto. Ambas llamadas nos darán información sobre los posts de nuestro blog, como puede ser sus títulos, contenidos, categorías, etc … Vamos a implementar un cliente php independiente que consuma parte de esta información, por ejemplo para listar los posts, mostrando su imagen, título y descripción. Primero vamos a consultar la información del listado de posts, por lo que tendremos que consultar el endpoint /wp-json/wp/v2/posts $url = 'https://wp/wp-json/wp/v2/posts'; $file = @file_get_contents($url); $json = json_decode($file, true); Con file_get_contents hacemos la llamada a la API, consiguiendo los datos en formato json. Cuyo resultado decodificamos, convirtiéndolos en un array con json_decode. Ya tendríamos la información de los posts disponibles para recorrerlos e irlos mostrando. Pero si nos fijamos en el json que nos proporciona WordPress, no tenemos la imagen destacada directamente, sino que tenemos el id del objeto media que tiene información sobre ella, por lo que para cada uno de los posts, tendremos que hacer una llamada adicional a la api, concretamente al endpoint **wp-json/wp/v2/media/123** (si 123 es el id de featured_media). $url = 'https://wp/wp-json/wp/v2/media/' . $post['featured_media']; $file = @file_get_contents($url); $json_media = json_decode($file, true); Entre los distintos datos que nos proporciona /media, tenemos la url de la imagen, así como información sobre las distintas miniaturas que genera WordPress. En nuestro caso, como estamos implementando un listado de posts, vamos a usar el tamaño medium. <img src="<?php echo $json_media['media_details']['sizes']['medium']['source_url'];?>" width="<?php echo $json_media['media_details']['sizes']['medium']['width'];?>" height="<?php echo $json_media['media_details']['sizes']['medium']['height'];?>"/> Con esto, ya tendríamos todo lo necesario para nuestro cliente que consumirá la API de WordPress, recorriendo los posts, y mostrando información sobre ellos. El código final quedaría: <html> <head> <title>Cliente API</title> </head> <body> <ul> <?php $url = 'https://wp/wp-json/wp/v2/posts'; $file = @file_get_contents($url); $json = json_decode($file, true); if ( $json && (is_array($json)) ) { foreach ( $json as $post ) { $url = 'https://wp/wp-json/wp/v2/media/' . $post['featured_media']; $file = @file_get_contents($url); $json_media = json_decode($file, true); ?> <li> <?php if ( $json_media && (is_array($json_media)) ) { ?> <img src="<?php echo $json_media['media_details']['sizes']['medium']['source_url'];?>" width="<?php echo $json_media['media_details']['sizes']['medium']['width'];?>" height="<?php echo $json_media['media_details']['sizes']['medium']['height'];?>"/> <?php } ?> <h2><?php echo $post['title']['rendered'];?></h2> <p><?php echo $post['excerpt']['rendered'];?></p> </li> <?php } } ?> </ul> </body> </html> Con el que obtendríamos como resultado, sin formato alguno, simplemente html plano: Ahora piensa en una app móvil, software de escritorio o las mil posibilidades que te da tener el contenido centralizado, en la nube, y el consumo de ellos por todo tipo de clientes. 🚀 🚀 --- ## Cómo añadir imagen de usuario con ACF URL: https://ablancodev.com/notes/como-anadir-imagen-de-usuario-con-acf/ Pues sí, con Advanced Custom Fields podemos añadir campos a los usuarios, y entre los distintos tipos de campos, tenemos el tipo Image, que es el que nos interesa, y así no tener de depender de Gravatar para que cada usuario tenga su propia imagen. 3 pasos 3, y ya lo tendremos funcionando: **1.- Descarga** este fichero json con la configuración para ACF, e impórtalo a ACF desde *Campos Personalizados –> Herramientas*. **2.-** Ya desde la** página de perfil** de cada usuario está disponible el nuevo campo para subir la imagen personalidad. **3.**– Si queremos **usarlo desde código** por ejemplo en una página de un post, tan sencillo como: &lt;?php $author_id = get_the_author_meta( 'ID' ); $imagen_author = get_field('imagen', 'user_' . $author_id); ?> &lt;img src="&lt;?php echo $imagen_author['url'];?>" alt="&lt;?php echo esc_attr($imagen_author['alt']);?>" title="&lt;?php echo esc_attr($imagen_author['title']);?>"/> Se usa como cualquier otro campo, salvo que al llamar a get_field(), en el segundo parámetro no se le pasa el ID del objeto, sino que lleva el prefijo «user_», y a continuación el ID del usuario. ¿Interesa instalar Advanced Custom Fields sólo para esto? pues claro que no, ya que tenemos otros plugins más livianos para poder añadir una imagen a los usuarios. Pero como ACF suele ser un plugin muy útil, principalmente para dinamizar nuestros propios temas, y se suele usar en multitud de proyectos, si lo tenemos, usémoslo y no añadamos más plugins. --- ## Crear custom taxonomies para Woocommerce URL: https://ablancodev.com/notes/crear-custom-taxonomies-para-woocommerce/ En ocasiones **necesitamos organizar nuestros productos** por criterios más allá que por categorías y etiquetas. Imaginemos que estamos montando una academia online, en la que queremos que **los cursos se puedan organizar por el profesor**, y permitir **listar los cursos de un determinado profesor**. ## Modelo de datos Para ello vamos a crear una **taxonomía ‘teacher’**, que nos permita dicha agrupación. En principio, como atributos necesitaremos: - Nombre: El nombre y apellidos del profesor. - Descripción: Una descripción sobre él, que corresponderá al content principal. - Imagen: La fotografía del profesor El código para la creación de dicha taxonomía sería: // Teacher $labels = array( 'name' => _x( 'Profesor', 'mi-dominio' ), 'singular_name' => _x( 'Profesor', 'mi-dominio' ), 'search_items' => __( 'Buscar profesores', 'mi-dominio' ), 'popular_items' => __( 'Profesores frecuentes', 'mi-dominio' ), 'all_items' => __( 'Todos los profesores', 'mi-dominio' ), 'parent_item' => null, 'parent_item_colon' => null, 'edit_item' => __( 'Editar profesor', 'mi-dominio' ), 'update_item' => __( 'Actualizar profesor', 'mi-dominio' ), 'add_new_item' => __( 'Añadir nuevo profesor', 'mi-dominio' ), 'new_item_name' => __( 'Nuevo nombre de profesor', 'mi-dominio' ), 'separate_items_with_commas' => __( 'Separar profesores por comas', 'mi-dominio' ), 'add_or_remove_items' => __( 'Añadir o eliminar profesor', 'mi-dominio' ), 'choose_from_most_used' => __( 'Elegir entre los profesores más frecuentes', 'mi-dominio' ), 'menu_name' => __( 'Teachers', 'mi-dominio' ), ); register_taxonomy( 'teacher', 'product', array( 'hierarchical' => false, 'labels' => $labels, 'show_ui' => true, 'show_admin_column' => true, 'query_var' => true, 'show_in_rest' => true, 'rewrite' => array( 'slug' => 'teacher', 'hierarchical' => false ), )); En la definición de la taxonomía, cabe destacar el custom post type al que se le asocia, que en nuestro caso es para *‘product’*, que es como se definen los productos en Woocommerce. ## Vistas Ya tenemos nuestro modelo de datos definido e implementado, pasamos a las vistas que necesitaremos. Al ser una forma de aglutinar elementos, queda claro que su vista debería ser una forma de listarlos, o lo que se suele denominar en WordPress, ‘archive’. Si nos fijamos en la jerarquía de plantillas proporcionada por WordPress. https://developer.wordpress.org/themes/basics/template-hierarchy/ Nuestra nueva taxonomía directamente tendría una vista preparada, ya que por lo general cualquier tema dispone de un archivo archive.php, o si me lo pones más difícil, un index.php Que nuestro tema dispone de un taxonomy.php, pues será usado. Pero si queremos dar cierto toque estético a nuestra nueva taxonomía, ya que al tratarse de profesores, quizás queramos añadir su ficha biográfica, o experiencia docente, bastará con crear un **taxonomy-teacher.php**, copiando por ejemplo del taxonomy.php o archive.php existente, y que modificaremos a nuestro antojo. Y de esta forma tan sencilla hemos creado una nueva forma de organizar los productos de nuestra tienda, por algún atributo común, creando incluso su propia vista. --- ## Mide Kanban y vencerás URL: https://ablancodev.com/essays/mide-kanban-y-venceras/ **Kanban** es un método de gestión del trabajo a realizar en los distintos proyectos con los que nos encontramos, el cual sigue una **estructura pull**, con la que serializamos las tareas a realizar, las cuales seguirán un camino, desde su aparición, hasta su consecución. Con ello lo que se pretende es **optimizar el flujo de trabajo** y mantener un ritmo estable y eficiente. Como siempre cuando aparece el término **eficiente**, sabemos que va directamente ligado a las tres palabras claves … **medir, medir y medir**. **Para saber, hay que medir.** Durante el ciclo de vida de nuestras tarjetas, estas pasarán por distintos estados, columna tras columna, siguiendo un camino, que deberemos estudiar si queremos mejorar nuestra eficiencia en el desarrollo del proyecto. Dependiendo de qué queramos analizar, la estructura de nuestro equipo, o la naturaleza de nuestro proyecto, nos podemos encontrar distintos flujos de trabajo. Personalmente suelo usar 2 modelos base sobre los que trabajar: - **En función del estado de la tarea**. Partiendo del flujo básico: **To Do –> In Progress –> Done**, al que podemos definir estados (columnas) adicionales como To Validate u otros que consideremos oportunos según cada proyecto. - **En función del departamento**. Si las tareas a desarrollar van a requerir pasar por distintos departamentos o personas, nos puede interesar definir columnas por cada departamento y que la tarjeta evolucione por ellos hasta llegar a un definitivo Done. Un ejemplo web sería: To Do –> Design –> FrontEnd –> BackEnd –> Testing –> Done Una vez tenemos definido nuestro flujo de trabajo, es hora de empezar a trabajar y medir. Según nuestro día a día, la evolución del proyecto, del equipo, o las mediciones que consigamos, harán que nuestro flujo de trabajo pueda cambiar, teniendo que añadir/eliminar columnas según vayamos viendo. Vamos a ver algunas de las métricas que nos ofrece Kanban para mejorar nuestros flujos de trabajo, y por consiguiente la eficiencia de nuestro proyecto. ## Lead Time **El Lead Time nos permite saber cuánto tardamos en responder a una necesidad del usuario**. Mide el tiempo que transcurre desde que una necesidad es identificada (entra en nuestro flujo de trabajo) hasta que se entrega (sale del flujo de trabajo). Es decir, mide el ciclo de vida de una actividad. En términos más concretos, el Lead Time de un PBI (Product Backlog Item) mide el tiempo que ha transcurrido desde que éste se creó en el Product Backlog hasta que se puso en DONE, pasando por todos los estados intermedios que tenga nuestro flujo de trabajo particular: priorización, desarrollo, pruebas de calidad, etc. Como vemos, el Lead Time va más allá del tiempo que tardo en desarrollar una tarea (este tiempo lo mide el Cycle Time, que veremos a continuación). Desde el punto de vista del usuario, es el tiempo que él tiene que esperar desde que me pide algo hasta que yo se lo entrego. ## Cycle Time El Cycle Time es similar al Lead Time, pero comienza a medir desde que la tarea empieza a desarrollarse. En otras palabras, el Cycle Time mide el tiempo transcurrido desde que un PBI entra en IN PROGRESS hasta que se pone en DONE. ¿En qué se diferencia del Lead Time? El Cycle Time no nos da información sobre cuánto hago esperar a mi usuario, sino sobre **cuán eficiente soy, como Development Team, en realizar el desarrollo de una tarea y entregarla**. **Un ejemplo: considerando el tiempo que los elementos pasan en el Backlog, un PBI podría tener un Lead Time muy alto y un Cycle Time muy corto: la diferencia es, casi en su totalidad, el tiempo que he tardado como Product Owner en priorizar ese PBI. ## WIP El WIP, o Work In Progress, no mide un período de tiempo transcurrido, sino que es un indicador que nos permite saber cuántos elementos tengo siendo desarrollados en un instante concreto. Es decir, es la cantidad de PBIs que se encuentran en IN PROGRESS en un momento dado. Pero ¿a qué nos referimos con IN PROGRESS? Parece evidente, pero no siempre lo es. Los tres estados naturales de cualquier acción son “Por hacer”, “Haciéndose” y “Hecho”. No hay ninguno más, es lógicamente imposible. Sin embargo, en nuestro flujo de trabajo seguramente nos encontremos la necesidad de desglosar estos estados en actividades o fases** más pequeñas para visualizar y gestionar más fácilmente mis procesos. Cuando planteemos estas nuevas fases, veremos que encajan en uno de los tres estados naturales: si quiero incluir una fase que sea QA para saber qué tareas tengo que testear, veré que es un estado de progreso, porque ni está por empezarse ni se ha terminado aún. Por otra parte, tener dos fases en TO DO como las de la imagen superior nos permite diferenciar entre los PBIs que están en el Backlog y los que están priorizados y listos para su desarrollo. A esto es a lo que nos referíamos hace unos párrafos cuando hablábamos de la optimización de nuestro flujo de trabajo. Volviendo al WIP: si tengo varias fases de progreso, ¿qué tengo que medir, entonces? Recordando el objetivo de esta métrica, que es saber la cantidad de PBIs que tengo en progreso, debemos medir el estado IN PROGRESS total. Sin embargo, medir cada una de las fases internas nos dará visibilidad sobre su estado de salud. ¿Pocas cosas en “Development” y muchas en “QA”? Suele ocurrir. Mide el WIP de cada una de ellas y tendrás información para mejorar tus procesos. ## Throughput Si conoces la *velocidad* de Scrum, esta métrica te será familiar. El Throughput mide la **cantidad de elementos de trabajo que termino en un período de tiempo**. A diferencia de su hermana, el Throughput no mide Story Points o Historias de Usuario, sino el total de PBIs que entrego en un período de tiempo, independientemente de su naturaleza. ¿Y qué período de tiempo es ese? Trabajando en Kanban, que no tiene iteraciones, será el que decidamos como equipo. Podemos comenzar midiendo lo que entrego en una semana o en un mes y mantener esa unidad de medida estable, pero adaptarla en el futuro si siento la necesidad de aumentar o reducir el período de tiempo que mido. --- ## Activar gutenberg en los Productos de Woocommerce URL: https://ablancodev.com/notes/activar-gutenberg-en-los-productos-de-woocommerce/ Trabajar con la versión «básica» del editor en un punto tan importante y a la vez tan crítico de tu tienda online, puede hacerte bajar tu porcentaje de conversión. Así que no dependamos sólo de la funcionalidad que nos pueda ofrecer una plantilla u otra, pasemos a la acción, usando el editor Gutenberg en los productos de Woocommerce y vitaminemos esas descripciones. Añadiendo el siguiente código en tu functions.php podrás activarlo y usarlo. function activar_gutenberg_products($can_edit, $post_type){ if($post_type == 'product'){ $can_edit = true; } return $can_edit; } add_filter('use_block_editor_for_post_type', 'activar_gutenberg_products', 10, 2); --- ## Recoger en tienda, monta tu McAuto URL: https://ablancodev.com/essays/recoger-en-tienda-monta-tu-mcauto/ Montar una tienda online de tu negocio físico no es algo tan trivial como puede parecer, entran muchos factores en juego. A veces pensamos que montar una versión online de nuestra tienda es simplemente elegir una plantilla bonita, configurar 4 cosas y listo. Pero nos olvidamos realmente de lo importante, el propio negocio. Los principales problemas que nos encontramos cuando queremos tener la versión online de nuestro negocio son: - **Control de Stock:** Tenemos dos fuentes de consumo de productos, la tienda física, y la tienda online, por lo que el control es importante. Aunque hay opciones de integrar ecommerces como sistemas ERP, en la mayoría de los casos nos vale con ir liberando cantidades pequeñas en la tienda online. Por ejemplo, si tenemos 5 sillas, podemos decir que en tienda tenemos 3, y online 2, y según se vayan vendiendo, irlo ajustando. Bastante tedioso si, pero una solución viable en una primera fase de nuestra versión online. - **Envíos:** Busca empresas de envío, calcula precios y tiempos, prepara los productos que tienen que salir diariamente para cuando llegue el chico de reparto, y un sin fin de quebraderos de cabeza. ## ¿Y si montamos una versión semi-online? Con semi-online me refiero a tener nuestra versión de tienda online, pero con la opción que conocemos como «**recoger en tienda**«. Con ello nos quitaríamos los quebraderos de cabeza que implica todo el sistema de envíos, los clientes una vez que nos compran, pueden venir a la tienda a recoger el producto. Este método lleva ya bastante tiempo siendo implementado por grandes negocios online, pero en la coyuntura mundial en la que nos encontramos actualmente con el COVID-19, cobra mucho más sentido. - Por **control de aforo** dentro de los negocios, reducimos considerablemente su tiempo en tienda, y el aforo, pero no así las ventas conseguidas. Evitando a los clientes tiempos de esperas y largas colas, con lo que lo agradecerán. - Estaremos **abiertos sí o sí.** En caso de tener que cerrar físicamente el negocio, tendremos nuestro plan B preparado, con el que seguiremos vendiendo y simplemente la gente vendrá para recoger los productos. ## Algún ejemplo ### Quien tiene un restaurante tiene un McAuto McAuto es marca registrada por McDonals y ni es de mi propiedad, ni tengo nada que ver con ella (ya me gustaría a mi). **Tienes un restaurante / bar** con tu clientela más o menos fija, que adora tus albóndigas con tomate. ¿Y si las vendes no sólo a los comensales que van al local? Monta tu **tienda online**, configurada con la opción de **recoger en local**, empieza a preparar tuppers **y a vender** !! Sin intermediación de servicios de terceros, ni posibles problemas venidos de la entrega a domicilio. ### Ferretería** **online, lo que necesita todo chapuzas En tiempos de confinamiento, mientras todo el mundo hacía pan, yo lo que eché de menos fueron unos tacos para la pared, mi vena chapuzas salió a flote, y no tenía forma humana de comprarlos. Me hubiesen hecho un rey si la ferretería de barrio hubiese tenido una tienda online, y luego yo pasarme a recogerlo en un segundo, pero un rey 🤴🏻 !! **Si tienes un negocio tradicional**, en el que no terminas de ver 100% tenerlo online, **no te cierres puertas**, hay muchos grados y formas de hacerlo. ¿Hablamos sobre tu caso? --- ## Presupuestar un proyecto URL: https://ablancodev.com/essays/presupuestar-un-proyecto/ Estimar un buen presupuesto puede hacer no sólo que llenemos o no la nevera a final de mes, sino que el proyecto vaya adelante o no, así que si eres de los que te chupas el dedo, lo levantas y calculas hacia donde sopla el viento, es hora de dejar de hacerlo, y «perder un poco de tu tiempo» en dar un presupuesto. Tranquilo, con el tiempo verás que no pierdes el tiempo, que avanzarás trabajo para luego, e incluso descubrirás especificaciones ocultas del proyecto que no te has planteado inicialmente. Hay multitud de teorías y métodos para presupuestar, algunos con base más analítica que otros. Mi idea en este post es contar cómo suelo hacer yo los presupuestos. ## ¿Tarifario? Es la forma más rápida y estandar de dar presupuestos, definir unos precios para los distintos tipos de productos y listo. En nuestro mundo del desarrollo, sea web o no, esto se complica, ya que aunque muchos proyectos se puedan parecer, que sean 100% iguales, va a ser casi casi imposible. Bueno, podemos tener un tarifario con rangos de precios, al menos para poder dar una estimación muy a grosso modo si alguien nos pregunta y quiere saber por dónde pueden ir los tiros. *Por ejemplo:* *Web corporativa: 500 – 750€* *Tienda online: 1000 – 1250€* Esto tendrá cientos de matices, pero al menos el usuario sabe que estás alrededor de los 1000€, y no de los 20.000€ ## Divide y vencerás Uno de los principios más aplicados en el desarrollo software, como es **«divide y vencerás»,** es en el que nos basaremos para crear nuestros presupuestos. **¿En qué consiste?** Pues **en dividir** una tarea compleja en otras más sencillas, que sean más fácil de abordar. Y esas más sencillas dividirlas a su vez en otras aún más sencillas, hasta que alcancemos un grado de granularidad que nos sea más sencillo de calcular. ### Un ejemplo Nos piden el desarrollo de una tienda online de un negocio, en la que quieres además tener toda la información corporativa y su propio blog. Calcular el precio a primer golpe de vista puede ser complicado, y eso que podemos considerarlo un proyecto medio-bajo de complicación. Vamos a dividirlo en un primer nivel: - Web corporativa - Blog - Tienda online - Otros (tareas de gestión, testeo, paso a producción …) Ahora quizás podamos afinar un poco más el precio, aunque sea a golpe de vista. ¿Qué tal si volvemos a dividir? - Web corporativa Portada - Quienes somos - Contacto - Otros (partes comunes como cabecera y pie) - Blog Portada del blog - Página de categoría - Página de etiqueta - Post - Tienda online Página tienda - Página categoría - Página producto - Carro de compra - Checkout - Configuración (envíos, pagos, impuestos …) - Otros Instalación WordPress + Woocommerce - Fase de testeo - Paso a producción - Gestión de proyecto Si ahora vamos tarea por tarea, seguro que es más sencillo estimarlas y acertar en los tiempos propuestos. Podríamos seguir dividiendo hasta que nos encontremos «cómodos» analizando las subtareas. Pruébalo, descubrirás que presupuestos que a priori te parecían muy muy caros, cuando los divides en subtareas, calculas los tiempos de cada una de ellas y los multiplica por tu precio hora, ese precio desorbitado, es ahora un precio ridículo. ### ¿Software? Para hacer esta división y estimar los presupuestos aconsejo usar algún software para la gestión de proyectos, que tenga la opción de diagrama de Gantt, ya que no sólo nos permitirá listar las distintas tareas, sino asignarles recursos (personas, que pueden tener costes distintos), ver las dependencias de las tareas (si por ejemplo para empezar una necesitamos terminar otra, o si pueden empezar en paralelo …), y otras funcionalidades con las que conseguiremos no sólo una cifra final, sino unas fechas de entrega. Personalmente, uso ProjectLibre para la subdivisión de las tareas y estimación de sus tiempos, sin entrar en esta primera etapa en valorar dependencias ni recursos (me uso «a mi» siempre como recurso, ya que suelo hacer yo un 90% del trabajo, y si tengo que tirar de red de freelances para alguna tarea específica, tienen precios muy similares). El ejemplo anterior quedaría algo como: * Una vez aprobado el proyecto, es cuando uso dicho diagrama para definirlo en más detalle y bajarlo a valores más reales. Como puedes ver, el esfuerzo que ha supuesto hacer un presupuesto más allá de levantar un dedo y ver hacia dónde va el viento, será ahora aprovechado durante el desarrollo del proyecto. ## La experiencia, un grado Todo esto está muy bien, pero si nuestros proyectos se parecen unos a otros en gran parte, o por lo menos comparten subtareas comunes, como pueden ser la instalación de WordPress o configuración de la pasarela de pago, podemos y debemos reutilizar datos históricos nuestros. No sólo para ahorrarnos tiempo en el desarrollo de presupuestos, sino para aprender de errores pasados, y así afinar en el precio estimado. Para aprender del pasado, necesitamos medir, medir nuestros proyectos, pero no sólo a nivel de proyecto, sino a nivel de tareas. Medir el tiempo que dedicamos a cada una de ellas, para cuando en el futuro nos llegue un proyecto con tareas similares, poder tener una estimación «real». Una herramienta de time tracking debería ser un must* en nuestro día a día. Personalmente uso Timecamp y me va muy bien con ella. ## En resumen - **Tarifario**: Por si alguien te atraca en el bar preguntándote por cuanto le saldría hacer tal proyecto, pero poco más. - **Divide y vencerás**: Si no te sientes cómodo calculando los tiempos de una tarea, divídela en subtareas con las que sí te encuentres cómodo. - **Aprende**: Mide en tu día a día lo que tardas en las tareas, te permitirá cometer menos errores en el futuro cuando abordes una tareas similar. --- ## Select de custom taxonomy URL: https://ablancodev.com/notes/select-de-custom-taxonomy/ Si necesitamos mostrar un **input select de un custom taxonomy** que hayamos creado, podemos montar el <select> con un foreach en el que ir montando los <option>, pero tenemos disponible esta maravilla en WordPress: wp_dropdown_categories(array( 'taxonomy' => 'funnel_state', 'class' => 'widefat', 'hide_empty' => 0, 'name' => "funnel_user_state", 'selected' => $current_state, 'orderby' => 'name', 'hierarchical' => 1, 'show_option_all' => " " )); Magic 🧙 !! **¿Cómo? ¿Que quieres que tu taxonomía se muestre como un select en el metabox de sus post types?**Pues más magic aún (de un mago finlandes, palabras mayores 🦄): https://gist.github.com/Ciantic/8a09107fcd9bed8bede0 --- ## Checkout en una página o multipaso, ¿Cuál es mejor? URL: https://ablancodev.com/essays/checkout-en-una-pagina-o-multipaso/ LLegado el momento clave en todo e-commerce, el proceso de pago o checkout, hay que andar con mucho cuidado, ya que podemos llegar a perder una venta en el último segundo. Normalmente disponemos en los distintos sistemas Magento, Prestashop, Woocommerce, la posibilidad de realizar el pago o bien en un sólo paso, o en un sistema multipasos. Cada una de estas posibilidades tienen una serie de pros y contras que veremos a continuación y que tendremos que tener en mente a la hora de elegir uno de ellos. ## 👣 Pago en una sóla página ### Pros de un checkout de una sóla página - **Menos clicks.** Las tiendas online que tienen un checkout en una sóla página, hacen que los clientes tengan que hacer menos clicks para terminar su proceso de compra. - **Aparenta ser más fácil.** Ya que completamos el proceso con menos pasos, aparenta ser un proceso más sencillo y con un menor coste de tiempo para los usuarios. ### Contras de un checkout de una sóla página - **Puede intimidar. **Pedir toda la información en una sóla página, puede hacer que dicha página sea muy larga e intimide un poco al cliente al ver tantos campos a rellenar. O incluso que no lleguen al final del scroll. - **Carga más lenta**. Esa gran cantidad de información en una sóla petición, hace que dicha petición sea más lenta que si es un sistema multipasos, y todos sabemos que la paciencia en internet hoy en día brilla por su ausencia, y no es el sitio donde precisamente tenemos que hacer esperar a nuestros futuros clientes. - **Analítica**. Tenerlo todo en una página nos complica saber en qué punto los usuarios abandonan el proceso de compra, y nos complica poder identificarlos dentro de nuestro funnel. ## 🖱️ Pago en un click No es lo mismo el pago en un click que el pago en un sólo paso o página. En este caso, debemos tener cierta información sobre el usuario que va a realizar la compra, por lo que es un método que se suele usar sólo con clientes ya existentes de los que tenemos al menos la información de contacto, de cobro y envío. ### Pros del checkout en un click - **Velocidad**. Es la opción más rápida que se dispone para realizar un proceso de pago, por lo que para clientes impacientes, es ideal. - **Mayor conversión**. Con ello se recoge lo que se llaman compras no-brainer, o compradores impulsivos, por lo que suele incrementar el número de compras. ### Contras del checkout en un click - **Revisión del pedido**. Muchos compradores prefieren disponer de una vista que resuma todo el pedido y donde ver todo de un sólo golpe antes de realizar el pedido. Con el pago en un click, esta vista se condensa bastante. - **Cuenta de usuario**. No todos los compradores quieren crear una cuenta de usuario donde se guarde información de contacto y pago, por lo que prefieren comprar como «invitados», por lo que no se podría usar este método con ellos. ## 👣👣👣 Pago multipaso o en múltiples páginas ### Pros de un checkout multipasos - **Recopilar información**. Aunque un usuario no termine el proceso de compra, podemos por ejemplo recoger el email en el primero de los pasos y tener esta información disponible para intentar recuperar dicho carrito abandonado. - **Layout**. Al poder dividir toda la información en distintos pasos, podemos ofrecer la información de cada paso en una forma más estructurada y con una presentación más cuidada. - **Analítica**. Podemos recoger información de todo el proceso, viendo facilmente dónde abandonan los usuarios para poder mejorarlo. ### Contras de un checkout multipasos - **Tiempo**. Multiples pasos da la sensación para el usuario que le va a llevar más tiempo, aunque realmente no sea así. Con el riesgo de aumento de abandono. - **Longitud**. Un proceso de checkout no debería llevar más de 3 o 4 pasos. Más pasos aumenta la probabilidad de abandono, por lo que es muy importante mostrar los pasos al usuarios, para que en todo momento sepa en qué punto está y qué le queda para finalizar la compra. ## 🏆 ¿Cuál es mejor? Aunque por lo general oirás que un proceso de pago en 1 sólo paso es mejor, eso no es 100% cierto. Depende de tu público, de tu producto, de tu carro de compra medio. Imaginemos que vendes joyas que valen **cientos de euros**. En este caso un **proceso de compra de varios pasos suele funcionar mejor**, ya que al cliente que se va a gastar ese dinero no le importa tanto perder un poco de tiempo, y en contraprestación, se siente «mejor atendido», y que no estamos sólo pendientes de que pague rápido y a por el siguiente cliente. Sin embargo carros que compras **por debajo de 30€** suelen funcionar muy bien **checkout de un sólo paso**, siendo un tipo de compra más impulsiva. En cualquier caso, la respuesta suele ser casi siempre la misma, test, test y más tests. Sólo **probando, midiente y testeando podrás conocer mejor a tus compradores** y mejorar tu ecommerce. **Anímate a hacer A/B tests con los que aprender**, y que serán los que te digan finalmente cual es la mejor opción para tu negocio. --- ## Cómo crear web stories URL: https://ablancodev.com/notes/como-crear-web-stories/ Los chicos de **AMP** han dado un paso adelante muy interesante al dar la posibilidad de tener **Stories en la web** al estilo de Instagram. El otro día mi amigo Luis Rull (gran profesional y mejor persona) lo comenta por twitter, y claro, a ver quién se resiste a meterse en un charco así, así que manos a la obra. 👨🏻‍💻 Tenemos buenas noticias 🥳 , y es que los chicos de **Google están trabajando en un plugin para ello**, y ya sabemos que Google es Google, y aunque está en fase Beta, tiene muy buena pinta. Aunque la **interfaz para crear las stories es muy chula e intuitiva**, de momento no lo estoy usando, porque **crea los stories como custom post type**, por lo que tendríamos algo como /story/story-1 /story/story-2 …. y no es lo que estaba buscando. Yo lo que quiero es tener una url, por ejemplo /on-air/ y que esa sea mi última story. Será cuestión de tiempo esperar que el plugin evolucione, y si no meten dicha funcionalidad, hacer fork y añadirla. Mientras ¿qué he hecho? Pues será por plugins 🧩🧩🧩 … estoy usando el plugin **AMP Stories for WordPress**, que aunque está un poco abandonado (supongo que el autor al ver los movimientos de Google, ha dicho «batalla perdida») **Amp Stories for WordPress Aunque la interfaz es más obsoleta y menos intuitiva, más del estilo de cuando creamos campos personalizados con Advanced Custom Fields, nos da la posibilidad de habilitar las stories en determinadas páginas**, y es justo lo que buscaba, ya que puedo crear una página llamada «on-air«, y sólo en dicha página habilitarlo. Añadimos las imágenes, los textos, publicamos y listo !! 🚀 Como al entrar en la página estamos en modo «slide», no tenemos forma de salir de ella, por lo que a la última imagen del story le suelo **añadir un enlace que permita al usuario seguir navegando por la web**, a modo de **CTA**. Con eso estaríamos listos, pero quería destacarlo en algún sitio, y pensé que lo mejor sería hacerlo como lo hace Instagram, con una miniatura en forma de círculo en la cabecera. Y **lo puse al lado del logo de la web**. Os añado el código que usé: // Stories // 4117 es el ID de la página on-air if ( function_exists('vp_metabox') ) { $amp_story_activated = vp_metabox('amp_story_vp_metabox.amp_story_tg', null, 4117); if ( $amp_story_activated == 1 ) { ?> <div class="thumb_story"> <a href="<?php echo get_the_permalink(4117);?>" title="@ablancodev on air"> <img src="<?php echo get_the_post_thumbnail_url(4117, 'full');?>" alt="Story web" width=37px" /> </a> </div> <?php } } Como imagen he cogido la imagen destacada de la página, ya que aunque la primera intención fue coger la primera de la story, al ser verticales no quedaban muy bien, y así puedo adaptarla según necesidades. Y tras toda esta chapa, el resumen es sencillo: - Instalamos el plugin AMP Stories for WordPress ✅ - Creamos una página, y diseñamos en ella nuestro story. ✅ - (opcional) Añadimos el botón miniatura que nos llevará al story en la cabecera. ✅ --- ## Adios fontawesome, hola emoji css URL: https://ablancodev.com/notes/adios-fontawesome-hola-emoji-css/ Todos hemos visto o usado fontawesome como método para añadir **iconos a nuestra web**. Incluso he llegado a verla cargada sólo para añadir los iconos de las redes sociales 😔 (a tomar por saco el performance) ¿Qué rollo, no? Pues vamos a decir **adios a fontawesome, y hola a emoji css** 👏👏👏 Sin más, yo la tengo (bueno, cuando leas este post, «tenía») para el típico icono del menú hamburguesa, así como para los distintos** items del menú.** Algunas capturas de pantallas, para que no se pierda en el olvido - - - Ahora que está muy de moda el uso de Notion.so, me han comentado más de un amigo que está muy chulo el menú con los emojis que tiene, ya que les «recuerda a su día a día» con Whatsapp y demás aplicaciones de mensajería. 💡 Y a mí que no me gusta nada un charco, pues **manos a la obra**, y me he puesto a cambiar el menú a «modo emoji». La solución **más sencilla, es copias y pegas** el emoji correspondiente en el texto del item del menú, y listo. Puedes coger los emojis por ejemplo de la emojipedia. Valer vale, pero no tenemos el potencial de colocarlos tan libremente como nos da fontawesome mediante clases css, así que tras buscar me encontré con **este maravilloso proyecto: emoji.css** que encima está disponible en github con licencia MIT !! El muchacho se merece una cerveza, no digo más 🍺🍺🍺 Tan sencillo como importar la librería: <link rel='stylesheet' href='https://unpkg.com/emoji.css/dist/emoji.min.css'> Y ya podemos usar sus clases. Como ejemplo el menú hamburguesa, que pasamos de usar: <i class="fa fa-bars" aria-hidden="true"></i> a usar: &lt;span class='ec ec-hamburger'>&lt;/span> ¿Y cómo ha quedado mi nuevo menú? 🥁🥁🥁 (Este menú hamburguesa me parece muy atrevido incluso para mi, así que como no sé cuanto durará, mejor he puesto captura del menú interior, con el que estoy **in love** 🥰) --- ## ¿Qué necesito de WordPress realmente? URL: https://ablancodev.com/notes/que-necesito-de-wordpress-realmente/ WordPress crece y crece día a día, tanto en su uso a nivel mundial, así como en sus funcionalidades, haciendo que se convierta con diferencia en **el CMS más potente del mercado.** 🏆 En esas estamos cuando me ha dado por pensar, **¿qué necesito de WordPress realmente?** y **¿qué es lo que uso habitualmente?** Hay mil y una funcionalidades que enamoran de WordPress, pero me voy a centrar en una web tipo catálogo, o blog, sin ser eCommerce, así que vamos allá con **la lista de la compra**: - 🕹 **Un admin**: Evidente, pero queda anotado. Necesito su admin, para que el cliente final, sin necesidad de conocimientos técnicos, pueda añadir contenido. - 👡 **Custom post types**: Para no quedarnos en un simple blog, necesitamos esa funcionalidad. Poder crear entidades propias, como pueden ser «productos», «portfolio», etc … - 🗄 **Custom taxonomy**: Agrupar los custom post types en «categorías», para tener una arquitectura web organizada (seguro que SEO nos lo agradece). - 🖼 **Templates**: Poder crear plantillas para los distintos elementos (cpt, taxonomy, páginas, etc), nos da esa versatilidad a nivel front que necesitamos. - 🧩 **Advanced custom fields**: Aunque no es una funcionalidad propia de WordPress, y normalmente hay que tirar del plugin ACF, en la mayoría de proyectos basados en plantillas desarrolladas a medida, necesitamos dinamizarlas, y la forma más directa es usar estos campos personalizados, con los que podemos desde el admin darles valores y que se muestren en el front. Sé que me dejo fuera de la lista elementos tan tan importantes en WordPress como son los **shortcodes o los hooks (filters / actions)**, que lo hacen tan potente y versátil. Pero la idea era hacer una lista mínima de funcionalidades sin las que no podría vivir (poder se puede vivir, pero la vida no tendría sentido !!) --- ## ¿Necesito WordPress? URL: https://ablancodev.com/notes/necesito-wordpress/ Si tienes un pequeño comercio, y has decidido dar el salto a internet (ya era hora, pero bueno, más vale tarde que nunca), y te has puesto a «bichear» por internet seguro que te has tropezado con WordPress como el sistema para «montar web» más extendido del mundo. Y te has dicho …. lo quiero !! Si lo tiene tanta gente, lo quiero !! 👏🏻👏🏻👏🏻 Pero, ¿realmente necesitas tener un CMS (gestor de contenidos) para tu web? Antes de nada, piensa en tu negocio, y para lo que quieres tener tu web, y contéstate estas preguntas: - ¿Cuál es el objetivo de mi web? (a) Escaparate de mi negocio - (b) Tienda online - ¿Voy a tener blog? (a) No - (b) Si - ¿Con qué frecuencia voy a modificar el contenido? (a) Rara vez - (b) Al menos 1 vez a la semana Contesta pensando realmente la respuesta, porque inicialmente todos nos venimos arriba, y queremos un blog, y vamos a publicar todos los días …. pero no. Si tus respuestas han sido (a), vas a tener una web con contenido relativamente estático, que dé a conocer tu negocio, pero modificaciones poco frecuentes, por lo que tener un CMS como WordPress para este tipo de página, sería matar moscas a cañonazos. 👽 ¿Pero hay vida después de WordPress? Poca, pero la hay. Tener un sistema como WordPress implica no sólo necesitar de la infraestructura para él (PHP + MYSQL), sino un mantenimiento recurrente, para tener todo al día actualizado y seguro. En estos casos en los que la web va a ser sino estática, casi casi, siempre recomiendo montarla directamente con HTML+CSS+JS, sin necesidad de recurrir a ningún CMS. Y valorar el coste/tiempo que nos puede llevar el mantenimiento con respecto a los cambios que vamos a requerir. Pongamos un ejemplo con tiempos, para no entrar en precios: - **WordPress**: Mantenimiento mensual de unas 2 horas, ya que tendremos que hacer actualizaciones (porque en un mes hay siempre siempre actualizaciones que hacer) - **Web estática**: Nos van a pedir por ejemplo que cambiemos algún dato puntual, y poner alguna imagen nueva del último trabajo realizado. ¿1 hora? porque no creo que tardemos muchos más. 🏆 En este ejemplo, claramente nos interesa tener una página web estática, que aunque tenga menos potencial de cambio y estemos más «atados», son características que no necesitamos, y en su lugar tenemos una página que va a volar, ya que por definición está cacheada sin necesidad de apenas cálculos por parte del servidor, apenas va a necesitar mantenimientos, y casi nos podemos olvidar de ella. 🤔 Entonces, mejor siempre una página estática, ¿no? pues no jejeje, y no seré yo quien eche piedras sobre WordPress, al que adoro 💛 . Va a depender de cada proyecto, de tus requisitos, e incluso de tus capacidades de gestión. Si tienes un proyecto en mente, y no sabes por dónde tirar, contacta conmigo, hablamos y vemos cual es la solución más adecuada. --- ## Growth Driven Design aplicado el desarrollo web URL: https://ablancodev.com/essays/growth-driven-designer/ Como Growth Driven Designer, tenemos que dar una vuelta a la forma de entender no sólo el diseño web, sino el propio desarrollo de esta. Estamos acostumbrados a desarrollar webs, en las que se plantea un proyecto a gran escala, bien haciéndola desde cero, o rediseñándola por completo, lo que equivaldría al clásico **desarrollo waterfall o en cascada**. Esto nos planteará **muchos inconvenientes**, tanto presupuestarios, así como de viabilidad del proyecto. Porque por muy entendidos que seamos el tema, no podemos saber de antemano cómo aceptarán los clientes nuestro sitio web, o cómo se comportarán dentro de él. **Para dar solución a este problema**, surgen las metodologías **agiles**, permitiendo iteraciones menores en el desarrollo de productos, que nos permitirán, no sólo estimar mejor y tenerlo más controlado, sino tener la **capacidad de adaptación a los cambios** que le mercado nos sugiera. Esta capacidad de adaptación, nos permitirá centrarnos en lo realmente importante de cualquier website, **el usuario**. Que será en última instancia quien la usará, y hará crecer, o no, nuestro negocio. Aplicar estas metodologías Agiles en el desarrollo web, siempre teniendo en mente **como centro de todo, al usuario**, es lo que llamamos **Growth-Driven Design (GDD)**. - **El usuario como centro**. Y sobre el cual pivota todo. Tenemos que medir, medir y medir. Medir para saber qué nos están pidiendo nuestros usuarios, mejorar su experiencia, y con ellos aumentar nuestro CRO. - **Be Agile**. La mejora continua será el secreto para hacer evolucionar la web, adaptándola a los nuevos requerimiento y volver a medir, para repetir esto hasta la saciedad. Vamos a definir el proceso en 2 fases: ### Fase 1: Empecemos ya Empezamos el proyecto, y lo primero que tenemos que hacer es **definir una estrategia** global y contestarnos cual es el objetivo de la web. Darnos a conocer, vender productos online, atraer clientes al local físico, … Definamos y pongamos online lo que llamamos la **Plataforma de Lanzamiento**, que no es ni más ni menos que un producto mínimo viable (MVP) de nuestra web. Cuanto antes estemos online, antes podremos empezar a «aprender» de los usuarios y empezar a mejorar nuestro producto y hacer crecer el negocio. ### Fase 2: Iteraciones y crecimiento Ya tenemos nuestro punto de partida del que aprender, y nuestro goal definido, es hora de empezar a evolucionar. Para ello, vamos a definir **ciclos de mejoras**, similares a sprints de Scrum, donde podríamos tener: *while (true) {* - Cogeremos de nuestro product backlog las **acciones a abordar** en este sprint. Siempre teniendo al usuario en el centro, y maximizando el valor para el negocio. - **Implementamos dichas mejoras**. Llevándolas a producción para poder empezar a aprender de ellas. - Una vez desplegados los cambios, entramos en la fase de **observación y estudio**. - Teniendo todos los datos a nuestro alcance, **los ponemos en conocimiento de todos los departamentos**, para que nos den feedback desde las distintas perspectivas de negocio. Los analizamos, y alimentamos nuestro product backlog. *} // volvemos a iterar* Un nuevo enfoque y una nueva forma de afrontar cualquier proyecto web, que nos hará ser más competitivos, reaccionar antes a los cambios de mercado, y hacer crecer nuestro negocio mucho más rápido. ### Algoritmo resumen: function GDD () { definir_estrategia(); lanzar_mvp(); while(true) { definir_sprint_backlog(); implementamos(); medir_medir_medir(); analizamos() } // iteramos hasta el infinito y más allá } --- ## Creando un MVP en 24 horas II : Más V y menos M URL: https://ablancodev.com/build-in-public/creando-un-mvp-en-24-horas-ii-mas-v-y-menos-m/ Pues vamos al segundo mini sprint, en el que vamos a atacar a las siguientes tareas: - Shortcode: para colocarlo donde más coraje nos dé (feedback de David Barrera por Linkedin) - Icono mejor que botón con texto - Mejorar la «landing page». - Y nada más, que me vengo arriba y luego nada de nada. >_ 08:15 Vamos a por el shortcode 💪🏻, que será muy práctico para poner el botón donde queramos. * Al final ha quedado como: * Como probablemente el shortcode vaya directamente a plantilla, tendremos que usar do_shortcode. >_ 9:30 Nos estaba faltando un rincón donde guardar las opciones del plugin. En este caso, al estar directamente relacionado con Woocommerce, vamos a meterlo como pestaña en el config de este. (Odio los plugins que me meten secciones y secciones en el admin  !!) --- ## Creando un MVP en 24 horas URL: https://ablancodev.com/build-in-public/creando-un-mvp-en-24-horas/ Voy a usar este post como **log de un MVP** que he decidido lanzar entre hoy sábado, ya las 21:43 de la noche y mañana domingo. >_ 21:45 ### El escenario ¿Cuántas veces no hemos visto portales, principalmente inmobiliarios, con la funcionalidad de «Avísame si el precio baja»? Si lo analizamos desde el punto de vista del usuario final, se trata de una funcionalidad muy práctica, ya que está interesado en el producto, pero quizás se le escapa de su presupuesto. Pero el verdadero potencial viene de cara al negocio, ya que con dicha funcionalidad estaremos captando leads, ya en una fase bastante avanzada. **¿Qué tal si aplicamos esta funcionalidad a nuestro ecommerce?** Aunque inicialmente pensemos que nuestros productos no son susceptibles de tener un precio variante con el que jugar, si lo pensamos más detenidamente, casi seguro que tenemos algún cupón descuento que estemos promocionando por newsletter, canales de publicidad, etc … Una vez captado el lead, podremos mandarle este cupón personalizado, con el que él conseguirá la bajada de precio deseada, aumentando nuestro ratio de conversión. Otra posible estrategia a seguir es poner ciertos productos en oferta, bajándoles el precio, según veamos el interés que los usuarios tienen al hacer click en «Avísame si el precio baja». Por ejemplo, una vez a la semana, o al mes, analizamos los leads captados por este método, y aplicamos ofertas según dichos datos. ### El producto Se va a dar solución al problema planteado, creando un plugin WordPress. El plugin constará principalmente de algún tipo de icono que se muestre en la ficha de productos, que al hacer click, nos lance un popup y nos permita indicar nuestro email para notificarnos cuando dicho producto baje de precio. Además a nivel de administración debería tener un listado de emails, o la posibilidad de integrarlo con mailchimp o poder exportarlo para tratar los leads de forma más sofisticada. Además necesitaremos de una landing donde promocionarlo. Aunque se podría integrar con varios eCommerce WordPress, y que el formulario de contacto sea bastante sofisticado, o que se muestre tanto en la ficha de producto, como en los listados de categorías, es hora de ir definiendo las características del producto mínimo viable (MVP). **Plugin:** - Aparecerá un icono en la ficha de producto. - Cuando se haga click, se mostrará un formulario que nos pedirá el email donde queremos ser notificados. - Será un email muy sencillo de notificación. - La sección de admin la dejamos para más adelante, no formará parte del MVP **Landing web** - Landing sencilla que mandará a la página de @eggemplo, donde ya tengo la plataforma de pago preparada y todo, para no tener que abordar esa implementación en esta fase. >_ 21:56 Es hora de empezar a picar código, crear repositorio en github, y manos a la obra. >_ 22:20 Ya tenemos repositorio: https://github.com/eggemplo/wpnotifyprice De momento vamos a optar por una tabla de base de datos con los datos mínimos para hacer la trazabilidad ( wpnotifyprice_lead ). Tendremos los siguientes campos: email <varchar 256>product_id <bigint>notified <datetime> Tabla que crearemos cuando se active el plugin, si no existe con anterioridad. Acabo de encontrar este post, que me va a venir muy bien, para ver los hooks que tenemos en las fichas de productos Woocommerce.**https://businessbloomer.com/woocommerce-visual-hook-guide-single-product-page/ Y creo que vamos a usar: *woocommerce_before_single_product_summary* >_ 23:00 Al final ese filtro es perfecto para donde queremos colocar el icono. He tenido que cargar bootstrap, ya que lo voy a necesitar para el modal donde mostrar el formulario de contacto. De momento estamos así: >_ 23:45 Y de momento así se queda por hoy. Estoy a medio implementar el envía del email por ajax, para evitar la recarga de la página. Mañana más, estimo que puedo ir por un 25% del plugin !!! <<<< Me veo que no llego !!!** >_ 07:35 Buenos días !! A ver si doy con la tecla del fallo en el envío por ajax … >_ 08:00 Bingo !! Nada como un buen descanso. Pues nada ahora parón, que hay que sacar perro y estirar las piernas. >_ 9:15 Ya de vuelta, vamos a empezar a guardar datos. Así que toca ir guardando la información de los leads en la base de datos. Guay, ya tenemos nuestros primeros leads: Siguiente punto a atacar, las notificaciones. Ver cómo detectar que un producto se ha puesto en oferta, y actuar en consecuencia. >_ 16:30 😱 Se ha liado la mañana más de la cuenta, y la pequeña de la casa ha requerido mi atención 🤤 Así que vamos con los tiempos MUY MAL, pero seguimos con el proyecto 🦾 >_ 17:31 Ya tenemos el envío de emails programado cuando se detecta que el precio de un producto ha bajado. Este requisito de ha adaptado, porque como MVP se había decidido inicialmente que se iba a chequear si el producto se ponía en oferta, pero como ha sido igual de «fácil» detectar que ha bajado el precio, es un requisito más genérico y más útil de cara al usuario final. El asunto y texto del email lo dejamos de momento no configurables, son textos fijos, pero en este punto del producto es completamente válido. MVP Plugin WordPress ✅ 🥳🥳🥳 >_ 18:39 La landing de lanzamiento, ha quedado como una página de producto dentro del portal eggemplo, permitiendo la descarga de la versión actual del plugin desde el repositorio de GitHub. https://www.eggemplo.com/es/plugin/wp-notify-price/ Conclusiones de este MVP express Como suele ocurrir en estos proyectos express, siempre salen acontecimientos que te impiden llegar hasta donde querías, quedando a veces el MVP, más mínimo que viable. Pero como punto de partida, y versión Beta sobre la que trabajar, obtener feedback y validación por parte del mercado, es muy buen comienzo. El punto que me ha dejado un sabor de boca amargo, ha sido la landing page, que con alguna que otra hora más, hubiese sacado algo más o menos decente. Y aunque me quedan algunas horas para cumplir las 24 horas de proyecto, como freelance, y domingo que es, pues ha salido una tarea prioritaria que tengo que abordar, así que la landing page ha quedado en página de producto. MVP Landing page 😖 Para la siguiente iteración, queda revisar el product backlog, ordenarlo por el valor que pueda aportar al producto y planificarla. Pero esto será otro fin de semana, o «noche larga». --- ## ¿Puedo aplicar Scrum si soy freelance? URL: https://ablancodev.com/essays/puedo-aplicar-scrum-si-soy-freelance/ Hoy me ha llegado un comentario en plan jocoso diciéndome: «¿cómo aplicas scrum si eres freelance y estás tu sólo en el equipo?, ¿te reúnes tú sólo contigo mismo por las mañanas?» Y me ha hecho recapacitar en: - La visión tan simple que tiene mucha gente de lo que es scrum y la metodología agile en general. Se piensan que aplicar Scrum, es tener un tablero bonito con un montón de post-its, y reunirse por las mañanas «un ratillo» para hablar de las tareas. - ¿Se puede aplicar metodología agile en equipos unipersonales? ## 1.- Scrum, algo más que una reunión matutina. En este punto no voy a profundizar, es más, no voy ni a entrar. Simplemente decir que se puede aplicar scrum sin necesidad de post-its, SIIII, increible pero cierto. Y que te reúnas por las mañanas con los compañeros de trabajo para hablar de las tareas que vas a realizar, eso no es un *daily*, eso es *hablar de trabajo mientras te tomas un café*. ## 2.- Aplicar scrum siendo freelance. Vamos al turrón, que es por lo que estás leyendo este post. Siendo puristas, cualquier Scrum Master te diría que NO, que para montar un equipo de scrum, necesitamos 3 roles: - Product owner - Scrum Master - Equipo de desarrollo (entre 3 y 9 personas) Vale que scrum nos permite que el scrum master y el product owner puedan pertenecer al equipo de desarrollo, pero aún así nos siguen faltando personas. Dado que ya tenemos el NO, ¿qué tal si apoyándonos en uno de los pilares del empirismo, sobre el cual se apoya scrum, nos *adaptamos* a nuestro entorno y condiciones? Ok, somos 1, para todo, y seguro que habrá cosas de scrum que no podamos hacer, pero sí vamos a aplicar scrum en todo lo que podamos, y sobre todo sus pilares y metodología que hay detrás, seremos lo más *agile* que podamos. Si uno de los pilares del empirismo nos dice estamos ante un entorno de incertidumbre, al que debemos constantemente adaptarnos, pues adaptemos incluso la metodología a nuestro entorno (que me perdonen los scrum masters más puristas). Veamos los distintos elementos: ### Roles En una sola persona se centrarán los 3 roles. Seremos: - Product owner: Intentando maximizar el valor del producto y mirando por su interés. - Scrum master: El scrum que apliquemos estará bajo nuestra responsabilidad y seremos nosotros los encargados de que se cumpla. - Development team: Toca picar código también. Como he mencionado antes, aquí no estamos cumpliendo scrum, pero … es lo que hay, ojalá el presupuesto diera para tener a un equipo al lado. ### Artefactos #### Backlog de producto: Esa lista de tareas o funcionalidades que nos piden y vemos que mejora o componen al producto. Pues nada, toca ponernos el sombrero de Product Owner y a tomar requisitos y gestionarlos teniendo siempre en mente maximizar el valor del producto final. #### Sprint backlog: Qué se va a hacer, y cómo se va a hacer lo que se ha definido para el sprint actual y alcanzar el sprint goal, podemos gestionarlo sin problema nosotros mismos, ya que nos tocará ser el equipo de desarrollo. #### Incremento: El incremento representa el alcance real, tanto de los sprints anteriores, así como lo que llevamos del actual. Nos ayudará a saber en qué estado estamos y hacer estimación de cuanto nos queda (estimación, ya que los requisitos del producto son cambiantes y puede que nos entren tareas en el product backlog antes de terminarlo) ### Eventos Veamos los eventos uno a uno: #### Sprint: Definir la funcionalidad y objetivo del producto a 1 mes vista máximo, es el principal potencial de scrum, evitando gran parte de la incertidumbre de abordar proyectos grandes y permitiéndonos ser más ágiles ante cambios. Aquí el centro es el producto, por lo que seas freelance o no, puedes aplicar la filosofía agile a tus proyectos, haciendo iteraciones de máximo 1 mes, y adaptando el producto a los nuevos requerimientos o situaciones. Imagina que te encargan hacer una web. El procedimiento en cascada sería, hacer un presupuesto general de todo el proyecto, anotando funcionalidad y demás. Poniendo una fecha de entrega final, o a lo sumo alguna entrega intermedia. Pues eso lo vamos a convertir en estudiar el negocio del cliente, ver el producto que desea, y maximizar el valor de los distintos requisitos tomados. Entregaremos por ejemplo en 3 semanas una primera landing, con la que el negocio pueda por ejemplo captar leads, y nos permita ir validando nuestra web. Pasadas esas tres semanas, volvemos a iterar, nos sentamos, recogemos feedback y vemos según lo obtenido cuales serán las tareas a abordar en las siguientes 3. Y así hasta conseguir un producto final, que probablemente diste bastante de lo que inicialmente teníamos pensado, pero que maximiza el valor del negocio, y escucha a los usuarios finales. #### Sprint planning: Reunión en la que se definen qué tareas del product backlog se van a abordar en el sprint para alcanzar el Sprint Goal, y se descomponen dichas tareas. Sin problema, hacemos esta reunión más solos que la una, pero podemos sin problema elegir las tareas y descomponerlas. #### Sprint review: El equipo scrum al completo ya sabemos que acudirá, ya que eres tú. Pero podrás invitar a otros stakeholders, como por ejemplo al cliente, para de forma informal, enseñarle el incremento alcanzado, ver qué vamos a necesitar del producto para añadir esa información el product backlog, y ver qué vamos a atacar en el siguiente sprint. #### Sprint retrospective: Es hora de evaluarnos, y ver en qué hemos fallado y qué podemos mejorar en el siguiente sprint. Será difícil autoevaluarnos, pero nos hará crecer tanto en el proyecto, como a nivel personal como profesional. #### Daily: Reunión diaria de no más de 15 minutos para que el equipo de desarrollo hable sobre los avances y tareas para conseguir el sprint. Fácil, ¿no? Pues eso, te sientas 15 minutos por las mañanas con un buen café y revisas las tareas del día. ### Sé agile Como puedes ver, poder se puede aplicar Scrum siendo freelance. Se trata de encontrar el grado de aceptación que consideres que te lleva a adaptarte a los cambios de forma ágil, permitiéndote sacar el máximo valor al producto. Sé que los puristas de scrum estarán afilando los cuchillos para matarme, pero tal y como comenté al principio del artículo, empirismo en estado puro, adaptando al entorno y circunstancias incluso al mismísimo framework scrum. >_ Be agile my friend !! --- ## Mejora tu web usando Google Analytics URL: https://ablancodev.com/notes/mejora-tu-web-usando-google-analytics/ Uno de los principales problemas que tenemos cuando desarrollamos webs, es que una vez que salen a producción, nos olvidamos de ellas. O a lo sumo, les hacemos sus mantenimientos técnicos, o alguna mejora estética. Es ahora cuento entramos en la fase de post-producción, y cuando debemos estudiarla, ver el comportamiento de los usuarios y asegurarnos que están teniendo una experiencia satisfactoria. Normalmente te piden instalar Google Analytics, para «medir» el tráfico, quedando estas mediciones, en un simple conteo del número de visitas, que nos hará crecer en mayor o menor medida nuestro ego. Pero … ¿y si usamos estos datos para mejorar nuestra web? o lo que es aún mejor, para ofrecer a los usuarios lo que están buscando, lo que terminará convirtiéndose en crecimiento de nuestro negocio. ## Algunas métricas que debemos tener en cuenta. ### Porcentaje de rebote La tasa o porcentaje de rebote es una de las estadísticas más importantes para optimizar nuestro sitio web. Si una gran mayoría de sus usuarios lo abandonan después de solo visitar una página, sabemos que tenemos un problema. Nos encontramos que podemos tener un contenido poco atractivo, y sin terminar de consumirlo nos abandonan. O que aunque el contenido es consumido con éxito, no tenemos una llamada a la acción (Call To Action – CTA), o contenido relacionado apetecible para seguir visitándolo. Pues ya tenemos los dos primeros puntos a mejorar de nuestra web, el contenido (si fuese el caso), y el CTA de dicha página. --- ## Catálogo de productos URL: https://ablancodev.com/notes/catalogo-de-productos/ ### Caso de uso Nos piden montar una web donde poder mostrar el catálogo de productos de los que dispone la empresa. No van a necesitar montar un e-commerce, por lo que el carro de compra, checkout y demás secciones propias de una tienda online no las necesitaremos. En muchas ocasiones optamos por sistemas de e-commerce completos, como puede ser Woocommerce, deshabilitando las opciones de añadir al carro, convirtiéndolo en un catálogo. Pero estaríamos matando moscas a cañonazos, con la consiguiente pérdida de rendimiento. ## Entidades En este caso de uso, las entidades que nos encontramos, se reducen a: - Product: Representa a un determinado producto. - Gallery: Será la galería de imágenes asociada a cada producto. - Image: Cada una de las imágenes de una galería. Los productos los agruparemos por categorías (product_category), quedando el diagrama de clases: << Diagrama de clases >> ## Implementación Realizaremos la implementación de los modelos de datos y controladores en plugin al que llamaremos ‘Catálogo’, dejando la vista delegada al tema hijo que estemos usando. Para los distintos atributos que necesitemos, para facilitar la implementación, haremos uso del plugin Advanced Custom Fields, que nos permitirá definirlos de forma muy sencilla y poco esfuerzo. - Product: Definiremos un custom post type ‘product’ para definir dicha entidad. - Gallery: Será un campo personalizado de tipo repeater que contendrá elementos de tipo ‘media’ - Image: Serán los elementos de tipo ‘media’ que contiene la galería. - Product_category: Definiremos una taxonomía personalizada. El código del plugin está disponible en este repositorio de Github. --- ## Prepara tu web para la búsqueda por voz URL: https://ablancodev.com/notes/prepara-tu-web-para-la-busqueda-por-voz/ Hoy en día todo el mundo habla sobre la búsqueda por voz y sobre los asistentes personales, siendo Alexa por parte de Amazon y Google Home por parte de Google, los dos grandes jugadores del mercado. Centrándome en Google (como no), vamos a ver cuales son las reglas que sigue para poder entender nuestra web y así poder ofrecerla a través de su asistente de voz. Google denomina «actions» a las distintas aplicaciones que vamos a poder añadir a nuestros asistentes de voz, que tendremos disponible tanto en el móvil, con los dispositivos de la casa. ## Actions implementadas por código. Mediante una API y un determinado SDK, podremos implementar actions a nivel de código. Este tema lo abordaremos en otro artículo, centrándonos en este en el segundo método. ## Actions desde el contenido web. Autogeneradas por Google a través de contenido enriquecido de nuestra web. En este caso no seremos nosotros quieres crearemos los actions, sino que será el propio Google quien las generará cuando tras analizar nuestra web, y ver que tenemos «todo en orden» pase a generarla. Para obtener una web optimizada para el asistente de voz, debemos seguir dos pautas: ### AMP Aunque no es 100% obligatorio, si que es altamente recomendada. En definitiva, tomémoslo como obligatorio y así nos harán más caso. Así que a desarrollar las versiones webs de nuestras páginas se dijo. ### SCHEMA Con el marcado de datos estructurados schema, podremos indicar a los buscadores (de voz o no), qué tipo de información estamos manejando, así que aunque finalmente Google no nos cree de forma automática su action, nos será de gran ayuda para posicionarnos en el buscador habitual. El asistente de voz permite reconocer actualmente 5 tipos de schemas: - FAQ: Para páginas con contenido preguntas / respuestas - How-to: Para contenido donde se explican cómo hacer algo paso a paso. - **News**: Para artículos. - Podcasts: Para contenidos de audio tipo podcasts. - Recipes: Para páginas de recetas. Veamos el caso de «news», el que permitirá que el usuario consuma nuestro contenido como parte del contenido de «Google News». Desde la página oficial de la documentación del tipo estructurado Artículos (news), nos indican 3 puntos a cumplir: - Que nuestra página cumpla con los requisitos AMP. Aunque podríamos hacer la versión no AMP, es muy recomendable usar dicha versión. Así que si no tenemos aún la versión AMP de nuestra web, es hora de ponerse manos a la obra. - Directrices a cumplir. Nos indica un conjunto de directrices a cumplir, tanto de los formatos a seguir en AMP, así como más genéricas a nivel de desarrollo web, como pueden ser el clásico de no copiar contenidos de terceros, no crear simples listados de enlaces, etc … - Datos estructurados. Indicando que debemos seguir la estructura propuesta en schema.org, pudiendo usar en el caso de news, los tipos: Article, NewsArticle, y BlogPosting. Como ejemplo de datos estructurados usando el formato JSON-LD, podemos encontrar en la misma página de documentación este de un artículo de la BBC: Una vez tengamos nuestra web disponible en formato AMP, y con el marcado de datos estructurados adecuado, según indican en la página de documentación, Google podrá crear automáticamente un action a medida que nos permitirá tener disponible nuestro feed en la «tienda de actions» . --- ## Montar Schema FAQs WordPress URL: https://ablancodev.com/notes/schema-faqs/ Hoy me ha tocado pelearme con el schema FAQs, el esquema de datos estructurados dedicados a las páginas de preguntas frecuentes. ## ¿Qué es eso del schema? El schema de una página web, es un conjunto de datos estructurados que la web contiene, y que el usuario a simple vista no puede ver, pero que los distintos buscadores y programas que la analizan utilizan para saber algo más de ella. En la página oficial de schema.org se puede ver toda la documentación sobre los distintos de datos que necesitamos incorporar según la naturaleza de nuestra página. ## Schema para Preguntas Frecuentes En este caso además de los datos genéricos que indican el tipo de contenido entre otros, se indicarán cada una de las preguntas, así como sus respuestas. Como ejemplo, tenemos: ## En el ecosistema WordPress Llevémonos nuestras preguntas frecuentes al mundo WordPress. Lo primero que vamos a necesitar es definir nuestra la entidad «faq», y para ello usaremos un custom post type «faq». Como las preguntas constan de un enunciado y su respuesta, vamos a hacerlo fácilmente usando el título como enunciado, y content del post type como respuesta, por lo que no tendremos ni que definir campos personalizados. La definición del custom port type quedaría tal que así: Definida la entidad, ya sólo nos quedaría recuperarlas en la plantilla para añadirlo al script del schema.Usaremos get_posts para recuperarlas y mediante bucle poder recuperar sus datos: add_shortcode('add_faq_schema', 'add_faq_schema' ); function add_faq_schema() { $output = ''; $faqs = get_posts( array( 'post_type' => 'faq', 'numberposts' => -1 ) ); if ( $faqs ) { $output .= ' <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{ "@type": "Question", "name": "Título general de nuestras preguntas frecuentes, a modo de pregunta", "acceptedAnswer": { "@type": "Answer", "text": "Descripción general de la respuesta a la pregunta general." } } '; foreach ( $faqs as $faq ) { $output .= ' , { "@type": "Question", "name": "' . addslashes( get_the_title($faq)) . '", "acceptedAnswer": { "@type": "Answer", "text": "' . addslashes( apply_filters('the_content', $faq->post_content) ) . '" } } '; } $output .= ' ] } </script> '; } return $output; } Y de esta forma tan sencilla tendríamos nueva web preparada para alimentar a los buscadores con datos enriquecidos sobre preguntas frecuentes. ## Eliminar schema Yoast en una página Como tendremos ya nuestro schema FAQs en nuestra página de preguntas frecuentes, no vamos a necesitar ningún otro schema adicional. Por lo que si estás usando el plugin de Yoast SEO, tendremos que quitar el schema que nos genera por defecto. Para ello, con este simple script que meteremos en functions.php, lo conseguiremos (suponemos que la página de preguntas frecuentes es la 156): add_filter( 'wpseo_json_ld_output', 'yoast_seo_json_remove_partial' ); function yoast_seo_json_remove_partial() { if ( is_page ( 156 ) ) { return false; } } --- ## Crear página de opciones en WordPress con ACF URL: https://ablancodev.com/notes/crear-pagina-de-opciones-en-wordpress-con-acf/ Vamos a ver cómo crear una página de opciones en el admin de WordPress facilmente con el plugin ACF (Advanced Custom Fields) Lo primero que necesitamos es crear la sección en el admin, para posteriormente añadirle los campos que necesitemos. En el codex de WordPress podemos encontrar la función add_menu_page que nos permite crear una sección en su admin, permitiéndonos un control más avanzado de ella, pero en el caso que nos ocupa, usaremos la API del plugin Advanced Custom Fields (ACF). Según la documentación oficial del plugin, disponemos de la función acf_add_options_page para ello: En gist he creado un plugin simple con esta funcionalidad, así no vamos «ensuciando» nuestro functions.php, lo tenéis disponible aquí. Una vez tenemos disponible nuestra sección de opciones, pasaremos a añadirle campos personalizados con la interfaz de diseño que nos ofrece ACF. Cuando creamos un grupo de campos, simplemente tenemos que indicar que los ubique en la página de opciones que hemos creado (en este caso, la hemos llamado ‘Festivos’). Ahora si que tenemos ya nuestra página de opciones 100% disponible para su uso. ¿Y cómo se accede al valor de dichas opciones? ACF en su afán de hacernos la vida muy fácil, nos permite usar la función get_field para ello. En este caso en concreto, dicha función deberemos usarla con el segundo parámetro con el valor ‘option’ (siempre ‘option’, independiente de como se llame nuestra sección). En la página de documentación oficial podemos entrar más información. Si necesitamos obtener el valor del campo creado, por ejmplo, ‘titulo_festivo’: Un ejemplo de uso, para cuando tenemos un repeater en la página de opciones creada, podría ser: Y con estos sencillos pasos, podemos disponer de páginas de opciones tan avanzadas como queramos, sin complicarnos en complejas codificaciones. --- ## El poder del «little data» URL: https://ablancodev.com/essays/el-poder-del-little-data/ Hoy en día estamos sumergidos en la ola del big data, sobre el que podemos leer todo tipo de artículos, libros, incluso hacer masters específicos. Pero ¿y si no disponemos de tantos datos? ¿Cómo empezamos? Es aquí donde entre el concepto al que he nominado «little data», es decir, el tratamiento de pequeñas/medianas cantidades de datos. Que quizás no tengan tanto valor a nivel estadístico, pero si su peso a nivel local, ya que focalizamos el análisis en una muestra más afín a nuestro público. --- ## Crear pestaña en la página Mi Cuenta de Woocommerce URL: https://ablancodev.com/notes/crear-pestana-en-pagina-mi-cuenta-woocommerce/ En ocasiones necesitamos añadir una nueva pestaña en la página «Mi Cuenta» que nos genera Woocommerce. Imaginemos por ejemplo una sección dónde añadir un formulario de contacto personalizado de soporte premium para nuestros compradores. A continuación muestro un código ejemplo en el que se usan los hooks y actions necesarios para conseguirlo. Podéis ver el artículo original en businessbloomer. --- ## Algoritmo de optimización por colonias de hormigas URL: https://ablancodev.com/essays/algoritmo-de-optimizacion-por-colonias-de-hormigas/ Basándonos en el comportamientos observado de ciertos tipos de hormigas, surge un algoritmo de optimización de rutas o caminos. ## ¿Hormigas? Si, hormigas, esos seres diminutos, que nos rodean, y que siempre están acompañados, por cientos de ellos. Imaginemos un punto A de origen, y uno B destino, con varios caminos de distinta longitud entre ellos. Las hormigas querrán ir desde A (el hormiguero), a B (la comida), y volver cuantas más veces mejor. Ahora pensemos que dichas hormigas emiten un olor característico (feromona), que van dejando a lo largo de su rastro. Inicialmente de forma aleatoria, las hormigas tomarán los distintos caminos, en busca de alimento. En función de la calidad y cantidad del alimento encontrado, las hormigas dejarán más rastro, para poder volver e indicar al resto del hormiguero el camino que han encontrado. Pero además, las que tomaron el camino más corto, llegarán antes a la comida, y podrán volver antes al hormiguero, para dar otro viaje. Esto hace que el camino mas corto tenga cada vez más cantidad de feromonas depositadas, ya que más hormigas lo visitan, tanto las que llegan al alimento y vuelven, como las nuevas, que por naturaleza tienden a seguir el camino con mas feromonas depositadas. De esta forma, seres tan simples, trabajando conjuntamente son capaces de averiguar el camino más corto entre dos puntos. ## Aplicación clásica Este tipo de algoritmos ha sido principalmente usado en el ámbito de las redes de computadores, en las que la obtención del camino mínimo entre dos puntos es de prioridad, pudiendo suponer mejoras de rendimiento y velocidades mayores. ## Aplicando el algoritmo en el desarrollo web Basándonos en dicho comportamiento, podemos aplicarlo en nuestra web, para conseguir bien el camino más corto, o el camino más largo de van a recorrer nuestros visitantes. ### El camino más corto Podemos aplicar dicho algoritmo para encontrar y/o mostrar el camino más corto desde cualquier página de nuestra web, terminando en una compra. En nuestro escenario, tenemos 3 elementos: - Punto de partida: Cualquier página en la que aterricen nuestros usuarios. - Punto destino (comida): Página de finalización de compra. - Camino: Secuencias de páginas (nodos) por la que pasarán nuestros usuarios. ### El camino más largo Vamos a maximizar el camino de nuestras «hormigas», para que visiten cuantos más nodos (páginas) mejor, antes de abandonar nuestra web. En este caso, los 3 elementos del escenario son: - Punto de partida: Cualquier página en la que aterricen nuestros usuarios. - Punto destino (comida): Página desde la cual el usuario abandona la web. - Camino: Secuencias de páginas (nodos) por la que pasarán nuestros usuarios. A continuación adjunto un poster sobre el tema que realicé en mis años como alumno en la Universidad de Málaga: --- ## Permisos de archivos correctos URL: https://ablancodev.com/notes/permisos-de-archivos-correctos/ Cuando hablamos de seguridad web, uno de los puntos principales a tener en cuenta es tener los permisos de archivos correctos. Pensemos que en realidad lo que estamos sirviendo a través de nuestra web, no son más que ficheros, que se ejecutan y generan unos resultados. Permitir que ciertos usuarios puedan escribir o ejecutar archivos, puede suponer una brecha de seguridad grave, pudiendo incrustar código malicioso, nuevo archivos o correr scripts que nos pongan en compromiso. Pero, ¿cuales son los permisos de archivos correctos? En un entorno de producción, visible al público, deberíamos tener: - Permisos 644 para los archivos. - Permisos 755 para las carpetas. ## ¿Qué nos indican estos números? ### Permisos de archivos (0644) 6 (110): Permisos de lectura y escritura para el dueño del archivo (quien lo creó) 4 (100): Permisos de sólo lectura para los miembros del grupo del dueño del archivo. 4 (100): Permisos de sólo lectura para el resto de usuarios. Es decir, que salvo el dueño del archivo, ningún otro usuario podrá modificar el archivo o ejecutarlo. ### Permisos de carpetas (755) 7 (111): Permisos de lectura, escritura y ejecución sobre la carpeta para el dueño de la carpeta (quién la creó) 5 (101): Permisos de lectura y ejecución sobre la carpeta para los usuarios del mismo grupo que el dueño de la carpeta. 5 (101): Permisos de lectura y ejecución sobre la carpeta para el resto de usuarios. Es decir, sólo los dueños de las carpetas podrá escribir en ellas. ## Permisos especiales Hay una serie de bits especiales para los archivos que otorgan ciertos privilegios extras. Entre ellos vamos a destacar SUID y SGID que hacen que el usuario que ejecuta un determinado archivo adquiera los privilegios del usuario (si es SUID) o grupo (SGID) que creó el archivo. Como se puede pensar esto puede ser muy grave, ya que si por algún motivo un usuario root crea un archivo y tiene estos bits activos, el usuario X que pueda ejecutar dicho archivo, tendrá temporalmente los privilegios de root. En este artículo podemos leer más información sobre dichos archivos. Acabamos de ver que tener en cuenta los permisos de archivos es de prioridad máxima, pero no olvidemos tener en cuenta otros aspectos que iré incluyendo en la sección de Seguridad WordPress del blog. --- ## Tricks WordPress URL: https://ablancodev.com/notes/tricks-wordpress/ Desactivar Gutemberg Si no vamos a usar Gutemberg, no tiene sentido que carguemos su css, por lo que evitamos cargarlo: Ya que estamos en modo ahorro, ¿qué tal si evitamos cargar los emojis y mejoramos nuestro WPO (Web Performance Optimization) un poco más? Sanitizar los nombres de archivos, para evitar ñ y demás caracteres extraños (código dado por el crack @fpuenteonline): Si tenemos un sitio en el que los usuarios se pueden loguear, quizás necesitemos ocultarles la barra de administrador. Con este pequeño trozo de código lo tendríamos (para todos los usuarios menos los admministradores, y sólo en el front). Revisiones sólo guardamos 3 Vaciamos la papelera cada x días Desactivar la actualización automática de WordPress. Se recomienda estar siempre actualizado, pero a nivel personal, me gusta controlar cuándo y cómo se actualizan mis webs, para hacerlo de forma manual y supervisada. --- ## Eliminar custom post type y taxonomías URL: https://ablancodev.com/notes/eliminar-custom-post-type-y-taxonomias/ En ocasiones necesitamos eliminar custom post type (CPT) y taxonomías que no vamos a necesitar, y que bien por algún plugin de tercero, o por tema WordPress se nos han instalado en nuestra web. Este caso lo solemos encontrar cuando compramos algún tema premium, por ejemplo en Themeforest, y dicho tema trae custom post types que no vamos a usar, por lo que lo correcto son eliminarlos. Pero no eliminarlos sólo a nivel de contenido desde el dashboard, sino también a nivel de código, para evitar que los buscadores puedan indexarlos. Para desinstalarlos, vamos a usar estos snipers, que podremos poner bien en nuestro propio plugin, o en el fichero functions.php del tema hijo que usemos. Este código en concreto eliminaría el custom post type ‘project’, y la taxonomía ‘proyect-cat’ Y de esta forma rápida y sencilla nos quitamos de encima la ‘basura’ que en ocasiones nos traen las plantillas. --- ## Scripts mysql para exportar posts de WordPress URL: https://ablancodev.com/notes/scripts-mysql-exportar-posts-wordpress/ Si nos encontramos ante la necesidad de exportar posts de WordPress de una instalación a otra, estos scripts mysql nos ayudarán a hacerlo directamente desde base de datos, sin necesidad de plugins externos. Los siguientes comando nos van a permitir ir seleccionando los distintos datos de la base de datos original, los cuales debemos exportar y luego importar en la nueva base de datos. Seleccionamos los posts (en este caso será un custom post type): Los distintos posts que tienen como padre los posts de tipo ‘movies_movie’.Esto nos dará los attachments, revisiones, etc … Los postmeta de nuestros posts de tipo movies_movie: Los postmeta de los attachments, revisions, etc …: --- ## Tips seguridad extra en WordPress URL: https://ablancodev.com/notes/tips-seguridad-extra-en-wordpress/ A añadir en el fichero functions.php: --- ## Membership site con WordPress URL: https://ablancodev.com/build-in-public/membership-site-con-wordpress/ En este caso de estudio, vamos a montar un membership site con WordPress, es decir, vamos a vender nuestro conocimiento o productos de forma periódica. ## Caso de uso Llevamos ya muchos años dedicados al mundo de la programación web, especialmente al CMS WordPress, y dándole vueltas a la cabeza hemos decidido «vender» nuestro conocimiento. Es por ello por lo que nos surge la idea de montar un site en el que iremos escribiendo de forma periódica artículos sobre programación WordPress, con tutoriales de cómo hacer esto y lo otro. Además de ello, mediante el sistema de comentarios de WordPress, daremos soporte premium a los miembros de nuestro site, dándoles un valor añadido al hecho de suscribirse. ## Implementación de la solución Empezando desde el principio, montaremos nuestro WordPress, al que añadiremos las funcionales adicionales que iremos necesitando. ### Sistema de membersía. Usaremos el plugin Groups, de @itthinx, que nos permite entre otras muchas cosas, crear tantos grupos de usuarios como necesitemos, y asignarlos a ellos. En nuestro caso, tendremos sólo un nivel de pago, por lo que tendremos el grupo Registered (por defecto de WordPress), y un grupo Premium, donde estarán los usuarios miembros de nuestra comunidad. Con Groups, podemos decidir quien tiene acceso a un determinado post, y quien no. Por lo que podremos crear nuestra tanda de artículos premium sólo disponibles para nuestros miembros especiales.   Esto hará que si un usuario no Premium intenta acceder al artículo, sea redireccionado a la página 404 de la web. Pero ¿qué tal si mejoramos esto, y lo redireccionamos a una landing page donde podremos colocar un CTA (call to action) que le motive a registrarse? La extensión Groups 404 redirect nos lo permitirá. Ya tenemos nuestro contenido restringido, la definición de los grupos de usuarios, y ¿que nos falta?, pues lo importante, «pasar por caja». Groups nos permite multitud de conexiones con carros de compras, como puede ser con Woocommerce, pero en nuestro caso, como sólo vamos a tener 1 producto, sería matar moscas a cañonazos, así que vamos a usar la integración con Paypal, para crear un botón de pago y poder vender la membersía (en la página de documentación de la extensión, podemos ver más información de cómo configurarla) Crearemos una página de venta y registro siguiendo las indicaciones da la empresa desarrolladora. Con estos sencillos pasos ya tendríamos nuestro negocio en marcha, preparado para generarnos dinero. ### Hagamos algunas mejoras y extensiones de funcionalidad. Para mejorar nuestro SEO, y ofrecer a los usuarios una pequeña muestra de nuestro conocimiento, podríamos mostrar parte de los contenidos de los artículos, usándolos además como ganchos. Es por ello, por lo que desde Groups tenemos disponible los shortcodes y .     --- ## Tienda Woocommerce con descuentos para distribuidores URL: https://ablancodev.com/notes/tienda-woocommerce-descuentos-distribuidores/ En este caso de uso, vamos a montar una tienda Woocommerce con descuentos para distribuidores, comerciales, clientes especiales o cualquier otro rol de usuario que tengamos. ## Caso de uso No todos nuestros clientes online son iguales, y por ello en ocasiones vamos a necesitar aplicarles descuentos según su rol en nuestro negocio. Tenemos por ejemplo: - El usuario fiel, que compra una y otra vez, y que ya es hora de agradecérselo. - Nuestra red de comerciales necesitan poder ofrecer los productos con un precio algo reducido, y contar con un incentivo para que el cliente final les compre. - Tenemos una tienda cuyo público puede ser el usuario final, o distribuidores (incluso usuarios dropshipping), por lo que los distribuidores deberían ver los precios rebajados con respecto al usuario final. ## ¿Qué necesitamos? Para variar, vamos a montar nuestra solución basándonos en el CRM WordPress, y cómo no, en el plugin para tienda por excelencia, Woocommerce. Con eso ya tenemos nuestra tienda online montada, ¿y ahora qué? …. pues vamos a montar nuestro sistema de descuentos. Empezamos creando nuestra estructura de roles, teniendo: - **Customer**: Comprado básico definido por Woocommerce. - **Premium**: Usuarios fieles, a los que ofreceremos un 5% de descuento. - **VIP**: Usuarios «aún más fieles», con un descuento por defecto del 10%. - **Comercial**: Role que usarán Juan y Beatriz, nuestros comerciales, que contarán con un descuento del 15%, con el que poder jugar en las negociaciones. - **Distribuidor**: Role creado para los distribuidores, a los que queremos aplicar un 25% de descuento. Para la creación de los distintos roles, usaremos el plugin User Role Editor, con lo que podremos crear tantos como necesitemos, y asignarlos a los distintos usuarios. Para montar los distintos descuentos aplicados por defecto, usaremos el plugin Woocommerce Role Pricing, inicialmente con su versión gratuita disponible en el repositorio de WordPress, y en caso de que necesitemos las funcionalidad extras, podemos pasar a la versión Pro, disponible en mi tienda online eggemplo.com. Podemos pensar, que para qué montar todo esto si aplicando cupones podemos dar descuentos especiales a cada usuario. Pero la principal ventaja de dicho plugin es que se aplican los descuentos o precios finales directamente sobre toda la tienda, con lo que el usuario ve la tienda ya con sus precios adaptados. Entre las opciones que dispone, podemos aplicar descuentos en porcentajes o precios fijos, pudiendo por ejemplo aplicar un 10% de descuento, o 1€ a todos los productos. Todas estas y más opciones las tenemos disponible en la versión gratuita. Y si tenemos todo esto gratis, ¿tiene sentido pasarnos a la versión Pro de pago?, la respuesta es que depende de nuestras necesidades: - Entre las opciones avanzadas disponibles, destaca la de poder aplicar descuentos o precios específicos para los distintos productos, pudiendo tener un 10% de descuento en toda la tienda, pero si en un producto en concreto tenemos menos margen de beneficio, podemos aplicar por ejemplo un 5%. - Es importante en ocasiones mostrar tanto el precio original, como el precio final, así conseguimos que el usuario sea consciente de las ventajas que tiene al comprar en nuestra tienda.       --- ## Cómo montar una página corporativa con WordPress URL: https://ablancodev.com/notes/como-montar-una-pagina-corporativa/ Uno de los casos de uso más frecuente con el que nos encontramos habitualmente en el desarrollo WordPress, es montar la página corporativa de una empresa, donde mostrar los datos de contacto, así como información propia de la empresa. ## Casos de uso Desde el punto de vista de la empresa, nos encontramos con el siguiente caso de uso: << Imagen del caso de uso con actor el empresario >> Teniendo como actor al usuario final, tendríamos: << Imagen del caso de uso teniendo como actor al usuario final de la web >> ## Estructura web Viendo las necesidades que hemos recabado, decidimos estructurar la web con las siguientes secciones: - Portada : Página de bienvenida. - La empresa : Información corporativa de la empresa y sus socios. - Productos : Información sobre los productos que vende la empresa. - Servicios : Información sobre los servicios que ofrece la empresa. - Blog : La página tendrá un blog, donde poder hablar de información relacionada con la empresa, y hacer inbound marketing, y usarlo para atraer posibles clientes. - Contacto : Página con información de contacto. A estas páginas, habría que añadir las páginas que contienen los datos legales de la empresa, como son: - Política de cookies : Deberemos informar de las distintas cookies que usamos, y para que las necesitamos. - Política de privacidad : Información sobre qué vamos a hacer con los datos que recabamos de los usuarios. Esto lo necesitamos, ya que tenemos un formulario de contacto, donde pedimos información a los usuarios. Vayamos sección por sección, más centrándonos en un layout genérico y módulos necesarios, más que en estética y datos concretos. ### Portada Usaremos esta sección como escaparate principal, ya que será la página de aterrizaje (landing page) donde suelen llegar los usuarios por primera vez. El layout a usar será: << Imagen del layout de la portada >> Con un slider principal con imágenes destacadas. Seguido de una breve descripción de bienvenida o informativa de la empresa. A continuación una muestra de los servicios y productos destacados. Y terminamos con un pie de página con la información de copyright, enlaces legales y redes sociales. ### La empresa Proximamente … ### Productos Proximamente …. ### Servicios Proximamente …. ### Blog Proximamente …. ### Contacto Proximamente …. ## Enlaces externos A continuación se indican enlaces a los distintos plugins usados para llevar a cabo la implementación de este caso de uso. Proximamente ….     --- ## Modelo vista controlador en WordPress URL: https://ablancodev.com/notes/modelo-vista-controlador/ Vamos a aplicar el patrón de diseño software **Modelo-Vista-Controlador** en el desarrollo de plugins y temas WordPress. ## **El patrón de diseño software** Estamos ante uno de los patrones de diseño más usado en el desarrollo software. Se considera uno de los básicos a la hora de estructurar los proyectos. Con él se consigue desacoplar la capa que muestra los datos en la interfaz, con la capa del modelo de datos. Todo ello a través de una capa de controladores, que se encargarán de que ambas partes «se entiendan».**En el siguiente esquema podemos ver su estructura: * Este desacoplo nos permite por ejemplo cambiar el sistema de almacenar los datos, sin que la «aplicación pública» se vea comprometida. Es más, ese cambio sería 100% transparente para el usuario. Podemos por ejemplo pasar de tener los datos en una base de datos MYSQL, a tenerlos en un sistema de archivos en el servidor. O nos permitiría por ejemplo, cambiar la vista de los datos, de html a un feed rss sin que el modelo de datos se vea alterado con ello. ## Caso de uso** Vamos a implementar un portal para una inmobiliaria, en la que mostrar a los usuarios las propiedades disponibles, y poder solicitar información sobre ella. ### **Aplicándolo al desarrollo de plugins** La funcionalidad principal del portal será implementada a través de un plugin, al que llamaremos realhouse*.**Veamos las distintas partes que componen el patrón de diseño MVC (modelo vista controlador), en nuestro plugin: * - Vista:** Usaremos shortcodes para mostrar la información en el frontend. A modo de referencia del código a mostrar, crearemos el shortcode: realhouse_search, que nos mostrará un buscador avanzado de propiedades. - **Controlador:** Crearemos clases que harán de controladores entre la vista y el modelo de datos. Desacoplando las vistas del modelo de datos. - **Modelo:** De los distintos modelos que contendrá el sistema, y que representarán las entidades «del mundo real», nos centraremos en el modelo que implementa a las propiedades inmobiliarias. El flujo de información y peticiones que se producen en el patrón Modelo Vista Controlador, lo podemos ver en el siguiente diagrama de secuencia: 1: La vista necesita consultar el listado de propiedades, por lo que se lo pide al controlador. 1.1: El controlador al recibir la consulta, pide al modelo de datos la información requerida. 1.1.1: El modelo de datos devuelve al controlador la información que se le pidió. 1.1.1.1: El controlador, que ya tiene los datos disponibles, se los revuelve a la vista, para que pueda mostrar por pantalla dicha información. Como podemos ver, en ningún momento la vista sabe cómo se almacenan los datos, ni el modelo sabe cómo se muestran los datos. A esto es a lo que llamamos «desacoplamiento». #### **Veamos la implementación:** Empezando por el modelo de datos, que representará a las entidades inmobiliarias, y haciendo uso de las herramientas que nos ofrece WordPress, lo implementaremos mediante un custom post type llamado property*. Para su gestión, en la clase RealhouseProperty, implementaremos los métodos CRUD (crear, leer, actualizar y borrar). Nuestro controlador RealhousePropertyController estará a la escucha de las posibles peticiones que desde la vista le puedan llegar. Interpretándolas, gestionándolas y dando respuesta, tomando como fuente de información la clase del modelo RealhouseProperty. Y por último tenemos la vista, nuestro shortcode. Que en lugar de encargarse de hacer la gestión de datos y todo el proceso, simplemente se encarga de su acometido, «pintar». El shortcode hará sus peticiones al conrtrolador, sin saber en ningún momento cómo ni dónde se guardan los datos. ### **Aplicándolo al desarrollo de temas** En el desarrollo de temas, es evidente donde tenemos la parte de «la vista», quedando por definir el controlador y el modelo. La teoría dice que la funcionalidad de un tema debería ser definida en el propio tema, si es funcionalidad vinculada a él, y en un plugin si es más independiente. Ya con el patrón MVC se pretende desacoplar las distintas partes, recomiendo implementar la parte del controlador y el modelo en un plugin, por lo que podemos seguir las indicaciones dadas arriba. --- ## Usando selectize en WordPress URL: https://ablancodev.com/notes/usando-selectize-en-wordpress/ Una de mis librerías preferidas para la implementación avanzada de selects, es selectize.js Son las funcionalidades de selector múltiple y de búsqueda entre un gran conjunto de datos, las que en su día hicieron que me decantara por ella. Lo primero que vamos a necesitar para usar en WordPress, será importar la librería, tanto los ficheros js como los css. Una vez importados, vamos a importar nuestro propio fichero javascript, que usaremos para realizar las llamadas y definir los selects a los que aplicar selectize. En nuestro caso vamos a plicarlo a un select con id «select-movies» Ahora vamos a la parte front, donde tendremos nuestro select, por ejemplo en una plantilla del tema, o en un shortcode. Que tras aplicar selectize, nuestro visitantes podrán ver algo parecido a esto: * A continuación dejo un sencillo plugin con el que tras instalarlo, tendremos disponible la librería selectize.js en nuestro WordPress. Además incluye un shortcode para poder probarlo, por lo que si usamos: * veremos un selector múltiple de países. Descargar plugin v1.0 ## Cargando datos por ajax Me he encontrado con el problema de tener que cargar las opciones del select dinámicamente por ajax, ya que en este caso podían ser un número muy elevado de opciones, y podría dar problemas de rendimiento en la web, por ejemplo al querer cargar 100.000 entradas. Para la carga por ajax, vamos a tener dos partes diferencias, por un lado el fichero javascript encargado de dar la funcionalidad al select, y por otra parte, el fichero php que nos proporcionará los datos a cargar tras la búsqueda. JS: PHP: --- ## Filtrar etiquetas HTML con javascript URL: https://ablancodev.com/notes/filtrar-etiquetas-html-con-javascript/ La idea no es filtrar las etiquetas enteras como tales, sino sustituir los ‘‘ por sus correspondientes valores HTML --- ## Distintos posts_per_page por categoría URL: https://ablancodev.com/notes/distintos-posts_per_page-por-categoria/ (Código y explicación obtenida de la página: https://amethystwebsitedesign.com/change-the-posts-per-page-in-your-portfolio-category-template/ ) --- ## Notas Ruby on Rails URL: https://ablancodev.com/notes/ruby-on-rails/ Vamos a empezar a trastear Ruby on Rails, así que usaré este post a modo de blog de notas Ruby on Rails. Lo primero a tener en cuenta con Rails, es que nos generará aplicaciones basadas en el patrón de diseño MVC (modelo-vista-controlador). Por lo que cada uno de los componentes que generemos tendrán estos tres elementos. ## Notas Ruby on Rails **Creamos la aplicación Blog:** *rails new blog* **Creación de un controlador (welcome) con la acción (index):** *rails generate controller welcome index* En config->routes.rb tendremos un:** get ‘welcome/index’ ### Generación de resources: Por ejemplo, si queremos gestionar artículos, creamos el resource ‘articles’. Un resource sería el equivalente a un tipo de objeto, con sus respectivos métodos CRUD. Lo definimos, editando el fichero *config/routes.rb*, añadiendo: *resources :articles* Generación de modelos:** rails generate model User nombre:string email:string Esto nos creará el fichero del modelo, y la definición de la tabla de la bd para cuando se haga la migración. Para desplegar la tabla correspondiente y/o crear la base de datos inicial: *bundle exec rake db:migrate*   ### Route Las direcciones y enrutamiento se harán desde el fichero *config/routes.rb* Por ejemplo, para indicar una ruta por defecto principal: *root :to => «welcome#index»*   ### Despliegue Arrancamos el servidor con: *rails server*   ### Código fuente Para el aprendizaje, he usado como fuente el manual de Ruby on Rails de LibrosWeb: Introducción a Ruby on Rails. El código que voy generando está disponible en este repositorio de Github. --- ## EOF while reading packet. Please contact your web hosting service provider for assistance – Cyberduck acceso ssh URL: https://ablancodev.com/notes/eof-while-reading-packet-please-contact-your-web-hosting-service-provider-for-assistance-cyberduck-acceso-ssh/ Hoy hemos amanecido sin poder conectarme al servidor de uno de mis clientes por ssh atravás del software Cyberduck. Todo estaba bien configurado, y nada había cambiado, pero me lanzaba el error: *«EOF while reading packet. Please contact your web hosting service provider for assistance.»* Tras ver posibles soluciones, una de ella comentaba que podía ser que la contraseña del usuario ssh hubiese caducado, y necesitara actualizarla. Aunque al principio me sonaba raro, ya que estaba usando para conectarme ficheros de claves, tras intentar conectarme por consola, usando el comando ssh, Bingo !!, me dice que la contraseña de mi usuario está caducada y que tengo que renovarla, así que tras renovarla, parece que todo vuelve a la normalidad.   --- ## Redsys con https no llega notificación de pago URL: https://ablancodev.com/notes/redsys-con-https-no-llega-notificacion-de-pago/ Trabajar con Redsys y certificados https a veces es un poco desesperante, sobre todo cuando tenemos certificados SSL básicos (de los baratos o que regalan los hostings). Redsys no es compatible con dichos certificados, y las notificaciones a veces no llegan, por lo que Woocommerce no puede procesarlas. Para ello, una solución, es hacer que nuestro sistema no redireccione las peticiones de notificación del banco a https, sino que las deje como http. ¿Cómo conseguirlo?, aquí dejo un ejemplo de código para añadir al fichero .htaccess:   Más o menos viene a decir: «si venimos por http, y la url no contiene ‘wp-api’ (todos los casos, excepto notificación de pago), entonces redirecciona a https», por lo que la notificación de pago se quedaría en http.   --- ## Primeros pasos con GIT URL: https://ablancodev.com/notes/primeros-pasos-con-git/ Implementando el artículo …. en cuento lo tengamos: git pull; --- ## Contact Form Submissions URL: https://ablancodev.com/notes/contact-form-submissions/ **El reto del día:** Añadir un checkbox en los formularios de Contact Form 7 donde indicar si el usuario quiere o no recibir publicidad. **Solución adaptada:** A priori, es sencillo, añadimos un checkbox de los que viene por defecto en la configuración de CF7, indicando en la parte de configuración del email que se envía, el valor de dicho campo y listo. Pero como peculiaridad, tenemos que el cliente quería un registro donde consultar a los usuarios que quieren o no recibir publicidad, por lo que necesitaremos guardar en base de datos la informacion suministrada por los formularios. Trasteando entre los plugins disponibles en el repositorio oficial de WordPress, y tras varias pruebas, me he decidido por usar el plugin Contact Form Submissions. Su uso es sencillo y rápido. Se instala y listo !! Ya tenemos el plugin funcionando, sin necesidad de configuración ni pasos adicionales. Desde *Contactos->Submissions* podremos ver los distintos envíos de formularios, y consultar la información suministrada. Además tiene la opción de exportar a CSV, lo que nos permitirá de forma muy sencilla filtrar los datos, y obtener de forma rápida el listado de usuarios que quieren o no recibir publicidad, con programas como Excel o similares. El plugin está disponible en el Repositorio Oficial de WordPress. --- ## GDPR Cookie consent + W3 Total Cache URL: https://ablancodev.com/notes/gdpr-cookie-consent-w3-total-cache/ Si instalamos ambos plugins, ocurre que cuando aceptamos las cookies, ya está cacheado el contenido, y no carga los scripts que queremos añadir, por ejemplo los de Google Analitycs. Código solución (suponiendo que una de las cookies que añadimos es ‘_ga’ de GA: --- ## Duración de tiempo a formato ISO 8601 en PHP URL: https://ablancodev.com/notes/duracion-de-tiempo-a-formato-iso-8601-en-php/ Pues eso, vamos a transformar duración de tiempos en formato ISO 8601, por ejemplo para su uso con Schema. Por ejemplo para las recipes de schema, para la duración de la receta: La función my_iso8601_duration la he cogido de stackoverflow. --- ## Spring Property – Aplicación de gestión de propiedades con Java Spring URL: https://ablancodev.com/notes/spring-property-aplicacion-de-gestion-de-propiedades-con-java-spring/ Vamos a desarrollar una aplicación de gestión de propiedades con el framework Spring Java. La idea es hacerlo incremental, empezando por un modelo sencillo e ir añadiendo funcionalidad, e ir creando artículos explicativos para cada una de las entregas. Empezamos con un diagrama de clases sencillo, donde tenemos: *Property *– Que representa a una propiedad *Amenity *– Que representa una característica de una propiedad, por ejemplo: TV, calefacción, etc … *Image *– Que representa a una imagen en nuestro sistema. Como se puede ver por las relaciones entre clases, y sus multiplicidades, una propiedad puede tener o no, muchas amenities e imágenes. Aunque puede parecer raro que una imagen pueda estar en más de una propiedad, se ha pensado así, por el caso de ejemplo de un bloque de pisos, donde varias propiedades comparten como imagen una foto de la piscina comunitaria. --- ## Aplicación de Contactos con Spring URL: https://ablancodev.com/notes/aplicacion-de-contactos-con-spring/ Qué mejor forma que empezar a trastear un nuevo framework, que hacerlo programando a la vez que leyendo documentación. Así que tras mi primer Hello World sin mucha trascendencia, nos ponemos con una pequeña aplicación de Contactos. La idea es tener un listado de contactos, donde poder añadir nuevos contactos, editarlos o eliminarlos. Para no complicar demasiado esta primera versión, se hará una simulación de checkeo de login, siendo el usuario de acceso: **user** y contraseña: **user** Aunque la dirección de entrada sería* /login*, hemos creado una redirección, para que directamente se entre en dicha dirección. Por lo que si lo corremos en local, bastaría con visitar: http://localhost:8080/login   Una vez «logueados» con nuestra usuario «user», podremos ver el listado de contactos que tenemos.   Teniendo las opciones de editarlos, eliminarlos o crear nuevos contactos.   Aplicación sencilla, que para dar los primeros pasos está muy bien. Iremos mejorándola añadiéndole funcionalidad. La tenéis disponible en github: https://github.com/eggemplo/contactos     --- ## Debug URL: https://ablancodev.com/notes/debug/ A debuguear se dijo…. ### PHP – var_dump to string Alguna que otra vez lo he buscado por google 😉 ob_start(); var_dump($someVar); $resulta = ob_get_clean(); ### En Prestashop: Logger::addLog( "Mensaje a añadir al log" ); --- ## Github URL: https://ablancodev.com/notes/github/ Algunas notas sobre github / git. ### Sincronizar un fork desde su master. 1. Clonamos el fork: git clone git@github.com:YOUR-USERNAME/YOUR-FORKED-REPO.git 2. Añadimos el remoto desde el original a nuestro fork: cd into/cloned/fork-repo git remote add upstream git://github.com/ORIGINAL-DEV-USERNAME/REPO-YOU-FORKED-FROM.git git fetch upstream 3. Actualizamos el form desde el repositorio original: git pull upstream master 4. Y push-eamos para actualizar el remoto: git push --- ## Integraciones de IA para WordPress URL: https://ablancodev.com/servicios/integraciones-ia-wordpress/ # Tu WordPress que habla con IA. No se trata de instalar un plugin de ChatGPT. Se trata de construir una capa que permite a agentes e IA acceder a tus datos, ejecutar acciones y automatizar procesos — respetando tus reglas de negocio. Cuéntame tu caso ## Qué significa exponer WordPress a la IA Consiste en desarrollar una interfaz —un MCP, una API propia o un conjunto de endpoints controlados— que permite a un agente de IA consultar información y ejecutar acciones sobre tu WordPress sin acceso directo a la base de datos ni al panel de administración. Tú decides qué puede hacer la IA y qué no. El agente actúa dentro de los límites que defines: puede leer pedidos pero no modificarlos, puede crear borradores pero no publicarlos, puede consultar el catálogo pero no cambiar precios. Control total, sin perder potencia. ## Ejemplos reales de lo que se puede construir ### WooCommerce en lenguaje natural Un agente que puede responder «¿cuántos pedidos de más de 100€ tuve la semana pasada?» o «busca al cliente que compró una chaqueta azul en marzo» directamente desde tu tienda, sin que nadie tenga que abrir el panel ni filtrar manualmente. ### Gestión de contenido asistida Un asistente que crea borradores basados en un brief, actualiza metadatos SEO de posts existentes, reorganiza categorías o revisa el estado de publicación de una sección completa — todo desde una conversación, sin tocar el editor. ### Soporte con contexto real Un agente de soporte que antes de responder consulta el historial de pedidos del cliente, sus tickets anteriores y el estado actual de su cuenta — y genera una respuesta personalizada, no una plantilla genérica. ### Plugins propios expuestos como herramientas IA Si tienes plugins desarrollados a medida, puedo crear wrappers para que un agente pueda usar sus funcionalidades: consultar reservas, gestionar suscripciones, ejecutar procesos de negocio propios — como si fueran comandos del agente. ## No solo ChatGPT Las integraciones que construyo funcionan con cualquier modelo o proveedor: Claude (Anthropic), GPT (OpenAI), modelos locales con Ollama, o el que mejor encaje en tu caso. La arquitectura es flexible por diseño — si mañana aparece un modelo mejor, no tienes que rehacer todo. Esto es especialmente relevante para proyectos que manejan datos sensibles y no quieren depender de un único proveedor externo. ## Dónde aporta más valor Tiendas WooCommerce con catálogos grandes, medios digitales con mucho contenido, plataformas de membresía, academias online, intranets corporativas sobre WordPress y cualquier instalación que tenga plugins propios con lógica de negocio específica. En general: cualquier WordPress donde haya datos que ahora solo son accesibles entrando al panel manualmente. ## ¿Tienes un WordPress con datos que deberían ser más accesibles? Cuéntame qué tienes y qué te gustaría poder hacer. Te digo si tiene solución y cómo lo enfocaría. Reservar una llamada rápida Prefiero escribir --- ## Archivo URL: https://ablancodev.com/proyectos/archive/ /projects/archive # Exploration history. Things I tried, built and ultimately stopped. Not failures — data points. Each one taught something real about products, markets and my own limits. ## AI Routes Assistant 2024 archived What I was trying Build an AI assistant that generates personalized travel routes from natural language. Ask «give me 3 days in Porto for a coffee nerd» and get a structured, bookmarkable itinerary. What worked The LLM prompting for route generation was surprisingly good. Got to a working MVP fast. What failed No clear distribution channel. The product worked but getting users was a different problem I had not solved. Also maintaining accurate POI data was expensive. What I learned Validation before building. Distribution is the product, especially for local-data dependent tools. Read the build log → ## Amazon SEO with AI 2023 experiment What I was trying Use LLMs to automatically optimize Amazon product listings — titles, bullets, descriptions — based on keyword research and competitor analysis. What worked GPT was decent at rewriting copy. The pipeline for fetching competitor data with scraping worked. What failed Amazon ToS makes aggressive scraping risky. The real constraint was API access to actual search volume data — everything was estimation. What I learned Platform dependency is a real risk. If the platform controls the data you need, you are always one policy change away from dead. Read the build log → ## AI Onboarding System 2023 POC What I was trying Replace static onboarding flows with a conversational AI that adapts the setup sequence based on user answers and behavior. What worked The conversational flow was engaging. Users stayed longer in the onboarding steps compared to static alternatives in early tests. What failed Integration complexity was high. Every client had a different stack. Turned into a consulting engagement rather than a repeatable product. What I learned B2B tools need strong opinions about the stack or infinite customization budget. I had neither. Read the build log → ## Invoice Generator with AI 2023 POC What I was trying Voice-to-invoice: describe what you did this week and get a structured, ready-to-send invoice in seconds. What worked The core flow worked. Speaking was faster than filling forms. The output was clean enough to send directly. What failed Too many free alternatives exist. Wave, Bonsai, FreshBooks. Hard to compete on price with existing solutions that do more. What I learned The voice UX was a real insight — reused it in TellMe. Sometimes a failed product is a feature in another one. Read the build log → ## MVP en 24 horas 2022 archived What I was trying Document the process of going from zero to a functional MVP in a single day — the decisions, the cuts, the order of operations. What worked The constraint forced clarity. Scope became obvious when you only have 24 hours. Great exercise for mental models around «what is an MVP really.» What I learned Speed is a skill. The faster you can de-risk assumptions the better. This shaped how I approach every new project now. Read the build log → --- ## Blog URL: https://ablancodev.com/blog/ /blog # Cómo pienso y qué construyo. Build logs, reflexiones y notas — del proceso de hacer cosas reales. ⚡ IA local Ver todas → Experimentos, tutoriales y proyectos reales con inteligencia artificial, modelos locales, open source, agentes y automatización. - ### Cómo montar tu segundo cerebro con IA 100% local Sep 2026 - ### Guía para empezar con IA Local Ago 2026 - ### Creo mi buscador de imágenes semántico con IA local Ago 2026 📡Build Logs Ver todos → El proceso real. Decisiones, errores, costes y lo que aprendí. - ### Conociendo a CURRO, nuestra IA familiar Hoy nace nuestro asistente de IA familiar, «CURRO». En casa cada día usamos más la IA, nada del… - ### Generar manuales automáticamente con Playwright y Claude Code Hoy tras terminar una de las nuevas piezas del ecosistema selfhosted-saas de GrowthPlugins, GrowthBalance, tocaba lo de siempre,… - ### Vigilancia casera con IA local: un móvil reciclado, un Mac y Docker Un móvil antiguo como cámara, detección de movimiento con OpenCV einterpretación de la escena con un modelo de… - ### Conectando el MCP de WordPress con claude code / desktop La idea es una vez que tenemos WordPress con las abilities, exponerlas con un mcp para que nuestro… - ### IA local: el kit de supervivencia cuando caigan las grandes IA Tu kit de supervivencia para cuando los grandes cierren el grifo Imagina que mañana te levantas y: ¿Qué… 🧠Ensayos Ver todos → Reflexiones largas sobre arquitectura, producto, IA y la mentalidad de builder. - ### MCP, un protocolo más humano de lo que parece Ago 2026 - ### Adios RAG, hola Gemini File Search Store Nov 2025 - ### Montar un LLM sin filtros May 2025 - ### Gestión de proyectos: Planning Oct 2022 🔍Explorar por tema IA Automatización Sistemas Productos Indie Hacking Experimentos Infraestructura --- ## Ahora URL: https://ablancodev.com/now/ /now # En qué estoy ahora. Actualizado: Mayo 2026 Construyendo ## ImageFlow Un flujo de creación de creatividades con IA. La idea es poder generar assets visuales para redes, anuncios y contenido sin depender de un diseñador para cada iteración. Todavía en fase de laboratorio — probando qué modelos dan resultados consistentes y cómo estructurar el flujo de prompts. Enviado ## Coffedoro para Mac Actualicé Coffedoro para que funcione también en Mac. Lo que empezó como una app de Pomodoro para iOS ya corre en desktop — mismo ambiente de café, misma lógica de foco, pantalla más grande. Aprendiendo ## Curso de IA para creativos — Founderz Liado con el curso de Founderz de IA para creativos. Más que herramientas, me interesa entender cómo cambia el flujo creativo cuando la IA entra en el proceso — y cómo eso conecta con lo que estoy construyendo en ImageFlow. --- ## Open Source URL: https://ablancodev.com/open-source/ Boilerplates, CLI tools, snippets and public repos on GitHub. --- ## Lab URL: https://ablancodev.com/labs/ Experiments, prototypes and weird automations. Not everything ships — and that’s fine. --- ## Home indie URL: https://ablancodev.com/ Ingeniero de software & builder # Antonio Blanco Construyo productos con IA: automatizaciones, integraciones y MVPs sobre la web. Y lo cuento todo — proceso real, código y costes incluidos. Ver lo que construyo Hablemos ## ¿En qué puedo ayudarte? Trabajo con negocios digitales que quieren sacar partido real a la IA — sin humo, con código. ### Integrar IA en tu negocio Agentes, automatizaciones y flujos inteligentes conectados a tus herramientas. Deja de hacer a mano lo que puede hacer una máquina. Ver servicio → ### Lanzar un producto o MVP Tienes una idea y necesitas validarla rápido con algo real. Construyo el MVP contigo, con foco en lo que importa y sin sobreingeniería. Ver servicio → ### Conectar sistemas e integraciones APIs, webhooks, MCPs, Zapier o código puro. Conecto lo que tienes con lo que necesitas, sin que tengas que tocar nada a mano. Ver servicio → ### IA sobre WordPress WordPress sigue siendo la plataforma de la mitad de la web. Lo que cambia es lo que puedes hacer encima de él con IA. Ver servicio → Reserva una reunión gratuita ## 📦 Productos en producción Ver todos → Proyectos que llegaron a producción con usuarios reales. Live ### GrowthPlugins Plugins para que las agencias WordPress gestionen proyectos, soporte y clientes sin depender de SaaS externos. growthplugins.com → Live ### SecondBrainBot Tu segundo cerebro en Telegram. Captura ideas, artículos y notas — las recuperas cuando las necesitas. secondbrainbot.com → Live ### NeveraVacía Planificador de menús semanal con lista de la compra automática. Lo que comes, organizado. neveravacia.com → Live · iOS ### Coffedoro Pomodoro con ambientes de café para mantener el foco. Para los que trabajan mejor con ruido de fondo. App Store → ## Últimas entradas Ver todos → El proceso real. Decisiones, errores, costes y lo que aprendí. - ### Conociendo a CURRO, nuestra IA familiar Jul 2026 - ### Generar manuales automáticamente con Playwright y Claude Code Jul 2026 - ### Vigilancia casera con IA local: un móvil reciclado, un Mac y Docker Jul 2026 ## ¿Tienes un proyecto en mente? Cuéntamelo en una llamada rápida — sin compromiso. Reservar una llamada rápida Hablemos --- ## Newsletter URL: https://ablancodev.com/newsletter/ # Una herramienta nueva,cada viernes. Gratis  ·  Sin spam  ·  Te vas cuando quieras ## Qué recibes cada semana 🎁 ### La herramienta Descargable y lista para usar ese mismo día. Plugin de WordPress, agente de IA, flujo de automatización o script. Algo que resuelve un problema real en menos de 10 minutos desde que lo recibes. ⚙️ ### El proceso Cuánto tardé en construirla, qué decidí no incluir y por qué, qué salió mal. No el tutorial pulido — lo que pasó de verdad, con datos concretos. 💡 ### El aprendizaje Una cosa técnica, de producto o de negocio que cambió cómo pienso esta semana. Independiente de la herramienta — breve y directo. ## Ediciones anteriores La primera edición sale pronto. Mientras tanto, puedes ver en qué estoy trabajando en la página de proyectos o leer el blog. ## ¿Quién está detrás? Soy Antonio Blanco. Ingeniero de software con 15+ años construyendo sobre WordPress — plugins en el repositorio oficial, productos comerciales, y un ecosistema propio de herramientas (GrowthPlugins). En los últimos años he pivotado hacia IA aplicada: agentes autónomos, MCPs e integraciones que reemplazan trabajo manual real. Construyo en público — lo que falla, lo que aprendo y lo que lanzo. Más sobre mí → ## ¿Te apuntas? Cada viernes, una herramienta que puedes usar ese mismo día. Gratis  ·  Sin spam  ·  Te vas cuando quieras --- ## Privacy Policy URL: https://ablancodev.com/legal/coffeedoro/ Your privacy is important to us. This privacy policy explains how our app, **Coffeedoro**, handles user data. ### 1. Data Collection **Coffedoro** does **not collect, store, or share any personal information**. All data, such as Pomodoro sessions and settings, is stored locally on your device. ### 2. Third-Party Services Our app does **not use any third-party services** that track or collect your data. ### 3. Cookies and Tracking This app does **not use cookies or any tracking technologies**. ### 4. Data Sharing Since no personal data is collected, we **do not share your information with anyone**. ### 5. Security All data remains **on your device**. We do not transmit any data over the internet. ### 6. Changes to This Privacy Policy We may update this policy from time to time. Any changes will be posted on this page with an updated effective date. ### 7. Contact Us If you have any questions about this privacy policy, please contact us at:antonio@blancoleon.com --- ## Política de privacidad URL: https://ablancodev.com/legal/qa-automation-flow-chrome-extension/ ## Política de Privacidad – QA Automation Flow **Última actualización:** 29 de junio de 2025 En QA Automation Flow nos tomamos tu privacidad muy en serio. Esta extensión ha sido diseñada para funcionar de forma **segura, privada y completamente local**, sin recopilar ningún tipo de dato personal ni información de navegación. ### 🔍 ¿Qué información recopilamos? **Ninguna.****QA Automation Flow no recopila, almacena ni transmite ningún dato personal**. Todo el contenido que generes (flujos de automatización, configuraciones o logs) se guarda exclusivamente en el navegador del usuario mediante la API de almacenamiento local de Chrome (chrome.storage). No utilizamos cookies, ni servicios de analítica, ni conectamos con servidores externos. ### 🔧 Permisos utilizados Para poder funcionar correctamente, la extensión solicita los siguientes permisos: - **activeTab**: Permite acceder temporalmente a la pestaña activa **cuando el usuario lo solicita**, por ejemplo, al seleccionar un elemento o ejecutar un flujo. - **storage**: Se utiliza para guardar localmente tus flujos, configuración y preferencias. - **scripting**: Permite inyectar scripts en la página actual para simular acciones como clics, escritura o validación. - **sidePanel**: Utilizado para mostrar la interfaz visual de la extensión dentro del panel lateral de Chrome. - **host_permissions (http://*/* y https://*/*)**: Necesario para que la extensión funcione en cualquier sitio web que el usuario decida automatizar. **Nunca se accede automáticamente a páginas sin consentimiento.** ### 🔐 ¿Qué hacemos con tus datos? **Nada.****No transferimos, compartimos, analizamos ni vendemos ningún tipo de información.La extensión está diseñada para ejecutarse 100% en local**, sin conexión a servidores externos. ### 🌍 Seguridad y transparencia - ✅ No se realiza ningún tipo de seguimiento o analítica. - ✅ Todos los flujos se almacenan localmente. - ✅ No solicitamos ni accedemos a información sensible. - ✅ La extensión es **open source** y auditable. ### 📦 Datos almacenados en tu navegador Todos los datos generados (como flujos o preferencias) se guardan en tu navegador usando chrome.storage.local. Puedes verlos, exportarlos o eliminarlos en cualquier momento desde la propia interfaz de la extensión. ### 📬 Contacto Si tienes alguna duda sobre esta política o necesitas más información, puedes escribirnos a:antonio@blancoleon.com --- ## Política de privacidad URL: https://ablancodev.com/legal/ai-content-helper/ ## Política de Privacidad – AI Content Helper Esta política de privacidad explica cómo se recopilan, usan y protegen los datos en la extensión de Chrome **AI Content Helper**. ## 🧠 Finalidad de la extensión La extensión tiene como única finalidad ayudar al usuario a generar o mejorar contenido textual mediante inteligencia artificial (IA), directamente en campos editables de páginas web como WordPress, Webflow, etc. ## 🔒 Qué datos recopilamos - No recopilamos datos personales ni identificables. - Todo el contenido generado o editado se procesa localmente en el navegador del usuario. - El único dato que puede almacenarse es la clave API de OpenAI, y lo hace localmente en tu navegador, nunca en servidores externos. ## 🌐 Conexiones externas La extensión se conecta únicamente con el servicio de OpenAI (https://api.openai.com) cuando el usuario pulsa manualmente el botón para generar texto. La información enviada es la que el usuario introduce como prompt. ## ⚙️ Permisos requeridos - **activeTab**: para insertar el botón de IA en la pestaña activa. - **scripting**: para inyectar o modificar contenido en campos de texto. - **storage**: para guardar configuraciones locales. - **host_permissions**: para que funcione en cualquier sitio web donde el usuario edite contenido. ## 📬 Contacto Para cualquier duda, sugerencia o solicitud sobre esta política, puedes escribir a antonio@ablancodev.com. Última actualización: junio de 2025. --- ## Sobre mí URL: https://ablancodev.com/sobre-mi/ # Soy Antonio Blanco. Ingeniero de software. Construyo productos con IA, automatizaciones e integraciones sobre la web — y lo cuento todo en el proceso. Reservar una llamada rápida Leer el blog Llevo más de 15 años construyendo sobre WordPress — plugins publicados en el repositorio oficial, productos comerciales vendidos internacionalmente, y un ecosistema propio de herramientas (GrowthPlugins) que sigo manteniendo y evolucionando. En los últimos años he pivotado hacia IA aplicada: agentes autónomos, MCPs, integraciones con LLMs y automatizaciones que reemplazan trabajo manual real. No el tipo de IA que impresiona en una demo y no funciona en producción — el tipo que resuelve un problema concreto a alguien que lo tiene. He trabajado como CTO y líder técnico en empresas de distintos tamaños. Ahora trabajo solo o en equipos pequeños, con clientes directos, sin capas intermedias. Prefiero proyectos donde importa el criterio técnico, no solo ejecutar lo que alguien ya decidió. ## Lo que construyo Además del trabajo con clientes, tengo productos en producción con usuarios reales: GrowthPlugins, SecondBrainBot, NeveraVacía y Coffedoro. No son side projects congelados — son cosas que uso, mantengo y en las que sigo aprendiendo qué funciona y qué no. Ver todos los proyectos → ## Build in public Cuento lo que construyo mientras lo construyo: decisiones, errores, costes, lo que funciona y lo que no. Si te interesa el proceso real detrás de lanzar productos con IA, el blog es el sitio. ## Dónde encontrarme - GitHub — código, plugins y experimentos - LinkedIn — si prefieres el canal profesional - Contacto directo — o simplemente escríbeme --- ## Desarrollo de MVPs URL: https://ablancodev.com/servicios/mvp/ # De la idea al producto funcional. En semanas, no en meses. El mayor riesgo de un proyecto no es equivocarse — es tardar seis meses y gastar miles de euros para descubrir que el mercado no quería exactamente eso. Cuéntame tu idea ## Qué es un MVP bien construido — y qué no es Un MVP no es software mal hecho. Es software con el alcance exacto para validar tu propuesta de valor ante usuarios reales — construido sobre una base que aguanta cuando necesites escalar, sin tener que tirarlo todo a la basura. La parte difícil no es programar. Es decidir qué no entra en la versión 1. Eso es lo primero que trabajamos juntos: podar todo lo que no es imprescindible para que alguien pague o use el producto, y quedarnos con el núcleo que realmente valida la hipótesis. ## Qué tipos de MVP construyo ### Aplicaciones web y SaaS Código limpio, arquitectura pensada para crecer. Autenticación, pagos con Stripe, analíticas básicas y todo lo que necesita un producto para empezar a funcionar de verdad desde el primer día. ### WordPress como backend de producto WordPress tiene sistema de usuarios, roles, API REST y miles de integraciones ya construidas. Usarlo como motor de un MVP reduce el tiempo de lanzamiento drásticamente — sin el coste de construir esa infraestructura desde cero. Es una ventaja real que muy poca gente aprovecha. ### IA dentro del producto Modelos de lenguaje, agentes autónomos o búsqueda semántica integrados en el core de tu producto — no como un añadido, sino como parte de la propuesta de valor. Si tu diferencial es la IA, lo construimos así desde el principio. ### Herramientas internas Paneles de gestión, dashboards, automatizaciones específicas para vuestro equipo. A veces el MVP más valioso no es el producto externo sino la herramienta que hace que vuestro equipo funcione el doble de rápido. ## Cómo funciona el proceso - **Definimos el alcance mínimo** — qué entra, qué no entra y por qué. Si una funcionalidad no es imprescindible para validar la hipótesis principal, se queda fuera de la versión 1. - **Diseñamos la arquitectura** — base limpia, escalable, sin deuda técnica que lastre el futuro. Tienes visibilidad total del avance semana a semana. - **Construimos y conectamos** — pagos, autenticación, integraciones con los servicios que necesita el producto desde el primer día. - **Lanzamos** — despliegue en entornos seguros y optimizados. El producto en manos de tus primeros usuarios, no en un staging que nadie ve. ## ¿Tienes una idea esperando para ser construida? Cuéntamela. No hace falta que esté definida — eso es parte del trabajo. 15 minutos y te digo si tiene sentido, cómo lo enfocaría y qué costaría aproximadamente. Reservar una llamada rápida Prefiero escribir --- ## Inteligencia Artificial y Automatizaciones URL: https://ablancodev.com/servicios/inteligencia-artificial-y-automatizaciones/ # Automatizaciones que no se rompen. IA que hace algo útil. El problema no es saber que la automatización existe. Es construirla de forma que funcione a las 3 de la mañana cuando nadie está mirando. Cuéntame tu caso ## El trabajo pesado que puedo quitarte de encima Muchos equipos pierden entre un 20 y un 30% de su jornada en tareas que no necesitan cabeza humana: copiar datos entre aplicaciones, clasificar correos, extraer información de PDFs, mover leads a mano, generar informes siempre iguales. Son tareas que se pueden automatizar — y que si no se automatizan bien, rompen cosas. Diseño e implemento sistemas que conectan tus herramientas actuales, procesan información con IA donde tiene sentido y ejecutan flujos de trabajo completos sin intervención manual. No demos — cosas que funcionan en producción. ## Qué construyo concretamente ### LLMs integrados en tus procesos Conecto APIs de OpenAI, Anthropic (Claude) o modelos locales con tus sistemas para automatizar tareas cognitivas: resúmenes de reuniones, análisis de contratos, extracción de datos estructurados, clasificación de incidencias. No un chatbot — lógica de negocio real. ### Agentes autónomos Agentes que razonan y actúan: consultan tu base de datos en lenguaje natural, redactan respuestas de soporte personalizadas con el historial del cliente, o activan flujos internos según el contexto. Sin que nadie tenga que pulsar nada. ### Flujos de trabajo entre sistemas Make, Zapier, n8n o código puro cuando ninguno llega. Tus herramientas hablando entre sí: CRM, ERP, email, Slack, bases de datos, APIs internas. Sin que nadie tenga que copiar y pegar nada entre aplicaciones. ### Búsqueda semántica y gestión del conocimiento Bases de conocimiento con embeddings para que tu equipo o tus clientes encuentren información exacta de forma instantánea — más allá de palabras clave. RAG sobre tus propios documentos, productos o historial de soporte. ## La diferencia entre un flujo que dura y uno que es un hilo de plastilina Cualquiera puede arrastrar cajas en Make y crear algo que funciona en la demo. El problema llega cuando una API cambia, entra un dato corrupto o el sistema falla a las 3 de la mañana y nadie se entera hasta que el cliente llama. Mi trabajo incluye siempre: gestión de errores con recuperación automática y notificación, optimización del uso de tokens de IA para que la factura no explote, y control del flujo de datos sensibles. Si Make o Zapier no permiten hacer exactamente lo que necesitas, escribo el código que lo resuelve. Y si tu problema se resuelve con un proceso bien diseñado sin IA, te lo digo. No vendo tecnología por vender. ## ¿Qué proceso te gustaría no tener que hacer a mano? Cuéntamelo. 15 minutos y te digo si tiene solución, cómo la enfocaría y qué costaría aproximadamente. Reservar una llamada rápida Prefiero escribir --- ## Servicios URL: https://ablancodev.com/servicios/ # Convierto problemas complejos en soluciones que funcionan. Soy Antonio Blanco, ingeniero de software. Ayudo a empresas y fundadores a automatizar procesos, aplicar IA donde de verdad aporta y construir las herramientas que las soluciones estándar no cubren. Entiendo antes de construir. Simplifico antes de complicar. Y te digo con honestidad lo que necesitas — aunque sea menos de lo que pensabas comprar. Cuéntame tu problema Cómo trabajamos juntos ## Los problemas con los que puedo ayudarte ### «Perdemos horas cada semana en tareas repetitivas» Diseño automatizaciones que conectan vuestras herramientas y eliminan el trabajo manual: sincronización entre aplicaciones, procesamiento de datos, gestión documental, flujos de aprobación, alertas. **Resultado: tu equipo dedica su tiempo a lo que sí necesita cabeza.** ### «Queremos usar IA, pero no sabemos dónde aporta de verdad» Integro modelos de lenguaje, asistentes conectados a vuestros datos, RAG y búsqueda semántica — solo donde tienen sentido. Y si vuestro problema se resuelve con un proceso bien diseñado en lugar de con IA, te lo diré. **Resultado: IA que produce valor medible, no una demo que impresiona.** ### «Las herramientas estándar no encajan con cómo trabajamos» Construyo software a medida: aplicaciones web, paneles internos, APIs, integraciones entre sistemas que no se hablan entre sí, y WordPress avanzado con IA — plugins, WooCommerce, Gutenberg, rendimiento. **Resultado: herramientas que se adaptan a tu negocio, y no al revés.** ### «Tenemos una idea, pero no sabemos si construirla ni cómo» Aterrizo la idea, defino el alcance mínimo que la valida y construyo el MVP. Sin sobreingeniería: lo más simple que te permite aprender si funciona. **Resultado: un producto real que probar con usuarios, en semanas.** ## Áreas de trabajo Si quieres entrar en detalle en alguna área específica: ### ⚡ IA y Automatizaciones Agentes, LLMs, flujos de trabajo autónomos, RAG, búsqueda semántica. Conectado a tus sistemas actuales. Ver en detalle → ### 🚀 MVPs y productos digitales De la idea al producto funcional en semanas. Con foco en validar, sin deuda técnica que lastre el futuro. Ver en detalle → ### 🔌 WordPress con IA +15 años sobre WordPress. Plugins a medida, WooCommerce avanzado, MVPs rápidos con WP como backend e IA integrada. Ver en detalle → ### 🧩 Integraciones IA para WordPress Expongo WordPress a agentes y asistentes de IA mediante MCPs y APIs propias. Tu WordPress que habla con Claude, ChatGPT o tu propio modelo. Ver en detalle → ## Cómo trabajo No empiezo por la tecnología. Empiezo por una pregunta: **¿qué problema estamos intentando resolver?** - **Entender** — el problema, el contexto y las restricciones reales. - **Diseñar** — la solución más simple que funciona. La complejidad se gana, no se presupone. - **Construir** — con las herramientas adecuadas, no las más populares. - **Medir y mejorar** — lo que no se mide, no existe. El código es una consecuencia. El criterio es el trabajo. ## Lo que puedes esperar de mí Te diré lo que pienso, aunque no sea lo que esperas oír. Si tu problema se resuelve con una hoja de cálculo bien hecha, no te venderé un desarrollo. Explico mis decisiones y sus porqués — quiero que entiendas qué se está construyendo y por qué. Y si un proyecto solo compite en precio, probablemente no soy tu persona. Te lo diré también. ## ¿Cómo empezamos? Con una llamada rápida. Me cuentas tu caso — qué problema tienes, qué has intentado, dónde estás atascado. Yo te digo si puedo ayudarte y cómo lo enfocaría. Sin compromiso y sin discurso comercial. Si encajamos, definimos juntos los siguientes pasos. Reservar una llamada rápida Prefiero escribir --- ## Web Stories URL: https://ablancodev.com/proyectos/web-stories/ Scrum for kids SCRUM SCRUM Bienvenido --- ## Proyectos URL: https://ablancodev.com/proyectos/ /proyectos # Lo que construyo. Apps, automatizaciones, herramientas y experimentos. Algunos llegan a producción, otros acaban en el archivo. Todos enseñan algo. ■ Productos En producción con usuarios reales ### CheckWebPulse – Plataforma de monitorización de webs (SaaS) Monitor de disponibilidad web con alertas instantáneas. Detecta caídas antes que tus usuarios. PHP · MySQL · Cron · Emailcheckwebpulse.com →### Coffedoro CoffeDoro es un temporizador Pomodoro para macOS inspirado en el ambiente acogedor de las cafeterías de Málaga. Diseñado para ayudarte a mantener la concentración y mejorar tu productividad, ofrece sesiones de enfoque personalizables, seguimiento de hábitos y temas únicos basados en el estilo de café malagueño. Ligero, minimalista y siempre accesible desde la barra de menú, es perfecto para estudiar, trabajar, programar o escribir sin distracciones. iOS · Swiftapps.apple.com/us/app/coffeedoro/id6754896521 →Privacy →### GrowthPlugins – Suite de plugins para convertir WordPress en una alternativa a los principales SaaS del mercado Suite de plugins para que las agencias WordPress gestionen proyectos, soporte y clientes sin depender de SaaS externos. growthplugins.com →### Export Anywhere – Plugin de Figma para automatización de flujos creativos Plugin de Figma para automatizar la exportación de recursos gráficos hacia sistemas externos y eliminar trabajo manual en producción creativa. API · Figmawww.figma.com/es-es/comunidad/plugin/1584148470519812614/export-anywhere →### QA Automation Flow – Chrome Extension Extensión Chrome para crear flujos de automatización web sin código. Ideal para testers y equipos que quieren documentar y repetir pruebas. Chrome Extension · JavaScriptVer proyecto → ◇ Open Source Herramientas y repos que merece la pena compartir ### Generador custom post type Genera tus propios custom post types (CPT) simplemente indicando los datos, y copiando el código php que te vamos a dar, en el fichero functions.php de tu tema hijo. PHP · WordPressVer proyecto →### Generador Taxonomías Genera tu taxonomía para WordPress. PHP · WordPressVer proyecto →### Semantic Search for WordPress Sustituí la búsqueda por palabras clave con embeddings vectoriales en WordPress. Writeup completo + código. Python · embeddings · WordPress · PostgreSQLVer build log →### DOKR Dashboard OKR construido con WordPress. Sigue objetivos y resultados clave sin la burocracia enterprise. WordPress · PHP · JSVer build log → □ Archivo Experimentos, POCs e ideas descontinuadas Cosas que intenté, construí y acabé parando. No son fracasos — son datos. Cada una tiene su postmortem: qué funcionó, qué falló, qué aprendí. ### SecondBrainBot Tu segundo cerebro en Telegram. Captura ideas, artículos y notas — las recuperas cuando las necesitas. Node.js · Telegram API · GPT-4o · PostgreSQLsecondbrainbot.com → --- ## Contacto URL: https://ablancodev.com/contacto/ # Hablemos. Si tienes un proyecto en mente, quieres saber si puedo ayudarte, o simplemente tienes una pregunta — escríbeme o reserva una llamada rápida. ### Escríbeme ### O reserva una llamada 15 minutos para ver si tiene sentido trabajar juntos. Sin compromiso. Reservar llamada gratuita ### Directo antonio@blancoleon.com LinkedIn GitHub --- ## WordPress con IA — Plugins, MVPs y automatizaciones URL: https://ablancodev.com/servicios/wordpress-con-ia/ Más de 15 años construyendo sobre WordPress. Plugins en el repositorio oficial, productos comerciales y ahora IA integrada encima de todo eso. Cuéntame qué necesitas ## WordPress sigue siendo la mitad de la web. Lo que cambia es lo que puedes hacer encima. WordPress no está en declive — está evolucionando. Y la diferencia entre un sitio WordPress estándar y uno que trabaja con IA es exactamente el tipo de trabajo que hago: plugins a medida, lógica de negocio compleja, WooCommerce avanzado e integraciones con modelos de lenguaje que hacen que tu instalación WordPress haga cosas que antes requerían un equipo entero. ## ¿En qué casos tiene sentido trabajar conmigo? ### Plugin a medida Necesitas una funcionalidad que no existe en ningún plugin del mercado, o los que hay no se adaptan a cómo funciona tu negocio. Lo construyo desde cero, limpio y mantenible. ### WordPress como backend de MVP WordPress es un backend maduro, con sistema de usuarios, roles, API REST y miles de integraciones. Úsalo como motor de tu producto en lugar de construir todo desde cero — es una ventaja real en tiempo de lanzamiento. ### WooCommerce avanzado Procesos de compra complejos, pasarelas de pago personalizadas, lógica de precios dinámica, integraciones con ERPs o marketplaces. Cuando WooCommerce out-of-the-box no llega. ### IA integrada en WordPress Desde asistentes que consultan tu catálogo de productos en lenguaje natural, hasta agentes que gestionan contenido o responden soporte usando tus propios datos como contexto. ### Soporte técnico para agencias Tu agencia tiene proyectos WordPress que necesitan un developer senior para resolver lo difícil. Entro como refuerzo técnico — sin fricción, con criterio. ### Auditoría y rescate técnico Heredaste un WordPress lento, mal construido o sin documentación. Lo analizo, te digo exactamente qué hay y qué haría — y si quieres, lo arreglo. ## Por qué tiene sentido que venga de alguien que también construye productos No solo desarrollo para clientes. Tengo plugins publicados en el repositorio oficial de WordPress.org, plugins comerciales vendidos internacionalmente y un ecosistema propio de herramientas (GrowthPlugins) construido sobre WordPress. Eso significa que cuando tomo una decisión técnica en tu proyecto, la tomo con la misma mentalidad que usaría si fuera mío: escalabilidad, coste de mantenimiento y qué pasa cuando algo falla a las 3 de la mañana. ## ¿Tienes un proyecto WordPress? Cuéntame qué necesitas y te digo cuál es la mejor solución técnica — aunque eso signifique no usar WordPress. ---