Mariano Guerra: Creemos en la Web: Uno, dos, muchos

En capítulos anteriores vimos tipos de datos "compuestos", también llamados colecciones, como listas y objetos, también vimos, solo por necesidad, como mostrar elementos de una lista.

En este capitulo vamos a ver en detalle como hacer algo con cada elemento de una lista o un objeto.

Repasando

Recordemos un poco los tipos de datos compuestos/colecciones, escribí cada linea en la consola del navegador (F12 y selecciona el tab Consola o similar) para ver el resultado.

Lista (o Array)

Una lista es una secuencia de cero o mas elementos de cualquier tipo, cada elemento se accede por su posición en la lista, empezando desde la posición cero.

Una lista vacía (sin elementos):

let listaVacia = [];

Una lista con un elemento:

let lista1 = [42];

Una lista con números:

let listaNumeros = [1, 2, 42, 100, 9001];

Una lista con valores de distinto tipo:

let listaTipos = [42, null, undefined, true, "hola", []];

Objeto

Un objeto es un conjunto de cero o mas elementos de cualquier tipo, cada elemento se accede por su clave, que es el nombre que le damos a ese campo, también llamado atributo del objeto.

Un objeto vacío:

let objetoVacio = {};

Un objeto con un campo:

let objetoCampo = {nombre: 'bob'};
// es lo mismo que lo siguiente, siempre y cuando la clave (key)
// sea un identificador valido, sino siempre hay que usar comillas
let objetoCampo1 = {'nombre': 'bob'};

Un objeto con valores de distinto tipo:

let objetoTipos = {numero: 42, null: null, undefined: undefined, booleano: true, texto: "hola", lista: [10, 20, 30], objeto: {clave: 'valor'}, 'clave rara!': 12.5, 100: 'cien'};

Vale aclarar que las claves de los objetos son siempre de tipo texto (strings), por mas que no le pongamos comillas, javascript las va a convertir en texto, por lo que las claves 'null', 'undefined' o si ponemos un numero, no son de ese tipo sino que se convierten en texto antes de guardarlas.

Accediendo a los valores a mano

Como accedemos a los valores almacenados en colecciones? hay dos formas dependiendo la colección, que queremos acceder dentro de esa colección y la clave.

Accediendo elementos de una lista

Como ya dijimos, las listas son una secuencia de elementos de cualquier tipo, donde cada elemento se accede por su posición en la lista, empezando desde la posición cero, probemos con las variables que declaramos arriba, si no las escribiste en la consola, este es un buen momento para hacerlo.

Si ya las escribiste y las intentas declarar de nuevo, puede que obtengas un error de que la variable ya fue declarada, proba con otro nombre o cerra y abrí el tab para poder declararla de nuevo.

Siempre me gusta empezar tratando de romper todo, así que veamos que pasa cuando intentamos acceder a un elemento que no esta definido, por ejemplo, el primer elemento de la lista vacía, que seria el elemento con indice 0, para acceder a un elemento de una colección usamos los corchetes después del nombre de la colección y entre corchetes ponemos el identificador del valor que queremos acceder:

listaVacia[0];

El resultado me da:

< undefined

Es decir que cuando le pedimos un elemento que no tiene nos devuelve undefined.

Pero como sabemos cuantos elementos tiene para saber hasta donde le podemos pedir?

En Javascript todos los valores son "objetos", es decir que tienen atributos y funciones asociadas para operar sobre sus valores, esto quiere decir que le podemos preguntar a una lista cuantos elementos tiene accediendo a su atributo length (que es "longitud" o "largo" en ingles).

Probemos:

listaVacia.length;
< 0

Nos dice que tiene 0 elementos, osea que es una lista vacía.

Como habrás visto usamos otra forma de acceder a un atributo de la lista, esta vez con un punto, esta forma se puede usar cuando el valor del atributo es un identificador valido, esto no quita que podamos hacer:

listaVacia['length'];
< 0

Lo que no es muy útil si el atributo al que queremos acceder es "fijo", pero nos va a ser útil para cuando queramos acceder a atributos que no conocemos cuando estamos escribiendo el programa, para darte una idea de lo que seria, imaginemos que tenemos una función que recibe dos parámetros, un valor y un nombre de un atributo de ese valor y devuelve el valor de ese atributo:

function devolverValor(valor, atributo) {
    return valor[atributo];
}

La función no sabe cual es el valor de atributo, ya que es una variable, puede ser cualquier cosa, probemos con listaVacia y 'length':

devolverValor(listaVacia, 'length');
< 0

Probemos con un indice de la lista:

devolverValor(listaVacia, 0);
< undefined

Probemos con lista1:

devolverValor(lista1, 'length');
< 1
devolverValor(lista1, 0);
< 42

Accediendo elementos de una objeto

Buenas noticias, ya sabes como acceder a elementos de un objeto, las formas son las mismas, con corchetes si la clave no es un identificador valido y con punto si lo es, empecemos rompiendo, tratemos de acceder a un atributo que no esta definido en el objeto:

objetoVacio.yoNoExisto;
< undefined

Como la lista, si no esta definido devuelve undefined. Ahora probemos con uno que si exista:

objetoCampo.nombre;
< 'bob'

Como ya vimos, se puede acceder con corchetes, cosa que no es necesaria en este caso pero probemos:

objetoCampo['nombre'];
< 'bob'

En donde si la vamos a necesitar es con nuestra 'clave rara!', que no podemos acceder con punto ya que no es un identificador valido:

objetoTipos['clave rara!'];
< 12.5

Como habíamos dicho, las claves de los objetos son siempre de tipo texto (string), probemos que es cierto:

objetoTipos[100];
< 'cien'

Eso no demuestra nada... pero esto si:

objetoTipos['100'];
< 'cien'

Como veras también funciona, porque si le pasamos el numero, javascript lo convierte en string, si le pasamos un string lo usa directamente, por eso ambas funcionan.

Iterando (Listas)

Hasta acá podemos acceder elementos de una colección de a uno, sabiendo la clave o indice que queremos acceder de antemano o cuando tenemos la clave/indice en una variable.

Pero muchas veces necesitamos acceder a todos los elementos/atributos de una colección sin saber de antemano cuales son.

Para esto vamos a aprender a iterar sobre los elementos de una colección, hay varias formas de hacerlo, vamos a empezar con una de las mas flexibles y estándares, aunque un poco complicada, así que no te preocupes si no la entendés la primera vez, yo programe por un año antes de entender bien como funcionaba.

Antes de mostrar la sintaxis pensemos que es lo que queremos hacer, para el caso de una lista:

Inicialización:

  • Preguntarle a la lista cuantos elementos tiene y guardarlo en una variable, llamemosle len
  • Declarar una variable a cero, llamemosle i (de indice)

Ciclo:

  • Si el indice es menor que el largo
    • Obtener el valor de la lista en ese indice
    • Hacer algo
    • Incrementar la variable "indice"
    • Ejecutar Ciclo de nuevo

Como veras, la lógica tiene dos partes, una que se ejecuta una sola vez al principio (Inicialización) y una que se ejecuta por cada elemento de la lista, hasta que una condición deje de cumplirse (que el indice este dentro del largo de la lista).

Esto se suele llamar "ciclo for", veamos la sintaxis a grandes rasgos, no es la sintaxis valida pero para darte una idea:

