sábado, 31 de agosto de 2024

Sinatra, un DSL útil para crear sitios web de manera rápida

Sinatra es una herramienta que toma su nombre del famoso cantante estadounidense. Similar a otras herramientas como Flask (salvando las diferencias, claro esta). Con la peculiaridad que esta diseñado para el lenguaje de programación Ruby.

¿Qué haremos?

  1. Instalar Ruby.
  2. Instalar Sinatra.
  3. Crear un proyecto sencillo con Sinatra.

Una vez instalado Ruby podemos comprobar la instalación:

ruby --version

Instalamos Sinatra y Rackup:

gem install sinatra
gem install rackup

También podemos instalar Puma (opcional):

gem install puma

Para ver nuestras "gemas" instaladas, basta con ubicarnos en esta carpeta (gem):

C:\Users\HP\.local\share\gem\ruby\3.3.0\gems

Creamos un programa llamado ``myapp.rb``. El cual tendrá lo siguiente:

# myapp.rb
require 'sinatra'

get '/' do
  'Hola, mundo!'
end

Ejecutamos la aplicación:

ruby myapp.rb

Si todo es correcto, veremos un mensaje de "Hola, mundo!" en la dirección http://[::1]:4567 o http://localhost:4567

¡Hemos creado nuestra primera aplicación con Sinatra!

En próximas entregas continuaremos con este tema.

Enlaces:

https://sinatrarb.com/
https://www.ruby-lang.org/es/
https://rubyinstaller.org/
https://crguezl.github.io/apuntes-ruby/node409.html

sábado, 10 de agosto de 2024

GCP: Calculando el IMC con BigQuery

 


En esta ocasión veremos cómo crear una función y procedimiento almacenado para el cálculo del IMC (Índice de Masa Corporal), necesitaremos lo siguiente:

  1. La fórmula para el cálculo del IMC.
  2. Crear una función para calcularlo.
  3. Crear un procedimiento almacenado (stored procedure) para usar la función.
  4. Crear un bloque de código en BigQuery para invocar el procedimiento. 

Tenemos la fórmula:

-- Obtener IMC
IMC = p/(t*t)

Donde:
p= peso
t= talla o estatura
imc = el índice de masa corporal

Creando función para calcular el IMC:

CREATE OR REPLACE FUNCTION `myproject.dataset.imcCalculo`(peso NUMERIC, talla NUMERIC)
RETURNS NUMERIC AS(
  (peso/(talla*talla))
);

Básicamente es una operación sencilla que solo incluye una multiplicación de la talla y la división del peso.

Creando procedimiento almacenado para invocar la función:

CREATE OR REPLACE PROCEDURE `myproject.dataset.getIMC`(peso NUMERIC, talla NUMERIC, OUT msg STRING)
BEGIN 
  DECLARE imc NUMERIC DEFAULT 0.0;
  
  set msg = 'Iniciamos calculo. ';
  SET imc = (SELECT `myproject.dataset.imcCalculo`(peso, talla));
  set msg = concat(msg, 'El IMC es: ', imc, '. Resultado: ');
  set msg = concat(msg, (  
    CASE imc
    WHEN imc<=16.0 OR imc<=16.99 THEN 'Delgadez moderada.'
    WHEN imc<=17.0 OR imc<=18.49 THEN 'Delgadez aceptable.'
    WHEN imc<=18.50 OR imc<=24.99 THEN 'Peso normal.'
    WHEN imc<=25.0 OR imc<=29.99 THEN 'Sobrepeso.'
    WHEN imc<=30.0 OR imc<=34.99 THEN 'Obeso tipo I.'
    WHEN imc<=35.0 OR imc<=40.0 THEN 'Obeso tipo II.'
    ELSE 'No existe clasificacion'
    END)
  );
  
  
  SELECT msg;
  

END;

En el procedimiento almacenado incluimos un mensaje de salida (OUT msg STRING) el cual podrá ser consultado al finalizar el llamado. Este contendrá el cálculo del IMC y el diagnóstico final (peso normal, sobrepeso, etc.).

Creando bloque de BigQuery para invocar procedimiento almacenado:

-- Obtener IMC
BEGIN  
DECLARE peso NUMERIC DEFAULT 56.0;
DECLARE talla NUMERIC DEFAULT 1.65;
DECLARE msg STRING DEFAULT 'Inicio';

CALL `myproject.dataset.getIMC`(peso, talla, msg);
SELECT msg;

END;

Usar un bloque nos ayuda a disminuir una cantidad "grosera" de sentencias.

El resultado de la operación nos dará como resultado: 20.5693. La salida será:

Iniciamos calculo. El IMC es:  20.569329. Resultado: Peso normal.

Crear funciones y procedimientos almacenados es algo muy útil y que nos ahorrará trabajo.

Enlaces:

https://cloud.google.com/bigquery/docs/reference/standard-sql/conditional_expressions
https://codemonkeyjunior.blogspot.com/2024/08/gcp-cargar-y-exportar-datos-en-bigquery.html

https://lineadecodigo.com/java/calcular-el-indice-de-masa-corporal-con-java/
https://codemonkeyjunior.blogspot.com/2013/05/programando-en-ceylon-no1_13.html

sábado, 3 de agosto de 2024

GCP: Cargar y exportar datos en BigQuery


GCP BigQuery nos permite tanto cargar como exportar datos. Los formatos de los archivos pueden ser TXT o CSV generalmente.

Si queremos exportar datos de una tabla a un archivo CSV podríamos hacer los siguiente.

Abrimos la consola de GCP y en el editor de consultas colocamos:

EXPORT DATA
  OPTIONS (
    uri = 'gs://bucket/folder/pesos*.csv',
    format = 'CSV',
    overwrite = true,
    header = true,
    field_delimiter = ';')
AS (
  SELECT fecha, valor
  FROM mydataset.pesos
  ORDER BY fecha
);

Como se puede ver:

  1. Colocamos la ubicación de nuestro Bucket.
  2. Seteamos el formato del archivo, en este caso será un CSV.
  3. El archivo será sobreescrito.
  4. El delimitardor será un punto y coma (;).

Una vez que el comando es ejecutado desde el editor de consultas GCP, debemos uicarnos en el directorio y descargar el archivo CSV generado.

Si queremos complicarnos la vida, podríamos crear un programa en Java que haga exactamente los mismo.

ExtractTableToCsv.java

import com.google.cloud.RetryOption;
import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.Job;
import com.google.cloud.bigquery.Table;
import com.google.cloud.bigquery.TableId;
import org.threeten.bp.Duration;

public class ExtractTableToCsv {

  public static void runExtractTableToCsv() {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "bigquery-public-data";
    String datasetName = "samples";
    String tableName = "shakespeare";
    String bucketName = "my-bucket";
    String destinationUri = "gs://" + bucketName + "/path/to/file";
    // For more information on export formats available see:
    // https://cloud.google.com/bigquery/docs/exporting-data#export_formats_and_compression_types
    // For more information on Job see:
    // https://googleapis.dev/java/google-cloud-clients/latest/index.html?com/google/cloud/bigquery/package-summary.html

    String dataFormat = "CSV";
    extractTableToCsv(projectId, datasetName, tableName, destinationUri, dataFormat);
  }

  // Exports datasetName:tableName to destinationUri as raw CSV
  public static void extractTableToCsv(
      String projectId,
      String datasetName,
      String tableName,
      String destinationUri,
      String dataFormat) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      TableId tableId = TableId.of(projectId, datasetName, tableName);
      Table table = bigquery.getTable(tableId);

      Job job = table.extract(dataFormat, destinationUri);

      // Blocks until this job completes its execution, either failing or succeeding.
      Job completedJob =
          job.waitFor(
              RetryOption.initialRetryDelay(Duration.ofSeconds(1)),
              RetryOption.totalTimeout(Duration.ofMinutes(3)));
      if (completedJob == null) {
        System.out.println("Job not executed since it no longer exists.");
        return;
      } else if (completedJob.getStatus().getError() != null) {
        System.out.println(
            "BigQuery was unable to extract due to an error: \n" + job.getStatus().getError());
        return;
      }
      System.out.println(
          "Table export successful. Check in GCS bucket for the " + dataFormat + " file.");
    } catch (BigQueryException | InterruptedException e) {
      System.out.println("Table extraction job was interrupted. \n" + e.toString());
    }
  }
}

Ahora, ¿si queremos cargar un archivo a una determinada tabla?

Procedemos a escribir la siguiente instrucción en el editor de consultas de GCP:

LOAD DATA INTO mydataset.pesos FROM FILES(
  format = 'CSV',
  uri = ['gs://bucket/folder/123.csv','gs://bucket/folder/456.csv']
);

Donde:

  1. Definimos el formato de los archivos a cargar, en este caso son CSV.
  2. Colocamos la ubicación de los archivos de datos a cargar en la tabla (pesos).

Nota: El archivo CSV debe contener campos que concuerden con los de la tabla. En caso contrario, habrá errores a la hora de la carga.

Si queremos complicarnos la vida, podríamos crear un programa en Java que haga exactamente los mismo.

LoadCsvFromGcs.java

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.CsvOptions;
import com.google.cloud.bigquery.Field;
import com.google.cloud.bigquery.Job;
import com.google.cloud.bigquery.JobInfo;
import com.google.cloud.bigquery.LoadJobConfiguration;
import com.google.cloud.bigquery.Schema;
import com.google.cloud.bigquery.StandardSQLTypeName;
import com.google.cloud.bigquery.TableId;

// Sample to load CSV data from Cloud Storage into a new BigQuery table
public class LoadCsvFromGcs {

