13 de abril de 2009

Creación de Reportes con JasperRepots y iReports - Parte 5: Gráficas en Reportes

Visita la parte 1 de este tutorial: Reportes con Conexión a Bases de Datos
Visita la parte 2 de este tutorial: Usando DataSources Personalizados
Visita la parte 3 de este tutorial: Parámetros y Variables
Visita la parte 4 de este tutorial: Reportes en aplicaciones web
Visita la parte 6 de este tutorial: Grupos
Visita la parte 7 de este tutorial: Subreportes

Algunas veces es más fácil entender la información cuando se nos presenta en una imagen. En el caso de los datos, cuando estamos haciendo comparaciones, lo más fácil es visualizarlos en una gráfica de barras o en una gráfica de pie o pastel. En este tutorial mostraré cómo podemos colocar gráficas en nuestros reportes para hacer comparaciones de un conjunto de datos.


Para este ejemplo los datos que graficaremos son las ventas de consolas de última generación (Wii, XBox360, y PS3). Tomaremos estos datos de VG Chartz que, aunque no son cifras oficiales, mantiene sus datos actualizados. En el momento de escribir este tutorial las ventas van:
  • Wii: 48.79% para nosotros será 49%
  • XBox 360: 29.63% para nosotros será 30%
  • PS3: 21.57% para nosotros será 21%
Nota: Recientemente han aparecido las versiones 3.5 de JasperReports y iReports, por lo que comenzaré a usarlos a partir de este tutorial. La configuración de la biblioteca JasperReports que creamos en este tutorial y el uso de iReport para lo que hemos hecho hasta ahora es igual, por lo que todo lo que hemos hecho servirá.

Bien, para comenzar crearemos un nuevo proyecto Java standalone en NetBeans (File -> New Project... -> Java -> Java Application), le asignamos un nombre (que en mi caso será ReporteGrafica) y presionamos el botón "Finish" para que el nuevo proyecto se genere y veamos en el editor nuestra clase Main.

Agregamos al nodo "Libraries" de nuestro proyecto la biblioteca "JasperReports" que creamos en la primer parte del tutorial y el jar "commons-logging-1.1.1.jar",como lo hemos venido haciendo hasta ahora.

Adicionalmente debemos agregar dos jars más que nos permitirán generar las gráficas. Puesto que JasperReports hace uso de JFreeChart para generar las gráficas debemos agregar dos de sus jars: jfreechart.jar y jcommon.jar. Si bajaron JasperReports en el archivo .zip que contiene las bibliotecas de soporte entonces ya tienen estos jars (que se encuentran en el directorio lib del .zip anterior), solo deben agregarlos al nodo "Libraries" del proyecto.

Ahora necesitamos, como en los casos anteriores, una clase cuyas instancias mantendrán los datos que mostraremos en el reporte. En este caso, como estamos hablando de consolas, creo que lo más adecuado es que esta sea una clase "Jugador". Así que agreguemos esta clase a nuestro proyecto.

Esta clase tendrá tan solo tres atributos: un id (que en este caso podría no ser necesario, pero es para no perder la costumbre, cuando veamos algo de Hibernate veremos que es mejor acostumbrarnos desde el prinicpio a poner este id), un nombre para distinguir al usuario, y el nombre de la consola que tiene (que es el dato que nos interesa para crear la gráfica).

El código de la clase Jugador queda de la siguiente forma:
public class Jugador
{ 
    private int id; 
    private String nombre; 
    private String consola;  

    public Jugador(int id, String nombre, String consola) 
    { 
        this.id = id; 
        this.nombre = nombre; 
        this.consola = consola; 
    }  

    public String getConsola() 
    { 
        return consola; 
    }  

    public void setConsola(String consola) 
    { 
        this.consola = consola; 
    }  

    public int getId() 
    { 
        return id; 
    }  

    public void setId(int id) 
    { 
        this.id = id; 
    }  
    
    public String getNombre() 
    { 
        return nombre; 
    }  

