Maratón de Nueva York 2013, preparación y desafios (I). A una semana de la carrera.

La calma antes de la tormenta

Sólo faltan 7 días antes de el Maratón de Nueva York. Estoy escribiendo antes de ir a mi última corrida larga de 8 millas para luego bajar drásticamente la distancia la semana que viene.

Ha sido toda una aventura.

El 13 de Octubre corrí 20 millas en preparación al Maratón de Nueva York de 2013. Mi entrenamiento no ha sido el que hubiera querido ya que me fracturé el pie izquierdo por sobre-esfuerzo, dos semanas después de terminar el medio Maratón de Nueva York del 2013. Para ser sincero, este ha sido un proyecto de más de un año cuando empece a ganarme mi cupo con el programa 9+1 hasta la cancelación de mis  última carreras favoritas, el  Dash To The Finish Line (y luego de inmediato el Maratón del 2013  gracias a una tomenta mostruosa llamada Sandy).

 

No es que no haya corrido tanto antes; Hasta el momento llevo 3 medio maratones en mi bolsillo (2 con resultados oficiales y con tiempos no tan malos) y un montón de carreras más cortas, pero nada se le acerca en magnitud a un maratón. El cuerpo simplemente se queda sin reservas de energía cuando corres por tanto tiempo, además de que debes mantener tu concentración y foco por lo menos 4 horas (si eres un corredor rápido es menos).

Pero la parte más difícil es el acondicionamiento mental. Esa parte de seguir repitiendole al cerebro que tienes que seguir adelante, no importa que.

Lesiones, lesiones (O como no correr más de 13.1 millas en Vibrans 5 Fingers)

Untitled

Acute non displaced stress fracture involving the second metatarsal bone. Asi me lo recitó el doctor…

 

Mi exceso de confianza me llevo a pensar que podría correr un maratón completo en esos zapatos. No me malentiendan, hay gente que puede hacerlo pero yo no soy uno de ellos. Mi pie izquierdo simplemente no pudo más al principio de este año y en el doctor termino de dar su diagnostico:

Si quieres volver a correr tienes que descansar 8 semanas. O puedes correr con la fractura y pasar a la sala de operaciones de una vez.

El doctor me regaño y me repitió el discurso de como su consultorio se habia llenado de gente con este tipo de fracturas desde que los zapatos habían salido en venta; Sin embargo el fué pragmático y no culpó a los zapatos sino a la falta de entrenamiento propio en los mismos.

La explicación fué muy sencilla: Al correr distancias más y más largas, el atleta pierde su buena forma al correr y los músculos ya no amortiguan tanto el impacto. Los zapatos «mínimos» no tienen nada de amortiguamiento así que todo el impacto va hacia los huesos. Una y otra vez.

La fractura llego como una bendición disfrazada, ya que tuve que cambiar el plan entrenamiento (correr menos y más intenso), además de que comencé a nadar en la piscina, sólo para descubrir que no lo se hacer bien en estilo libre (o ningún estilo 🙂).

Si, 8 semanas fuera o lo que es lo mismo nada de correr desde el 2 de Marzo del 2013 hasta el 31 de Junio del mismo año. Y de allí empezar a entrenar, casi desde cero hasta el 3 de Noviembre, fecha del maratón.

Entrenamiento y recuperación después de una fractura por sobre-uso


Entrenar en la piscina salvó parte de mi capacidad aeróbica, pero sobre todo me mantuvo cuerdo mientras no podía correr…

La clave de la recuperación era comenzar a subir la distancia poco a poco. Sin embargo yo no tenía mucho tiempo y la mayoría de los planes (incluso el de principiante de Hal Higdon), te pedían correr al menos 3 veces por semana, incluyendo los largos del fin de semana.

¿Era hora de cancelar el maratón? En las próximas semanas lo iba averiguar.

Re-descubrí la piscina por accidente. Allí una de las instructoras (la cual ha participado al menos dos veces en el Iron Man de Kona) me dijo lo mal que lo estaba haciendo. Es una de las mejores cosas que me han pasado ya que descubrí lo mucho que tengo que mejorar al nadar en estilo libre.

Al mismo tiempo la entrenadora me sugirió que me incorporara al programa de «Masters Introduction» del Chelsea Pears (es el lugar en donde hago ejercicio de resistencia y natación), por lo menos 3 veces a la semana. Yo le expliqué que estaba decidido a correr el maratón así tuviera que arrastrarme, por lo que ella me enseño como hacer «deep water running» para mantener el nivel aeróbico mientras al mismo tiempo yo hacia ejercicios con pesas (nada de impacto en las piernas).

A la final me quede con un programa diseñado por el Runners World Smart Coach, de 16 semanas, el cual incluía Tempo Runs, Speed Work y las indispensables carreras larga el fin de semana (long runs). Corriendo solamente dos veces por semana eso me daba suficiente tiempo para subir la distancia de manera segura (no más de %10 cada semana) mientras yo continuaba con la piscina (menos veces ahora) y con los ejercicios de pesas para todo el cuerpo:

