Búsquedas complejas en Postgresql

La mayoría de las veces realizaremos búsquedas por patrones conocidos y fáciles de obtener, simples y repetitivos, búsquedas directas, o parciales utilizando el comando “like“. Pero otras veces la información que intentamos localizar no se encuentra formateada como esperamos, conteniendo diferentes símbolos o signos repartidos entre el texto, lo cual dificulta enormemente encontrar los datos que buscamos.

Postgres cuenta con la función “similarity“. Esta función elimina todos los caracteres extraños; guiones, comas, puntos, signos de admiración y exclamación, etc. Dejando el texto lo más limpio posible, y divide la cadena resultante en trigramas. Realizando la comparación entre estos trigramas se obtiene un porcentaje de similitud. Este procedimiento funciona realmente bien al realizar búsquedas con cadenas complejas pero el problema es que es realmente lento. En una tabla con 200.000 registros, realizar una búsqueda con similarity puede llevar de 6 a 10 segundos, incluso más.

Para utilizar similarity en Postgres, necesitamos instalar la extensión pg_trgm con el siguiente comando:

CREATE EXTENSION pg_trgm;

Podemos obtener el trigrama de una cadena con:

select show_trgm('A-Team Year?, the')

Obtendremos el siguiente array:

{”  a”,”  t”,”  y”,” a “,” te”,” th”,” ye”,”am “,”ar “,eam,ear,”he “,tea,the,yea}

Para realizar una busqueda con similarity utilizamos la siguiente consulta:

SELECT * FROM filmografia
WHERE similarity('A-Team Year?, the', titulo) > 0.90

Titulo, es el campo en el que realizamos la búsqueda. 0.90 significa el porcentaje de similitud que requerimos. Si la comparación es superior al porcentaje requerido, se incluirá en los resultados. Podemos jugar con diferentes porcentajes hasta encontrar el que mejor se adapte a las búsquedas requeridas.

Este método es muy efectivo al realizar búsquedas, aunque también es muy lento.

Búsquedas contranslate

Otro método de búsqueda que nos puede resultar útil es utilizar la función “translate“. Con translate podemos reemplazar una serie de caracteres por otros:

select lower(translate('A-Team Year?, the', ' ,-,?,(,)', ''))

Con la consulta anterior obtenemos: “ateamyearthe

Como vemos se han eliminado todos los caracteres superfluos y los espacios, e igualando la cadena de búsqueda con las cadenas a explorar, podremos realizar búsquedas más complejas con buenos resultados.

Aquí la utilizamos para realizar búsquedas eliminando todos los caracteres innecesarios:

SELECT * FROM filmografia
WHERE LOWER(translate(titulo, ' ,-,?,(,)', '')) = LOWER(translate('A-Team Year?, the', ' ,-,?,(,)', ''))

Búsquedas con “Expresiones regulares”

Al realizar determinadas búsquedas también podemos utilizar “expresiones regulares“. Nos permiten obtener resultados que de otra forma seria mucho más complicado o no seria posible.

Si por ejemplo queremos buscar todas las cadenas que comienzan por un número podemos utilizar la siguiente expresión:

SELECT * FROM filmografía
WHERE titulo ~ '^[0-9].*'

Obtendremos todas las cadenas que por lo menos el primer carácter sea un número.

Existen infinidad de formas en las que podemos obtener aquellos datos que necesitamos, aquí, solo hemos comentado algunas de ellas.



Si encuentras esta noticia interesante la puedes compartir en tus redes sociales, a tus seguidores les puede gustar. Utiliza los botones que tienes más abajo.

Share

Convertir Arduino en un controlador de dispositivos con Alexa

Vamos a convertir, una tarjeta Arduino Wemos (Con modulo wifi), utilizando un modulo de reles, en un accesorio para encender/apagar diferentes dispositivos; luces, ventiladores, calefactores, discos duros o cualquier otro dispositivo que se te ocurra.

Para realizar esta tarea nos hace falta:

  • Una tarjeta Wemos.
  • Un modulo de reles, puede ser de 1, 2, 4 u 8 reles.
  • Darnos de alta en la web https://sinric.com/
  • Instalar la skill Sinric en Amazon Alexa.

Sinric es una Api gratuita desde la que vamos a poder crear todos aquellos dispositivos que necesitemos controlar. Por cada dispositivo que creamos, Sinric asigna un ID para su identificación.

