{"id":11229,"date":"2025-11-27T17:28:04","date_gmt":"2025-11-27T16:28:04","guid":{"rendered":"https:\/\/bitacora.eniac2000.com\/?p=11229"},"modified":"2025-11-27T17:28:04","modified_gmt":"2025-11-27T16:28:04","slug":"creacion-de-articulos-deportivos-con-ia-generativa-arquitectura-del-sistema","status":"publish","type":"post","link":"https:\/\/bitacora.eniac2000.com\/?p=11229","title":{"rendered":"Creaci\u00f3n de art\u00edculos deportivos con IA generativa. Arquitectura del sistema"},"content":{"rendered":"<div class=\"seriesmeta\">Esta entrada es la parte 3 de 5 de la serie <a href=\"https:\/\/bitacora.eniac2000.com\/?series=creacion-de-articulos-deportivos-con-ia-generativa\" class=\"series-1854\" title=\"Creaci\u00f3n de art\u00edculos deportivos con IA generativa\">Creaci\u00f3n de art\u00edculos deportivos con IA generativa<\/a><\/div>\n<p>La aplicaci\u00f3n sigue una arquitectura moderna y escalable, aprovechando las capacidades de <a href=\"https:\/\/supabase.com\/\" data-type=\"link\" data-id=\"https:\/\/supabase.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Supabase<\/a> para el backend y las funciones Edge para las integraciones con APIs externas y la IA.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. Frontend<a href=\"https:\/\/github.com\/i82hisaj\/wordpress-strava-piwigo-youtube#1-frontend\"><\/a><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Tecnolog\u00eda:<\/strong> React, TypeScript, Vite.<\/li>\n\n\n\n<li><strong>Estilo:<\/strong> Tailwind CSS con componentes Shadcn\/ui para una interfaz de usuario moderna y responsiva.<\/li>\n\n\n\n<li><strong>Navegaci\u00f3n:<\/strong> React Router para la gesti\u00f3n de rutas.<\/li>\n\n\n\n<li><strong>Gesti\u00f3n de Estado:<\/strong> <code>react-query<\/code> para la gesti\u00f3n de datos as\u00edncronos y <code>useSession<\/code> para el contexto de autenticaci\u00f3n.<\/li>\n\n\n\n<li><strong>Notificaciones:<\/strong> <code>sonner<\/code> para mensajes de \u00e9xito\/error\/carga.<\/li>\n<\/ul>\n\n\n\n<p>La aplicaci\u00f3n se despliega en mi entorno de virtualizaci\u00f3n, espec\u00edficamente en un servidor de <em>backend<\/em> de prop\u00f3sito general. La aplicaci\u00f3n que se presenta permite, en un primer lugar, gestionar las integraciones con las distintas fuentes de datos (Strava, Piwigo, YouTube), el <em>blog<\/em> de destino (WordPress).<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"643\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-01-1024x643.png\" alt=\"\" class=\"wp-image-11230\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-01-1024x643.png 1024w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-01-300x188.png 300w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-01-768x482.png 768w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-01.png 1425w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Interfaz inicial de acceso. Una vez realizadas las autenticaciones, permite iniciar el proceso de correlaci\u00f3n<\/figcaption><\/figure>\n\n\n\n<p>Las credenciales de cada uno de los or\u00edgenes o destinos de datos son gestionadas a trav\u00e9s de una secci\u00f3n de administraci\u00f3n. En el caso de Strava, es necesario gestionar un <em>token<\/em> que requiere de renovaci\u00f3n peri\u00f3dica; en el caso de Piwigo, mediante usuario\/contrase\u00f1a, y en el de YouTube, a trav\u00e9s de una API del canal. Con respecto a WordPress, se realiza mediante usuario\/contrase\u00f1a, con respecto a la API espec\u00edfica que permite gestionar la creaci\u00f3n de art\u00edculos.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"462\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-02-1024x462.png\" alt=\"\" class=\"wp-image-11231\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-02-1024x462.png 1024w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-02-300x135.png 300w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-02-768x346.png 768w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-02.png 1421w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Interfaz de administraci\u00f3n de credenciales. Se muestra la correspondiente a Strava<\/figcaption><\/figure>\n\n\n\n<p>Para cada una de las fuentes de datos se ha habilitado un cacheado de contenido (recorridos, galer\u00edas, art\u00edculos), a fin de evitar excesivas consultas a las fuentes que puedan resultar en un bloqueo de las consultas a la API (algo que experiment\u00e9 en el caso de YouTube durante las primeras fases del desarrollo). Esto permite reducir al m\u00ednimo las llamadas a las APIs. Para cada una de las fuentes se puede consultar el estado de las cach\u00e9s, renovarlo de manera individual para cada uno de los registros, o bien de manera masiva para todo el contenido:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"596\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-03-1024x596.png\" alt=\"\" class=\"wp-image-11232\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-03-1024x596.png 1024w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-03-300x175.png 300w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-03-768x447.png 768w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-03.png 1417w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Cach\u00e9 de contenidos. En este caso, del canal de YouTube<\/figcaption><\/figure>\n\n\n\n<p>Una vez gestionadas las credenciales, es posible empezar el proceso de correlaci\u00f3n en la interfaz principal. En la misma se muestran un listado de las activididades registradas en Strava, clasificadas por a\u00f1o. Para cada una de ellas es posible buscar correlaciones con las galer\u00edas de Piwigo y el canal de YouTube existentes. Una vez encontradas estas correlaciones, se puede proceder a generar el art\u00edculo, escogiendo el motor de IA preferido (DeepSeek o Mistral AI), e incorporando directrices espec\u00edficas por parte del usuario:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"572\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correolacion-04-1024x572.png\" alt=\"\" class=\"wp-image-11233\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correolacion-04-1024x572.png 1024w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correolacion-04-300x168.png 300w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correolacion-04-768x429.png 768w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correolacion-04.png 1336w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Listado de actividades con medios vinculados<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"854\" height=\"540\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/correlacion-actividades.png\" alt=\"Actividades correlacionadas\" class=\"wp-image-11219\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/correlacion-actividades.png 854w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/correlacion-actividades-300x190.png 300w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/correlacion-actividades-768x486.png 768w\" sizes=\"auto, (max-width: 854px) 100vw, 854px\" \/><figcaption class=\"wp-element-caption\">Actividades correlacionadas<\/figcaption><\/figure>\n\n\n\n<p>Tras confirmar la correlaci\u00f3n de actividades con medios, si existen, y una vez introducidas las instrucciones adicionales para la IA, se puede generar el art\u00edculo.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"908\" height=\"779\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-05.png\" alt=\"\" class=\"wp-image-11234\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-05.png 908w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-05-300x257.png 300w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-05-768x659.png 768w\" sizes=\"auto, (max-width: 908px) 100vw, 908px\" \/><figcaption class=\"wp-element-caption\">Listado de art\u00edculos generados<\/figcaption><\/figure>\n\n\n\n<p>El sistema permite revisar los borradores de manera previa a su traslado al <em>blog<\/em> de WordPress, por si se quiere realizar alguna modificaci\u00f3n antes de su env\u00edo.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"896\" height=\"833\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-07.png\" alt=\"\" class=\"wp-image-11235\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-07.png 896w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-07-300x279.png 300w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/dashboard-correlacion-07-768x714.png 768w\" sizes=\"auto, (max-width: 896px) 100vw, 896px\" \/><figcaption class=\"wp-element-caption\">Previsualizaci\u00f3n del art\u00edculo generado<\/figcaption><\/figure>\n\n\n\n<p>Una vez enviado el art\u00edculo a WordPress, se almacena como borrador, para permitir al usuario controlar de manera completa el proceso de publicaci\u00f3n del contenido, realizar los \u00faltimos cambios, a\u00f1adir <em>tags<\/em>, categor\u00edas, etc&#8230;<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2. Backend (Supabase &amp; Edge Functions)<a href=\"https:\/\/github.com\/i82hisaj\/wordpress-strava-piwigo-youtube#2-backend-supabase--edge-functions\"><\/a><\/h3>\n\n\n\n<p>El coraz\u00f3n de la l\u00f3gica de integraci\u00f3n y generaci\u00f3n de contenido reside en las <strong>Supabase Edge Functions<\/strong>, que se ejecutan en un entorno Deno. Esto permite una l\u00f3gica de servidor sin necesidad de gestionar un servidor tradicional.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Base de Datos:<\/strong> Supabase PostgreSQL para almacenar:\n<ul class=\"wp-block-list\">\n<li>Perfiles de usuario (<code>public.profiles<\/code>).<\/li>\n\n\n\n<li>Credenciales de cuentas externas (Strava, Piwigo, YouTube, WordPress) de forma segura, vinculadas al <code>user_id<\/code> de Supabase Auth.<\/li>\n\n\n\n<li>Correlaciones entre actividades de Strava y medios (<code>activity_media_correlations<\/code>), incluyendo las instrucciones de la IA.<\/li>\n\n\n\n<li>Art\u00edculos de blog generados (<code>generated_posts<\/code>), con su estado y, si aplica, el <code>wordpress_post_id<\/code>.<\/li>\n\n\n\n<li>Cach\u00e9s de datos de APIs externas (actividades de Strava, categor\u00edas de Piwigo, videos de YouTube, <strong>estaciones y datos diarios de AEMET<\/strong>) para optimizar el rendimiento y reducir llamadas a APIs externas.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Autenticaci\u00f3n:<\/strong> Supabase Auth para la gesti\u00f3n de usuarios.<\/li>\n\n\n\n<li><strong>Row Level Security (RLS):<\/strong> Implementado en todas las tablas para asegurar que los usuarios solo puedan acceder a sus propios datos.<\/li>\n<\/ul>\n\n\n\n<p>A continuaci\u00f3n se muestra como ejemplo parte de la tabla que cachea la relaci\u00f3n de estaciones meteorol\u00f3gicas de la AEMET a nivel nacional. La idea es poder obtener la estaci\u00f3n meteorol\u00f3gica m\u00e1s cercana a la actividad para la que se genera el contenido.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"573\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/supabase-cache-aemet-01-1024x573.png\" alt=\"\" class=\"wp-image-11236\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/supabase-cache-aemet-01-1024x573.png 1024w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/supabase-cache-aemet-01-300x168.png 300w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/supabase-cache-aemet-01-768x430.png 768w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/supabase-cache-aemet-01.png 1359w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Cach\u00e9 de informaci\u00f3n de estaciones meteorol\u00f3gicas de la AEMET<\/figcaption><\/figure>\n\n\n\n<p>Una vez determinada la estaci\u00f3n m\u00e1s cercana, se accede a la informaci\u00f3n meteorol\u00f3gica del d\u00eda de la actividad. Lo ideal ser\u00eda tener un registro horario, pero la AEMET s\u00f3lo ofrece informaci\u00f3n con una granularidad a nivel de d\u00eda, y no de hora. Esta informaci\u00f3n se almacena como un JSON que posteriormente se proporciona a la IA generativa para enriquecer el art\u00edculo con informaci\u00f3n meteorol\u00f3gica general de la jornada.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"920\" height=\"174\" src=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/supabase-cache-aemet-02.png\" alt=\"\" class=\"wp-image-11237\" srcset=\"https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/supabase-cache-aemet-02.png 920w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/supabase-cache-aemet-02-300x57.png 300w, https:\/\/bitacora.eniac2000.com\/wp-content\/uploads\/2025\/11\/supabase-cache-aemet-02-768x145.png 768w\" sizes=\"auto, (max-width: 920px) 100vw, 920px\" \/><figcaption class=\"wp-element-caption\">Tabla de informaci\u00f3n meteorol\u00f3gica de la AEMET para la fecha de determinadas etapas<\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">3. Integraciones Externas<a href=\"https:\/\/github.com\/i82hisaj\/wordpress-strava-piwigo-youtube#3-integraciones-externas\"><\/a><\/h3>\n\n\n\n<p>La aplicaci\u00f3n se conecta con varias APIs externas a trav\u00e9s de las Edge Functions:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Strava API:<\/strong> Para obtener las actividades del usuario.<\/li>\n\n\n\n<li><strong>Piwigo API:<\/strong> Para acceder a \u00e1lbumes y fotos, incluyendo metadatos EXIF de geolocalizaci\u00f3n.<\/li>\n\n\n\n<li><strong>YouTube Data API:<\/strong> Para obtener detalles de videos de un canal espec\u00edfico.<\/li>\n\n\n\n<li><strong>WordPress REST API:<\/strong> Para publicar art\u00edculos de blog.<\/li>\n\n\n\n<li><strong>DeepSeek API (v\u00eda OpenRouter):<\/strong> Para la generaci\u00f3n de contenido de los art\u00edculos de blog.<\/li>\n\n\n\n<li><strong>Mistral AI API:<\/strong> Para la generaci\u00f3n de contenido de los art\u00edculos de blog. Se usa de manera alternativa a DeepSeek.<\/li>\n\n\n\n<li><strong>AEMET OpenData API:<\/strong> Para obtener informaci\u00f3n meteorol\u00f3gica hist\u00f3rica de las ubicaciones de las actividades.<\/li>\n\n\n\n<li><strong>Nominatim (OpenStreetMap):<\/strong> Para geocodificaci\u00f3n inversa de coordenadas a nombres de lugares.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<div class=\"seriesmeta\">Esta entrada es la parte 3 de 5 de la serie <a href=\"https:\/\/bitacora.eniac2000.com\/?series=creacion-de-articulos-deportivos-con-ia-generativa\" class=\"series-1854\" title=\"Creaci\u00f3n de art\u00edculos deportivos con IA generativa\">Creaci\u00f3n de art\u00edculos deportivos con IA generativa<\/a><\/div><p>La aplicaci\u00f3n sigue una arquitectura moderna y escalable, aprovechando las<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"advanced_seo_description":"","jetpack_seo_html_title":"","jetpack_seo_noindex":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[1845,24,8,13],"tags":[1861,1864,1857,1863,1862,1848,1858,1855,1859,1794,1809],"series":[1854],"class_list":["post-11229","post","type-post","status-publish","format-standard","hentry","category-generado-con-ia","category-ciclismo","category-deporte","category-informatica","tag-aemet","tag-api","tag-deepseek","tag-mistral-ai","tag-openstreetmap","tag-piwigo","tag-react","tag-strava","tag-supabase","tag-wordpress","tag-youtube","series-creacion-de-articulos-deportivos-con-ia-generativa"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/posts\/11229","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=11229"}],"version-history":[{"count":1,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/posts\/11229\/revisions"}],"predecessor-version":[{"id":11238,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=\/wp\/v2\/posts\/11229\/revisions\/11238"}],"wp:attachment":[{"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=11229"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=11229"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=11229"},{"taxonomy":"series","embeddable":true,"href":"https:\/\/bitacora.eniac2000.com\/index.php?rest_route=%2Fwp%2Fv2%2Fseries&post=11229"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}