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.

Facundo Batista: Trabajando en Bruselas

La semana pasada estuve de sprint, laburando en Bruselas, Bélgica.

No era la primera vez que iba, pero la vez pasada (que fue medio reunión de trabajo, medio UDS) todo lo que fue hospedaje y evento ocurrió en un hotel en el medio de un bosque lejos del centro. Una hermosura, pero lo que es "conocer Bruselas", no conocí casi nada.

Esta vez nos hospedamos y trabajamos en un hotel céntrico, y como el horario era de 8:30 a 17:30 y estábamos cerrando el verano, se podía salir a pegar una vuelta habiendo todavía luz luego de cada día de trabajo.

Jeanneke Pis

Por otro lado no tuve tiempos largos para ir de visita lejos de la ciudad, ya que llegué el domingo durante la tarde tarde y me fui el sábado pasado el mediodía.

Igual, uno sale, pasea, saca fotos.

El clima también acompañó porque hicieron un par de grados más de lo normal para la época, así que estuvo fresquito lindo, e incluso un día estuve de cortos. El sábado nos llovió un poco a Guillo y a mí justo cuando estábamos pegando una vuelta, pero no tanto como para molestar realmente.

Het Zinneke

La ciudad es linda, limpia, "europea vieja" pero bien. Estaba todo un poco roto porque estaban trabajando en un par de avenidas y eso hacía que la mitad de los caminos estuvieran con cascotes, zonas de construcción y eso, pero nada que moleste demasiado.

Si uno se guía por los locales que encuentra en la ciudad, el 50% del PBI de Bruselas se debe explicar por los chocolates, y el otro 50% por las cervezas... Mucha cervecería, alguna con más onda, otra con menos onda. ¿Recomendaciones? A La Mort Subite (un café muy viejo con toda la onda, comida decente y cervezas locales) y BrewDog (buena cerveza, comida normal para una cervecería (pobre, digamos, aunque la hamburguesa "chipotle chorizo" estaba catorce puntos), y buen ambiente y música).

Nota aparte sobre BrewDog, recién buscando la URL para poner acá vi que historia loca y buena filosofía tiene esta cadena de bares, super interesante, y que seguro algo tiene que ver con la onda que tiene el lugar.

Ah, y traten de probar cervezas trapenses. Todo un estilo, interesante y ricas.

Manneken Pis

A nivel de laburo también estuvo muy bien la semana.

Intra-equipo (somos 16, deberíamos ser más) tenemos una modalidad para estas reuniones mezcla de Trello y Google Docs que funciona muy muy bien. Y en esta oportunidad se formalizó que durante la mañana cada equipo trabajaba entre sí, y luego del almuerzo se metían todas las reuniones "cross-team". Eso da un espacio sin interrupciones que cada equipo (o al menos nosotros) puede aprovechar muy bien.

Lo malo a nivel laboral es que pasamos de un sistema "per-diem", donde te asignan un monto de dinero para que gastes en cenas, almuerzos o desayunos, y después vos gastás más, menos, te preocupás o no, etc, a un sistema "rendir los tickets", con lo cual para cada gasto relacionado con las comidas tenés que acordarte de pedir el (los) tickets, sacarles una foto con el teléfono, meterlos en la app de reportar gastos, etc.

En fin. En general, una semana que estuvo muy bien, con la excepción de Moni en casa que tuvo extra complicado el tema de los horarios de los niños, pero no podía hacer mucho yo por eso. Por suerte, es el último viaje por laburo con Male en el jardín, desde el año que viene es todo más simple.