for (inicializarVariables (1); condicionDeCorte (2); siguiente (3)) {
    // lógica por cada paso (4)
}

Veamoslo con código valido, no hace falta que lo entiendas entero, es para que te des una idea:

for (let i = 0, len = lista.length; i < len; i += 1) {
    let elemento = lista[i];
    console.log('elemento', i, 'de lista es ', elemento);
}

Inicialización:

la sección inicializarVariables (1) se ejecuta una sola vez al principio.

Ciclo:

Luego se evalúa la condicionDeCorte (2) para ver si la lógica (4) se va a ejecutar o no.

Si la condición de corte evalúa a false el ciclo termina, si evalúa a true se ejecuta la lógica (4) con el valor de i actual. Luego se evalúa siguiente (3), que normalmente incrementa el valor de i, pero puede hacer otras cosas.

Luego de evaluar siguiente (3), se repite el ciclo.

Probemoslo con varias listas de largos distintos, para eso pongamos nuestro ciclo for en una función:

function mostrarElementos(lista) {
    console.log('antes del ciclo for para', lista);

    for (let i = 0, len = lista.length; i < len; i += 1) {
        let elemento = lista[i];
        console.log('elemento', i, 'de lista es ', elemento);
    }

    console.log('después del ciclo for');
}

Probemoslo con una lista vacía:

mostrarElementos(listaVacia);

Imprime lo siguiente:

antes del ciclo for para []
después del ciclo for

Como podemos ver, el paso (4) nunca se ejecuto, porque la condición de corte (2) evaluó a false la primera vez.

La secuencia completa para listaVacia es:

// Inicialización
// (1)
i = 0
len = 0

// Ciclo
// (2)
i < len // false

// Fin

Probemos con una lista de un elemento:

mostrarElementos(lista1);

Imprime:

antes del ciclo for para [ 42 ]
elemento 0 de lista es  42
después del ciclo for

Ahora el paso (4) y (3) se ejecutaron 1 vez, veamos la secuencia:

// Inicialización
// (1)
i = 0
len = 1

// Ciclo
// (2)
i < len // true

// (4)
elemento = lista[0]; // 42

// (3)
i += 1 // 1

// (2)
i < len // false

// Fin

Ultima, con dos elementos:

mostrarElementos([10, 20]);

Imprime:

antes del ciclo for para [ 10, 20 ]
elemento 0 de lista es  10
elemento 1 de lista es  20
después del ciclo for
// Inicialización
// (1)
i = 0
len = 2

// Ciclo
// (2)
i < len // true

// (4)
elemento = lista[0]; // 10

// (3)
i += 1 // 1

// Ciclo
// (2)
i < len // true

// (4)
elemento = lista[1]; // 20

// (3)
i += 1 // 2

// (2)
i < len // false

// Fin

Iterando (Objetos)

Iterar listas es lo mas común, pero algunas veces necesitamos iterar por todas las claves de un objeto, para eso usamos una versión distinta (y por suerte mas simple) del ciclo for, veamoslo directamente en una función:

function mostrarElementosObjeto(objeto) {
    console.log('antes del ciclo for para', objeto);

    for (let key in objeto) {
        let elemento = objeto[key];
        console.log('elemento', key, 'de objeto es ', elemento);
    }

    console.log('después del ciclo for');
}

Esta versión es mas simple, asigna a key cada clave en objeto y ejecuta la lógica.

Probemos con un objeto vacío:

mostrarElementosObjeto({});

Imprime:

antes del ciclo for para {}
después del ciclo for

Con varios elementos:

mostrarElementosObjeto(objetoTipos);

Imprime:

antes del ciclo for para { '100': 'cien',
  numero: 42,
  null: null,
  undefined: undefined,
  booleano: true,
  texto: 'hola',
  lista: [ 10, 20, 30 ],
  objeto: { clave: 'valor' },
  'clave rara!': 12.5 }

elemento 100 de objeto es  cien
elemento numero de objeto es  42
elemento null de objeto es  null
elemento undefined de objeto es  undefined
elemento booleano de objeto es  true
elemento texto de objeto es  hola
elemento lista de objeto es  [ 10, 20, 30 ]
elemento objeto de objeto es  { clave: 'valor' }
elemento clave rara! de objeto es  12.5

después del ciclo for

Para saber cuantos atributos tiene un objeto, podemos usar una función llamada Object.keys que nos devuelve una lista de las claves de un objeto, probemosla:

Object.keys(objetoVacio);
< []
Object.keys(objetoCampo);
< [ 'nombre' ]
Object.keys(objetoTipos);
< [ '100',
  'numero',
  'null',
  'undefined',
  'booleano',
  'texto',
  'lista',
  'objeto',
  'clave rara!' ]

Veamos como podríamos iterar sobre las claves de un objeto usando Object.keys y lo que aprendimos sobre listas:

function mostrarElementosObjeto1(objeto) {
    console.log('antes del ciclo for para', objeto);

    let keys = Object.keys(objeto);
    for (let i = 0, len = keys.length; i < len; i += 1) {
        let key = keys[i],
            elemento = objeto[key];
        console.log('elemento', key, 'de objeto es ', elemento);
    }

    console.log('después del ciclo for');
}

Probemoslo:

mostrarElementosObjeto1({});

Imprime:

antes del ciclo for para {}
después del ciclo for

Con varios elementos:

mostrarElementosObjeto1(objetoTipos);

Imprime:

antes del ciclo for para { '100': 'cien',
  numero: 42,
  null: null,
  undefined: undefined,
  booleano: true,
  texto: 'hola',
  lista: [ 10, 20, 30 ],
  objeto: { clave: 'valor' },
  'clave rara!': 12.5 }

elemento 100 de objeto es  cien
elemento numero de objeto es  42
elemento null de objeto es  null
elemento undefined de objeto es  undefined
elemento booleano de objeto es  true
elemento texto de objeto es  hola
elemento lista de objeto es  [ 10, 20, 30 ]
elemento objeto de objeto es  { clave: 'valor' }
elemento clave rara! de objeto es  12.5

después del ciclo for

Como podemos ver, funciona igual que mostrarElementosObjeto.

Facundo Batista: Pasó la PyCon Argentina 2018

¡La décima! Todo un hito. Tuvimos 1080 asistentes, lo cual es más de lo que esperábamos, :)

El evento en si salió muy bien, todes les organizadores estamos muy contentes (y muy cansades). Pero valió la pena.

Arranqué más o menos prolijo......terminé bastante roto :p

Le pusimos mucho amor y creo que se notó. Increíble el equipo que se formó de trabajo, el laburazo que se pegaron es indescriptible. Estoy seguro que ese tipo de compromiso no se logra pagándoles. Creo que fue todo mucho mejor que si se los hubiese contratado para que trabajen (onda "organizadores profesionales de conferencias").

Sí, lleva mucho laburo, pero nos gusta más así: una conferencia hecha por todes nosotres, con nuestra impronta, no por un organizador de eventos con saco y corbata.

De la comunidad, para la comunidad.

Organizadores, ayudantes de sala, ayudantes de registración, etc...

La conferencia se sucedió durante tres días. El jueves fueron los talleres: tres tracks en paralelo de talleres de tres horas, más un cuarto track con el Django Girls, el único taller que tenía registración previa (y cupo, tuvimos que cortar en los 100 asistentes).