Para añadir un nuevo dispositivo pulsamos en el botón “ADD” de la sección “Smart Home Device“, se abrira un panel como el de la imagen superior.

  • Friendly Name. Es el nombre con el que invocaremos a Alexa, para gestionar nuestro dispositivo, encender / apagar.
  • Description. La descripción del dispositivo.
  • Device Type. De la lista desplegable seleccionamos el que más se adapte a nuestro tipo de dispositivo.

Guardamos el nuevo dispositivo, y realizamos la misma operación para crear todos los que necesitemos.

En la pantalla principal de sinric veremos todos los dispositivos creados, es importante apuntar los ID que la aplicación ha generado. También debemos anotar el “Your API Key“, se encuentra en la parte superior, con estos datos podremos controlar los dispositivos desde nuestra Arduino.

En este repositorio de github podemos encontrar los archivos “ino” para la tarjeta Arduino, existen diferentes ejemplos para el uso de la api de sinric.

Aqui tienes el código que utilizo para gestionar mis dispositivos a traves de Alexa. Se pueden controlar 4 dispositivos, pero es muy fácil añadir más.

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsClient.h> //  https://github.com/kakopappa/sinric/wiki/How-to-add-dependency-libraries
#include <ArduinoJson.h> // https://github.com/kakopappa/sinric/wiki/How-to-add-dependency-libraries
#include <StreamString.h>

ESP8266WiFiMulti WiFiMulti;
WebSocketsClient webSocket;
WiFiClient client;

#define MyApiKey "Tu-Api-Key-de-Sinric" // TODO: Change to your sinric API Key. Your API Key is displayed on sinric.com dashboard
#define MySSID "El-Nombre-de-tu-WIFI" // TODO: Change to your Wifi network SSID
#define MyWifiPassword "La-Contraseña-de-tu-WIFI" // TODO: Change to your Wifi network password

#define HEARTBEAT_INTERVAL 300000 // 5 Minutes 

const int relayPin1 = D1;
const int relayPin2 = D2;
const int relayPin3 = D3;
const int relayPin4 = D4;

uint64_t heartbeatTimestamp = 0;
bool isConnected = false;

void setPowerStateOnServer(String deviceId, String value);
void setTargetTemperatureOnServer(String deviceId, String value, String scale);

// deviceId is the ID assgined to your smart-home-device in sinric.com dashboard. Copy it from dashboard and paste it here

void turnOn(String deviceId) {

  // Device 1
  if (deviceId == "El-ID-de-tu-Dispositivo") // Device ID of first device
  {
    Serial.print("Turn on device id: ");
    Serial.println(deviceId);

    digitalWrite(relayPin1, HIGH); // turn on relay with voltage HIGH
  }
  else {
    Serial.print("Turn on for unknown device id: ");
    Serial.println(deviceId);
  }

  // Device 2
  if (deviceId == "El-ID-de-tu-Dispositivo") // Device ID of first device
  {
    Serial.print("Turn on device id: ");
    Serial.println(deviceId);

    digitalWrite(relayPin2, HIGH); // turn on relay with voltage HIGH
  }
  else {
    Serial.print("Turn on for unknown device id: ");
    Serial.println(deviceId);
  }

  // Device 3
  if (deviceId == "El-ID-de-tu-Dispositivo") // Device ID of first device
  {
    Serial.print("Turn on device id: ");
    Serial.println(deviceId);

    digitalWrite(relayPin3, HIGH); // turn on relay with voltage HIGH
  }
  else {
    Serial.print("Turn on for unknown device id: ");
    Serial.println(deviceId);
  }

  // Device 4
  if (deviceId == "El-ID-de-tu-Dispositivo") // Device ID of first device
  {
    Serial.print("Turn on device id: ");
    Serial.println(deviceId);

    digitalWrite(relayPin4, HIGH); // turn on relay with voltage HIGH
  }
  else {
    Serial.print("Turn on for unknown device id: ");
    Serial.println(deviceId);
  }
}

void turnOff(String deviceId) {

  // Device 1
  if (deviceId == "El-ID-de-tu-Dispositivo") // Device ID of first device
  {
    Serial.print("Turn off Device ID: ");
    Serial.println(deviceId);

    digitalWrite(relayPin1, LOW);  // turn off relay with voltage LOW
  }
  else {
    Serial.print("Turn off for unknown device id: ");
    Serial.println(deviceId);
  }

  // Device 2
  if (deviceId == "El-ID-de-tu-Dispositivo") // Device ID of first device
  {
    Serial.print("Turn off Device ID: ");
    Serial.println(deviceId);

    digitalWrite(relayPin2, LOW);  // turn off relay with voltage LOW
  }
  else {
    Serial.print("Turn off for unknown device id: ");
    Serial.println(deviceId);
  }

  // Device 3
  if (deviceId == "El-ID-de-tu-Dispositivo") // Device ID of first device
  {
    Serial.print("Turn off Device ID: ");
    Serial.println(deviceId);

    digitalWrite(relayPin3, LOW);  // turn off relay with voltage LOW
  }
  else {
    Serial.print("Turn off for unknown device id: ");
    Serial.println(deviceId);
  }

  // Device 4
  if (deviceId == "El-ID-de-tu-Dispositivo") // Device ID of first device
  {
    Serial.print("Turn off Device ID: ");
    Serial.println(deviceId);

    digitalWrite(relayPin4, LOW);  // turn off relay with voltage LOW
  }
  else {
    Serial.print("Turn off for unknown device id: ");
    Serial.println(deviceId);
  }
}