Macintosh:Documents josevnz$ egrep SUMMARY full_nyc.ical
SUMMARY:Easy Run 3 Mi @9:06
SUMMARY:Easy Run 3 Mi @9:07
SUMMARY:Easy Run 4 Mi @9:07
SUMMARY:Easy Run 4 Mi @9:06
SUMMARY:Easy Run 4 Mi @9:06
SUMMARY:Easy Run 3 Mi @9:06
SUMMARY:Easy Run 4 Mi @9:06
SUMMARY:Easy Run 5 Mi @9:06
SUMMARY:Easy Run 5 Mi @9:06
SUMMARY:Speedwork 5 Mi, inc Warm; 2×1600 in 7:11 w/800 jogs; Cool
SUMMARY:Long Run 6 Mi @9:05
SUMMARY:Tempo Run 5 Mi, inc Warm; 2 Mi @ 7:31; Cool
SUMMARY:Long Run 7 Mi @9:04
SUMMARY:Easy Run 5 Mi @9:02
SUMMARY:Easy Run 5 Mi @9:02
SUMMARY:Tempo Run 5 Mi, inc Warm; 2 Mi @ 7:29; Cool
SUMMARY:Long Run 8 Mi @9:02
SUMMARY:Speedwork 5 Mi, inc Warm; 2×1600 in 7:07 w/800 jogs; Cool
SUMMARY:Long Run 9 Mi @9:01
SUMMARY:Tempo Run 5 Mi, inc Warm; 2 Mi @ 7:27; Cool
SUMMARY:Long Run 10 Mi @8:59
SUMMARY:Easy Run 6 Mi @8:58
SUMMARY:Easy Run 6 Mi @8:58
SUMMARY:Tempo Run 4 Mi, inc Warm; 2 Mi @ 7:25; Cool
SUMMARY:Long Run 12 Mi @8:58
SUMMARY:Speedwork 4 Mi, inc Warm; 2×1600 in 7:03 w/800 jogs; Cool
SUMMARY:Long Run 14 Mi @8:56
SUMMARY:Tempo Run 5 Mi, inc Warm; 3 Mi @ 7:27; Cool
SUMMARY:Long Run 16 Mi @8:55
SUMMARY:Tempo Run 5 Mi, inc Warm; 3 Mi @ 7:26; Cool
SUMMARY:Long Run 18 Mi @8:53
SUMMARY:Speedwork 4 Mi, inc Warm; 2×1600 in 6:59 w/800 jogs; Cool
SUMMARY:Long Run 12 Mi @8:52
SUMMARY:Tempo Run 4 Mi, inc Warm; 2 Mi @ 7:19; Cool
SUMMARY:Long Run 8 Mi @8:51
SUMMARY:Easy Run 2 Mi @8:50
SUMMARY:Speedwork 3 Mi, inc Warm; 1×1600 in 6:58 w/800 jogs; Cool
SUMMARY:Easy Run 2 Mi @8:50
SUMMARY:Marathon 26.2 Mi @8:01 Time: 3:30:15

Calzado, ropa y nutrición

Jugo de remolacha antes de correr, proteína Whey después de correr y levantar pesas, Caseina en forma de Yogurt Griego en las noches, más carbohidratos complejos en el almuerzo,  una dieta no tan complicada. Compresión para ayudar en la recuperación. Pero el cambio más importante fueron los zapatos, esta vez me fui con unos Altra Zero Drop, los cuales si bien no son mínimos tampoco tienen suela exageradamente grande y están diseñados para darle mayor movilidad a los dedos mientras se corre.

No he tenido ningún problema con ellos, altamente recomendados (lo más que he corrido con ellos son 20 millas, también he hecho carreras cortas con los mismos).

Finalmente decidí utilizar la compresión para las piernas y las pantorrillas para ayudar en la recuperación (sentí una diferencia notable en el tiempo de recuperación, al menos un día menos de molestias).

 

 ¿Que viene después?

En el siguiente articulo les voy a dar detalles de como me pueden seguir el día de la carrera y otra faceta importante del entrenamiento, ¡la preparación mental!

 

 

NYC Half Maraton 2013

Untitled
Mañana Domingo 17 de Marzo voy a correr 13.1 millas en una carrera de gran popularidad en New York

Bueno, mañana me toca correr 13.1 millas en compañía de otros 15 mil corredores, muchísimos de ellos provenientes de otros países. Yo voy a representar a Venezuela lo mejor que puedo, he estado entrenando desde Diciembre del 2012 y no les voy a negar que ha sido difícil en los últimos dos meses; El invierno ha sido duro pero todo el esfuerzo va a tener fruto mañana a las 7:30 de la mañana.

Siga leyendo, más abajo les doy algunos detalles entre ellos del entrenamiento, motivación y como pueden seguir el progreso de esta carrera por Internet.

¿Cual es mi motivación para correr 22 kilómetros?

Untitled¿Porqué corremos?

Para mi este es un peldaño más hacia el Maratón de NYC del 2013. Si no puedo con esta carrera entonces tengo que pensar en como mejorar mi entrenamiento. También está el ejemplo que quiero dejarle a mis hijos, todo se puede con constancia y trabajo.

Esta carrera es algo que quiero compartir con mi familia. Ellos son mi principal soporte en esta aventura y sin ellos no hubiera podido dar un paso en la calle en estos últimos 4 meses de invierno.

El entrenamiento

Untitled
El entrenamiento no fué fácil. Cometí errores y aprendí lo mejor que pude de ellos

Este año decidí salirme del entrenamiento de Hal Higdon (el cual utilicé para mi previo medio maratón) y decidí utilizar el entrenado virtual de Runners World (Smart Coach). El plan fué exigente y pese a que perdí un par de sesiones por enfermedad (3 días en el hospital debido a una comida contaminada) y mal tiempo con mucha nieve, logré cumplir con las pautas. En los días que no corría practique entrenamiento de resistencia con pesos, siguiendo planes de bodybuilding.com (en particular el 6-12-25 me ayudo mucho).

El plan de Runners World fué complejo, no sólo incluyó las carreras largas del fin de semana sino que además me obligo a hacer ‘Tempo Runs‘ y ‘Speed Work‘. Según ellos espero completar el recorrido de la siguiente manera: 13.1 Mi @8:02. Tiempo: 1:45:17

