sábado, 15 de agosto de 2009

Mono, C# y Archivos ODS



Tratando de acceder a un archivo en formato xls de Excel con C#, tuve algunos problemas accediendo al contenido de la celda de cada columna de la hoja de cálculo. Así pues empece a investigar otra manera de hacerlo rápido y usando por supuesto C#. Con OpenOffice.org Calc encontré una manera de acceder a ello despues de varias horas probando el acceso al contenido de las celdas. El inconveniente es que no había mucho código para leer y documentarse un poco.

Como estoy aprendiendo el perfil de tester, y usamos el TFS para reportar Bugs o incidencias, y de ahí generamos reportes de los mismos en archivos xls. Quería averiguar la manera de acceder al contenido de las celdas de cada columna para comparar si este bug estaba duplicado o no, o bien, si este bug tenía una relación con otro bug que se le pareciera en su contenido de acuerdo a un porcentaje de parecido.

Usando Fedora 11, Mono 2.x y OOo Calc, lo logré. Aquí el código fuente del archivo bugs.cs:


/*
* Copyright (C) Gerardo Gonzalez Cruz gerardogc2378@gmail.com July 2009.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

using System;
using System.IO;
using Gtk;
using System.Xml;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace XMLBugs
{
public class Bugs
{
private string currentDescription = string.Empty;
private string nextDescription = string.Empty;
private string currentBugID = string.Empty;
private string nextBugID = string.Empty;
private string currentSteps = string.Empty;
private string nextSteps = string.Empty;
private string error = string.Empty;
private double diff = -1;
private string similarBugs = string.Empty;
private string equalBugs = string.Empty;
private Gtk.TreeStore bugsTreeStore = new Gtk.TreeStore(typeof (string), typeof (string), typeof (string));
private Gtk.TreeIter iterBug;
private Stack<string> stack = new Stack<string>();

public static void Main()
{
new Bugs();
}

public Bugs()
{
readXMLFile();
//saveToFile();
setBinding();
}

private void saveToFile()
{
string fileName = getFileName();
TextWriter twEqualBugs = new StreamWriter("equal" + fileName);
TextWriter twSimilarBugs = new StreamWriter("similar" + fileName);

twEqualBugs.WriteLine(equalBugs);
twSimilarBugs.WriteLine(similarBugs);

twEqualBugs.Close();
twSimilarBugs.Close();
}

private void setBinding()
{
Application.Init();

Gtk.Window window = new Gtk.Window("Bugs");
window.SetSizeRequest(800,600);

Gtk.ScrolledWindow sw = new Gtk.ScrolledWindow();

Gtk.TreeView tree = new Gtk.TreeView();

sw.Add(tree);

window.Add(sw);

Gtk.TreeViewColumn statusColumn = new Gtk.TreeViewColumn();
statusColumn.Title = "Estado";

Gtk.CellRendererText statusCell = new Gtk.CellRendererText();

statusColumn.PackStart(statusCell, true);

Gtk.TreeViewColumn bugIDColumn = new Gtk.TreeViewColumn();
bugIDColumn.Title = "BugID";

Gtk.CellRendererText bugIDCell = new Gtk.CellRendererText();

bugIDColumn.PackStart(bugIDCell, true);

Gtk.TreeViewColumn diffColumn = new Gtk.TreeViewColumn();
diffColumn.Title = "% Similitud";

Gtk.CellRendererText diffCell = new Gtk.CellRendererText();

diffColumn.PackStart(diffCell, true);

tree.AppendColumn(statusColumn);
tree.AppendColumn(bugIDColumn);
tree.AppendColumn(diffColumn);

statusColumn.AddAttribute(statusCell, "text", 0);
bugIDColumn.AddAttribute(bugIDCell, "text", 1);
diffColumn.AddAttribute(diffCell, "text", 2);

statusColumn.SetCellDataFunc(statusCell, new Gtk.TreeCellDataFunc(RenderStatus));

tree.Model = bugsTreeStore;

window.DeleteEvent += new DeleteEventHandler(delete_window);
window.ShowAll();

Application.Run();
}

private void RenderStatus(Gtk.TreeViewColumn column, Gtk.CellRenderer cell, Gtk.TreeModel model, Gtk.TreeIter iter)
{
if(model.GetValue(iter, 0).ToString() == "Duplicado:")
(cell as Gtk.CellRendererText).Foreground = "red";
else
(cell as Gtk.CellRendererText).Foreground = "black";
}

private static void delete_window(System.Object o, DeleteEventArgs args)
{
Application.Quit();
args.RetVal = true;
}

private void readXMLFile()
{
try
{
XmlDocument xDoc = new XmlDocument();
xDoc.Load("content.xml");
XmlNodeList xnlBugs = xDoc.GetElementsByTagName("table:table");
XmlNodeList xnlAllRows = ((XmlElement)xnlBugs[0]).GetElementsByTagName("table:table-row");

for(int currentRow = 1; currentRow < xnlAllRows.Count; currentRow++)
{
XmlNodeList xnlCurrentRow = ((XmlElement)xnlAllRows[currentRow]).GetElementsByTagName("table:table-cell");

for(int currentCol = 0; currentCol < xnlCurrentRow.Count; currentCol++)
{
switch(currentCol)
{
case 0:
{
XmlNodeList xnlColumn = ((XmlElement)xnlCurrentRow[currentCol]).GetElementsByTagName("text:p");

for(int line = 0; line < xnlColumn.Count; line++)
currentBugID = xnlColumn[line].InnerText.ToString();

break;
}
case 1:
{
XmlNodeList xnlColumn = ((XmlElement)xnlCurrentRow[currentCol]).GetElementsByTagName("text:p");

for(int line = 0; line < xnlColumn.Count; line++)
currentDescription += xnlColumn[line].InnerText.ToString();

break;
}
case 2:
{
XmlNodeList xnlColumn = ((XmlElement)xnlCurrentRow[currentCol]).GetElementsByTagName("text:p");

for(int line = 0; line < xnlColumn.Count; line++)
currentSteps += xnlColumn[line].InnerText.ToString();

break;
}
default: break;
}
}

if(currentBugID == string.Empty)
{
currentBugID = string.Empty;
currentDescription = string.Empty;
currentSteps = string.Empty;
continue;
}
else
Console.WriteLine("Processing: {0}", currentBugID);

for(int nextRow = currentRow + 1; nextRow < xnlAllRows.Count; nextRow++)
{
XmlNodeList xnlNextRow = ((XmlElement)xnlAllRows[nextRow]).GetElementsByTagName("table:table-cell");

for(int nextCol = 0; nextCol < xnlNextRow.Count; nextCol++)
{
switch(nextCol)
{
case 0:
{
XmlNodeList xnlColumn = ((XmlElement)xnlNextRow[nextCol]).GetElementsByTagName("text:p");

for(int line = 0; line < xnlColumn.Count; line++)
nextBugID = xnlColumn[line].InnerText.ToString();

break;
}
case 1:
{
XmlNodeList xnlColumn = ((XmlElement)xnlNextRow[nextCol]).GetElementsByTagName("text:p");

for(int line = 0; line < xnlColumn.Count; line++)
nextDescription += xnlColumn[line].InnerText.ToString();

break;
}
case 2:
{
XmlNodeList xnlColumn = ((XmlElement)xnlNextRow[nextCol]).GetElementsByTagName("text:p");

for(int line = 0; line < xnlColumn.Count; line++)
nextSteps += xnlColumn[line].InnerText.ToString();

break;
}
default: break;
}
}

if(nextBugID == string.Empty)
{
nextBugID = string.Empty;
nextDescription = string.Empty;
nextSteps = string.Empty;
continue;
}

if(currentDescription.Equals(nextDescription)
&& currentSteps.Equals(nextSteps))
{
if(!stack.Contains(currentBugID))
{
iterBug = bugsTreeStore.AppendValues(currentBugID, "", "");
stack.Push(currentBugID);
}

equalBugs += currentBugID + " >> " + nextBugID + "\n";
bugsTreeStore.AppendValues(iterBug, "Duplicado:", nextBugID, "100%");
}
else
{
if(isSimilar(currentDescription + " " + currentSteps,
nextDescription + " " + nextSteps))
{
if(!stack.Contains(currentBugID))
{
iterBug = bugsTreeStore.AppendValues(currentBugID, "", "");
stack.Push(currentBugID);
}

similarBugs += currentBugID + " >> " + nextBugID + "\n";
bugsTreeStore.AppendValues(iterBug, "Similar:", nextBugID, diff.ToString() + "%");
}
}

nextBugID = string.Empty;
nextDescription = string.Empty;
nextSteps = string.Empty;
diff = -1;
}

currentBugID = string.Empty;
currentDescription = string.Empty;
currentSteps = string.Empty;
}

xnlAllRows = null;
xnlBugs = null;
xDoc = null;
}
catch(Exception err)
{
this.error = err.Message;
Console.WriteLine(error);
}
}

private bool isSimilar(string A, string B)
{
if(A == string.Empty
|| B == string.Empty)
{
diff = -1;
return false;
}
else
{
string[] words = null;
string finalString = string.Empty;

switch(A.Length > B.Length)
{
case true:
{
words = B.ToLower().Split(' ');
finalString = A.ToLower();

for(int i = 0; i < words.Length; i++)
{
if(words[i] != string.Empty)
finalString = Regex.Replace(finalString, @"\b(" + removeRareCharacter(words[i]) + @")\b", "~");
}

diff = (finalString.Split('~').Length * 100) / A.Split(' ').Length;

words = null;
finalString = string.Empty;
break;
}
case false:
{
words = A.ToLower().Split(' ');
finalString = B.ToLower();

for(int i = 0; i < words.Length; i++)
{
if(words[i] != string.Empty)
finalString = Regex.Replace(finalString, @"\b(" + removeRareCharacter(words[i]) + @")\b", "~");
}

diff = (finalString.Split('~').Length * 100) / B.Split(' ').Length;

words = null;
finalString = string.Empty;
break;
}
}

if(diff >= 85 && diff <= 100)
return true;
else
return false;
}
}

private string removeRareCharacter(string Obj)
{
string _Obj = Obj;
string[] invalidCharaters = {"\"", ".", ";", ",", "{", "}", "[", "]", "\'", "(", ")", "*", ".-", "-"};

for(int i = 0; i < invalidCharaters.Length; i++)
_Obj = _Obj.Replace(invalidCharaters[i], string.Empty);

return _Obj;
}

private string getFileName()
{
return "Bugs_" +
DateTime.Today.ToShortDateString().Replace("/", ".") +
"_" +
System.DateTime.Now.Hour +
"." +
System.DateTime.Now.Minute +
".txt";
}
}
}


Los pasos que he seguido para ello son los siguientes:

1. Obtengo el arhivo XLS.
2. Lo abro con OOo Calc y lo guardo como ODS.
3. Extraigo del mismo fichero ODS un arhivo de nombre content.xml
4. Lo coloco junto al ejecutable que genera Mono y lo ejecuto.
5. Fin.

lunes, 13 de julio de 2009

OpenSuSE 11.1 x86_64 y Azalia Sound Card

He decido poner OpenSuSE 11.1 de 64 Bits en mi máquina, el problema que tuve fue con la tarjeta de sonido, no había sonido uppps, la solución que me encontré fue la siguiente:
En el directorio de modprobe.d ubicado en etc, existe un archivo llamado sound. Lo primero que realice fue una copia de seguridad del mismo copiandolo hacia el directorio de root, luego lo edite y quedo de la siguiente manera:

-----------------------------------------------------------------------------
options snd cards_limit=1
alias snd-card-0 snd-hda-intel
options snd-hda-intel enable=1 model=hp-m4 enable_msi=1 single_cmd=0 power_save_controller=0 power_save=0
-----------------------------------------------------------------------------

El comando lspci me mostró lo siguiente:

ggc:/home/ggc # lspci | grep Audio
00:14.2 Audio device: ATI Technologies Inc SBx00 Azalia (Intel HDA)

Si tienes una tarjeta parecida a la mía puede resultar que con esas líneas tengas sonido, despues de un reinicio de tu máquina o de ejecutar el comando:

# rcalsasound --restart

miércoles, 13 de mayo de 2009

Instalando Ruby On Rails en mi Fedora 10 x86_64

Cuando leí sobre Ruby On Rails y sobre Ruby como su codebehind, realmente quede impresionado con la fácilidad de programar para entornos Web. Hace 2 años (Septiembre de 2007) que empece con Ruby On Rails con una pequeña aplicación para el departamento del servicio social para el Instituto Tecnológico de Orizaba. Recibí muchas críticas sobre ello, pues los lenguajes de programación que reinaban era Java, Php y C# a nivel licenciatura y con muchos devotos. Me preguntaban por qué había elegido esa cosa rara llamada Ruby On Rails que nadie ocupaba en aquel momento y que sonaba también raro. Mi respuesta fue simple, porque Ruby On Rails tiene un futuro prometedor. ¿Acaso no estamos en un Instituto Tecnológico? ¿No podemos ser visionarios sobre otras tecnologías? ¡Es fácil de aprender!, agregue.

Ahora Rails está en su versión 2.3, vaya el tiempo pasa rápido. Lo tengo con Fedora 10 en la nueva máquina que he comprado, he instalado Ruby On Rails en su versión 2.1.1 y estoy ansioso ya de probar poco a poco que ha cambiado. Lo que sé, es que MySQL ya NO es la base de datos por definición, sino SQLite3, vaya giro y me parece acertado, su uso es simple y muy divertido. Los paquetes que tengo instalados son:

$ rpm -qa | grep rails

rubygem-rails-2.1.1-2.fc10.noarch


$ rpm -qa | grep ruby

rubygems-1.3.1-1.fc10.noarch
rubygem-fastthread-1.0.1-1.fc10.x86_64
ruby-sqlite3-1.2.1-2.fc9.x86_64
ruby-1.8.6.287-2.fc10.x86_64
rubygem-activeresource-2.1.1-1.fc10.noarch
ruby-irb-1.8.6.287-2.fc10.x86_64
rubygem-actionpack-2.1.1-2.fc10.noarch
rubygem-rake-0.8.4-1.fc10.noarch
rubygem-daemons-1.0.7-2.fc8.noarch
ruby-rdoc-1.8.6.287-2.fc10.x86_64
rubygem-actionmailer-2.1.1-1.fc10.noarch
rubygem-rails-2.1.1-2.fc10.noarch
rubygem-gem_plugin-0.2.3-1.fc10.noarch
ruby-libs-1.8.6.287-2.fc10.x86_64
rubygem-activerecord-2.1.1-2.fc10.noarch
rubygem-mongrel-1.0.1-6.fc9.x86_64
rubygem-activesupport-2.1.1-1.fc10.noarch

¿Cómo los instalas? Usa yum para ello, por ejemplo:

# yum install rubygem-rails

En estos momentos estoy reescribiendo la aplicación que realice hace 2 años.
Si te gusta aprender cosas nuevas, con comodidad y elegancia en el código yo creo que Ruby y Rails son para ti.

martes, 12 de mayo de 2009

Usando wget

Andaba buscando libros sobre el Kernel Linux y me encontré uno muy bueno de hecho ya lo había tenido antes pero desgracidamente perdí mi libro. El libro en cuestión es "Linux Device Drivers, Third Edition" en cual se encuentra en la siguiente liga http://oreilly.com/catalog/linuxdrive3/book/index.csp. Al verlo de nuevo me emocione y por supuesto tenía que tenerlo de vuelta. Mi problema es que no quería bajarlo uno por uno dando tantos clicks como fuese necesario por cada capítulo del mismo, así pues me ayude un poco con el comando GNU Wget estos son los pasos que he usado:

  • Primero cheque que había un patron entre ellos, el cual es: http://oreilly.com/catalog/linuxdrive3/book/ch01, http://oreilly.com/catalog/linuxdrive3/book/ch02 ... http://oreilly.com/catalog/linuxdrive3/book/ch'n', terminando con la extensión '.pdf'.
  • Luego definí una variable: export LDD="http://oreilly.com/catalog/linuxdrive3/book/ch0".
  • Confirme que estaba: echo $LDD.
  • Fije mi secuencia con: seq 1 9.
  • Y finalmente ejecute: for i in `seq 1 9`; do wget -c $LDD$i.pdf; done.
  • :-O y segui con el siguiente patrón que es: http://oreilly.com/catalog/linuxdrive3/book/ch10, http://oreilly.com/catalog/linuxdrive3/book/ch11 ... http://oreilly.com/catalog/linuxdrive3/book/ch'n', terminando con la extensión '.pdf'.
  • redefini mi variable LDD: export LDD="http://oreilly.com/catalog/linuxdrive3/book/ch".
  • y ejecute: for i in `seq 10 18`; do wget -c $LDD$i.pdf; done.
Ahora están de vuelta :D y vemos cómo es útil este comando GNU Wget el cual tengo en su versión 1.11.4 que viene definido en mi Fedora 10.

domingo, 3 de mayo de 2009

Shell interactivo de C Sharp

Leo en el Blog de Miguel De Icaza algo que me llamo la atención sobre su shell interactivo de C Sharp (C#) con código de autocompletado, es decir con un solo click sobre la tecla de tabulador puedes autocompletar el código que en ese momento se este escribiendo o bien presionando dos veces puedes tener un listado de opciones para que escojas el adecuado, muy similar al shell de GNU Bash :-D. Según De Icaza sirve para conocer las posibilidades que ofrece el API y explorarlo a fondo, por ejemplo el API de GTK#, la primera vez que leí este API me dieron dolores de cabeza, ya que en la documentación no había tantos ejemplos como me lo hubiera esperado y además mi Firefox se atascaba en diversas partes de la misma, no sé si esta situación ya este arreglada, esto fue hace 2 años cuando empece a programar en capas con C#, Gtk#, Glade#, usando Mono 1.x y PostgreSQL 8.x (PLPGSQL).

Referencia:
http://tirania.org/blog/archive/2008/Sep-08.html

Influenza humana

Leo en Slashdot algo sobre la influenza humana llamada H1N1, al parecer esta puede transmitirse de humanos a cerdos. La historia es la siguiente:

Por lo visto un agricultor de cerdos canadiense vacacionó en México, regresó (a Canadá) e infectó a aproximadamente el 10 % de los cerdos de una granja de Alberta. Los cerdos posteriormente desarrollaron síntomas de gripe. "

Vaya noticia no se ve muy alentadora sobre todo para nosotros los mexicanos que hemos perdido nuestras inversiones locales en miles de millones de pesos diarios y otros han quedado desempleados por la crisis económica mundial. Esta de miedo :-(.

Referencias:
http://www.cbc.ca/canada/story/2009/05/02/swineflu-ns-cases789.html
http://slashdot.org/

Uso de GNU/Linux en el desktop

Leo en Barrapunto.com que el GNU/Linux ocupa el apenas 1% en los PC's Domesticos, según un informe mensual de Market Share. Juraría que GNU/Linux tiene un poco más que eso pero en fin. Según leo Windows XP tiene el 62.21%, el Mac OSX 10.5 el 6.17%. Caray mexicanos ya usen más GNU/Linux, hay de distintos sabores, colores y con un amplio software de calidad que no se pueden ya quejar, que sino jala esta cosa, que si dependo del MS Office, que Windows hace esto, que mi autocad, que mi inche p2p, bueno usa máquinas virtuales y gasta menos. Hay distros que hasta niños pueden mantener como Ubuntu (bueno quizás no tanto pero los hay), y si te consideras ya más especializado y toda la cosa usa Gentoo, Fedora, OpenSuSE, Mandriva, Arch, Debian o LFS anyway!.

Referencias:
http://marketshare.hitslink.com/operating-system-market-share.aspx?qprid=10
http://softlibre.barrapunto.com/softlibre/09/05/02/2315222.shtml