Todo el equipo del taller de Django Girls con asistentes y todo

Viernes y sábado fue el formato de conferencias clásicas: tres tracks de charlas en paralelo, la mayoría de 25 minutos, algunas de 50. También tuvimos espacios abiertos, speed interviews, lightning talks, e incluso un Panel de Diversidad y Género.

Y dos invitados internacionales: Carol Willing, core developer de Jupyter y ex directora de la PSF, y Brandon Rhodes, programador Python en Dropbox y organizador de la PyCon USA en Portland 2016 y 2017.

El que suscribe, con Brandon y Carol luego de darles regalitos

Incluso tuvimos un evento puramente social al aire libre, el domingo: un Pycnic totalmente abierto al que quisiera venir.

Pero más allá del desglose de actividades, quiero resaltar que logramos armar una conferencia de nivel internacional, sin nada que envidiarles a las que se hacen en otros países, pero completamente libre y gratuita. Zaffaroni dijo una vez que "El cambio social profundo, inclusivo, la revolución del siglo XXI, se hace apoderándose del conocimiento... conocimiento que la elite se empeña en monopolizar". Y en verdad, en este momento macrista donde nuestro país ya no tiene ni Ministerio de Ciencia y Tecnología, distribuir información gratis es más valioso que nunca.

Vino mucha gente: un venue más chico no nos hubiese alcanzado

De la misma manera, en este momento con tanta xenofobia, homofobia y "diversofobia", tenemos que ser más inclusivos que nunca. Y ser explícitos en darle la bienvenida a todo el mundo.

Por eso armamos este maravilloso texto sobre Diversidad, del cual incluimos un resumen en el librito de la conferencia.

Un panel de lujo, una charla super interesante

Durante el cierre tiré un par de anuncios para el año que viene. El primero es que tenemos el PyCamp 2019 confirmado, se va a hacer en San Rafael, Mendoza, del 2 al 5 de Marzo (estamos esperando que nos pasen un par de datos para confirmar el precio y salir a juntar el dinero).

El otro anuncio es que la PyCon 2019 se va a hacer también en Buenos Aires... la idea es empezar a hacer dos conferencias por cada sede (2018 y 2019 en Buenos Aires, 2020 y 2021 en otro lugar, etc), porque de esta manera se baja muchísimo el costo para los organizadores de hacer el evento. Sí, el primer año es un quilombo, pero el segundo año se repiten tantas cosas que es mucho más fácil.

La grupal (socavada notablemente por el partido "importante" :/ )

Facundo Batista: Décima Edición de la Conferencia Nacional de Python Argentina

Suena fuerte, ¡diez ediciones! Cuánta agua abajo del puente pasó desde que organicé la PyCon 2009, también en la Ciudad de Buenos Aires. Desde ese arranque gracias a otres organizadores pudimos disfrutar el evento en Córdoba, Junín, Quilmes, Rosario, Rafaela, Mendoza, Bahía Blanca, nuevamente Córdoba.

Y ahora volvemos al origen. Cerramos un círculo, pero no para terminar, sino para subir la apuesta y salir con más ímpetu a una nueva ronda. No sólo tener una conferencia con invitades internacionales, talleres y charlas relámpagos, sino también otras actividades como espacios abiertos, coaching para la preparación de charlas, entrevistas rápidas, y profundizar acciones sobre un tema que tratamos desde hace años en la comunidad: nuestra diversidad.

Claro, los tiempos no son los mismos. Incluso Python, como lenguaje, cambió notablemente. En esa época Python 3 era un futuro difuso, ahora Python 2 está en sus últimos estertores. Y no hay especialidad profesional relacionado con la programación que no use Python.

La situación del lenguaje en el país también cambió muchísimo. En esa época la problemática era encontrar trabajos relacionados con Python, hoy en día el desafío es encontrar suficientes trabajadores en nuestro querido lenguaje para poder satisfacer las necesidades de empresas, cooperativas y el Estado mismo.

Considerando que difundir el lenguaje era un objetivo del grupo de Python Argentina, entendemos que también esta comunidad haya cambiado, siendo un hito notable la creación de la Asociación Civil hace ya un par de años.

Y es con la ayuda de la Asociación Civil que pudimos hacer las últimas conferencias, incluyendo esta por supuesto. Y también con la colaboración y soporte del Centro Cultural General San Martín, de los sponsors que pueden visitar durante la conferencia y conocer en este mismo libro, y especialmente de todes aquelles que colaboraron en la organización, es que logramos hacer una nueva conferencia de calidad internacional, totalmente gratuita e inclusiva.

Una nueva PyCon Argentina. De la comunidad, para la comunidad. Que la disfruten.

Facundo Batista: Inminente gran PyCon gran

¡¡¡La PyCon arranca el jueves que viene!!! AAAAAAAAAAAAAHHHHHHHHH

respira

AAAAAAAAAAAAAAAAAAAAHHHHHHHHHHHHHHHHHHHHHHHH

Sabrán disculpar que no posteo mucho por acá...

A recontra full

Ya está publicado el cronograma de actividades, con todes les talleres y charlas que se van a dar.

La conferencia es libre y gratuita, para venir a aprender lo único que necesitás hacer es registrarte en este formulario y listo, ya podés acceder a todo lo que ofrece la conferencia (excepto al Django Girls, que era con cupo limitado y la registración al mismo ya cerró hace varias semanas). Si por algún motivo necesitás un certificado de asistencia, pedilo acá.

Entre todas las actividades que podés acceder están las Speed Interviews. Una speed interview es un modo de conocer empresas de la misma forma que una entrevista tradicional, solo que las entrevistas duran 5 minutos, y en una hora se pueden conocer a varias empresas, interiorizarse en lo que hacen, y generar un contacto para luego profundizar el conocimiento mutuo. Es ideal para aquelles que tienen mediana, poca o incluso ninguna experiencia laboral. No hay que tener miedo de aprovechar la oportunidad, ¡es un servicio gratuito de la conferencia! Sólo hay que reservar la posición anotándose acá.

Por último, acordate de reservar tu remera! No te cuelgues! También las vas a poder comprar los días de la conferencia, pero tené en cuenta que la forma de pago es la misma que ahora (no se puede manejar efectivo en el Centro Cultural)... metele que algunos talles se están acabando...

¡Nos vemos en unos días!

Marcos Dione: pefan

A few weeks ago I needed to do some line based manipulation that kinda went further of what you can easyly do with awk. My old-SysAdmin brain kicked in and the first though was, if you're going to use awk and sed, you might as well use perl. Thing is, I really can't remember when was the last time I wrote even a oneliner in perl, maybe 2011, in my last SysAdmin-like position.

Since then I've been using python for almost anything, so why not? Well, the python interpreter does not have an equivalent of perl's -n switch; and while we're at it, -a, -F, -p are also interesting for this.

So I wrote a little program for that. Based on those switch names, I called it pefan. As python does not have perl's special variables, and in particuar, $_ and @_, the wrapper sets the line variable for each line of the input, and if you use the -a or -F switches, the variable data with the list that's the result of splitting the line.

