<?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>Temas Innecesarios &#187; jcarrascal</title>
	<atom:link href="http://jcesar.artelogico.com/author/jcarrascal/feed/" rel="self" type="application/rss+xml" />
	<link>http://jcesar.artelogico.com</link>
	<description>Estará listo en seis meses así tengamos que trabajar en el por un año.</description>
	<lastBuildDate>Sun, 25 Sep 2011 01:27:04 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>Instalando Python 2.7 y Django 1.3 en Dreamhost</title>
		<link>http://jcesar.artelogico.com/2011/09/instalando-python-2-7-y-django-1-3-en-dreamhost/</link>
		<comments>http://jcesar.artelogico.com/2011/09/instalando-python-2-7-y-django-1-3-en-dreamhost/#comments</comments>
		<pubDate>Fri, 02 Sep 2011 03:55:18 +0000</pubDate>
		<dc:creator>jcarrascal</dc:creator>
				<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://jcesar.artelogico.com/?p=229</guid>
		<description><![CDATA[Dreamhost es genial para ejecutar sitios web desarrollados con Django. Nos dan acceso SSH que nos permite ejecutar los comandos de manage.py con facilidad; ejecutan Django con Passanger que es más rápido y usa menos memoria que mod_python; y por supuesto: recursos ilimitados, bajos costos y excelente soporte técnico. Para ejecutar una aplicación Django en [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.dreamhost.com/r.cgi?357959">Dreamhost</a> es genial para ejecutar sitios web desarrollados con <a href="https://www.djangoproject.com/">Django</a>. Nos dan acceso SSH que nos permite ejecutar los comandos de manage.py con facilidad; ejecutan Django con <a href="http://www.modrails.com/">Passanger</a> que es más rápido y usa menos memoria que mod_python; y por supuesto: recursos ilimitados, bajos costos y excelente soporte técnico.</p>
<p>Para ejecutar una aplicación Django en Dreamhost solo es necesario configurar el dominio para que utilice Passenger. Es cuestión de editar el dominio y seleccionar la caja que dice &#8220;Passenger (Ruby/Python apps only)&#8221; y asegurarse que la carpeta pública sea algo como <code>/dominio.com/public/</code>.</p>
<p>El panel de control configura el resto. Sin embargo, la versión de Python y Django que instalan en sus servidores está muy desactualizada (Python 2.4, Django 1.1 por defecto). Por suerte nos permiten compilar programas en el servidor así que instalar una versión moderna es extremadamente fácil.</p>
<p>Este es el proceso que yo utilizo para instalar versiones actualizadas de Python y Django en mis servidores ( sí, plural <img src='http://jcesar.artelogico.com/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' />  ). Pueden ir copiando y pegando línea por línea en la ventana SSH. Casi podría ser un script automatizado, excepto por que hay que salir y volver a entrar en cierta parte.</p>
<p>Para empezar, hay que conectarse por SSH a su servidor. En Windows pueden usar <a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/">Putty</a>, en Linux y MacOS ya deberían tener instalado el comando ssh que viene con OpenSSH.</p>
<p>Una vez conectados, vamos a crear una carpeta para descargar los instaladores.</p>
<pre>mkdir ~/installers
cd ~/installers</pre>
<p>Ahora vamos a descargar, compilar e instalar Python 2.7.2 en unos pocos comandos:</p>
<pre>wget http://python.org/ftp/python/2.7.2/Python-2.7.2.tgz
tar -xzvf Python-2.7.2.tgz
cd Python-2.7.2
nice ./configure --prefix=$HOME/local
nice make
nice make install</pre>
<p>Ahora tenemos que configurar la shell para que use el nuevo Python 2.7.2 y no la versión desactualizada que estaba instalada.</p>
<pre>echo "export PYTHONPATH=\"$HOME/local/lib/python2.7/site-packages\"" >> ~/.bash_profile
echo "export LD_LIBRARY_PATH=\"$HOME/packages/lib\"" >> ~/.bash_profile
echo "export PATH=\"$HOME/local/bin:$PATH\"" >> ~/.bash_profile
logout</pre>
<p>Esta es la parte en que tenemos que salir de la sesión y volver a ingresar.</p>
<p>Ahora vamos a comprobar que el nuevo Python quedó instalado. El siguiente comando debería decir algo como <code>/home/&lt;usuario&gt;/local/bin/python</code></p>
<pre>which python</pre>
<p>¿Funcionó? Bueno, continuemos.</p>
<p>Nos falta bajar easy_install que luego utilizaremos para actualizar los demás paquetes.</p>
<pre>cd ~/installers
wget http://pypi.python.org/packages/2.7/s/setuptools/setuptools-0.6c11-py2.7.egg
sh setuptools-0.6c11-py2.7.egg</pre>
<p>Ahora podemos usar el comando easy_install para bajar el resto de utilidades.</p>
<pre>easy_install http://pypi.python.org/packages/source/P/Paste/Paste-1.7.5.1.tar.gz
easy_install PIL
easy_install MySQL-python
easy_install Django</pre>
<p>La utilidad de Django y MySQL-python debería ser bastante obvia en este punto ¿No?. <a href="http://www.pythonware.com/products/pil/">PIL</a> es un paquete para manipular imágenes (Para crear thumbnails, dibujar y convertir imágenes). Y Paste lo utilizaremos más adelante para depurar problemas en el servidor.</p>
<p>Nos falta configurar Passenger para que utilice nuestro Python. Para esto creamos un archivo passenger_wsgi.py con el siguiente contenido</p>
<pre>#!$HOME/local/bin/python
# -*- coding: utf-8 -*-
INTERP = "/home/&lt;usuario&gt;/local/bin/python"
DEBUG = True

import sys
import os
if sys.executable != INTERP: os.execl(INTERP, INTERP, *sys.argv)
sys.path.append(os.getcwd())
os.environ['DJANGO_SETTINGS_MODULE'] = "&lt;proyecto&gt;.settings"
import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

if DEBUG:
	from paste.exceptions.errormiddleware import ErrorMiddleware
	application = ErrorMiddleware(application, debug=True)</pre>
<p>Recuerden cambiar &lt;usuario&gt; y &lt;proyecto&gt; por el nombre de su usuario y proyecto. Hay una variable DEBUG que permite mostrar en el browser las excepciones que se produzcan. En producción se debería desactivar, pero es muy útil mientras estamos desarrollando. Para esto es que necesitábamos Paste.</p>
<p>Su carpeta $HOME ahora se debería ver así:</p>
<pre>installers/
local/
dominio.com/
dominio.com/public/
dominio.com/&lt;proyecto&gt;
dominio.com/passenger_wsgi.py</pre>
<p>Con esto ya debería funcionar. Puedes probar en el browser si carga tu aplicación y si hay algún error te debería mostrar la excepción. No olvides desactivar la depuración una vez que pases a producción poniendo DEBUG=False en passenger.py y en settings.py.</p>
]]></content:encoded>
			<wfw:commentRss>http://jcesar.artelogico.com/2011/09/instalando-python-2-7-y-django-1-3-en-dreamhost/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>FlashPunk para Windows Phone 7</title>
		<link>http://jcesar.artelogico.com/2011/08/flashpunk-para-windows-phone-7/</link>
		<comments>http://jcesar.artelogico.com/2011/08/flashpunk-para-windows-phone-7/#comments</comments>
		<pubDate>Thu, 04 Aug 2011 03:03:22 +0000</pubDate>
		<dc:creator>jcarrascal</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[c#]]></category>
		<category><![CDATA[flashpunk]]></category>
		<category><![CDATA[wp7]]></category>
		<category><![CDATA[xna]]></category>

		<guid isPermaLink="false">http://jcesar.artelogico.com/?p=214</guid>
		<description><![CDATA[Acabo de publicar mi versión de FlashPunk para Windows Phone 7 (XNA). FlashPunk es un engine para hacer juegos 2D tipo Mario o AngryBirds. Fuentes &#124; Más info. El screenshot es el emulador de WP7 corriendo el Splash animado de FlashPunk.]]></description>
			<content:encoded><![CDATA[<p>Acabo de publicar mi versión de FlashPunk para Windows Phone 7 (XNA). FlashPunk es un engine para hacer juegos 2D tipo Mario o AngryBirds. <a href="https://petprojects.googlecode.com/svn/trunk/xna-flashpunk">Fuentes</a> | <a href="http://flashpunk.net/forums/index.php?topic=2772.0">Más info</a>.</p>
<div id="attachment_222" class="wp-caption aligncenter" style="width: 565px"><a href="http://jcesar.artelogico.com/wp-content/uploads/2011/08/FlashPunkSplash.png" rel="shadowbox[sbpost-214];player=img;"><img src="http://jcesar.artelogico.com/wp-content/uploads/2011/08/FlashPunkSplash-555x1024.png" alt="" title="FlashPunkSplash" width="555" height="1024" class="size-large wp-image-222" /></a><p class="wp-caption-text">Splash de FlashPunk en el emulador de Windows Phone 7 (XNA)</p></div>
<p>El screenshot es el emulador de WP7 corriendo el Splash animado de FlashPunk.</p>
]]></content:encoded>
			<wfw:commentRss>http://jcesar.artelogico.com/2011/08/flashpunk-para-windows-phone-7/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Serializar a JSON en Python</title>
		<link>http://jcesar.artelogico.com/2011/06/serializar-a-json-en-python/</link>
		<comments>http://jcesar.artelogico.com/2011/06/serializar-a-json-en-python/#comments</comments>
		<pubDate>Fri, 03 Jun 2011 19:03:15 +0000</pubDate>
		<dc:creator>jcarrascal</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[Ajax]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[JSON]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://jcesar.artelogico.com/?p=212</guid>
		<description><![CDATA[Un truquillo de esos que ahorran mucho tiempo: Para serializar a JSON sin utilizar librerías externas simplemente es cuestión de crear un diccionario, convertirlo en string y reemplazar comillas simples por comillas dobles: &#62;&#62;&#62; print unicode({ 'foo': 'hello', 'bar': [4, 8, 15, 16, 23, 42] }).replace("'", '"') {"foo": "hello", "bar": [4, 8, 15, 16, 23, [...]]]></description>
			<content:encoded><![CDATA[<p>Un truquillo de esos que ahorran mucho tiempo: Para serializar a JSON sin utilizar librerías externas simplemente es cuestión de crear un diccionario, convertirlo en string y reemplazar comillas simples por comillas dobles:</p>
<pre class=prettyprint>
&gt;&gt;&gt; print unicode({ 'foo': 'hello', 'bar': [4, 8, 15, 16, 23, 42] }).replace("'", '"')
{"foo": "hello", "bar": [4, 8, 15, 16, 23, 42]}
</pre>
<p>El resultado es un string JSON válido y se puede convertir en un objeto JavaScript con jQuery.parseJSON(str). Este truco maneja perfectamente cadenas unicode o con comillas.</p>
<p>El único problema es al serializar un objeto que pueda contener None (El null de Python) en sus propiedades. En este caso es necesario definir antes el siguiente objeto:</p>
<pre class=prettyprint>
class NullType:
	def __repr__(self):
		return 'null'
null = NullType()
</pre>
<p>Y al serializar usarlo así:</p>
<pre class=prettyprint>
&gt;&gt;&gt; print unicode({ 'date_of_birth': <strong>user.date_of_birth or null</strong> }).replace("'", '"')
{"date_of_birth": null}
</pre>
<p>Funciona muy bien cuando hay que regresar una respuesta rápida por Ajax. Si es algo más complicado les recomiendo usar <a href="http://pypi.python.org/pypi/simplejson/">SimpleJSON</a> que ya viene incluido en Django o para modelos usar la <a href="https://docs.djangoproject.com/en/dev/topics/serialization/">serialización de Django</a></p>
]]></content:encoded>
			<wfw:commentRss>http://jcesar.artelogico.com/2011/06/serializar-a-json-en-python/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>¿Diferencias en los browsers? ¡Blasfemia!</title>
		<link>http://jcesar.artelogico.com/2011/03/%c2%bfdiferencias-en-los-browsers-%c2%a1blasfemia/</link>
		<comments>http://jcesar.artelogico.com/2011/03/%c2%bfdiferencias-en-los-browsers-%c2%a1blasfemia/#comments</comments>
		<pubDate>Fri, 18 Mar 2011 04:08:59 +0000</pubDate>
		<dc:creator>jcarrascal</dc:creator>
				<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://jcesar.artelogico.com/?p=184</guid>
		<description><![CDATA[¿Sería mucho pedirle a los señores fabricantes de browsers se pongan de acuerdo al implementar un estándar? Las sombras se ven diferentes en Firefox 4 RC, Chrome 9 e Internet Explorer 9. Y mientras estamos deseando cosas imposibles&#8230; ¿Sería posible que Microsoft implemente el estándar sin BUGS? El IE9 no muestra la sombra si la [...]]]></description>
			<content:encoded><![CDATA[<p>¿Sería mucho pedirle a los señores fabricantes de browsers se pongan de acuerdo al implementar un estándar? Las sombras se ven diferentes en Firefox 4 RC, Chrome 9 e Internet Explorer 9.</p>
<p><a href="http://jcesar.artelogico.com/wp-content/uploads/2011/01/box-shadow.png" rel="shadowbox[sbpost-184];player=img;"><img src="http://jcesar.artelogico.com/wp-content/uploads/2011/01/box-shadow-300x120.png" alt="Firefox 4, Chrome 9 y IE9 renderizan box-shadow de forma diferente." title="box-shadow" width="300" height="120" class="aligncenter size-medium wp-image-194" /></a></p>
<p>Y mientras estamos deseando cosas imposibles&#8230; ¿Sería posible que Microsoft implemente el estándar sin BUGS? El IE9 no muestra la sombra si la tabla tiene box-shadow y border-collapse: collapse.</p>
<p>Bueno, será volver al viejo truco de tener una hoja de estilos para todos los browser, una para IE7, otra para IE8 y ahora una más para IE9.</p>
<p>Aquí está el archivo de prueba: <a href='http://jcesar.artelogico.com/wp-content/uploads/2011/03/test.html'>box-shadow test</a></p>
]]></content:encoded>
			<wfw:commentRss>http://jcesar.artelogico.com/2011/03/%c2%bfdiferencias-en-los-browsers-%c2%a1blasfemia/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Django Haystack + Xapian en Dreamhost</title>
		<link>http://jcesar.artelogico.com/2011/01/django-haystack-xapian-en-dreamhost/</link>
		<comments>http://jcesar.artelogico.com/2011/01/django-haystack-xapian-en-dreamhost/#comments</comments>
		<pubDate>Thu, 13 Jan 2011 16:23:09 +0000</pubDate>
		<dc:creator>jcarrascal</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[django]]></category>
		<category><![CDATA[dreamhost]]></category>
		<category><![CDATA[haystack]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[xapian]]></category>

		<guid isPermaLink="false">http://jcesar.artelogico.com/?p=182</guid>
		<description><![CDATA[Casi me saco un ojo instalando Xapian en Dreamhost. Lo necesito para usarlo como máquina de búsqueda Haystack en un sitio Django que estoy haciendo (Ya casi les digo cuál, je je). Haystack es una aplicación Django (un componente) que trae todo listo para hacer un buscador. Solo es cuestión de configurarlo, cambiar unas cuantas [...]]]></description>
			<content:encoded><![CDATA[<p>Casi me saco un ojo instalando Xapian en Dreamhost. Lo necesito para usarlo como máquina de búsqueda Haystack en un sitio Django que estoy haciendo (Ya casi les digo cuál, je je).</p>
<p><a href="http://haystacksearch.org/">Haystack</a> es una aplicación <a href="http://www.djangoproject.com/">Django</a> (un componente) que trae todo listo para hacer un buscador. Solo es cuestión de configurarlo, cambiar unas cuantas plantillas HTML y el buscador queda listo. Y <a href="http://xapian.org/">Xapian</a> es la máquina de búsqueda que hace todo el trabajo. Con Haystack tambien se puede usar Solr y Whoosh pero por desempeño me decidí por Xapian.</p>
<p>Me tocó probar muchas versiones y bajar un paquete adicional para poder compilar Xapian. Para documentar el proceso, y por si a alguien le puede servir, aquí están los pasos que tuve que realizar:</p>
<p><strong>NOTA:</strong> Yo estoy usando un Python 2.7 que compilé e instalé en el mismo servidor. Todo está en ~/local/ así que si necesitan instalar en otro directorio tendrán que modificar las rutas en los comandos.</p>
<p>Instalar Haystack es fácil. Solo es cuestión de instalar estos dos paquetes:</p>
<pre class="prettify">
easy_install http://pypi.python.org/packages/source/d/django-haystack/django-haystack-1.1.0.tar.gz
easy_install http://pypi.python.org/packages/source/x/xapian-haystack/xapian-haystack-1.1.5beta.tar.gz
</pre>
<p>Esas son las únicas versiones que me funcionaron. Todas las demás combinaciones que probé me dieron errores al crear el índice.</p>
<p>Xapian es más difícil. Primero hay que instalar util-linux-ng y no funciona cualquier versión. La única que no me dio problemas fue la 2.16 (y las probé casi todas):</p>
<pre class="prettify">
cd ~/installers
wget ftp://ftp.kernel.org/pub/linux/utils/util-linux/v2.16/util-linux-ng-2.16.tar.gz
tar -xzf util-linux-ng-2.16.tar.gz
cd util-linux-ng-2.16
nice ./configure --prefix=$HOME/local
nice make
nice make install
</pre>
<p>Ignoren los errores de chgrp que salen al hace el make install. Ahora sí, instalemos xapian-core:</p>
<pre class="prettify">
cd ~/installers
wget http://oligarchy.co.uk/xapian/1.2.4/xapian-core-1.2.4.tar.gz
tar -xzf xapian-core-1.2.4.tar.gz
cd xapian-core-1.2.4
nice ./configure --prefix=$HOME/local CPPFLAGS="-I$HOME/local/include" LDFLAGS="-L$HOME/local/lib"
nice make
nice make install
</pre>
<p>CPPFLAGS y LDFLAGS son necesarios para que encuentre las cabeceras y la biblioteca que instalamos con util-linux-ng. Ahora necesitamos xapian-bindings:</p>
<pre class="prettify">
cd ~/installers
wget http://oligarchy.co.uk/xapian/1.2.4/xapian-bindings-1.2.4.tar.gz
tar -xzf xapian-bindings-1.2.4.tar.gz
cd xapian-bindings-1.2.4
nice ./configure --prefix=$HOME/local CPPFLAGS="-I$HOME/local/include" LDFLAGS="-L$HOME/local/lib" XAPIAN_CONFIG=$HOME/local/bin/xapian-config --without-php --without-ruby --without-java --without-csharp --without-tcl --with-python
nice make
nice make install
</pre>
<p>Listo, ahora solo es cuestión de seguir las instrucciones del <a href="http://docs.haystacksearch.org/dev/tutorial.html">tutorial de Haystack</a>.</p>
<p><strong>PD:</strong> En Windows es mucho más sencillo porque se pueden bajar los binarios pre-compilados aunque dan uno que otro error. No les recomiendo que lo usen para producción pero para desarrollo funciona. Para instalar todo solo hay que ejecutar estos comandos:</p>
<pre class="prettify">
easy_install http://pypi.python.org/packages/source/d/django-haystack/django-haystack-1.1.0.tar.gz
easy_install http://pypi.python.org/packages/source/x/xapian-haystack/xapian-haystack-1.1.5beta.tar.gz
</pre>
<p>Y luego bajar este instalador y correrlo como administrador:</p>
<p>http://www.flax.co.uk/xapian/124/xapian-python-bindings%20for%20Python%202.7.0%20-1.2.4.win32.exe</p>
<p>Para crear el índice manage.py rebuild_index me da un error todo raro pero manage.py update_index funciona sin problemas.</p>
]]></content:encoded>
			<wfw:commentRss>http://jcesar.artelogico.com/2011/01/django-haystack-xapian-en-dreamhost/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Error 0&#215;80049228 usando autenticación de Windows Live ID</title>
		<link>http://jcesar.artelogico.com/2010/10/error-0x80049228-usando-autenticacion-de-windows-live-id/</link>
		<comments>http://jcesar.artelogico.com/2010/10/error-0x80049228-usando-autenticacion-de-windows-live-id/#comments</comments>
		<pubDate>Tue, 05 Oct 2010 15:07:31 +0000</pubDate>
		<dc:creator>jcarrascal</dc:creator>
				<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://jcesar.artelogico.com/?p=170</guid>
		<description><![CDATA[Horas y horas perdidas luchando con ese error. Estoy tratando de agregar autenticación por Live ID a un sitio que estoy haciendo. La idea es que quien tenga una cuenta de MSN Messenger/Hotmail pueda entrar al sitio sin registrarse. Al parecer ya funciona pero de vez en cuando me redirige a esta página: https://consent.live.com/Error.aspx?mkt=en-US&#038;Detail=0&#215;80049228&#038;ru=&#8230; &#8230; [...]]]></description>
			<content:encoded><![CDATA[<p>Horas y horas perdidas luchando con ese error.</p>
<p>Estoy tratando de agregar autenticación por Live ID a un sitio que estoy haciendo. La idea es que quien tenga una cuenta de MSN Messenger/Hotmail pueda entrar al sitio sin registrarse.</p>
<p>Al parecer ya funciona pero de vez en cuando me redirige a esta página:</p>
<p>https://consent.live.com/Error.aspx?mkt=en-US&#038;Detail=0&#215;80049228&#038;ru=&#8230;</p>
<p>&#8230; con un amable mensaje que dice que hay un problema *con mi sitio*.</p>
<p>Lo poquito que he podido averiguar es que alguna cookie de <strong>live.com</strong> expira y la única forma es cerrar la sesión en <strong>live.com</strong> y volver a autenticarse.</p>
<p>Lo peor es que <strong>¡le echan la culpa a mi sitio cuando yo no puedo ni mirar las cookies de live.com!</strong></p>
<p>Será ponerlo en el manual: Si les sale error 0&#215;80049228 cierre la sesión en live.com y vuelva a intentar.</p>
]]></content:encoded>
			<wfw:commentRss>http://jcesar.artelogico.com/2010/10/error-0x80049228-usando-autenticacion-de-windows-live-id/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Adicionar días hábiles en python</title>
		<link>http://jcesar.artelogico.com/2010/09/adicionar-dias-habiles-en-python/</link>
		<comments>http://jcesar.artelogico.com/2010/09/adicionar-dias-habiles-en-python/#comments</comments>
		<pubDate>Thu, 02 Sep 2010 03:02:06 +0000</pubDate>
		<dc:creator>jcarrascal</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[arithmetic]]></category>
		<category><![CDATA[business days]]></category>
		<category><![CDATA[date]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[timedelta]]></category>
		<category><![CDATA[workdays]]></category>

		<guid isPermaLink="false">http://jcesar.artelogico.com/?p=172</guid>
		<description><![CDATA[Una función para sumar o restar días hábiles en Python: from datetime import date, timedelta (MON, TUE, WED, THU, FRI, SAT, SUN) = range(7) def addworkdays(start, days, holidays=(), workdays=(MON,TUE,WED,THU,FRI)): weeks, days = divmod(days, len(workdays)) result = start + timedelta(weeks=weeks) lo, hi = min(start, result), max(start, result) count = len([h for h in holidays if h [...]]]></description>
			<content:encoded><![CDATA[<p>Una función para sumar o restar días hábiles en Python:</p>
<pre class="pretiffy">
from datetime import date, timedelta

(MON, TUE, WED, THU, FRI, SAT, SUN) = range(7)

def addworkdays(start, days, holidays=(), workdays=(MON,TUE,WED,THU,FRI)):
    weeks, days = divmod(days, len(workdays))
    result = start + timedelta(weeks=weeks)
    lo, hi = min(start, result), max(start, result)
    count = len([h for h in holidays if h >= lo and h <= hi])
    days += count * (-1 if days < 0 else 1)
    for _ in range(days):
        result += timedelta(days=1)
        while result in holidays or result.weekday() not in workdays:
            result += timedelta(days=1)
    return result
</pre>
<p>Ejemplos:</p>
<pre class="pretiffy">
today = date.today()
print 'hoy:', today             # 2010-09-01
print addworkdays(today, 1)     # 2010-09-02
print addworkdays(today, 3)     # 2010-09-06
print addworkdays(today, 20)    # 2010-09-29

print addworkdays(today, -1)    # 2010-08-31
print addworkdays(today, -3)    # 2010-08-27
print addworkdays(today, -20)	# 2010-08-04
</pre>
<p>Para especificar los días festivos deben pasar un iterable (un tupple o una lista) con las días que serían días hábiles si no fueran declarados festivos. <strong>IMPORTANTE: Solo pasen los festivos que caen entre lunes a viernes. No pasen, por ejemplo, el Domingo de Ramos.</strong></p>
<p>Por ejemplo, los siguientes son los <a href="http://es.wikipedia.org/wiki/Anexo:D%C3%ADas_feriados_en_Colombia">días festivos para Colombia</a> sacados de Wikipedia (Solo los que quedan del 2010 y uno que ya pasó para probar):</p>
<pre class="pretiffy">
holidays2010 = (
    date(2010,  8, 16), date(2010, 10, 18),
    date(2010, 11,  1), date(2010, 11, 15),
    date(2010, 12,  8),
)

print addworkdays(today, 100)               # 2011-01-19
print addworkdays(today, 100, holidays2010) # 2011-01-25

print addworkdays(today, -20)               # 2010-08-04
print addworkdays(today, -20, holidays2010) # 2010-08-05
</pre>
<p>Licencia: <a href="http://www.opensource.org/licenses/mit-license.php">MIT</a></p>
]]></content:encoded>
			<wfw:commentRss>http://jcesar.artelogico.com/2010/09/adicionar-dias-habiles-en-python/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Formato de moneda en JavaScript</title>
		<link>http://jcesar.artelogico.com/2010/08/formato-de-moneda-en-javascript/</link>
		<comments>http://jcesar.artelogico.com/2010/08/formato-de-moneda-en-javascript/#comments</comments>
		<pubDate>Tue, 31 Aug 2010 01:33:38 +0000</pubDate>
		<dc:creator>jcarrascal</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[currency]]></category>
		<category><![CDATA[format]]></category>
		<category><![CDATA[formato]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[moneda]]></category>

		<guid isPermaLink="false">http://jcesar.artelogico.com/?p=164</guid>
		<description><![CDATA[La función quedó un poquito larga pero permite especificar el número de decimales y los separadores (que varían de país a país). function currency(value, decimals, separators) { decimals = decimals >= 0 ? parseInt(decimals, 0) : 2; separators = separators &#124;&#124; ['.', "'", ',']; var number = (parseFloat(value) &#124;&#124; 0).toFixed(decimals); if (number.length 1 ? parts.length [...]]]></description>
			<content:encoded><![CDATA[<p>La función quedó un poquito larga pero permite especificar el número de decimales y los separadores (que varían de país a país).</p>
<pre class="pretiffy">
function currency(value, decimals, separators) {
    decimals = decimals >= 0 ? parseInt(decimals, 0) : 2;
    separators = separators || ['.', "'", ','];
    var number = (parseFloat(value) || 0).toFixed(decimals);
    if (number.length <= (4 + decimals))
        return number.replace('.', separators[separators.length - 1]);
    var parts = number.split(/[-.]/);
    value = parts[parts.length > 1 ? parts.length - 2 : 0];
    var result = value.substr(value.length - 3, 3) + (parts.length > 1 ?
        separators[separators.length - 1] + parts[parts.length - 1] : '');
    var start = value.length - 6;
    var idx = 0;
    while (start > -3) {
        result = (start > 0 ? value.substr(start, 3) : value.substr(0, 3 + start))
            + separators[idx] + result;
        idx = (++idx) % 2;
        start -= 3;
    }
    return (parts.length == 3 ? '-' : '') + result;
}
</pre>
<p>El primer parámetro debe ser un número (cualquier valor inválido regresa &#8220;0.00&#8243;). Este es el único parámetro obligatorio.</p>
<p>El segundo parámetro es el número de decimales (por defecto 2) y el tercero es un arreglo con los separadores en este orden: Separador de miles, separador de millones, separador de decimales. Por defecto es <code>['.', "'", ',']</code> que es el que se usa en Colombia.</p>
<p>Algunos ejemplos:</p>
<pre class="pretiffy">
currency(NaN); // "0.00"
currency(0); // "0.00"
currency(123456567.89); // "123'456.567,89"
currency(-123456567.89); // "-123'456.567,89"
currency(1234.56, 1); // "1.234,5"
currency(1234.56, 1, [',', "'", '.']); // "1,234.5"
</pre>
<p>Licencia: <a href="http://www.opensource.org/licenses/mit-license.php">MIT</a></p>
]]></content:encoded>
			<wfw:commentRss>http://jcesar.artelogico.com/2010/08/formato-de-moneda-en-javascript/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Sumar/restar fechas con Javascript</title>
		<link>http://jcesar.artelogico.com/2010/05/sumarrestar-fechas-con-javascript/</link>
		<comments>http://jcesar.artelogico.com/2010/05/sumarrestar-fechas-con-javascript/#comments</comments>
		<pubDate>Fri, 28 May 2010 15:45:02 +0000</pubDate>
		<dc:creator>jcarrascal</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[javascript date datetime adddays]]></category>

		<guid isPermaLink="false">http://jcesar.artelogico.com/?p=160</guid>
		<description><![CDATA[No encontré un ejemplo que sirviera pero leyendo la documentación me dí cuenta que se puede utilizar el constructor que acepta milisegundos: var today = new Date(); // Sumar 7 días. new Date(today.getTime() + (7 * 24 * 3600 * 1000)), // Restar 5 días new Date(today.getTime() - (5 * 24 * 3600 * 1000)), [...]]]></description>
			<content:encoded><![CDATA[<p>No encontré un ejemplo que sirviera pero leyendo la documentación me dí cuenta que se puede utilizar el constructor que acepta milisegundos:</p>
<pre class="prettify">
var today = new Date();

// Sumar 7 días.
new Date(today.getTime() + (7 * 24 * 3600 * 1000)),

// Restar 5 días
new Date(today.getTime() - (5 * 24 * 3600 * 1000)),
</pre>
<p>Tenga en cuenta que esta solución ignora los <a href="http://es.wikipedia.org/wiki/Segundo_intercalar">segundos intercalares</a>. En mi caso no son necesarios pero puede que en el suyo si.</p>
]]></content:encoded>
			<wfw:commentRss>http://jcesar.artelogico.com/2010/05/sumarrestar-fechas-con-javascript/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>DataGrid de Silverlight 3 que agrega nuevas filas</title>
		<link>http://jcesar.artelogico.com/2009/11/datagrid-de-silverlight-3-que-agrega-nuevas-filas/</link>
		<comments>http://jcesar.artelogico.com/2009/11/datagrid-de-silverlight-3-que-agrega-nuevas-filas/#comments</comments>
		<pubDate>Sun, 01 Nov 2009 15:59:27 +0000</pubDate>
		<dc:creator>jcarrascal</dc:creator>
				<category><![CDATA[General]]></category>

		<guid isPermaLink="false">http://jcesar.artelogico.com/?p=142</guid>
		<description><![CDATA[El DataGrid de Silverlight 3 es una contradicción: Por un lado es mucho más flexible que el de Windows Forms porque es muy fácil agregar cualquier control que queramos a una columna (Solo se agrega el control a la propiedad DataGridTemplateColumn) pero por otro es tan primitivo que no permite que el usuario agregue nuevas [...]]]></description>
			<content:encoded><![CDATA[<p>El DataGrid de Silverlight 3 es una contradicción: Por un lado <strong>es mucho más flexible que el de Windows Forms</strong> porque es muy fácil agregar cualquier control que queramos a una columna (Solo se agrega el control a la propiedad DataGridTemplateColumn) <strong>pero por otro es tan primitivo que no permite que el usuario agregue nuevas filas</strong> a la colección. </p>
<p>Para un proyecto que estoy realizando, agregar filas al DataGrid es una funcionalidad básica así que me puse a buscar la solución a este problema. La respuesta que dan por todos lados es agregar un elemento adicional a la colección y eliminarlo si el usuario lo dejó en blanco (Y prepararse para distinguir entre un objeto nuevo y uno modificado).</p>
<p>En mi caso, era una molestia agregar ese elemento en blanco ya que esta funcionalidad se necesitaba en muchos lugares. Así que, en vez de repetir esa lógica por cada DataGrid, decidí buscar la forma de obtener el mismo resultado sin tener que modificar la colección original. La solución a la que llegué me gustó tanto que decidí compartirla en este blog.</p>
<p>Este es el resultado:</p>
<div id="DataGridAddRow_before" style="text-align:center">
	<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="500" height="375"><param name="source" value="/wp-content/uploads/2009/10/DataGridAddRow_after.xap"/><param name="background" value="white" /><param name="minRuntimeVersion" value="3.0.40624.0" /><param name="autoUpgrade" value="true" /><a href="http://go.microsoft.com/fwlink/?LinkID=149156&#038;v=3.0.40624.0" style="text-decoration:none"><br />
			<img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style:none"/><br />
		</a><br />
	</object>
</div>
<h4>Primero un micro-repaso del uso del DataGrid.</h4>
<p>Vamos a crear un nuevo proyecto Silverlight con el nombre DataGridAddRow y le decimos que no cree un proyecto Web ya que no lo necesitamos para probar. Abrimos MainPage.xaml y agregamos un DataGrid:</p>
<pre class="prettyprint">&lt;Grid x:Name="LayoutRoot" Margin="20"&gt;
    &lt;data:DataGrid x:Name="m_dgMovies"&gt;
    &lt;/data:DataGrid&gt;
&lt;/Grid&gt;</pre>
<p>En MainPage.xaml.cs está el código de la página. En el constructor le pasamos los datos al DataGrid. En este caso es una lista que estoy creando en el método ListOfPixarMovies() pero bien pueden ser datos que salen de un servicio web o algo así.</p>
<pre class="prettyprint">public partial class MainPage : UserControl
{
	public MainPage()
	{
		InitializeComponent();

		m_dgMovies.ItemsSource = ListOfPixarMovies();
	}

	private List&lt;Movie&gt; ListOfPixarMovies()
	{
		return new List&lt;Movie&gt;
		{
			new Movie { Name = "The Incredibles", Year = 2004 },
			new Movie { Name = "Chicken Little", Year = 2005 },
			new Movie { Name = "Ratatouille", Year = 2007 },
			new Movie { Name = "WALL·E", Year = 2008 },
			new Movie { Name = "Up", Year = 2009 },
		};
	}
}</pre>
<p>Y, por supuesto, tenemos que crear la clase Movie. <strong>El único requerimiento de esta solución es que la clase debe implementar INotifyPropertyChanged</strong> pero si uno está utilizando data binding para editar un objeto normalmente termina implementando INotifyPropertyChanged también. Esta es la clase que utilicé en el ejemplo:</p>
<pre class="prettyprint">public class Movie
		: System.ComponentModel.INotifyPropertyChanged
{
	string m_name;

	public string Name
	{
		get { return m_name; }
		set
		{
			m_name = value;
			OnPropertyChanged(new PropertyChangedEventArgs("Name"));
		}
	}

	int m_year;

	public int Year
	{
		get { return m_year; }
		set
		{
			m_year = value;
			OnPropertyChanged(new PropertyChangedEventArgs("Year"));
		}
	}

	public event PropertyChangedEventHandler PropertyChanged;

	protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
	{
		if (PropertyChanged != null)
			PropertyChanged(this, e);
	}
}</pre>
<p>Nada del otro mundo: Una clase con dos propiedades y cada vez que se modifican esas propiedades ejecutamos el evento PropertyChanged que requiere la interfaz INotifyPropertyChanged.</p>
<p>En este punto podemos ejecutar el proyecto y nos encontramos con un DataGrid con varias filas y podemos editar los datos pero no podemos agregar nuevas filas, como este:</p>
<div id="DataGridAddRow_before" style="text-align:center">
	<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="500" height="375"><param name="source" value="/wp-content/uploads/2009/10/DataGridAddRow_before.xap"/><param name="background" value="white" /><param name="minRuntimeVersion" value="3.0.40624.0" /><param name="autoUpgrade" value="true" /><a href="http://go.microsoft.com/fwlink/?LinkID=149156&#038;v=3.0.40624.0" style="text-decoration:none"><br />
			<img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style:none"/><br />
		</a><br />
	</object>
</div>
<h4>La solución: AppendableCollection</h4>
<p>Para permitirle al usuario agregar nuevas filas todo lo que tenemos que hacer es agregar la clase <a href="/wp-content/uploads/2009/10/DataGridAddRow.zip">AppendableCollection</a> y en el constructor de la página modificamos la línea en la que pasábamos la lista de películas para envolverla con una instancia de AppendableCollection&lt;Movie&gt;:</p>
<pre class="prettyprint">public MainPage()
{
	InitializeComponent();

	//m_dgMovies.ItemsSource = ListOfPixarMovies();
	m_dgMovies.ItemsSource = new AppendableCollection&lt;Movie&gt;(ListOfPixarMovies());
}</pre>
<p><strong>El resultado es una fila vacía al final de los datos y el usuario ahora puede agregar nuevas filas editando esa fila vacía</strong>. Por lo menos hasta que salga una nueva versión del DataGrid con soporte para agregar nuevas filas, esta es probablemente la solución más sencillas para simular esta funcionalidad.</p>
<p>Realmente eso es todo lo que que hay que hacer: <strong>Agregar la clase, implementar INotifyPropertyChanged y envolver la colección en AppendableCollection</strong>. Ahora puede <a href="#descargar-la-clase-y-el-ejemplo">saltar hasta el final del artículo y descargar el código</a>. Para los tres que se quedaron, así es como funciona:</p>
<p>El DataGrid solo requiere que le pasemos un IEnumerable entonces AppendableCollection necesita implementar esa interfaz. En el método GetEnumerator() lo que hacemos es devolver el Enumerator de la colección original pero al final le agregamos la nueva instancia:</p>
<pre class="prettyprint">public class AppendableCollection&lt;T&gt;
	: System.Collections.Generic.IEnumerable&lt;T&gt;
	where T : System.ComponentModel.INotifyPropertyChanged, new()
{
	public IEnumerator&lt;T&gt; GetEnumerator()
	{
		return m_collection.Union(m_newItem).GetEnumerator();
	}

	readonly T[] m_newItem;
...
}</pre>
<p>Solo nos falta detectar cuando el usuario ha modificado el objeto. Por eso la clase de la colección debe implementar INotifyPropertyChanged. Cuando recibimos el evento de que una propiedad ha sido modificada agregamos la instancia actual a la colección original y creamos una nueva:</p>
<pre class="prettyprint">void newItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
	m_newItem[0].PropertyChanged -= newItem_PropertyChanged;
	m_collection.Add(m_newItem[0]);
	m_newItem[0] = new T();
	m_newItem[0].PropertyChanged += newItem_PropertyChanged;
}</pre>
<p>Solo nos falta avisarle al DataGrid que agregamos un nuevo elemento. Para eso implementamos la interfaz INotifyCollectionChanged y ejecutamos el evento CollectionChanged:</p>
<pre class="prettyprint">
public class AppendableCollection&lt;T&gt;
	: System.Collections.Generic.IEnumerable&lt;T&gt;
	, System.Collections.Specialized.INotifyCollectionChanged
	where T : System.ComponentModel.INotifyPropertyChanged, new()
{
	public event NotifyCollectionChangedEventHandler CollectionChanged;

	protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
	{
		if (CollectionChanged != null)
			CollectionChanged(this, e);
	}

	void newItem_PropertyChanged(object sender, PropertyChangedEventArgs e)
	{
		m_newItem[0].PropertyChanged -= newItem_PropertyChanged;
		m_collection.Add(m_newItem[0]);
		m_newItem[0] = new T();
		m_newItem[0].PropertyChanged += newItem_PropertyChanged;
		OnCollectionChanged(new NotifyCollectionChangedEventArgs(
			NotifyCollectionChangedAction.Add, m_newItem[0],
			m_collection.Count));
	}
...
}</pre>
<h4 id="descargar-la-clase-y-el-ejemplo">Descargar la clase y el ejemplo</h4>
<p>El código de la clase está bajo licencia MIT y puede descargarse desde aquí: <a href="/wp-content/uploads/2009/10/DataGridAddRow.zip">DataGridAddRow.zip &#8211; Agregar nuevas filas a un DataGrid de Silverlight 3</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://jcesar.artelogico.com/2009/11/datagrid-de-silverlight-3-que-agrega-nuevas-filas/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