void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
  switch (type) {
    case WStype_DISCONNECTED:
      isConnected = false;
      Serial.printf("[WSc] Webservice disconnected from sinric.com!\n");
      break;
    case WStype_CONNECTED: {
        isConnected = true;
        Serial.printf("[WSc] Service connected to sinric.com at url: %s\n", payload);
        Serial.printf("Waiting for commands from sinric.com ...\n");
      }
      break;
    case WStype_TEXT: {
        Serial.printf("[WSc] get text: %s\n", payload);
        // Example payloads

        // For Switch or Light device types
        // {"deviceId": xxxx, "action": "setPowerState", value: "ON"} // https://developer.amazon.com/docs/device-apis/alexa-powercontroller.html

        // For Light device type
        // Look at the light example in github

        DynamicJsonBuffer jsonBuffer;
        JsonObject& json = jsonBuffer.parseObject((char*)payload);
        String deviceId = json ["deviceId"];
        String action = json ["action"];

        if (action == "setPowerState") { // Switch or Light
          String value = json ["value"];
          if (value == "ON") {
            turnOn(deviceId);
          } else {
            turnOff(deviceId);
          }
        }
        else if (action == "SetTargetTemperature") {
          String deviceId = json ["deviceId"];
          String action = json ["action"];
          String value = json ["value"];
        }
        else if (action == "test") {
          Serial.println("[WSc] received test command from sinric.com");
        }
      }
      break;
    case WStype_BIN:
      Serial.printf("[WSc] get binary length: %u\n", length);
      break;
  }
}

