<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>KodeGeek</title>
	<atom:link href="http://kodegeek.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://kodegeek.com/blog</link>
	<description>Interés geek</description>
	<lastBuildDate>Fri, 26 Mar 2021 17:08:00 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.1.1</generator>

<image>
	<url>http://kodegeek.com/blog/wp-content/uploads/2016/02/cropped-kgeek-32x32.png</url>
	<title>KodeGeek</title>
	<link>http://kodegeek.com/blog</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Installing WordPress on the Canakit Raspberry pi 4 Extreme kit, with Ubuntu Server 64 bit and Docker containers</title>
		<link>http://kodegeek.com/blog/2021/03/26/installing-wordpress-on-the-canakit-raspberry-pi-4-extreme-kit-with-ubuntu-server-64-bit-and-docker-containers/</link>
		
		<dc:creator><![CDATA[josevnz]]></dc:creator>
		<pubDate>Fri, 26 Mar 2021 17:08:00 +0000</pubDate>
				<category><![CDATA[kodegeek]]></category>
		<guid isPermaLink="false">http://kodegeek.com/blog/?p=4696</guid>

					<description><![CDATA[https://kodegeek-com.medium.com/installing-wordpress-on-the-canakit-raspberry-pi-4-extreme-kit-with-ubuntu-server-64-bit-and-113f5bf00c51]]></description>
										<content:encoded><![CDATA[
<p><a href="https://kodegeek-com.medium.com/installing-wordpress-on-the-canakit-raspberry-pi-4-extreme-kit-with-ubuntu-server-64-bit-and-113f5bf00c51" target="_blank" rel="noreferrer noopener" aria-label=" (abre en una nueva pestaña)">https://kodegeek-com.medium.com/installing-wordpress-on-the-canakit-raspberry-pi-4-extreme-kit-with-ubuntu-server-64-bit-and-113f5bf00c51</a></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Serializando objetos en MONGODB Java (POJO) usando Codecs (II)</title>
		<link>http://kodegeek.com/blog/2018/03/30/serializando-objetos-en-mongodb-java-pojo-usando-codecs-ii/</link>
		
		<dc:creator><![CDATA[josevnz]]></dc:creator>
		<pubDate>Fri, 30 Mar 2018 14:37:05 +0000</pubDate>
				<category><![CDATA[java]]></category>
		<category><![CDATA[kodegeek]]></category>
		<category><![CDATA[nosql]]></category>
		<category><![CDATA[opensource]]></category>
		<category><![CDATA[programación]]></category>
		<category><![CDATA[codec]]></category>
		<category><![CDATA[jackson]]></category>
		<category><![CDATA[jaxb]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[mongodb]]></category>
		<guid isPermaLink="false">http://kodegeek.com/blog/?p=4660</guid>

					<description><![CDATA[En el articulo anterior, les traté de mostrar como guardar y recuperar documentos en MongoDB &#8230; sin mucho éxito. El problema es que el Java POJO utiliza enumeraciones y la base de datos no puede por si sólo encargarse de su manejo. Después de leer otra vez la guía de Jackson con las anotaciones &#8216;@JsonDeserialize&#8217; <a class="read-more" href="http://kodegeek.com/blog/2018/03/30/serializando-objetos-en-mongodb-java-pojo-usando-codecs-ii/">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>En el articulo anterior, <a href="http://kodegeek.com/blog/2018/03/25/serializando-objetos-en-java-pojo-usando-codecs-i/" rel="noopener" target="_blank">les traté de mostrar</a> como guardar y recuperar documentos en MongoDB &#8230; sin mucho éxito.</p>
<p>El problema es que el Java POJO utiliza enumeraciones y la base de datos no puede por si sólo encargarse de su manejo.</p>
<p>Después de leer otra vez la guía de Jackson con las anotaciones &#8216;@JsonDeserialize&#8217; &#8216;@JsonSerialize&#8217; y MongoDB con Java Codecs entendí que esta es la estrategia:</p>
<ul>
<li>En Jackson, debemos guardar y leer las enumeraciones como cadena de caracteres. De hecho mi definición contiene una representación mas amigable que la que estaba usando. Esto lo voy a usar más tarde para un servicio web que estoy escribiendo y no tiene nada que ver con MongoDB <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></li>
<li>En MongoDB registramos un Codec que se encarga de leer la enumeración desde una cadena de caracteres y la escribe de vuelta como una cadena de caracteres</li>
</ul>
<p>Todo se ve más claro en código, así que allí vamos. Lo primero es anotar nuestro POJO con Jackson (sólo muestro el atributo StatusEnumType):</p>
<pre lang="Java">
public class ItemType {
    @JsonDeserialize(using = StatusEnumTypeDeserializer.class)
    @JsonSerialize(using = StatusEnumTypeSerializer.class)
    @XmlElement(required = true)
    @XmlSchemaType(name = "token")
    protected StatusEnumType status;
</pre>
<p>La anotación registra el atributo con las clases que van a leer y a escribir la enumeración de forma que pueda ser entendida por nuestro ObjectMapper en Jackson. </p>
<p>La clase que codifica:</p>
<pre lang="Java">
package com.kodegeek.cvebrowser.persistence.serializers;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.kodegeek.cvebrowser.entity.SimplePhaseEnumType;

import java.io.IOException;

public class SimplePhaseEnumTypeSerializer extends JsonSerializer<simplephaseenumtype> {
    @Override
    public void serialize(SimplePhaseEnumType value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (! StringChecker.isMissing(value.value())) {
            gen.writeString(value.value());
        } else {
            gen.writeNull();
        }
    }
}
</simplephaseenumtype></pre>
<p>Y la que decodifica:</p>
<pre lang="Java">
package com.kodegeek.cvebrowser.persistence.serializers;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.kodegeek.cvebrowser.entity.SimplePhaseEnumType;

import java.io.IOException;

public class SimplePhaseEnumTypeDeserializer extends JsonDeserializer<simplephaseenumtype> {
    @Override
    public SimplePhaseEnumType deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        SimplePhaseEnumType value = null;
        try {
            value = SimplePhaseEnumType.fromValue(p.getValueAsString());
        } catch (IllegalArgumentException eaexp) {
            eaexp.printStackTrace();
        }
        return value;
    }
}
</simplephaseenumtype></pre>
<p>No hay que hacer nada más para que Jackson funcione. En cuanto a MongoDB, debemos escribir una clase que implemente &#8220;Codec&#8221;. Esta clase se encarga de codificar y descodificar nuestra enumeración:</p>
<pre lang="Java">
package com.kodegeek.cvebrowser.persistence.serializers;

import com.kodegeek.cvebrowser.entity.SimplePhaseEnumType;
import org.bson.BsonReader;
import org.bson.BsonWriter;
import org.bson.codecs.Codec;
import org.bson.codecs.DecoderContext;
import org.bson.codecs.EncoderContext;

public class SimplePhaseEnumTypeCodec implements Codec<simplephaseenumtype>{
    @Override
    public SimplePhaseEnumType decode(BsonReader reader, DecoderContext decoderContext) {
        return SimplePhaseEnumType.fromValue(reader.readString());
    }

    @Override
    public void encode(BsonWriter writer, SimplePhaseEnumType value, EncoderContext encoderContext) {
        writer.writeString(value.value());
    }

    @Override
    public Class</simplephaseenumtype><simplephaseenumtype> getEncoderClass() {
        return SimplePhaseEnumType.class;
    }
}
</simplephaseenumtype></pre>
<p>El paso final es registrar nuestros codecs con el registro, de manera que estos sean llamados cuando procesemos este tipo de datos:</p>
<pre lang="Java">
    /**
     * MongoDB could not make this any simpler ;-)
     * @return a Codec registry
     */
    public static CodecRegistry getCodecRegistry() {
        final CodecRegistry defaultCodecRegistry = MongoClient.getDefaultCodecRegistry();
        final CodecProvider pojoCodecProvider = PojoCodecProvider.builder().register(packages).build();
        final CodecRegistry cvePojoCodecRegistry = CodecRegistries.fromProviders(pojoCodecProvider);
        // Aqui e stan los nuevos codecs, el del ejemplo es "SimplePhaseEnumTypeCodec"
        final CodecRegistry customEnumCodecs = CodecRegistries.fromCodecs(
                new SimplePhaseEnumTypeCodec(),
                new StatusEnumTypeCodec(),
                new TypeEnumTypeCodec()
        );
        return CodecRegistries.fromRegistries(defaultCodecRegistry, customEnumCodecs, cvePojoCodecRegistry);
    }
</pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Serializando objetos en MONGODB Java (POJO) usando Codecs (I)</title>
		<link>http://kodegeek.com/blog/2018/03/25/serializando-objetos-en-java-pojo-usando-codecs-i/</link>
		
		<dc:creator><![CDATA[josevnz]]></dc:creator>
		<pubDate>Sun, 25 Mar 2018 12:53:15 +0000</pubDate>
				<category><![CDATA[java]]></category>
		<category><![CDATA[kodegeek]]></category>
		<category><![CDATA[programación]]></category>
		<guid isPermaLink="false">http://kodegeek.com/blog/?p=4651</guid>

					<description><![CDATA[Estos días he estado trabajando con Java JPA para un proyecto de la oficina; También he estado trabajando con MongoDB y objetos con soporte para JAXP para CVEBrowser. No es tan fácil como parece :-). En el caso de Mongo no tiene sentido pensar en JPA (aunque hay soporte para esto), así que comencé a <a class="read-more" href="http://kodegeek.com/blog/2018/03/25/serializando-objetos-en-java-pojo-usando-codecs-i/">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>Estos días he estado trabajando con Java JPA para un proyecto de la oficina; También he estado trabajando con MongoDB y objetos con soporte para JAXP para CVEBrowser.</p>
<p>No es tan fácil como parece :-). En el caso de Mongo no tiene sentido pensar en JPA (aunque hay soporte para esto), así que comencé a jugar con el API del manejado de conexiones.</p>
<p>La razón es que cuando tenemos un esquema previo, por ejemplo una definición que viene de XSD, no hay mucha elección en cuanto a como se ven los atributos.</p>
<p>Veamos por ejemplo una clase que resulta de convertir la definición de una entrada de <a href="https://cve.mitre.org/schema/cve/cve_1.0.xsd" rel="noopener" target="_blank">XSD CVE (https://cve.mitre.org/schema/cve/cve_1.0.xsd)</a>.</p>
<pre lang="Java">
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2018.02.17 at 08:52:30 PM EST 
//


package com.kodegeek.cvebrowser.entity;

import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlEnumValue;
import javax.xml.bind.annotation.XmlType;


/**
 * Java class for simplePhaseEnumType.
 * 
 * The following schema fragment specifies the expected content contained within this class.
 * 
 * 
 * &lt;simpleType name="simplePhaseEnumType">
 *   &lt;restriction base="{http://www.w3.org/2001/XMLSchema}token">
 *     &lt;enumeration value="Proposed"/>
 *     &lt;enumeration value="Interim"/>
 *     &lt;enumeration value="Modified"/>
 *     &lt;enumeration value="Assigned"/>
 *   &lt;/restriction>
 * &lt;/simpleType>
 * 
 * 
 */
@XmlType(name = "simplePhaseEnumType")
@XmlEnum
public enum SimplePhaseEnumType {

    @XmlEnumValue("Proposed")
    PROPOSED("Proposed"),
    @XmlEnumValue("Interim")
    INTERIM("Interim"),
    @XmlEnumValue("Modified")
    MODIFIED("Modified"),
    @XmlEnumValue("Assigned")
    ASSIGNED("Assigned");
    private final String value;

    SimplePhaseEnumType(String v) {
        value = v;
    }

    public String value() {
        return value;
    }

    public static SimplePhaseEnumType fromValue(String v) {
        for (SimplePhaseEnumType c: SimplePhaseEnumType.values()) {
            if (c.value.equals(v)) {
                return c;
            }
        }
        throw new IllegalArgumentException(v);
    }

}
</pre>
<p>Ahora mi problema es que puedo guardar los datos (aparentemente) pero no puedo recuperarlos debido a que el codec no puede transformar la enumeración (la cual por definición no tiene constructores públicos). </p>
<p>Según MongoDB (<a href="https://github.com/mongodb/mongo-java-driver/blob/master/driver-async/src/examples/tour/PojoQuickTour.java" rel="noopener" target="_blank">PojoQuickTour,java</a>), debería ser tan fácil como usar una base de datos con el Codec apropiado:</p>
<pre lang="Java">

private final static String [] packages = {  "com.kodegeek.cvebrowser.entity" };
// ...

public static CodecRegistry getCodecRegistry() {
        final CodecRegistry defaultCodecRegistry = MongoClient.getDefaultCodecRegistry();
        final CodecProvider pojoCodecProvider = PojoCodecProvider.builder().register(packages).build();
        final CodecRegistry cvePojoCodecRegistry = CodecRegistries.fromProviders(pojoCodecProvider);
        return CodecRegistries.fromRegistries(defaultCodecRegistry, cvePojoCodecRegistry);
    }

// Later on, different place we get a database with a POJO codec
// ...
mongoClient.getDatabase("cvebrowser").withCodecRegistry(getCodecRegistry())


</pre>
<p>Sin embargo si trato de hacer una búsqueda usando el codec (Joses-iMac:CVEBrowser josevnz$ gradle test):</p>
<pre lang="Java">
org.bson.codecs.configuration.CodecConfigurationException: Failed to decode 'phase'. Failed to decode 'value'. Cannot find a public constructor for 'SimplePhaseEnumType'.
	at org.bson.codecs.pojo.PojoCodecImpl.decodePropertyModel(PojoCodecImpl.java:192)
	at org.bson.codecs.pojo.PojoCodecImpl.decodeProperties(PojoCodecImpl.java:168)
	at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:122)
	at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:126)
	at com.mongodb.operation.CommandResultArrayCodec.decode(CommandResultArrayCodec.java:52)
	at com.mongodb.operation.CommandResultDocumentCodec.readValue(CommandResultDocumentCodec.java:60)
	at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:84)
	at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:41)
	at org.bson.codecs.configuration.LazyCodec.decode(LazyCodec.java:47)
	at org.bson.codecs.BsonDocumentCodec.readValue(BsonDocumentCodec.java:101)
	at com.mongodb.operation.CommandResultDocumentCodec.readValue(CommandResultDocumentCodec.java:63)
	at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:84)
	at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:41)
	at com.mongodb.connection.ReplyMessage.<init>(ReplyMessage.java:51)
	at com.mongodb.connection.InternalStreamConnection.receiveCommandMessageResponse(InternalStreamConnection.java:301)
	at com.mongodb.connection.InternalStreamConnection.sendAndReceive(InternalStreamConnection.java:255)
	at com.mongodb.connection.UsageTrackingInternalConnection.sendAndReceive(UsageTrackingInternalConnection.java:98)
	at com.mongodb.connection.DefaultConnectionPool$PooledConnection.sendAndReceive(DefaultConnectionPool.java:441)
	at com.mongodb.connection.CommandProtocolImpl.execute(CommandProtocolImpl.java:80)
	at com.mongodb.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:189)
	at com.mongodb.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:264)
	at com.mongodb.connection.DefaultServerConnection.command(DefaultServerConnection.java:126)
	at com.mongodb.connection.DefaultServerConnection.command(DefaultServerConnection.java:118)
	at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:226)
	at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:217)
	at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:120)
	at com.mongodb.operation.FindOperation$1.call(FindOperation.java:717)
	at com.mongodb.operation.FindOperation$1.call(FindOperation.java:711)
	at com.mongodb.operation.OperationHelper.withConnectionSource(OperationHelper.java:471)
	at com.mongodb.operation.OperationHelper.withConnection(OperationHelper.java:415)
	at com.mongodb.operation.FindOperation.execute(FindOperation.java:711)
	at com.mongodb.operation.FindOperation.execute(FindOperation.java:83)
	at com.mongodb.Mongo$3.execute(Mongo.java:826)
	at com.mongodb.MongoIterableImpl.execute(MongoIterableImpl.java:130)
	at com.mongodb.MongoIterableImpl.iterator(MongoIterableImpl.java:77)
	at com.mongodb.MongoIterableImpl.forEach(MongoIterableImpl.java:100)
	at com.kodegeek.cvebrowser.persistence.TestCVEMongoPojoManager.testPrintIssues(TestCVEMongoPojoManager.java:47)