Meanwhile, while reading the perlrun manpage to write this post, I found out that -i and even -s sound useful, so I'll be adding support for those in the future. I'm also thinking of adding support for curly-brace-based block definitions, to make oneliners easier to write. Yes, it's a travesty, but it's all in line with my push to make python more SysAdmin friendly.

In the meantime, I added a couple of switches I find useful too. See the whole usage:

usage: pefan.py [-h] [-a] -e SCRIPT [-F SPLIT_CHAR] [-i] [-M MODULE_SPEC]
                [-m MODULE_SPEC] [-N] [-n] [-p] [--no-print] [-r RANDOM]
                [-s SETUP] [-t [FORMAT]] ...

Tries to emulate Perl's (Yikes!) -peFan switches.

positional arguments:
FILE                  Files to process. If ommited or file name is '-',
                      stdin is used. Notice you can use '-' at any point in
                      the list; f.i. "foo bar - baz".

optional arguments:
-h, --help            show this help message and exit
-a, --split           Turns on autosplit, so the line is split in elements.
                      The list of e lements go in the 'data' variable.
-e SCRIPT, --script SCRIPT
                      The script to run inside the loop.
-F SPLIT_CHAR, --split-char SPLIT_CHAR
                      The field delimiter. This implies [-a|--split].
-i, --ignore-empty    Do not print empty lines.
-M MODULE_SPEC, --import MODULE_SPEC
                      Import modules before runing any code. MODULE_SPEC can
                      be MODULE or MODULE,NAME,... The latter uses the 'from
                      MODULE import NAME, ...' variant. MODULE or NAMEs can
                      have a :AS_NAME suffix.
-m MODULE_SPEC        Same as [-M|--import]
-N, --enumerate-lines
                      Prepend each line with its line number, like less -N
                      does.
-n, --iterate         Iterate over all the lines of inputs. Each line is
                      assigned in the 'line' variable. This is the default.
-p, --print           Print the resulting line. This is the default.
--no-print            Don't automatically print the resulting line, the
                      script knows what to do with it
-r RANDOM, --random RANDOM
                      Print only a fraction of the output lines.
-s SETUP, --setup SETUP
                      Code to be run as setup. Run only once after importing
                      modules and before iterating over input.
-t [FORMAT], --timestamp [FORMAT]
                      Prepend a timestamp using FORMAT. By default prints it
                      in ISO-8601.

FORMAT can use Python's strftime()'s codes (see
https://docs.python.org/3/library/datetime.html#strftime-and-strptime-
behavior).

Go get it here.


python pefan sysadmin

Marcos Dione: ansible-in-a-box-or-iso

At my $NEWJOB I'm in the team that installs the products we sell on the client's machines. Most of the time the client has bought appliances from us, so they come with the installer and some tools for setting them up. Because of the type of product we sell, the customer might have bought between 3 to 12 or more nodes that will form a cluster, and some times they're spread over several data centers.

The system needs a frontend network and a backend one, and the nodes come with two high speed NICs (typically 10Gib), two low speed (1Gib), and a BMC/IPMI interface. The typical use is to bond both high speed NICs and then build two VLANs on top. The atypical use might be whatever the client came up with. One client has bonded each of the high speed NICs with one of the low speed in primary/backup mode, and has two physical networks. Another one does everything through a single interface with no VLANs. This should give you an idea of how disparate the networking setups can be, so the networking has to be custom made for each client.

Our first step is to connect to the nodes and configure networking. The only preconfigured interface is the BMC/IPMI one, which asks for an IPv4 via DHCP. So we connect to the BMC interface. This involves connecting via HTTP to a web interface that is run within the IPMI subsystem, then download a Java application that gives us a virtual KVM so we can use the computer as if we just had connected a keyboard and a monitor to it.

For those who don't know (I didn't before I started this new position), the IPMI/BMC system is a mini computer fully independent of the main system, which boots once the node has power connected to it, but not necessarily powered on. You can turn on/off the machine, divert KVM as I mentioned before, and more, as you'll see. If you're surprised to find out you have more than one computer in your computer, just read this.

Once connected to the node, we run a setup script, to which we feed all the networking info, including static IPs, gateways, DNS servers, timezone, etc. All this for each node. By hand. Slow, error prone, boring.

Let's automate this. The simplest tool I can think of is Ansible. In fact, I also think it's perfect for this. But there's a catch: there's no Ansible installed on the node, there is no way Ansible will be able to talk KVMimplementedasajavaapp-ese, and again, there's no networking yet, so no ssh or any other remote access. But most modern IPMI systems have an extra feature: virtual devices. You can upload iso images and IPMI will present them as a USB cd reader with media inside.

So today's trick involves in creating an iso image with ansible on it that can run on the target system. It's suprisingly easy to do. In fact, it would be as easy as creating a virtualenv, install ansible, add the playbooks and stuff, etc, and create an iso image from that, if it were not for the fact that the image has to be at least less than 50MiB (we have seen this limit on Lenovo systems). Ansible alone is 25MiB of source code, and compiled into .pyc files doubles that. So the most difficult part is to trim it down to size.

Of course, we fisrt get rid of all the .py source code. But not all. Modules and module tools in ansible are loaded from the .py files, so we have to keep those. I can also get rid of pip, setuptools and wheel, as I won't be able to install new stuff for two reasons: one, this is going to be a read only iso image, and two, remember, networking is not setup yet :) Also, ansbile is going to be run locally (--connection local), so paramiko is gone too. Next come all those modules I won't be using (cloud, clustering, database, etc). There a couple more details, so let's just have the script we currently use:

#! /bin/bash
# this is trim.sh

set -e

while [ $# -gt 0 ]; do
    case "$1" in
      -a|--all)
        # get rid of some python packages
        for module in pip setuptools pkg_resources wheel; do
            rm -rfv "lib/python2.7/site-packages/$module"
        done

        shift
        ;;
    esac
done

# cleanup
find lib -name '*.py' -o -name '*.dist-info' | egrep -v 'module|plugins' | xargs rm -rfv

for module in paramiko pycparser; do
    rm -rfv "lib/python2.7/site-packages/$module"
done

ansible_prefix="lib/python2.7/site-packages/ansible"

# trim down modules
for module in cloud clustering database network net_tools notification remote_management source_control web_infrastructure windows; do
    rm -rfv $ansible_prefix/modules/$module
done

# picking some by hand
find $ansible_prefix/module_utils | \
    egrep -v 'module_utils$|__init__|facts|parsing|six|_text|api|basic|connection|crypto|ismount|json|known_hosts|network|pycompat|redhat|service|splitter|urls' | \
    xargs -r rm -rfv
find $ansible_prefix/module_utils/network -type d | egrep -v 'network$|common' | xargs -r rm -rfv
find $ansible_prefix/modules/packaging -type f | \
    egrep -v '__init__|package|redhat|rhn|rhsm|rpm|yum' | xargs -r rm -v
find $ansible_prefix/modules/system    -type f | \
    egrep -v '__init__|authorized_key|cron|filesystem|hostname|known_hosts|lvg|lvol|modprobe|mount|parted|service|setup|sysctl|systemd|timezone' | \
    xargs -r rm -v

Notice that if I was even more space constrained (and it could be possble, if we find another IPMI implementation with smaller staging space) I could go further and make the venv use the Python installed in the system and not the one copied in the venv.

