OpenSource OpenData

Jorge Aguilera

Who I am?

Jorge Aguilera

https://jorge.aguilera.soy

@jagedn

Groovy developer

Speaker MadridGuG, Codemotion

Tutorial Asciidoctor (Amazon)

PuraVida Software

Anteriormente una empresa de servicios

Actualmente "marca personal"

Qué es (y qué no es) esta charla

Agenda

  • Poquito de teoría

  • Planteamiento

  • Catálogo de datos de Madrid (y otros)

  • Casos de uso

    • Publicar en canales

    • Bots

    • Little Data

Teoría

Las 5 estrellas de Tim Berners-Lee

5 star steps

OpenSource - OpenData - OpenSource

Mi planteamiento:

  • usar OpenSource en la medida de lo posible

  • consumir OpenData

  • publicar OpenSource

Catálogos de datos abiertos

OpenDataMadrid

Mensaje diario a un canal de Telegram, Twitter, Mastodon, etc

OpenDataMadrid, proyecto

Usando el scheduler pipeline de Gitlab ejecutamos diariamente un build que envía a los canales los diferentes eventos

Demo

Parseo CSV con Groovy

String text = "https://raw.githubusercontent.com/jagedn/versosalpaso.madrid.es.csv/master/versosalpaso.csv".toURL().text
String line = text.split('\n')[ Calendar.instance[Calendar.DAY_OF_YEAR] ]
String[] fields = line.split('\\|',-1)
new File("build/versos_telegram.txt").text = """\"${fields[5]}\"
-${fields[3]}

${fields[6]} ${fields[4]}

#VersosAlPaso #Madrid
https://versosalpaso.madrid.es
"""

Parseo Excel con Groovy

xlsx.bytes = 'https://datos.madrid.es/egob/catalogo/300302-10875226-carreras-urbanas.xlsx'.toURL().bytes
Workbook workbook = WorkbookFactory.create(xlsx)
workbook.sheetIterator().each{ sheet ->
    sheet.rowIterator().each{ row ->
        Cell when = row.getCell(0)
        if( when.cellTypeEnum == CellType.NUMERIC ){
            eventos.add when:when.dateCellValue.format( 'dd/MM/yyyy' ),
                    duration: duration.days,
                    title: row.getCell(2),
                    distance: row.getCell(3),
                    where: row.getCell(4)
        }
        ....

Spin-off Gradle Plugin SocialNetwork

Plugin Gradle para enviar mensajes vía Telegram, Twitter, Slack y Mastodon

Interactuar con usuario

  • Páginas web,

  • Una aplicación para móvil

  • o algo más molón, un bot de Telegram (especial para backends sin habilidades en el diseño web)

Requisitos Bot

Poder ejecutar código bajo demanda (servidor java, node, serverless, etc) (un http endpoint)

Responder de forma inmediata (un ACK, luego se puede mantener la conversación)

Bot en sí no requiere recursos excesivos. Dependerá de la funcionalidad (obvio)

Heroku, GoogleCloud, Okteto, Netlify

Cámaras trafico bot (Madrid,Barcelona,Granada)

Bot de Telegram

recibe peticiones de calles y devuelve un gif animado con las últimas cámaras

Groovy (java) corriendo en capa gratuita Okteto

Cámaras trafico bot, proyecto

Parseo XML con Groovy

Namespace gml = new Namespace("http://www.opengis.net/gml", 'gml')
Namespace cite = new Namespace("http://www.opengeospatial.net/cite", 'cite')

String xml = barcelonaUrl.toURL().text
def kml = new XmlParser().parseText(xml)

kml[gml.featureMember].eachWithIndex{ f, idx->

   String nombre = f[cite.cameres][cite.carretera].text()
   camaras.add new Camara(ciudad: 'barcelona',
                    id: idx,
                    nombre: nombre,
                    url: f[cite.cameres][cite.link].text()
}

Demo

Spin-off Gif generator

Bot @EstacionesDeServicioBot

Bot de Telegram para conocer el precio de la gasolinera más cercana

Puedes guardar tu favorita y recibir aviso si cambia el precio

EstacionesDeServicioBot

Groovy (java) corriendo en capa gratuita de Okteto

Sin base de datos

usa un sistema de ficheros persistente para mantener sesion y preferencias (anonimizado)

Un pipeline diario revisa cambio de precios y notifica

Demo

Parseo XML con Groovy

url = "https://sedeaplicaciones.minetur.gob.es/ServiciosRESTCarburantes/PreciosCarburantes/EstacionesTerrestres/"
String xml = new InputStreamReader(url.toURL().openStream(), 'UTF-8').text
new XmlParser().parseText(xml).ListaEESSPrecio.EESSPrecio.each { eess ->
    estaciones.add new Estacion(
                id : node.IDEESS.text(),
                direccion : node.Dirección.text() ?: "",
                marca : node.Rótulo.text() ?: "",
                latitude: node.Latitud.text().replace(',', '.') as float,
                longitude: node.Longitud.text().replace(',', '.') as float,
                gasolina95 : stringToPrice(node.PrecioGasolina95Protección.text()),
                ...)
}

Localización

float metersTo(float lat1, float lng1, float lat2, float lng2) {
    double radioTierra = 6371;
    double dLat = Math.toRadians(lat2 - lat1);
    double dLng = Math.toRadians(lng2 - lng1);
    double sindLat = Math.sin(dLat / 2);
    double sindLng = Math.sin(dLng / 2);
    double va1 = Math.pow(sindLat, 2) + Math.pow(sindLng, 2) * Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2));
    double va2 = 2 * Math.atan2(Math.sqrt(va1), Math.sqrt(1 - va1));
    double meters = radioTierra * va2;
    meters as float;
}

Spin-off TelegramApi para Micronaut

@Client('https://api.telegram.org')
interface TelegramClient {

    @Post('/bot${telegram.token}/sendMessage')
    Single<Message> sendMessage(@Body Message message)

    @Post(value='/bot${telegram.token}/sendPhoto', produces = MediaType.MULTIPART_FORM_DATA)
    Single<Message> sendPhoto(@Body MultipartBody photo)

    @Post(value='/bot${telegram.token}/sendAnimation', produces = MediaType.MULTIPART_FORM_DATA)
    Single<Message> sendAnimation(@Body MultipartBody animation)

    @Get(value='/bot${telegram.token}/sendLocation')
    Single<Message> sendLocation(@QueryValue("chat_id") String chat_id,
                                 @QueryValue("latitude") float latitude,
                                 @QueryValue("longitude") float longitude)

}

"Little" Data, Préstamos Bibliotecas

Compartir en el canal los libros más leídos en las Bibliotecas de Madrid del mes anterior

Reto: parsear cada mes un CSV con 200K líneas y buscar los TOP en pocos minutos

Solución (entre muchas): usar una bbdd Derby embebida

Spin-off 1: QueryCSV Plugin

Plugin Gradle para tratar CSV "pesados" y extraer información mediante SQL

Spin-off 2: Dataset público con BigQuery

Recolectando en un BigQuery los prestamos desde 2014 para ofrecerlos como dataset publico (en pausa por el COVID19)

Conclusiones

  • Infinidad de datos ahí fuera

  • OpenData NO es solo tratar ingentes cantidades de datos

  • Múltiples formas de interactuar

  • Oportunidad de aprender

  • Coste "cero" (económico)

Q&A & Feedback

ContactFeedback

@jagedn

https://forms.gle/{google-form} https://forms.gle/{google-form}

https://gitlab.com/puravida-software

https://www.linkedin.com/in/jagedn/

gracias multilingue