  public static void runLoadCsvFromGcs() throws Exception {
    // TODO(developer): Replace these variables before running the sample.
    String datasetName = "MY_DATASET_NAME";
    String tableName = "MY_TABLE_NAME";
    String sourceUri = "gs://cloud-samples-data/bigquery/us-states/us-states.csv";
    Schema schema =
        Schema.of(
            Field.of("name", StandardSQLTypeName.STRING),
            Field.of("post_abbr", StandardSQLTypeName.STRING));
    loadCsvFromGcs(datasetName, tableName, sourceUri, schema);
  }

  public static void loadCsvFromGcs(
      String datasetName, String tableName, String sourceUri, Schema schema) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      // Skip header row in the file.
      CsvOptions csvOptions = CsvOptions.newBuilder().setSkipLeadingRows(1).build();

      TableId tableId = TableId.of(datasetName, tableName);
      LoadJobConfiguration loadConfig =
          LoadJobConfiguration.newBuilder(tableId, sourceUri, csvOptions).setSchema(schema).build();

      // Load data from a GCS CSV file into the table
      Job job = bigquery.create(JobInfo.of(loadConfig));
      // Blocks until this load table job completes its execution, either failing or succeeding.
      job = job.waitFor();
      if (job.isDone()) {
        System.out.println("CSV from GCS successfully added during load append job");
      } else {
        System.out.println(
            "BigQuery was unable to load into the table due to an error:"
                + job.getStatus().getError());
      }
    } catch (BigQueryException | InterruptedException e) {
      System.out.println("Column not added during load append \n" + e.toString());
    }
  }
}

Nota: El código se tomó de la página oficial de GCP. Tu lo puedes editar a tu conveniencia.

Hemos visto como cargar y exportar datos a tablas en BigQuery.

Enlaces:

https://cloud.google.com/bigquery/docs/exporting-data
https://cloud.google.com/bigquery/docs/loading-data-cloud-storage-csv

F#: un lenguaje funcional

F# (Efe sharp) es un lenguaje de paradigma funcional desarrollado por (la malévola empresa) Microsoft.

Presume de una simplicidad y facilidad para ser aprendido (y usado).

A primera vista nos recuerda a lenguajes como Powershell, Javascript, Perl, Python o Ruby.

F# tiene una sintaxis ligera que requiere muy poco código para compilar software.

Está respaldado por un sistema de tipos eficaz, una biblioteca estándar cómoda y un entorno de ejecución de .NET en el que puede confiar para crear software crítico que sea correcto, rápido y confiable.

La extensión de un programa en F# puede ser *.fs o *.fsx.

Si no tienes instalado Dotnet ve este sitio:

https://dotnet.microsoft.com/en-us/download

Al tenerlo ya instalado, te permitirá crear programas y aplicaciones F#.

Escribamos el clásico "Hola, mundo" en este lenguaje:

holamundo.fsx

printfn "Hola, mundo!!"

Para ejecutar podemos usar dotnet:

dotnet fsi holamundo.fsx

Salida:

Hola, mundo!!

Crear un proyecto F#

Para crear un proyecto F# deberás teclear esto:

dotnet new console --language F#
dotnet run

Se generará un proyecto F# y se ejecutará.

Variables en F#

F# es un lenguaje fuertemente tipado que utiliza inferencia de tipos (int, float, string, bool, etc.). Por lo que deberás cuidar el tipo de dato a declarar. Aunque existe la inferencia de tipos en caso de que no quieras definir el tipo.

variables.fs

// Tipos de datos
let numeroEntero = 23
let (numerico: int) = 32
let (cadena: string) = "FERROCARRILERO"
let (isOk: bool) = true
let real = 90.43

Hemos declarado algunas variables y definido sus tipos. Imprimiremos sus valores de la siguiente forma:

printfn "numeroEntero: %i" numeroEntero
printfn "numerico: %i" numerico
printfn "cadena: %s" cadena
printfn "isOk: %b" isOk
printfn "real: %f" real

Tambien podemos introducir datos, usando las liberías de .Net

entrada.fs

open System

printfn "Introduce tu nombre:"
let nombre = Console.ReadLine()
printfn "Hola, %s" nombre

Ejecutamos (dotnet run):

Introduce tu nombre:
Thomas Muller
Hola, Thomas Muller

En F# todas las variables son inmutables, es decir, no cambian de valor. Si requieres que una variable cambie su valor deberás usar la palabra ``mutable``, esto similar a lenguajes como Rust.

// Variables mutables
let mutable contador = 0
printfn "Contador: %i" contador
contador <- 1
printfn "Contador: %i" contador 

Esto es todo por hoy. En próximas entregas continuaremos con este tema.

Enlaces:

https://fsharp.org/
Vídeos sobre F#
https://es.wikipedia.org/wiki/F_Sharp
https://dotnet.microsoft.com/en-us/learn/dotnet/in-browser-tutorial/1