Now, the next step is to fix the venv to be runnable from any place. The first step is to make it relocatable. This fixes all the binaries in bin to use /usr/bin/env python2 instead of the hardcoded path to the python binary copied into the venv. One thing I never understood is why it didn't went a step further and also declared the VIRTUAL_ENV as relative to the path were bin/activate resides. In any case, I do an extra fix with sed and I'm done.

Last step is just to create the iso image. It's been ages since I last generated one by hand, and the resulting command line (which I simply stole from running k3b) resulted more complext than I expected (what happened to sensible defaults?). Here are the interesting parts:

#! /bin/bash

set -eu

ansible-playbook --syntax-check --inventory-file inventory.ini playbook.yaml

./check_config.py

./trim.sh --all

# make relative
/usr/bin/python2 -m virtualenv --relocatable .

# try harder
# this doesn't cover all the possibilities were bin/activate might be sourced
# from, but in our case we have a wrapper script that makes sure we're in a sane place
sed -i -e 's/VIRTUAL_ENV=".*"/VIRTUAL_ENV="$(pwd)"/' bin/activate

genisoimage -sysid LINUX -rational-rock -joliet -joliet-long \
    -no-cache-inodes -full-iso9660-filenames -disable-deep-relocation -iso-level 3 \
    -input-charset utf-8 \
    -o foo.iso .

