joe di castrohttp://joedicastro.com2012-06-07T22:40:00+02:00Productividad & Linux: Aplicaciones2012-06-07T22:40:00+02:00joe di castrohttp://joedicastro.com/productividad-linux-aplicaciones.html<p>En la anterior serie de artículos sobre <a href="http://joedicastro.com/productividad-en-el-escritorio-linux-introduccion.html">Productividad en el escritorio
Linux</a>, hacia referencia a los lanzadores de aplicaciones y a los gestores
de ventanas de mosaico como elementos diferenciadores a la hora de ser más
productivos. Aunque la productividad en el escritorio es fundamental para
trabajar de forma más eficiente, son las aplicaciones que empleamos, las que en
último lugar, determinaran la realidad de nuestro día a día frente a la
pantalla.</p>
<p>Gran parte de las aplicaciones para Linux (las más conocidas y extendidas) son
realmente una adaptación de los conceptos de los sistemas operativos más
difundidos: Windows & Mac OS. Esto es, aplicaciones muy pesadas, repletas de
opciones y funciones de las que se aprovechan en realidad muy pocas. Además
están por lo general fuertemente orientadas al empleo del ratón y muchas veces
con un interfaz gráfico muy agradable visualmente pero con un concepto de
usabilidad, en mi opinión, no siempre bien enfocado.</p>
<h2 id="la_usabilidad_mal_entendida_como_enemiga_de_la_productividad">La usabilidad mal entendida, como enemiga de la productividad</h2>
<p>La <a href="https://es.wikipedia.org/wiki/Usabilidad">usabilidad</a>, ese concepto tan de moda últimamente, está desde mi
punto de vista, frecuentemente muy mal enfocado. Dejando a un lado lo que
teóricamente se pretende, en el 80% de las ocasiones esto se traduce en la
práctica en dos palabras en ingles: <em>For dummies</em> ("pá tontos", en castizo). Es
decir, que cualquiera pueda manejar la aplicación sin grandes dificultades y en
el menor tiempo posible. Reducir las barreras de entrada está muy bien, pero
desgraciadamente muchas veces esto es una estrategia a corto plazo que no
debería emplearse <strong>NUNCA de este modo</strong> en aplicaciones profesionales.
Entiendo, y además apoyo, está estrategia en aplicaciones web y aplicaciones de
consumo, pero lo veo nocivo a la hora de aplicaciones destinadas a producir
datos y contenidos de forma masiva e intensiva. Y a mi modo de ver es un
problema serio en un gran número de escenarios.</p>
<h3 id="un_caso_real_de_usabilidad_enga+osa">Un caso real de usabilidad engañosa</h3>
<p>No proporcionare datos reales por respeto a las partes implicadas, pero quiero
aportar aquí un ejemplo real que he podido vivir de cerca durante varios años,
de lo que la pretendida usabilidad, ni lo es, ni ayuda a sacar más partido de
las herramientas:</p>
<p><strong>Escenario fallido</strong> : un negocio que se dedica el 90% del tiempo a editar
grandes cantidades de texto. Se informatizan a mediados de los 80 y adoptan como
procesador de textos WordPerfect para consola. Después de varios años, están en
la versión de WordPerfect 5.1 para MSDOS con un 90% de los empleados (unos
diez), con un dominio asombroso de la aplicación, basando todo su trabajo en
atajos de teclado y una pantalla donde únicamente se visualiza el texto sobre el
que están trabajando. Productividad: realmente elevada. Principios del siglo
XXI, un consultor informático embaucador (<em>aka</em> comercial) los convence de que
el futuro es <a href="https://es.wikipedia.org/wiki/Wysiwyg">WYSIWYG</a> y que eso del terminal de texto es cosa del pasado.
Tras una primera migración a WordPerfect para Windows, la productividad baja de
forma significativa. La solución propuesta: Word, la productividad cae de forma
dramática. La excusa del "asesor": necesidad de adaptación al nuevo concepto.
Dos años después, la productividad sigue sin incrementarse, el trabajo se
acumula y se contratan a dos personas más para refuerzo. El personal ya se ha
adaptado y están tan hipnotizados por el ratón, los iconos y los "regalitos" que
su software les proporciona en cada nueva versión, que se oponen radicalmente a
volver al "pasado". En realidad, nadie desde dentro se cuestionaba que el
problema real era que habían dejado de centrarse en el contenido, para centrarse
en el continente. Lo más irónico: lo que realmente hacían era crear e imprimir
documentos, impresos en impresoras matriciales que seguían imprimiendo con la
misma calidad y la misma fuente Courier 11 sobre el mismo tipo de documentos
oficiales.</p>
<p><strong>Escenario exitoso</strong> : Paralelamente, en un área de negocio distinta, pero
estrechamente relacionada con la anterior, una empresa que ha trabajado siempre
en entorno de terminal contra un <a href="https://es.wikipedia.org/wiki/As/400">AS/400,</a> presentaba una mentalidad y un
resultado totalmente opuestos. Nunca se han dejado seducir por los cantos de
sirena del marketing y se han opuesto radicalmente a cualquier cambio de
sistema, simplemente han evolucionado el que ya tenían y conservando su
plantilla prácticamente intacta durante el mismo periodo. El resultado: la
productividad se ha mantenido constante a lo largo de los años y han podido
asumir importantes picos de carga de trabajo sin demasiadas dificultades. Y un
efecto secundario de esta toma de decisiones: el haberse mantenido dentro de un
sistema maduro y estable les ha librado de un mantenimiento casi nulo en el área
de software, limitándose el mismo al área de hardware (lo habitual). Como podéis
imaginar, en un sistema basado en plataforma Windows y actualizaciones sucesivas
de SO y aplicaciones, el mantenimiento del área de software les ha supuesto una
importante carga económica al otro negocio.</p>
<p>Que cada uno extraiga sus propias conclusiones. La mía: que la carrera hacia
adelante de fabricantes de sistemas operativos y aplicaciones (cuyo único
recurso para seguir vendiendo ha sido con frecuencia un simple
lavado de cara) ha supuesto, en muchos aspectos, un paso atrás en varias áreas de
la informática.</p>
<h2 id="la_usabilidad_bien_entendida">La usabilidad bien entendida</h2>
<p>No quiero decir con lo anterior, que las aplicaciones gráficas sean el problema
ni mucho menos. De hecho, hay aplicaciones gráficas que son una maravilla de la
usabilidad. Por ejemplo, Autocad, que te permite un control casi absoluto como
usuario avanzado (con los comandos y <a href="https://es.wikipedia.org/wiki/Autolisp">AutoLISP</a>) y que al mismo tiempo te
permite realizar gran parte del trabajo de manera muy intuitiva (siempre
teniendo en cuenta el contexto del usuario al que va dirigido) empleando la
interfaz gráfica. Lo que permite que sea muy usable sin detrimento de la
productividad. Lo que quiero decir, es que es necesario añadir el sentido común
como un criterio más, si no el primero, a la hora de implementar la usabilidad.</p>
<p>Si consultamos la Wikipedia, encontramos dos definiciones de la ISO para el
concepto de <a href="https://es.wikipedia.org/wiki/Usabilidad">usabilidad</a> :</p>
<blockquote>
<p>La usabilidad se refiere a la capacidad de un software de ser comprendido,
aprendido, usado y ser atractivo para el usuario, en condiciones específicas de
uso</p>
<p>Usabilidad es la eficacia, eficiencia y satisfacción con la que un producto
permite alcanzar objetivos específicos a usuarios específicos en un contexto de
uso específico</p>
</blockquote>
<p>Como decía anteriormente, la realidad dista mucho de la teoría. En la
práctica se ha adoptado con bastante fidelidad la primera definición, eso sí,
ignorando con bastante frecuencia la parte de "condiciones específicas" y la
segunda definición parece haber sido ampliamente ignorada.</p>
<p>Si bien es cierto que la mayoría de las aplicaciones actuales pueden ser muy
potentes, lo que yo veo mirando al pasado, es que la gran mayoría de los
empleados que utilizaban una aplicación a diario eran usuarios avanzados y
cuando no expertos. Hoy en día, y ciñéndonos al ámbito profesional, a algunos
les cambias el icono de la misma, y no saben iniciarla. Y no es culpa de ellos,
las aplicaciones se diseñan para que las puedan usar hasta los más reacios a la
tecnología (siempre en su contexto), pero una vez que necesitas realizar algo
complejo, existe un salto conceptual que la mayoría de los usuarios, ni se
atreven a dar, ni quieren darlo.</p>
<p>Es realmente complejo, muy complejo, conseguir una interfaz de usuario que sea
realmente amigable e intuitiva y que al mismo tiempo consiga satisfacer las
necesidades reales tanto del usuario profesional, como del usuario ocasional. El
problema de base está en intentar crear herramientas todoterreno. Ni maldita
falta que le hace un Word actual a un usuario domestico y que demonios hace un
escritor profesional empleándolo en vez de algo como Latex + editor de texto.
Ni un usuario domestico necesita de herramientas para correspondencia, programar
macros, etc, ni un escritor necesita preocuparse del formato del texto. Y sin
embargo es empleado hasta por editores que deberían estar empleando
herramientas de autoedición.</p>
<p>Por ejemplo, una aplicación que es un claro ejemplo de buena usabilidad y que
está teniendo una buena acogida últimamente, es <a href="http://www.sublimetext.com/">Sublime Text</a>. Es bonita,
es potente, es amigable y es francamente muy, muy usable. Realmente lo que han
hecho ha sido recoger el testigo que dejo <a href="http://macromates.com/">TextMate</a>, que fueron los
primeros en darse cuenta de algo muy sencillo. Esto es, los editores de texto y
los IDEs para programadores son una de las aplicaciones en las que nunca llueve
a gusto de todos, surgen continuamente nuevas alternativas, pero no se acaba por
imponer ninguna. Por que en realidad, si nos fijamos en los programadores más
experimentados, dos de los editores más utilizados y defendidos a muerte, son
<a href="https://es.wikipedia.org/wiki/Vim">Vim</a> y <a href="https://es.wikipedia.org/wiki/Emacs">Emacs</a>, ambos basados en conceptos de hace casi cuatro
décadas. Es que la usabilidad ya estaba inventada hace mucho tiempo, y no tiene
nada que ver ni con gráficos bonitos, ni con tratar a los usuarios como
imbéciles. La usabilidad, señores, está en la adaptación del interfaz del
ordenador de la manera más optima posible al interfaz que la naturaleza nos dio,
nuestras manos. Y desde luego, jamás un ratón, con los botones que quieras
ponerle, sera más eficaz que un teclado (usado con los diez deditos), al igual
que no lo es más que un bolígrafo o una tableta gráfica con lápiz. Que para eso
tenemos los dedos y no muñones. Si vamos a comprar un libro en Internet, el
ratón es nuestro gran aliado para hacerlo de forma intuitiva sin conocimientos
previos, pero si me vas a obligar a introducir entradas de almacén 40 horas a la
semana con la misma aplicación, por favor, no me condenes a usar el ratón cada
dos pasos. Sentido común!</p>
<h2 id="las_aplicaciones_amigas_de_la_productividad">Las aplicaciones amigas de la productividad</h2>
<p>Este entrada pretende ser la introducción y el indice de una nueva serie de
artículos dedicada a esas aplicaciones para Linux que para mi marcan la
diferencia entre <em>hacer el trabajo</em> y <em>marear la perdiz</em>. Estas aplicaciones
comparten en general tres puntos en común:</p>
<ul>
<li>Son muy eficientes y ayudan a incrementar nuestra productividad.</li>
<li>Realizan a una sola tarea y lo hacen bien o muy bien.</li>
<li>No están muy extendidas.</li>
</ul>
<p>Iré actualizando la entrada a medida que vaya creando los artículos para cada
aplicación.</p>
<h3 id="aplicaciones">Aplicaciones</h3>
<p>Estas son algunas de las aplicaciones que yo empleo a menudo y que al igual que
a mí, pueden servirte para incrementar de forma notable tu productividad.</p>
<ul>
<li>
<p><a href="http://joedicastro.com/productividad-linux-pentadactyl.html">Pentadactyl</a>, complemento para Firefox para controlar el navegador
completamente desde el teclado, permite navegar entre las páginas a velocidad
de vértigo. Además es muy potente y posee muchísimas opciones y posibilidades.</p>
</li>
<li>
<p><a href="http://joedicastro.com/productividad-linux-newsbeuter.html">Newsbeuter</a>, aplicación para leer subscripciones RSS y Atom. Funciona
en modo texto, con una interfaz tipo ncurses y completamente controlable desde
el teclado. Es una aplicación rapidisima y con un consumo muy escaso de
recursos.</p>
</li>
<li>
<p><a href="http://joedicastro.com/productividad-linux-turses.html">Turses</a>, cliente twitter para la consola con interfaz ncurses.
Controlable desde el teclado con atajos basados en Vim. Muy personalizable y
con un funcionamiento muy versátil y ágil.</p>
</li>
<li>
<p><a href="http://joedicastro.com/productividad-linux-tmux.html">tmux</a>, es un multiplexor de terminales. Permite agrupar varios
terminales en una sola ventana de forma muy eficiente. La herramienta ideal
para los amantes de la consola, desarrolladores y administradores de sistemas.</p>
</li>
<li>
<p><a href="http://joedicastro.com/productividad-linux-zathura.html">zathura</a>, es un visor de documentos minimalista y controlable desde
el teclado. Soporta documentos en formato PDF, DJVU, PostScript y Comic Book.</p>
</li>
<li>
<p><a href="http://joedicastro.com/productividad-linux-ranger.html">Ranger</a>, es un administrador de archivos en modo texto y
revolucionario en su interfaz y manejo. Muy potente y personalizable. </p>
</li>
<li>
<p><a href="http://joedicastro.com/productividad-linux-ncdu.html">Ncdu</a>, una herramienta con interfaz ncurses que nos permite conocer el
espacio consumido en nuestro disco duro y navegar por los distintos
directorios. La mejor herramienta del estilo que existe para la consola.</p>
</li>
</ul>Logger, informes legibles para tus scripts Python2011-05-07T23:02:00+02:00joe di castrohttp://joedicastro.com/logger-informes-legibles-para-tus-scripts-python.html<p>Normalmente los scripts que se crean para ser ejecutados periódicamente, como
los de administración de sistemas, realizan tareas que generan cambios que
queremos conocer. Esta información se suele guardar en logs o se envía por
correo a una dirección de email. Hay muchas formas de generar estos logs, pero
normalmente es una simple salida de texto plano en consola, que no suele tener
una presentación muy "amigable" o legible. Cuando tienes una cantidad generosa
de estos scripts, los recibes por correo electrónico y con una frecuencia diaria
o mayor, lo que menos deseas es andar buscando la información entre lineas de
texto plano. Lo que yo quiero es poder identificar la información rápidamente de
un vistazo, además tenía una idea rondando por la cabeza, que seria aún más
cómodo si todos emplearan un formato similar. Con estas premisas cree un módulo
<strong>Python</strong>, <code>logger.py</code>, que empleo en muchos de mis scripts en Python y que me
permite analizar adecuadamente la información que me interesa.</p>
<p>Un ejemplo de informe generado por este modulo Python sería el siguiente (el
mismo que se generaría si ejecutáramos el modulo como script):</p>
<div class="codehilite"><pre>SCRIPT =========================================================================
logger (ver. 0.3)
http://joedicastro.com
This is a test of class Logger
================================================================================
START TIME =====================================================================
Saturday 05/07/11, 21:51:27
================================================================================
BLOCK ==========================================================================
This
is
a
sample
of
Logger.block()
================================================================================
LIST ___________________________________________________________________________
This
is
a
sample
of
Logger.list()
This a sample of logger.free() text.
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque sed
tortor eget justo vehicula consequat vel eu quam. Suspendisse non lectus eget
orci varius adipiscing."
END TIME =======================================================================
Saturday 05/07/11, 21:51:27
================================================================================
</pre></div>
<p>Este modulo contiene una clase, <code>Logger()</code>, que dispone de una serie de métodos
que nos permiten diversas tareas, supongamos que tenemos un objeto <code>log</code> de la
clase <code>Logger()</code>:</p>
<ul>
<li>
<p><code>log.header(url, msg)</code></p>
<p>Nos crea una cabecera para el script que nos aporta cierta información que
nos sirve para identificar el mismo y el contexto en el que se ejecuta. </p>
<div class="codehilite"><pre>SCRIPT =========================================================================
nombre del script (versión)
http://miweb.com/script
mensaje que nos informa de la finalidad del mismo o de
información dependiente del contexto. Totalmente
personalizable.
================================================================================
</pre></div>
</li>
<li>
<p><code>log.time(title)</code></p>
<p>Nos permite registrar el tiempo de un evento. Normalmente lo empleo para
registrar el comienzo y el final de la ejecución del script. </p>
<div class="codehilite"><pre>TITULO =========================================================================
Sábado 07/05/11, 22:18:27
================================================================================
</pre></div>
</li>
<li>
<p><code>log.block(title, content)</code></p>
<p>Crea un bloque de texto con titulo y enmarcado por líneas compuestas por el
carácter <code>=</code>. Entre esas lineas y el contenido no hay ninguna linea en
blanco. Es útil para destacar cierto contenido del resto de forma muy
notable, suelo utilizarlo para ubicar el script en su contexto. </p>
<div class="codehilite"><pre>TITULO =========================================================================
contenido
sigue el contenido
...
================================================================================
</pre></div>
</li>
<li>
<p><code>log.list(title, content)</code></p>
<p>Como su nombre bien indica, es una lista de líneas con un simple encabezado
que lo distinga del resto. Suelo emplearlo para volcar la información generada
por el script. Entre el encabezado y la primera línea de texto existe una
línea en blanco.</p>
<div class="codehilite"><pre>TITULO __________________________________________________
Primera línea del contenido
Segunda línea del contenido
...
</pre></div>
</li>
<li>
<p><code>log.free(content)</code></p>
<p>Texto libre, párrafos que se mostrarán tal y como son, sin formato ni
cabecera alguna. Raramente lo empleo, pero es útil por ejemplo para
introducir comentarios, licencias, etc.</p>
</li>
<li>
<p><code>log.send(subject, send_from='', dest_to='', mail_server='localhost',
server_user='', server_pass='')</code></p>
<p>El meollo del script. Se emplea para mandar el resultado por
correo electrónico. Si solo especificamos el asunto, empleará nuestro
servidor de correo local para mandar el informe al buzón local del usuario
que programó/ejecuto el script. Pero se pueden especificar tanto el empleo
de un servidor de correo (SMTP) distinto como otro (o varios)
destinatario(s) en particular. </p>
</li>
<li>
<p><code>log.write(append=False)</code></p>
<p>Escribe el resultado del log en un fichero de texto. El nombre de este
fichero estará compuesto por el nombre del script sin extensión, más la
extensión <em>.log</em>. Si <code>append</code> es <code>True</code> entonces añadirá el resultado al
final de texto, si no, lo reescribirá en cada ejecución guardando
únicamente el último informe. </p>
</li>
<li>
<p><code>log.get()</code></p>
<p>Nos devuelve el contenido del log. Es útil cuando estamos con tareas de
depurado, con un <code>print</code> llamando a este método podemos volcar en la
consola la información registrada en el log.</p>
</li>
</ul>
<p>El código del script es el siguiente: </p>
<div class="codehilite"><pre><span class="c">#!/usr/bin/env python</span>
<span class="c"># -*- coding: utf8 -*-</span>
<span class="sd">"""</span>
<span class="sd"> logger.py: Create a log object to log script messages in a elegant way</span>
<span class="sd">"""</span>
<span class="c">#===============================================================================</span>
<span class="c"># This module create a log object to log script messages in a elegant way</span>
<span class="c">#===============================================================================</span>
<span class="c">#===============================================================================</span>
<span class="c"># Copyright 2010 joe di castro <joe@joedicastro.com></span>
<span class="c">#</span>
<span class="c"># This program is free software: you can redistribute it and/or modify</span>
<span class="c"># it under the terms of the GNU General Public License as published by</span>
<span class="c"># the Free Software Foundation, either version 3 of the License, or</span>
<span class="c"># (at your option) any later version.</span>
<span class="c">#</span>
<span class="c"># This program is distributed in the hope that it will be useful,</span>
<span class="c"># but WITHOUT ANY WARRANTY; without even the implied warranty of</span>
<span class="c"># MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the</span>
<span class="c"># GNU General Public License for more details.</span>
<span class="c">#</span>
<span class="c"># You should have received a copy of the GNU General Public License</span>
<span class="c"># along with this program. If not, see <http://www.gnu.org/licenses/>.</span>
<span class="c">#===============================================================================</span>
<span class="n">__author__</span> <span class="o">=</span> <span class="s">"joe di castro <joe@joedicastro.com>"</span>
<span class="n">__license__</span> <span class="o">=</span> <span class="s">"GNU General Public License version 3"</span>
<span class="n">__date__</span> <span class="o">=</span> <span class="s">"10/09/2010"</span>
<span class="n">__version__</span> <span class="o">=</span> <span class="s">"0.3"</span>
<span class="k">try</span><span class="p">:</span>
<span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">smtplib</span>
<span class="kn">import</span> <span class="nn">socket</span>
<span class="kn">from</span> <span class="nn">email.mime.text</span> <span class="kn">import</span> <span class="n">MIMEText</span>
<span class="kn">from</span> <span class="nn">email.mime.multipart</span> <span class="kn">import</span> <span class="n">MIMEMultipart</span>
<span class="kn">from</span> <span class="nn">email.utils</span> <span class="kn">import</span> <span class="n">COMMASPACE</span><span class="p">,</span> <span class="n">formatdate</span>
<span class="k">except</span> <span class="ne">ImportError</span><span class="p">:</span>
<span class="c"># Checks the installation of the necessary python modules </span>
<span class="k">print</span><span class="p">((</span><span class="n">os</span><span class="o">.</span><span class="n">linesep</span> <span class="o">*</span> <span class="mi">2</span><span class="p">)</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="s">"An error found importing one module:"</span><span class="p">,</span>
<span class="nb">str</span><span class="p">(</span><span class="n">sys</span><span class="o">.</span><span class="n">exc_info</span><span class="p">()[</span><span class="mi">1</span><span class="p">]),</span> <span class="s">"You need to install it"</span><span class="p">,</span> <span class="s">"Stopping..."</span><span class="p">]))</span>
<span class="n">sys</span><span class="o">.</span><span class="n">exit</span><span class="p">(</span><span class="o">-</span><span class="mi">2</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">Logger</span><span class="p">():</span>
<span class="sd">"""</span>
<span class="sd"> Create a log object to log script messages.</span>
<span class="sd"> These messages can be sended via email or writed in a log file</span>
<span class="sd"> """</span>
<span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""Create the object Logger itself and set various attributes.</span>
<span class="sd"> These attributes are about the python file wich invokes this module:</span>
<span class="sd"> __script_vers = The version of python file which invokes this module</span>
<span class="sd"> __script_name = The name of the python file which invokes this module</span>
<span class="sd"> filename = the log file's name</span>
<span class="sd"> """</span>
<span class="kn">from</span> <span class="nn">__main__</span> <span class="kn">import</span> <span class="n">__dict__</span> <span class="k">as</span> <span class="n">__dict</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__log</span> <span class="o">=</span> <span class="s">''</span>
<span class="k">if</span> <span class="s">'__version__'</span> <span class="ow">in</span> <span class="n">__dict</span><span class="o">.</span><span class="n">keys</span><span class="p">():</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__script_vers</span> <span class="o">=</span> <span class="n">__dict</span><span class="p">[</span><span class="s">'__version__'</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__script_vers</span> <span class="o">=</span> <span class="s">'Unknown'</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__script_name</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">path</span><span class="o">.</span><span class="n">basename</span><span class="p">(</span><span class="n">__dict</span><span class="p">[</span><span class="s">'__file__'</span><span class="p">])</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s">'.'</span><span class="p">)[</span><span class="mi">0</span><span class="p">]</span>
<span class="bp">self</span><span class="o">.</span><span class="n">filename</span> <span class="o">=</span> <span class="s">'{0}.log'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__script_name</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">__len__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">return</span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__log</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">__format__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">tit</span><span class="p">,</span> <span class="n">cont</span><span class="p">,</span> <span class="n">decor</span><span class="p">):</span>
<span class="sd">"""Format a block or a list of lines to enhance comprehension.</span>
<span class="sd"> (str) tit -- title for the block or list</span>
<span class="sd"> (str or iterable) cont -- line/s for the list/block content</span>
<span class="sd"> ('=' or '_') decor - define if it's list or block and decorate it</span>
<span class="sd"> make the looks of self.block() and self.list()</span>
<span class="sd"> """</span>
<span class="n">ending</span> <span class="o">=</span> <span class="p">{</span><span class="s">'='</span><span class="p">:</span><span class="s">''</span><span class="p">,</span> <span class="s">'_'</span><span class="p">:</span><span class="n">os</span><span class="o">.</span><span class="n">linesep</span><span class="p">}[</span><span class="n">decor</span><span class="p">]</span>
<span class="n">end</span> <span class="o">=</span> <span class="p">{</span><span class="s">'='</span><span class="p">:</span> <span class="s">'='</span> <span class="o">*</span> <span class="mi">80</span><span class="p">,</span> <span class="s">'_'</span><span class="p">:</span><span class="s">''</span><span class="p">}[</span><span class="n">decor</span><span class="p">]</span>
<span class="n">begin</span> <span class="o">=</span> <span class="s">' '</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="n">tit</span><span class="o">.</span><span class="n">upper</span><span class="p">(),</span> <span class="p">(</span><span class="mi">80</span> <span class="o">-</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">tit</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">))</span> <span class="o">*</span> <span class="n">decor</span><span class="p">])</span> <span class="o">+</span> <span class="n">ending</span>
<span class="n">cont</span> <span class="o">=</span> <span class="p">[</span><span class="n">cont</span><span class="p">]</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">cont</span><span class="p">,</span> <span class="nb">str</span><span class="p">)</span> <span class="k">else</span> <span class="n">cont</span>
<span class="n">sep</span> <span class="o">=</span> <span class="n">os</span><span class="o">.</span><span class="n">linesep</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__log</span> <span class="o">+=</span> <span class="n">sep</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="n">begin</span><span class="p">,</span> <span class="n">sep</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">cont</span><span class="p">),</span> <span class="n">end</span><span class="p">,</span> <span class="n">sep</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">block</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">title</span><span class="p">,</span> <span class="n">content</span><span class="p">):</span>
<span class="sd">"""A block of text lines headed and followed by a line full of '='.</span>
<span class="sd"> (str) title -- The title that start the first line of '='</span>
<span class="sd"> (str or iterable) content -- The line/s between the '=' lines</span>
<span class="sd"> There's not any empty line between the '=' lines and content, e.g.:</span>
<span class="sd"> TITLE ==================================================</span>
<span class="sd"> content</span>
<span class="sd"> ========================================================</span>
<span class="sd"> """</span>
<span class="k">if</span> <span class="n">content</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__format__</span><span class="p">(</span><span class="n">title</span><span class="p">,</span> <span class="n">content</span><span class="p">,</span> <span class="s">'='</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">list</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">title</span><span class="p">,</span> <span class="n">content</span><span class="p">):</span>
<span class="sd">"""A list of text lines headed by a line full of '_'.</span>
<span class="sd"> (str) title -- The title that start the line of '_'</span>
<span class="sd"> (str or iterable) content -- The line/s after the '_' line</span>
<span class="sd"> After the '_' line is a empty line between it and the content, e.g.:</span>
<span class="sd"> TITLE __________________________________________________</span>
<span class="sd"> content</span>
<span class="sd"> """</span>
<span class="k">if</span> <span class="n">content</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__format__</span><span class="p">(</span><span class="n">title</span><span class="p">,</span> <span class="n">content</span><span class="p">,</span> <span class="s">'_'</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">free</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">content</span><span class="p">):</span>
<span class="sd">"""Free text unformatted.</span>
<span class="sd"> (str) content -- Text free formated</span>
<span class="sd"> """</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">content</span><span class="p">,</span> <span class="nb">str</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">__log</span> <span class="o">+=</span> <span class="n">content</span> <span class="o">+</span> <span class="n">os</span><span class="o">.</span><span class="n">linesep</span> <span class="o">*</span> <span class="mi">2</span>
<span class="k">def</span> <span class="nf">time</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">title</span><span class="p">):</span>
<span class="sd">"""A self.block() formated line with current time and date.</span>
<span class="sd"> (str) title -- Title for self.block()</span>
<span class="sd"> Looks like this, the data and time are right-justified:</span>
<span class="sd"> TITLE ==================================================</span>
<span class="sd"> Friday 09/10/10, 20:01:39</span>
<span class="sd"> ========================================================</span>
<span class="sd"> """</span>
<span class="bp">self</span><span class="o">.</span><span class="n">block</span><span class="p">(</span><span class="n">title</span><span class="p">,</span> <span class="s">'{0:>80}'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">time</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s">'%A </span><span class="si">%x</span><span class="s">, </span><span class="si">%X</span><span class="s">'</span><span class="p">)))</span>
<span class="k">def</span> <span class="nf">header</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">msg</span><span class="p">):</span>
<span class="sd">"""A self.block() formated header for the log info.</span>
<span class="sd"> (str) url -- The url of the script</span>
<span class="sd"> (str) msg -- Message to show into the header. To Provide any useful info</span>
<span class="sd"> It looks like this:</span>
<span class="sd"> SCRIPT =================================================</span>
<span class="sd"> script name and version</span>
<span class="sd"> url</span>
<span class="sd"> msg</span>
<span class="sd"> ========================================================</span>
<span class="sd"> """</span>
<span class="n">script</span> <span class="o">=</span> <span class="s">'{0} (ver. {1})'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__script_name</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">__script_vers</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">block</span><span class="p">(</span><span class="s">'Script'</span><span class="p">,</span> <span class="p">[</span><span class="n">script</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="s">''</span><span class="p">,</span> <span class="n">msg</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">get</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">"""Get the log content."""</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">__log</span>
<span class="k">def</span> <span class="nf">send</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">subject</span><span class="p">,</span> <span class="n">send_from</span><span class="o">=</span><span class="s">''</span><span class="p">,</span> <span class="n">dest_to</span><span class="o">=</span><span class="s">''</span><span class="p">,</span> <span class="n">mail_server</span><span class="o">=</span><span class="s">'localhost'</span><span class="p">,</span>
<span class="n">server_user</span><span class="o">=</span><span class="s">''</span><span class="p">,</span> <span class="n">server_pass</span><span class="o">=</span><span class="s">''</span><span class="p">):</span>
<span class="sd">"""Send a email with the log.</span>
<span class="sd"> Arguments:</span>
<span class="sd"> (str) send_from -- a sender's email address (default '')</span>
<span class="sd"> (str or list) dest_to -- a list of receivers' email addresses ('')</span>
<span class="sd"> (str) subject -- the mail's subject</span>
<span class="sd"> (str) mail_server -- the smtp server (default 'localhost')</span>
<span class="sd"> (str) server_user -- the smtp server user (default '')</span>
<span class="sd"> (str) server_pass --the smtp server password (default '')</span>
<span class="sd"> If 'send_from' or 'dest_to' are empty or None, then script user's</span>
<span class="sd"> mailbox is assumed instead. Useful for loggin scripts</span>
<span class="sd"> """</span>
<span class="n">local_email</span> <span class="o">=</span> <span class="s">'@'</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="n">os</span><span class="o">.</span><span class="n">getenv</span><span class="p">(</span><span class="s">'LOGNAME'</span><span class="p">),</span> <span class="n">socket</span><span class="o">.</span><span class="n">gethostname</span><span class="p">()])</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">send_from</span><span class="p">:</span>
<span class="n">send_from</span> <span class="o">=</span> <span class="n">local_email</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">dest_to</span><span class="p">:</span>
<span class="n">dest_to</span> <span class="o">=</span> <span class="p">[</span><span class="n">local_email</span><span class="p">]</span>
<span class="n">dest_to_addrs</span> <span class="o">=</span> <span class="n">COMMASPACE</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">dest_to</span><span class="p">)</span> <span class="c"># receivers mails</span>
<span class="n">message</span> <span class="o">=</span> <span class="n">MIMEMultipart</span><span class="p">()</span>
<span class="n">message</span><span class="p">[</span><span class="s">'Subject'</span><span class="p">]</span> <span class="o">=</span> <span class="s">'{0} - {1}'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">subject</span><span class="p">,</span>
<span class="n">time</span><span class="o">.</span><span class="n">strftime</span><span class="p">(</span><span class="s">'%A </span><span class="si">%x</span><span class="s">, </span><span class="si">%X</span><span class="s">'</span><span class="p">))</span>
<span class="n">message</span><span class="p">[</span><span class="s">'From'</span><span class="p">]</span> <span class="o">=</span> <span class="n">send_from</span>
<span class="n">message</span><span class="p">[</span><span class="s">'To'</span><span class="p">]</span> <span class="o">=</span> <span class="n">dest_to_addrs</span>
<span class="n">message</span><span class="p">[</span><span class="s">'Date'</span><span class="p">]</span> <span class="o">=</span> <span class="n">formatdate</span><span class="p">(</span><span class="n">localtime</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="n">message</span><span class="o">.</span><span class="n">preamble</span> <span class="o">=</span> <span class="s">"You'll not see this in a MIME-aware mail reader.</span><span class="se">\n</span><span class="s">"</span>
<span class="n">message</span><span class="o">.</span><span class="n">attach</span><span class="p">(</span><span class="n">MIMEText</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__log</span><span class="p">))</span>
<span class="c"># initialize the mail server</span>
<span class="n">server</span> <span class="o">=</span> <span class="n">smtplib</span><span class="o">.</span><span class="n">SMTP</span><span class="p">()</span>
<span class="c"># Connect to mail server</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">server</span><span class="o">.</span><span class="n">connect</span><span class="p">(</span><span class="n">mail_server</span><span class="p">)</span>
<span class="k">except</span> <span class="n">socket</span><span class="o">.</span><span class="n">gaierror</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="s">'mail error'</span><span class="p">,</span> <span class="s">'Wrong server, are you sure is correct?'</span><span class="p">)</span>
<span class="k">except</span> <span class="n">socket</span><span class="o">.</span><span class="n">error</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="s">'mail error'</span><span class="p">,</span> <span class="s">'Server unavailable or connection refused'</span><span class="p">)</span>
<span class="c"># Login in mail server</span>
<span class="k">if</span> <span class="n">mail_server</span> <span class="o">!=</span> <span class="s">'localhost'</span><span class="p">:</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">server</span><span class="o">.</span><span class="n">login</span><span class="p">(</span><span class="n">server_user</span><span class="p">,</span> <span class="n">server_pass</span><span class="p">)</span>
<span class="k">except</span> <span class="n">smtplib</span><span class="o">.</span><span class="n">SMTPAuthenticationError</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="s">'mail error'</span><span class="p">,</span> <span class="s">'Authentication error'</span><span class="p">)</span>
<span class="k">except</span> <span class="n">smtplib</span><span class="o">.</span><span class="n">SMTPException</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="s">'mail error'</span><span class="p">,</span> <span class="s">'No suitable authentication method'</span><span class="p">)</span>
<span class="c"># Send mail</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">server</span><span class="o">.</span><span class="n">sendmail</span><span class="p">(</span><span class="n">send_from</span><span class="p">,</span> <span class="n">dest_to_addrs</span><span class="p">,</span> <span class="n">message</span><span class="o">.</span><span class="n">as_string</span><span class="p">())</span>
<span class="k">except</span> <span class="n">smtplib</span><span class="o">.</span><span class="n">SMTPRecipientsRefused</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="s">'mail error'</span><span class="p">,</span> <span class="s">'All recipients were refused.'</span>
<span class="s">'Nobody got the mail.'</span><span class="p">)</span>
<span class="k">except</span> <span class="n">smtplib</span><span class="o">.</span><span class="n">SMTPSenderRefused</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="s">'mail error'</span><span class="p">,</span> <span class="s">'The server didn’t accept the from_addr'</span><span class="p">)</span>
<span class="k">except</span> <span class="n">smtplib</span><span class="o">.</span><span class="n">SMTPDataError</span><span class="p">:</span>
<span class="bp">self</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="s">'mail error'</span><span class="p">,</span> <span class="s">'An unexpected error code, Data refused'</span><span class="p">)</span>
<span class="c"># Disconnect from server</span>
<span class="n">server</span><span class="o">.</span><span class="n">quit</span><span class="p">()</span>
<span class="k">def</span> <span class="nf">write</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">append</span><span class="o">=</span><span class="bp">False</span><span class="p">):</span>
<span class="sd">"""Write the log to a file.</span>
<span class="sd"> The name of the file will be like this:</span>
<span class="sd"> script.log</span>
<span class="sd"> where 'script' is the name of the script file without extension (.py)</span>
<span class="sd"> (boolean) append -- If true appends log to file, else writes a new one</span>
<span class="sd"> """</span>
<span class="n">mode</span> <span class="o">=</span> <span class="s">'ab'</span> <span class="k">if</span> <span class="n">append</span> <span class="k">else</span> <span class="s">'wb'</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">filename</span><span class="p">,</span> <span class="n">mode</span><span class="p">)</span> <span class="k">as</span> <span class="n">log_file</span><span class="p">:</span>
<span class="n">log_file</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">__log</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="sd">"""Main section"""</span>
<span class="n">url</span> <span class="o">=</span> <span class="s">'http://joedicastro.com'</span>
<span class="n">head</span> <span class="o">=</span> <span class="s">'This is a test of class Logger'</span>
<span class="n">log</span> <span class="o">=</span> <span class="n">Logger</span><span class="p">()</span>
<span class="n">log</span><span class="o">.</span><span class="n">header</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">head</span><span class="p">)</span>
<span class="n">log</span><span class="o">.</span><span class="n">time</span><span class="p">(</span><span class="s">'Start time'</span><span class="p">)</span>
<span class="n">log</span><span class="o">.</span><span class="n">block</span><span class="p">(</span><span class="s">'Block'</span><span class="p">,</span> <span class="s">'This is a sample of Logger.block()'</span><span class="o">.</span><span class="n">split</span><span class="p">())</span>
<span class="n">log</span><span class="o">.</span><span class="n">list</span><span class="p">(</span><span class="s">'List'</span><span class="p">,</span> <span class="s">'This is a sample of Logger.list()'</span><span class="o">.</span><span class="n">split</span><span class="p">())</span>
<span class="n">log</span><span class="o">.</span><span class="n">free</span><span class="p">(</span><span class="s">'''This a sample of logger.free() text.</span>
<span class="s">"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque sed</span>
<span class="s">tortor eget justo vehicula consequat vel eu quam. Suspendisse non lectus eget</span>
<span class="s">orci varius adipiscing."'''</span><span class="p">)</span>
<span class="n">log</span><span class="o">.</span><span class="n">time</span><span class="p">(</span><span class="s">'End time'</span><span class="p">)</span>
<span class="n">log</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="s">'This is mail test'</span><span class="p">)</span>
<span class="n">log</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="bp">True</span><span class="p">)</span>
<span class="n">log</span><span class="o">.</span><span class="n">free</span><span class="p">(</span><span class="s">'All of this had been recorded in {0}'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span><span class="n">log</span><span class="o">.</span><span class="n">filename</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="n">log</span><span class="o">.</span><span class="n">get</span><span class="p">())</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">"__main__"</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
</pre></div>
<p>Para acceder a la versión más reciente del mismo, acudir a el repositorio
<em>Python Recipes</em> en <a href="http://github.com/joedicastro/python-recipes">github</a>.</p>
<p>Ejemplos de utilización de este modulo pueden ser
<a href="http://joedicastro.com/sincronizar_una_carpeta_local_y_una_remota_a_traves_de_ftp_lftp_mirror">lftp-mirror</a> (incluido dentro del mismo), <a href="http://joedicastro.com/ted_talks_descargar_videos_y_subtitulos_de_las_charlas">TEDTalks</a>, <a href="http://joedicastro.com/generar_informes_de_cambios_en_paquetes_instalados_en_debian_y_ubuntu">dpkg_diff</a> y <a href="http://joedicastro.com/combatir_el_spam_en_drupal">ban_drupal_spammers</a></p>