    public void setNombre(String nombre) 
    { 
        this.nombre = nombre; 
    } 
}
El código de la clase Main es el código estandar que hemos estado usando en esta serie de tutoriales de JasperReports, con excepción de la parte en la que se crean los Jugadores. En este caso usaremos tres ciclos, uno para cada consola. Por lo que esta parte del código queda más o menos así (listaJugadores es una lista de objetos Jugador):
for (int i = 1; i <= 49; i++)   
{       
    listaJugadores.add(new Jugador(i, "Jugador " + i , "Wii"));   
}    

for(int i = 50; i <= 79; i++)   
{       
    listaJugadores.add(new Jugador(i, "Jugador " + i , "XBox"));   
}    

for(int i = 80; i <= 100; i++)   
{       
    listaJugadores.add(new Jugador(i, "Jugador " + i , "PS3"));   
}
Ahora abriremos el iReport y comenzaremos con el diseño de nuestro reporte. El nuevo iReport (3.5.0) muestra los menús en el mismo idioma que nuestro sistema operativo, que en mi caso es español, por lo que apartir de ahora colocaré los nombres de las opciones y los menús en español (en el caso en el que los muestre así, si alguno se muestra en inglés colocaré el nombre en inglés).

Creamos un nuevo reporte vacio llendo al menú Archivo -> New... -> Empty Report. En la ventana que se abre colocamos el nombre de nuestro reporte (reporteGrafica.jrxml) y lo guardamos en el directorio raíz de nuestro proyecto de NetBeans:


Comencemos colocando un título y los encabezados de nuestras columnas de datos usando textos estáticos, así como los fields para los datos que obtendremos de cada uno de los objetos Jugador (id, nombre, y consola) de la misma forma que lo hicimos en este tutorial (no olviden guardar de forma constante sus avances para no perder su trabajo):


Ahora vienen los pasos interesantes. Primero debemos crear un grupo. Un grupo nos permite tratar de forma especial un conjunto de datos que se encuentran asociados (es como un group by en SQL). Además, al crear un grupo se genera también una variable que mantiene el conteo de los elementos de cada grupo (como si hicieramos un count sobre los elementos de los grupos en SQL).

En este caso el grupo que nos interesa es el de las consolas.

Si estuvieramos usando consultas SQL obtener estos datos sería sencillo. Es más, si hubieramos creado nuestro reporte usando el "Report Wizard" podríamos haber creado el grupo que nos interesa desde ahi. Pero como estamos usando un DataSource propio, tenemos que agregar el grupo a mano. Afortunadamente es muy sencillo agregar grupos desde iReport. Lo único que debemos hacer es click derecho sobre el nodo "report name" (la raíz de los elementos del proyecto) en el panel "Report Inspector". Con esto se abrirá un menú contextual. De este seleccionamos el elemento "Add Report Group":



Al hacer esto se abrirá la ventana del "Group Wizard". En esta colocamos el nombre del grupo, en este caso será CONSOLAS, y en el campo "Group by the following report object:" seleccionamos el field "consola" que creamos anteriormente. Con esto conseguiremos que el nuevo grupo que se creará se haga con respecto al valor del atributo "cosola" de nuestros objeto "Jugador".


Presionamos el botón "Siguiente >" y se nos mostrarán dos radio buttons preguntando si quieremos agregar la cabecera (header) y el pie de pagina (footer) del grupo. Nosotros deseleccionamos estas opciones (se encuentran seleccionadas por default) ya que no nos interesa agregar ninguno de estos elementos. Para terminar presionamos el botón "Finalizar" y ya tendremos agregado nuestro grupo ^-^.

No es muy obvio el que este grupo existe, de hecho los unicos indicadores que tenemos son que se han agregado en la zona de las bandas del "Report Inspector" dos nuevas bandas: "CONSOLAS Group Header" y "CONSOLAS Group Footer", además se ha agregado una variable llamada "CONSOLAS_COUNT", que es la variable que nos interesa y por la que hemos creado el grupo, que mantendrá el número de jugadores que tienen cada una de las consolas:


Ahora agregaremos nuestra gráfica. Para eso arrastraremos desde la Paleta de elementos del reporte un "Chart" y lo colocamos en la banda "Summary" (ajusten el tamaño de la banda para que la gráfica se vea bien).