We threw in some checks on the syntax and contents of the playbook (it's annoying to find a bug when running on the target machine, come back, generate a new iso, upload it, mount, etc). It is possible that you would also like to exclude more stuff in your working directory, so just create a build dir, copy over your files (maybe with rsync --archive --update --delete) and run genisoimage there.

This method produces an iso image 26MiB big that works both in virtual machines, with which I developed this solution, and on some IPMI systems, like the Lenovo I mentioned before. Unluckily I couldn't get my hands on many different systems that have IPMI and not being used for anything else.

One final note about sizing. If you run du on your working/staging directory to see how far are from the limit, use --apparent-sizes, as the iso format packs files better than generic filesystems (in my case I see 26MiB apparent vs 46MiB 'real'; this is due to block sizes and internal fragmentation).


ansible ipmi bmc sysadmin

Mariano Guerra: Creemos en la Web: Ver o no ver, también es una cuestión

En el capítulo anterior vimos como calcular o no según el resultado de una condición, ahora vamos a ver como cambiar que mostramos en HTML según el valor de condiciones.

Vamos a hacerlo dándole una cara a nuestro despertador.

Empecemos con la versión mas simple, inicializando un despertador con una sola alarma y mostrando si esta prendido o no y a que hora esta configurada la alarma.

<div id="despertador-app-1">
  <p class="alert alert-info">Despertador Prendido? <strong>{{despertadorPrendido}}</strong></p>
  <p class="alert alert-info">Hora Despertador: {{horaDespertador}}</p>
</div>
<script>
  new Vue({
    el: '#despertador-app-1',
    data: {
      despertadorPrendido: false,
      horaDespertador: 8
    }
  })
</script>

El resultado:

Despertador Prendido? {{despertadorPrendido}}

Hora Despertador: {{horaDespertador}}

Repasando un poco vue.js, tenemos un div raíz de nuestra aplicación, el cual tiene que tener un atributo id así le podemos indicar a vue.

Dentro del div tenemos dos párrafos, el primero muestra el valor de la variable despertadorPrendido usando el formato de vue {{variable}} para indicar que queremos que ahí ponga el valor de variable, el segundo muestra el valor de la variable horaDespertador.

Luego en un tag script inicializamos nuestra aplicación con new Vue(...) donde en ... indicamos usando un objeto javascript dos atributos:

el
Id del tag raíz de esta aplicación, para indicarle que es un id tenemos que prefijarlo con un #.
data
Los datos de nuestra aplicación, por ahora las dos variables que usamos en el HTML arriba y que usamos en el capitulo anterior.

Con esto ya mostramos el estado actual de nuestro simple despertador, pero no podemos prenderlo ni cambiar la hora, veamos como hacer eso:

<div id="despertador-app-2">
  <p v-if ="despertadorPrendido" class="alert alert-success">Despertador Prendido</p>
  <p v-else class="alert alert-danger">Despertador Apagado</p>

  <p class="alert alert-info">Hora Despertador: {{horaDespertador}}</p>

  <input v-model.number="horaDespertador" type="number" class="form-control" placeholder="Hora Despertador">

  <div class="mt-3 text-center">
    <button v-if="despertadorPrendido" v-on:click="despertadorPrendido = false" class="btn btn-danger">Apagar</button>
    <button v-else v-on:click="despertadorPrendido = true" class="btn btn-success">Prender</button>
  </div>
</div>

<script>
  new Vue({
    el: '#despertador-app-2',
    data: {
      despertadorPrendido: false,
      horaDespertador: 8
    }
  })
</script>

El resultado, proba cambiando la hora y haciendo click en el botón:

Despertador Prendido

Despertador Apagado

Hora Despertador: {{horaDespertador}}

Algunos cambios con respecto a la versión anterior, notaras que vue tiene su propio if como javascript, en este caso se escribe v-if="condicion" lo usamos para decidir que párrafo mostramos, si despertadorPrendido es true, mostramos:

<p v-if="despertadorPrendido" class="alert alert-success">Despertador Prendido</p>

Sino:

<p v-else class="alert alert-danger">Despertador Apagado</p>

v-else tiene que estar después de un tag con un atributo v-if o v-else-if.

A la hora la seguimos mostrando de la misma forma pero ahora tenemos un campo de texto para poder cambiarlo:

<input v-model.number="horaDespertador" type="number" class="form-control" placeholder="Hora Despertador">

Con v-model le indicamos a vue que queremos que el contenido de este input refleje el valor del campo horaDespertador, es decir que va a mostrar su valor y cuando lo cambiemos va a actualizar su valor.

Como el campo es un numero y no queremos tener un valor de tipo texto con un numero dentro, le indicamos a vue que nos lo convierta a numero con v-model.number.

El resto es HTML estándar.

Finalmente usamos v-if y v-else de nuevo para mostrar el botón Prender o Apagar según el valor de despertadorPrendido:

<button v-if="despertadorPrendido"
    v-on:click="despertadorPrendido = false"
    class="btn btn-danger">Apagar</button>

<button v-else
    v-on:click="despertadorPrendido = true"
    class="btn btn-success">Prender</button>

En cada botón le indicamos a vue con v-on:click que cuando el botón sea clickeado queremos cambiar el valor de despertadorPrendido.

Con esto ya tenemos una aplicación para prender/apagar un despertador y cambiar la hora de la alarma, pero en el capítulo anterior teníamos una alarma con día, veamos como replicar eso en HTML.

Lo primero que vamos a tener que hacer es tener por cada día de la semana un campo para despertadorPrendido y uno para horaDespertador.

Podemos elegir mostrar todos los días o podemos hacerlo mas simple con un selector del día que queremos ver y manipular.

El resto debería ser como hasta ahora.

Veamos como seria eso:

<div id="despertador-app-3">
  <p v-if="dias[diaSeleccionado].despertadorPrendido"
     class="alert alert-success">Despertador Prendido</p>
  <p v-else
     class="alert alert-danger">Despertador Apagado</p>

  <p class="alert alert-info">Hora Despertador:
    {{dias[diaSeleccionado].horaDespertador}}</p>

  <input v-model.number="dias[diaSeleccionado].horaDespertador"
     type="number" class="form-control" placeholder="Hora Despertador">

  <select v-model="diaSeleccionado" class="custom-select mt-3">
    <option value="lunes">Lunes</option>
    <option value="martes">Martes</option>
    <option value="miercoles">Miércoles</option>
    <option value="jueves">Jueves</option>
    <option value="viernes">Viernes</option>
    <option value="sabado">Sábado</option>
    <option value="domingo">Domingo</option>
  </select>

  <div class="mt-3 text-center">
    <button v-if="despertadorPrendido"
        v-on:click="despertadorPrendido = false"
        class="btn btn-danger">Apagar</button>

    <button v-else
        v-on:click="despertadorPrendido = true"
        class="btn btn-success">Prender</button>
  </div>
</div>

<script>
  new Vue({
    el: '#despertador-app-3',
    data: {
      diaSeleccionado: 'lunes',
      dias: {
        lunes: {
          despertadorPrendido: true,
          horaDespertador: 8
        },
        martes: {
          despertadorPrendido: true,
          horaDespertador: 7
        },
        miercoles: {
          despertadorPrendido: true,
          horaDespertador: 8
        },
        jueves: {
          despertadorPrendido: false,
          horaDespertador: 8
        },
        viernes: {
          despertadorPrendido: true,
          horaDespertador: 7
        },
        sabado: {
          despertadorPrendido: false,
          horaDespertador: 8
        },
        domingo: {
          despertadorPrendido: false,
          horaDespertador: 8
        },
      }
    }
  })
</script>

Resultado:

Despertador Prendido

Despertador Apagado

Hora Despertador: {{dias[diaSeleccionado].horaDespertador}}

El HTML es bastante similar, solo que donde antes teníamos despertadorPrendido ahora tenemos dias[diaSeleccionado].despertadorPrendido y donde antes teníamos horaDespertador ahora tenemos dias[diaSeleccionado].horaDespertador, ya que tenemos que mostrar y cambiar los valores del día seleccionado.

También tenemos un tag select con v-model seteado a diaSeleccionado así muestra el día seleccionado y si elegimos otro valor en el select el valor de diaSeleccionado es actualizado.

Si no te gusta repetir mucho las cosas como yo, notaras que dias[diaSeleccionado] esta por todos lados, es molesto escribirlo, podemos cometer un error si lo escribimos mal y si llegamos a renombrar algo vamos a tener que ir a todos los lugares a actualizarlo.

Seria mas fácil si pudiéramos "nombrar" este pedazo de código a algo mas claro y simple.

Otra cosa que podrás haber notado si tenes una tendencia a intentar romper todo lo que te dan es que se puede poner horas incoherentes, por ejemplo 42 en el campo de horaDespertador, estaría bueno poder evitar eso.

Veamos como podemos solucionar estos problemas:

<div id="despertador-app-4">
  <p v-if ="diaActual.despertadorPrendido"
     class="alert alert-success">Despertador Prendido</p>
  <p v-else
     class="alert alert-danger">Despertador Apagado</p>

  <p class="alert alert-info">Hora Despertador:
    {{diaActual.horaDespertador}}</p>

  <div class="input-group mt-3">
    <div class="input-group-prepend">
      <label class="input-group-text" for="inputGroupSelect01">Cambiar Hora</label>
    </div>
  <input v-model.number="diaActual.horaDespertador"
     type="range" min="0" max="23" class="form-control" placeholder="Hora Despertador">
  </div>

  <div class="input-group mt-3">
    <div class="input-group-prepend">
      <label class="input-group-text" for="inputGroupSelect01">Día</label>
    </div>
    <select v-model="diaSeleccionado" class="custom-select">
      <option value="lunes">Lunes</option>
      <option value="martes">Martes</option>
      <option value="miercoles">Miércoles</option>
      <option value="jueves">Jueves</option>
      <option value="viernes">Viernes</option>
      <option value="sabado">Sábado</option>
      <option value="domingo">Domingo</option>
    </select>
  </div>

  <div class="mt-3 text-center">
    <button v-if="diaActual.despertadorPrendido"
            v-on:click="diaActual.despertadorPrendido = false"
            class="btn btn-danger">Apagar</button>
    <button v-else
            v-on:click="diaActual.despertadorPrendido = true"
            class="btn btn-success">Prender</button>
  </div>
</div>

<script>
  new Vue({
    el: '#despertador-app-4',
    computed: {
      diaActual: function () {
        return this.dias[this.diaSeleccionado];
      }
    },
    data: {
      diaSeleccionado: 'lunes',
      dias: {
        lunes: {
          despertadorPrendido: true,
          horaDespertador: 8
        },
        martes: {
          despertadorPrendido: true,
          horaDespertador: 7
        },
        miercoles: {
          despertadorPrendido: true,
          horaDespertador: 8
        },
        jueves: {
          despertadorPrendido: false,
          horaDespertador: 8
        },
        viernes: {
          despertadorPrendido: true,
          horaDespertador: 7
        },
        sabado: {
          despertadorPrendido: false,
          horaDespertador: 8
        },
        domingo: {
          despertadorPrendido: false,
          horaDespertador: 8
        },
      }
    }
  })
</script>

Resulta en:

Despertador Prendido

Despertador Apagado

Hora Despertador: {{diaActual.horaDespertador}}

Notaras que dias[diaSeleccionado] cambio a diaActual, pero como diaActual no es un valor, sino un valor "calculado" (computed en ingles) en base a otros dos valores dias y diaSeleccionado, no podemos ponerlo en el campo data, para estos datos calculados, vue nos permite especificarlos en el atributo computed, donde cada campo es un nombre y una función que devuelve su valor.

En nuestro caso:

computed: {
  diaActual: function () {
    return this.dias[this.diaSeleccionado];
  }

Ahora podemos decir diaActual en nuestro HTML y vue lo va a reemplazar por el valor de dias[diaSeleccionado].

Notaras que a diferencia del código en v-on:click en el HTML acá cada variable empieza con this., esto es porque en el HTML de vue las únicas variables que podemos acceder son las que están en el campo data y computed de nuestra aplicación, por eso vue nos hace el favor de ponerle this. adelante a cada nombre, cuando pasamos ese código a javascript, el navegador no sabe a que nos estamos refiriendo, puede ser una variable en la función actual, una variable global o un atributo de nuestra aplicación vue, para especificar que nos referimos a un nombre dentro de nuestra aplicación, tenemos que prefijar los nombres con this (que en ingles se traduce a "esto/este" y siempre se refiere al objeto al que pertenece la función que estamos ejecutando.

El otro cambio, mas allá de algunas mejoras estéticas usando clases de bootstrap, es que le cambiamos el tipo al input de number a range y le especificamos dos atributos nuevos min y max, de esta manera el navegador en lugar de mostrar un campo de texto donde se puede escribir cualquier cosa, muestra un "slider" donde solo se puede especificar un valor en el rango valido.

Con eso ya tenemos nuestro despertador con soporte para alarmas por día, en el camino repasamos vuejs y aprendimos sobre campos calculados y el tipo range del tag input.

Mariano Guerra: Creemos en la Web: Calcular o no calcular, esa es la cuestión

En capítulos anteriores vimos un tipo de dato llamado bool o boolean que puede tomar dos valores, verdadero (true) o falso (false), los cuales son el resultado de operaciones lógicas y de comparación.

En el siguiente capitulo vimos como hacer cálculos, ahora vamos a unir estos dos conceptos usando el resultado de comparaciones y operaciones lógicas para decidir que calculamos y que no, para eso vamos a introducir una nueva expresión llamada if, pero antes veamos un ejemplo.

Vas a poner el despertador, si "el día es un día de semana", entonces pones el despertador a las 8, sino lo dejas apagado.

Esto lo podríamos expresar en javascript así:

if (esDiaDeSemana) {
    ponerDespertador(8);
}

Esta es la versión mas simple, en la cual escribimos la palabra clave if, luego entre paréntesis la condición que va a evaluar a verdadero o falso, luego entre llaves las expresiones que queremos evaluar si la condición es verdadera.

Ese ejemplo asume que el despertador esta apagado, por lo que no necesitamos hacer nada si esDiaDeSemana es falso.

Pero que pasa si queremos apagarlo por las dudas este prendido, no queremos despertarnos temprano por accidente un sábado!

Entonces podemos usar otra palabra clave, veamos un ejemplo:

if (esDiaDeSemana) {
    ponerDespertador(8);
} else {
    apagarDespertadorSiEstaPrendido();
}

En este caso, después de la llave de cierre escribimos la palabra clave else que significa "sino" y entre llaves las expresiones a evaluar si la condición en el if es falsa.

Podemos imaginar que el código de apagarDespertadorSiEstaPrendido es algo como:

if (despertadorPrendido) {
    apagarDespertador();
}

Con esto podemos hacer muchísimas cosas, pero si tu semana es variada y tenes que despertarte a horas distintas según el día, vas a necesitar tener mas de una condición, veamos como seria eso uniendo las dos palabras claves que ya aprendimos y pongamos todo en una función.

function configurarDespertador(diaDeSemana) {
    if (diaDeSemana === "lunes" || diaDeSemana === "miércoles") {
        ponerDespertador(8);
    } else if (diaDeSemana === "martes" || diaDeSemana === "viernes") {
        ponerDespertador(7);
    } else {
        apagarDespertadorSiEstaPrendido();
    }
}

Antes de describir la función por completo, notar que para tener una segunda condición a comprobar después del primer if, escribimos la palabra clave else seguida de la palabra clave if, esto se leería algo así como:

si (condicion1) entonces
    ejecutar bloque1
sino si (condicion2) entonces
    ejecutar bloque2
sino
    ejecutar bloque3.

Definimos la función configurarDespertador que recibe como parámetro el diaDeSemana, y con el tenemos 3 bloques que podemos evaluar:

  • Si es lunes o miércoles: despertador a las 8
  • Si es martes o viernes: despertador a las 7
  • sino, asegurarse que el despertador este apagado

Si te fijas, el despertador no va a sonar los fines de semana ni el jueves, cuando escribimos condiciones complejas o encadenadas hay que fijarse bien que estamos probando todos los casos.

Para poder probar esto con código completo y ver si tomamos la decisión correcta vamos a aprender dos formas de mostrar al usuario de nuestra aplicación el resultado.

Mostrando texto en la consola de desarrolladores con console.log.

Durante el desarrollo nos puede pasar que queremos saber por donde se ejecuto el código o cual es el valor de una variable, para eso podemos usar el objeto console que tiene algunas funciones útiles, la mas útil de ellas es la función log.

Abrí una pagina cualquiera, abrí las herramientas de desarrollo (usualmente F12 la abre), abrí el tab "Consola" (o nombre similar) si no esta abierto en ese, si ves muchas cosas ahí hace click en el icono del tacho de basura para limpiar la consola y escribí:

console.log("hola");

Y apretá enter, deberías ver que la linea siguiente dice "hola", probemos algunos otros ejemplos:

let numero = 42;
console.log(numero);
console.log("numero: " + numero);
console.log("numero", numero);
console.log("numero", numero, true, null, "hola");

A mi me quedo así:

/galleries/cew/if-else/console-log.png

Podrás notar que console.log recibe tantos parámetros como deseemos y los muestra a todos, no hay necesidad de juntarlos todos en un solo valor de tipo texto.

El objeto console tiene otras funciones útiles, proba alguno de los ejemplos anteriores reemplazando la función log con warn, error, info.

console.warn(numero);
console.error("numero: " + numero);
console.info("numero", numero);

Con esto podemos "simular el despertador", sin tener que escribir todo ahora, solo vamos a imprimir que haría.

Pero antes de ir a eso, vamos a ver otra forma de mostrar información, que si bien es simple y medio molesta es una buena herramienta cuando estamos empezando un proyecto.

Como existe el objeto console, que tiene varias funciones relacionadas a la consola de desarrollo, existe el objeto window que tiene funciones relacionadas a la ventana donde esta nuestra pagina, la cantidad de funciones que tiene es impresionante, pero por ahora nos vamos a enfocar en solo tres, la función alert, que nos permite mostrar un mensaje al usuario, la función confirm que nos permite mostrar un mensaje al usuario y el usuario puede responder el clásico "OK" o "Cancelar" y la función prompt que nos permite preguntar algo y el usuario puede responder con texto o cancelar.

Empecemos por la mas fácil, alert:

window.alert("hola");
let numero = 42;
window.alert("numero: " + numero);
/galleries/cew/if-else/alert.png

A diferencia de console.log, window.alert recibe un solo parámetro, si queremos mostrar el valor de múltiples variables tenemos que juntarlas en un solo valor de tipo texto.

Veamos window.confirm:

let respuesta;
// responde una de las dos opciones
respuesta = window.confirm("Seguir?");
console.log('Respuesta', respuesta);

// responde la otra
respuesta = window.confirm("Seguir?");
console.log('Respuesta', respuesta);
/galleries/cew/if-else/confirm.png

la función devuelve true si se selecciono "OK" y false si se selecciono "Cancel".

Por ultimo window.prompt, el cual recibe dos parámetros, el primero es el mensaje a mostrar, el segundo es el valor por defecto para el campo de texto, si no lo especificamos empieza con el texto vació.

// selecciona "Cancel"
respuesta = window.prompt("Día de Semana", "lunes");
console.log('Respuesta', respuesta);

// selecciona "OK"
respuesta = window.prompt("Día de Semana", "lunes");
console.log('Respuesta', respuesta);
/galleries/cew/if-else/prompt.png

La función devuelve null si se selecciono "Cancel" y el texto en el campo de texto si se selecciono "OK".

Probemos combinándolas un poco:

let r1 = window.confirm("Seguir?");
if (r1) {
    window.alert(":)");
} else {
    window.alert(":(");
}

El código te va a preguntar si querés seguir, si respondes OK va a mostrar :), si respondes cancel va a mostrar :(.

Una nota por si estas probando y te da este error o algo parecido:

SyntaxError: redeclaration of let nombreDeVariableAca

Eso es porque declaraste dos veces la misma variable, las variables se declaran una sola vez por función con let, acá estamos en la consola, así que es como una función eterna que ejecuta cada linea que le damos, así que solo tenemos que declarar la variable una vez, después simplemente se siguen usando, sin tener que declararla de nuevo.

Veamos como usaríamos la función window.prompt para configurar nuestro despertador:

let r2;
r2 = window.prompt("Día", "lunes");
if (r2 === null) {
    window.alert("Acción cancelada");
} else {
    configurarDespertador(r2);
}

Acá estamos llamando a configurarDespertador, que definimos mas arriba solo si el usuario ingreso un día y apretó OK.

La función configurarDespertador llama a un par de funciones que todavía no definimos, así que si probas este código te va a dar un error diciendo que apagarDespertadorSiEstaPrendido o ponerDespertador no están definidas, por ahora vamos a usar window.alert y una variables globales para simular el despertador.

Acá va todo el código junto.

// una variable global (fuera de las funciones) para saber si el despertador
// esta prendido o no, inicializado a false, indicando que esta apagado
let despertadorPrendido = false,
    // variable global para saber a que hora esta puesta la alarma
    horaDespertador = 0;

function preguntarDiaYPonerDespertador() {
    let respuesta = window.prompt("Día", "lunes");

    if (respuesta === null) {
        window.alert("Acción cancelada");
    } else {
        configurarDespertador(respuesta);
    }
}

function configurarDespertador(diaDeSemana) {
    if (diaDeSemana === "lunes" || diaDeSemana === "miércoles") {
        ponerDespertador(8);
    } else if (diaDeSemana === "martes" || diaDeSemana === "viernes") {
        ponerDespertador(7);
    } else {
        apagarDespertadorSiEstaPrendido();
    }
}

function prenderDespertadorSiEstaApagado() {
    // si no esta prendido
    if (!despertadorPrendido) {
        // lo prendemos
        despertadorPrendido = true;
        console.log('despertador prendido');
    } else {
         console.log('despertador ya estaba prendido');
    }
}

function apagarDespertadorSiEstaPrendido() {
    // si esta prendido
    if (despertadorPrendido) {
        // lo apagamos
        despertadorPrendido = false;
        console.log('despertador apagado');
    } else {
        console.log('despertador ya estaba apagado');
    }
}

function ponerDespertador(hora) {
    prenderDespertadorSiEstaApagado();
    console.log('nueva hora para despertador', hora);
    horaDespertador = hora;
}

Te recomiendo que escribas el código en lugar de copiar y pegarlo, de esa forma te vas a ir acostumbrando a escribir los paréntesis, llaves y puntos y coma en su lugar y a entender los errores cuando te equivocaste en algo.

Luego de declarar las variables y funciones llama preguntarDiaYPonerDespertador(); y proba con distintos días varias veces viendo que se imprime en la consola.

Como ejercicio queda comprobar que el día ingresado es un día valido, sino informarlo con window.alert y no intentar configurar el despertador.

Como ayuda, te recomiendo que declares una nueva función esDiaValido, que uses if y else if para comprobar que el día pasado como parámetro es un día valido, si lo es devolvé true (return true;), en el else devolvé false (return false;).

Facundo Batista: PyCon Argentina 2018

Estas últimas semanas vengo a recontrafull con la organización de la PyCon Argentina 2018. Más allá de un comienzo lento hace unos meses, por un problema interno de la organización, se formó un equipo que está laburando un montón.

La conferencia se va a realizar en el Centro Cultural General San Martín, en la Ciudad de Buenos Aires, del jueves 22 al sábado 24 de Noviembre, más alguna actividad social el domingo 25 (nos vamos de Pycnic!).

¡Registrate y vení que es gratis y va a estar buenísima!

El logo de la conferencia

El jueves van a haber varios talleres, entre los que se destaca un Django Girls hecho y derecho. Viernes y sábados es más de "conferencia clásica", pero van a haber otras actividades como espacios abiertos para que cualquiera hable del tema que quiere, zonas de trabajo, charlas rápidas, y algunas cositas más que describo mejor acá abajo.

Con respecto a las charlas, ya están elegidas y confirmadas: en la página principal del sitio ya se pueden ver cuales quedaron. La agenda detallada con los días y horarios de cada una (como así también de los talleres) va a estar en unos días. Destaco que tenemos dos keynotes internacionales: Carol Willing, core developer de Jupyter y ex directora de la PSF, y Brandon Rhodes, programador Python en Dropbox y organizador de la PyCon USA en Portland 2016 y 2017.

También vamos a hacer una muestra de fotografía con las mejores fotos que la gente haya sacado alguna vez en un evento de PyAr (esto es, si la gente propone fotos! Este es el formulario al respecto).

Ya pusimos a la venta las remeras de la conferencia! Las estamos vendiendo al costo, así que tenemos que vender todas las que compramos para no perder plata con eso.. así que comprate una y regalale otra a alguien más :) Y aprovechá que están con descuento por algunos días. Ah, fijate que hay corte hombre y corte mujer, y desde XS a XXXL, ¡no hay excusas!

La remera de la conferencia

Hablando de no tener excusas, no sólo vamos a tener becas para ayudar a que la gente viaje (prontito sale el anuncio) sino que también la Asociación Civil Python Argentina hizo un acuerdo con la Fundación Flechabus y los socios van a poder comprar pasajes con un 40% de descuento.

Los asistentes a la conferencia también van a poder participar de una actividad que realizamos por primera vez (que copiamos de otros eventos): el "speed interview", que es una actividad ideal si estás buscando trabajo, o incluso si estás con ganas de saber qué ofertas hay aunque no tengas nada en mente. En una hora podés charlar con gente de una decena de empresas distintas, a razón de cinco minutos cada una, y si hay algo que te interese puntualmente te queda el contacto para seguir charlando luego.

Finalmente, algo en lo que estamos innovando es un espacio para que madres y padres que tienen que cuidar a sus hijes puedan asistir a las charlas sin mayor preocupación: van a poder dejarles al cuidado de personal especializado en las mismas instalaciones de la conferencia, a pocos metros de donde se desarrollan las charlas y otras actividades, en un ámbito cuidado y con la estructura necesaria para que no haya ningún problema. Hay que anotarse con la mayor anticipación posible, así podemos dimensionarlo correctamente, este es el form con toda la info.

Creo que esas son los principales puntos de la conferencia. ¡Va a estar genial! Ya voy a ir tirando otras novedades en las pocas semanas que falta...

Facundo Batista: Por la educación pública gratuita, laica, y de calidad

Hoy no se hace una revolución tomando por la fuerza, violentamente, el palacio de invierno y derrocando zares, porque, además de que la violencia provoca violencia y a la larga casi nada más, no hay más zares ni existe un poder concentrado en ningún palacio. El cambio social profundo, inclusivo, la revolución del siglo XXI se hace apoderándose del conocimiento, que la elite se empeña en monopolizar. La persona que carece de conocimiento está destinada a ser subalternizada en la sociedad actual, al igual que la nación que carece de desarrollo científico lo está en el concierto mundial. Las elites saben que financiar universidades nacionales es serruchar la rama en que están sentados. Por eso, tienen miedo.

Les recomiendo este genial artículo de opinión: El miedo a la revolución del saber, por Eugenio Raul Zaffaroni, Juez integrante de la Comisión Interamericana de Derechos Humanos, ex integrante de la Corte Suprema de la Nación, y Profesor emérito de la Universidad de Buenos Aires... en general, un tipo al que me encanta leer.