Herramientas, aditamentos

Untitled
Para correr en el frio hay que vestirse en capas. Eso sin contar con otros aditamentos para registrar el entrenamiento

Siempre mantuve una bitacora de eventos en donde registré mi velocidad, datos geográficos (usando el GPS de mi Nike GPS Sports Watch) y ritmo cardíaco (con un sensor Polar compatible con el reloj). Toda la información quedo registrada en el sitio de Nike+ el cual ha mejorado bastante desde su última actualización el año pasado.

Aún sigo utilizando Vibrams 5 Fingers para correr. Compré unos Bikila y los clásicos Komodo, los Bikilas aún me lastiman los pies por lo que creo yo es un error de diseño en las correas. Así que seguiré usando lo que funciona, Komodos.

Alimentación y descanso

La alimentación incluyó más carbohidratos además de las proteínas después de entrenar. No quiero recomendar ninguna marca así que si estan interesados en saber que suplementos tomé me pueden contactar en privado (asegúrense de poner una dirección de correo electrónico valida en los comentarios). Como siempre, trato de comer balanceado (más difícil de lo que usted cree), tomo mis suplementos de vitaminas y las pastillas de aceite de pescado (para completar mi dosis de Omega-6).

Lo más importante es comer bien, natural y descansar cuando el cuerpo lo pida. Durante todo este tiempo siempre me tomé un día a la semana durante el cual no hice nada.

La carrera

Screen Shot 2013-03-16 at 3.28.38 PM
El recorrido de este año. Muy colorido

¡Apoyo moral!

Me encantaría que estuvieran conmigo para compartir los detalles de la carrera, pero como se que no es posible entonces les tengo unos detalles de como hacerlo remotamente.

No hay excusa para que no me sigan el día de la carrera, yo soy el BIB# 7466 🙂

Muchas opciones. Hay una aplicación para el Iphone, aunque también lo pueden hacer por Internet. Además de eso la carrera va a ser transmitida en vivo en ESPN3 y ABC..

Ya les escribiré en los comentarios como me fué, ahora sólo queda esperar y relajarse antes del evento.

–José

Nike+ REST API

Last year I started playing with the Nike+ website; So Nike finally decided to release a proper API to interact with their website. The API is small yet looks functional, but I am having a hard time trying to figure out how to reproduce the results from a simple REST client:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Macintosh:python josevnz$ ./nikeplus_unit_test.py 
send: 'GET /me/sport?access_token=XXXXXX HTTP/1.1\r\nHost: api.nike.com:443\r\nAccept-Encoding: identity\r\nContent-Type: application/jsonrequest\r\nAccept: application/jsonrequest\r\nAppid: Kodegeek/NikePlus\r\n\r\n'
E
======================================================================
ERROR: test_getAggregateSportData (__main__.TestNikePlus)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "./nikeplus_unit_test.py", line 16, in test_getAggregateSportData
    activities = activity.getAggregateSportData(True)
  File "/Users/josevnz/Documents/src/python/com/kodegeek/fitness/nikeplus/activity/wrapper.py", line 30, in getAggregateSportData
    res = con.getresponse()
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 1013, in getresponse
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 402, in begin
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/httplib.py", line 360, in _read_status
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 430, in readline
error: [Errno 54] Connection reset by peer
 
----------------------------------------------------------------------
Ran 1 test in 0.570s
 
FAILED (errors=1)

Contacted the developers, lets see how it goes. But it is exiting, so many possibilities!

Creando entradas para mi calendario en Ical usando Python

Runners World Half Marathon training plan
Runners World Half Marathon training plan

Para quienes siguen esta bitácora, seguramente recordarán que me aceptaron en el NYC Half Marathon del 2013. Eso significa que me va a tocar entrenar este inverno, en esta ocasión quise probar los planes gratuitos de la gente de Runners World para ver que tan buenos son.

Después de registrarme y de colocar varios parámetros, esto fué más o menos lo que el plan me generó:

El plan básico de Runners World Smart Coach no soporta enviar correos electrónicos para recordarte acerca de tus entrenamientos; Esto es algo que puedo hacer yo sólo y no creo que justifique que pagué por la mejora al servicio pago, dado lo corto de la carrera (para el maratón es otra cosa y allí les recomiendo que paguen su suscripción).

Para tener mis recordatorios lo único que tengo que hacer es guardar el plan en formato de texto plano, simplemente seleccionamos la tabla del sitio web y la guardamos en un archivo de texto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#Macintosh:Documents josevnz$ vim half.txt
 
WEEK 1: 4 Mi
Sun     Dec 30  Long Run        Dist: 4 Mi @9:35
 
WEEK 2: 11 Mi
Tue     Jan 1   Easy Run        Dist: 4 Mi @9:36
Thu     Jan 3   Easy Run        Dist: 3 Mi @9:36
Sun     Jan 6   Easy Run        Dist: 4 Mi @9:36
 
WEEK 3: 12 Mi
Tue     Jan 8   Easy Run        Dist: 3 Mi @9:35
Thu     Jan 10  Tempo Run       Dist: 4 Mi, inc Warm; 2 Mi @ 7:59; Cool
Sun     Jan 13  Long Run        Dist: 5 Mi @9:35
 
WEEK 4: 10 Mi
Tue     Jan 15  Easy Run        Dist: 3 Mi @9:32
Thu     Jan 17  Easy Run        Dist: 3 Mi @9:32
Sun     Jan 20  Easy Run        Dist: 4 Mi @9:32
 
WEEK 5: 13 Mi
Tue     Jan 22  Easy Run        Dist: 4 Mi @9:32
Thu     Jan 24  Tempo Run       Dist: 4 Mi, inc Warm; 2 Mi @ 7:57; Cool
Sun     Jan 27  Long Run        Dist: 5 Mi @9:32
 