void setup() {
  Serial.begin(115200);

  // Relay PIN eg: https://github.com/wemos/D1_mini_Examples/blob/master/examples/04.Shields/Relay_Shield/Blink/Blink.ino
  pinMode(relayPin1, OUTPUT);
  pinMode(relayPin2, OUTPUT);
  pinMode(relayPin3, OUTPUT);
  pinMode(relayPin4, OUTPUT);
  

  WiFiMulti.addAP(MySSID, MyWifiPassword);
  Serial.println();
  Serial.print("Connecting to Wifi: ");
  Serial.println(MySSID);

  // Waiting for Wifi connect
  while (WiFiMulti.run() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  if (WiFiMulti.run() == WL_CONNECTED) {
    Serial.println("");
    Serial.print("WiFi connected. ");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  }

  // server address, port and URL
  webSocket.begin("iot.sinric.com", 80, "/");

  // event handler
  webSocket.onEvent(webSocketEvent);
  webSocket.setAuthorization("apikey", MyApiKey);

  // try again every 5000ms if connection has failed
  webSocket.setReconnectInterval(5000);   // If you see 'class WebSocketsClient' has no member named 'setReconnectInterval' error update arduinoWebSockets
}

void loop() {
  webSocket.loop();

  if (isConnected) {
    uint64_t now = millis();

    // Send heartbeat in order to avoid disconnections during ISP resetting IPs over night. Thanks @MacSass
    if ((now - heartbeatTimestamp) > HEARTBEAT_INTERVAL) {
      heartbeatTimestamp = now;
      webSocket.sendTXT("H");
    }
  }
}

// If you are going to use a push button to on/off the switch manually, use this function to update the status on the server
// so it will reflect on Alexa app.
// eg: setPowerStateOnServer("deviceid", "ON")

// Call ONLY If status changed. DO NOT CALL THIS IN loop() and overload the server.
void setPowerStateOnServer(String deviceId, String value) {
  DynamicJsonBuffer jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  root["deviceId"] = deviceId;
  root["action"] = "setPowerState";
  root["value"] = value;
  StreamString databuf;
  root.printTo(databuf);

  webSocket.sendTXT(databuf);
}

//eg: setPowerStateOnServer("deviceid", "CELSIUS", "25.0")

// Call ONLY If status changed. DO NOT CALL THIS IN loop() and overload the server.
void setTargetTemperatureOnServer(String deviceId, String value, String scale) {
  DynamicJsonBuffer jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  root["action"] = "SetTargetTemperature";
  root["deviceId"] = deviceId;

  JsonObject& valueObj = root.createNestedObject("value");
  JsonObject& targetSetpoint = valueObj.createNestedObject("targetSetpoint");
  targetSetpoint["value"] = value;
  targetSetpoint["scale"] = scale;

  StreamString databuf;
  root.printTo(databuf);

  webSocket.sendTXT(databuf);
}

Rellena los siguientes apartados con tus datos:

  • Tu-Api-Key-de-Sinric.
  • El-Nombre-de-tu-WIFI
  • La-Contraseña-de-tu-WIFI
  • El-ID-de-tu-Dispositivo

Carga el archivo “ino” en la tarjeta Arduino.

Ahora queda montar tu tarjeta Arduino Wemos con la placa de reles, hacer las conexiones entre Arduino y reles, y las conexiones para la corriente electrica de los enchufes que utilices.

Para comprobar que todo funciona correctamente, aun nos queda realizar un paso más.

Desde la aplicación “Amazon Aleza“, de tu dispositivo móvil, instala la skill de “Sinric“, puedes ver más información pinchando en el enlace.

Una vez configurada debes pulsar en “Más” y “Añadir dispositivo“. Pulsa sobre uno de los dispositivos, por ejemplo “Luz“, y en la siguiente pantalla, pulsa en “Otro“. Pulsa en el botón “Detectar Dispositivos“. Si todo fue bien, pasados unos minutos, Alexa habra detectado los nuevos dispositivos.

Dile a Alexa que encienda alguno de los dispositivos que has creado en Sinric.

Recuerda que estos dispositivos se comportan exactamente como cualquier otro dispositivo que has comprado. Creando rutinas, puedes programarlo para que se encienda/apage a una hora determinada o cuando se produzca un determinado evento.



Si encuentras esta noticia interesante la puedes compartir en tus redes sociales, a tus seguidores les puede gustar. Utiliza los botones que tienes más abajo.

Share

Crea un log fácilmente para tus proyectos en Python

Un fichero de registro o log nos permite llevar una cronología de lo que va sucediendo en nuestra aplicación. Podemos registrar con facilidad todos aquellos procesos, fallos y errores que consideramos oportunos, esto nos permite poder encontrar fallos y errores presentes en el script que ejecutamos.

Esta utilidad esta presente en el modulo “logging“. Nos permite crear de forma fácil y rápida los archivos log en nuestros proyectos.

Logging dispone de 5 métodos para notificar los diferentes mensajes:

  • Debug. Normalmente se utiliza cuando estamos desarrollando nuestra aplicación, nos permite depurar el proyecto.
  • Info. Mensajes informativos. Pueden indicar que los procesos se realizan de forma correcta.
  • Warning. Utilizado para realizar advertencias, algo puede no ir bien o causar problemas en un futuro proximo.
  • Error. Algo ha salido mal o el resultado no es el esperado.
  • Critical. Posiblemente el programa deje de funcionar o responder.

Podemos configurar logging para que cree un archivo de texto, en el cual se guardarán los mensajes que vamos colocando en el código.

import logging
from datetime import datetime, date, time

ahora = datetime.now()
fecha_log = ahora.strftime('%d-%m-%Y_%H-%M-%S')

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s : %(levelname)s : %(message)s',
                    filename = fecha_log + '_registro.log',
                    filemode = 'w',)
  • Level. Nivel del mensaje.
  • Format. El formato en el cual serán guardados los mensajes.
  • Filename. El archivo que contendrá el log.
  • Filemode. El modo del archivo.

Ejemplo del formato de salida de los mensajes:

Ahora solo nos queda añadir la salida de los mensajes en los lugares de nuestra aplicación que consideremos más oportunos, propensos a errores, criticos o simplemente informativos.

El formato para los mensajes es el siguiente:

logging.debug('Información detallada, generalmente de interés solo cuando se diagnostican problemas.')
logging.info('Confirmación de que las cosas funcionan como se esperaba.')
logging.warning('Una indicación de que sucedió algo inesperado o indicativo de algún problema en el futuro cercano (por ejemplo, "espacio en disco bajo"). El software sigue funcionando como se esperaba.')
logging.error('Debido a un problema más serio, el software no ha podido realizar alguna función.')
logging.critical('Un error grave que indica que el programa en sí no puede continuar ejecutándose.')

