JMP 19 ampliará la función Conector de Datos introducida en JMP 18 para permitir un acceso uniforme a más fuentes de datos. Además de añadir más tipos de conexión integrados, JMP 19 también permitirá a los usuarios definir sus propios tipos de conexión mediante una API de Python expuesta. Al igual que los tipos integrados, estos tipos compartirán las ventajas de la función Conector de Datos, incluyendo la gestión de la configuración y la integración con el Generador de Consultas. Además, al estar implementados en Python, pueden utilizar las bibliotecas de Python existentes. En esta presentación, describimos la API, analizamos los conceptos que la componen y la mostramos en acción.
Hola. Hoy les presento el marco de trabajo Data Connector de JMP, específicamente cómo en JMP 19 se podrá ampliar con Python. En JMP 18, presentamos el marco de trabajo Data Connector y lo usamos para ofrecer una conectividad mejorada con ODBC. En JMP 19, lo ampliamos.
Quizás escuchen en otros lugares cómo lo usamos para reconectarnos a SaaS y cómo proporcionamos conectividad nativa a Snowflake. También usamos este mismo framework del que hablaré, esta API de Python, para implementar la conectividad con Amazon S3 y Microsoft SharePoint, que no se incluirá en JMP, sino que se distribuirá a través de JMP Marketplace.
Hay algunos temas que quiero abordar en esta charla. Primero, quiero explicar brevemente por qué, si tienes tu propia fuente de datos, podrías integrarla con JMP. Si quieres integrarla, querrás saber dónde encaja, qué necesita JMP para comunicarse con tu fuente de datos y qué necesitas proporcionar para que esto suceda. En particular, qué código necesitas escribir.
Finalmente, hablaremos sobre cómo compartirlo. El código que has escrito funciona para ti, pero es posible que otros en tu empresa quieran usarlo. Quizás quieras compartirlo con otros usuarios de JMP de todo el mundo. Para facilitarlo, elegimos Python, en parte por las bibliotecas que ya están disponibles o por el código que podrías haber escrito. Así es como realizamos las conexiones para Amazon S3 y Microsoft SharePoint. Podemos integrar las bibliotecas de Python existentes y esperamos que, como resultado, también te resulte más fácil.
Otra razón por la que podría interesar la integración, además de la simplicidad, es la posibilidad de gestionar las configuraciones. Un usuario puede crear y editar las configuraciones que describen las conexiones mediante el Editor de Conectores y el Generador de Consultas, accesibles de esta manera, lo que permite crear consultas gráficas, filtrar datos, seleccionar columnas y mucho más. Finalmente, se puede colaborar. Las conexiones y configuraciones que se crean se pueden exportar y compartir mediante complementos.
Si no has visto esta funcionalidad antes, pronto la verás con más detalle cuando la muestre en acción. Pero antes, veamos cómo encaja todo y cómo se integra. Para empezar, tienes tu fuente de datos y quieres que se comunique con JMP.
Pero cuando hablamos de datos en JMP, nos referimos a tablas de datos. Estas tablas son algo que quizás sea muy sencillo de proporcionar desde la fuente de datos. Quizás sea algo que requiera algún trabajo para que suceda. Por ejemplo, quizás tenga un punto final web que devuelva estos datos JSON estructurados jerárquicamente. En ese caso, será necesario realizar algún procesamiento. Pero siempre que se puedan exponer como una tabla de datos para JMP, potencialmente se podrán usar.
El Generador de Consultas, al acceder a esos datos, los procesará como si se tratara de una base de datos tradicional, como MySQL o SQL Server. Espera que los datos estén organizados en tablas con nombre, y que estas tablas se organicen a su vez en esquemas con nombre.
Pero probablemente tengas más de una fuente de datos. Si no, hay al menos varias maneras de acceder a ella. Es posible que cada usuario tenga credenciales diferentes. O quizás simplemente no quieras codificar la dirección de tus datos. De alguna manera, probablemente tendrás opciones. Al proporcionar valores específicos para esas opciones, obtienes una configuración. Con esa configuración, puedes crear una fuente de datos. Estas configuraciones se crean con el editor del Conector de Datos.
Esa es la imagen en resumen. Pero ¿cómo se convierte en código? Veamos. Estoy aquí en JMP. Quitaremos esa ventana y comencemos de cero. Tenemos código aquí a la izquierda que implementa un par de tipos de conectores de datos. Un tipo es algo como ODBC, integrado en JMP. Podría ser uno de los tipos SaaS, que puede ser local, remoto o mediante conectividad. Podría ser Snowflake. En cualquier caso, Python también es una opción para cualquier tipo que implementes.
En la parte superior, tenemos algunas importaciones. En particular, tenemos una importación del módulo JMP integrado. Tenemos esta función de utilidad que nos permite ver cuándo se llama a una función. Se usa en esta demostración porque JMP es el que llama al código. No vamos a llamarlo tanto. Es importante que, para comprender qué sucede, podamos controlarlo.
El código a continuación implementa el tipo. Antes de profundizar en esto, revisemos esta referencia. Estos elementos de la lista se corresponden, en muchos sentidos, con el diagrama que acabo de mostrar. Necesitamos opciones. Para ello, subclasificaremos la clase jmp.DataConnectorType y definiremos un atributo de campo que defina dichas opciones. Tenemos la configuración que necesitamos convertir en nuestra fuente de datos. Subclasificaremos jmp.DataConnector y definiremos una función, _do_as_data_source, que realiza dicha conversión.
Finalmente, tenemos la fuente de datos. Esta es otra subclase. Definiremos funciones que nos permitirán saber cuáles son los esquemas (si los tenemos), cuáles son nuestras tablas y cuáles son los datos de una tabla en particular. Aunque no lo veremos en nuestra demostración, es útil tener en cuenta que si tiene recursos que no se limpiarán automáticamente (por ejemplo, si tiene una conexión que aún no tiene este comportamiento), deberá encontrar el método especial de doble subrayado de Python que puede limpiarlos cuando ya no se hagan referencias a ellos, ya que dejar que Python lo limpie es la forma en que JMP gestionará el cierre de la fuente de datos.
Con esto en mente, revisemos el código nuevamente. Vemos SimpleFolderConnectorType, que es una subclase de jmp.DataConnectorType. Como se mencionó, tiene campos con opciones. La primera y única opción es "Folder".
En este caso, el significado de "carpeta" (de hecho, la conexión a SimpleFolder) es que tenemos nuestra fuente de datos. Esto corresponderá a una sola carpeta en mi sistema de archivos local, y esa carpeta mostrará los datos en forma de los archivos que contiene.
Para empezar, tendremos "Carpeta", y su valor en el diccionario que describe nuestras opciones es de tipo str | None. Esto significa que tendremos este valor de cadena opcional. Luego tenemos nuestra clase de configuración, SimpleFolderConnector, que subclasifica jmp.DataConnector. Definirá la función _do_as_data_source que convertirá, o mejor dicho, conectará, desde nuestra configuración.
Podemos acceder a nuestro valor de configuración, el de "Carpeta", simplemente indexándolo en self, como lo haríamos con un diccionario integrado. Comprobaremos si es none, si el usuario omitió este valor predeterminado y le informaremos de que es necesario mediante un error.
Finalmente, lo convertiremos a la fuente de datos. La clase de la fuente de datos es la última de nuestra lista. Por supuesto, debemos poder crearla. Definimos una función init. En este caso, comprobamos si se trata de un directorio, ya que queremos avisar al usuario (tú mismo o quien use el código) si hay un error. Al crear una fuente de datos, debería ser al conectar. En este caso, no es necesario realizar ninguna conexión, pero al menos podemos comprobar que la carpeta existe.
El siguiente paso en la lista fue get-schemas. En este caso, la forma en que nos referíamos a la fuente de datos es que no hay esquemas. Solo tenemos acceso directo a las tablas. No tenemos que definir get-schemas, y nuestro JMP sabrá que nuestra fuente de datos no usa esquemas.
Pero sí tenemos que definir qué son las tablas. En este caso, aunque debemos tomar un parámetro de esquema, lo ignoraremos porque no lo estamos usando. Pero, como estamos viendo los archivos, usaremos algún nombre para ellos. En este caso, solo el nombre. Iteraremos sobre la carpeta y cada archivo que contenga devolverá su nombre.
Finalmente, necesitamos obtener las tablas de datos. Para eso sirve la última función de abrir tabla. Primero, debemos reconstruir la ruta del archivo que contiene los datos. Tomaremos el nombre de la tabla, que es simplemente el nombre del archivo y el anexo a la carpeta, y luego devolveremos la ruta como una cadena.
El modelo del que hablaba requiere devolver una tabla de datos, y puedes hacerlo. Si tienes, como en el ejemplo anterior, un punto final web, devuelve este JSON jerárquico o algo similar. Probablemente quieras convertirlo en una tabla de datos o devolverlo directamente.
Pero en este caso, dado que se trata solo del sistema de archivos (un caso que vimos al crear los complementos de S3 y SharePoint, y algo que creo que hemos escuchado con interés en cuanto al acceso a otros almacenes de datos similares a archivos), es posible abrir el archivo no directamente, sino simplemente pasando esta cadena. Entonces, JMP lo abrirá automáticamente.
Se encargará de aspectos como asegurar que el archivo se abra de forma privada. La tabla de datos que se devuelve aquí es un detalle de implementación. Es algo que no debería exponerse directamente al usuario. Al usar el Constructor de Consultas, no se muestran las tablas intermedias, solo el resultado final. En este caso, JMP se encargará de ello. De esta manera, JMP también conserva cualquier configuración que pueda estar involucrada al abrir la tabla. Hablaré de esto más adelante.
Finalmente, tenemos esta invocación, la función jmp.DataConnector.tie. Esta función toma sus dos argumentos, la clase de tipo y la clase de conector, y se asegura de que se conozcan mutuamente. Actualmente, ninguna hace referencia a la otra, pero la clase DataConnectorType, aquí en la parte superior, es lo que JMP usa como punto de acceso. El tipo es este elemento integral que captura todo el código, todas las definiciones que conforman su conectividad. Necesitan conocerse mutuamente.
Finalmente, tengo un comentario que indica cuál es la cadena de tipo final. Si es DBC o simplemente ODBC, podría ser SAS Viya o SAS Remote. En este caso, para Python, usamos dos puntos para indicar que es un tipo de función. Tenemos __main__, que es el nombre del módulo en este caso, ya que lo ejecutamos directamente desde JMP. Finalmente, tenemos SimpleFolderConnectorType, el nombre de esta clase.
Veamos esto en acción. Si voy al cuadro de diálogo Conector de Datos y creo una nueva conexión, abro el Editor de Conectores de Datos. Si cambiamos el tipo a Python, podemos escribir el resto de la cadena aquí. Lo completamos.
Una vez hecho esto, podemos ver nuestra opción "Carpeta". No tiene ningún valor, por lo que corresponderá a "Ninguno". Si intentamos conectarla, llamamos a esta función y a esta otra, y veremos el mensaje de error: "Se requiere carpeta".
Ahora, para poder acceder a los datos, usaré los datos de importación de ejemplo que vienen con JMP. Esta es la carpeta. La copiaré. La mostraré de nuevo. Copiaremos la ruta, revisaremos JMP y la colocaremos aquí. Vemos lo que probamos. Ahora todo está bien, así que vamos a conectarnos.
Ahora que nos hemos conectado, pueden ver una lista de tablas aquí. De hecho, estas tablas corresponden a los archivos que tenemos aquí. Vemos Animals_L.txt aquí, y lo volvemos a ver aquí. Si revisamos el registro, vemos que usar esta función de entrada de registro ha sido muy útil. Vemos que hemos llamado a _do_as_data_source varias veces, y que hemos llamado al init de la fuente de datos varias veces mientras la probábamos. También vemos que se llamó a get-tables, lo que nos proporciona nuevamente esta lista.
Si llamo al Generador de Consultas, podemos ver que tenemos acceso a las columnas. Si creamos la consulta, podemos agregarlas. Podemos aplicar algún tipo de filtro. Por ejemplo, podríamos seleccionar solo dos de estas temporadas. Si revisamos el SQL, veremos que captura todo eso. Ejecutamos la consulta y obtenemos los resultados.
Quizás te preguntes cómo funciona todo esto. Creamos una consulta SQL. Ejecutamos código SQL. Sin embargo, al definir las cosas, apenas existía la noción de SQL. La razón por la que funciona es porque proporcionamos esta llamada a tabla abierta; proporcionamos esta forma de obtener los datos. Luego, JMP hace el resto. Utiliza SQLite, una especie de base de datos integrada, para exponer los datos que tenemos y poder ejecutar código SQL. Por ejemplo, la cláusula where que nos permitió ver solo dos de las estaciones.
Veamos el archivo más complejo. Quizás volvamos a la clase principal, nuestra favorita, que está en Excel. Vayamos de nuevo al Generador de consultas. Vemos que antes, para hacer cualquier cosa, aparecía este cuadro de diálogo. Decidimos que si tienes archivos, [inaudible 00:17:43] veces, necesitas que se te solicite información para especificar qué contiene. Cuando JMP intenta abrir este archivo, te pide que completes, en este caso, la información que necesitas con el Asistente de importación.
Todo parece correcto, así que haz clic en Importar. Ahora puedes ver, como antes, los nombres de las columnas y la instantánea. Voy a hacer clic en Importar. Ya tenemos nuestros datos. Quizás te preguntes: ¿tengo que pasar por esto cada vez? La respuesta es no.
Veamos este script fuente. En la parte superior, podemos ver lo que esperamos. Tenemos una nueva consulta SQL. Tenemos nuestra información de conexión, que en este caso la proporciona el conector de datos. Pueden ver que hace referencia a nuestro tipo y que también contiene la carpeta que especificamos.
Al examinar la tabla, vemos que hay una nueva opción, "Abrir configuración", que captura lo especificado en el Asistente de importación. En particular, toma los argumentos adicionales que se pasaron a "abrir" en los scripts de origen de la tabla de clase grande subyacente y los proporciona aquí.
De esta manera, si vuelvo a ejecutarlo, verá que todo se ha abierto y todo se ve bien. Si hubiéramos hecho algo inusual con la configuración, como empezar en una columna posterior, eso se reflejaría aquí. Vemos que todo se ha reiniciado desde el registro. Vemos que volvimos a llamar a _do_as_data_source. Obtuvimos la lista de tablas y volvimos a abrir esta tabla.
Eso es lo básico. Esto parece un poco más complicado. Pero no solo lo es, sino que también ofrece más funciones. Esto era, de nuevo, lo mínimo que se podía hacer para tener conexión.
Ahora se ve un poco más sofisticado. En lugar de un simple Conector de Carpetas, tendremos un Conector de Carpetas completo. Verás, la definición de campos es mucho más compleja, pero se basa en componentes similares a los anteriores.
Lo primero que se añade en este caso es el uso de jmp.DataConnectoGroupedFields. Esto nos permite especificar que los campos estén en grupos diferentes. Esto afecta su visualización en el Editor de Conectores de Datos. Para ODBC, las opciones se agrupan en grupos diferentes, y podemos usar la misma funcionalidad con Python.
Cada grupo especifica un par en esta lista que se pasa a jmp.DataConnectorGroupedFields. Tenemos el nombre y un diccionario similar al anterior, donde se encuentra el nombre de la opción y la definición del campo. En la definición del campo, aún se puede ver el tipo str | None. Pero también lo hemos encapsulado en jmp.DataConnectorField, al que le puse un alias porque lo usamos varias veces y es un poco largo, para especificar información adicional, como, por ejemplo, una descripción emergente que aparecerá en el Editor de Conectores de Datos al pasar el cursor sobre el nombre de una opción.
Se especifica el valor predeterminado. También se puede especificar un nombre que aparecerá en el Editor del Conector de Datos, diferente del nombre que se usará en los archivos de configuración de JSL. Usamos esta funcionalidad en JMP, por ejemplo, con ODBC, para localizar los nombres de las opciones o para proporcionar cadenas ligeramente mejores que las que se usarían de otra manera, ya que, por defecto, estas son las que se usan en JSL. Por lo tanto, necesitamos un nombre de estilo JSL.
Creo que "Ordner" significa carpeta en alemán. Lo veremos allí. En cuanto a los cambios en la visualización de nuestra fuente de datos, esta sigue basándose en la carpeta, pero también admitiremos esquemas opcionalmente. En este caso, tendremos la opción de ver los datos de una manera nueva: las carpetas secundarias de la carpeta proporcionada aquí se consideran esquemas, y sus archivos se consideran tablas de datos.
Tendremos otra opción que mezclará la forma en que vemos la asignación de los nombres de las tablas a los archivos subyacentes al limitarlos a cierta extensión y también al recortarlos.
Nuestra conversión a una fuente de datos al conectarnos es similar, pero un poco más compleja porque tenemos más opciones. Seguimos marcando "Carpeta", pero también marcaremos esta opción de extensión. Generaremos un error y luego pasaremos todo a nuestra nueva fuente de datos.
Nuestra nueva fuente de datos revisará las carpetas previamente y configurará las demás opciones. Proporcionará esquemas. Si usamos esquemas, tendremos un código similar, donde revisaremos las carpetas dentro de la carpeta. Cualquier directorio secundario obtendrá su nombre y lo usaremos como nombre del esquema.
Sin embargo, si no usamos esquemas, si no configuramos nuestra nueva opción, simplemente devolveremos "Ninguno". Antes, no definíamos esquemas (get-schemas), pero aquí no podemos definir una función opcionalmente, así que, en su lugar, podemos devolver "Ninguno" para indicar "No tengo ninguno". No usamos esquemas.
Obtener tablas, una idea similar a la anterior. En definitiva, obtenemos el nombre de varios archivos. Pero, debido a nuestras opciones, lo hacemos de varias maneras. Obtenemos el directorio. Dependerá del esquema. De hecho, dependerá de si usamos esquemas. Luego, filtraremos los archivos antes de obtener sus nombres, o haremos algo más sofisticado si procesamos las extensiones.
Finalmente, para obtener la tabla de datos, invertimos el proceso de obtener esquemas y obtener tablas. Debemos reconstruir la ruta. Podríamos añadir la extensión. Sin embargo, no se obtendrá el directorio involucrado, así que volveremos a poner el nombre del archivo al final antes de devolverlo como ruta de nuevo... Devolviendo la ruta como cadena de nuevo. Unimos los datos y obtenemos el nombre del tipo como antes.
Echemos un vistazo. Retrocedamos un poco para poder referenciarlo. Como antes, configuremos el tipo aquí. Vemos que tenemos nuestro nombre personalizado junto con la información sobre herramientas. Usaremos lo mismo que antes para la carpeta. Obtengamos la ruta. De hecho, vayamos un paso más arriba. Usemos nuestra funcionalidad de esquema. La copiaremos.
Entonces podemos decir que queremos usar carpetas secundarias como esquemas. Ya que incluimos esta opción sofisticada, la usaremos. Asegurémonos de que todo se vea bien. Sí. Podemos conectar. Asegurémonos de que todas las ventanas estén en primer plano.
Ahora vemos a la izquierda los esquemas, que en realidad son solo los nombres de las carpetas. Aquí están nuestros datos de importación. Vemos que los hemos limitado a los archivos .txt. Tomemos nuestro ejemplo de animales como antes, ábralo. Tenemos nuestros datos. Todo funciona. Tenemos las llamadas aquí como antes. Si revisamos el registro, tenemos llamadas a get-schemas, get-tables a nuestra función de utilidad y a open table.
Has hecho todo este trabajo para crear estos tipos. Ahora quieres compartirlo con los demás. Eso es lo último que queremos tratar: cómo hacerlo. Tengo otra lista de verificación.
Tienes el código Python que has escrito, pero dependiendo de cómo lo hayas escrito, podrías querer estructurarlo un poco en un paquete. El objetivo de estructurarlo en un paquete es asegurar que esté todo integrado y que pueda instalarse con una herramienta como pip, o en este caso, jpip, encapsulado en JMP, para que puedas especificar dependencias al hacerlo. Por ejemplo, con S3, dependemos del paquete Boto3, que está en PyPI, y usar un paquete proporciona una forma muy natural de especificar esa dependencia.
Querrás crear configuraciones base. Recuerda que antes ingresaba el nombre del tipo directamente en el Editor del Conector de Datos. Eso está muy bien, pero sería más fácil si pudiera encontrar algo en la lista de configuraciones. Podría crear algo nuevo a partir de eso en lugar de tener que recordar el nombre completo.
Probablemente quieras escribir un script de instalación. Tenemos este maravilloso paquete de Python, pero necesitamos colocarlo en un lugar donde realmente se pueda usar. Si pudiéramos hacerlo automáticamente, sería genial.
Finalmente, queremos integrarlo todo en un complemento. Con un complemento, tenemos algo fácil de instalar y que se puede distribuir a través del Marketplace. En cualquier caso, todo está en un solo archivo de paquete.
Veamos cómo se ve. Tengo un complemento que contiene una versión más desarrollada del código que vimos hoy. Lo he descomprimido aquí para que podamos ver su contenido. Creo que lo primero en la lista era el paquete de Python. Lo he creado aquí. Tenemos nuestro código Python, pero está estructurado en un proyecto de Python... Perdón, en un paquete de Python. Tenemos nuestro archivo pyproject.toml. Hoy no tengo tiempo para explicar cómo se construye uno, pero hay recursos en línea disponibles para ello.
Tenemos una carpeta "Conectores de Datos". Aquí se proporcionan las configuraciones que acompañan al complemento; en este caso, las configuraciones básicas. Si observo una de ellas, por ejemplo, veremos que solo tiene una línea donde especifica el tipo. No especifica ninguna configuración adicional, pero proporciona algo que el usuario puede señalar. Luego tenemos, en este caso, addinLoad, que contendrá el script de instalación.
Claro que puedes intentar crear todo esto directamente, pero también puedes usar el creador de complementos de JMP. Si vamos a Archivo, Nuevo, Complemento, puedo proporcionar un nombre. Puedo proporcionar un ID. Puedo especificar la versión mínima de JMP, que en este caso debería ser la 19. En el script de inicio, colocaremos el script que instalará el paquete. Queremos asegurarnos de instalarlo solo una vez.
Tenemos los archivos que queremos agregar. En este caso, ya están descomprimidos aquí, pero es posible que los tengas en otro lugar si lo creas tú mismo por primera vez. Podemos agregar el paquete. Mira, tenemos todo ahí. Podemos agregar los conectores de datos. Si tenemos el subíndice, podremos guardar y crear nuestro propio complemento. Una vez que tengas todo ahí, el proceso de creación será bastante sencillo.
Eso es todo lo que quería mostrar. Resumamos. Primero, quiero explicar por qué conviene integrar. Espero que, como vimos al principio, el marco de trabajo del Conector de Datos ofrezca muchas ventajas, como la posibilidad de editar configuraciones, compartirlas y usar el Generador de Consultas para construir consultas interactivamente. Esto es una buena razón para encapsular la fuente de datos. Aquí es donde encaja. Hablamos de que, en JMP, todo es una tabla de datos y queremos que parezca una base de datos.
¿Qué código escribir? Revisamos las distintas subclases que se deben definir, las distintas funciones que se deben escribir y cómo especificar las opciones. Finalmente, hablamos sobre cómo crear un complemento y compartir lo que se ha escrito con otros o en JMP Marketplace.
Este es, en cierto modo, un resumen relativamente rápido de todo lo que es posible. Algunos de ustedes quizá estén tomando muy buenas notas y muchas capturas de pantalla o fotos, pero si son como yo, probablemente quieran tener algo más que consultar. La buena noticia es que tenemos la información y estamos creando más.
La API que mostré hoy ya está documentada. Se encuentra en las Notas de EA, en la sección "Acceso a Datos". Es importante asegurarse de obtener la última versión, que, dependiendo del caso, podría no ser la versión actual de EA si no se han realizado cambios. Estamos añadiendo documentación que estará en el índice de scripts. No ofrecerá la misma descripción general que, por ejemplo, se encuentra en las Notas de EA, pero esperamos que sirva como referencia para las funciones individuales de la API.
Finalmente, estamos desarrollando un poco más el complemento que mostré antes y lo publicaremos en JMP Marketplace. Queremos que sirva como recurso para mostrar la API y también para mostrar cómo crear un paquete de Python y luego publicarlo en un complemento.
Con toda esa documentación, les mostraremos de nuevo todo lo que han visto hoy y un par de funciones más que no tuve tiempo de comentar, pero que son un poco más avanzadas, pero que podrían ser útiles. No se pierdan estas funciones.
Finalmente, quiero mencionar lo que podríamos hacer en el futuro. En JMP 20 y versiones posteriores, estamos considerando extensiones adicionales para esta API. Por ejemplo, habrán notado que, para procesar sus datos, JMP los importará localmente, incluyendo toda la tabla de datos, ya que eso es lo que se debe devolver desde OpenTable. Luego, la usaremos localmente, como mencioné, a través de SQLite.
Pero si se puede realizar parte de ese procesamiento en el servidor, sin tener que descargar todo localmente, queremos ofrecer soporte. Estamos estudiando cómo podemos ofrecer también ese tipo de funcionalidad.
Con esto, creo que hemos terminado. Gracias por su atención. Espero que puedan aprovechar lo aprendido hoy y crear nuevas fuentes de datos integradas en este marco de Data Connector. Podrán usarlas y publicarlas en un complemento. En resumen, les resultará más fácil trabajar con sus datos e incorporarlos a JMP. Gracias de nuevo. Eso es todo.