WEEK 6: 14 Mi
Tue     Jan 29  Easy Run        Dist: 4 Mi @9:30
Thu     Jan 31  Speedwork       Dist: 4 Mi, inc Warm; 2x1600 in 7:32 w/800 jogs; Cool
Sun     Feb 3   Long Run        Dist: 6 Mi @9:30
 
WEEK 7: 15 Mi
Tue     Feb 5   Easy Run        Dist: 3 Mi @9:27
Thu     Feb 7   Tempo Run       Dist: 4 Mi, inc Warm; 2 Mi @ 7:52; Cool
Fri     Feb 8   Easy Run        Dist: 2 Mi @9:27
Sun     Feb 10  Long Run        Dist: 6 Mi @9:27
 
WEEK 8: 13 Mi
Tue     Feb 12  Easy Run        Dist: 3 Mi @9:25
Thu     Feb 14  Easy Run        Dist: 3 Mi @9:25
Fri     Feb 15  Easy Run        Dist: 3 Mi @9:25
Sun     Feb 17  Easy Run        Dist: 4 Mi @9:25
 
WEEK 9: 17 Mi
Tue     Feb 19  Easy Run        Dist: 2 Mi @9:25
Thu     Feb 21  Tempo Run       Dist: 5 Mi, inc Warm; 3 Mi @ 7:54; Cool
Fri     Feb 22  Easy Run        Dist: 2 Mi @9:25
Sun     Feb 24  Long Run        Dist: 8 Mi @9:25
 
WEEK 10: 18 Mi
Tue     Feb 26  Easy Run        Dist: 3 Mi @9:22
Thu     Feb 28  Speedwork       Dist: 4 Mi, inc Warm; 2x1600 in 7:25 w/800 jogs; Cool
Fri     Mar 1   Easy Run        Dist: 2 Mi @9:22
Sun     Mar 3   Long Run        Dist: 9 Mi @9:22
 
WEEK 11: 19 Mi
Tue     Mar 5   Easy Run        Dist: 2 Mi @9:20
Thu     Mar 7   Tempo Run       Dist: 5 Mi, inc Warm; 3 Mi @ 7:50; Cool
Fri     Mar 8   Easy Run        Dist: 2 Mi @9:20
Sun     Mar 10  Long Run        Dist: 10 Mi @9:20
 
WEEK 12: 18 Mi
Tue     Mar 12  Easy Run        Dist: 2 Mi @9:17
Thu     Mar 14  Speedwork       Dist: 3 Mi, inc Warm; 1x1600 in 7:21 w/800 jogs; Cool
Sun     Mar 17  Half Marathon Race Day  13.1 Mi @8:02 Time: 1:45:17

El formato es super fácil de digerir; Con un poco de ayuda para entender el formato de iCalendar, escribí un pequeño script en Python el cual toma el archivo de texto y lo convierte al formato adecuado:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#!/usr/bin/env python
# Simple script to parse output from the Runners World Smart Coach calendar to convert it to Ical entries. PLEASE CONSIDER BUYING THEIR PLAN, IS WORTH IT
# http://kodegeek.com/blog
import os, sys, re
from datetime import *
 
class Cal:
        def __init__(self, params):
                self.params = params
 
        def __str__(self):
                return '''BEGIN:VEVENT
DTEND;TZID=%(date)s
TRANSP:OPAQUE
SUMMARY:%(summary)s
DTSTART;TZID=%(date)s
SEQUENCE:4
BEGIN:VALARM
X-WR-ALARMUID:454BC86C-A39E-4C87-8CF9-7D79D80AC01B
TRIGGER:-PT1M
ATTACH;VALUE=URI:Basso
ACTION:AUDIO
END:VALARM
END:VEVENT''' % self.params
 
(Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec) = range(0, 12)
 
def parseLine(line, now, format):
        if len(line) == 1 or re.search('WEEK', line):
                return None
        ''' Parsing the data. Each line format looks like this:
        Sun     Dec 30  Long Run        Dist: 4 Mi @9:35
        Tue     Jan 29  Easy Run        Dist: 4 Mi @9:30
        Thu     Jan 31  Speedwork       Dist: 4 Mi, inc Warm; 2x1600 in 7:32 w/800 jogs; Cool
        Sun     Mar 17  Half Marathon Race Day  13.1 Mi @8:02 Time: 1:45:17
        '''
        matcher = re.search('(.*)\s+Dist:(.*)', line.strip())
        if matcher == None:
                matcher = re.search('(.*)\s+Race Day(.*)', line.strip())
                if matcher == None:
                        return None # Don't know how to handle this!
        params = {}
        # Runs finish and end the same day
        tokens = matcher.group(1).strip().split(' ', -1)
        cdate = ' '.join(tokens[:3]).strip()
        time = "05:00"
        if tokens[0] in [ "Sun", "Sat" ]:
                time = "06:00"
        # We add the year and start time to the date. Start time is the time I want to run
        # We figure out the year as none of these plans are longer than 13 weeks
        year = 2013 # Yes, it is hardcoded. This version of the script doesn't handle the special case of dates across years
 
        # For date we expect something like: Sun Dec 30 2012 05:00
        date = "%s %s %s" % (cdate, year, time)
        ndate = datetime.strptime(date, "%a %b %d %Y %H:%M")
        if ndate == None:
                raise Exception("Unable to parse date, won't continue: '%s'" % date)
        params['date'] = ndate.strftime(format)
        desc = ' '.join(tokens[3:])
        params['summary'] = '%s %s' % (desc.strip(), matcher.group(2).strip())
        return Cal(params)
 
