Fullmenu null

 

23 November 2017

Caso de Uso

Nuestra empresa ha entrado con ganas en el mundo de las redes sociales y quiere generar contenido sobre nuestros productos. Hasta hemos contratado a una persona que se va a encargar de generarlos y publicarlos en Twitter.

Esta persona querría poder compartir de una forma sencilla los "Top Ventas" de nuestros productos así que para ello todos los días te pide que le busques en las bases de datos qué 3 productos son los más vendidos y cuantas unidades hemos vendido de ellos.

Con esta información, y mediante alguna aplicación ofimática tipo Excel, es capaz de generar un gráfico de tartas, salvar la imagen a su disco, abrir Twitter, escribir un mensaje, adjuntar la imagen y twittearla. Fácil. Pesado. Laborioso.

Twitter App

Para poder publicar tweets de forma automática, el primer paso es crear una app en https://apps.twitter.com/ para que Twitter nos genere un conjunto de claves. Si sigues los pasos que ofrece la página al final del proceso obtendras 4 claves que deberás guardar en el fichero twitter4j.properties en el mismo directorio que resida nuestro script.

twitter4j.properties
oauth.consumerKey=XXXXXXXXXX
oauth.consumerSecret=YYYYYYYYYYYYYYYYYYYYYY
oauth.accessToken=527902906-asdfdsafassssssssssssssss
oauth.accessTokenSecret=123mlkjdfd9sfldsjlkj

Dependencias

Nuestro script va a necesitar las siguientes dependencias agrupadas por funcionalidad:

@GrabConfig(systemClassLoader=true)
@Grab('mysql:mysql-connector-java:5.1.6')
@Grab(group='org.groovyfx',module='groovyfx',version='8.0.0',transitive=false, noExceptions=true)
@Grab(group='org.twitter4j', module='twitter4j-core', version='4.0.6')

import groovy.sql.Sql

import static groovyx.javafx.GroovyFX.start
import javafx.application.Platform
import javafx.scene.SnapshotParameters
import javax.imageio.ImageIO
import java.awt.image.BufferedImage

import twitter4j.TwitterFactory;
import twitter4j.StatusUpdate

Básicamente:

  • conexión a la base de datos

  • librerías graficas JavaFX

  • librería de twitter

Customize

Para poder ajustar nuestro script de forma cómoda vamos a utilizar las siguientes variables:

String message ="""
Estamos que nos salimos!!!
Top Ventas de nuestro catálogo. Gracias a todos por elegirnos
#groovy-script
"""

String chartTitle="Top Ventas"

int width=height=400

Obtener Top Sales

Obtener los productos más vendidos puede ser desde una operación trivial hasta una operación compleja de agregados. Eso ya dependerá de tu negocio. Para nuestro caso vamos a suponer que disponemos de una tabla MySQL donde ya se encuentran agregados las ventas por su descripción

def data=[:]    //(1)

Sql sql = Sql.newInstance( "jdbc:mysql://localhost:3306/scripts?jdbcCompliantTruncation=false",
        "root",
        "my-secret-pw",
        "com.mysql.jdbc.Driver")

sql.eachRow("select product_name, sales from sales order by sales limit 4"){
    data[it.product_name] = it.sales as double  //(2)
}
  1. Usaremos un mapa "producto"=unidades para generar el gráfico de tartas

  2. Buscamos en la bbdd e insertamos en nuestro mapa

Generamos el gráfico de tartas

En esta parte podremos usar infinidad de técnicas y capacidades que nos ofrece JavaFX, como por ejemplo objetos 3D, rotaciones, hojas de estilo, etc. Por ahora a nosotros nos bastará con un gráfico de tartas donde se reflejen los productos obtenidos anteriormente y un título para el gráfico.

Una vez renderizado el gráfico nos interesará hacer una foto (snapshot) al nodo en concreto que contiene el gráfico y volcar a fichero la imagen (en formato PNG).