Al arrastrar la gráfica se abrirá una nueva ventana preguntándonos qué tipo de gráfico queremos mostrar. En este caso seleccionaremos el gráfico de pie o de pastel (la primer o segunda opción):


Presionamos el botón "OK" y la gráfica se agregará a nuestro reporte, ajustenla para que abarque toda el área de la banda.


Lo último que haremos es configurar la gráfica para que obtenga los datos de los campos y variables que nos interesa mostrar. Para esto, hacemos click derecho sobre la gráfica, con lo que se abrirá un menú contextual. En este menú seleccionamos la opción "Chart Data".


Con lo que se abrirá una nueva ventana, y en esta ventana seleccionamos la pestaña "Details". En esta pestaña configuraremos tres valores:
  • Key expression
  • Value expression
  • Label expression
Nota: Dependiendo del tipo de gráfica que estemos haciendo puede que debamos agregar más valores, pero para los gráficos de pie solo son necesarios estos.

La "Key expression" dice cuál será la base que se usará para cada uno de las piezas de la gráfica. En nuestro caso queremos que cada trozo muestre las consolas. Por lo que colocamos como valor $F{consola}.

La "Value expression" dice cuál será el tamaño de cada una de las piezas de la gráfica. Nosotros queremos que cada pieza sea equivalente al número de consolas que se han comprado. Por lo que colocamos como valor $V{CONSOLAS_COUNT}.

Label expression es la etiqueta que se mostrará para cada valor. Nosotros queremos mostrar el nombre de la consola junto con el número de unidades que se han vendido de cada una. Por lo que colocamos como valor $F{consola} + " - " + $V{CONSOLAS_COUNT}. Lo cual colocará como valor el nombre de la consola, concatenándole el signo " - " y concatenándole el número de consolas vendidas (el número de jugadores que han comprado cada una de las consolas).

Esta ventana queda de la siguiente forma:


Presionamos el botón "Close" y hemos terminado con nuestro reporte. Cambiamos a la vista "Preview" para compilar el reporte. No se nos mostrará nada del reporte final. De hecho los regresará a la vista del "Designer" y mostrará una excepción en la ventana de salida indicando algo así: "Key is null in pie dataset". Esto es normal ya que la gráfica no puede generarse ya que no existen datos. Lo importante es que se haya generado el archivo "reporteGrafica.jasper".

Ahora ejecutamos nuestra aplicación, con lo que debe generarse nuestro reporte en PDF y debemos ver, al final del reporte, la siguiente gráfica:

Con lo que comprobamos la gráfica se ha creado correctamente ^-^.

Finalmente este es el codigo de la clase Main (omitiendo los imports):
public class Main 
{ 
    public static void main(String[] args) throws Exception 
    {    
        List listaJugadores = new ArrayList();     

        for (int i = 1; i <= 49; i++)    
        {        
            listaJugadores.add(new Jugador(i, "Jugador " + i , "Wii"));    
        }     

        for(int i = 50; i <= 79; i++)    
        {        
            listaJugadores.add(new Jugador(i, "Jugador " + i , "XBox"));    
        }     

        for(int i = 80; i <= 100; i++)    
        {        
            listaJugadores.add(new Jugador(i, "Jugador " + i , "PS3"));    
        }     

        JasperReport reporte = (JasperReport)JRLoader.loadObject("reporteGrafica.jasper");    
        JasperPrint jasperPrint = JasperFillManager.fillReport(reporte, null, new JRBeanCollectionDataSource(listaJugadores));     

        JRExporter exporter = new JRPdfExporter();    
        exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);    
        exporter.setParameter(JRExporterParameter.OUTPUT_FILE, new File("reporte grafica.pdf"));     

        exporter.exportReport(); 
    } 
}



Bueno, espero que les sea de utilidad. No olviden dejar sus comentarios, dudas y sugerencias.

Saludos y gracias.

Visita la parte 1 de este tutorial: Reportes con Conexión a Bases de Datos
Visita la parte 2 de este tutorial: Usando DataSources Personalizados
Visita la parte 3 de este tutorial: Parámetros y Variables
Visita la parte 4 de este tutorial: Reportes en aplicaciones web
Visita la parte 6 de este tutorial: Grupos
Visita la parte 7 de este tutorial: Subreportes