# You may want to override some defaults here
def writeEvents(cals, calendarName='Races', tz='America/New_York'):
        vals = {}
        vals['tz'] = tz
        vals['calname'] = calendarName
        print '''BEGIN:VCALENDAR
METHOD:PUBLISH
VERSION:2.0
X-WR-CALNAME:%(calname)s
PRODID:-//Apple Inc.//iCal 5.0.3//EN
X-APPLE-CALENDAR-COLOR:#B90E28
X-WR-TIMEZONE:%(tz)s
CALSCALE:GREGORIAN
BEGIN:VTIMEZONE
TZID:%(tz)s
BEGIN:DAYLIGHT
TZOFFSETFROM:-0500
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
DTSTART:20070311T020000
TZNAME:EDT
TZOFFSETTO:-0400
END:DAYLIGHT
BEGIN:STANDARD
TZOFFSETFROM:-0400
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
DTSTART:20071104T020000
TZNAME:EST
TZOFFSETTO:-0500
END:STANDARD
END:VTIMEZONE''' % vals
        for cal in cals:
                print "%s" % cal
        print '''END:VCALENDAR'''
 
def main(args):
        format = "America/New_York:%Y%m%dT%H%M00" # America/New_York:20130101T060000
        now = date.today()
        fh = open(args[0], 'r')
        cals = []
        for line in fh.xreadlines():
                cal = parseLine(line, now, format)
                if cal != None:
                        cals.append(cal)
        writeEvents(cals)
 
if __name__ == "__main__":
        main(sys.argv[1:])
# End of script

Para correrlo sólo tiene que escribir lo siguiente:
bin/runnersworld_to_ical.py ~/Documents/half.ics

Después sólo hay que importarlo en la aplicación Ical en OSX.

Espero que le sea de utilidad, mañana me toca correr en nieve (a 25F), así que aún no ando seguro si me toca usar un Treadmil o si voy a correr en la carretera. Si sólo eso se pudiera resolver con un pequeño programita…

Jugando con MongoDB, recolectando eventos (Mongo Tail-Log)

Esta es una idea con la cual he venido jugando desde hace tiempo, la cual es capturar eventos usando MongoDB como base de datos. Me gusta la idea que se pueden controlar el espacio en disco usando un arreglo circular (en MongoDB se llaman ‘capped collections‘), además de que definir el esquema de los datos es muy sencillo.

Preparación

Lo primero es bajarse e instalar MongoDB (yo utilizo OSX):

1
2
3
4
cd /usr/share && mkdir mongodb
tar -xzvf /Users/josevnz/Downloads/mongodb-osx-x86_64-2.2.2.tgz
sudo ln -s mongodb-osx-x86_64-2.2.2 mongo
cd mongo && sudo mkdir data etc log scripts

Luego creamos un archivo de configuración básico:

1
2
3
4
5
6
7
8
9
10
Macintosh:mongo josevnz$ cat etc/mongodb.conf 
fork = true
bind_ip = 127.0.0.1
port = 27017
quiet = true
dbpath = /usr/share/mongodb/mongo/data
logpath = /usr/share/mongodb/mongo/log/mongodb.log
pidfilepath = /usr/share/mongodb/mongo/mongodb.pid
logappend = true
journal = true

Y un par de ‘scripts’ para hacernos la vida más sencilla:

1
2
3
4
5
6
7
8
9
10
Macintosh:mongo josevnz$ cat etc/mongodb.conf 
fork = true
bind_ip = 127.0.0.1
port = 27017
quiet = true
dbpath = /usr/share/mongodb/mongo/data
logpath = /usr/share/mongodb/mongo/log/mongodb.log
pidfilepath = /usr/share/mongodb/mongo/mongodb.pid
logappend = true
journal = true

Y la arrancamos:

1
2
3
4
5
Macintosh:~ josevnz$ sudo mongod -f $MONGO_HOME/etc/mongodb.conf 
Password:
forked process: 2782
all output going to: /usr/share/mongodb/mongo/log/mongodb.log
child process started successfully, parent exiting

Preparando la colección

Ahora hay que preparar el sitio en donde vamos a poner los datos.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Macintosh:mongo josevnz$ mongo
MongoDB shell version: 2.2.2
connecting to: test
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
	http://docs.mongodb.org/
Questions? Try the support group
	http://groups.google.com/group/mongodb-user
> use logdb
switched to db logdb
> db.createCollection("logs", {capped:true, size:100000})
{ "ok" : 1 }
> db.logs.isCapped()
true

Código del cliente que inserta los datos

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#!/usr/bin/env jython
# Simple class that simulates an event writer
# Author: josevnz@kodegeek.com
# BLOG: http://kodegeek.com/blog
# Asumes that you created a database called 'logsdb' and a collection called 'logs':
# use logdb
# db.createCollection("logs", {capped:true, size:100000})
#
from com.mongodb import Mongo, MongoException, WriteConcern, DB, DBCollection, BasicDBObject, DBObject, DBCursor, ServerAddress
from java.util import Arrays, Date, Random
from java.util.concurrent import Executors, TimeUnit
from java.lang import Runnable, Thread
import sys, os
 
class EventWriter(Runnable):
 
        def __init__(self, db):
                self.db = db
                self.col = db.getCollection("logs")
                self.random = Random(1973)
 
        def run(self):
                number = self.random.nextLong()
                event = BasicDBObject('datetime', Date().toString()).append('text', 'This is an event, random # %d' % number)
                print "New event: %s" % event
                self.col.insert(event)
 