</init></pre>
<p>&#8220;Failed to decode &#8216;phase&#8217;. Failed to decode &#8216;value&#8217;. Cannot find a public constructor for &#8216;SimplePhaseEnumType&#8221;. Estoy aprendiendo como enseñarle a MongoDB como guardar y leer estos valores sin que le de un infarto.</p>
<p>(Por cierto, <a href="https://stackoverflow.com/questions/49477589/cannot-read-or-serialize-pojo-with-enumerations-using-java-mongodb-driver" rel="noopener" target="_blank">estoy preguntando en StackOverFlow</a>. Vamos a ver que tan complicado es).</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Mis primeros pasos con MongoDB</title>
		<link>http://kodegeek.com/blog/2016/10/30/mis-primeros-pasos-con-mongodb/</link>
		
		<dc:creator><![CDATA[josevnz]]></dc:creator>
		<pubDate>Mon, 31 Oct 2016 01:22:01 +0000</pubDate>
				<category><![CDATA[kodegeek]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[mongo]]></category>
		<category><![CDATA[nosql]]></category>
		<category><![CDATA[python]]></category>
		<guid isPermaLink="false">http://kodegeek.com/blog/?p=4631</guid>

					<description><![CDATA[Para ser honesto yo ya había instalado MongoDB hace años, pero no es sino hasta ahora que me ha tocado un proyecto en el cual creo que le puedo sacar mucho provecho. En vista de esto, me senté a jugar un poco con la herramienta. La Internet cuenta con muchos lugares de donde podemos bajar <a class="read-more" href="http://kodegeek.com/blog/2016/10/30/mis-primeros-pasos-con-mongodb/">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>Para ser honesto yo ya había instalado MongoDB hace años, pero no es sino hasta ahora que me ha tocado un proyecto en el cual creo que le puedo sacar mucho provecho. En vista de esto, me senté a jugar un poco con la herramienta.</p>
<p>La Internet cuenta con muchos lugares de donde podemos bajar juegos de datos, en este caso <a href="https://data.cityofnewyork.us/Health/Most-Popular-Baby-Names-by-Sex-and-Mother-s-Ethnic/25th-nujf" target="_blank">me decidí bajarme la lista de los nombres de bebes más populares en el estado de Nueva York entre los años 2011 y 2014</a>, en formato <a href="https://data.cityofnewyork.us/api/views/25th-nujf/rows.json?accessType=DOWNLOAD" target="_blank">JSON</a>.</p>
<pre lang="Javascript">
{
  "meta" : {
    "view" : {
      "id" : "25th-nujf",
      "name" : "Most Popular Baby Names by Sex and Mother's Ethnic Group, New York City",
      "attribution" : "Department of Health and Mental Hygiene (DOHMH)",
      "averageRating" : 0,
      "category" : "Health",
      "createdAt" : 1382724894,
      "description" : "The most popular baby names by sex and mother's ethnicity in New York City.",
      "displayType" : "table",
      "downloadCount" : 4328,
      "hideFromCatalog" : false,
      "hideFromDataJson" : false,
      "indexUpdatedAt" : 1465427458,
      "newBackend" : false,
...
  },
  "data" : [ [ 1, "EB6FAA1B-EE35-4D55-B07B-8E663565CCDF", 1, 1386853125, "399231", 1386853125, "399231", "{\n}", "2011", "FEMALE", "HISPANIC", "GERALDINE", "13", "75" ]
, [ 2, "2DBBA431-D26F-40A1-9375-AF7C16FF2987", 2, 1386853125, "399231", 1386853125, "399231", "{\n}", "2011", "FEMALE", "HISPANIC", "GIA", "21", "67" ]
, [ 3, "54318692-0577-4B21-80C8-9CAEFCEDA8BA", 3, 1386853125, "399231", 1386853125, "399231", "{\n}", "2011", "FEMALE", "HISPANIC", "GIANNA", "49", "42" ]
, [ 4, "17C1236A-5778-412D-8DC9-94EBC01BB9A1", 4, 1386853125, "399231", 1386853125, "399231", "{\n}", "2011", "FEMALE", "HISPANIC", "GISELLE", "38", "51" ]
, [ 5, "F53CF696-A3F4-4EC3-8DFD-10C0A111B2D8", 5, 1386853125, "399231", 1386853125, "399231", "{\n}", "2011", "FEMALE", "HISPANIC", "GRACE", "36", "53" ]
, [ 6, "6615893F-39B8-440C-98D3-5A37CCF1C44B", 6, 1386853125, "399231", 1386853125, "399231", "{\n}", "2011", "FEMALE", "HISPANIC", "GUADALUPE", "26", "62" ]
, [ 7, "CC9BE461-34B8-4BD7-BEF2-BDB23CA1ADC6", 7, 1386853125, "399231", 1386853125, "399231", "{\n}", "2011", "FEMALE", "HISPANIC", "HAILEY", "126", "8" ]
, [ 8, "4EA2FFD4-1B1D-4859-A5C2-045949E3FD36", 8, 1386853125, "399231", 1386853125, "399231", "{\n}", "2011", "FEMALE", "HISPANIC", "HALEY", "14", "74" ]
]}
</pre>
<p>Antes de importar el archivo en Mongo, hay que darle un masaje a los datos, para ignorar la metadata y para separar cada entrada en el archivo para que luzca como un documento individual, sobre el cual podremos hacer agregación.</p>
<p>Primero les muestro como arrancar MongoDB, importar los datos (no muestro como bajarse el archivo pero es trivial) y luego corremos unos cuantos consultas:</p>
<pre lang="Bash">
#!/bin/bash
export PATH=$PATH:/Users/josevnz/mongo/mongodb-osx-x86_64-3.2.10/bin
mongod --fork --logpath /Users/josevnz/mongo/mongod.log --noauth --rest --pidfilepath /Users/josevnz/mongo/mongod.pid --dbpath /Users/josevnz/mongo/data
</pre>
<p>El programa para masajear los datos, en Python:</p>
<pre lang="Python">
#!/usr/bin/env python
# josevn at kodegeek.com
import json
import sys
from pprint import pprint
if len(sys.argv) < 2:
  raise ValueError("Missing JSON file...")

'''
// From this (array of arrays):
{
    "data": [
        [
            1,
            "EB6FAA1B-EE35-4D55-B07B-8E663565CCDF",
            1,
            1386853125,
            "399231",
            1386853125,
            "399231",
            "{\n}",
            "2011",
            "FEMALE",
            "HISPANIC",
            "GERALDINE",
            "13",
            "75"
        ],
        [
          ...
        ]
    ],
    "metadata" : {
...
    }
}
 // To this individual maps, no metadata and no extra attributes:
      {
       "key": 1,
       "id": "EB6FAA1B-EE35-4D55-B07B-8E663565CCDF",
       "year": "2011",
       "gender": "FEMALE",
       "ethnicity": "HISPANIC",
       "name": "GERALDINE",
       "cnt": 13
      },
      {
       ...
      }
'''
with open(sys.argv[1]) as data_file:
  data = json.load(data_file)
  for item in data["data"]:
    newitem = {}
    newitem['key'] = item[0]
    newitem['id'] = item[1]
    newitem['year'] = int(item[8])
    newitem['gender'] = item[9]
    newitem['ethnicity'] = item[10]
    newitem['name'] = item[11]
    newitem['cnt'] = int(item[12])
    print(json.dumps(newitem))
</pre>
<p>Luego importamos los datos y nos conectamos listos para correr un par de consultas:</p>
<pre lang="Bash">
./jsonMap.py ~/Downloads/NY.babynames.json > ~/Downloads/NY.babynames.pretty.json
mongoimport --db ny --collection babynames --file ~/Downloads/NY.babynames.pretty.json --upsert
mongo localhost:27017/ny
</pre>
<p>Me encanta que no tuve que crear una base de datos, o una tabla en SQL :-). El documento se describe sólo y ahora estoy listo para hacer consultas:</p>
<pre lang="Javascript">
// ¿Cuantos bebes hispanos entre el 2011 y el 2014?
> db.babynames.find({ "ethnicity": "HISPANIC"}).count()
4254
// Muestre los total agrupando por raza, sexo y año de mayor  a menor
> db.babynames.aggregate([ { $group: { _id: { ethnicity: "$ethnicity", gender: "$gender", year: "$year" }, total: { $sum: "$cnt"} } }, {$sort: {total:-1}} ])
{ "_id" : { "ethnicity" : "HISPANIC", "gender" : "MALE", "year" : 2011 }, "total" : 56236 }
{ "_id" : { "ethnicity" : "WHITE NON HISPANIC", "gender" : "MALE", "year" : 2011 }, "total" : 54392 }
{ "_id" : { "ethnicity" : "WHITE NON HISPANIC", "gender" : "FEMALE", "year" : 2011 }, "total" : 45120 }
{ "_id" : { "ethnicity" : "HISPANIC", "gender" : "FEMALE", "year" : 2011 }, "total" : 41532 }
{ "_id" : { "ethnicity" : "BLACK NON HISPANIC", "gender" : "MALE", "year" : 2011 }, "total" : 24540 }
{ "_id" : { "ethnicity" : "ASIAN AND PACIFIC ISLANDER", "gender" : "MALE", "year" : 2011 }, "total" : 18580 }
{ "_id" : { "ethnicity" : "BLACK NON HISPANIC", "gender" : "FEMALE", "year" : 2011 }, "total" : 17624 }
{ "_id" : { "ethnicity" : "WHITE NON HISPANIC", "gender" : "MALE", "year" : 2014 }, "total" : 14831 }
{ "_id" : { "ethnicity" : "WHITE NON HISPANIC", "gender" : "MALE", "year" : 2013 }, "total" : 14537 }
{ "_id" : { "ethnicity" : "WHITE NON HISP", "gender" : "MALE", "year" : 2012 }, "total" : 14273 }
{ "_id" : { "ethnicity" : "HISPANIC", "gender" : "MALE", "year" : 2012 }, "total" : 13809 }
{ "_id" : { "ethnicity" : "ASIAN AND PACIFIC ISLANDER", "gender" : "FEMALE", "year" : 2011 }, "total" : 13672 }
{ "_id" : { "ethnicity" : "HISPANIC", "gender" : "MALE", "year" : 2013 }, "total" : 13312 }
{ "_id" : { "ethnicity" : "HISPANIC", "gender" : "MALE", "year" : 2014 }, "total" : 13126 }
{ "_id" : { "ethnicity" : "WHITE NON HISPANIC", "gender" : "FEMALE", "year" : 2014 }, "total" : 12884 }
{ "_id" : { "ethnicity" : "WHITE NON HISP", "gender" : "FEMALE", "year" : 2012 }, "total" : 12402 }
{ "_id" : { "ethnicity" : "WHITE NON HISPANIC", "gender" : "FEMALE", "year" : 2013 }, "total" : 12303 }
{ "_id" : { "ethnicity" : "HISPANIC", "gender" : "FEMALE", "year" : 2013 }, "total" : 9755 }
{ "_id" : { "ethnicity" : "HISPANIC", "gender" : "FEMALE", "year" : 2012 }, "total" : 9738 }
{ "_id" : { "ethnicity" : "HISPANIC", "gender" : "FEMALE", "year" : 2014 }, "total" : 9729 }
Type "it" for more
> it
{ "_id" : { "ethnicity" : "BLACK NON HISP", "gender" : "MALE", "year" : 2012 }, "total" : 5965 }
{ "_id" : { "ethnicity" : "ASIAN AND PACI", "gender" : "MALE", "year" : 2012 }, "total" : 5962 }
{ "_id" : { "ethnicity" : "BLACK NON HISPANIC", "gender" : "MALE", "year" : 2013 }, "total" : 5866 }
{ "_id" : { "ethnicity" : "BLACK NON HISPANIC", "gender" : "MALE", "year" : 2014 }, "total" : 5702 }
{ "_id" : { "ethnicity" : "ASIAN AND PACIFIC ISLANDER", "gender" : "MALE", "year" : 2014 }, "total" : 5636 }
{ "_id" : { "ethnicity" : "ASIAN AND PACIFIC ISLANDER", "gender" : "MALE", "year" : 2013 }, "total" : 5281 }
{ "_id" : { "ethnicity" : "ASIAN AND PACI", "gender" : "FEMALE", "year" : 2012 }, "total" : 4338 }
{ "_id" : { "ethnicity" : "BLACK NON HISPANIC", "gender" : "FEMALE", "year" : 2013 }, "total" : 4278 }
{ "_id" : { "ethnicity" : "BLACK NON HISPANIC", "gender" : "FEMALE", "year" : 2014 }, "total" : 4255 }
{ "_id" : { "ethnicity" : "BLACK NON HISP", "gender" : "FEMALE", "year" : 2012 }, "total" : 4243 }
{ "_id" : { "ethnicity" : "ASIAN AND PACIFIC ISLANDER", "gender" : "FEMALE", "year" : 2014 }, "total" : 4198 }
{ "_id" : { "ethnicity" : "ASIAN AND PACIFIC ISLANDER", "gender" : "FEMALE", "year" : 2013 }, "total" : 4012 }
</pre>
<p>Estoy emocionado con la herramienta, pienso usarla para ver los datos en mi trabajo de manera distinta.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Trucos con &#8216;static&#8217; en Java</title>
		<link>http://kodegeek.com/blog/2016/07/31/trucos-con-static-en-java/</link>
		
		<dc:creator><![CDATA[josevnz]]></dc:creator>
		<pubDate>Sun, 31 Jul 2016 10:13:18 +0000</pubDate>
				<category><![CDATA[kodegeek]]></category>
		<category><![CDATA[instance]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[static trick]]></category>
		<guid isPermaLink="false">http://kodegeek.com/blog/?p=4626</guid>

					<description><![CDATA[Les voy a mostrar un pequeño truco en Java. ¿Cual es la salida del siguiente código? package com.kodegeek; public class StaticWeird { public static int count = 0; public static void print() { System.out.println(++count); } /** * What is the output? * @param args */ public static void main(String [] args) { StaticWeird weird = <a class="read-more" href="http://kodegeek.com/blog/2016/07/31/trucos-con-static-en-java/">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>Les voy a mostrar un pequeño truco en Java. ¿Cual es la salida del siguiente código?</p>
<pre lang="Java">
package com.kodegeek;


public class StaticWeird {

    public static int count = 0;

    public static void print() {
        System.out.println(++count);
    }

    /**
     * What is the output?
     * @param args
     */
    public static void main(String [] args) {
        StaticWeird weird = new StaticWeird();
        weird.print();
        weird = null; // Magic hat trick
        weird.print(); // Expecting an NPE :-)?
    }


}
</pre>
<p>No es &#8220;NullPointerException&#8221;. Java sabe que la referencia al método es estática (static) y utiliza esa en vez de una referencia a la instancia de la clase.</p>
<p>¡Java tramposo! <img src="https://s.w.org/images/core/emoji/14.0.0/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Decoración de funciones en Python usando functools</title>
		<link>http://kodegeek.com/blog/2016/03/27/decoracion-de-funciones-en-python-usando-functools/</link>
		
		<dc:creator><![CDATA[josevnz]]></dc:creator>
		<pubDate>Sun, 27 Mar 2016 16:22:45 +0000</pubDate>
				<category><![CDATA[kodegeek]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[coroutine]]></category>
		<category><![CDATA[functools]]></category>
		<category><![CDATA[pipe]]></category>
		<guid isPermaLink="false">http://kodegeek.com/blog/?p=3775</guid>

					<description><![CDATA[La decoración de funciones, usando functools, en Python nos permite eliminar código repetitivo que debemos escribir una y otra vez, y también nos permite modificar funciones existentes con unas pocas lineas de código. Como ejemplo, veamos la tubería que escribí hace tiempo atrás: #!/usr/bin/env python3 import sys, re def grep(expression): while True: text = (yield) <a class="read-more" href="http://kodegeek.com/blog/2016/03/27/decoracion-de-funciones-en-python-usando-functools/">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>La decoración de funciones, usando <a href="https://docs.python.org/2/library/functools.html" target="_blank">functools</a>, en Python nos permite eliminar código repetitivo que debemos escribir una y otra vez, y también nos permite modificar funciones existentes con unas pocas lineas de código. Como ejemplo, veamos la tubería que escribí hace tiempo atrás:</p>
<pre lang="python">
#!/usr/bin/env python3
import sys, re

def grep(expression):
    while True:
        text = (yield)
        if re.search(expression, text):
            print(text)

def cat(file, target):
    next(target)
    with open(file, "r") as fh:
        for line in fh.readlines():
            target.send(line.strip())

def main(args):
    cat(args[0], grep(args[1]))

if __name__ == "__main__":
    main(sys.argv[1:])
</pre>
<p>En la linea 11 pueden ver que hay que preparar el otro lado de la tubería antes de enviar datos, lo cual es un fastidio; Usando &#8216;functools&#8217; podemos crear una anotación a la medida, la cual llamaremos &#8216;pipe&#8217; (lineas 6-12):</p>
<pre lang="python">
#!/usr/bin/env python3
# Shows how to use a function decorator to create an annotation
# josevnz@kodegeek.com
import sys, re, functools

def pipe(function):
    @functools.wraps(function)
    def wrapper(*args, **kwargs):
        target = function(*args, **kwargs)
        next(target) # Saves us the trouble of calling next on the pipe
        return target
    return wrapper

@pipe
def grep(expression):
    while True:
        text = (yield)
        if re.search(expression, text):
            print(text)

def cat(file, target):
    with open(file, "r") as fh:
        for line in fh.readlines():
            target.send(line.strip())

def main(args):
    cat(args[0], grep(args[1]))

if __name__ == "__main__":
    main(sys.argv[1:])
</pre>
<p>En la línea 14 agregamos la nueva anotación y note como eliminamos el &#8216;next(target)&#8217; completamente de la función cat(file, target). No paree gran cosa, pero es una línea menos de código que poner en cada llamada.</p>
<p>¿Pero se pueden hacer otras cosas? Se me ocurren decoraciones como &#8216;count&#8217; o &#8216;sorted&#8217; para funciones que trabajan con listas, estoy aprendiendo como usar esta nueva herramienta, ya les diré como me va.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Usando &#8216;data pipes y coroutines&#8217; en Python</title>
		<link>http://kodegeek.com/blog/2016/03/14/usando-data-pipes-y-coroutines-en-python/</link>
		
		<dc:creator><![CDATA[josevnz]]></dc:creator>
		<pubDate>Mon, 14 Mar 2016 10:00:31 +0000</pubDate>
				<category><![CDATA[kodegeek]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[programación]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[dolartoday]]></category>
		<category><![CDATA[pipes]]></category>
		<guid isPermaLink="false">http://kodegeek.com/blog/?p=3763</guid>

					<description><![CDATA[El concepto es similar a usar &#8216;pipes&#8217; en UNIX. Por ejemplo, en UNIX podemos combinar varias herramientas para filtrar los contenidos en un archivo de texto. ¿Que hace el siguiente comando? # Contar cuantos 'root' hay en el archivo de passwords del servidor Linux cat /etc/password&#124; egrep -i root En Python 3 podemos hacer algo <a class="read-more" href="http://kodegeek.com/blog/2016/03/14/usando-data-pipes-y-coroutines-en-python/">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>El concepto es similar a usar &#8216;pipes&#8217; en UNIX. Por ejemplo, en UNIX podemos combinar varias herramientas para filtrar los contenidos en un archivo de texto.  ¿Que hace el siguiente comando?</p>
<pre lang="bash">
# Contar cuantos 'root' hay en el archivo de passwords del servidor Linux
cat /etc/password| egrep -i root
</pre>
<p>En Python 3 podemos hacer algo como lo siguiente:</p>
<pre lang="python">
#!/usr/bin/env python3
# pipeline.py /etc/passwd root
import sys, re

def grep(expression):
    while True:
        text = (yield) # Espere por el texto enviados por la otra co-rutina
        if re.search(expression, text):
            print(text)

def cat(file, target):
    next(target) # Initialize el otro lado de la tuberia
    with open(file, "r") as fh:
        for line in fh.readlines():
            target.send(line.strip()) # envie la linea a la co-rutina

def main(args):
    cat(args[0], grep(args[1])) # La 'tuberia' se hace de afuera hacia adentro

if __name__ == "__main__":
    main(sys.argv[1:])
</pre>
<p>Otro ejemplo, vamos a modificar el programa que lee los datos del archivo de datos de DolarToday para filtrar por fecha de inicio, final, valores máximos y mínimos. Note como el uso de los filtros es opcional y si no se aplican entonces retornan todos los datos (para mantener el ejemplo sencillo, removí el código que convierte los datos al formato binario):</p>
<pre lang="python">
#!/usr/bin/env python3
# Simple program to save the 'Dolartoday' extra official dollar rates from CSV to a custom format
# Revisited to use data pipes, couroutines
# @author Jose Vicente Nunez, josevnz@kodegeek.com
import os, sys, datetime, re, gzip, struct
from optparse import OptionParser, OptionValueError
from datetime import datetime

class DollarToday:
    
    __slot__ = ("__date", "__value")
    
    def __init__(self, ddate, value):
        if isinstance(ddate, datetime):
            self.__date = ddate
        else:
            self.__date = datetime.strptime(ddate, "%m-%d-%Y")
        self.__value = float(value)
        assert self.__value > 0.0, "{} for date {} is invalid!".format(value, self.__date)
        
    @property
    def date(self):
        return self.__date
    
    @date.setter
    def date(self, date):
        assert isinstance(date, datetime), "Invalid date {}".format(date)
        self.__date = date
    
    @property
    def value(self):
        return self.__value
    
    def __str__(self):
        return "DollarToday[date={}, value={}]".format(self.__date, self.__value)
    
    def __hash__(self):
        return str(id(self))
            
class DollarCollection(dict):
    
    def values(self):
        for dateId in sorted(self.keys()):
            yield self[dateId]
    
    def items(self):
        for dateId in self.keys():
            yield (dateId, self[dateId])
    
    def __iter__(self):
        for dateId in sorted(super().keys()):
            yield dateId

    def __str__(self):
        return "Records={},\n{}".format(len(self), ",\n".join([str(date) for date in self.values()]))

# Pipe: Read from binary file
def readBinary(file, target):
    next(target)
    FILE_MAGIC = b"DLR\x00"
    FILE_VERSION = b"\x00\x01"
    dollarStruct = struct.Struct("<id ")
    try:
       with gzip.open(file, "rb") as fh:
        magic = fh.read(len(FILE_MAGIC))
        if magic != FILE_MAGIC:
          raise "File doesn't look like a KodeGeek.com binary file!"
        version = fh.read(len(FILE_VERSION))
        if version > FILE_VERSION:
          raise "Unsupported file version: {}, expected {}".format(version, FILE_VERSION)
        while True:
          data = fh.read(dollarStruct.size)
          if len(data) == 0:
           break
          numbers = dollarStruct.unpack(data)
          target.send(DollarToday(datetime.fromordinal(numbers[0]), numbers[1]))
    except (Exception) as err:
            raise

# Pipe: Filter elements >= minimum
def min(min, target):
    next(target)
    while True:
        dollar = (yield)
        if dollar.value >= min:
            target.send(dollar)

# Pipe: Filter elements < = max
def max(max, target):
    next(target)
    while True:
        dollar = (yield)
        if dollar.value <= max:
            target.send(dollar)

# Pipe: filter from date
def from_date(fromd, target):
        next(target)
        while True:
            dollar = (yield)
            if dollar.date >= fromd:
                target.send(dollar)

# Pipe: filter from date
def to_date(tod, target):
        next(target)
        while True:
            dollar = (yield)
            if dollar.date < = tod:
                target.send(dollar)

def adder(collection):
    while True:
        dollar = (yield)
        collection[dollar.date] = dollar

def main(options):

    dc = DollarCollection()
    
    pipeline = adder(dc) # Destination of the pipe
    # Add pipes (if needed)
    if options.min != 99999999:
        pipeline = min(options.min, pipeline)
    
    if options.max != -1:
        pipeline = max(options.min, pipeline)

    if options.fromd is not None:
        pipeline = from_date(datetime.strptime(options.fromd, "%Y-%m-%d"), pipeline)
        
    if options.tod is not None:
        pipeline = to_date(datetime.strptime(options.tod, "%Y-%m-%d"), pipeline)

    readBinary(options.report, pipeline) # Start of the pipe that reads the file contents
 
    print("{}".format(dc))

if __name__ == "__main__":
    
    usagetext = """
%prog --report binary.file [--from_date YYYYMMDD] [--to YYYYMMDD] [--min amount] [--max amount]
"""

    op = OptionParser(usage=usagetext)
    op.add_option(
                  "-p", "--report",
                  action="store",
                  dest="report",
                  help="Read the contents from the binary storage and generate a report.")
    op.add_option(
                  "-f", "--from",
                  action="store",
                  dest="fromd",
                  help="Optional filter. Start date yyyy-mm-dd")
    op.add_option(
                  "-t", "--to",
                  action="store",
                  dest="tod",
                  help="Optional filter. End date yyyy-mm-dd")
    op.add_option(
                  "-m", "--min",
                  action="store",
                  dest="min",
                  default=99999999,
                  type="float",
                  help="Optional filter. Minimal amount")
    op.add_option(
                  "-M", "--max",
                  action="store",
                  dest="max",
                  default=-1,
                  type="float",
                  help="Optional filter. Maximum amount")
    
    (options, values) = op.parse_args()
    main(options)

</pre>
<p> </id></p>
<p>Una salida de ejemplo:</p>
<pre lang="bash">
DolarTodayReader.py --report /Users/josevnz/Documents/dolartoday.jose --min 1050 --from 2016-02-23 --to 2016-02-24
Records=2,
DollarToday[date=2016-02-23 00:00:00, value=1060.0],
DollarToday[date=2016-02-24 00:00:00, value=1071.19]
</pre>
<p>Para cerrar, les recomiendo el siguiente tutorial: <a href="http://www.dabeaz.com/coroutines/index.html" target="_blank">http://www.dabeaz.com/coroutines/index.html</a>. La sintaxis es de Python 2.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>&#8216;Contexts&#8217; en Python</title>
		<link>http://kodegeek.com/blog/2016/03/09/contexts-in-python/</link>
		
		<dc:creator><![CDATA[josevnz]]></dc:creator>
		<pubDate>Wed, 09 Mar 2016 11:00:11 +0000</pubDate>
				<category><![CDATA[programación]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[context]]></category>
		<category><![CDATA[dolartoday]]></category>
		<category><![CDATA[file handle]]></category>
		<guid isPermaLink="false">http://kodegeek.com/blog/?p=3760</guid>

					<description><![CDATA[En Python, un contexto (context) es una clase que implementa los métodos &#8216;__enter__&#8217; y &#8216;__exit__&#8217; los cuales son llamados si la clase en llamada con la palabra reservada &#8216;with&#8217;. Por ejemplo, los descriptores de archivo (file handle) en Python se pueden llamar con un contexto, ahorrando llamar &#8216;finally&#8217; para cerrar archivos, sin importar si hay <a class="read-more" href="http://kodegeek.com/blog/2016/03/09/contexts-in-python/">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>En Python, un contexto (context) es una clase que implementa los métodos &#8216;__enter__&#8217; y &#8216;__exit__&#8217; los cuales son llamados si la clase en llamada con la palabra reservada &#8216;with&#8217;. Por ejemplo, los descriptores de archivo (file handle) en Python se pueden llamar con un contexto, ahorrando llamar &#8216;finally&#8217; para cerrar archivos, sin importar si hay un error.</p>
<p>Les traigo de vuelta el programa de &#8216;DolarToday&#8217; el cual escribe datos en un archivo binario, pero ahora utilizando contextos:</p>
<pre lang="python">
#!/usr/bin/env python3
# Simple program to save the 'Dolartoday' extra official dollar rates from CSV to a custom format
# Revisited to use 'contexts' to avoid using finally on exception handling
# @author Jose Vicente Nunez, josevnz@kodegeek.com
import os, sys, datetime, re, gzip, struct
from optparse import OptionParser, OptionValueError
from datetime import datetime

class DollarToday:
    
    def __init__(self, ddate, value):
        if isinstance(ddate, datetime):
            self.__date = ddate
        else:
            self.__date = datetime.strptime(ddate, "%m-%d-%Y")
        self.__value = float(value)
        assert self.__value > 0.0, "{} for date {} is invalid!".format(value, self.__date)
        
    @property
    def date(self):
        return self.__date
    
    @date.setter
    def date(self, date):
        assert isinstance(date, datetime), "Invalid date {}".format(date)
        self.__date = date
    
    @property
    def value(self):
        return self.__value
    
    def __str__(self):
        return "DollarToday[date={}, value={}]".format(self.__date, self.__value)
    
    def __hash__(self):
        return str(id(self))
            
class DollarCollection(dict):
    
    # https://en.wikipedia.org/wiki/List_of_file_signatures
    __FILE_MAGIC = b"DLR\x00"
    __FILE_VERSION = b"\x00\x01"
    __dollarStruct = struct.Struct("<id ")
    
    def values(self):
        for dateId in sorted(self.keys()):
            yield self[dateId]
    
    def items(self):
        for dateId in self.keys():
            yield (dateId, self[dateId])
    
    def __iter__(self):
        for dateId in sorted(super().keys()):
            yield dateId

    def save(self, file):
        try:
            with gzip.open(file, "wb") as fh:
                fh.write(self.__FILE_MAGIC)
                fh.write(self.__FILE_VERSION)
                for dollar in self.values():
                    data = bytearray()
                    data.extend(
                           self.__dollarStruct.pack(
                                dollar.date.toordinal(),
                                dollar.value
                           )
                                )
                    fh.write(data)
        except (Exception) as err:
            raise

    def readBinary(self, file, verbose=False):
        try:
            with gzip.open(file, "rb") as fh:
                magic = fh.read(len(self.__FILE_MAGIC))
                if magic != self.__FILE_MAGIC:
                    raise "File doesn't look like a KodeGeek.com binary file!"
                version = fh.read(len(self.__FILE_VERSION))
                if version > self.__FILE_VERSION:
                    raise "Unsupported file version: {}, expected {}".format(version, self.__FILE_VERSION)
                self.clear()
                while True:
                    data = fh.read(self.__dollarStruct.size)
                    if len(data) == 0:
                        break
                    numbers = self.__dollarStruct.unpack(data)
                    #if verbose:
                    #    print("{}".format(",".join([str(x) for x in numbers])))
                    dolar = DollarToday(
                                    datetime.fromordinal(numbers[0]),
                                    numbers[1]
                                    )
                    self[dolar.date] = dolar
        except (Exception) as err:
            raise
                
    def readCsv(self, file):
        tempMap = {}
        try:
            with open(file, "r", encoding="UTF-8") as fh:
                for line in fh.readlines():
                    # 6-23-2010       9.92
                    (date, value) = line.strip().split("\t")
                    if re.match("Fecha", date):
                        continue
                    try:
                        dolVsBol = DollarToday(date, value)
                        tempMap[dolVsBol.date] = dolVsBol
                    except (Exception) as err:
                        print(err)
            self.clear()
            self.update(tempMap)
        except (Exception) as err:
            raise

    def __str__(self):
        return "Records={},\n{}".format(len(self), ",\n".join([str(date) for date in self.values()]))

def main(options):

    verbose = options.verbose
    dc = DollarCollection()
    if options.report == None:
        dc.readCsv(options.read)
        dc.save(options.write)
    else:
        dc.readBinary(options.report, verbose)
    if verbose:
        print("{}".format(dc))

if __name__ == "__main__":
    
    usagetext = """
%prog --read csv.file --write binary.file

Or:

%prog --report binary.file

"""

    op = OptionParser(usage=usagetext)
    op.add_option(
                  "-r", "--read",
                  action="store", 
                  dest="read", 
                  help="Full path to CSV file with date,value pairs per line")
    op.add_option(
                  "-w", "--write", 
                  action="store", 
                  dest="write", 
                  help="Full path to destination file in binary format")
    op.add_option(
                  "-p", "--report", 
                  action="store", 
                  dest="report",
                  help="Read the contents of the binary storage and generate a report. Incompatible with --read and --write")
    op.add_option(
                  "-v", "--verbose", 
                  action="store_true", 
                  default=False, 
                  dest="verbose", 
                  help="Enable verbose mode")
    
    (options, values) = op.parse_args()

    main(options)
</id></pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Functors en Python</title>
		<link>http://kodegeek.com/blog/2016/03/08/functors-en-python/</link>
		
		<dc:creator><![CDATA[josevnz]]></dc:creator>
		<pubDate>Tue, 08 Mar 2016 11:00:56 +0000</pubDate>
				<category><![CDATA[programación]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[elections]]></category>
		<category><![CDATA[functors]]></category>
		<guid isPermaLink="false">http://kodegeek.com/blog/?p=3757</guid>

					<description><![CDATA[Un &#8216;functor&#8217; es u objeto el cual puede ser llamado como si fuera una función. En Python simplemente hay que implementar el método &#8216;__call__&#8217;. En este ejemplo, vamos a tomar a los candidatos presidenciales del articulo anterior y vamos a escribir una pequeña clase (llamada SortKey) la cual nos va a permitir ordenar por cualquier <a class="read-more" href="http://kodegeek.com/blog/2016/03/08/functors-en-python/">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>Un &#8216;functor&#8217; es u objeto el cual puede ser llamado como si fuera una función. En Python simplemente hay que implementar el método &#8216;__call__&#8217;. En este ejemplo, vamos a tomar a los candidatos presidenciales del articulo anterior y vamos a escribir una pequeña clase (llamada SortKey) la cual nos va a permitir ordenar por cualquier attributo o combinación de attributos (en nuestro ejemplo vamos a ordendar por sexo y luego por edad):</p>
<pre lang="python">
#!/usr/bin/env python3
# A little fun with the candidates for the US presidency for 2016 election year
# Revisited versions with functors, slots, abstract classes
# @author josevnz@kodegeek.com
#
import abc

class SortKey:
    
    def __init__(self, *attributes):
        self.attributes = attributes
    def __call__(self, instance):
        return [getattr(instance, attribute) for attribute in self.attributes]

class Candidate(metaclass=abc.ABCMeta):
    
    __slots__ = ("__name", "__sex", "__age")

    @abc.abstractmethod    
    def __init__(self, name, sex="F", age=21):
        self.__name = name
        self.__sex = "F" if sex not in ["M", "F"] else sex
        self.__age = 21 if not (21 < = age <= 120) else age
    
    @property
    def name(self):
        return self.__name
    
    def get_party(self):
        raise NotImplemented()
    
    party = abc.abstractproperty(get_party)
    
    @property
    def sex(self):
        return self.__sex
    
    @property
    def age(self):
        return self.__age
    
    def __hash__(self):
        return hash(id(self))
    
    def __str__(self):
        return "{}=[name={}, sex={}, age={}]".format(self.__class__.__name__,
                                                            self.__name,
                                                            self.__sex,
                                                            self.__age
                                                            )
    
    def __repr__(self):
        return "{}({}{}{}{})".format(
                                 self.__class__.__name__, 
                                 self.__name, 
                                 self.__sex, 
                                 self.__age
                                )
    
    @abc.abstractmethod
    def __bool__(self):
        raise NotImplemented()
    
    def __lt__(self, other):
        return self.__age < other.__age
    
    def __gt__(self, other):
        return self.__age > other.__age
    
    def __eq__(self, other):
        return (
                self.__age == other.__age and 
                self.__name == other.__name and
                self.__sex == other.__sex
                )

class Republican(Candidate):
    
    def __init__(self, name, sex="F", age=21):
        return super().__init__(name, sex, age)
    
    party = "Replublican Party (GOP)"
    
    def __bool__(self):
        return False
            
class Democrat(Candidate):
    
    def __init__(self, name, sex="F", age=21):
        return super().__init__(name, sex, age)
    
    party = "Democratic Party"
    
    def __bool__(self):
        return True
    
if __name__ == "__main__":
        candidates = []
        candidates.append(Democrat("Hillary Clinton", "F", 68))
        candidates.append(Republican("Donald Trump", "M", 70))
        candidates.append(Democrat("Bernie Sanders", "M", 75))
        candidates.append(Republican("Marco Rubio", "M", 45))
        candidates.sort(key=SortKey("sex", "age"), reverse=False)
        for candidate in candidates:
            isDemocrat = "yes" if candidate else "no"
            print("Candidate: {}, democrat? {}".format(candidate, isDemocrat))        
</pre>
<p>Y la salida (fíjense que definimos el orden en la linea 103, ademas de que ordenamos la lista primero para después utilizarla):</p>
<pre lang="bash">
Candidate: Democrat=[name=Hillary Clinton, sex=F, age=68], democrat? yes
Candidate: Republican=[name=Marco Rubio, sex=M, age=45], democrat? no
Candidate: Republican=[name=Donald Trump, sex=M, age=70], democrat? no
Candidate: Democrat=[name=Bernie Sanders, sex=M, age=75], democrat? yes
</pre>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Slots, Abstracts classes en Python 3</title>
		<link>http://kodegeek.com/blog/2016/03/07/slots-abstracts-classes-en-python-3/</link>
		
		<dc:creator><![CDATA[josevnz]]></dc:creator>
		<pubDate>Mon, 07 Mar 2016 11:00:31 +0000</pubDate>
				<category><![CDATA[kodegeek]]></category>
		<category><![CDATA[programación]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[abstract class]]></category>
		<category><![CDATA[elections]]></category>
		<category><![CDATA[slots]]></category>
		<guid isPermaLink="false">http://kodegeek.com/blog/?p=3752</guid>

					<description><![CDATA[¿Se acuerdan del ejemplo anterior usando classes en Python? En este caso hice un par de cambios para aprender más sobre clases abstractas y slots (una manera de ahorrar memoria en Python cuando creamos objetos). En este caso: Uso un &#8216;slot&#8217; para guardar sólo 3 atributos que son definidos en el constructor Declaro la propiedad <a class="read-more" href="http://kodegeek.com/blog/2016/03/07/slots-abstracts-classes-en-python-3/">[&#8230;]</a>]]></description>
										<content:encoded><![CDATA[<p>¿Se acuerdan del ejemplo anterior usando classes en Python? En este caso hice un par de cambios para aprender más sobre clases abstractas y slots (una manera de ahorrar memoria en Python cuando creamos objetos).</p>
<p>En este caso:</p>
<ul>
<li>Uso un &#8216;slot&#8217; para guardar sólo 3 atributos que son definidos en el constructor<br />
Declaro la propiedad &#8216;party&#8217; como abstracta ya que quiero que el que extienda las clases se vea forzado a implementar un método adecuado que me diga de que partido viene<br />
También obligo al usuario a declarar el método &#8216;__bool__&#8217; el cual utilizo para saber si es Democrata u otra clase (si, el ejemplo es forzado :-))</li>
</ul>
<pre lang="python">#!/usr/bin/env python3
# A little fun with the candidates for the US presidency for 2016 election year
# Revisited versions with slots, abstract classes
# @author josevnz@kodegeek.com
#
import abc

class Candidate(metaclass=abc.ABCMeta):
    
    __slots__ = ("__name", "__sex", "__age")

    @abc.abstractmethod    
    def __init__(self, name, sex="F", age=21):
        self.__name = name
        self.__sex = "F" if sex not in ["M", "F"] else sex
        self.__age = 21 if not (21 &lt; = age &lt;= 120) else age
    
    @property
    def name(self):
        return self.__name
    
    def get_party(self):
        raise NotImplemented()
    
    party = abc.abstractproperty(get_party)
    
    @property
    def sex(self):
        return self.__sex
    
    @property
    def age(self):
        return self.__age
    
    def __hash__(self):
        return hash(id(self))
    
    def __str__(self):
        return "{}=[name={}, sex={}, age={}]".format(self.__class__.__name__,
                                                            self.__name,
                                                            self.__sex,
                                                            self.__age
                                                            )
    
    def __repr__(self):
        return "{}({}{}{}{})".format(
                                 self.__class__.__name__, 
                                 self.__name, 
                                 self.__sex, 
                                 self.__age
                                )
    
    @abc.abstractmethod
    def __bool__(self):
        raise NotImplemented()
    
    def __lt__(self, other):
        return self.__age < other.__age 

     def __gt__(self, other): 
        return self.__age > other.__age
    
    def __eq__(self, other):
        return (
                self.__age == other.__age and 
                self.__name == other.__name and
                self.__sex == other.__sex
                )

class Republican(Candidate):
    
    def __init__(self, name, sex="F", age=21):
        return super().__init__(name, sex, age)
    
    party = "Republican Party (GOP)"
    
    def __bool__(self):
        return False
            
class Democrat(Candidate):
    
    def __init__(self, name, sex="F", age=21):
        return super().__init__(name, sex, age)
    
    party = "Democratic Party"
    
    def __bool__(self):
        return True
    
if __name__ == "__main__":
        candidates = []
        candidates.append(Democrat("Hillary Clinton", "F", 68))
        candidates.append(Republican("Donald Trump", "M", 70))
        candidates.append(Democrat("Bernie Sanders", "M", 75))
        candidates.append(Republican("Marco Rubio", "M", 45))
        for candidate in sorted(candidates, reverse=True):
            isDemocrat = "yes" if candidate else "no"
            print("Candidate: {}, democrat? {}".format(candidate, isDemocrat))        
</pre>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