Aqui, puedes encontrar la documentación oficial de logging.



Si encuentras esta noticia interesante la puedes compartir en tus redes sociales, a tus seguidores les puede gustar. Utiliza los botones que tienes más abajo.

Share

Ejecutar con Python una función con determinados periodos de tiempo

Muchas veces utilizamos crontab para programar la ejecución de un determinado script de python en determinadas horas o fechas. Utilizando crontab, el periodo de tiempo más corto para lanzar un script es de un minuto, si necesitamos realizar procesos con periodos de tiempo más cortos debemos utilizar otras técnicas.

Existen multitud de formas para ejecutar un función que lance los procesos de nuestro script, unas más complejas y sofisticadas, otras más sencillas, que no dependen de funcionalidades ni complementos externos.

Este pequeño ejemplo de script de python, ejecuta una función cada 15 segundos:

import time

while True:
    funcion_a_ejecutar()
    time.sleep(15)
    print("Se Ejecuto!!!")

Este proceso crea un bucle infinito. Ejecuta la función “funcion_a_ejecutar()“, y seguidamente espera a que transcurran 15 segundos, e imprime por pantalla el mensaje “Se Ejecuto!!!“. Al ser un bucle infinito, ejecutara este mismo proceso continuamente.

Existen complementos que realizan tareas similares como pueden ser:

  • InterruptableLoop
  • threading
  • Scheduler

Entre otras muchas opciones disponibles con diferentes librerias de python. Hoy hemos propuesto el sistema más sencillo y sin la necesidad de librerias externas.



Si encuentras esta noticia interesante la puedes compartir en tus redes sociales, a tus seguidores les puede gustar. Utiliza los botones que tienes más abajo.

Share

Añade un menú animado y colorido en tu pagina Web, en CSS

Es posible crear un menú animado muy vistoso para tu pagina web con un sencillo código CSS.

CSS nos permite dar formato a las paginas web, convertirlas en algo mucho más atractivo que lo que nos da el soso html. Es increíble la cantidad de posibilidades que nos ofrece CSS para crear un atractivo, colorido y dinámico menú.

Este ejemplo de menú esta diseñado para contener 5 elementos, pero fácilmente puede ser modificado, si necesitas un número de elementos mayor o menor.

Código de ejemplo html:

<nav>
    <a href="#">Inicio</a>
    <a href="#">Blog</a>
    <a href="#">Sitemap</a>
    <a href="#">Portafolio</a>
    <a href="#">Contact0</a>
    <div class="animation start-home"></div>
</nav>

Código CSS:

nav {
    margin: 27px auto 0;
    position: relative;
    width: 590px;
    height: 50px;
    background-color: #34495e;
    border-radius: 8px;
    font-size: 0;
    margin-top: 200px;
}

nav a {
    line-height: 50px;
    height: 100%;
    font-size: 15px;
    display: inline-block;
    position: relative;
    z-index: 1;
    text-decoration: none;
    text-transform: uppercase;
    text-align: center;
    color: white;
    cursor: pointer;
}

nav .animation {
    position: absolute;
    height: 100%;
    top: 0;
    z-index: 0;
    transition: all .5s ease 0s;
    border-radius: 8px;
}

a:nth-child(1) {
    width: 100px;
}

a:nth-child(2) {
    width: 110px;
}

a:nth-child(3) {
    width: 100px;
}

a:nth-child(4) {
    width: 160px;
}

a:nth-child(5) {
    width: 120px;
}

nav .start-home,
a:nth-child(1):hover~.animation {
    width: 100px;
    left: 0;
    background-color: #1abc9c;
}

nav .start-about,
a:nth-child(2):hover~.animation {
    width: 110px;
    left: 100px;
    background-color: #e74c3c;
}

nav .start-blog,
a:nth-child(3):hover~.animation {
    width: 100px;
    left: 210px;
    background-color: #3498db;
}

nav .start-portefolio,
a:nth-child(4):hover~.animation {
    width: 160px;
    left: 310px;
    background-color: #9b59b6;
}

nav .start-contact,
a:nth-child(5):hover~.animation {
    width: 120px;
    left: 470px;
    background-color: #e67e22;
}

body {
    font-size: 12px;
    font-family: sans-serif;
    background: #2c3e50;
}

span {
    color: #2BD6B4;
}

Da un aspecto elegante a tu menú con este llamativo ejemplo en CSS.



Si encuentras esta noticia interesante la puedes compartir en tus redes sociales, a tus seguidores les puede gustar. Utiliza los botones que tienes más abajo.

Share