def main(args):
 
        initialDelay = 0
        delay = 5
        # Do not use 'localhost', that makes the driver to report a stupid error and hung
        list = Arrays.asList(ServerAddress("127.0.0.1", 27017))
        m = Mongo(list)
        # m.setWriteConcern(WriteConcern.JOURNALED)
        m.setWriteConcern(WriteConcern.NONE) # Do not care if the write makes it or not
        db = m.getDB( "logsdb" )
        print "Connected to %s" % db.getName()
 
        command = EventWriter(db)
        # Use a sigle thread for this example, but in reality the report uses a separate thread to avoid blocking the application
        executor = Executors.newSingleThreadScheduledExecutor()
        print "Press Ctrl-C to abort this script, events will be written periodically into the database"
        future = executor.scheduleWithFixedDelay(command, initialDelay, delay, TimeUnit.SECONDS)
        #sys.exit(0)
 
if __name__ == "__main__":
        main(sys.argv[1:])

La salida se ve asi:

Macintosh:mongodb josevnz$./log_writer.py
Connected to logsdb
Press Ctrl-C to abort this script, events will be written periodically into the database
New event: { "datetime" : "Thu Dec 13 10:13:21 EST 2012" , "text" : "This is an event, random # -6901132129250388696"}
New event: { "datetime" : "Thu Dec 13 10:13:27 EST 2012" , "text" : "This is an event, random # 2141911474641068654"}
New event: { "datetime" : "Thu Dec 13 10:13:32 EST 2012" , "text" : "This is an event, random # -7447082860282012741"}
New event: { "datetime" : "Thu Dec 13 10:13:37 EST 2012" , "text" : "This is an event, random # 3042277681337134497"}
New event: { "datetime" : "Thu Dec 13 10:13:42 EST 2012" , "text" : "This is an event, random # -2682038860783877385"}
New event: { "datetime" : "Thu Dec 13 10:13:47 EST 2012" , "text" : "This is an event, random # -6576368686118448135"}
New event: { "datetime" : "Thu Dec 13 10:13:52 EST 2012" , "text" : "This is an event, random # -294840040020254100"}
New event: { "datetime" : "Thu Dec 13 10:13:57 EST 2012" , "text" : "This is an event, random # 4202626908153060298"}
New event: { "datetime" : "Thu Dec 13 10:14:02 EST 2012" , "text" : "This is an event, random # -6313895213434337152"}
New event: { "datetime" : "Thu Dec 13 10:14:07 EST 2012" , "text" : "This is an event, random # 983475561958631366"}
New event: { "datetime" : "Thu Dec 13 10:14:12 EST 2012" , "text" : "This is an event, random # -6651143639772223084"}
New event: { "datetime" : "Thu Dec 13 10:14:17 EST 2012" , "text" : "This is an event, random # -7909942155638967101"}

En otra ventana verificamos que en verdad estamos recibiendo eventos, usando la mongo Shell:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Macintosh:mongodb josevnz$ mongo
MongoDB shell version: 2.2.2
connecting to: test
> use logsdb
switched to db logsdb
// Imprime el último registro recibido
> db.logs.find().skip(db.logs.count()-1).forEach(printjson)
{
	"_id" : ObjectId("50d6ec7cef869f82b3f656ea"),
	"datetime" : "Sun Dec 23 06:35:24 EST 2012",
	"text" : "This is an event, random # -8356391245437638799"
}
 
// Imprime el primer registro recibido
> db.logs.findOne()
{
	"_id" : ObjectId("50c9f092ef863d028e1ab74b"),
	"datetime" : "Thu Dec 13 10:13:21 EST 2012",
	"text" : "This is an event, random # -6901132129250388696"
}

Código del cliente que lee continuamente de la base de datos

Siempre ayuda tener a la mano la equivalencia de MongoDB a SQL. Aqui queremos simular el mismo comportamiento de la herramienta de UNIX ‘tail’, asi que utilizamos algo llamado ‘Tailable cursors‘:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#!/usr/bin/env jython
# Simple class that simulates an event reader
# Author: josevnz@kodegeek.com
# BLOG: http://kodegeek.com/blog
# Asumes that you created a database called 'logsdb' and a collection called 'logs':
# use logdb
# db.createCollection("logs", {capped:true, size:100000})
# Or convert an existing one to capped: db.runCommand({"convertToCapped": "logs", size: 100000})
# It is very important that you get a driver version more recent than 2.7.1 (Collection.isCapped is broken there)
#
from com.mongodb import Mongo, BasicDBObjectBuilder, DB, DBCollection, BasicDBObject, DBObject, ServerAddress, Bytes
from java.util import Arrays, Date
from java.util.concurrent import Executors, TimeUnit
from java.lang import Runnable, Thread
import sys, os
 
class EventReader(Runnable):
 
        def __init__(self, db):
                self.db = db
                if not db.collectionExists("logs"):
                        raise Exception("Logs doesn't exist, please create!")
                self.coll = db.getCollection("logs")
                if not self.coll.isCapped():
                        raise Exception("Logs is not a capped collection!")
                self.sortBy = BasicDBObjectBuilder().start("$natural", 1).get()
                print "Ready to read events..."
 
        def run(self):
                lastVal = None # This could be refined to get the last event
                cursor = self.coll.find(lastVal).sort(self.sortBy).addOption(Bytes.QUERYOPTION_TAILABLE).addOption(Bytes.QUERYOPTION_AWAITDATA)
                while cursor.hasNext():
                        print "%s" % cursor.next()
 