start {
    def saveNode  //(1)
    stage(visible: true) { //(2)
        scene(fill: BLACK, width: width, height: height) {
            saveNode = tilePane() {
                pieChart(data: data, title: chartTitle) //(3)
            }
        }
    }
  1. saveNode será una referencia al nodo que contiene el gráfico

  2. creamos un gráfico de tartas con los datos obtenidos en la bbdd y almacenados en el mapa

  3. asignamos la referencia para poder usarla después

Una vez que la scene JavaFX se encuentra lista realizamos el volcado. Básicamente pediremos a JavaFX que nos vuelque en un objeto Image los pixeles y así podamos convertirlos a fichero PNG

    def snapshot = saveNode.snapshot(new SnapshotParameters(), null);
    BufferedImage bufferedImage = new BufferedImage(saveNode.width as int, saveNode.height as int, BufferedImage.TYPE_INT_ARGB);
    BufferedImage image = javafx.embed.swing.SwingFXUtils.fromFXImage(snapshot, bufferedImage)

    File file = new File('screenshot.png')
    ImageIO.write(image, "png", file )

A Twitter

Generar un tweet mediante twitter4j es tan sencillo como crear un StatusUpdate, adjuntar un fichero si así lo deseamos y enviarlo:

    StatusUpdate status = new StatusUpdate(message) //(1)
    status.media(file ) //(2)
    TwitterFactory.singleton.updateStatus status
  1. El mensaje que definimos en la parte de customizacion al principio

  2. El fichero png screenshot que hemos generado en el apartado anterior

Aquí puedes ver cómo quedaría un tweet:

tweetsales

Cerrar automáticamente

Por último una vez cumplida su misión simplemente indicamos a JavaFX que cierre la ventana automáticamente

    Platform.exit();
    System.exit(0);

Script
//tag::dependencies[]
@GrabConfig(systemClassLoader=true)
@Grab('mysql:mysql-connector-java:5.1.6')
@Grab(group='org.groovyfx',module='groovyfx',version='8.0.0',transitive=false, noExceptions=true)
@Grab(group='org.twitter4j', module='twitter4j-core', version='4.0.6')

import groovy.sql.Sql

import static groovyx.javafx.GroovyFX.start
import javafx.application.Platform
import javafx.scene.SnapshotParameters
import javax.imageio.ImageIO
import java.awt.image.BufferedImage

import twitter4j.TwitterFactory;
import twitter4j.StatusUpdate
//end::dependencies[]


//tag::customize[]
String message ="""
Estamos que nos salimos!!!
Top Ventas de nuestro catálogo. Gracias a todos por elegirnos
#groovy-script
"""

String chartTitle="Top Ventas"

int width=height=400
//end::customize[]

//tag::business[]
def data=[:]    //(1)

Sql sql = Sql.newInstance( "jdbc:mysql://localhost:3306/scripts?jdbcCompliantTruncation=false",
        "root",
        "my-secret-pw",
        "com.mysql.jdbc.Driver")

sql.eachRow("select product_name, sales from sales order by sales limit 4"){
    data[it.product_name] = it.sales as double  //(2)
}
//end::business[]

//tag::vista[]
start {
    def saveNode  //(1)
    stage(visible: true) { //(2)
        scene(fill: BLACK, width: width, height: height) {
            saveNode = tilePane() {
                pieChart(data: data, title: chartTitle) //(3)
            }
        }
    }
    //end::vista[]

    //tag::snapshot[]
    def snapshot = saveNode.snapshot(new SnapshotParameters(), null);
    BufferedImage bufferedImage = new BufferedImage(saveNode.width as int, saveNode.height as int, BufferedImage.TYPE_INT_ARGB);
    BufferedImage image = javafx.embed.swing.SwingFXUtils.fromFXImage(snapshot, bufferedImage)

    File file = new File('screenshot.png')
    ImageIO.write(image, "png", file )
    //end::snapshot[]

    //tag::twitter[]
    StatusUpdate status = new StatusUpdate(message) //(1)
    status.media(file ) //(2)
    TwitterFactory.singleton.updateStatus status
    //end::twitter[]

    //tag::exit[]
    Platform.exit();
    System.exit(0);
    //end::exit[]
}