 Hola, otra vez. Mi nombre es Julia, trabajo en el Quantitative Biology Center de la Universidad de Tübingen y voy a estar guiando también la sesión de training de hoy. Para repasar un poco lo que hicimos en la sesión 1, estuvimos viendo una introducción a Nextflow y usamos una packline como prueba de concepto con RNA-seq. En la sesión 2 estuvimos viendo que es NFCore y cómo nos puede ayudar si somos usuarios, si somos desarrolladores y también vimos algunos comandos para crear módulos y software flows o usar módulos y software flows. Durante la sesión de hoy, en la sesión 3, vamos a estar viendo cómo gestionar dependencias y cómo usar los containers. Luego revisaremos muchas de los conceptos que estuvimos usando durante la sesión 1 y 2, pero entraremos mucho más en detalle en canales, procesos y operadores. Luego terminaremos con una introducción a Groovy y hablando de modularización. Y mañana seguiríamos con la sesión 4, hablando de cómo configurar nuestras packlines, diferentes escenarios de implementación, de cached resume, cómo solucionar problemas y también una introducción a Nextflow Tower. Recordaros que en el caso de que tengáis dudas, tenéis estos canales disponibles en Slack para preguntar las dudas. Y empezamos. Hoy vamos a usar el... vamos a estar siguiendo el training de Nextflow. No les vamos a acceder a esta página, training.nextflow.io. Vale, pues aquí tenemos la página de training. Recordaros que aquí tenéis el botón para abrir una nueva sesión de Gitbot y si vamos aquí a estar Nextflow Training, vemos el training. Ya tengo el entorno aquí abierto para ahorrar un poco de tiempo. Ahora estamos empezando desde cero, es decir, con un entorno nuevo y limpio que no contiene ninguno de los ficheros o modificaciones que estuvimos haciendo durante la sesión 1. Entonces hay algunas cosas que tenemos que modificar ya que no las tenemos guardadas. ¿Cómo es? ¿Verdad? Ahora sí. Para usar la misma versión de Nextflow, para que todos tengamos la misma versión, exportamos la variable NextflowVersion y comprobamos, bueno, discargamos la nueva versión y comprobamos la versión que tenemos. Y luego también en NextflowConfig para no tener que especificar siempre que queremos correr a nuestros scripts con Docker, vamos a añadir aquí Docker, Enabled, True. Vale, entonces una vez hecho esto, que es lo que ya habíamos estado haciendo durante la sesión 1 también, vamos a ir a la sección de Managed dependencies and containers para ver, empezar a ver cómo gestionar dependencias. Entonces primer de todo porque es importante usar containers en una workflow, como ya habréis visto, podemos usar muchos scripts, muchos diferentes programas con librerías. Entonces instalar y mantener todas estas dependencias es muy costoso y además puede influenciar o evitar que nuestra pipeline sea reproducible, ya que diferentes sistemas van a tener diferentes programas, entonces instalar cada una de estas dependencias pues hace muy difícil la reproducibilidad de nuestra pipeline. Una solución es usar containers que ya contienen las dependencias necesarias. Entonces nosotros vamos a estar usando Docker ahora, vamos a comprobar que está instalando en nuestra en nuestro entorno y aquí veis pues que si que lo tenemos y que ya tenemos el gel, entonces las diferentes cosas que podemos hacer con Docker. Entonces para ejecutar containers de Docker podemos usar Nextflow, Docker Run y el nombre del container, por ejemplo este Hello World es un ejemplo de container que podemos ejecutar, voy a hacer eso un poco más grande para que se vea bien. Entonces hemos hecho Docker Run Hello World, tenemos aquí que no teníamos esta imagen Hello World con el tag Latest en local, entonces ha descargado del repositorio y aquí pues veis que ha ejecutado el container que en este caso pues nos devuelve este mensaje. Otra cosa que podemos hacer si seguimos con el tutorial es descargar una imagen de Linux y encima de es como una imagen básica que solo contiene Linux, en este caso estaríamos usando Debian que es la versión de una de las distribuciones de Linux. Entonces podemos descargar y usar una imagen base y encima de esta instalar los programas que queramos o las librerías que nosotras queramos. Entonces lo que vamos a hacer ahora es ver cómo podemos descargar esto encima, habéis visto que solo con hacer Run directamente como nos está encontrado en local pues la he ido a buscar en remoto, pero otra opción es descargarla con NextflowPool, entonces voy a copiar el nombre de esta imagen base de Linux y lo que lo que está haciendo es descargar en local, guardar en quien local esta imagen, entonces podemos usar Docker Images para ver las imágenes que tenemos disponibles en local y veis que tenemos RNSI que ya venía, viene con nuestro entorno de github, ya que estamos usando aquí en la tablet, luego hemos usado antes que local y acabamos de descargar esta imagen de Debian. Vale, luego lo que podemos hacer con estos containers es ejecutarlos en interactivo, que es el comando este que tenemos aquí, entonces usamos Docker, Run, Guion y T de Interactive, el nombre de la imagen y bash que es el comando que se va a ejecutar una vez hablamos, entonces veis que estamos ahora dentro del container, si hago LS veis que es como un sistema parte dentro del container no tenemos acceso a nada de nuestro sistema, no tenemos acceso a estos ficheles que estaban en nuestro sistema, sino que es como una máquina totalmente aislada, es un entorno nuevo y aislado. Entonces si queremos salir, escribimos exit y salimos del container. Ahora hemos estado usando imágenes o containers que ya estaban disponibles en repositorios, pero también podemos crear nuestro propio container. ¿Cómo podemos hacer esto? Necesitamos crear un fichero llamado Dockerfile, entonces voy a usar boat para crear el fichero y abrirlo aquí, aunque podíais crearlo manualmente y vamos a copiar este fragmento de aquí, del tutorial y vamos a ver qué es. Vale, entonces lo que está diciendo en este Dockerfile podéis encontrar más y mucha más información sobre Docker en si buscáis la documentación de Docker, pero básicamente lo que estamos haciendo es empezar de esta imagen básica de Linux, luego añadimos un par de labels de etiquetas y luego ejecutamos este comando que lo que está haciendo es usar actualizar apt y luego instalar este programa que se llama caosay y finalmente lo está exportando para que esté disponible en el path. Para generar un container a partir de este Dockerfile que hemos creado, usamos el comando DockerViews de para añadir un TAC, añadirle un nombre a nuestra imagen, por ejemplo myImage y un punto que significa pues que está en la carpeta donde estamos ahora. Entonces como veis, pues está primero descargando que ya estaba disponible, perdón. La imagen de tebia en luego está ejecutando las diferentes líneas, las instrucciones que hemos dado en este Dockerfile y finalmente tenemos nuestra imagen construida con el nombre myImageLatest. Si hablamos oferimages otra vez para ver la lista de imágenes disponibles, vemos que ya nos aparece myImage aquí que le acabamos de crear hace 10 segundos. Para usar esta imagen podemos otra vez usar Dockerfile, el nombre myImage y directamente podemos decir el comando que queremos que se ejecuta. Por ejemplo, y ahora veis que se ha ejecutado este comando. Diferente de cuando usábamos guión y T para ejecutar esto en interactivo, lo que hemos hecho es pasar un comando y esto se ha ejecutado dentro del container, pero nosotros no estamos dentro del container, si hago LS, veis que sigo viendo los scripts que veía antes y si probamos de ejecutar este comando aquí en nuestro entorno local, pues efectivamente este programa no está disponible. Lo que vamos a hacer ahora es instalar un software, un programa que tiene una aplicación real, además lo estuvimos viendo en nuestra página de RONIC que es Salmon, entonces voy a copiar esta línea que lo que haces es instalar este programa y voy a generar la imagen otra vez y le voy a poner el mismo nombre porque no nos importa sobre escribir, en este caso veis que las instrucciones que ya se han ejecutado anteriormente pues se está usando la cacha y no hace falta volver a ejecutarlas y pues la última instrucción sí y tenemos aquí nuestra imagen, entonces podemos vamos a ejecutar para ver la versión de Salmon y veis que tenemos la versión 1.5.2 que es la que habíamos instalado aquí, también podemos pues igual que hicimos antes ejecutar este o abrir, no entrar dentro de este container en interactivo, entonces vamos a usar otra vez guión y el primer comando que ejecutamos es bash para entrar en un terminal, veis que ahora estamos dentro del container y tenemos aquí disponible Salmon que lo hemos instalado que antes no lo tengamos y podemos volver a usar este programa aquí, perfecto. Vale, entonces los containers pueden como veis incluir o construir a partir de una imagen base, podemos instalar uno o varios programas encima de esta imagen base. Una cosa que hemos visto es que una vez entramos dentro de estos containers no tenemos disponibles ninguno de los ficheros que tenemos en local, entonces como solucionar esto hacemos un mount que sería como montar nuestros ficheros, voy a copiar este comando en donde es lo que estamos haciendo aquí es ejecutar nuestra imagen MyImage con el comando de Salmon y le estamos dando un fichero transcriptum.fa que lo tenemos aquí en data.chical transcriptum.fa pero como veis esto está fallando, está fallando porque este fichero no existe dentro del container. La manera de solucionar esto como he dicho es montando que se hace con los dos guiones volume como veis vamos a ejecutar este programa y ahora lo que hemos hecho es vamos a ver el comando arriba lo que hemos hecho es con volume indicar que nuestro fichero que se encuentra aquí en local lo separamos por dos puntos primero va el path de nuestro fichero local y luego el nombre o el sitio o la ruta del fichero que queremos dentro del container entonces este fichero lo estamos creando o si lo estamos como añadiendo con el nombre transcriptum.fa y aquí le damos pues el fichero transcriptum.fa y efectivamente el comando ahora sí que puede correr ahora hemos estado montando sólo un fichero pero otra cosa que se puede hacer es montar una carpeta donde aquí estamos usando pwd que es el directorio actual y lo montamos en el mismo en el mismo path en la misma ruta dentro del container entonces tendremos acceso a este fichero dentro de la misma localización que teníamos en local y al usar una carpeta no en este caso pwd que si podemos no sigamos que contiene es la carpeta donde nos encontramos ahora pues tendremos acceso a todos estos ficheros que tenemos aquí y otras cosas que se pueden hacer es definir otras otras variables como por ejemplo aquí sólo la carpeta data y pues vamos a montar iríamos a montar sólo solamente esta carpeta data y el contenido de esta carpeta vale lo que otra cosa que se puede hacer es en lugar de generar las imágenes como habíamos hecho ahora a partir de un docker file es decir la hemos generado a partir del docker file y la teníamos creada en local lo que podemos hacer es también subirla a docker hub que es un repositorio de imágenes de docker podéis visitar esta web con el link que aparece aquí y crear una cuenta de forma gratuita y entonces subir vuestra imagen que esté disponible en este repositorio esto no lo vamos a demostrar ahora porque no tenemos tanto tiempo pero podéis visitarlo y que sepáis que se puede hacer exacto ahora hemos estado ejecutando imágenes y ejecutando comandos en una imagen directamente pero cómo usamos estos containers dentro de nextflow vamos a estar usando el script 2 que vimos en la sesión o no que si recordáis pues lo que hacía era ejecutar sólo un proceso index que usaba salmón entonces tenemos esto comando de aquí nextflow run script 2 con docker y usando la imagen my image lo estamos ahora tanto a través del comando de ejecución de nextflow para que sobre escriba esta carpeta este container perdón que teníamos definido en nextflow.config entonces en lugar de usar este container le decimos que usé my image y como veis pues ahora se ha ejecutado usando nuestra imagen my image que contenía salmón otra opción de gestor de containers es singularity esto no lo vamos a estar viendo durante esta sesión tampoco ya que singularity no está disponible en el entorno de gitbot pero en el caso de que necesitéis usar singularity por algún motivo pues aquí tenéis toda la información y lo que sigamos a ver es la tercera opción que es usar konda o bio konda para estar usando konda vamos a ejecutar estas dos líneas en gitbot que sean simplemente para preparar el entorno y que tenga que tenga konda disponible para poder usarla y lo que vamos a ver es este fichero en .yaml que tenemos aquí este fichero es lo que llamamos una receta de konda o una konda recipe y lo que nos está diciendo bueno mientras tanto porque esto tarda un poco voy a ir creando creando nuestro container de konda con konda en create y usando el fichero de en .yaml entonces esto lo que nos está diciendo es le está asignando un nombre en f tutorial a nuestro container de konda luego está usando estos tres canales disponibles que son como los repositorios donde hay imágenes disponibles y las dependencias que va a incluir nuestro container son Salmon, FastQC, MultQC y TPP que estos son los programas que usamos en el script 7 que contenía aquí usamos Salmon, FastQC y MultQC en donde los tenemos disponibles aquí mientras esto se va mientras se va creando este container de konda si tarda mucho vamos a poner un corte aquí en el vídeo para que no perdamos tanto tiempo mientras tanto también añadir que konda es un poco menos reproducible porque no es un container aislado sino que lo construimos es decir que no lo construimos a partir de estas imágenes base que habíamos visto así que por eso puede haber algunas interacciones con nuestro sistema local que lo hacen un poco menos reproducible que usar singularity o docker vale pues aquí tenemos que ya si nos ha creado nuestro environment, nuestro entorno, lo que podemos hacer es usar konda en list para ver los entornos disponibles que tenemos, tenemos base que es el que viene es como el entorno básico del konda y en f tutorial que acabamos de crear ahora y entonces lo podemos activar con este comando que nos aparece aquí y ahora veis que ya hemos activado este container entonces vamos a correr nuestro script 7 usando este container de konda como veis pues se ejecuta en nuestro script y para salir de activar este container podemos usar konda de activate otra opción es usar entornos de konda usando micromamba para esto tendremos también un fichero que es la receta de konda y también usamos un docker file como habíamos hecho al inicio para crear containers del docker entonces veis que empezamos en lugar de linux de una imagen que ya contiene instalado micromamba y luego montamos o copiamos el fichero en punto yama el que es el que contiene nuestra receta de la imagen de konda que queremos que queremos obtener y usamos micromambra create para crear el entorno el environment que queríamos crear pues con estos comandos de micromamba vale aquí vemos pues que se ha ejecutado nuestro script nuestro script 7 igual que habíamos usado containers de docker y los habíamos podido coger de repositorios de internet también los podemos coger de otros repositorios como en este caso bio containers que contiene bastantes containers de con programas que se usan en bio informática bueno y aquí lo estamos pues obteniendo de este repositorio de bio containers entonces aquí he abierto este recuadro para ver que en lugar de especificar el container con with docker o with konda como habíamos hecho como hemos estado viendo también lo podemos definir dentro del proceso con la palabra clave container y luego el nombre del container entonces pues directamente este proceso se va a ejecutar usando este container también como como nota como apunte es mejor usar containers pequeños como más modulares para cada proceso en lugar de crear un container grande que englobe o que pueda ejecutar todos los procesos con todos los programas ya que sino cada vez que tengamos que actualizar alguna herramienta pues tendramos que modificar todo el container entonces pues es más recomendable usar un container para cada para cada proceso y aquí efectivamente pues ya se nos ha descargado nuestro container vamos a pasar ahora que ya hemos visto cómo manejar las dependencias a hablar de canales procesos y operadores en el siguiente bloque entonces vamos a empezar por canales como ya estuvimos viendo los canales son unas estructuras de datos que se usan para pasar datos de una tarea a otra es decir que conectar como otros procesos el otro un proceso se va a un canal y este canal será el input del siguiente proceso tenemos en extra dos tipos de canales los canales de tipo q de tipo cola y los canales de tipo value o valor empezaremos viendo los canales de tipo q son así en cronos que lo que quiere decir es que los elementos no tienen una prioridad y se puede pueden dar varios elementos al mismo tiempo es decir que no haya no hay uno que esté esperando aquí el otro termine es un direccional es decir que va a pasar de un proceso ha producido el canal y el otro proceso será el que consume el canal siempre en una misma dirección y finalmente cifo que se refiere a que el primer elemento que entra es el primer elemento que va a salir este concepto de siempre que definamos el output de un canal ese canal que nos retorna perdón el output de un proceso este canal que nos retorna el el proceso siempre será un canal de tipo cola y además también se pueden construir a partir de las fábricas de canales que ya estuvimos viendo channel off y channel from path entonces bueno aquí tenemos algún ejemplo tenemos este canal channel off 1 2 3 vamos a probar aquí tenemos este fichero snippet punto nf voy a cerrar todo lo demás que ya no necesitamos para ir copiando el código y irlo probando entonces vemos aquí que hemos creado un canal con los elementos 1 2 3 primero estamos imprimiendo el canal que es un de un objeto de tipo de broadcast y luego con bio pues vemos los elementos 1 2 y 3 los otros tipos de canales son canales de tipo valio de tipo valor estos canales siempre contienen un solo valor y se pueden consumir varias veces mientras que los canales que no han dicho los canales de tipo q sólo pueden ser consumidos una vez estos canales de tipo valio pueden ser consumidos varias veces y entonces los creamos a partir de esta fábrica de canales valio y además hay algunos operadores que lo que retornan son canales de tipo valio como por ejemplo first last a coleg van aquí veis varios ejemplos pues esto first nos retornará un canal de tipo valio a partir de un canal de tipo cola que que retorno puse el primer elemento de este otro canal de tipo q aquí tenemos un ejemplo otra vez que podemos ver lo que tenemos aquí es dos canales de tipo q o el primero con elementos 1 2 y 3 y el segundo con elemento 1 y este proceso son que le pasamos los dos canales y lo que hace es sumar el elemento del primer canal y el elemento del segundo canal y entonces usamos bio para ver qué ha pasado cuando hemos ejecutado este script que sólo vemos dos es la suma de uno más uno porque pasa esto y no vemos los siguientes valores de este canal que tenía tres elementos pues porque al ser un canal de tipo cola este aquí es un canal de tipo q hemos dicho que no se puede consumir varias veces por lo que sólo tiene un elemento se va a ejecutar con el primer elemento y luego no hay más elementos para seguir entonces estos estos dos pues son ignorados si añadiramos otro valor deberíamos ver pues ahora dos dos valores pero lo que hemos dicho es que los canales de tipo valio sí que se pueden consumir más de una vez entonces vamos a probar a cambiar este canal con un solo elemento porque los canales vale sólo pueden tener siempre un solo elemento y como vemos pues sí que se ha ejecutado más veces tenemos uno más uno que son dos uno más dos que son tres y uno más tres que son cuatro y con un solo elemento sí que se han ejecutado las tres veces que esperábamos por si tenéis curiosidad de ver por qué pasa eso cómo funciona aquí lo que está pasando es que los canales de tipo que tienen este predator de aquí pues son cil que lo que hace es que cuando se al final de un canal de tipo cola cuando se encuentra este poison pill la ejecución separa es por eso que los canales de tipo q no se pueden re usar pero en cambio los canales de tipo valio no contienen este poison pill y por eso no son consumidos no se consumen y se pueden usar más de una vez otro ejemplo que tenemos aquí es usando el mismo proceso pero aquí estamos pasando el canal tipo 2 con el operador first que nos retorna el primer elemento de este chano 2 y hemos dicho que este operador retorna un canal tipo valio como resultado es decir que por eso se está pudiendo re usar entonces vamos a ver ahora los diferentes tipos de chano factores de fábricas de canales y hemos visto a valio que genera pues un un canal de tipo valio por ejemplo pues este canal tendría este valor que es una string el odor y este canal de aquí vamos a probarlo voy a usar un bio para verlo que no se imprima el resultado por pantalla lo que hace es que aquí tenemos una lista y esta lista es un solo elemento como ves toda la lista es un elemento canal de tipo valio solo puede tener un elemento luego tenemos chano lo que también lo hemos visto que nos genera pues un canal tipo q cada cada uno de los de estos valores es un elemento del canal luego también podemos usar vamos a ver este ejemplo de aquí con canales de tipo que podemos usar rangos aquí por ejemplo esto sería como para hacer si quisiamos tener los cromosomas humanos por ejemplo tendríamos todos los valores del 1 al 23 sin necesidad de especificarlos uno por uno más x y y el rango lo definimos con estos dos puntos como habéis puesto los números de 123 y x y y otros chano factores es por ejemplo from list a partir de una lista lo que tenemos es que cada elemento de la lista va a ser un elemento del canal entonces tenemos que lo igual luego tenemos from pad que también lo estuvimos viendo en la en la sesión uno a partir de una ruta de un fichero que puede incluir wild cards es decir por ejemplo asterisco pues sería cualquier fichero que termine con csv va a ser un elemento de este canal que hemos creado y también especificar aquí que que la mayoría de de fábricas de canales tienen varias opciones esto lo mejor es revisar la la documentación de nexlo para ver las opciones disponibles y ver pues que podemos usar por ejemplo aquí podemos usar club como hemos dicho para este disco para definir cualquier cualquier carácter hay varios varios que lo suyo es pues que y que reviséis la documentación y veáis las opciones disponibles luego tenemos from file pairs que pudiendo habíamos visto durante la sesión uno que lo que hace es coger todos los ficheros de esta carpeta data y cualquier cualquier carácter más uno o dos punto fasquio y lo que retorna es pues un elemento para cara para cada pareja de ficheros con un identificador que es normalmente el nombre del fichero en todo lo que está definido por el asterisco y luego el fichero uno y el fichero dos luego hay otras opciones como por ejemplo from es que lo que puede hacer este constructor de canales es coger datos de repositorio de sr a directamente esto no vamos a probar ahora porque es un poco más complicado no tenemos tanto tiempo pero para que veáis luego también podemos estar usando ficheros y a partir de los ficheros crear canales vamos a ver este ejemplo de aquí lo que estamos haciendo aquí es primero crear un canal a partir de este fichero de data y lo que estamos haciendo es usar split text este operador para separar cada línea de este fichero en un elemento del canal como veis pues cada línea ha pasado a ser un elemento luego todos los operadores también otra vez podéis revisar la documentación para ver todas las opciones tienen varias opciones por ejemplo va y tú pues estará creando grupos de dos líneas exacto grupos de dos líneas y luego imprimimos el elemento del canal y le añadimos en chan para divertirlo mejor como ves pues ha cogido dos líneas esto es un elemento esto es otro elemento esto es un elemento y al final como no es un número para pues sólo tenemos una línea una cosa que estamos viendo aquí es que estamos pudiendo concatenar diferentes operadores no hace falta que este esta línea la asignemos a un canal y luego a este canal le apliquemos el operador sino que lo podemos concatenar y esto es como mucho más estándar el next flow y fácil de de leer podemos ver por ejemplo este otro ejemplo aquí contes una variable y lo que estamos haciendo al dividir el fichero por líneas es imprimir esta variable y sumarle uno cada vez por cada elemento entonces nos imprime el número de la línea a parte de ficheros de texto también podemos operar o obtener canales a partir de leyendo otros tipos de ficheros como el los ficheros de su que son ficheros que cada columna está separada por comas aquí pues estamos leyendo este canal uno que tenemos aquí como ves pues separado por comas y tenemos primero y diferentes valores lo que estamos haciendo es usar split ccub que recorre cada cada línea de este fichero y luego podemos acceder a cada columna con indexes en donde aquí estamos usando bio con cada elemento es decir cada línea lo hemos llamado row y accedemos a row 0 que era el prision id y row 3 que era ese tres directores ese tres tir y como veis pues obtenemos el id y perdón número de samples esa columna es el index tres y tenemos aquí pues el número de muestras vale otras pues también igual que igual como vimos con split text tiene opciones por ejemplo header true que lo que va a hacer es indicar que tenemos un un header es decir que la primera fila la primera línea es el gd otra vez pues ir viendo la documentación con las diferentes opciones que podemos usar lo mismo que hemos hecho con con fichero este cv lo podemos hacer con fichero este cv que están separados por tabuladores en lugar de comas y en este caso es otra opción que es sep y añadimos contra barrate para indicar que está separado por tabulaciones aparte de estos formatos también hay otros formatos de ficheros más complejos como json o como llamar entonces también podemos leer este tipo de ficheros y obtener canales a partir de su contenido vale pasamos al siguiente punto que sería procesos como hemos estado viendo los procesos son elementos básicos que ejecutan funciones como script o otros programas y para definirlos usamos la palabra clave process normalmente le damos un nombre que por convención definimos con mayúsculas y los definimos entre una closure que es son usar estos corchetes estos corchetes en donde es un proceso básico siempre tendrá que tener un script pero además hay otros bloques otras definiciones hay cinco disponibles primero tenemos las directives que serían unas declaraciones iniciales que nos permiten pues añadir información bueno la iremos viendo también más en detalle ahora lo que tenemos input que serían los ficheros que pasamos en input tenemos output que sería pues lo que retorna como output este proceso finalmente tenemos buen que es para añadir una condición y script que hemos dicho que es lo que ejecuta el comando que queremos ejecutar aquí veis la estructura básica de un proceso como hayas dicho usamos process le damos un nombre primero tendríamos las directivas luego el input el output buen y script que aparte de script pues como veis también puede podemos darle diferentes nombres ahora vemos y recordaros que aquí tenéis los símbolos de las cruces para obtener más información vale vamos a empezar con el bloque de script lo que hace este bloque es ejecutar el comando o los comandos porque como veis puede tener varias líneas que que va a ejecutar nuestro proceso entonces vamos a estar grabando este ejemplo aquí tenemos un script que lo que hace es imprimir esta esta frase la guarda en un fichero y luego vemos que dividimos el fichero en un fichero chunk uno y luego lo estamos cumpliendo podemos ver que se ha creado nuestra carpeta o que ya estuvimos viendo que aquí se ejecuta cada uno de estos procesos y en esta carpeta con este identificador deberíamos ver pues nuestros ficheros que se han creado file chunk uno y chunk archived comprimido por defecto el contenido donde nuestro script se interpreta como código en bash pero también puede interpretar cualquier otro tipo de código y como se hace pues usando el shibang no después por ejemplo aquí estamos indicando que nuestro script es código de python y lo que estamos haciendo es tenemos una variable hello una variable y luego los estamos imprimiendo entonces lo que podemos mirar otra vez es nuestra carpeta world en este caso no hay ficheros pero como estuvimos viendo en la sesión uno tenemos estos ficheros esos ficheros que contienen el código el comand punto comand punto shell contiene código y punto comand punto o contiene el estándar o puto en donde vemos que efectivamente se ha empreso lo que lo que pretendíamos y si miramos un shell pues veremos nuestro código este es el script que se ha ejecutado entonces nuestras variables que le pasamos a nuestro script las definimos las pasamos con el símbolo del dólar aquí ves que estamos definiendo un parámetro params punto data llamado data que le asignamos la palabra world y entonces con el símbolo del dólar nos referimos a él y lo que estamos haciendo es imprimir hello world vale ahora lo que pasa no estamos viendo el output igual que antes porque los procesos han funcionado entonces no nos enseñar el estándar output por defecto lo que voy a hacer para que lo podamos ver es añadir la directives y eso es bastante útil en el caso de que queréis devolver código entonces deberíamos de ver el estándar output impreso en nuestra terminal y veis ahora sí que vemos que efectivamente nos ha imprimido hello world que pasa cuando queremos usar variables de linux que también se definen con el símbolo de dólar para especificar que queremos usar una variable del entorno en lugar de una variable de nexlo vamos a tener que escapar el símbolo del dólar con la contrabaja entonces si ejecutamos este comando perdón voy a ir otra vez de y vamos a ejecutar este proceso nos va a imprimir nuestra variable de que es donde se está ejecutando el proceso es decir la variable del pat donde se ejecuta el proceso que veis que es el directory de 9 de 1 si en lugar de ejecutarlo así lo ejecutaramos sin escapar veríamos el pat es la variable de nexlo entonces será la carpeta donde estamos ejecutando nexlo en lugar de ser donde está corriendo este script porque es una variable de nexlo y veis efectivamente ahora pues es la carpeta donde nos estamos encontrando ahora más opciones exacto en donde ahora hemos visto que debíamos escapar el dólar para proporcionar variables de entorno pero lo que podemos hacer es definir definir nuestro script en lugar de con la palabra script con la palabra shell y entonces entre comid triples comillas simples en lugar de dobles esto será interpretado como una variable de entorno por el contrario para usar una variable de linux como params.data tenemos que usar la exclamación y en este caso la ponemos entre corchetes añadimos algo para ver que se imprime y deberíamos ver pues que x es una variable que hemos definido dentro del script bonju lemon aquí entonces pues podemos usar shell script o ejecutar codigo de backton por ejemplo de r de cualquier codigo queramos en función de lo que queramos que nuestro proceso haga y lo que sea más conveniente en cada momento vale luego la otra opción que tenemos en cuanto a scripts es tener scripts condicionales entonces esto es un if else o if else if else como podríamos tener en otros lenguajes de programación que lo que van a hacer es ejecutar nuestro código nuestro script en función de estas condiciones entonces si params compres es vemos aquí params compres es jzip va a ejecutar va a comprimir el fichero si es vzip2 va a comprimir el fichero en el otro formato vz2 y si no si no es ninguno de los dos nos va vamos a tener una excepción entonces para ver esto lo que voy a hacer es imprimir el comando por pantalla porque realmente no nos interesa tanto ver si el fichero se ha comprimido no como ver cuál de los dos codigos se está ejecutando entonces tenemos definido por defecto que params compres es jzip y efectivamente vemos que se está ejecutando el código de jzip si en lugar de esto sobrescribimos el parameter compres por vzip2 deberíamos ver que se está ejecutando el otro el otro script este de aquí como veis efectivamente ahora pues se ha ejecutado este otro script bueno entonces pasamos al siguiente bloque que es el bloque de inputs al ser como hemos dicho ya varias veces también en cada proceso independiente es decir que corren estas carpetas del work es como un entorno más aislado debe tener todos los archivos o todo lo que necesite para ejecutarse disponible dentro de el sitio donde se está ejecutando para eso pasamos los inputs los vamos a los pasamos con canales para que cada elemento por ejemplo si eso fuera un fichero pues el fichero estuviera disponible para el proceso la tarea que debe que debe ejecutar en donde siempre definimos los inputs con el nombre input y un calificador y un nombre como si le dieramos un nombre a la variable vamos a ver este ejemplo aquí ya tenemos de putru en donde tenemos el canal no que contiene 1 2 y 3 que es nuestro input como veis le definimos el calificador de valio ya que lo que tenemos son valores y le damos el nombre de x y aquí lo examos la variable x con el símbolo del dólar y vemos que efectivamente obtenemos proces job y el número 2 3 1 aparte de el calificador de valio también tenemos a los calificadores como paz que nos indica que lo que tenemos aquí no es un valor sino que es un fichero y entonces aquí salimos viendo listado este fichero que le hemos pasado vemos pues que tenemos santo el vasquio aquí lo que está pasando es que estamos cogiendo todos los ficheros de esta carpeta pero estamos dándole el nombre veis que en lugar de una variable lo ponemos entre comillas estamos diciendo que el nombre del fichero es santo el punto vasquio por eso todos los ficheros tienen el mismo nombre lo que podemos hacer es usar una variable como hacíamos antes definir el nombre de la variable y luego usar esta variable de esa forma pues es más mucho más dinámico y los nombres de los ficheros se mantendrán tal y como vemos aquí veis que los nombres de los ficheros se ha mantenido ese es otro tipo de comando que podríamos ejecutar vale entonces también podemos combinar diferentes canales por ejemplo aquí tenemos dos inputs vale x que usamos aquí y vale y usamos aquí y en el proceso le pasamos canal uno y canal dos nuestra estructura ya le hemos estado viendo le hemos estado usando de hecho en los ejemplos y en este caso pues canal uno es 1 2 3 canal 2 es a b c como veis pues 1 a 2 b 3 c lo que nos está pasando aquí de hecho es bien que nos haya salido así porque tenemos la demostración es que siempre los elementos primeros segundos y terceros van a ir parejados pero al ser pues independientes ya dijimos que los bases pueden ser paralelizables el orden no tiene porque ser el mismo las parejas siempre serán las mismas pero el orden no aquí en este caso pues tenemos el tercer elemento que se ha imprimido segundo lo que pasa bueno esto lo hemos visto cuando hemos visto canales de tipo q y canales de tipo vale o no lo que ha pasado antes cuando teníamos un canal de tipo q con tres elementos y un de tipo vale o con un elemento es que sí que se han ejecutado los tres en cambio cuando el canal de tipo q tenía menos elementos sólo se ejecutaba dos veces porque los canales de tipo q no se pueden usar varias veces sino que son consumidos luego también podemos repetir inputs en este caso lo que estamos usando es este calificador y que lo que va a hacer es que por cada uno de nuestros ficheros de esta carpeta por cada uno de estos ficheros se va a ejecutar el proceso por todos los elementos de esta lista methods es decir tendremos pues fichero que termina el 0 1 bueno el fichero que termina en 27 con el modo expreso con el modo si cofi y con el modo regular deberíamos encontrarlo por aquí aquí con el modo regular entonces como veis con hich significa que se va a ejecutar el proceso para cada elemento de esta lista que estamos que estamos pasando vamos a pasar entonces para hablar del siguiente bloque el bloque de outputs en este bloque output especificamos todo lo que va a formar parte del canal de todo lo que va a retornar nuestro proceso entonces siempre tiene también un calificador igual que el input el nombre del output y aquí estamos usando emit y el nombre del canal emit lo veremos o emit lo veremos más profundamente en siguientes sesiones pero lo que hace es definir un canal entonces tiene un nombre esto cuando lo usemos fuera del proceso nos podemos definir referir perdón a este canal usando el nombre del proceso por ejemplo y luego pondemos vamos a hacer un ejemplo imagínamos que tenemos un proceso con su input con su output y suscript ahora estoy haciendo rápido porque no nos importa cuál es el input del output el output será cualquier valor x emit bar entonces si queremos acceder a este canal fuera de nuestro proceso podremos acceder a él con full output bar que es el nombre de este canal y así haremos referencia al canal que ha creado este proceso pues aquí lo que vemos en este ejemplo es que nuestro output lo hemos definido con el mismo nombre de la variable de input es decir que también es dinámico y aquí lo que tenemos es son tres métodos por DNA y RNA que los estamos guardando en el fichero y el output es todo lo que es decir el mismo nombre digamos que teníamos de input aquí estamos viendo el canal de output que contiene y imprimimos resípt y el elemento del canal y como vemos resípt contiene la lista que hemos pasado de input entonces de outputs a parte de valores también podemos tener ficheros como en este caso bar lo que estamos haciendo aquí de hecho si lo ejecutáis veréis un error porque random no es una variable de next low entonces se tiene que escapar es que estamos imprimiendo un número aleatorio guardándole en este fichero results.txt y el fichero results.txt es nuestro output y aquí con text estamos leyendo el contenido de cada elemento del canal que en este caso el contenido de este fichero y hemos recibido pues este valor este número aleatorio en los outputs también podemos usar wild cards igual que hacíamos con los inputs con los path de input para definir cualquier fichero entonces aquí este output como vemos pues estamos separando una palabra con letras de uno es decir sub palabras de largo uno y les estamos portando en ficheros que empiezan por chunk para baja y le decimos a nuestro output que cualquier fichero llamado chunk para baja y cualquier otra cosa va a formar parte de nuestro entonces si ejecutamos esto obtendremos pues que nos imprime fichero y el nombre del fichero con name obtenemos el nombre del fichero y el contenido con text y veremos pues que tenemos las diferentes letras de la palabra hola bueno aquí tenemos la especificación de que latmap lo que hace es que podamos leer cada línea por cada uno de los valores en lugar de si no lo estamos usando este este operador pues veríamos este output luego como ya hemos visto también podemos usar variables nombres de variables para definir el input y nombres de variables o usar estas mismas variables para definir el output en donde aquí estamos diciendo que el output de este de ese proceso se va a guardar en un fichero nombrado el input el valor punto a ln y cualquier fichero con este valor a ln va a ser va a formar parte de nuestro canal de de esta forma pues estamos definiendo los outputs de manera dinámica perdón entonces deberíamos ver aquí estamos pasando el valor x es este canal species que le tenemos aquí un canal from list y deberíamos tener ficheros llamados cad punto a ln doc a ln y slot a ln y efectivamente como veis pues aquí los tenemos los ficheros que tienen este nombre seguimos vale que introducimos tappel con tappel lo que podemos hacer es que no tenemos un solo elemento que tiene de un canal sino que tenemos una tupla en este caso pues de dos elementos si os acordáis de lo que generaba el canal from file pairs teníamos el nombre primero y luego una lista que contenía el fichero con uno y el fichero con dos entonces aquí decimos con tappel que primero vamos a leer el nombre con sample id y luego un pad que son ficheros que son los ficheros uno y dos lo mismo con el output primero vamos a guardar sample id de nuestro tappel y luego paz en este caso el fichero llamado sample punto van que es el que generamos aquí y si vemos lo que contiene output pues efectivamente primero tenemos sample id y luego tenemos sample punto van entonces el siguiente bloque es el bloque bueno que se usa para definir una condición entonces el proceso sólo se va a ejecutar si esta condición se cumple en este caso nuestra condición que está aquí es si fasta que es un fichero de input punto name el nombre del fichero cumple esta expresión regular y type es nr entonces el input que estamos pasando es data pros y todo lo que termina en tfi es decir todos estos ficheros y todos empiezan por pp1 1 y entonces pues aquí vemos los comandos que se han ejecutado si esto lo cambiáramos perdón sólo los que empiezan por pp1 1 son los que se han ejecutado en cambio los de pp3 estos no se han ejecutado si esto lo cambiamos de 30 deberíamos tener estos dos ficheros de aquí que son los que han cumplido esta condición y entonces el proceso se ha ejecutado aunque nuestro canal de input contiene todos los ficheros sólo se ejecutan con los que cumplen la la condición y finalmente tenemos las directives son declaraciones que pudimos usar nos permiten especificar opciones que van a ejecutar a que van a afectar la ejecución de nuestro proceso sin necesidad de que tengamos que modificar el código de nuestro proceso entonces tenemos varias algunas de ellas y ahora estuvimos viendo como por ejemplo cpu's que nos permite definir con cuantas cpu's que debemos ejecutar nuestro proceso o memory para definir la memoria o container para definir el container con el que vamos a ejecutar el proceso y aquí tenéis pues varias directivas que podéis que podéis especificar de hecho vamos a ver un container voy al repositorio de github de nfcor el que contiene todos los modulos disponibles de nfcor y por ejemplo vamos a usar salmón index que lo hemos estado usando en algún ejemplo y vemos que aquí pues tiene la directiva tag que en este caso pues es la variable transcript hasta el label que definimos para definir cpu's a memoria que va a usar y con el container de con o container de docker y singularity especificados aquí a las directivas que están al principio de nuestro proceso y este proceso también pues tiene su input o output un un bloque de buen condición y el script donde definimos algunas variables y tenemos el código que se ejecuta vale entonces también lo que podemos hacer es organizar nuestros outputs si no especificamos nada nuestro output va a estar dentro de la carpeta de work pero no se va a guardar en ningún otro sitio entonces lo que podemos hacer es usar esta directiva publish div para especificar donde queremos que se guarde nuestro output en donde aquí le estamos diciendo que los ficheros de output se van a guardar dentro de params outdir que está definido como la carpeta my results luego una subcarpeta bumpfiles y en este caso usamos mod copy que significa que queremos que copie los ficheros en esta carpeta en donde vemos que se ha creado aquí my results bumpfiles y contiene los ficheros de output que en este caso son ficheros que terminan en results punto text cualquier cosa que termine en punto text además ahí podemos incluso organizar un poco más teniendo subcarpetas en este caso definimos params outdir que es otra vez my results luego la subcarpeta sample ID que es la pasa le pasamos la variable con input y luego otras subcarpetas count y outlooks y aquí en lugar de mod definimos pattern esto otra vez podéis revisar la documentación de de next load hecho la tenemos aquí para ver las diferentes opciones que tenéis es decir mod que hemos usado pattern y otras opciones que hay disponibles pattern lo que hace es separar en este caso cualquier fichero está formando parte de nuestro canal de output pero aquí lo separamos con los que terminan en fast queue van a ir en esta carpeta los que terminan en cons punto txt en esta otra carpeta y outlook en esta otra carpeta entonces vemos aquí que los tenemos exacto primero este es lo que teníamos antes con bumpfiles y ahora se ha generado my results sample ID cada sample ID tiene una subcarpeta y dentro hay los fast queue cons y outputs vale entonces hemos hemos terminado de ver los procesos lo que podemos ver ahora son los operadores los operadores son métodos que podemos usar para manipular canales y a transformar sus valores hemos estado viendo algunos y ahora vamos a repasar otros se dividen básicamente en estos tipos para filtrar para transformar separar combinar o separar en fork no como en horquillas digamos operadores matemáticos y otros operadores entonces otra vez os recomiendo ir a la documentación de next load tenéis la sección de operadores y veis que además hay muchos más que se pueden usar y también aquí podréis ver ejemplos y revisar si tienen algunas opciones por ejemplo estos operadores de aquí pues tienen algunas opciones que se pueden usar donde vamos a ir revisando esto por cuestiones de tiempo voy a intentar ir un poco rápido aquí primero tenemos un ejemplo básico donde tenemos un canal que contiene 1 2 3 4 y luego usamos map map lo que haces aplicar una función y le damos nombre y a cada uno de los elementos y lo que hacemos es multiplicar por el mismo entonces tenemos el canal y lo que va a pasar es que tendremos pues cuatro por cuatro y los elementos de escuel van a ser el cuadrado de los elementos del primer canal otra vez no hace falta tener separado en cada línea o guardado en cada canal cada una de las cosas que queremos hacer sino que podemos encadenar diferentes operadores o incluso fábricas de canales luego un operador luego otro operador entonces empezamos con los básicos primero tenemos bio que que ya lo hemos estado usando bastante lo que haces imprimir los valores y además añade un salto de línea además bio lo podemos definir entre paréntesis o podemos usar closures para definir cómo se va a imprimir en este caso pues primero imprimimos un guión y luego el elemento y aquí veníamos el logo tenemos map que como ya ha dicho aplica una función a cada elemento aquí pues a cada elemento le estamos aplicando reverts que lo que va a hacer es girar las el orden no de estas palabras o en este otro caso y lo podemos ver aquí en un ejemplo estamos definiendo el canal de output que contiene una dupla y aquí lo estamos viendo con bio que en este caso tiene dos variables del primer elemento de la tupla y el segundo elemento de la tupla que son la palabra y la el largo de la palabra y vemos aquí hello contains 5 letters all contains 5 letters luego tenemos mix que lo que hace es combinar diferentes canales aquí tenemos c1 que lo estamos combinando con c2 y c3 y como ves pues tenemos los elementos de cada uno de estos canales luego también tenemos flatten que lo que hace es que tenemos aquí dos canales es decir perdón dos listas este canal contiene dos listas dos elementos que son una lista con lo que hacemos con flatten es que cada uno de los elementos de la lista sea un elemento del canal entonces en lugar de tener los elementos tenemos seis elementos collect es un poco lo contrario de flatten y lo que hace es que todos los elementos del canal los pone en una lista donde el canal resultante es un canal de un solo elemento que es una lista conteniendo todos los elementos del canal inicial lo que tenemos que lo que nos va a hacer esto vamos a ver un poco más en detalle es que nos va a unir todos los tapples en este caso tenemos un canal de tapples y nos lo va a unir por todos los que empiecen por uno van a estar unidos con los segundos valores a b y c todos los que tiene el primer valor 2 se van a unir y 3 y por ejemplo como se ha dicho ver las diferentes opciones que están disponibles si ponemos bye pues en lugar de bye uno en este caso el index en lugar de por el index 0 nos los va a unir por el index 2 a perdón el index 1 el segundo elemento de de los tapples como veis pues aquí tenemos a 1 2 a 1 o 3 de etcétera hemos visto corrupta por luego tenemos join que funciona ese día aquí tenemos el primer canal left luego tenemos el primer canal right y lo que vamos a obtener es que nos va a unir en función del primer del primer elemento de la tupla nos va a unir los elementos de los dos canales x tenemos x 1 y x 4 obtenemos x 1 4 y lo mismo con los otros igual que en el anterior también hay opciones por ejemplo bye en este caso también está disponible luego tenemos branch que lo que nos va a hacer es separar un canal creando dos canales o dos subcanales podríamos decir en este caso aplica una condición si el elemento es más pequeño de 10 va a tener el subcanal y si es más grande que 10 y luego la accedemos en con result que es el nombre del canal resultante es mal que es el nombre del subcanal o la rama digamos y aplicamos bio para ver si el elemento es pequeño o grande y aquí vemos el resultado como ya ha dicho revisar la documentación de nexo donde podéis ver otros tipos de canales ya que hay bastantes y además los tenéis también separados por tipo de de operador y podéis revisar pues las diferentes opciones de cada uno de ellos siempre tendréis pues ejemplos y en el caso de que tengan opciones pues veréis también las opciones vale hemos llegado hemos hablado de canales procesos y operadores entonces la siguiente la siguiente sección de esta sesión va a ser una introducción a groovy vamos a hacer ahora una pequeña pausa y seguimos luego con la introducción a groovy vale seguimos pues bueno empezamos con la introducción a groovy como yo me has comentado next flow es un lenguaje de dominio específico que está implementado encima de groovy no hace falta ser expertos en programación con groovy pero a veces es útil porque podemos usar código de groovy en nuestras pipelines que nos puede ayudar donde vamos a ir repasando algunas de las estructuras de las estructuras básicas de groovy por ejemplo empezamos con print y print ln que ya los hemos estado usando alguna vez en nuestros scripts entonces lo que hacen básicamente es imprimir por pantalla la string que les pasamos podemos usar print que podíamos usar print ln de print line luego también vimos durante la sesión uno que podemos añadir comentarios que no afectan nuestro código con dos barras o para añadir más de una línea entre el símbolo de una barra y un asterisco y para cerrar al revés un asterisco y una barra como veis esto pues no afecta la ejecución luego tenemos variables las variables simplemente se se definen asignándoles un valor entonces aquí te podemos tener variables que son números o que son otros tipos de objetos en este caso pues es una fecha también un número decimal booleanos de true or false o strings por ejemplo veis diferentes tipos de variables y simplemente les asignamos con el igual y como veis también aquí estamos usando siempre el mismo nombre es decir que se pueden sobrescribir otra opción es definir las variables con la palabra clave def y esto es recomendable o de hecho se debe usar cuando definimos variables dentro de una función o una closure para ver cómo funciona ahora debería pues de la misma forma deberíamos ver imprimido fu también y vimos algunos ejemplos por ejemplo el módulo de salmón que está está debiendo como ejemplo salmón index y muchas veces vemos en especialmente en estos módulos de nfcor que tenemos variables definidas con def dentro de el el bloque de script o sea dentro justo antes de nuestro script que esto define pues definen varias variables que luego usamos si por ejemplo pues aquí estamos definiendo arts y luego lo estamos usando con el símbolo de dólar igual que usamos las variables otras variables de nexlo como como los inputs por ejemplo después también tenemos listas las listas se definen entre los paréntesis cuadrados y se puede acceder a sus elementos utilizando los index o utilizando el método get entonces vamos a definir esta lista y vamos a ver sus elementos el index 0 y por ejemplo vamos a usar index 3 y deberíamos obtener pues 10 y 40 efectivamente luego también rápidamente hablar de assert que sería para hacer tests es decir lo que hace un assert es comprobar que esta condición en igual aquí en este caso pues el elemento este primer elemento y ese segundo elemento son lo mismo en el caso de que a ser sea true no no pasa nada es decir no vemos nada simplemente sigue la ejecución pero en el caso de que hace hacer sea falso veríamos un error entonces aquí veis que no ha pasado nada pero si ahora decimos pues list 0 es igual a 20 por ejemplo nos debería parecer un error ya que este assert es false y nos aparece información en la ejecución de de nuestro de nuestro script separe es decir todo esto nos está ejecutando ahora y vemos pues que nos dice list contiene esto a list 0 es 10 y el assert está comprobando si es veible entonces con con listas también podemos acceder a ellas con index negativos por ejemplo en este caso menos uno sería la primera posición empezando por el final que es 2 y también podemos usar rangos aquí pues vemos que si accedemos a la lista desde menos uno a cero pues tendremos la lista leída al revés y ahora pues todos estos test de a ser deberían pasar exactamente todos están pasando aquí tenéis otras operaciones más complicadas usando a ser también y podéis acceder a más documentación en el caso de que estéis interesados esos son cosas ya más avanzadas podríamos decir entonces dependiendo del caso pues es ir a mirar la documentación y ver si alguna de estas opciones es útil luego tenemos maps en los maps o mapas podríamos decir en groovy son similares a diccionarios o listas indexadas se definen entre 46 cuadrados y asignamos pues a cada valor tiene una key o una clave 2 puntos y el valor asociado a esta clave podemos acceder a los elementos de un map con como si fuera un index usando la clave el nombre de la clave en este caso a 0 luego también podemos acceder con un punto y el nombre de la clave de en este caso es 1 y luego también tenemos el método get y le pasamos de argumento el nombre de la clave es igual a 2 además bueno ahora todo debería no parece nada porque es correcto además también podemos modificar estos maps de la misma forma que accedemos a los elementos podemos sobre escribirlo entonces lo que estamos haciendo aquí es que map a le estamos asignando una string x a map de la string y y luego con el método put estamos asignando a la clave c la string z y entonces aquí comprobamos que a map ahora es igual a este mapa de aquí efectivamente pues todos los test han pasado correctamente lo siguiente que tenemos son strings con las las strings se pueden definir entre comillas simples o comillas dobles luego también podemos tener usar variables que se popularan dentro de la string y podemos por ejemplo aquí pues estamos usando esta string fox color con el método join o el operador perdón fox color es esta lista con join pues estamos uniendo las diferentes strings de la lista vamos a probar este momento y aquí veréis que pues foxtap se va a subir por quick de quick brown que es lo que es nos ha unido aquí box y exactamente la diferencia entre comillas dobles y comillas simples es que con comillas dobles las variables son interpretadas como variables en cambio con comillas simples se interpreta literalmente es decir que esto no será la variable x sino que se imprimirá dolar x literalmente luego tenemos otras opciones cuando usamos strings con perdón cuando usamos una contra barra en una string por ejemplo en este caso contra barra t lo que hace la contra barra es escapar un carácter ya lo hemos estado usando también antes pero hay caracteres especiales como contra barra t que significan la tabulación en este caso entonces lo que va a pasar aquí es que esto se va a interpretar como la tabulación si en lugar de definir comillas simples usamos nuestra string entre dos barras lo que va a pasar es que se va a leer como literal veis aquí en el primer caso pues se imprime contra barra t en cambio el segundo caso es sustituido por una tabulación lo que también podemos tener strings con múltiples líneas y en este caso las definimos entre comillas triples o también podemos usar las barras definiendo la string entre entre comillas simples perdón entre triples comillas simples dobles pues vemos que se nos imprime en las varias líneas y usando usando la string entre barras pues también vemos que tenemos múltiples líneas luego tenemos if statements es decir condicionales esto lo estuvimos viendo con condicionales en un script y se usan pues if entre paréntesis la condición y luego entre claudators el código ejecutar y el código ejecutar en el caso de que no se cumpla la condición también se puede igual que nos estamos enseñando este ejemplo se pueden opiar los claudators por ejemplo podemos ver opción de aquí lo que tenemos es una lista y aquí vemos si la lista no es nul es decir no es una lista vacía y si el tamaño de la lista es más grande que cero pues nos imprime el contenido de la lista y si no pues debería imprimirnos del list y sentimos luego también tenemos force es decir loops que lo que hacen es pues iterar en varios elementos aquí en este caso pues desde cero hasta que y sea más pequeño que tres y cada iteración le vamos tomando uno pues nos va a imprimir el valor de 0 1 2 y cuando es igual a 3 pues ya se para el y de la misma forma podemos iterar en diferentes elementos en este caso de una lista no se imprime cada elemento de de la lista luego también tenemos funciones en este caso estamos definiendo una función llamada fit y entre paréntesis tenemos los argumentos que le vamos a dar un integr llamado n también usamos los claudatos para definir la función y en este caso estamos diciendo que lo que retorna es un integr en donde aquí tenemos el statement de retorno y esta operación que es un poco más más compleja básicamente esto es como un y se evalúa esta condición si la condición es cierta vamos a obtener un uno si no es cierta vamos a operar entonces es como y por y vemos pues que el resultado es este otra forma de definir funciones en lugar de especificar que tipo de variable va a retornar es una especificarlo y usar def de que viene de define de definir también podemos obvia usar la palabra clave retorn como veis aquí no la hemos usado y entonces lo que se va a retornar siempre es el último la última operación entonces tenemos las closures las closures identifican lo que es de código además se pueden pasar como argumentos ahora lo veremos por ejemplo aquí estamos pasando un argumento y también se pueden usar para definir una función anónimo o una función sin nombre se definen entre claudatos entonces como veis aquí pues square es estamos definiendo una una closure entre claudatos que lo que hace es coger un elemento llamado item y multiplicarlo por el mismo esta closure lo que hacemos aquí es ejecutarla con el método call y la pasamos un elemento 5 entonces item está 105 y aquí obtendremos pues 5 elevado al cuadrado 5 por 5 y también se puede usar similar a como usamos una función con square y entre paréntesis el argumento que le pasamos en lugar de tener que usar el método call y en este ejemplo lo que vemos es que estamos tenemos una lista usamos collect y le pasamos la closure como el como argumento a este a este operador collect entonces lo que va a hacer collect es aplicar esta función para cada uno de los elementos entonces deberíamos ver que cada uno de los elementos de esta lista aparece elevado al cuadrado es decir 1 4 9 16 por defecto cada elemento o si cada elemento usa el nombre y el nombre de variable y pero esto se puede sobrescribir usando este símbolo de una una flecha entonces aquí en lugar de y le estamos diciendo que usé no como nombre de variable y pues lo mismo nos retorna num elevado multiplicado por el mismo se pueden hacer cosas más complejas como por ejemplo aquí que lo que tenemos es una closure con dos dos argumentos a y b y lo que nos imprime es un este ing usando a y b si lo aplicamos en un un map valios es un map lo que obtenemos es aquí estamos usando valios y es decir aplicado cada cada elemento de este map y pasamos la closure como argumento y lo que obtenemos es pues para cada elemento aplicamos esta función y aquí ves que se nos han imprimido las frases vale lo siguiente que nos indica es que las closure se pueden usar de forma anónima o pueden definir funciones anónimas lo que significa que las estamos usando o las estamos programando digamos en el sitio donde se van a usar sin necesidad de guardarlas en una variable como aquí sino que directamente veis aquí usábamos y y pasamos esta closure como input aquí tenemos y directamente la línea de la closure que vamos a aplicar y aquí tenéis algunos enlaces con información extra sobre groovy en el caso de que necesitéis mirar la documentación para aprender un poco más o también tenéis este libro disponible en el caso de que estéis interesados en aprender más sobre groovy vale vamos a pasar a la última sección de hoy de la sesión 3 que es modularización esto lo estuvimos viendo especialmente durante la sesión 2 cuando hablamos de los módulos de nexlo hablamos de módulos isoporkflux y esto es modularización entonces lo que vamos a hacer estar haciendo ahora es ya no vamos a usar snippet sino que vamos a usar el primer fichero que usamos en la sesión 1 jello punto en f que era una una workflow con dos procesos y luego nuestro workflow como veis aquí pues todo está en el mismo fichero lo que nos permite hacer la modularización es importar módulos o importar procesos que en este caso pues son módulos de este otro fichero entonces voy a recortar y voy a crear un nuevo fichero por ejemplo módules punto en f y voy a copiar los dos procesos aquí entonces lo que lo que podemos hacer es incluir estos módulos para usarlos en nuestro workflow aquí tenemos la línea con include que se compone pues de include el nombre del módulo que vamos a importar splitletters lo tenemos aquí y convert to upper from y la ruta del fichero que es relativa al script jello punto en f y entonces vamos a ejecutar esto y deberíamos ver que aunque no tengamos los procesos aquí en el mismo fichero la pipeline se sigue ejecutando igual que se ejecutaba antes efectivamente pues vemos que jello world se ha separado y lo tenemos en mayúsculas como son módulos que están en el mismo fichero una cosa que podemos hacer es incluirlos incluirlos en la misma línea separados por un punto y como y con vez pues se sigue ejecutando como esperamos otras opciones aquí tenemos la misma línea otras opciones es usar aliases es decir qué pasa si quisiéramos usar el mismo módulo dos veces por ejemplo queríamos repetir esta ejecución y guardarle un nuevo canal pero usar el mismo módulo con el mismo lo que vemos es que nos aparece este error y nos dice que hemos usado un mismo que hemos usado el proceso split letters dos veces que lo deberíamos incluir con un nombre diferente o en una workflow diferente entonces cómo vamos a solucionar esto vamos a copiar este ejemplo de aquí es usando aliases entonces un alias se define con as estamos incluindo este módulo pero con el nombre split letters 1 o split letters 2 y este este módulo también con nombre converto a por 1 converto a por 2 y entonces podemos aquí usarlo una vez y dos veces y como veis pues todo sea al ser el mismo código se ha repetido hemos ejecutado dos veces pero ya no tenemos el error vale otra cosa que podemos hacer en un módulo es definir un otro tal vez y entonces voy a copiar los inputs que teníamos antes porque ya no necesitamos ahora demostrar los alias vale lo que estamos haciendo aquí es asignar cada vez que ejecutamos un proceso un módulo lo asignamos a un canal y este canal es el input del otro y aquí el canal final pues vamos estamos viendo los resultados que tenemos usamos otro cosa que podemos hacer es usar en lugar de asignar cada cada output a un canal es usar directamente el output de este módulo accedemos a él con split letters punto o sea el nombre del módulo punto o de output y ya está y aquí pues vemos que tenemos converto a por punto o y luego usamos vio y estamos viendo los resultados lo que podemos hacer es también usar index y como sólo tenemos un elemento en este canal que es decir lo que tenemos son un elemento no tenemos tuples ni nada parecido pues sólo podemos acceder con el index 0 si tuviéramos tuples pues podríamos acceder a los varios elementos del table con indexes lo que podemos hacer también es nombrar estés o estos outputs entonces aquí en nuestro fichero modules lo que hacemos es decir que el resultado es decir el odd punto converto a por antes lo teníamos igual que en split letter que sólo definíamos pues el tipo de output podemos usar la palabra clave emit dos puntos y un nombre que le queramos dar al canal en este caso a por entonces la forma de usar esto en nuestra workflow es con el nombre del módulo punto o y punto a por este es el nombre que le hemos dado a nuestro canal de auto esto es útil si tenemos más de un output pues para diferenciar al que queremos acceder con diferentes nombres además también podemos usar directamente pipes que son con la barra esta vertical para concatenar el output de en este caso pues primero pasamos el input y el output de este de este módulo le aplicamos flaten y luego se convierte en el input del siguiente módulo y este pues no es retorna el output esto se puede hacer en el caso de que lo necesitéis aunque como veis pues ahora es una pipeline muy muy sencilla y se ve bien pero esto se puede complicar mucho entonces pues no es no es lo más recomendable otra cosa que podemos hacer es usar una workflow dentro de otra aquí tenemos hemos incluido nuestros módulos tenemos nuestra workflow que usa pues todos los módulos lo mismo que hacíamos antes lo que hemos hecho es nombrar esta workflow my pipeline entonces podemos usarla y ejecutarla dentro de otra workflow esto pues debería correr lo mismo que estábamos ejecutando antes para ver lo mejor porque ahora claro esto no voy a copiar exactamente lo mismo y usar mi pipeline 2 darle un nombre diferente y veremos que estamos ejecutando lo mismo dos veces y además estamos podiendo usar los mismos módulos dos veces que esto si os acordáis del error que teníamos antes cuando hemos repetido dentro de la misma workflow nos decía o lo importamos con un nombre diferente es decir con un alias o en un contexto en un scope digamos diferente en una workflow diferente este es el caso y estamos ejecutando todo dos veces esto también podría ser útil por ejemplo si tenéis una pipeline con procesamiento de datos y luego algunos análisis de quisierais pues saltaros la parte de procesamiento de datos se ejecutan dentro de una workflow dos workflows diferentes lo que vemos aquí es que las workflows también pueden tener inputs esto los definimos con tec y aquí veis que en lugar de estar usando para uns gritting que usábamos antes lo que tenemos es gritting y lo estamos usando aquí estamos diciendo que tiene un input que lo vamos a llamar gritting y lo usamos aquí luego cuando ejecutamos nuestra workflow antes teníamos simplemente mi pipeline y nada más porque ya teníamos dentro esa vamos creábamos el canal en cambio aquí le estamos pasando un input que es este canal como veis pues igual que antes se se ejecuta y luego el contenido es decir el código pues lo ponemos en el bloque main también como seguramente habréis imaginado podemos tener outputs teníamos tec que define el input de una workflow main que define el código digamos con el cuerpo y emit que define el o en este caso pues no tenemos convert to upper output aquí dentro de main sino que lo tenemos dentro del bloque emit entonces de la misma forma que con un módulo podemos acceder a punto en una workflow también podemos acceder a su output con punto y luego estamos usando bio para ver qué contiene este canal de output en este siguiente ejemplo bueno sólo para ver pues que se ejecuta igual en este siguiente ejemplo lo que estamos haciendo es darle un nombre a este canal de output de la workflow my data y aquí igual que antes pues usamos my pipeline punto o punto my data bio entonces pues también le estamos dando un nombre a este canal otra vez útil en el caso de que tengamos más más outputs y como veis pues esto queda bastante estructurado en inputs main y output entonces antes os he dicho que si tenemos varias workflows dentro de la misma workflow en este caso tenemos my pipeline 1 my pipeline 2 y aquí vamos las dos podíamos saltarnos una de las dos en este caso usamos el parámetro de next flow con una sola barra entri perdón el nombre del fichero sigue siendo hello entri y el nombre de la workflow es decir nos hemos saltado perdón aquí estamos entrando en my pipeline 1 sólo estamos ejecutando esta parte si pusiéramos entri my pipeline 2 y estaríamos ejecutando sólo esta otra en este caso como hacen lo mismo pues el resultado siempre es siempre es el mismo sólo para ver un ejemplo pues aquí deberíamos ver hola mientras que con claro que le hemos dado otra vez para el scripting bueno este ejemplo lo que deberíamos hacer aquí es definir pues también inputs y outputs con tec y emit a ver si esto va a funcionar ahora exactamente pues ahora tenemos output en cambio si entraramos sólo ejecutar la primera parte pues seguiríamos teniendo el ogro luego también finalmente los parámetros que hemos estado ya hablando mucho sobre ellos de los definimos con la keyword con la palabra clave params y los podemos usar dentro de en este caso de esa función con el símbolo del dólar y params punto y el nombre del parámetro disculpad dentro de este módulo en este caso tendríamos aquí nuestros módulos y podríamos usar vale vamos a copiar este nuevo módulo de aquí en nuestro fichero de módulos módulos y geló y aquí tenemos params fu y params bar y los usamos dentro del módulo entonces aquí teníamos pues una nueva nueva workflow que incluye este módulo y lo usa entonces ves que aquí no usamos params un y params bar sólo lo usamos en el fichero de módulos pero al ejecutarlo si que los tenemos disponibles con hola para para sobre escribir estos parámetros lo que vamos lo que podemos hacer es en el momento de incluir de usar incluir nuestro módulo usar este este método a params y luego lo que está haciendo es decir que fu va a ser hola en este módulo y ahora pues deberíamos tener hola inscritos igual hoy al centro en el mundo y efectivamente tenemos volamos y ya llegamos al final esto es sólo un apunte de que antes nexlo usaba DSL1 en el caso de que tuvieras una pipeline creada con DSL1 pues aquí hay las notas de cómo pasar o migrarla o coletarla a DSL2 pero como seguramente es el caso de estar empezando desde cero las nuevas versiones de workflow sólo aceptan DSL2 así que podemos ignorar DSL1 y directamente sólo centrarnos en DSL2 vale pues esto es todo por la sesión 3 gracias por por asistir y nos vemos mañana en la sesión 4