def main(args):
 
        delay = 1
 
        list = Arrays.asList(ServerAddress("127.0.0.1", 27017))
        m = Mongo(list)
        db = m.getDB( "logsdb" )
        print "Connected to %s" % db.getName()
 
        command = EventReader(db)
        executor = Executors.newSingleThreadScheduledExecutor()
        print "Press Ctrl-C to abort this script, reading events from the database"
        future = executor.schedule(command, delay, TimeUnit.SECONDS)
        #sys.exit(0)
 
if __name__ == "__main__":
        main(sys.argv[1:])

Pienso implementar una herramienta que use esto en mi trabajo, pero el código se ve fácil de usar y promete mucho 🙂

Maratón de Nueva York del 2012: Nos vemos en el 2013

Todo el mundo tiene una opinión

Si usted es un corredor entonces seguro ya leyó las noticias. Sin más preámbulos, les copio la notificación oficial del NYRR:

Screen Shot 2012-11-03 at 6.14.30 PM
Primera vez que se cancela el evento en 40 años de vida

Sin embargo hay explicaciones alternativas.

¿Y que opina los corredores sobre la cancelación?:

¿Y que pienso yo?

Yo sólo perdí mi registro al Dash to the Finish Line. La tormenta sólo me hizo difícil trabajar durante 3 días, tuve que buscar un sitio en donde pudiéramos bañarnos con agua caliente y comer comida decente. Pero la casa salió intacta de ese lio. La familia está bien.

Cancelar el maratón fué la decisión acertada. Fué difícil. Pero no quedó otra alternativa.

Ya vendrán otros maratones, incluyendo la versión del 2013 del medio y completo maratón. Si Dios quiere, nos vemos allá.

¿Qué es lo realmente importante?

La gente se va a recuperar de este bajón. Hoy los corredores del maratón se fueron a Long Island a ayudar a repartir comida y otras cosas; Hay donativos. Los corredores no son egoístas, simplemente querían ver a su ciudad en pie lo más pronto posible.

Hoy hay gente corriendo el viejo recorrido del maratón, 4 vueltas y algo más alrededor de Central Park. Yo les digo a esos corredores que con el favor de Dios nos vemos en el 2013. Y esa será la mejor carrera de todas.

Carrera #9: 5th Avenue Mile, los demonios de la velocidad

Chill morning in Times Square. No people, just for myself
Tomé el tren de las 5:45 A.M a Nueva York para estar cerca del sitio de la carrera. Al que madruga Dios lo ayuda

Ayer corrí la última carrera que necesitaba para completar mi 9 + 1 para garantizar así mi entrada al maratón de Nueva York del 2013. Esta carrera es de solamente una milla, ¿así que tan malo puede ser?

Female speed daemons. This group pace was below 5:30
¿Un ritmo de 6’30» / milla le parece lento? Lo invito a tratar por una milla

Esa es la parte engañosa. Precisamente por ser de solamente una milla, el ritmo es muchísimo más intenso que el de una carrera como un 5K; Lo otro es que no es una carrera corta, digamos de 400 metros, así que hay que conservar las energías y tener una estrategia.

Como siempre ayudó estudiar el mapa; El club de corredores de Nueva York recomendaba tener cuidado a los 800 metros (2/4 de la carrera) ya que allí habia una pendiente fuerte, pero que después de eso era bajada por otros 400 metros y los últimos 400 eran planos.

Mi milla más rápida (no oficial) anterior era 6:25 en una pista cerrada (sin elevaciones), y mi meta era bajar completamente a 6:00. ¿Como hacerlo?

Untitled
Alguien del público me grito: «¡Vamos Venezuela, VAMOS! La Vinotinto siempre me da suerte

Bueno, además de tener conocimiento del terreno el día de la carrera hice lo siguiente:
* Corrí una milla muy suave, para calentar las piernas y bombear más sangre. Por ser una milla, no hay mucho tiempo para calentar el cuerpo.
* A diferencia de otras carreras más largas, corrí más rápido los primero 800 metros. Eso me cansó bastante y lo único que pude hacer fué mantener el ritmo lo mejor que pude mientras veía como me pasaban otros corredores más rápidos en mi mismo rango de edad (35-39 años). Al final sorprendí (y arrebate la victoria) al menos a 5 de ellos ya que logré correr los últimos 200 metros antes de llegar a la meta.

Untitled
Al final logré establecer un registro personal que me pone en el porcentaje %65. %5 más y podré estar en competencia regional 🙂

¿Que viene ahora? Tengo en mente hacer mi primer medio maratón, oficial. Nada de tiempos sino hacer la distancia en un recorrido difícil como es Central Park. Ya les escribiré por acá si me decidí hacerlo o no.

Percy Sutton Harlem 5K Run, race #7 of the 9+1 and Nikey watches

Untitled
Percy Sutton Harlem 5K. A nice race, challenging first 2 miles.

Percy Sutton Harlem 5K Run. A nice 5K celebrating the Harlem week. The first 2 miles had a pronounced hill but then most of the course was downhill with the last bit completely flat. I did not PR on this one but I had fun and weather wasn’t that bad.

It is too hot!
The day of the race was not that hot, but it was pretty humid. Things like this helped 🙂

Also I finally got back my watch from Nike, after they agreed to make an ‘exception’ for me and give me a new one. It was a nice gesture from them, but still doesn’t solve the issues about the lack of extended warranty or the fact than once your watch is damaged you will need to purchase a brand new one (hopefully I will have to wait another year for that to happen and then will switch to something else).

Got a new watch from Nike
I agree, let’s go and run some more

Now I’m preparing for the last 2 races so I can secure my spot at the NYC Marathon next year.

Calling Python from Java, a quick way to implement a rule engine

This is a robot that my son made with some parts I was using to assemble a crib.
It is amazing what you can build with small simple parts!

I have the following situation at work: One of our applications gets data from a proprietary data source and the rules on how to filter the data change very often; On top of that the traders are the ones who know what is the best criteria so hard-coding this rules into Java is tedious, error prone and takes more time than it should.

I’ve been using Jython for a while and I must say I’m in love with the project. I have the flexibility of a scripting language with the ability to use all the Java libraries and also our application classes, which is something that is priceless as the majority of the code is tested and I can reuse a lot of it.

So, what about calling Python from Java? It would be great it I can let the traders implement their own business rules (we will work together but they can prototype stuff faster that way). Also the traders I work with are very technical savvy 🙂

After checking the chapter ‘Jython and Java integration‘ from the Jython book I decided to experiment myself.

For the sake of the example, I want to have a simple Python class where I receive a Map and a String and print some values:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from com.kodegeek.blog.jython import HelperInt;
 
class Helper(HelperInt):
 
        def doSomething(self, data, name):
 
                if name == None:
                        raise Exception("Name is missing!")
 
                if data == None:
                        raise Exception("No data was provided!")
 
                print "Hello, my name is: %s" % name
                if data.containsKey('age'):
                        age = data.get('age')
                        if age > 21:
                                print "%s, you can also have a beer!" % name
                        else:
                                print "%s, it is time for your milshake :-)" % name

So far is good, but who is the class ‘HelperInt’? Is an interface and that will help us to glue our Python class into the Java world:

1
2
3
4
5
6
7
8
9
package com.kodegeek.blog.jython;
 
import java.util.Map;
 
public interface HelperInt {
 
        public void doSomething(Map data, String name);
 
}

Nothing weird so far, uh?. Now the most complicated part is to bring the Jython implementation back into the Java world. The book give us a really nice Singleton Factory class that I used as is (the code is below, but I strongly suggest you read the book explanation on how it works. It seems there are many ways to skin this cat):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package com.kodegeek.blog.jython;
 
import org.python.core.Py;
import org.python.core.PyObject;
import org.python.core.PySystemState;
 
/**
 * Jython Object Factory using PySystemState
 */
public class JythonObjectFactory {
 
 private final Class interfaceType;
 private final PyObject klass;
 
 // Constructor obtains a reference to the importer, module, and the class name
 public JythonObjectFactory(PySystemState state, Class interfaceType, String moduleName, String className) {
     this.interfaceType = interfaceType;
     PyObject importer = state.getBuiltins().__getitem__(Py.newString("__import__"));
     PyObject module = importer.__call__(Py.newString(moduleName));
     klass = module.__getattr__(className);
     System.err.println("module=" + module + ",class=" + klass);
 }
 
 // This constructor passes through to the other constructor
 public JythonObjectFactory(Class interfaceType, String moduleName, String className) {
     this(new PySystemState(), interfaceType, moduleName, className);
 }
 
 // All of the followng methods return
 // a coerced Jython object based upon the pieces of information
 // that were passed into the factory. The differences are
 // between them are the number of arguments that can be passed
 // in as arguents to the object.
 
 public Object createObject() {
     return klass.__call__().__tojava__(interfaceType);
 }
 
 
 public Object createObject(Object arg1) {
     return klass.__call__(Py.java2py(arg1)).__tojava__(interfaceType);
 }
 
 public Object createObject(Object arg1, Object arg2) {
     return klass.__call__(Py.java2py(arg1), Py.java2py(arg2)).__tojava__(interfaceType);
 }
 
 public Object createObject(Object arg1, Object arg2, Object arg3)
 {
     return klass.__call__(Py.java2py(arg1), Py.java2py(arg2),
         Py.java2py(arg3)).__tojava__(interfaceType);
 }
 
 public Object createObject(Object args[], String keywords[]) {
     PyObject convertedArgs[] = new PyObject[args.length];
     for (int i = 0; i < args.length; i++) {
         convertedArgs[i] = Py.java2py(args[i]);
     }
 
     return klass.__call__(convertedArgs, keywords).__tojava__(interfaceType);
 }
 
 public Object createObject(Object... args) {
     return createObject(args, Py.NoKeywords);
 }

The last piece of the puzzle is our application, which calls the factory and then the Python object from Java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.kodegeek.blog.app;
 
import java.util.Map;
import java.util.HashMap;
 
import com.kodegeek.blog.jython.HelperInt;
import com.kodegeek.blog.jython.JythonObjectFactory;
 
public final class Caller {
 
        public static void main(final String [] args) throws Exception {
 
                JythonObjectFactory factory = new JythonObjectFactory(HelperInt.class, "JHelper", "Helper");
                HelperInt helper = (HelperInt) factory.createObject();
                Map<string , Integer> data = new HashMap</string><string , Integer>();
                data.put("age", 39);
                String name = "Jose Vicente";
                helper.doSomething(data, name);
        }
 
}
</string>

Time to compile and run the stuff. As usual, make sure you have Jython on the Classpath:

1
2
3
4
5
6
7
8
9
Macintosh:jython josevnz$ javac -classpath /users/Shared/jython2.5.2/jython.jar:. -d . *.java
Macintosh:jython josevnz$ 
Macintosh:jython josevnz$ java -classpath /users/Shared/jython2.5.2/jython.jar:. com.kodegeek.blog.app.Caller
*sys-package-mgr*: processing new jar, '/Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home/jre/lib/resources.jar'
*sys-package-mgr*: processing new jar, '/Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home/jre/lib/ext/zipfs.jar'
module=<module 'JHelper' from '__pyclasspath__/JHelper.py'>,class=<class 'JHelper.Helper'>
Hello, my name is: Jose Vicente
Jose Vicente, you can also have a beer!
</class></module>

Success!. Let’s see how far I can get with this on a real application 🙂